GF_EXPORT GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com) { GF_Err e; u32 BodyStart, size; if (!sess || !com) return GF_BAD_PARAM; //reset the command gf_rtsp_command_reset(com); //if no connection, we have sent a "Connection: Close" if (!sess->connection) return GF_IP_CONNECTION_CLOSED; //lock gf_mx_p(sess->mx); //fill TCP buffer e = gf_rtsp_fill_buffer(sess); if (e) goto exit; //this is upcoming, interleaved data if (strncmp(sess->TCPBuffer+sess->CurrentPos, "RTSP", 4)) { e = GF_IP_NETWORK_EMPTY; goto exit; } e = gf_rtsp_read_reply(sess); if (e) goto exit; gf_rtsp_get_body_info(sess, &BodyStart, &size); e = RTSP_ParseCommandHeader(sess, com, BodyStart); //before returning an error we MUST reset the TCP buffer //copy the body if any if (!e && com->Content_Length) { com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length)); memcpy(com->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, com->Content_Length); } //reset TCP buffer sess->CurrentPos += BodyStart + com->Content_Length; if (!com->CSeq) com->StatusCode = NC_RTSP_Bad_Request; if (e || (com->StatusCode != NC_RTSP_OK)) goto exit; //NB: there is no "session state" in our lib when acting at the server side, as it depends //on the server implementation. We cannot block responses / announcement to be sent //dynamically, nor reset the session ourselves as we don't know the details of the session //(eg TEARDOWN may keep resources up or not, ...) //we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it //happens during aggregation of PLAY/PAUSE with overlapping ranges) //however store the last CSeq in case for client checking if (!sess->CSeq) { sess->CSeq = com->CSeq; } //check we're in the right range else { if (sess->CSeq >= com->CSeq) com->StatusCode = NC_RTSP_Header_Field_Not_Valid; else sess->CSeq = com->CSeq; } // //if a connection closed is signal, check this is the good session // and reset it (the client is no longer connected) if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id) && com->Connection && !stricmp(com->Connection, "Close")) { gf_rtsp_session_reset(sess, 0); //destroy the socket if (sess->connection) gf_sk_del(sess->connection); sess->connection = NULL; //destroy the http tunnel if any if (sess->HasTunnel && sess->http) { gf_sk_del(sess->http); sess->http = NULL; } } exit: gf_mx_v(sess->mx); return e; }
M4Err RTSP_GetRequest(RTSPSession *sess, RTSPCommand *com) { M4Err e; u32 BodyStart, size; if (!sess || !com) return M4BadParam; //reset the command RTSP_ResetCommand(com); //if no connection, we have sent a "Connection: Close" if (!sess->connection) return M4ConnectionClosed; //lock MX_P(sess->mx); //fill TCP buffer e = RTSP_FillTCPBuffer(sess); if (e) goto exit; //this is upcoming, interleaved data if (strncmp(sess->TCPBuffer+sess->CurrentPos, "RTSP", 4)) { e = M4NetworkEmpty; goto exit; } e = RTSP_ReadReply(sess); if (e) goto exit; RTSP_GetBodyInfo(sess, &BodyStart, &size); e = RTSP_ParseCommandHeader(sess, com, BodyStart); //before returning an error we MUST reset the TCP buffer //copy the body if any if (!e && com->Content_Length) { com->body = malloc(sizeof(char) * (com->Content_Length)); memcpy(com->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, com->Content_Length); } //reset TCP buffer sess->CurrentPos += BodyStart + com->Content_Length; if (!com->CSeq) com->StatusCode = NC_RTSP_Bad_Request; if (e || (com->StatusCode != NC_RTSP_OK)) goto exit; //NB: there is no "session state" in our lib when acting at the server side, as it depends //on the server implementation. We cannot block responses / announcement to be sent //dynamically, nor reset the session ourselves as we don't know the details of the session //(eg TEARDOWN may keep resources up or not, ...) //we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it //happens during aggregation of PLAY/PAUSE with overlapping ranges) //however store the last CSeq in case for client checking if (!sess->CSeq) { sess->CSeq = com->CSeq; } //check we're in the right range else { if (sess->CSeq >= com->CSeq) com->StatusCode = NC_RTSP_Header_Field_Not_Valid; else sess->CSeq = com->CSeq; } // //if a connection closed is signal, check this is the good session // and reset it (the client is no longer connected) if (sess->SessionID && com->Session && !strcmp(com->Session, sess->SessionID) && com->Connection && !stricmp(com->Connection, "Close")) { RTSP_ResetSession(sess, 0); //destroy the socket if (sess->connection) SK_Delete(sess->connection); sess->connection = NULL; //destroy the http tunnel if any if (sess->HasTunnel && sess->http) { SK_Delete(sess->http); sess->http = NULL; } } exit: MX_V(sess->mx); return e; }