Example #1
0
GF_Err RP_InitStream(RTPStream *ch, Bool ResetOnly)
{
	gf_rtp_depacketizer_reset(ch->depacketizer, !ResetOnly);

	if (!ResetOnly) {
		const char *ip_ifce = NULL;
		u32 reorder_size = 0;
		if (!ch->owner->transport_mode) {
			const char *sOpt = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Streaming", "ReorderSize");
			if (sOpt) reorder_size = atoi(sOpt);
			else reorder_size = 10;


			ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "DefaultMCastInterface");
			if (!ip_ifce) {
				const char *mob_on = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "MobileIPEnabled");
				if (mob_on && !strcmp(mob_on, "yes")) {
					ip_ifce = gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(ch->owner->service), "Network", "MobileIP");
					ch->flags |= RTP_MOBILEIP;
				}

			}
		}
		return gf_rtp_initialize(ch->rtp_ch, RTP_BUFFER_SIZE, 0, 0, reorder_size, 200, (char *)ip_ifce);
	}
	//just reset the sockets
	gf_rtp_reset_buffers(ch->rtp_ch);
	return GF_OK;
}
Example #2
0
RTSPSession *RP_NewSession(RTPClient *rtp, char *session_control)
{
	char *szCtrl, *szExt;
	RTSPSession *tmp;
	GF_RTSPSession *rtsp;

	if (!session_control) return NULL;

	/*little fix: some servers don't understand DESCRIBE URL/trackID=, so remove the trackID...*/
	szCtrl = gf_strdup(session_control);
	szExt = szCtrl ? strrchr(szCtrl, '.') : NULL;
	if (szExt) {
		szExt = strchr(szExt, '/');
		if (szExt) {
			if (!strnicmp(szExt+1, "trackID=", 8) || !strnicmp(szExt+1, "ESID=", 5) || !strnicmp(szExt+1, "ES_ID=", 6)) szExt[0] = 0;
		}
	}

	rtsp = gf_rtsp_session_new(szCtrl, rtp->default_port);
	gf_free(szCtrl);

	if (!rtsp) return NULL;

	GF_SAFEALLOC(tmp, RTSPSession);
	tmp->owner = rtp;
	tmp->session = rtsp;


	szCtrl = (char *)gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIPEnabled");
	if (szCtrl && !strcmp(szCtrl, "yes")) {
		char *ip = (char *)gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIP");
		gf_rtsp_set_mobile_ip(rtsp, ip);
	}

	if (rtp->transport_mode) {
		gf_rtsp_set_buffer_size(rtsp, RTSP_TCP_BUFFER_SIZE);
	} else {
		gf_rtsp_set_buffer_size(rtsp, RTSP_BUFFER_SIZE);
	}
	tmp->rtsp_commands = gf_list_new();
	tmp->rtsp_rsp = gf_rtsp_response_new();

	gf_list_add(rtp->sessions, tmp);

	return tmp;
}
Example #3
0
void SDP_NetIO(void *cbk, GF_NETIO_Parameter *param)
{
	GF_Err e;
	RTPClient *rtp = (RTPClient *)cbk;
	SDPFetch *sdp = rtp->sdp_temp;

	gf_service_download_update_stats(rtp->dnload);

	e = param->error;
	switch (param->msg_type) {
	case GF_NETIO_GET_METHOD:
		if (sdp->original_url)
			param->name = "POST";
		return;
	case GF_NETIO_GET_CONTENT:
		if (sdp->original_url) {
			char szBody[4096], *opt;
			opt = (char *) gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Network", "MobileIP");
			sprintf(szBody, "ipadd\n%s\n\nurl\n%s\n\n", opt, sdp->original_url);
			param->data = szBody;
			param->size = (u32) strlen(szBody);
		}
		return;
	case GF_NETIO_DATA_TRANSFERED:
		if (sdp->original_url) {
			u32 sdp_size;
			e = gf_dm_sess_get_stats(rtp->dnload, NULL, NULL, &sdp_size, NULL, NULL, NULL);
			if (sdp_size) {
				const char *szFile = gf_dm_sess_get_cache_name(rtp->dnload);
				if (!szFile) {
					e = GF_SERVICE_ERROR;
				} else {
					e = GF_OK;
					RP_SDPFromFile(rtp, (char *) szFile, sdp->chan);
					gf_free(sdp->remote_url);
					if (sdp->original_url) gf_free(sdp->original_url);
					gf_free(sdp);
					rtp->sdp_temp = NULL;
					return;
				}
			}
		}
		break;
	default:
		if (e == GF_OK) return;
	}

	if (sdp->original_url) {
		char *url = sdp->original_url;
		gf_free(sdp->remote_url);
		gf_free(sdp);
		rtp->sdp_temp = NULL;
		RP_SendMessage(rtp->service, e, "Error fetching session state - restarting");
		RP_ConnectServiceEx(gf_service_get_interface(rtp->service), rtp->service, url, GF_TRUE);
		gf_free(url);
		return;
	}

	/*error*/
	if (sdp->chan) {
		gf_service_connect_ack(rtp->service, sdp->chan->channel, e);
	} else {
		gf_service_connect_ack(rtp->service, NULL, e);
		rtp->sdp_temp = NULL;
	}
	gf_free(sdp->remote_url);
	if (sdp->original_url) gf_free(sdp->original_url);
	gf_free(sdp);
	rtp->sdp_temp = NULL;
}
Example #4
0
void isor_declare_objects(ISOMReader *read)
{
	GF_ObjectDescriptor *od;
	GF_ESD *esd;
	const char *tag;
	u32 i, count, ocr_es_id, tlen, base_track, j, track_id;
	Bool highest_stream;

	ocr_es_id = 0;

	/*TODO check for alternate tracks*/
	count = gf_isom_get_track_count(read->mov);
	for (i=0; i<count; i++) {
		if (!gf_isom_is_track_enabled(read->mov, i+1))
			continue;

		switch (gf_isom_get_media_type(read->mov, i+1)) {
		case GF_ISOM_MEDIA_AUDIO:
		case GF_ISOM_MEDIA_VISUAL:
		case GF_ISOM_MEDIA_TEXT:
		case GF_ISOM_MEDIA_SUBT:
		case GF_ISOM_MEDIA_SCENE:
		case GF_ISOM_MEDIA_SUBPIC:
			break;
		default:
			continue;
		}
		//some subtypes are not declared as readable objects
		switch (gf_isom_get_media_subtype(read->mov, i+1, 1)) {
		case GF_ISOM_SUBTYPE_HVT1:
			continue;
		default:
			break;
		}
		/*we declare only the highest video track (i.e the track we play)*/
		highest_stream = GF_TRUE;
		track_id = gf_isom_get_track_id(read->mov, i+1);
		if (read->play_only_track_id && (read->play_only_track_id != track_id)) continue;

		for (j = 0; j < count; j++) {
			if (gf_isom_has_track_reference(read->mov, j+1, GF_ISOM_REF_SCAL, track_id) > 0) {
				highest_stream = GF_FALSE;
				break;
			}
		}
		if ((gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) && !highest_stream)
			continue;
		esd = gf_media_map_esd(read->mov, i+1);
		if (esd) {
			gf_isom_get_reference(read->mov, i+1, GF_ISOM_REF_BASE, 1, &base_track);
			esd->has_ref_base = base_track ? GF_TRUE : GF_FALSE;

			if (!esd->langDesc) {
				esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG);
				gf_isom_get_media_language(read->mov, i+1, &esd->langDesc->full_lang_code);
			}

			od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
			od->service_ifce = read->input;
			od->objectDescriptorID = 0;
			if (!ocr_es_id) ocr_es_id = esd->ESID;
			esd->OCRESID = ocr_es_id;
			gf_list_add(od->ESDescriptors, esd);
			if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
				send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL);
			} else {
				gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE);
			}
		}
	}
	/*if cover art, extract it in cache*/
	if (gf_isom_apple_get_tag(read->mov, GF_ISOM_ITUNE_COVER_ART, &tag, &tlen)==GF_OK) {
		const char *cdir = gf_modules_get_option((GF_BaseInterface *)gf_service_get_interface(read->service), "General", "CacheDirectory");
		if (cdir) {
			char szName[GF_MAX_PATH];
			const char *sep;
			FILE *t;
			sep = strrchr(gf_isom_get_filename(read->mov), '\\');
			if (!sep) sep = strrchr(gf_isom_get_filename(read->mov), '/');
			if (!sep) sep = gf_isom_get_filename(read->mov);

			if ((cdir[strlen(cdir)-1] != '\\') && (cdir[strlen(cdir)-1] != '/')) {
				sprintf(szName, "%s/%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg");
			} else {
				sprintf(szName, "%s%s_cover.%s", cdir, sep, (tlen & 0x80000000) ? "png" : "jpg");
			}

			t = gf_fopen(szName, "wb");

			if (t) {
				Bool isom_contains_video = GF_FALSE;

				/*write cover data*/
				assert(!(tlen & 0x80000000));
				gf_fwrite(tag, tlen & 0x7FFFFFFF, 1, t);
				gf_fclose(t);

				/*don't display cover art when video is present*/
				for (i=0; i<gf_isom_get_track_count(read->mov); i++) {
					if (!gf_isom_is_track_enabled(read->mov, i+1))
						continue;
					if (gf_isom_get_media_type(read->mov, i+1) == GF_ISOM_MEDIA_VISUAL) {
						isom_contains_video = GF_TRUE;
						break;
					}
				}

				if (!isom_contains_video) {
					od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG);
					od->service_ifce = read->input;
					od->objectDescriptorID = GF_MEDIA_EXTERNAL_ID;
					od->URLString = gf_strdup(szName);
					if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
						send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, (GF_Descriptor*)od, NULL);
					} else {
						gf_service_declare_media(read->service, (GF_Descriptor*)od, GF_TRUE);
					}
				}
			}
		}
	}
	if (read->input->query_proxy && read->input->proxy_udta && read->input->proxy_type) {
		send_proxy_command(read, GF_FALSE, GF_TRUE, GF_OK, NULL, NULL);
	} else {
		gf_service_declare_media(read->service, NULL, GF_FALSE);
	}
}
Example #5
0
RTPStream *RP_NewStream(RTPClient *rtp, GF_SDPMedia *media, GF_SDPInfo *sdp, RTPStream *input_stream)
{
	GF_RTSPRange *range;
	RTPStream *tmp;
	GF_RTPMap *map;
	u32 i, ESID, ODID, ssrc, rtp_seq, rtp_time;
	Bool force_bcast = 0;
	Double Start, End;
	Float CurrentTime;
	u16 rvc_predef = 0;
	char *rvc_config_att = NULL;
	u32 s_port_first, s_port_last;
	GF_X_Attribute *att;
	Bool is_migration = 0;
	char *ctrl;
	GF_SDPConnection *conn;
	GF_RTSPTransport trans;
	u32 mid, prev_stream, base_stream;

	//extract all relevant info from the GF_SDPMedia
	Start = 0.0;
	End = -1.0;
	CurrentTime = 0.0f;
	ODID = 0;
	ESID = 0;
	ctrl = NULL;
	range = NULL;
	s_port_first = s_port_last = 0;
	ssrc = rtp_seq = rtp_time = 0;
	mid = prev_stream = base_stream = 0;
	i=0;
	while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &i))) {
		if (!stricmp(att->Name, "control")) ctrl = att->Value;
		else if (!stricmp(att->Name, "gpac-broadcast")) force_bcast = 1;
		else if (!stricmp(att->Name, "mpeg4-esid") && att->Value) ESID = atoi(att->Value);
		else if (!stricmp(att->Name, "mpeg4-odid") && att->Value) ODID = atoi(att->Value);
		else if (!stricmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value);
		else if (!stricmp(att->Name, "x-stream-state") ) {
			sscanf(att->Value, "server-port=%u-%u;ssrc=%X;npt=%g;seq=%u;rtptime=%u",
			       &s_port_first, &s_port_last, &ssrc, &CurrentTime, &rtp_seq, &rtp_time);
			is_migration = 1;
		}
		else if (!stricmp(att->Name, "x-server-port") ) {
			sscanf(att->Value, "%u-%u", &s_port_first, &s_port_last);
		} else if (!stricmp(att->Name, "rvc-config-predef")) {
			rvc_predef = atoi(att->Value);
		} else if (!stricmp(att->Name, "rvc-config")) {
			rvc_config_att = att->Value;
		} else if (!stricmp(att->Name, "mid")) {
			sscanf(att->Value, "L%d", &mid);
		} else if (!stricmp(att->Name, "depend")) {
			char buf[3000];
			memset(buf, 0, 3000);
			sscanf(att->Value, "%*d lay L%d %*s %s", &base_stream, buf);
			if (!strlen(buf))
				sscanf(att->Value, "%*d lay %s", buf);
			sscanf(buf, "L%d", &prev_stream);
		}
	}

	if (range) {
		Start = range->start;
		End = range->end;
		gf_rtsp_range_del(range);
	}

	/*check connection*/
	conn = sdp->c_connection;
	if (conn && (!conn->host || !strcmp(conn->host, "0.0.0.0"))) conn = NULL;

	if (!conn) conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0);
	if (conn && (!conn->host || !strcmp(conn->host, "0.0.0.0"))) conn = NULL;

	if (!conn) {
		/*RTSP RFC recommends an empty "c= " line but some server don't send it. Use session info (o=)*/
		if (!sdp->o_net_type || !sdp->o_add_type || strcmp(sdp->o_net_type, "IN")) return NULL;
		if (strcmp(sdp->o_add_type, "IP4") && strcmp(sdp->o_add_type, "IP6")) return NULL;
	} else {
		if (strcmp(conn->net_type, "IN")) return NULL;
		if (strcmp(conn->add_type, "IP4") && strcmp(conn->add_type, "IP6")) return NULL;
	}
	/*do we support transport*/
	if (strcmp(media->Profile, "RTP/AVP") && strcmp(media->Profile, "RTP/AVP/TCP")
	        && strcmp(media->Profile, "RTP/SAVP") && strcmp(media->Profile, "RTP/SAVP/TCP")
	   ) return NULL;

	/*check RTP map. For now we only support 1 RTPMap*/
	if (media->fmt_list || (gf_list_count(media->RTPMaps) > 1)) return NULL;

	/*check payload type*/
	map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0);

	/*this is an ESD-URL setup, we likely have namespace conflicts so overwrite given ES_ID
	by the app one (client side), but keep control (server side) if provided*/
	if (input_stream) {
		ESID = input_stream->ES_ID;
		if (!ctrl) ctrl = input_stream->control;
		tmp = input_stream;
	} else {
		tmp = RP_FindChannel(rtp, NULL, ESID, NULL, 0);
		if (tmp) return NULL;

		GF_SAFEALLOC(tmp, RTPStream);
		tmp->owner = rtp;
	}

	/*create an RTP channel*/
	tmp->rtp_ch = gf_rtp_new();
	if (ctrl) tmp->control = gf_strdup(ctrl);
	tmp->ES_ID = ESID;
	tmp->OD_ID = ODID;
	tmp->mid = mid;
	tmp->prev_stream = prev_stream;
	tmp->base_stream = base_stream;

	memset(&trans, 0, sizeof(GF_RTSPTransport));
	trans.Profile = media->Profile;
	trans.source = conn ? conn->host : sdp->o_address;
	trans.IsUnicast = gf_sk_is_multicast_address(trans.source) ? 0 : 1;
	if (!trans.IsUnicast) {
		trans.port_first = media->PortNumber;
		trans.port_last = media->PortNumber + 1;
		trans.TTL = conn ? conn->TTL : 0;
	} else {
		trans.client_port_first = media->PortNumber;
		trans.client_port_last = media->PortNumber + 1;
		trans.port_first = s_port_first ? s_port_first : trans.client_port_first;
		trans.port_last = s_port_last ? s_port_last : trans.client_port_last;
	}

	if (gf_rtp_setup_transport(tmp->rtp_ch, &trans, NULL) != GF_OK) {
		RP_DeleteStream(tmp);
		return NULL;
	}
	/*setup depacketizer*/
	tmp->depacketizer = gf_rtp_depacketizer_new(media, rtp_sl_packet_cbk, tmp);
	if (!tmp->depacketizer) {
		RP_DeleteStream(tmp);
		return NULL;
	}
	/*setup channel*/
	gf_rtp_setup_payload(tmp->rtp_ch, map);

