sal_sdp.c 40.6 KB
Newer Older
1

jehan's avatar
jehan committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
linphone
Copyright (C) 2012  Belledonne Communications, Grenoble, France

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
jehan's avatar
jehan committed
19 20 21 22
*/
#include "sal_impl.h"
#define keywordcmp(key,b) strncmp(key,b,sizeof(key))

23 24 25 26 27
inline OrtpRtcpXrStatSummaryFlag operator|=(OrtpRtcpXrStatSummaryFlag a, OrtpRtcpXrStatSummaryFlag b) {
	int ia = static_cast<int>(a);
	int ib = static_cast<int>(b);
	return static_cast<OrtpRtcpXrStatSummaryFlag>(ia |= ib);
}
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

static void add_ice_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
	char buffer[1024];
	const SalIceCandidate *candidate;
	int nb;
	int i;

	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) {
		candidate = &desc->ice_candidates[i];
		if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break;
		nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s",
			candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type);
		if (nb < 0) {
			ms_error("Cannot add ICE candidate attribute!");
			return;
		}
		if (candidate->raddr[0] != '\0') {
Benjamin REIS's avatar
Benjamin REIS committed
45
			nb = snprintf(buffer + nb, sizeof(buffer) - (size_t)nb, " raddr %s rport %d", candidate->raddr, candidate->rport);
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
			if (nb < 0) {
				ms_error("Cannot add ICE candidate attribute!");
				return;
			}
		}
		belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("candidate",buffer));
	}
}

static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
	char buffer[1024];
	char *ptr = buffer;
	const SalIceRemoteCandidate *candidate;
	int offset = 0;
	int i;

	buffer[0] = '\0';
	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) {
		candidate = &desc->ice_remote_candidates[i];
		if ((candidate->addr[0] != '\0') && (candidate->port != 0)) {
Benjamin REIS's avatar
Benjamin REIS committed
66
			offset = snprintf(ptr, (size_t)(buffer + sizeof(buffer) - ptr), "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port);
67 68 69 70 71 72 73 74 75 76
			if (offset < 0) {
				ms_error("Cannot add ICE remote-candidates attribute!");
				return;
			}
			ptr += offset;
		}
	}
	if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer));
}

77
static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) {
78
	bctbx_list_t *pt_it;
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	bool_t first = TRUE;
	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
		PayloadType *pt = (PayloadType *)pt_it->data;
		if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
			if (first == TRUE) {
				*trr_int = payload_type_get_avpf_params(pt).trr_interval;
				first = FALSE;
			} else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) {
				return FALSE;
			}
		}
	}
	return TRUE;
}

94
static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint16_t trr_int) {
95 96 97 98 99 100 101
	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT);
	belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int);
	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}

102 103 104 105 106 107 108 109
static void add_rtcp_fb_ack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_ACK);
	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}

110 111 112 113 114 115 116 117
static void add_rtcp_fb_nack_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK);
	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}

118 119 120 121 122 123 124 125
static void add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t *media_desc, int8_t id, belle_sdp_rtcp_fb_val_param_t param) {
	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_CCM);
	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
}

126
static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) {
127
	bctbx_list_t *pt_it;
128
	PayloadType *pt;
129
	PayloadTypeAvpfParams avpf_params;
130
	bool_t general_trr_int;
131
	uint16_t trr_int = 0;
132

133
	general_trr_int = is_rtcp_fb_trr_int_the_same_for_all_payloads(stream, &trr_int);
134
	if (general_trr_int == TRUE && trr_int != 0) {
135 136
		add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int);
	}
137 138 139 140 141 142
	if (stream->rtcp_fb.generic_nack_enabled == TRUE) {
		add_rtcp_fb_nack_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_NONE);
	}
	if (stream->rtcp_fb.tmmbr_enabled == TRUE) {
		add_rtcp_fb_ccm_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_TMMBR);
	}
143 144 145 146

	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
		pt = (PayloadType *)pt_it->data;

147
		/* AVPF/SAVPF profile is used so enable AVPF for all payload types. */
148 149 150
		payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
		avpf_params = payload_type_get_avpf_params(pt);

151
		/* Add trr-int if not set generally. */
