void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value) { char LineBuffer[400]; s32 LinePos; GF_RTSPTransport *trans; GF_X_Attribute *x_Att; if (!stricmp(Header, "Accept")) com->Accept = gf_strdup(Value); else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = gf_strdup(Value); else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = gf_strdup(Value); else if (!stricmp(Header, "Authorization")) com->Authorization = gf_strdup(Value); else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &com->Bandwidth); else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &com->Blocksize); else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = gf_strdup(Value); else if (!stricmp(Header, "Conference")) com->Conference = gf_strdup(Value); else if (!stricmp(Header, "Connection")) com->Connection = gf_strdup(Value); else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &com->Content_Length); else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &com->CSeq); else if (!stricmp(Header, "From")) com->From = gf_strdup(Value); else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = gf_strdup(Value); else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = gf_strdup(Value); else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value); else if (!stricmp(Header, "Referer")) com->Referer = gf_strdup(Value); else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale); else if (!stricmp(Header, "Session")) com->Session = gf_strdup(Value); else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed); else if (!stricmp(Header, "User_Agent")) com->User_Agent = gf_strdup(Value); //Transports else if (!stricmp(Header, "Transport")) { LinePos = 0; while (1) { LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400); if (LinePos <= 0) return; trans = gf_rtsp_transport_parse(Value); if (trans) gf_list_add(com->Transports, trans); } } //eXtensions attributes else if (!strnicmp(Header, "x-", 2)) { x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute)); x_Att->Name = gf_strdup(Header+2); x_Att->Value = NULL; if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value); gf_list_add(com->Xtensions, x_Att); } //the rest is ignored }
void gf_rtsp_set_response_value(GF_RTSPResponse *rsp, char *Header, char *Value) { char LineBuffer[400], buf[1000], param_name[100], param_val[1000]; s32 LinePos, Pos, nPos, s_val; GF_RTPInfo *info; GF_RTSPTransport *trans; GF_X_Attribute *x_Att; if (!stricmp(Header, "Accept")) rsp->Accept = gf_strdup(Value); else if (!stricmp(Header, "Accept-Encoding")) rsp->Accept_Encoding = gf_strdup(Value); else if (!stricmp(Header, "Accept-Language")) rsp->Accept_Language = gf_strdup(Value); else if (!stricmp(Header, "Allow")) rsp->Allow = gf_strdup(Value); else if (!stricmp(Header, "Authorization")) rsp->Authorization = gf_strdup(Value); else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &rsp->Bandwidth); else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &rsp->Blocksize); else if (!stricmp(Header, "Cache-Control")) rsp->Cache_Control = gf_strdup(Value); else if (!stricmp(Header, "com.ses.streamID")) sscanf(Value, "%u", &rsp->StreamID); else if (!stricmp(Header, "Conference")) rsp->Conference = gf_strdup(Value); else if (!stricmp(Header, "Connection")) rsp->Connection = gf_strdup(Value); else if (!stricmp(Header, "Content-Base")) rsp->Content_Base = gf_strdup(Value); else if (!stricmp(Header, "Content-Encoding")) rsp->Content_Encoding = gf_strdup(Value); else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &rsp->Content_Length); else if (!stricmp(Header, "Content-Language")) rsp->Content_Language = gf_strdup(Value); else if (!stricmp(Header, "Content-Location")) rsp->Content_Location = gf_strdup(Value); else if (!stricmp(Header, "Content-Type")) rsp->Content_Type = gf_strdup(Value); else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &rsp->CSeq); else if (!stricmp(Header, "Date")) rsp->Date = gf_strdup(Value); else if (!stricmp(Header, "Expires")) rsp->Expires = gf_strdup(Value); else if (!stricmp(Header, "From")) rsp->From = gf_strdup(Value); else if (!stricmp(Header, "Host")) rsp->Host = gf_strdup(Value); else if (!stricmp(Header, "If-Match")) rsp->If_Match = gf_strdup(Value); else if (!stricmp(Header, "If-Modified-Since")) rsp->If_Modified_Since = gf_strdup(Value); else if (!stricmp(Header, "Last-Modified")) rsp->Last_Modified = gf_strdup(Value); else if (!stricmp(Header, "Location")) rsp->Location = gf_strdup(Value); else if (!stricmp(Header, "Proxy-Authenticate")) rsp->Proxy_Authenticate = gf_strdup(Value); else if (!stricmp(Header, "Proxy-Require")) rsp->Proxy_Require = gf_strdup(Value); else if (!stricmp(Header, "Public")) rsp->Public = gf_strdup(Value); else if (!stricmp(Header, "Referer")) rsp->Referer = gf_strdup(Value); else if (!stricmp(Header, "Require")) rsp->Require = gf_strdup(Value); else if (!stricmp(Header, "Retry-After")) rsp->Retry_After = gf_strdup(Value); else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &rsp->Scale); else if (!stricmp(Header, "Server")) rsp->Server = gf_strdup(Value); else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &rsp->Speed); else if (!stricmp(Header, "Timestamp")) rsp->Timestamp = gf_strdup(Value); else if (!stricmp(Header, "Unsupported")) rsp->Unsupported = gf_strdup(Value); else if (!stricmp(Header, "User-Agent")) rsp->User_Agent = gf_strdup(Value); else if (!stricmp(Header, "Vary")) rsp->Vary = gf_strdup(Value); else if (!stricmp(Header, "Via")) rsp->Vary = gf_strdup(Value); else if (!stricmp(Header, "WWW_Authenticate")) rsp->Vary = gf_strdup(Value); else if (!stricmp(Header, "Transport")) { LinePos = 0; while (1) { LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400); if (LinePos <= 0) return; trans = gf_rtsp_transport_parse(Value); if (trans) gf_list_add(rsp->Transports, trans); } } //Session else if (!stricmp(Header, "Session")) { LinePos = gf_token_get(Value, 0, ";\r\n", LineBuffer, 400); rsp->Session = gf_strdup(LineBuffer); //get timeout if any if (Value[LinePos] == ';') { LinePos += 1; /*LinePos = */gf_token_get(Value, LinePos, ";\r\n", LineBuffer, 400); rsp->SessionTimeOut = 60; //default sscanf(LineBuffer, "timeout=%u", &rsp->SessionTimeOut); } } //Range else if (!stricmp(Header, "Range")) rsp->Range = gf_rtsp_range_parse(Value); //RTP-Info else if (!stricmp(Header, "RTP-Info")) { LinePos = 0; while (1) { LinePos = gf_token_get(Value, LinePos, ",\r\n", LineBuffer, 400); if (LinePos <= 0) return; GF_SAFEALLOC(info, GF_RTPInfo); if (!info) return; Pos = 0; while (1) { Pos = gf_token_get(LineBuffer, Pos, " ;", buf, 1000); if (Pos <= 0) break; if (strstr(buf, "=")) { nPos = gf_token_get(buf, 0, "=", param_name, 100); nPos += 1; /*nPos = */gf_token_get(buf, nPos, "", param_val, 1000); } else { strcpy(param_name, buf); } if (!stricmp(param_name, "url")) info->url = gf_strdup(param_val); else if (!stricmp(param_name, "seq")) sscanf(param_val, "%u", &info->seq); else if (!stricmp(param_name, "rtptime")) { sscanf(param_val, "%i", &s_val); info->rtp_time = (s_val>0) ? s_val : 0; } else if (!stricmp(param_name, "ssrc")) { sscanf(param_val, "%i", &s_val); info->ssrc = (s_val>0) ? s_val : 0; } } gf_list_add(rsp->RTP_Infos, info); } } //check for extended attributes else if (!strnicmp(Header, "x-", 2)) { x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute)); x_Att->Name = gf_strdup(Header+2); x_Att->Value = NULL; if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value); gf_list_add(rsp->Xtensions, x_Att); } //unknown field - skip it }
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; }
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; }