//format a DESCRIBE, SETUP, PLAY or PAUSE on a session //YOUR COMMAND MUST BE FORMATTED ACCORDINGLY //sCtrl contains a control string if needed, formating the REQUEST as server_url/service_name/sCtrl GF_EXPORT GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com) { GF_Err e; char *sCtrl; const char *rad; u32 size; char buffer[1024], *result, *body; if (!com || !com->method) return GF_BAD_PARAM; sCtrl = com->ControlString; //NB: OPTIONS is not sent this way if (strcmp(com->method, GF_RTSP_DESCRIBE) && strcmp(com->method, GF_RTSP_ANNOUNCE) && strcmp(com->method, GF_RTSP_GET_PARAMETER) && strcmp(com->method, GF_RTSP_SET_PARAMETER) && strcmp(com->method, GF_RTSP_SETUP) && strcmp(com->method, GF_RTSP_PLAY) && strcmp(com->method, GF_RTSP_PAUSE) && strcmp(com->method, GF_RTSP_RECORD) && strcmp(com->method, GF_RTSP_REDIRECTE) && strcmp(com->method, GF_RTSP_TEARDOWN) && strcmp(com->method, GF_RTSP_OPTIONS) ) return GF_BAD_PARAM; //check the state machine if (strcmp(com->method, GF_RTSP_PLAY) && strcmp(com->method, GF_RTSP_PAUSE) && strcmp(com->method, GF_RTSP_RECORD) && sess->RTSP_State != GF_RTSP_STATE_INIT) return GF_SERVICE_ERROR; //aggregation is ONLY for the same request - unclear in RFC2326 ... //it is often mentioned "queued requests" at the server, like 3 PLAYS //and a PAUSE .... /* else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL && strcmp(com->method, sess->RTSPLastRequest)) && strcmp(com->method, GF_RTSP_OPTIONS)) return GF_BAD_PARAM; */ //OPTIONS must have a parameter string if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM; //update sequence number sess->CSeq += 1; sess->NbPending += 1; if (!strcmp(com->method, GF_RTSP_OPTIONS)) { sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION); } else { rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu"; if (sCtrl) { //if both server and service names are included in the control, just //use the control if (strstr(sCtrl, sess->Server) && strstr(sCtrl, sess->Service)) { sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION); } //if service is specified in ctrl, do not rewrite it else if (strstr(sCtrl, sess->Service)) { sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, GF_RTSP_VERSION); } else if (!strnicmp(sCtrl, "rtsp", 4)) { sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, GF_RTSP_VERSION); } //otherwise rewrite full URL else { sprintf(buffer, "%s %s://%s/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Service, sCtrl, GF_RTSP_VERSION); // sprintf(buffer, "%s %s://%s:%d/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, sCtrl, GF_RTSP_VERSION); } } else { sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION); } } //Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY body = NULL; if (strcmp(com->method, GF_RTSP_ANNOUNCE) && strcmp(com->method, GF_RTSP_GET_PARAMETER) && strcmp(com->method, GF_RTSP_SET_PARAMETER) ) { //this is an error, but don't say anything if (com->body) { body = com->body; com->body = NULL; } } result = NULL; e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size); //restore body if needed if (body) com->body = body; if (e) goto exit; GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result)); //send buffer e = gf_rtsp_send_data(sess, result, size); if (e) goto exit; //update our state if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL; else sess->RTSP_State = GF_RTSP_STATE_WAITING; //teardown invalidates the session most of the time, so we force the user to wait for the reply //as the reply may indicate a connection-closed strcpy(sess->RTSPLastRequest, com->method); exit: if (result) gf_free(result); return e; }
//format a DESCRIBE, SETUP, PLAY or PAUSE on a session //YOUR COMMAND MUST BE FORMATTED ACCORDINGLY //sCtrl contains a control string if needed, formating the REQUEST as server_url/service_name/sCtrl M4Err RTSP_SendRequest(RTSPSession *sess, RTSPCommand *com) { M4Err e; char *rad, *sCtrl; u32 size; char buffer[1024], *result, *body; if (!com || !com->method) return M4BadParam; sCtrl = com->ControlString; //NB: OPTIONS is not sent this way if (strcmp(com->method, RTSP_DESCRIBE) && strcmp(com->method, RTSP_ANNOUNCE) && strcmp(com->method, RTSP_GET_PARAMETER) && strcmp(com->method, RTSP_SET_PARAMETER) && strcmp(com->method, RTSP_SETUP) && strcmp(com->method, RTSP_PLAY) && strcmp(com->method, RTSP_PAUSE) && strcmp(com->method, RTSP_RECORD) && strcmp(com->method, RTSP_REDIRECT) && strcmp(com->method, RTSP_TEARDOWN) && strcmp(com->method, RTSP_OPTIONS) ) return M4BadParam; //check the state machine if (strcmp(com->method, RTSP_PLAY) && strcmp(com->method, RTSP_PAUSE) && strcmp(com->method, RTSP_RECORD) && sess->RTSP_State != RSM_Init) return M4ServiceError; //aggregation is ONLY for the same request - unclear in RFC2326 ... //it is often mentioned "queued requests" at the server, like 3 PLAYS //and a PAUSE .... /* else if (sess->RTSP_State == RSM_WaitForControl && strcmp(com->method, sess->RTSPLastRequest)) && strcmp(com->method, RTSP_OPTIONS)) return M4BadParam; */ //OPTIONS must have a parameter string if (!strcmp(com->method, RTSP_OPTIONS) && !sCtrl) return M4BadParam; //update sequence number sess->CSeq += 1; sess->NbPending += 1; if (!strcmp(com->method, RTSP_OPTIONS)) { sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, RTSP_VERSION); } else { rad = (sess->ConnectionType == SK_TYPE_TCP) ? "rtsp" : "rtspu"; if (sCtrl) { //if both server and service names are included in the control, just //use the control if (strstr(sCtrl, sess->Server) && strstr(sCtrl, sess->Service)) { sprintf(buffer, "%s %s %s\r\n", com->method, sCtrl, RTSP_VERSION); } //if service is specified in ctrl, do not rewrite it else if (strstr(sCtrl, sess->Service)) { sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sCtrl, RTSP_VERSION); } //otherwise rewrite full URL else { sprintf(buffer, "%s %s://%s/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Service, sCtrl, RTSP_VERSION); // sprintf(buffer, "%s %s://%s:%d/%s/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, sCtrl, RTSP_VERSION); } } else { sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, RTSP_VERSION); } } //Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY body = NULL; if (strcmp(com->method, RTSP_ANNOUNCE) && strcmp(com->method, RTSP_GET_PARAMETER) && strcmp(com->method, RTSP_SET_PARAMETER) ) { //this is an error, but don't say anything if (com->body) { body = com->body; com->body = NULL; } } result = NULL; e = RTSP_WriteCommand(sess, com, buffer, (unsigned char **) &result, &size); //restore body if needed if (body) com->body = body; if (e) goto exit; //send buffer e = RTSP_Send(sess, result, size); if (e) goto exit; if (sess->rtsp_log) fprintf(sess->rtsp_log, "\n/*RTSP Send Command*/\n\n%s\n", result); //update our state if (!strcmp(com->method, RTSP_RECORD)) sess->RTSP_State = RSM_WaitForControl; else if (!strcmp(com->method, RTSP_PLAY)) sess->RTSP_State = RSM_WaitForControl; else if (!strcmp(com->method, RTSP_PAUSE)) sess->RTSP_State = RSM_WaitForControl; else sess->RTSP_State = RSM_Waiting; //teardown invalidates the session most of the time, so we force the user to wait for the reply //as the reply may indicate a connection-closed strcpy(sess->RTSPLastRequest, com->method); exit: if (result) free(result); return e; }