152
		if (general_trr_int != TRUE && trr_int != 0) {
153
			add_rtcp_fb_trr_int_attribute(media_desc, (int8_t)payload_type_get_number(pt), avpf_params.trr_interval);
154 155
		}

156 157
		/* Add rtcp-fb attributes according to the AVPF features of the payload types. */
		if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) {
158
			add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI);
159 160
		}
		if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) {
161
			add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI);
162 163
		}
		if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) {
164
			if (avpf_params.rpsi_compatibility == TRUE) {
165
				add_rtcp_fb_nack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
166
			} else {
167
				add_rtcp_fb_ack_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
168
			}
169
		}
170
		if (avpf_params.features & PAYLOAD_TYPE_AVPF_FIR) {
171
			add_rtcp_fb_ccm_attribute(media_desc, (int8_t)payload_type_get_number(pt), BELLE_SDP_RTCP_FB_FIR);
172
		}
173 174 175
	}
}

176
static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) {
177
	belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new();
178 179 180 181
	if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
		if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttAll) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "all");
		else if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttSender) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "sender");
		belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(attribute, config->rcvr_rtt_max_size);
182
	}
183 184 185 186 187 188 189
	belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, (config->stat_summary_enabled == TRUE));
	if (config->stat_summary_enabled == TRUE) {
		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryLoss) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "loss");
		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryDup) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "dup");
		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryJitt) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "jitt");
		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryTTL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "TTL");
		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryHL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "HL");
190
	}
191
	belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, (config->voip_metrics_enabled == TRUE));
192 193 194
	return BELLE_SDP_ATTRIBUTE(attribute);
}

195
static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) {
jehan's avatar
jehan committed
196 197
	belle_sdp_mime_parameter_t* mime_param;
	belle_sdp_media_description_t* media_desc;
198
	int j;
199
	bctbx_list_t* pt_it;
jehan's avatar
jehan committed
200 201
	PayloadType* pt;
	char buffer[1024];
202
	const char* dir=NULL;
203 204 205 206 207
	const char *rtp_addr;
	const char *rtcp_addr;
	int rtp_port;
	int rtcp_port;
	bool_t different_rtp_and_rtcp_addr;
208

209 210 211 212
	rtp_addr=stream->rtp_addr;
	rtcp_addr=stream->rtcp_addr;
	rtp_port=stream->rtp_port;
	rtcp_port=stream->rtcp_port;
213

214
	media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream)
215 216 217 218
				 ,stream->rtp_port
				 ,1
				 ,sal_media_proto_to_string ( stream->proto )
				 ,NULL );
219 220 221 222
	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
223 224
					, payload_type_get_number ( pt )
					, pt->clock_rate
225
					, pt->channels>0 ? pt->channels : -1 );
226 227 228 229 230 231
			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 );
232
		}
233 234 235 236 237
	} 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);
238
	}
239 240 241
	/*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;
jehan's avatar
jehan committed
242
		belle_sdp_connection_t *connection;
243 244 245
		if (strchr(rtp_addr,':')!=NULL){
			inet6=TRUE;
		}else inet6=FALSE;
jehan's avatar
jehan committed
246 247 248 249 250 251 252 253
		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);
254
	}
255

256 257 258
	if ( stream->bandwidth>0 )
		belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );

259
	if (sal_stream_description_has_srtp(stream)) {
260 261
		/* add crypto lines */
		for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) {
262 263 264 265
			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);
266
				else
267
					snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key );
268

269 270
				belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer));
			}else break;
271 272
		}
	}
johan's avatar
johan committed
273 274 275

	/* insert DTLS session attribute if needed */
	if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) {
276
		char* ssrc_attribute = ms_strdup_printf("%u cname:%s",stream->rtp_ssrc,stream->rtcp_cname);
johan's avatar
johan committed
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
		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));
		}
292
		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute));
293
		ms_free(ssrc_attribute);
johan's avatar
johan committed
294 295
	}

johan's avatar
johan committed
296 297 298 299 300
	/* insert zrtp-hash attribute if needed */
	if (stream->haveZrtpHash == 1) {
		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("zrtp-hash", (const char *)(stream->zrtphash)));
	}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
	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 ) );
317

