void RP_ProcessCommands(RTSPSession *sess) { GF_RTSPCommand *com; GF_Err e; u32 time; com = RP_GetCommand(sess); /*if asked or command to send, flushout TCP - TODO: check what's going on with ANNOUNCE*/ if ((com && !(sess->flags & RTSP_WAIT_REPLY) ) || (sess->flags & RTSP_TCP_FLUSH) ) { while (1) { e = gf_rtsp_session_read(sess->session); if (e) break; } sess->flags &= ~RTSP_TCP_FLUSH; } /*handle response or announce*/ if ( (com && (sess->flags & RTSP_WAIT_REPLY) ) /*|| (!com && sess->owner->handle_announce)*/) { e = gf_rtsp_get_response(sess->session, sess->rtsp_rsp); if (e!= GF_IP_NETWORK_EMPTY) { e = RP_ProcessResponse(sess, com, e); /*this is a service connect error -> plugin may be discarded */ if (e!=GF_OK) { RP_RemoveCommand(sess); gf_rtsp_command_del(com); gf_term_on_connect(sess->owner->service, NULL, e); return; } RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; } else { /*evaluate timeout*/ time = gf_sys_clock() - sess->command_time; /*don't waste time waiting for teardown ACK, half a sec is enough. If server is not replying in time it is likely to never reply (happens with RTP over RTSP) -> kill session and create new one*/ if (!strcmp(com->method, GF_RTSP_TEARDOWN) && (time>=500) ) time = sess->owner->time_out; //signal what's going on if (time >= sess->owner->time_out) { if (!strcmp(com->method, GF_RTSP_TEARDOWN)) gf_rtsp_session_reset(sess->session, 1); RP_ProcessResponse(sess, com, GF_IP_NETWORK_FAILURE); RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; gf_rtsp_reset_aggregation(sess->session); } } return; } if (!com) return; /*send command - check RTSP session state first*/ switch (gf_rtsp_get_session_state(sess->session)) { case GF_RTSP_STATE_WAITING: case GF_RTSP_STATE_WAIT_FOR_CONTROL: return; case GF_RTSP_STATE_INVALIDATED: RP_SendFailure(sess, com, GF_IP_NETWORK_FAILURE); RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; return; } /*process*/ com->User_Agent = (char*)gf_modules_get_option((GF_BaseInterface *) gf_term_get_service_interface(sess->owner->service), "Downloader", "UserAgent"); if (!com->User_Agent) com->User_Agent = "GPAC " GPAC_VERSION " RTSP Client"; com->Accept_Language = RTSP_LANGUAGE; /*if no session assigned and a session ID is valid, use it*/ if (sess->session_id && !com->Session) com->Session = sess->session_id; e = GF_OK; /*preprocess describe before sending (always the ESD url thing)*/ if (!strcmp(com->method, GF_RTSP_DESCRIBE)) { com->Session = NULL; if (!RP_PreprocessDescribe(sess, com)) { e = GF_BAD_PARAM; goto exit; } } /*preprocess play/stop/pause before sending (aggregation)*/ if (!strcmp(com->method, GF_RTSP_PLAY) || !strcmp(com->method, GF_RTSP_PAUSE) || !strcmp(com->method, GF_RTSP_TEARDOWN)) { //command is skipped if (!RP_PreprocessUserCom(sess, com)) { e = GF_BAD_PARAM; goto exit; } } e = gf_rtsp_send_command(sess->session, com); if (e) { RP_SendFailure(sess, com, e); RP_ProcessResponse(sess, com, e); } else { sess->command_time = gf_sys_clock(); sess->flags |= RTSP_WAIT_REPLY; } exit: /*reset static strings*/ com->User_Agent = NULL; com->Accept_Language = NULL; com->Session = NULL; /*remove command*/ if (e) { RP_RemoveCommand(sess); gf_rtsp_command_del(com); sess->flags &= ~RTSP_WAIT_REPLY; sess->command_time = 0; } }
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; }