/*process describe reply*/ void RP_ProcessDescribe(RTPSession *sess, RTSPCommand *com, M4Err e) { RTPStream *ch; ChannelDescribe *ch_desc; ch = NULL; ch_desc = com->user_data; if (e) goto exit; switch (sess->rtsp_rsp->ResponseCode) { //TODO handle all 3xx codes (redirections) case NC_RTSP_Multiple_Choice: e = ch_desc ? M4ChannelNotFound : M4URLNotFound; goto exit; case NC_RTSP_Not_Found: e = M4URLNotFound; goto exit; case NC_RTSP_OK: break; default: //we should have a basic error code mapping here e = M4ServiceError; goto exit; } ch = NULL; if (ch_desc) { ch = RP_FindChannel(sess->owner, ch_desc->channel, ch_desc->ES_ID, ch_desc->esd_url, 0); } else { NM_OnMessage(sess->owner->service, M4OK, "Connected"); } /*error on loading SDP is done internally*/ RP_LoadSDP(sess->owner, sess->rtsp_rsp->body, sess->rtsp_rsp->Content_Length, ch); if (!ch_desc) goto exit; if (!ch) { e = M4ChannelNotFound; goto exit; } e = RP_SetupChannel(ch, ch_desc); exit: if (e) { if (!ch_desc) { NM_OnConnect(sess->owner->service, NULL, e); } else if (ch) { RP_ConfirmChannelConnect(ch, e); } else { NM_OnConnect(sess->owner->service, ch_desc->channel, e); } } if (ch_desc) free(ch_desc); com->user_data = NULL; }
static GF_Err RP_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) { RTPStream *ch; RTPClient *priv = (RTPClient *)plug->priv; ch = RP_FindChannel(priv, channel, 0, NULL, 0); if (!ch) return GF_STREAM_NOT_FOUND; if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR; if (ch->status != RTP_Running) return GF_SERVICE_ERROR; /*this will trigger EOS at next fetch*/ ch->current_start = -1.0; return GF_OK; }
/*send describe*/ void RP_Describe(RTPSession *sess, char *esd_url, LPNETCHANNEL channel) { RTPStream *ch; ChannelDescribe *ch_desc; RTSPCommand *com; /*locate the channel by URL - if we have one, this means the channel is already described this happens when 2 ESD with URL use the same RTSP service - skip describe and send setup*/ if (esd_url || channel) { ch = RP_FindChannel(sess->owner, channel, 0, esd_url, 0); if (ch) { if (!ch->channel) ch->channel = channel; ch_desc = malloc(sizeof(ChannelDescribe)); ch_desc->esd_url = esd_url ? strdup(esd_url) : NULL; ch_desc->channel = channel; RP_SetupChannel(ch, ch_desc); if (esd_url) free(ch_desc->esd_url); free(ch_desc); return; } /*channel not found, send describe on service*/ } /*send describe*/ com = RTSP_NewCommand(); com->method = strdup(RTSP_DESCRIBE); if (channel || esd_url) { com->Accept = strdup("application/sdp"); com->ControlString = esd_url ? strdup(esd_url) : NULL; ch_desc = malloc(sizeof(ChannelDescribe)); ch_desc->esd_url = esd_url ? strdup(esd_url) : NULL; ch_desc->channel = channel; com->user_data = ch_desc; } else { //always accept both SDP and IOD com->Accept = strdup("application/sdp, application/mpeg4-iod"); // com->Accept = strdup("application/sdp"); } com->Bandwidth = sess->owner->bandwidth; MX_P(sess->owner->mx); ChainAddEntry(sess->rtsp_commands, com); MX_V(sess->owner->mx); }
static GF_Err RP_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) { RTPStream *ch; RTPClient *priv = (RTPClient *)plug->priv; GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Disconnecting channel @%08x\n", channel)); ch = RP_FindChannel(priv, channel, 0, NULL, 0); if (!ch) return GF_STREAM_NOT_FOUND; gf_mx_p(priv->mx); /*disconnect stream BUT DO NOT DELETE IT since we don't store SDP*/ ch->flags &= ~RTP_CONNECTED; ch->channel = NULL; gf_mx_v(priv->mx); gf_term_on_disconnect(priv->service, channel, GF_OK); return GF_OK; }
void RP_DeleteStream(RTPStream *ch) { if (ch->rtsp) { if (ch->status == RTP_Running) { RP_Teardown(ch->rtsp, ch); ch->status = RTP_Disconnected; } RP_RemoveStream(ch->owner, ch); } else { RP_FindChannel(ch->owner, ch->channel, 0, NULL, 1); } if (ch->depacketizer) gf_rtp_depacketizer_del(ch->depacketizer); if (ch->rtp_ch) gf_rtp_del(ch->rtp_ch); if (ch->control) gf_free(ch->control); if (ch->session_id) gf_free(ch->session_id); gf_free(ch); }
/*filter describe commands in case of ESD URLs*/ Bool RP_PreprocessDescribe(RTPSession *sess, RTSPCommand *com) { RTPStream *ch; ChannelDescribe *ch_desc; /*not a channel describe*/ if (!com->user_data) { NM_OnMessage(sess->owner->service, M4OK, "Connecting..."); return 1; } ch_desc = (ChannelDescribe *)com->user_data; ch = RP_FindChannel(sess->owner, NULL, ch_desc->ES_ID, ch_desc->esd_url, 0); if (!ch) return 1; /*channel has been described already, skip describe and send setup directly*/ RP_SetupChannel(ch, ch_desc); if (ch_desc->esd_url) free(ch_desc->esd_url); free(ch_desc); return 0; }
static GF_Err RP_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) { char *data; RTPStream *ch; RTPClient *priv = (RTPClient *)plug->priv; ch = RP_FindChannel(priv, channel, 0, NULL, 0); if (!ch) return GF_STREAM_NOT_FOUND; if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_SERVICE_ERROR; if (ch->status != RTP_Running) return GF_SERVICE_ERROR; data = strstr(ch->control, ";base64"); if (!data) return GF_SERVICE_ERROR; if (ch->current_start>=0) { *sl_compressed = 0; memset(out_sl_hdr, 0, sizeof(GF_SLHeader)); out_sl_hdr->accessUnitEndFlag = 1; out_sl_hdr->accessUnitStartFlag = 1; out_sl_hdr->compositionTimeStamp = (u64) (ch->current_start * ch->ts_res); out_sl_hdr->compositionTimeStampFlag = 1; out_sl_hdr->randomAccessPointFlag = 1; *out_reception_status = GF_OK; *is_new_data = (ch->flags & GF_RTP_NEW_AU) ? 1 : 0; /*decode data*/ data = strstr(data, ","); data += 1; *out_data_size = gf_base64_decode(data, strlen(data), ch->buffer, RTP_BUFFER_SIZE); /*FIXME - currently only support for empty SL header*/ *out_data_ptr = ch->buffer; ch->flags &= ~GF_RTP_NEW_AU; } else { *out_data_ptr = NULL; *out_data_size = 0; *out_reception_status = GF_EOS; ch->flags |= RTP_EOS; } return GF_OK; }
static GF_Err RP_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) { RTPStream *ch; RTPClient *priv = (RTPClient *)plug->priv; if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { u32 i; for (i=0; i<gf_list_count(priv->channels); i++) { ch = gf_list_get(priv->channels, i); if (ch->depacketizer->sl_map.StreamType==GF_STREAM_AUDIO) return GF_OK; } return GF_NOT_SUPPORTED; } if (com->command_type==GF_NET_SERVICE_MIGRATION_INFO) { RP_SaveSessionState(priv); priv->session_migration=1; if (priv->session_state_data) { com->migrate.data = priv->session_state_data; com->migrate.data_len = strlen(priv->session_state_data); return GF_OK; } return GF_NOT_SUPPORTED; } /*ignore commands other than channels one*/ if (!com->base.on_channel) { if (com->command_type==GF_NET_IS_CACHABLE) return GF_OK; return GF_NOT_SUPPORTED; } ch = RP_FindChannel(priv, com->base.on_channel, 0, NULL, 0); if (!ch) return GF_STREAM_NOT_FOUND; switch (com->command_type) { case GF_NET_CHAN_SET_PULL: if (ch->rtp_ch || ch->rtsp || !ch->control) return GF_NOT_SUPPORTED; /*embedded channels work in pull mode*/ if (strstr(ch->control, "data:application/")) return GF_OK; return GF_NOT_SUPPORTED; case GF_NET_CHAN_INTERACTIVE: /*looks like pure RTP / multicast etc, not interactive*/ if (!ch->control) return GF_NOT_SUPPORTED; /*emulated broadcast mode*/ else if (ch->flags & RTP_FORCE_BROADCAST) return GF_NOT_SUPPORTED; /*regular rtsp mode*/ else if (ch->flags & RTP_HAS_RANGE) return GF_OK; /*embedded data*/ else if (strstr(ch->control, "application")) return GF_OK; return GF_NOT_SUPPORTED; case GF_NET_CHAN_BUFFER: if (!(ch->rtp_ch || ch->rtsp || !ch->control)) { com->buffer.max = com->buffer.min = 0; } else { const char *opt; /*amount of buffering in ms*/ opt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "BufferLength"); com->buffer.max = opt ? atoi(opt) : 1000; /*rebuffer low limit in ms - if the amount of buffering is less than this, rebuffering will never occur*/ opt = gf_modules_get_option((GF_BaseInterface *)plug, "Network", "RebufferLength"); if (opt) com->buffer.min = atoi(opt); else com->buffer.min = 500; if (com->buffer.min >= com->buffer.max ) com->buffer.min = 0; } return GF_OK; case GF_NET_CHAN_DURATION: com->duration.duration = (ch->flags & RTP_HAS_RANGE) ? (ch->range_end - ch->range_start) : 0; return GF_OK; /*RTP channel config is done upon connection, once the complete SL mapping is known however we must store some info not carried in SDP*/ case GF_NET_CHAN_CONFIG: if (com->cfg.frame_duration) ch->depacketizer->sl_hdr.au_duration = com->cfg.frame_duration; ch->ts_res = com->cfg.sl_config.timestampResolution; return GF_OK; case GF_NET_CHAN_PLAY: GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Processing play on channel @%08x - %s\n", ch, ch->rtsp ? "RTSP control" : "No control (RTP)" )); /*is this RTSP or direct RTP?*/ ch->flags &= ~RTP_EOS; if (ch->rtsp) { if (ch->status==RTP_SessionResume) { const char *opt = gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationPause"); if (opt && !strcmp(opt, "yes")) { ch->status = RTP_Connected; com->play.start_range = ch->current_start; } else { ch->status = RTP_Running; return GF_OK; } } RP_UserCommand(ch->rtsp, ch, com); } else { ch->status = RTP_Running; if (ch->rtp_ch) { /*technically we shouldn't attempt to synchronize streams based on RTP, we should use RTCP/ However it may happen that the RTCP traffic is absent ...*/ ch->check_rtp_time = RTP_SET_TIME_RTP; ch->rtcp_init = 0; gf_mx_p(priv->mx); RP_InitStream(ch, (ch->flags & RTP_CONNECTED) ? 1 : 0); gf_mx_v(priv->mx); gf_rtp_set_info_rtp(ch->rtp_ch, 0, 0, 0); } else { /*direct channel, store current start*/ ch->current_start = com->play.start_range; ch->flags |= GF_RTP_NEW_AU; gf_rtp_depacketizer_reset(ch->depacketizer, 0); } } return GF_OK; case GF_NET_CHAN_STOP: /*is this RTSP or direct RTP?*/ if (ch->rtsp) { if (! ch->owner->session_migration) { RP_UserCommand(ch->rtsp, ch, com); } } else { ch->status = RTP_Connected; ch->owner->last_ntp = 0; } ch->rtcp_init = 0; return GF_OK; case GF_NET_CHAN_SET_SPEED: case GF_NET_CHAN_PAUSE: case GF_NET_CHAN_RESUME: assert(ch->rtsp); RP_UserCommand(ch->rtsp, ch, com); return GF_OK; case GF_NET_CHAN_GET_DSI: if (ch->depacketizer && ch->depacketizer->sl_map.configSize) { com->get_dsi.dsi_len = ch->depacketizer->sl_map.configSize; com->get_dsi.dsi = (char*)gf_malloc(sizeof(char)*com->get_dsi.dsi_len); memcpy(com->get_dsi.dsi, ch->depacketizer->sl_map.config, sizeof(char)*com->get_dsi.dsi_len); } else { com->get_dsi.dsi = NULL; com->get_dsi.dsi_len = 0; } return GF_OK; case GF_NET_GET_STATS: memset(&com->net_stats, 0, sizeof(GF_NetComStats)); if (ch->rtp_ch) { u32 time; Float bps; com->net_stats.pck_loss_percentage = gf_rtp_get_loss(ch->rtp_ch); if (ch->flags & RTP_INTERLEAVED) { com->net_stats.multiplex_port = gf_rtsp_get_session_port(ch->rtsp->session); com->net_stats.port = gf_rtp_get_low_interleave_id(ch->rtp_ch); com->net_stats.ctrl_port = gf_rtp_get_hight_interleave_id(ch->rtp_ch); } else { com->net_stats.multiplex_port = 0; gf_rtp_get_ports(ch->rtp_ch, &com->net_stats.port, &com->net_stats.ctrl_port); } if (ch->stat_stop_time) { time = ch->stat_stop_time - ch->stat_start_time; } else { time = gf_sys_clock() - ch->stat_start_time; } bps = 8.0f * ch->rtp_bytes; bps *= 1000; bps /= time; com->net_stats.bw_down = (u32) bps; bps = 8.0f * ch->rtcp_bytes; bps *= 1000; bps /= time; com->net_stats.ctrl_bw_down = (u32) bps; bps = 8.0f * gf_rtp_get_tcp_bytes_sent(ch->rtp_ch); bps *= 1000; bps /= time; com->net_stats.ctrl_bw_up = (u32) bps; } return GF_OK; } return GF_NOT_SUPPORTED; }
static GF_Err RP_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) { u32 ESID; RTPStream *ch; RTSPSession *sess; char *es_url; RTPClient *priv = (RTPClient *)plug->priv; if (upstream) return GF_NOT_SUPPORTED; GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Connecting channel @%08x - %s\n", channel, url)); ch = RP_FindChannel(priv, channel, 0, (char *) url, 0); if (ch && (ch->status != RTP_Disconnected) ) return GF_SERVICE_ERROR; es_url = NULL; sess = NULL; if (strstr(url, "ES_ID=")) { sscanf(url, "ES_ID=%ud", &ESID); /*first case: simple URL (same namespace)*/ ch = RP_FindChannel(priv, NULL, ESID, NULL, 0); /*this should not happen, the sdp must describe all streams in the service*/ if (!ch) return GF_STREAM_NOT_FOUND; /*assign app channel*/ ch->channel = channel; sess = ch->rtsp; } /*rtsp url - create a session if needed*/ else if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8)) { sess = RP_CheckSession(priv, (char *) url); if (!sess) sess = RP_NewSession(priv, (char *) url); es_url = (char *) url; } /*data: url*/ else if (strstr(url, "data:application/mpeg4-od-au;base64") || strstr(url, "data:application/mpeg4-bifs-au;base64") || strstr(url, "data:application/mpeg4-es-au;base64") ) { GF_SAFEALLOC(ch, RTPStream); ch->control = gf_strdup(url); ch->owner = priv; ch->channel = channel; ch->status = RTP_Connected; /*register*/ gf_list_add(priv->channels, ch); RP_ConfirmChannelConnect(ch, GF_OK); return GF_OK; } /*session migration resume - don't send data to the server*/ if (ch->status==RTP_SessionResume) { ch->flags |= RTP_CONNECTED; RP_InitStream(ch, 0); RP_ConfirmChannelConnect(ch, GF_OK); return GF_OK; } /*send a DESCRIBE (not a setup) on the channel. If the channel is already created then the describe is skipped and a SETUP is sent directly, otherwise the channel is first described then setup*/ if (sess) RP_Describe(sess, es_url, channel); /*otherwise confirm channel connection*/ else RP_ConfirmChannelConnect(ch, GF_OK); return GF_OK; }
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_term_get_service_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_term_get_service_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; }
void RP_ProcessUserCommand(RTPSession *sess, RTSPCommand *com, M4Err e) { ChannelControl *ch_ctrl; RTPStream *ch, *agg_ch; u32 i; RTP_Info *info; ch_ctrl = com->user_data; ch = ch_ctrl->ch; if (!channel_is_valid(sess->owner, ch)) { free(ch_ctrl); com->user_data = NULL; return; } assert(ch->channel==ch_ctrl->com.base.on_channel); /*some consistency checking: on interleaved sessions, some servers do NOT reply to the teardown. If our command is STOP just skip the error notif*/ if (e) { if (!strcmp(com->method, RTSP_TEARDOWN)) { goto process_reply; } else { goto err_exit; } } switch (sess->rtsp_rsp->ResponseCode) { //handle all 3xx codes (redirections) case NC_RTSP_Method_Not_Allowed: e = M4NotSupported; goto err_exit; case NC_RTSP_OK: break; default: //we should have a basic error code mapping here e = M4ServiceError; goto err_exit; } process_reply: NM_OnCommand(sess->owner->service, &ch_ctrl->com, M4OK); if ( (ch_ctrl->com.command_type==CHAN_PLAY) || (ch_ctrl->com.command_type==CHAN_SET_SPEED) || (ch_ctrl->com.command_type==CHAN_RESUME) ) { //auto-detect any aggregated control if not done yet if (ChainGetCount(sess->rtsp_rsp->RTP_Infos) > 1) { sess->has_aggregated_control = 1; } //process all RTP infos for (i=0;i<ChainGetCount(sess->rtsp_rsp->RTP_Infos); i++) { info = ChainGetEntry(sess->rtsp_rsp->RTP_Infos, i); agg_ch = RP_FindChannel(sess->owner, NULL, 0, info->url, 0); if (!agg_ch || (agg_ch->rtsp != sess) ) continue; /*if play/seeking we must send update RTP/NPT link*/ if (ch_ctrl->com.command_type != CHAN_RESUME) { agg_ch->check_rtp_time = 1; } /*this is used to discard RTP packets re-sent on resume*/ else { agg_ch->check_rtp_time = 2; } /* reset the buffers */ RP_InitStream(agg_ch, 1); RTP_SetInfo(agg_ch->rtp_ch, info->seq, info->rtp_time, info->ssrc); agg_ch->status = RTP_Running; /*skip next play command on this channel if aggregated control*/ if (ch!=agg_ch && ch->rtsp->has_aggregated_control) agg_ch->skip_next_command = 1; if (RTP_IsInterleaved(agg_ch->rtp_ch)) { RTSP_RegisterTCPChannel(sess->session, agg_ch, RTP_GetLowInterleavedID(agg_ch->rtp_ch), RTP_GetHighInterleavedID(agg_ch->rtp_ch)); } } /*no rtp info (just in case), no time mapped - set to 0 and specify we're not interactive*/ if (!i) { ch->current_start = 0.0; ch->check_rtp_time = 1; RP_InitStream(ch, 1); ch->status = RTP_Running; if (RTP_IsInterleaved(ch->rtp_ch)) { RTSP_RegisterTCPChannel(sess->session, ch, RTP_GetLowInterleavedID(ch->rtp_ch), RTP_GetHighInterleavedID(ch->rtp_ch)); } } ch->skip_next_command = 0; } else if (ch_ctrl->com.command_type == CHAN_PAUSE) { SkipCommandOnSession(ch); ch->skip_next_command = 0; } else if (ch_ctrl->com.command_type == CHAN_STOP) { assert(0); } free(ch_ctrl); com->user_data = NULL; return; err_exit: ch->status = RTP_Disconnected; NM_OnCommand(sess->owner->service, &ch_ctrl->com, e); RTSP_ResetAggregation(ch->rtsp->session); ch->check_rtp_time = 0; free(ch_ctrl); com->user_data = NULL; }