318 319 320
	if (stream->rtcp_mux){
		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create ("rtcp-mux",NULL ) );
	}
321

322 323 324 325 326 327 328 329 330 331 332
	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));
		}
	}
333
	if (stream->set_nortpproxy == TRUE) {
334 335 336 337 338 339
		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) {
340
			if (stream->ice_pwd[0] != '\0')
341 342 343 344 345 346 347
				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);
		}
	}
348

349
	if ((rtp_port != 0) && (sal_stream_description_has_avpf(stream) || sal_stream_description_has_implicit_avpf(stream))) {
350
		add_rtcp_fb_attributes(media_desc, md, stream);
351 352
	}

353
	if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) {
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
		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);
		}
372
	}
373 374 375 376 377 378 379 380 381 382

	if (stream->custom_sdp_attributes) {
		belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)stream->custom_sdp_attributes;
		belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc);
		belle_sip_list_t *elem;
		for (elem = l; elem != NULL; elem = elem->next) {
			belle_sdp_media_description_add_attribute(media_desc, (belle_sdp_attribute_t *)elem->data);
		}
	}

383 384 385 386 387 388 389
	/*
	 * 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.*/

390

391
	belle_sdp_session_description_add_media_description(session_desc, media_desc);
392 393 394 395 396 397 398
}

belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescription *desc ) {
	belle_sdp_session_description_t* session_desc=belle_sdp_session_description_new();
	bool_t inet6;
	belle_sdp_origin_t* origin;
	int i;
jehan's avatar
jehan committed
399

400 401 402 403
	if ( strchr ( desc->addr,':' ) !=NULL ) {
		inet6=1;
	} else inet6=0;
	belle_sdp_session_description_set_version ( session_desc,belle_sdp_version_create ( 0 ) );
jehan's avatar
jehan committed
404

405 406 407 408 409 410
	origin = belle_sdp_origin_create ( desc->username
									  ,desc->session_id
									  ,desc->session_ver
									  ,"IN"
									  , inet6 ? "IP6" :"IP4"
									  ,desc->addr );
jehan's avatar
jehan committed
411

412
	belle_sdp_session_description_set_origin ( session_desc,origin );
jehan's avatar
jehan committed
413

414 415
	belle_sdp_session_description_set_session_name ( session_desc,
		belle_sdp_session_name_create ( desc->name[0]!='\0' ? desc->name : "Talk" ) );
jehan's avatar
jehan committed
416

417 418
	if ( !sal_media_description_has_dir ( desc,SalStreamInactive ) || desc->ice_ufrag[0] != '\0' ) {
		/*in case of sendonly, setting of the IP on cnx we give a chance to receive stun packets*/
419 420
		belle_sdp_session_description_set_connection ( session_desc
				,belle_sdp_connection_create ( "IN",inet6 ? "IP6" :"IP4",desc->addr ) );
jehan's avatar
jehan committed
421 422

	} else 	{
423 424 425 426
		belle_sdp_session_description_set_connection ( session_desc
				,belle_sdp_connection_create ( "IN"
								,inet6 ? "IP6" :"IP4"
								,inet6 ? "::0" :"0.0.0.0" ) );
jehan's avatar
jehan committed
427 428 429

	}

430
	belle_sdp_session_description_set_time_description ( session_desc,belle_sdp_time_description_create ( 0,0 ) );
jehan's avatar
jehan committed
431

432 433
	if ( desc->bandwidth>0 ) {
		belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth );
jehan's avatar
jehan committed
434
	}
435

436
	if (desc->set_nortpproxy == TRUE) belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("nortpproxy","yes"));
437 438
	if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd));
	if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag));
439 440 441 442

	if (desc->rtcp_xr.enabled == TRUE) {
		belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr));
	}
443 444 445 446 447 448 449 450
	if (desc->custom_sdp_attributes) {
		belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)desc->custom_sdp_attributes;
		belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc);
		belle_sip_list_t *elem;
		for (elem = l; elem != NULL; elem = elem->next) {
			belle_sdp_session_description_add_attribute(session_desc, (belle_sdp_attribute_t *)elem->data);
		}
	}
451