//	tmp->status = NM_Disconnected;

	ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Streaming", "DisableRTCP");
	if (!ctrl || stricmp(ctrl, "yes")) tmp->flags |= RTP_ENABLE_RTCP;

	/*setup NAT keep-alive*/
	ctrl = (char *) gf_modules_get_option((GF_BaseInterface *) gf_service_get_interface(rtp->service), "Streaming", "NATKeepAlive");
	if (ctrl) gf_rtp_enable_nat_keepalive(tmp->rtp_ch, atoi(ctrl));

	tmp->range_start = Start;
	tmp->range_end = End;
	if (End != -1.0) tmp->flags |= RTP_HAS_RANGE;

	if (force_bcast) tmp->flags |= RTP_FORCE_BROADCAST;

	if (is_migration) {
		tmp->current_start = (Double) CurrentTime;
		tmp->check_rtp_time = RTP_SET_TIME_RTP;
		gf_rtp_set_info_rtp(tmp->rtp_ch, rtp_seq, rtp_time, ssrc);
		tmp->status = RTP_SessionResume;
	}

	if (rvc_predef) {
		tmp->depacketizer->sl_map.rvc_predef = rvc_predef ;
	} else if (rvc_config_att) {
		char *rvc_data=NULL;
		u32 rvc_size;
		Bool is_gz = 0;
		if (!strncmp(rvc_config_att, "data:application/rvc-config+xml", 32) && strstr(rvc_config_att, "base64") ) {
			char *data = strchr(rvc_config_att, ',');
			if (data) {
				rvc_size = (u32) strlen(data) * 3 / 4 + 1;
				rvc_data = gf_malloc(sizeof(char) * rvc_size);
				rvc_size = gf_base64_decode(data, (u32) strlen(data), rvc_data, rvc_size);
				rvc_data[rvc_size] = 0;
			}
			if (!strncmp(rvc_config_att, "data:application/rvc-config+xml+gz", 35)) is_gz = 1;
		} else if (!strnicmp(rvc_config_att, "http://", 7) || !strnicmp(rvc_config_att, "https://", 8) ) {
			char *mime;
			if (gf_dm_get_file_memory(rvc_config_att, &rvc_data, &rvc_size, &mime) == GF_OK) {
				if (mime && strstr(mime, "+gz")) is_gz = 1;
				if (mime) gf_free(mime);
			}
		}
		if (rvc_data) {
			if (is_gz) {
#ifdef GPAC_DISABLE_ZLIB
				fprintf(stderr, "Error: no zlib support - RVC not supported in RTP\n");
				return NULL;
#endif
				gf_gz_decompress_payload(rvc_data, rvc_size, &tmp->depacketizer->sl_map.rvc_config, &tmp->depacketizer->sl_map.rvc_config_size);
				gf_free(rvc_data);
			} else {
				tmp->depacketizer->sl_map.rvc_config = rvc_data;
				tmp->depacketizer->sl_map.rvc_config_size = rvc_size;
			}
		}
	}

	return tmp;
}
Example #6
0
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_service_connect_ack(sess->owner->service, NULL, e);
				return;
			}

			RP_RemoveCommand(sess);
			gf_rtsp_command_del(com);
			sess->flags &= ~RTSP_WAIT_REPLY;
			sess->command_time = 0;
		} else {
			u32 time_out = sess->owner->time_out;
			/*evaluate timeout*/
			time = gf_sys_clock() - sess->command_time;

			if (!strcmp(com->method, GF_RTSP_DESCRIBE) && (time_out < 10000) ) time_out = 10000;
			/*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*/
			else if (!strcmp(com->method, GF_RTSP_TEARDOWN) && (time>=500) ) time = time_out;

			//signal what's going on
			if (time >= time_out) {
				if (!strcmp(com->method, GF_RTSP_TEARDOWN)) {
					gf_rtsp_session_reset(sess->session, 1);
				} else {
					GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] Request Timeout for command %s after %d ms\n", com->method, time));
				}

				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_service_get_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;
	}
}