GF_Err gf_rtsp_refill_buffer(GF_RTSPSession *sess) { GF_Err e; u32 res; char *ptr; if (!sess) return GF_BAD_PARAM; if (!sess->connection) return GF_IP_NETWORK_EMPTY; res = sess->CurrentSize - sess->CurrentPos; if (!res) return gf_rtsp_fill_buffer(sess); // printf("Forcing reading\n"); ptr = (char *)gf_malloc(sizeof(char) * res); memcpy(ptr, sess->TCPBuffer+sess->CurrentPos, res); memcpy(sess->TCPBuffer, ptr, res); gf_free(ptr); sess->CurrentPos = 0; sess->CurrentSize = res; //now read from current pos e = gf_sk_receive(sess->connection, sess->TCPBuffer + sess->CurrentSize, RTSP_TCP_BUF_SIZE - sess->CurrentSize, 0, &res); if (!e) { sess->CurrentSize += res; } return e; }
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; }
GF_EXPORT GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp) { GF_Err e; Bool force_reset = GF_FALSE; u32 BodyStart, size; if (!sess || !rsp) return GF_BAD_PARAM; gf_rtsp_response_reset(rsp); //LOCK gf_mx_p(sess->mx); e = gf_rtsp_check_connection(sess); if (e) goto exit; //push data in our queue e = gf_rtsp_fill_buffer(sess); if (e) goto exit; //this is interleaved data if (!IsRTSPMessage(sess->TCPBuffer+sess->CurrentPos) ) { gf_rtsp_session_read(sess); e = GF_IP_NETWORK_EMPTY; goto exit; } e = gf_rtsp_read_reply(sess); if (e) goto exit; //get the reply gf_rtsp_get_body_info(sess, &BodyStart, &size); e = RTSP_ParseResponseHeader(sess, rsp, BodyStart); //copy the body if any if (!e && rsp->Content_Length) { rsp->body = (char *)gf_malloc(sizeof(char) * (rsp->Content_Length)); memcpy(rsp->body, sess->TCPBuffer+sess->CurrentPos + BodyStart, rsp->Content_Length); } GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Response:\n%s\n", sess->TCPBuffer+sess->CurrentPos)); //reset TCP buffer sess->CurrentPos += BodyStart + rsp->Content_Length; if (e) goto exit; //update RTSP aggreagation info if (sess->NbPending) sess->NbPending -= 1; if (sess->RTSP_State == GF_RTSP_STATE_WAITING) sess->RTSP_State = GF_RTSP_STATE_INIT; //control, and everything is received else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL) { if (!sess->NbPending) sess->RTSP_State = GF_RTSP_STATE_INIT; } //this is a late reply to an aggregated control - signal nothing if (!strcmp(sess->RTSPLastRequest, "RESET") && sess->CSeq > rsp->CSeq) { e = GF_IP_NETWORK_EMPTY; goto exit; } //reset last request if (sess->RTSP_State == GF_RTSP_STATE_INIT) strcpy(sess->RTSPLastRequest, ""); //check the CSeq is in the right range. The server should ALWAYS reply in sequence //to an aggreagated sequence of requests //if we have reseted the connection (due to an APP error) return empty if (rsp->CSeq && sess->CSeq > rsp->CSeq + sess->NbPending) { gf_mx_v(sess->mx); return gf_rtsp_get_response(sess, rsp); } if (sess->CSeq != rsp->CSeq + sess->NbPending) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Invalid sequence number - got %d but expected %d\n", sess->CSeq, rsp->CSeq + sess->NbPending)); e = GF_REMOTE_SERVICE_ERROR; goto exit; } /*check session ID*/ if (rsp->Session && sess->last_session_id && strcmp(sess->last_session_id, rsp->Session)) { e = GF_REMOTE_SERVICE_ERROR; goto exit; } //destroy sessionID if needed - real doesn't close the connection when destroying //session if (!strcmp(sess->RTSPLastRequest, GF_RTSP_TEARDOWN)) { sess->last_session_id = NULL; } exit: if (rsp->Connection && !stricmp(rsp->Connection, "Close")) force_reset = GF_TRUE; else if (e && (e != GF_IP_NETWORK_EMPTY)) force_reset = GF_TRUE; if (force_reset) { gf_rtsp_session_reset(sess, GF_FALSE); //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; } } gf_mx_v(sess->mx); return e; }