452
	for ( i=0; i<desc->nb_streams; i++ ) {
453
		stream_description_to_sdp(session_desc, desc, &desc->streams[i]);
jehan's avatar
jehan committed
454 455 456 457 458
	}
	return session_desc;
}


459 460
static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
	PayloadType *pt;
461
	PayloadTypeAvpfParams avpf_params;
462 463 464
	belle_sip_list_t* mime_param_it=NULL;
	belle_sdp_mime_parameter_t* mime_param;
	belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc );
465
	memset(&avpf_params, 0, sizeof(avpf_params));
466 467 468
	for ( mime_param_it=mime_params
						; mime_param_it!=NULL
			; mime_param_it=mime_param_it->next ) {
469
		mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data );
470 471 472 473 474 475 476

		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 ) );
477
		payload_type_set_avpf_params(pt, avpf_params);
478
		stream->payloads=bctbx_list_append ( stream->payloads,pt );
479 480 481 482 483 484 485 486 487
		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 );
}

static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
	belle_sip_list_t *attribute_it;
488
	belle_sdp_attribute_t *attribute;
489
	char tmp[256], tmp2[256], parameters[256]={0};
490 491 492 493 494 495 496 497 498
	int valid_count = 0;
	int nb;

	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 );

499
		if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) {
500
			nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s %256s",
501 502
							&stream->crypto[valid_count].tag,
							tmp,
503
							tmp2, parameters );
504

505 506 507 508 509 510 511 512
			if ( nb >= 3 ) {
				MSCryptoSuite cs;
				MSCryptoSuiteNameParams np;

				np.name=tmp;
				np.params=parameters[0]!='\0' ? parameters : NULL;
				cs=ms_crypto_suite_build_from_name_params(&np);
				if (cs==MS_CRYPTO_SUITE_INVALID){
513
					ms_warning ( "Failed to parse crypto-algo: '%s'", tmp );
514
					stream->crypto[valid_count].algo = MS_CRYPTO_SUITE_INVALID;
515 516 517 518 519 520
				}else{
					char *sep;
					strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)-1 );
					sep=strchr(stream->crypto[valid_count].master_key,'|');
					if (sep) *sep='\0';
					stream->crypto[valid_count].algo = cs;
521 522 523 524 525 526
					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++;
				}
527
			}else{
528
				ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb );
529 530 531
			}
		}
	}
532
	ms_message("Found: %d valid crypto lines", valid_count );
533 534
}

535
static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
536
	belle_sip_list_t *attribute_it;
537
	belle_sdp_attribute_t *attribute;
538 539 540 541 542
	const char *att_name;
	const char *value;
	int nb_ice_candidates = 0;

	for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) {
543
		attribute=BELLE_SDP_ATTRIBUTE(attribute_it->data);
544
		att_name = belle_sdp_attribute_get_name(attribute);
545
		value = belle_sdp_attribute_get_value(attribute);
546

547
		if ((nb_ice_candidates < (int)(sizeof(stream->ice_candidates)/sizeof(SalIceCandidate)))
548 549
				&& (keywordcmp("candidate", att_name) == 0)
				&& (value != NULL)) {
550
			SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates];
jehan's avatar
jehan committed
551 552 553
			char proto[4];
			int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d",
				candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port,
554
				candidate->type, candidate->raddr, &candidate->rport);
jehan's avatar
jehan committed
555
			if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++;
556 557 558 559
			else {
				ms_error("ice: Failed parsing a=candidate SDP attribute");
				memset(candidate, 0, sizeof(*candidate));
			}
560 561 562 563 564 565 566 567 568
		} 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];
569
					strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)-1);
570 571 572 573 574 575 576 577
					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)) {
578
			strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)-1);
579
		} else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) {
580
			strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd) -1);
581 582 583 584 585 586
		} else if (keywordcmp("ice-mismatch", att_name) == 0) {
			stream->ice_mismatch = TRUE;
		}
	}
}

587
static void enable_avpf_for_stream(SalStreamDescription *stream) {
588
	bctbx_list_t *pt_it;
589
	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
590
		PayloadType *pt = (PayloadType *)pt_it->data;
591 592 593 594
		payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
	}
}

