GF_Err SDP_CheckConnection(GF_SDPConnection *conn) { if (!conn) return GF_BAD_PARAM; if (!conn->host || !conn->add_type || !conn->net_type) return GF_REMOTE_SERVICE_ERROR; if (gf_sk_is_multicast_address(conn->host)) { if (conn->TTL < 0 || conn->TTL > 255) return GF_REMOTE_SERVICE_ERROR; } else { conn->TTL = -1; conn->add_count = 0; } return GF_OK; }
GF_Err PNC_InitRTP(GF_RTPChannel **chan, char *dest, int port, unsigned short mtu_size) { GF_Err res; GF_RTSPTransport tr; *chan = gf_rtp_new(); res = gf_rtp_set_ports(*chan, 0); if (res) { fprintf(stderr, "Cannot set RTP ports: %s\n", gf_error_to_string(res)); gf_rtp_del(*chan); return res; } tr.destination = dest; tr.IsUnicast = gf_sk_is_multicast_address(dest) ? 0 : 1; tr.Profile="RTP/AVP";//RTSP_PROFILE_RTP_AVP; tr.IsRecord = 0; tr.Append = 0; tr.source = "0.0.0.0"; tr.SSRC=rand(); tr.port_first = port; tr.port_last = port+1; if (tr.IsUnicast) { tr.client_port_first = port; tr.client_port_last = port+1; } else { tr.source = dest; tr.client_port_first = 0; tr.client_port_last = 0; } res = gf_rtp_setup_transport(*chan, &tr, dest); if (res) { fprintf(stderr, "Cannot setup RTP transport %s\n", gf_error_to_string(res)); gf_rtp_del(*chan); return res; } res = gf_rtp_initialize(*chan, 0, 1, mtu_size, 0, 0, NULL); if (res) { fprintf(stderr, "Cannot initialize RTP transport %s\n", gf_error_to_string(res)); gf_rtp_del(*chan); return res; } return GF_OK; }
static GF_Err rtp_stream_init_channel(GF_RTPStreamer *rtp, u32 path_mtu, const char * dest, int port, int ttl, const char *ifce_addr) { GF_RTSPTransport tr; GF_Err res; rtp->channel = gf_rtp_new(); gf_rtp_set_ports(rtp->channel, 0); memset(&tr, 0, sizeof(GF_RTSPTransport)); tr.IsUnicast = gf_sk_is_multicast_address(dest) ? 0 : 1; tr.Profile="RTP/AVP"; tr.destination = (char *)dest; tr.source = "0.0.0.0"; tr.IsRecord = 0; tr.Append = 0; tr.SSRC = rand(); tr.TTL = ttl; tr.port_first = port; tr.port_last = port+1; if (tr.IsUnicast) { tr.client_port_first = port; tr.client_port_last = port+1; } else { tr.source = (char *)dest; } res = gf_rtp_setup_transport(rtp->channel, &tr, dest); if (res !=0) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot setup RTP transport info: %s\n", gf_error_to_string(res) )); return res; } res = gf_rtp_initialize(rtp->channel, 0, 1, path_mtu, 0, 0, (char *)ifce_addr); if (res !=0) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot initialize RTP sockets: %s\n", gf_error_to_string(res) )); return res; } return GF_OK; }
GF_EXPORT GF_Err gf_sk_setup_multicast(GF_Socket *sock, const char *multi_IPAdd, u16 MultiPortNumber, u32 TTL, Bool NoBind, char *local_interface_ip) { s32 ret; u32 flag; struct ip_mreq M_req; u32 optval; #ifdef GPAC_HAS_IPV6 struct sockaddr *addr; struct addrinfo *res, *aip; Bool is_ipv6 = 0; u32 type; #endif unsigned long local_add_id; if (!sock || sock->socket) return GF_BAD_PARAM; if (TTL > 255) TTL = 255; /*check the address*/ if (!gf_sk_is_multicast_address(multi_IPAdd)) return GF_BAD_PARAM; /*turn on MobileIP*/ if (local_interface_ip && MobileIPAdd && !strcmp(MobileIPAdd, local_interface_ip) ) { if (gf_net_mobileip_ctrl(1)==GF_OK) { sock->flags |= GF_SOCK_IS_MIP; } else { local_interface_ip = NULL; } } #ifdef GPAC_HAS_IPV6 is_ipv6 = gf_net_is_ipv6(multi_IPAdd) || gf_net_is_ipv6(local_interface_ip) ? 1 : 0; type = (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM; if (is_ipv6) { res = gf_sk_get_ipv6_addr(local_interface_ip, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); if (!res) { if (local_interface_ip) { res = gf_sk_get_ipv6_addr(NULL, MultiPortNumber, AF_UNSPEC, AI_PASSIVE, type); local_interface_ip = NULL; } if (!res) return GF_IP_CONNECTION_FAILURE; } /*for all interfaces*/ for (aip=res; aip!=NULL; aip=aip->ai_next) { if (type != (u32) aip->ai_socktype) continue; sock->socket = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (sock->socket == INVALID_SOCKET) { sock->socket = NULL_SOCKET; continue; } if ((aip->ai_family!=PF_INET) && aip->ai_next && (aip->ai_next->ai_family==PF_INET) && !gf_net_is_ipv6(multi_IPAdd)) continue; /*enable address reuse*/ optval = 1; setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)); #ifdef SO_REUSEPORT optval = 1; setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); #endif /*TODO: copy over other properties (recption buffer size & co)*/ if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); memcpy(&sock->dest_addr, aip->ai_addr, aip->ai_addrlen); sock->dest_addr_len = (u32) aip->ai_addrlen; if (!NoBind) { ret = bind(sock->socket, aip->ai_addr, (int) aip->ai_addrlen); if (ret == SOCKET_ERROR) { closesocket(sock->socket); sock->socket = NULL_SOCKET; continue; } } if (aip->ai_family==PF_INET6) sock->flags |= GF_SOCK_IS_IPV6; else sock->flags &= ~GF_SOCK_IS_IPV6; break; } freeaddrinfo(res); if (!sock->socket) return GF_IP_CONNECTION_FAILURE; if (!gf_sk_ipv6_set_remote_address(sock, multi_IPAdd, MultiPortNumber)) return GF_IP_CONNECTION_FAILURE; addr = (struct sockaddr *)&sock->dest_addr; if (addr->sa_family == AF_INET) { M_req.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; M_req.imr_interface.s_addr = INADDR_ANY; ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; /*set TTL*/ ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &TTL, sizeof(TTL)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; /*Disable loopback*/ flag = 1; ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; } if (addr->sa_family == AF_INET6) { struct ipv6_mreq M_reqV6; memcpy(&M_reqV6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); M_reqV6.ipv6mr_interface = 0; /*set TTL*/ ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &TTL, sizeof(TTL)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; /*Disable loopback*/ flag = 1; ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; ret = setsockopt(sock->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *) &M_reqV6, sizeof(M_reqV6)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; } sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; return GF_OK; } #endif //IPv4 setup sock->socket = socket(AF_INET, (sock->flags & GF_SOCK_IS_TCP) ? SOCK_STREAM : SOCK_DGRAM, 0); if (sock->flags & GF_SOCK_NON_BLOCKING) gf_sk_set_block_mode(sock, 1); sock->flags &= ~GF_SOCK_IS_IPV6; /*enable address reuse*/ optval = 1; ret = setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, SSO_CAST &optval, sizeof(optval)); #ifdef SO_REUSEPORT optval = 1; setsockopt(sock->socket, SOL_SOCKET, SO_REUSEPORT, SSO_CAST &optval, sizeof(optval)); #endif if (local_interface_ip) local_add_id = inet_addr(local_interface_ip); else local_add_id = htonl(INADDR_ANY); if (!NoBind) { struct sockaddr_in local_address; local_address.sin_family = AF_INET; local_address.sin_addr.s_addr = local_add_id; local_address.sin_port = htons( MultiPortNumber); ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); if (ret == SOCKET_ERROR) { /*retry without specifying the local add*/ local_address.sin_addr.s_addr = local_add_id = htonl(INADDR_ANY); local_interface_ip = NULL; ret = bind(sock->socket, (struct sockaddr *) &local_address, sizeof(local_address)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; } /*setup local interface*/ if (local_interface_ip) { ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_IF, (char *) &local_add_id, sizeof(local_add_id)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; } } /*now join the multicast*/ M_req.imr_multiaddr.s_addr = inet_addr(multi_IPAdd); M_req.imr_interface.s_addr = local_add_id; ret = setsockopt(sock->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &M_req, sizeof(M_req)); if (ret == SOCKET_ERROR) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[core] cannot join multicast: error %d\n", LASTSOCKERROR)); return GF_IP_CONNECTION_FAILURE; } /*set the Time To Live*/ ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&TTL, sizeof(TTL)); if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; /*Disable loopback*/ flag = 1; ret = setsockopt(sock->socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &flag, sizeof(flag)); // if (ret == SOCKET_ERROR) return GF_IP_CONNECTION_FAILURE; #ifdef GPAC_HAS_IPV6 ((struct sockaddr_in *) &sock->dest_addr)->sin_family = AF_INET; ((struct sockaddr_in *) &sock->dest_addr)->sin_addr.s_addr = M_req.imr_multiaddr.s_addr; ((struct sockaddr_in *) &sock->dest_addr)->sin_port = htons( MultiPortNumber); sock->dest_addr_len = sizeof(struct sockaddr); #else sock->dest_addr.sin_family = AF_INET; sock->dest_addr.sin_addr.s_addr = M_req.imr_multiaddr.s_addr; sock->dest_addr.sin_port = htons( MultiPortNumber); #endif sock->flags |= GF_SOCK_IS_MULTICAST | GF_SOCK_HAS_PEER; 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; }
GF_EXPORT GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size) { GF_SDPBandwidth *bw; GF_SDPConnection *conn; GF_SDPMedia *media; GF_SDPTiming *timing; u32 i; s32 pos, LinePos; char LineBuf[3000], comp[3000]; media = NULL; timing = NULL; if (!sdp) return GF_BAD_PARAM; //Clean SDP info gf_sdp_info_reset(sdp); LinePos = 0; while (1) { LinePos = gf_token_get_line(sdp_text, LinePos, text_size, LineBuf, 3000); if (LinePos <= 0) break; if (!strcmp(LineBuf, "\r\n") || !strcmp(LineBuf, "\n") || !strcmp(LineBuf, "\r")) continue; switch (LineBuf[0]) { case 'v': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->Version = atoi(comp); break; case 'o': pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); sdp->o_username = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); sdp->o_session_id = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); sdp->o_version = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); sdp->o_net_type = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); sdp->o_add_type = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); sdp->o_address = gf_strdup(comp); break; case 's': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->s_session_name = gf_strdup(comp); break; case 'i': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->i_description = gf_strdup(comp); break; case 'u': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->u_uri = gf_strdup(comp); break; case 'e': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->e_email = gf_strdup(comp); break; case 'p': pos = gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000); sdp->p_phone = gf_strdup(comp); break; case 'c': //if at session level, only 1 is allowed for all SDP if (sdp->c_connection) break; conn = gf_sdp_conn_new(); pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); conn->net_type = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); conn->add_type = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000); conn->host = gf_strdup(comp); if (gf_sk_is_multicast_address(conn->host)) { //a valid SDP will have TTL if address is multicast pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000); if (pos > 0) { conn->TTL = atoi(comp); //multiple address indication is only valid for media pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000); } if (pos > 0) { if (!media) { gf_sdp_conn_del(conn); break; } conn->add_count = atoi(comp); } } if (!media) sdp->c_connection = conn; else gf_list_add(media->Connections, conn); break; case 'b': pos = gf_token_get(LineBuf, 2, ":\r\n", comp, 3000); if (strcmp(comp, "CT") && strcmp(comp, "AS") && (comp[0] != 'X')) break; bw = (GF_SDPBandwidth*)gf_malloc(sizeof(GF_SDPBandwidth)); bw->name = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000); bw->value = atoi(comp); if (media) { gf_list_add(media->Bandwidths, bw); } else { gf_list_add(sdp->b_bandwidth, bw); } break; case 't': if (media) break; //create a new time structure for each entry GF_SAFEALLOC(timing, GF_SDPTiming); pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); timing->StartTime = atoi(comp); pos = gf_token_get(LineBuf, pos, "\r\n", comp, 3000); timing->StopTime = atoi(comp); gf_list_add(sdp->Timing, timing); break; case 'r': if (media) break; pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); timing->RepeatInterval = SDP_MakeSeconds(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); timing->ActiveDuration = SDP_MakeSeconds(comp); while (1) { pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); if (pos <= 0) break; timing->OffsetFromStart[timing->NbRepeatOffsets] = SDP_MakeSeconds(comp); timing->NbRepeatOffsets += 1; } break; case 'z': if (media) break; pos = 2; while (1) { pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); if (pos <= 0) break; timing->AdjustmentTime[timing->NbZoneOffsets] = atoi(comp); pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000); timing->AdjustmentOffset[timing->NbZoneOffsets] = SDP_MakeSeconds(comp); timing->NbZoneOffsets += 1; } break; case 'k': pos = gf_token_get(LineBuf, 2, ":\t\r\n", comp, 3000); if (media) { media->k_method = gf_strdup(comp); } else { sdp->k_method = gf_strdup(comp); } pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000); if (pos > 0) { if (media) { media->k_key = gf_strdup(comp); } else { sdp->k_key = gf_strdup(comp); } } break; case 'a': SDP_ParseAttribute(sdp, LineBuf+2, media); break; case 'm': pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000); if (strcmp(comp, "audio") && strcmp(comp, "data") && strcmp(comp, "control") && strcmp(comp, "video") && strcmp(comp, "text") && strcmp(comp, "application")) { return GF_SERVICE_ERROR; } media = gf_sdp_media_new(); //media type if (!strcmp(comp, "video")) media->Type = 1; else if (!strcmp(comp, "audio")) media->Type = 2; else if (!strcmp(comp, "text")) media->Type = 3; else if (!strcmp(comp, "data")) media->Type = 4; else if (!strcmp(comp, "control")) media->Type = 5; else media->Type = 0; //port numbers gf_token_get(LineBuf, pos, " ", comp, 3000); if (!strstr(comp, "/")) { pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); media->PortNumber = atoi(comp); media->NumPorts = 0; } else { pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000); media->PortNumber = atoi(comp); pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); media->NumPorts = atoi(comp); } //transport Profile pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); media->Profile = gf_strdup(comp); pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000); media->fmt_list = gf_strdup(comp); gf_list_add(sdp->media_desc, media); break; } } //finally rewrite the fmt_list for all media, and remove dynamic payloads //from the list i=0; while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) { pos = 0; LinePos = 1; strcpy(LineBuf, ""); while (1) { if (!media->fmt_list) break; pos = gf_token_get(media->fmt_list, pos, " ", comp, 3000); if (pos <= 0) break; if (!SDP_IsDynamicPayload(media, comp)) { if (!LinePos) { strcat(LineBuf, " "); } else { LinePos = 0; } strcat(LineBuf, comp); } gf_free(media->fmt_list); media->fmt_list = NULL; if (strlen(LineBuf)) { media->fmt_list = gf_strdup(LineBuf); } } } return GF_OK; }