static void test_session_description(void) { const char* l_src = big_sdp; belle_sdp_origin_t* l_origin; belle_sdp_session_description_t* lTmp; belle_sip_list_t* media_descriptions; belle_sdp_session_description_t* l_session_description = belle_sdp_session_description_parse(l_src); char* l_raw_session_description = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_session_description)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_session_description)); lTmp = belle_sdp_session_description_parse(l_raw_session_description); belle_sip_free(l_raw_session_description); l_session_description = BELLE_SDP_SESSION_DESCRIPTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_version(l_session_description)); CU_ASSERT_EQUAL(belle_sdp_version_get_version(belle_sdp_session_description_get_version(l_session_description)),0); l_origin = belle_sdp_session_description_get_origin(l_session_description); CU_ASSERT_PTR_NOT_NULL(l_origin); CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address(l_origin),"2a01:e35:1387:1020:6233:4bff:fe0b:5663") CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address_type(l_origin),"IP6") CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_network_type(l_origin),"IN") CU_ASSERT_EQUAL(belle_sdp_origin_get_session_id(l_origin),1239) CU_ASSERT_EQUAL(belle_sdp_origin_get_session_version(l_origin),1239) CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_session_name(l_session_description)); CU_ASSERT_STRING_EQUAL(belle_sdp_session_name_get_value(belle_sdp_session_description_get_session_name(l_session_description)),"SIP Talk"); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_connection(l_session_description)); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_time_descriptions(l_session_description)); CU_ASSERT_EQUAL(belle_sdp_time_get_start(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0); CU_ASSERT_EQUAL(belle_sdp_time_get_stop(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0); media_descriptions = belle_sdp_session_description_get_media_descriptions(l_session_description); CU_ASSERT_PTR_NOT_NULL(media_descriptions); CU_ASSERT_STRING_EQUAL (belle_sdp_media_get_media_type(belle_sdp_media_description_get_media((belle_sdp_media_description_t*)(media_descriptions->data))),"audio"); media_descriptions=media_descriptions->next; CU_ASSERT_PTR_NOT_NULL(media_descriptions); test_media_description_base((belle_sdp_media_description_t*)(media_descriptions->data)); belle_sip_object_unref(l_session_description); return; }
static void test_media_description_base(belle_sdp_media_description_t* media_description) { const char* attr[] ={"99 MP4V-ES/90000" ,"99 profile-level-id=3" ,"97 theora/90000" ,"98 H263-1998/90000" ,"98 CIF=1;QCIF=1"}; belle_sdp_connection_t* lConnection; belle_sdp_media_description_t* l_media_description=media_description; belle_sdp_media_t* l_media = belle_sdp_media_description_get_media(l_media_description); belle_sip_list_t* list; int fmt[] ={99,97,98}; int i=0; CU_ASSERT_PTR_NOT_NULL(l_media); CU_ASSERT_STRING_EQUAL(belle_sdp_media_get_media_type(l_media), "video"); CU_ASSERT_EQUAL(belle_sdp_media_get_media_port(l_media), 8078); CU_ASSERT_STRING_EQUAL(belle_sdp_media_get_protocol(l_media), "RTP/AVP"); list = belle_sdp_media_get_media_formats(l_media); CU_ASSERT_PTR_NOT_NULL(list); for(;list!=NULL;list=list->next){ CU_ASSERT_EQUAL(BELLE_SIP_POINTER_TO_INT(list->data),fmt[i++]); } /*connection*/ lConnection = belle_sdp_media_description_get_connection(l_media_description); CU_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "192.168.0.18"); CU_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP4"); CU_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); /*bandwidth*/ CU_ASSERT_EQUAL(belle_sdp_media_description_get_bandwidth(l_media_description,"AS"),380); /*attributes*/ list = belle_sdp_media_description_get_attributes(l_media_description); CU_ASSERT_PTR_NOT_NULL(list); i=0; for(;list!=NULL;list=list->next){ CU_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_value((belle_sdp_attribute_t*)(list->data)),attr[i++]); } }
static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) { belle_sdp_mime_parameter_t* mime_param; belle_sdp_media_description_t* media_desc; int j; MSList* pt_it; PayloadType* pt; char buffer[1024]; char* dir=NULL; const char *rtp_addr; const char *rtcp_addr; int rtp_port; int rtcp_port; bool_t different_rtp_and_rtcp_addr; rtp_addr=stream->rtp_addr; rtcp_addr=stream->rtcp_addr; rtp_port=stream->rtp_port; rtcp_port=stream->rtcp_port; media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream) ,stream->rtp_port ,1 ,sal_media_proto_to_string ( stream->proto ) ,NULL ); if (stream->payloads) { for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) { pt= ( PayloadType* ) pt_it->data; mime_param= belle_sdp_mime_parameter_create ( pt->mime_type , payload_type_get_number ( pt ) , pt->clock_rate ,stream->type==SalAudio?1:-1 ); belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp ); if ( stream->ptime>0 ) { belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime ); } belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param ); belle_sip_object_unref ( mime_param ); } } else { /* to comply with SDP we cannot have an empty payload type number list */ /* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/ belle_sip_list_t* format = belle_sip_list_append(NULL,0); belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format); } /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; belle_sdp_media_description_set_connection(media_desc,belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr)); } if ( stream->bandwidth>0 ) belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth ); if ( stream->proto == SalProtoRtpSavp ) { /* add crypto lines */ for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) { switch ( stream->crypto[j].algo ) { case AES_128_SHA1_80: snprintf ( buffer, sizeof ( buffer ), "%d %s inline:%s", stream->crypto[j].tag, "AES_CM_128_HMAC_SHA1_80", stream->crypto[j].master_key ); belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( "crypto",buffer ) ); break; case AES_128_SHA1_32: snprintf ( buffer, sizeof ( buffer ), "%d %s inline:%s", stream->crypto[j].tag, "AES_CM_128_HMAC_SHA1_32", stream->crypto[j].master_key ); belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( "crypto",buffer ) ); break; case AES_128_NO_AUTH: ms_warning ( "Unsupported crypto suite: AES_128_NO_AUTH" ); break; case NO_CIPHER_SHA1_80: ms_warning ( "Unsupported crypto suite: NO_CIPHER_SHA1_80" ); break; default: j = SAL_CRYPTO_ALGO_MAX; /* no break */ } } } switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ dir=NULL; break; case SalStreamRecvOnly: dir="recvonly"; break; case SalStreamSendOnly: dir="sendonly"; break; case SalStreamInactive: dir="inactive"; break; } if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) ); if (rtp_port != 0) { different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { if (different_rtp_and_rtcp_addr == TRUE) { snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); } else { snprintf(buffer, sizeof(buffer), "%u",rtcp_port); } belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer)); } } if (stream->ice_completed == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes")); } if (stream->ice_mismatch == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL)); } else { if (rtp_port != 0) { if (stream->ice_pwd[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd)); if (stream->ice_ufrag[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag)); add_ice_candidates(media_desc,stream); add_ice_remote_candidates(media_desc,stream); } } if (stream->rtcp_xr.enabled == TRUE) { char sastr[1024] = {0}; char mastr[1024] = {0}; size_t saoff = 0; size_t maoff = 0; const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); belle_sdp_attribute_t *media_attribute; if (session_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff); } media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr); if (media_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff); } if (strcmp(sastr, mastr) != 0) { belle_sdp_media_description_add_attribute(media_desc, media_attribute); } else { belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } belle_sdp_session_description_add_media_description(session_desc, media_desc); }
static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) { SalStreamDescription *stream; belle_sdp_connection_t* cnx; belle_sdp_media_t* media; belle_sdp_attribute_t* attribute; const char* value; const char *mtype,*proto; stream=&md->streams[md->n_total_streams]; media=belle_sdp_media_description_get_media ( media_desc ); memset ( stream,0,sizeof ( *stream ) ); proto = belle_sdp_media_get_protocol ( media ); stream->proto=SalProtoOther; if ( proto ) { if ( strcasecmp ( proto,"RTP/AVP" ) ==0 ) stream->proto=SalProtoRtpAvp; else if ( strcasecmp ( proto,"RTP/SAVP" ) ==0 ) { stream->proto=SalProtoRtpSavp; }else{ strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); if ( stream->rtp_port > 0 ) md->n_active_streams++; mtype = belle_sdp_media_get_media_type ( media ); if ( strcasecmp ( "audio", mtype ) == 0 ) { stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); } if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) { stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ); } if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) { stream->dir=SalStreamSendRecv; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) { stream->dir=SalStreamSendOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) { stream->dir=SalStreamRecvOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) { stream->dir=SalStreamInactive; } else { stream->dir=md->dir; /*takes default value if not present*/ } /* Get media payload types */ sdp_parse_payload_types(media_desc, stream); /* Get media specific RTCP attribute */ stream->rtcp_port = stream->rtp_port + 1; snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr); attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ char tmp[256]; int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp); if (nb == 1) { /* SDP rtcp attribute only contains the port */ } else if (nb == 2) { strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1); } else { ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb); } } /* Read crypto lines if any */ if ( stream->proto == SalProtoRtpSavp ) { sdp_parse_media_crypto_parameters(media_desc, stream); } /* Get ICE candidate attributes if any */ sdp_parse_media_ice_parameters(media_desc, stream); /* Get RTCP-XR attributes if any */ stream->rtcp_xr = md->rtcp_xr; // Use session parameters if no stream parameters are defined sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr); md->n_total_streams++; return stream; }
static void test_simple_session_description(void) { const char* l_src = "v=0\r\n"\ "o=jehan-mac 1239 1239 IN IP4 192.168.0.18\r\n"\ "s=Talk\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "t=0 0\r\n"\ "m=audio 7078 RTP/AVP 111 110 3 0 8 101\r\n"\ "a=rtpmap:111 speex/16000\r\n"\ "a=fmtp:111 vbr=on\r\n"\ "a=rtpmap:110 speex/8000\r\n"\ "a=fmtp:110 vbr=on\r\n"\ "a=rtpmap:101 telephone-event/8000\r\n"\ "a=fmtp:101 0-11\r\n"\ "m=video 8078 RTP/AVP 99 97 98\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "a=rtpmap:99 MP4V-ES/90000\r\n"\ "a=fmtp:99 profile-level-id=3\r\n"\ "a=rtpmap:97 theora/90000\r\n"\ "a=rtpmap:98 H263-1998/90000\r\n"\ "a=fmtp:98 CIF=1;QCIF=1\r\n"; belle_sdp_origin_t* l_origin; belle_sip_list_t* media_descriptions; belle_sdp_session_description_t* lTmp; belle_sdp_session_description_t* l_session_description = belle_sdp_session_description_parse(l_src); char* l_raw_session_description = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_session_description)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_session_description)); lTmp = belle_sdp_session_description_parse(l_raw_session_description); belle_sip_free(l_raw_session_description); l_session_description = BELLE_SDP_SESSION_DESCRIPTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_version(l_session_description)); CU_ASSERT_EQUAL(belle_sdp_version_get_version(belle_sdp_session_description_get_version(l_session_description)),0); l_origin = belle_sdp_session_description_get_origin(l_session_description); CU_ASSERT_PTR_NOT_NULL(l_origin); CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address(l_origin),"192.168.0.18") CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address_type(l_origin),"IP4") CU_ASSERT_STRING_EQUAL(belle_sdp_origin_get_network_type(l_origin),"IN") CU_ASSERT_EQUAL(belle_sdp_origin_get_session_id(l_origin),1239) CU_ASSERT_EQUAL(belle_sdp_origin_get_session_version(l_origin),1239) CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_session_name(l_session_description)); CU_ASSERT_STRING_EQUAL(belle_sdp_session_name_get_value(belle_sdp_session_description_get_session_name(l_session_description)),"Talk"); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_connection(l_session_description)); CU_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_time_descriptions(l_session_description)); CU_ASSERT_EQUAL(belle_sdp_time_get_start(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0); CU_ASSERT_EQUAL(belle_sdp_time_get_stop(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0); media_descriptions = belle_sdp_session_description_get_media_descriptions(l_session_description); CU_ASSERT_PTR_NOT_NULL(media_descriptions); CU_ASSERT_STRING_EQUAL (belle_sdp_media_get_media_type(belle_sdp_media_description_get_media((belle_sdp_media_description_t*)(media_descriptions->data))),"audio"); media_descriptions=media_descriptions->next; CU_ASSERT_PTR_NOT_NULL(media_descriptions); test_media_description_base((belle_sdp_media_description_t*)(media_descriptions->data)); belle_sip_object_unref(l_session_description); return; }
static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) { SalStreamDescription *stream; belle_sdp_connection_t* cnx; belle_sdp_media_t* media; belle_sdp_attribute_t* attribute; const char* value; const char *mtype,*proto; stream=&md->streams[md->nb_streams]; media=belle_sdp_media_description_get_media ( media_desc ); proto = belle_sdp_media_get_protocol ( media ); stream->proto=SalProtoOther; if ( proto ) { if (strcasecmp(proto, "RTP/AVP") == 0) { stream->proto = SalProtoRtpAvp; } else if (strcasecmp(proto, "RTP/SAVP") == 0) { stream->proto = SalProtoRtpSavp; } else if (strcasecmp(proto, "RTP/AVPF") == 0) { stream->proto = SalProtoRtpAvpf; } else if (strcasecmp(proto, "RTP/SAVPF") == 0) { stream->proto = SalProtoRtpSavpf; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) { stream->proto = SalProtoUdpTlsRtpSavp; } else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) { stream->proto = SalProtoUdpTlsRtpSavpf; } else { strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1); } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 ); stream->ttl=belle_sdp_connection_get_ttl(cnx); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); mtype = belle_sdp_media_get_media_type ( media ); if ( strcasecmp ( "audio", mtype ) == 0 ) { stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); } if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) { stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ); } if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) { stream->dir=SalStreamSendRecv; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) { stream->dir=SalStreamSendOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) { stream->dir=SalStreamRecvOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) { stream->dir=SalStreamInactive; } else { stream->dir=md->dir; /*takes default value if not present*/ } /* Get media payload types */ sdp_parse_payload_types(media_desc, stream); /* Get media specific RTCP attribute */ stream->rtcp_port = stream->rtp_port + 1; snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr); attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ char tmp[256]; int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp); if (nb == 1) { /* SDP rtcp attribute only contains the port */ } else if (nb == 2) { strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1); } else { ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb); } } /* Read DTLS specific attributes : check is some are found in the stream description otherwise copy the session description one(which are at least set to Invalid) */ if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) { attribute=belle_sdp_media_description_get_attribute(media_desc,"setup"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ if (strncmp(value, "actpass", 7) == 0) { stream->dtls_role = SalDtlsRoleUnset; } else if (strncmp(value, "active", 6) == 0) { stream->dtls_role = SalDtlsRoleIsClient; } else if (strncmp(value, "passive", 7) == 0) { stream->dtls_role = SalDtlsRoleIsServer; } } if (stream->dtls_role != SalDtlsRoleInvalid && (attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"))) { strncpy(stream->dtls_fingerprint, belle_sdp_attribute_get_value(attribute),sizeof(stream->dtls_fingerprint)); } } /* Read crypto lines if any */ if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) { sdp_parse_media_crypto_parameters(media_desc, stream); } /* Get ICE candidate attributes if any */ sdp_parse_media_ice_parameters(media_desc, stream); /* Get RTCP-FB attributes if any */ if ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf)) { enable_avpf_for_stream(stream); sdp_parse_rtcp_fb_parameters(media_desc, stream); } /* Get RTCP-XR attributes if any */ stream->rtcp_xr = md->rtcp_xr; // Use session parameters if no stream parameters are defined sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr); md->nb_streams++; return stream; }
static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) { belle_sdp_mime_parameter_t* mime_param; belle_sdp_media_description_t* media_desc; int j; MSList* pt_it; PayloadType* pt; char buffer[1024]; char* dir=NULL; const char *rtp_addr; const char *rtcp_addr; int rtp_port; int rtcp_port; bool_t different_rtp_and_rtcp_addr; rtp_addr=stream->rtp_addr; rtcp_addr=stream->rtcp_addr; rtp_port=stream->rtp_port; rtcp_port=stream->rtcp_port; media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream) ,stream->rtp_port ,1 ,sal_media_proto_to_string ( stream->proto ) ,NULL ); if (stream->payloads) { for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) { pt= ( PayloadType* ) pt_it->data; mime_param= belle_sdp_mime_parameter_create ( pt->mime_type , payload_type_get_number ( pt ) , pt->clock_rate , pt->channels>0 ? pt->channels : -1 ); belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp ); if ( stream->ptime>0 ) { belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime ); } belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param ); belle_sip_object_unref ( mime_param ); } } else { /* to comply with SDP we cannot have an empty payload type number list */ /* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/ belle_sip_list_t* format = belle_sip_list_append(NULL,0); belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format); } /*only add a c= line within the stream description if address are differents*/ if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){ bool_t inet6; belle_sdp_connection_t *connection; if (strchr(rtp_addr,':')!=NULL){ inet6=TRUE; }else inet6=FALSE; connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr); if (ms_is_multicast(rtp_addr)) { /*remove session cline in case of multicast*/ belle_sdp_session_description_set_connection(session_desc,NULL); if (inet6 == FALSE) belle_sdp_connection_set_ttl(connection,stream->ttl); } belle_sdp_media_description_set_connection(media_desc,connection); } if ( stream->bandwidth>0 ) belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth ); if ((stream->proto == SalProtoRtpSavpf) || (stream->proto == SalProtoRtpSavp)) { /* add crypto lines */ for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) { MSCryptoSuiteNameParams desc; if (ms_crypto_suite_to_name_params(stream->crypto[j].algo,&desc)==0){ if (desc.params) snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params); else snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key ); belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer)); }else break; } } /* insert DTLS session attribute if needed */ if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) { char* ssrc_attribute = ms_strdup_printf("%u cname:%s",htonl(stream->rtp_ssrc),stream->rtcp_cname); if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) { switch(stream->dtls_role) { case SalDtlsRoleIsClient: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active")); break; case SalDtlsRoleIsServer: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive")); break; case SalDtlsRoleUnset: default: belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass")); break; } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); /* truc de Jehan a virer? */ ms_free(ssrc_attribute); } switch ( stream->dir ) { case SalStreamSendRecv: /*dir="sendrecv";*/ dir=NULL; break; case SalStreamRecvOnly: dir="recvonly"; break; case SalStreamSendOnly: dir="sendonly"; break; case SalStreamInactive: dir="inactive"; break; } if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) ); if (rtp_port != 0) { different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0); if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) { if (different_rtp_and_rtcp_addr == TRUE) { snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr); } else { snprintf(buffer, sizeof(buffer), "%u",rtcp_port); } belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer)); } } if (stream->ice_completed == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes")); } if (stream->ice_mismatch == TRUE) { belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL)); } else { if (rtp_port != 0) { if (stream->ice_pwd[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd)); if (stream->ice_ufrag[0] != '\0') belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag)); add_ice_candidates(media_desc,stream); add_ice_remote_candidates(media_desc,stream); } } if ((rtp_port != 0) && ((stream->proto == SalProtoRtpAvpf) || (stream->proto == SalProtoRtpSavpf))) { add_rtcp_fb_attributes(media_desc, md, stream); } if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) { char sastr[1024] = {0}; char mastr[1024] = {0}; size_t saoff = 0; size_t maoff = 0; const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr"); belle_sdp_attribute_t *media_attribute; if (session_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff); } media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr); if (media_attribute != NULL) { belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff); } if (strcmp(sastr, mastr) != 0) { belle_sdp_media_description_add_attribute(media_desc, media_attribute); } else { belle_sip_object_unref((belle_sip_object_t*)media_attribute); } } /* * rfc5576 * 4.1. The "ssrc" Media Attribute * <ssrc-id> is the synchronization source (SSRC) ID of the * source being described, interpreted as a 32-bit unsigned integer in * network byte order and represented in decimal.*/ belle_sdp_session_description_add_media_description(session_desc, media_desc); }
int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, SalMediaDescription *desc ) { /* typedef struct SalMediaDescription{ int refcount; char addr[64]; char username[64]; int nstreams; int bandwidth; unsigned int session_ver; unsigned int session_id; SalStreamDescription streams[SAL_MEDIA_DESCRIPTION_MAX_STREAMS]; } SalMediaDescription; */ belle_sdp_connection_t* cnx; belle_sip_list_t* media_desc_it; belle_sdp_media_description_t* media_desc; const char *mtype,*proto; SalStreamDescription *stream; belle_sdp_media_t* media; belle_sip_list_t* mime_params=NULL; belle_sip_list_t* mime_param_it=NULL; belle_sdp_mime_parameter_t* mime_param; PayloadType *pt; belle_sip_list_t* attribute_it; const belle_sdp_attribute_t* attribute; int valid_count = 0; char tmp[256], tmp2[256]; int nb=0; SalStreamDir stream_dir=SalStreamSendRecv; const char* value; desc->n_active_streams = 0; desc->n_total_streams = 0; if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) ); } if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) { desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ); } /*in some very rare case, session attribute may set stream dir*/ if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) { stream_dir=SalStreamSendRecv; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) { stream_dir=SalStreamSendOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) { stream_dir=SalStreamRecvOnly; } else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) { stream_dir=SalStreamInactive; } /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag)); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd"); if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)); value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite"); if (value) desc->ice_lite = TRUE; for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc ) ; media_desc_it!=NULL ; media_desc_it=media_desc_it->next ) { int nb_ice_candidates=0; media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data ); stream=&desc->streams[desc->n_total_streams]; media=belle_sdp_media_description_get_media ( media_desc ); memset ( stream,0,sizeof ( *stream ) ); proto = belle_sdp_media_get_protocol ( media ); stream->proto=SalProtoUnknown; if ( proto ) { if ( strcasecmp ( proto,"RTP/AVP" ) ==0 ) stream->proto=SalProtoRtpAvp; else if ( strcasecmp ( proto,"RTP/SAVP" ) ==0 ) { stream->proto=SalProtoRtpSavp; } } if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) { strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ),sizeof ( stream->rtp_addr ) ); } stream->rtp_port=belle_sdp_media_get_media_port ( media ); if ( stream->rtp_port > 0 ) desc->n_active_streams++; mtype = belle_sdp_media_get_media_type ( media ); if ( strcasecmp ( "audio", mtype ) == 0 ) { stream->type=SalAudio; } else if ( strcasecmp ( "video", mtype ) == 0 ) { stream->type=SalVideo; } else { stream->type=SalOther; strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 ); } if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) { stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ); } if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) { stream->dir=SalStreamSendRecv; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) { stream->dir=SalStreamSendOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) { stream->dir=SalStreamRecvOnly; } else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) { stream->dir=SalStreamInactive; } else { stream->dir=stream_dir; /*takes default value if not present*/ } /* for each payload type */ mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc ); for ( mime_param_it=mime_params ; mime_param_it!=NULL ; mime_param_it=mime_param_it->next ) { mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data ) pt=payload_type_new(); payload_type_set_number ( pt,belle_sdp_mime_parameter_get_media_format ( mime_param ) ); pt->clock_rate=belle_sdp_mime_parameter_get_rate ( mime_param ); pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) ); pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param ); payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) ); stream->payloads=ms_list_append ( stream->payloads,pt ); stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param ); ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, pt->send_fmtp ? pt->send_fmtp : "" ); } if ( mime_params ) belle_sip_list_free_with_data ( mime_params,belle_sip_object_unref ); /* Get media specific RTCP attribute */ stream->rtcp_port = stream->rtp_port + 1; snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr); attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ char tmp[256]; int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp); if (nb == 1) { /* SDP rtcp attribute only contains the port */ } else if (nb == 2) { strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)); } else { ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb); } } /* read crypto lines if any */ if ( stream->proto == SalProtoRtpSavp ) { valid_count=0; memset ( &stream->crypto, 0, sizeof ( stream->crypto ) ); for ( attribute_it=belle_sdp_media_description_get_attributes ( media_desc ) ; valid_count < SAL_CRYPTO_ALGO_MAX && attribute_it!=NULL; attribute_it=attribute_it->next ) { attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data ); if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) { nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s", &stream->crypto[valid_count].tag, tmp, tmp2 ); ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'", stream->crypto[valid_count].tag, tmp, tmp2 ); if ( nb == 3 ) { if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_80",tmp ) == 0 ) stream->crypto[valid_count].algo = AES_128_SHA1_80; else if ( keywordcmp ( "AES_CM_128_HMAC_SHA1_32",tmp ) == 0 ) stream->crypto[valid_count].algo = AES_128_SHA1_32; else { ms_warning ( "Failed to parse crypto-algo: '%s'", tmp ); stream->crypto[valid_count].algo = 0; } if ( stream->crypto[valid_count].algo ) { strncpy ( stream->crypto[valid_count].master_key, tmp2, 41 ); stream->crypto[valid_count].master_key[40] = '\0'; ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'", stream->crypto[valid_count].tag, tmp, stream->crypto[valid_count].master_key ); valid_count++; } } else { ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb ); } } } ms_message ( "Found: %d valid crypto lines", valid_count ); } /* Get ICE candidate attributes if any */ for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) { const char *att_name; attribute=(belle_sdp_attribute_t*)attribute_it->data; att_name=belle_sdp_attribute_get_name(attribute); value=belle_sdp_attribute_get_value(attribute); if ((keywordcmp("candidate", att_name) == 0) && (value != NULL)) { SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates]; int nb = sscanf(value, "%s %u UDP %u %s %d typ %s raddr %s rport %d", candidate->foundation, &candidate->componentID, &candidate->priority, candidate->addr, &candidate->port, candidate->type, candidate->raddr, &candidate->rport); if ((nb == 6) || (nb == 8)) nb_ice_candidates++; else memset(candidate, 0, sizeof(*candidate)); } else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) { SalIceRemoteCandidate candidate; unsigned int componentID; int offset; const char *ptr = value; const char *endptr=value+strlen(ptr); while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) { if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) { SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1]; strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)); remote_candidate->port = candidate.port; } ptr += offset; if (ptr<endptr){ if (ptr[offset] == ' ') ptr += 1; }else break; } } else if ((keywordcmp("ice-ufrag", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)); } else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) { strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd)); } else if (keywordcmp("ice-mismatch", att_name) == 0) { stream->ice_mismatch = TRUE; } } desc->n_total_streams++; } return 0; }