595
static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, SalStreamDescription *stream, PayloadType *pt) {
596 597
	PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt);
	switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) {
598 599 600 601 602
		case BELLE_SDP_RTCP_FB_ACK:
			if (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute) == BELLE_SDP_RTCP_FB_RPSI) {
				avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
			}
			break;
603 604 605 606 607 608 609 610 611
		case BELLE_SDP_RTCP_FB_NACK:
			switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
				case BELLE_SDP_RTCP_FB_PLI:
					avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI;
					break;
				case BELLE_SDP_RTCP_FB_SLI:
					avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI;
					break;
				case BELLE_SDP_RTCP_FB_RPSI:
612 613 614
					/* Linphone uses positive feeback for RPSI. However first versions handling
					 * AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility
					 * with these versions but will probably be removed at some point in time. */
615
					avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
616
					avpf_params.rpsi_compatibility = TRUE;
617
					break;
618
				case BELLE_SDP_RTCP_FB_NONE:
619 620
					stream->rtcp_fb.generic_nack_enabled = TRUE;
					break;
621 622 623 624 625
				default:
					break;
			}
			break;
		case BELLE_SDP_RTCP_FB_TRR_INT:
626
			avpf_params.trr_interval = belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute);
627
			break;
628 629 630 631 632
		case BELLE_SDP_RTCP_FB_CCM:
			switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
				case BELLE_SDP_RTCP_FB_FIR:
					avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR;
					break;
633 634 635
				case BELLE_SDP_RTCP_FB_TMMBR:
					stream->rtcp_fb.tmmbr_enabled = TRUE;
					break;
636 637 638 639
				default:
					break;
			}
			break;
640 641 642 643 644 645
		default:
			break;
	}
	payload_type_set_avpf_params(pt, avpf_params);
}

646
static bool_t sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
647 648 649
	belle_sip_list_t *it;
	belle_sdp_attribute_t *attribute;
	belle_sdp_rtcp_fb_attribute_t *fb_attribute;
650
	bctbx_list_t *pt_it;
651 652
	PayloadType *pt;
	int8_t pt_num;
653
	bool_t retval = FALSE;
654

655 656 657 658 659 660 661 662
	/* Handle rtcp-fb attributes that concern all payload types. */
	for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
		attribute = BELLE_SDP_ATTRIBUTE(it->data);
		if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
			fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
			if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) {
				for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
					pt = (PayloadType *)pt_it->data;
663
					apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
664
					retval = TRUE;
665 666 667 668 669 670 671 672 673 674 675 676 677
				}
			}
		}
	}

	/* Handle rtcp-fb attributes that are specefic to a payload type. */
	for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
		attribute = BELLE_SDP_ATTRIBUTE(it->data);
		if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
			fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
			pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute);
			for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
				pt = (PayloadType *)pt_it->data;
678
				retval = TRUE;
679
				if (payload_type_get_number(pt) == (int)pt_num) {
680
					apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
681 682 683 684
				}
			}
		}
	}
685
	return retval;
686 687
}

688 689 690 691
static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) {
	config->enabled = FALSE;
	config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone;
	config->rcvr_rtt_max_size = -1;
692
	config->stat_summary_flags = OrtpRtcpXrStatSummaryNone;
693
	config->voip_metrics_enabled = FALSE;
694 695
}

696
static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute, OrtpRtcpXrConfiguration *config) {
697
	if (attribute != NULL) {
698 699 700 701 702
		const belle_sdp_rtcp_xr_attribute_t *xr_attr;
		const char *rcvr_rtt_mode;
		sal_init_rtcp_xr_description(config);
		xr_attr = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute);
		rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(xr_attr);
703 704
		if (rcvr_rtt_mode != NULL) {
			if (strcasecmp(rcvr_rtt_mode, "all") == 0) {
705
				config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll;
706
			} else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) {
707
				config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender;
708
			}
709
			config->rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(xr_attr);
710
		}
