GF_Err RP_AddStream(RTPClient *rtp, RTPStream *stream, char *session_control) { Bool has_aggregated_control; char *service_name, *ctrl; RTSPSession *in_session = RP_CheckSession(rtp, session_control); has_aggregated_control = 0; if (session_control) { //if (!strcmp(session_control, "*")) session_control = NULL; if (session_control) has_aggregated_control = 1; } /*regular setup in an established session (RTSP DESCRIBE)*/ if (in_session) { in_session->flags |= RTSP_AGG_CONTROL; stream->rtsp = in_session; gf_list_add(rtp->channels, stream); return GF_OK; } /*setup through SDP with control - assume this is RTSP and try to create a session*/ if (stream->control) { /*stream control is relative to main session*/ if (strnicmp(stream->control, "rtsp://", 7) && strnicmp(stream->control, "rtspu://", 7)) { /*locate session by control - if no control was provided for the session, use default session*/ if (!in_session) in_session = RP_CheckSession(rtp, session_control ? session_control : "*"); /*none found, try to create one*/ if (!in_session) in_session = RP_NewSession(rtp, session_control); /*cannot add an RTSP session for this channel, check if multicast*/ // if (!in_session && gf_rtp_is_unicast(stream->rtp_ch) ) return GF_SERVICE_ERROR; } /*stream control is absolute*/ else { in_session = RP_CheckSession(rtp, stream->control); if (!in_session) in_session = RP_CheckSession(rtp, session_control); if (!in_session) { if (session_control && strstr(stream->control, session_control)) in_session = RP_NewSession(rtp, session_control); else in_session = RP_NewSession(rtp, stream->control); if (!in_session) return GF_SERVICE_ERROR; } /*remove session control part from channel control*/ service_name = gf_rtsp_get_service_name(in_session->session); ctrl = strstr(stream->control, service_name); if (ctrl && (strlen(ctrl) != strlen(service_name)) ) { ctrl += strlen(service_name) + 1; service_name = gf_strdup(ctrl); gf_free(stream->control); stream->control = service_name; } } } /*no control specified, assume this is multicast*/ else { in_session = NULL; } if (in_session) { if (has_aggregated_control) in_session->flags |= RTSP_AGG_CONTROL; } else if (stream->control) { gf_free(stream->control); stream->control = NULL; } stream->rtsp = in_session; gf_list_add(rtp->channels, stream); return GF_OK; }
GF_Err RP_ConnectServiceEx(GF_InputService *plug, GF_ClientService *serv, const char *url, Bool skip_migration) { char *session_cache; RTSPSession *sess; RTPClient *priv = (RTPClient *)plug->priv; /*store user address*/ priv->service = serv; if (priv->dnload) gf_term_download_del(priv->dnload); priv->dnload = NULL; GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Opening service %s\n", url)); /*load preferences*/ RT_LoadPrefs(plug, priv); /*start thread*/ gf_th_run(priv->th, RP_Thread, priv); if (!skip_migration) { session_cache = (char *) gf_modules_get_option((GF_BaseInterface *) plug, "Streaming", "SessionMigrationFile"); if (session_cache && session_cache[0]) { FILE *f = gf_f64_open(session_cache, "rb"); if (f) { fclose(f); GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Restarting RTSP session from %s\n", session_cache)); RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url); return GF_OK; } if (!strncmp(session_cache, "http://", 7)) { GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTP] Restarting RTSP session from %s\n", session_cache)); RP_FetchSDP(priv, (char *) session_cache, NULL, (char *) url); return GF_OK; } } } /*local or remote SDP*/ if (strstr(url, "data:application/sdp") || (strnicmp(url, "rtsp", 4) && strstr(url, ".sdp")) ) { RP_FetchSDP(priv, (char *) url, NULL, NULL); return GF_OK; } /*rtsp and rtsp over udp*/ if (!strnicmp(url, "rtsp://", 7) || !strnicmp(url, "rtspu://", 8)) { char *the_url = gf_strdup(url); char *the_ext = strrchr(the_url, '#'); if (the_ext) { if (!stricmp(the_ext, "#audio")) priv->media_type = GF_MEDIA_OBJECT_AUDIO; else if (!stricmp(the_ext, "#video")) priv->media_type = GF_MEDIA_OBJECT_VIDEO; the_ext[0] = 0; } sess = RP_NewSession(priv, (char *) the_url); gf_free(the_url); if (!sess) { gf_term_on_connect(serv, NULL, GF_NOT_SUPPORTED); } else { RP_Describe(sess, 0, NULL); } return GF_OK; } /*direct RTP (no control) or embedded data - this means the service is attached to a single channel (no IOD) reply right away*/ gf_term_on_connect(serv, NULL, GF_OK); RP_SetupObjects(priv); return GF_OK; }
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; }
GF_Err RP_SetupSDP(RTPClient *rtp, GF_SDPInfo *sdp, RTPStream *stream) { GF_Err e; GF_SDPMedia *media; Double Start, End; u32 i; char *sess_ctrl, *session_id, *url; GF_X_Attribute *att; GF_RTSPRange *range; RTPStream *ch; RTSPSession *migrate_sess = NULL; Start = 0.0; End = -1.0; sess_ctrl = NULL; range = NULL; session_id = url = NULL; i=0; while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) { //session-level control string. Keep it in the current session if any if (!strcmp(att->Name, "control") && att->Value) sess_ctrl = att->Value; //NPT range only for now else if (!strcmp(att->Name, "range") && !range) range = gf_rtsp_range_parse(att->Value); /*session migration*/ else if (!strcmp(att->Name, "x-session-name")) url = att->Value; else if (!strcmp(att->Name, "x-session-id")) session_id = att->Value; /*we have the H264-SVC streams*/ else if (!strcmp(att->Name, "group") && !strncmp(att->Value, "DDP", 3)) rtp->is_svc = 1; } if (range) { Start = range->start; End = range->end; gf_rtsp_range_del(range); } if (url) { migrate_sess = RP_NewSession(rtp, url); if (migrate_sess && session_id) { migrate_sess->session_id = gf_strdup(session_id); } sess_ctrl = url; } //setup all streams i=0; while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { ch = RP_NewStream(rtp, media, sdp, stream); //do not generate error if the channel is not created, just assume //1 - this is not an MPEG-4 configured channel -> not needed //2 - this is a 2nd describe and the channel was already created if (!ch) continue; e = RP_AddStream(rtp, ch, sess_ctrl); if (e) { RP_DeleteStream(ch); return e; } if (!(ch->flags & RTP_HAS_RANGE)) { ch->range_start = Start; ch->range_end = End; if (End > 0) ch->flags |= RTP_HAS_RANGE; } /*force interleaving whenever needed*/ if (ch->rtsp) { switch (ch->depacketizer->sl_map.StreamType) { case GF_STREAM_VISUAL: case GF_STREAM_AUDIO: if ((rtp->transport_mode==1) && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) { gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE); ch->rtsp->flags |= RTSP_FORCE_INTER; } break; default: if (rtp->transport_mode && ! (ch->rtsp->flags & RTSP_FORCE_INTER) ) { gf_rtsp_set_buffer_size(ch->rtsp->session, RTSP_TCP_BUFFER_SIZE); ch->rtsp->flags |= RTSP_FORCE_INTER; } break; } } } return GF_OK; }