711 712
		config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0);
		if (config->stat_summary_enabled) {
Simon Morlat's avatar
Simon Morlat committed
713
			const belle_sip_list_t *stat_summary_flag_it;
714 715 716
			for (stat_summary_flag_it = belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(xr_attr); stat_summary_flag_it != NULL; stat_summary_flag_it = stat_summary_flag_it->next ) {
				const char *flag = (const char *)stat_summary_flag_it->data;
				if (flag != NULL) {
717 718 719 720 721
					if (strcasecmp(flag, "loss") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryLoss;
					else if (strcasecmp(flag, "dup") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryDup;
					else if (strcasecmp(flag, "jitt") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryJitt;
					else if (strcasecmp(flag, "TTL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryTTL;
					else if (strcasecmp(flag, "HL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryHL;
722 723 724
				}
			}
		}
725 726
		config->voip_metrics_enabled = (belle_sdp_rtcp_xr_attribute_has_voip_metrics(xr_attr) != 0);
		config->enabled = TRUE;
727 728 729
	}
}

730
static void sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t *session_desc, OrtpRtcpXrConfiguration *config) {
731
	const belle_sdp_attribute_t *attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr");
732
	sdp_parse_rtcp_xr_parameters(attribute, config);
733 734
}

735
static void sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t *media_desc, OrtpRtcpXrConfiguration *config) {
736
	const belle_sdp_attribute_t *attribute = belle_sdp_media_description_get_attribute(media_desc, "rtcp-xr");
737
	sdp_parse_rtcp_xr_parameters(attribute, config);
738 739
}

740 741 742 743
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;
744
	belle_sdp_attribute_t* attribute;
745
	belle_sip_list_t *custom_attribute_it;
746 747
	const char* value;
	const char *mtype,*proto;
748
	bool_t has_avpf_attributes;
749

750
	stream=&md->streams[md->nb_streams];
751 752 753
	media=belle_sdp_media_description_get_media ( media_desc );

	proto = belle_sdp_media_get_protocol ( media );
754
	stream->proto=SalProtoOther;
755
	if ( proto ) {
756 757 758 759 760 761 762 763
		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;
johan's avatar
johan committed
764 765 766 767
		} else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) {
			stream->proto = SalProtoUdpTlsRtpSavp;
		} else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) {
			stream->proto = SalProtoUdpTlsRtpSavpf;
768
		} else {
769
			strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1);
770 771 772
		}
	}
	if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
773
		strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 );
jehan's avatar
jehan committed
774
		stream->ttl=belle_sdp_connection_get_ttl(cnx);
775 776 777 778 779 780 781 782 783
	}

	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;
784 785
	} else if ( strcasecmp ( "text", mtype ) == 0 ) {
		stream->type=SalText;
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
	} 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*/
	}

807
	stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp-mux") != NULL;
808

809 810 811 812 813 814 815
	/* 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");
816
	if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){
817 818 819 820 821
		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) {
822
			strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1);
823 824 825 826 827
		} else {
			ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb);
		}
	}

johan's avatar
johan committed
828 829 830 831 832 833 834 835 836 837 838
	/* 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;
			}
839 840 841
		}
		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));
johan's avatar
johan committed
842 843 844
		}
	}

845
	/* Read crypto lines if any */
846
	if (sal_stream_description_has_srtp(stream)) {
847 848 849
		sdp_parse_media_crypto_parameters(media_desc, stream);
	}

johan's avatar
johan committed
850 851 852 853 854 855 856 857
	/* Read zrtp-hash attribute */
	if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"zrtp-hash"))!=NULL) {
		if ((value=belle_sdp_attribute_get_value(attribute))!=NULL) {
			strncpy((char *)(stream->zrtphash), belle_sdp_attribute_get_value(attribute),sizeof(stream->zrtphash));
			stream->haveZrtpHash = 1;
		}
	}

858
	/* Get ICE candidate attributes if any */
859
	sdp_parse_media_ice_parameters(media_desc, stream);
860

861
	has_avpf_attributes = sdp_parse_rtcp_fb_parameters(media_desc, stream);
862

863
	/* Get RTCP-FB attributes if any */
864
	if (sal_stream_description_has_avpf(stream)) {
865
		enable_avpf_for_stream(stream);
866 867 868
	}else if (has_avpf_attributes ){
		enable_avpf_for_stream(stream);
		stream->implicit_rtcp_fb = TRUE;
869 870
	}

871
	/* Get RTCP-XR attributes if any */
872
	stream->rtcp_xr = md->rtcp_xr;	// Use session parameters if no stream parameters are defined
873
	sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr);
874

875 876 877 878 879 880
	/* Get the custom attributes */
	for (custom_attribute_it = belle_sdp_media_description_get_attributes(media_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) {
		belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data;
		stream->custom_sdp_attributes = sal_custom_sdp_attribute_append(stream->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr));
	}

881
	md->nb_streams++;
882 883 884 885
	return stream;
}


886
int sdp_to_media_description ( belle_sdp_session_description_t  *session_desc, SalMediaDescription *desc ) {
jehan's avatar
jehan committed
887 888 889
	belle_sdp_connection_t* cnx;
	belle_sip_list_t* media_desc_it;
	belle_sdp_media_description_t* media_desc;
890
	belle_sdp_session_name_t *sname;
891
	belle_sip_list_t *custom_attribute_it;
892
	const char* value;
893 894
	SalDtlsRole session_role=SalDtlsRoleInvalid;
	int i;
895

896
	desc->nb_streams = 0;
897
	desc->dir = SalStreamSendRecv;
jehan's avatar
jehan committed
898

899
	if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
900
		strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) -1  );
jehan's avatar
jehan committed
901
	}
902
	if ( (sname=belle_sdp_session_description_get_session_name(session_desc)) && belle_sdp_session_name_get_value(sname) ){
903
		strncpy(desc->name,belle_sdp_session_name_get_value(sname),sizeof(desc->name) - 1);
904
	}
905

906 907
	if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) {
		desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" );
jehan's avatar
jehan committed
908
	}
909

910
	/*in some very rare case, session attribute may set stream dir*/
911
	if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) {
912
		desc->dir=SalStreamSendRecv;
913
	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) {
914
		desc->dir=SalStreamSendOnly;
915
	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) {
916
		desc->dir=SalStreamRecvOnly;
917
	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) {
918
		desc->dir=SalStreamInactive;
919 920
	}

921
	/*DTLS attributes can be defined at session level.*/
johan's avatar
johan committed
922 923 924
	value=belle_sdp_session_description_get_attribute_value(session_desc,"setup");
	if (value){
		if (strncmp(value, "actpass", 7) == 0) {
925
			session_role = SalDtlsRoleUnset;
johan's avatar
johan committed
926
		} else if (strncmp(value, "active", 6) == 0) {
927
			session_role = SalDtlsRoleIsClient;
johan's avatar
johan committed
928
		} else if (strncmp(value, "passive", 7) == 0) {
929 930
			session_role = SalDtlsRoleIsServer;
		}
johan's avatar
johan committed
931
	}
932 933 934 935 936 937 938
	value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint");
	/*copy dtls attributes to every streams, might be overwritten stream by stream*/
	for (i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;i++) {
		if (value)
			strncpy(desc->streams[i].dtls_fingerprint, value, sizeof(desc->streams[i].dtls_fingerprint));
		desc->streams[i].dtls_role=session_role; /*set or reset value*/
	}
939

940 941
	/* Get ICE remote ufrag and remote pwd, and ice_lite flag */
	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag");
942
	if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1);
943

944
	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd");
945
	if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)-1);
946

947 948
	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite");
	if (value) desc->ice_lite = TRUE;
949 950 951 952

	/* Get session RTCP-XR attributes if any */
	sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr);

953 954 955 956 957 958
	/* Get the custom attributes */
	for (custom_attribute_it = belle_sdp_session_description_get_attributes(session_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) {
		belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data;
		desc->custom_sdp_attributes = sal_custom_sdp_attribute_append(desc->custom_sdp_attributes, belle_sdp_attribute_get_name(attr), belle_sdp_attribute_get_value(attr));
	}

959 960 961
	for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc )
						; media_desc_it!=NULL
			; media_desc_it=media_desc_it->next ) {
962 963
		if (desc->nb_streams==SAL_MEDIA_DESCRIPTION_MAX_STREAMS){
			ms_warning("Cannot convert mline at position [%i] from SDP to SalMediaDescription",desc->nb_streams);
964 965
			break;
		}
966
		media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data );
967
		sdp_to_stream_description(desc, media_desc);
jehan's avatar
jehan committed
968 969 970
	}
	return 0;
}