1 
2 /*
3 linphone
4 Copyright (C) 2012  Belledonne Communications, Grenoble, France
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 #include "sal_impl.h"
21 #define keywordcmp(key,b) strncmp(key,b,sizeof(key))
22 
23 
add_ice_candidates(belle_sdp_media_description_t * md,const SalStreamDescription * desc)24 static void add_ice_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
25 	char buffer[1024];
26 	const SalIceCandidate *candidate;
27 	int nb;
28 	int i;
29 
30 	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; i++) {
31 		candidate = &desc->ice_candidates[i];
32 		if ((candidate->addr[0] == '\0') || (candidate->port == 0)) break;
33 		nb = snprintf(buffer, sizeof(buffer), "%s %u UDP %u %s %d typ %s",
34 			candidate->foundation, candidate->componentID, candidate->priority, candidate->addr, candidate->port, candidate->type);
35 		if (nb < 0) {
36 			ms_error("Cannot add ICE candidate attribute!");
37 			return;
38 		}
39 		if (candidate->raddr[0] != '\0') {
40 			nb = snprintf(buffer + nb, sizeof(buffer) - nb, " raddr %s rport %d", candidate->raddr, candidate->rport);
41 			if (nb < 0) {
42 				ms_error("Cannot add ICE candidate attribute!");
43 				return;
44 			}
45 		}
46 		belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("candidate",buffer));
47 	}
48 }
49 
add_ice_remote_candidates(belle_sdp_media_description_t * md,const SalStreamDescription * desc)50 static void add_ice_remote_candidates(belle_sdp_media_description_t *md, const SalStreamDescription *desc){
51 	char buffer[1024];
52 	char *ptr = buffer;
53 	const SalIceRemoteCandidate *candidate;
54 	int offset = 0;
55 	int i;
56 
57 	buffer[0] = '\0';
58 	for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; i++) {
59 		candidate = &desc->ice_remote_candidates[i];
60 		if ((candidate->addr[0] != '\0') && (candidate->port != 0)) {
61 			offset = snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s%d %s %d", (i > 0) ? " " : "", i + 1, candidate->addr, candidate->port);
62 			if (offset < 0) {
63 				ms_error("Cannot add ICE remote-candidates attribute!");
64 				return;
65 			}
66 			ptr += offset;
67 		}
68 	}
69 	if (buffer[0] != '\0') belle_sdp_media_description_add_attribute(md,belle_sdp_attribute_create("remote-candidates",buffer));
70 }
71 
is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription * stream,uint16_t * trr_int)72 static bool_t is_rtcp_fb_trr_int_the_same_for_all_payloads(const SalStreamDescription *stream, uint16_t *trr_int) {
73 	bctbx_list_t *pt_it;
74 	bool_t first = TRUE;
75 	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
76 		PayloadType *pt = (PayloadType *)pt_it->data;
77 		if (payload_type_get_flags(pt) & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
78 			if (first == TRUE) {
79 				*trr_int = payload_type_get_avpf_params(pt).trr_interval;
80 				first = FALSE;
81 			} else if (payload_type_get_avpf_params(pt).trr_interval != *trr_int) {
82 				return FALSE;
83 			}
84 		}
85 	}
86 	return TRUE;
87 }
88 
add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t * media_desc,int8_t id,uint16_t trr_int)89 static void add_rtcp_fb_trr_int_attribute(belle_sdp_media_description_t *media_desc, int8_t id, uint16_t trr_int) {
90 	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
91 	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
92 	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_TRR_INT);
93 	belle_sdp_rtcp_fb_attribute_set_trr_int(attribute, trr_int);
94 	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
95 }
96 
add_rtcp_fb_ack_attribute(belle_sdp_media_description_t * media_desc,int8_t id,belle_sdp_rtcp_fb_val_param_t param)97 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) {
98 	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
99 	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
100 	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_ACK);
101 	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
102 	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
103 }
104 
add_rtcp_fb_nack_attribute(belle_sdp_media_description_t * media_desc,int8_t id,belle_sdp_rtcp_fb_val_param_t param)105 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) {
106 	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
107 	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
108 	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_NACK);
109 	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
110 	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
111 }
112 
add_rtcp_fb_ccm_attribute(belle_sdp_media_description_t * media_desc,int8_t id,belle_sdp_rtcp_fb_val_param_t param)113 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) {
114 	belle_sdp_rtcp_fb_attribute_t *attribute = belle_sdp_rtcp_fb_attribute_new();
115 	belle_sdp_rtcp_fb_attribute_set_id(attribute, id);
116 	belle_sdp_rtcp_fb_attribute_set_type(attribute, BELLE_SDP_RTCP_FB_CCM);
117 	belle_sdp_rtcp_fb_attribute_set_param(attribute, param);
118 	belle_sdp_media_description_add_attribute(media_desc, BELLE_SDP_ATTRIBUTE(attribute));
119 }
120 
add_rtcp_fb_attributes(belle_sdp_media_description_t * media_desc,const SalMediaDescription * md,const SalStreamDescription * stream)121 static void add_rtcp_fb_attributes(belle_sdp_media_description_t *media_desc, const SalMediaDescription *md, const SalStreamDescription *stream) {
122 	bctbx_list_t *pt_it;
123 	PayloadType *pt;
124 	PayloadTypeAvpfParams avpf_params;
125 	bool_t general_trr_int;
126 	uint16_t trr_int = 0;
127 
128 	general_trr_int = is_rtcp_fb_trr_int_the_same_for_all_payloads(stream, &trr_int);
129 	if (general_trr_int == TRUE && trr_int != 0) {
130 		add_rtcp_fb_trr_int_attribute(media_desc, -1, trr_int);
131 	}
132 	if (stream->rtcp_fb.generic_nack_enabled == TRUE) {
133 		add_rtcp_fb_nack_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_NONE);
134 	}
135 	if (stream->rtcp_fb.tmmbr_enabled == TRUE) {
136 		add_rtcp_fb_ccm_attribute(media_desc, -1, BELLE_SDP_RTCP_FB_TMMBR);
137 	}
138 
139 	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
140 		pt = (PayloadType *)pt_it->data;
141 
142 		/* AVPF/SAVPF profile is used so enable AVPF for all payload types. */
143 		payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
144 		avpf_params = payload_type_get_avpf_params(pt);
145 
146 		/* Add trr-int if not set generally. */
147 		if (general_trr_int != TRUE && trr_int != 0) {
148 			add_rtcp_fb_trr_int_attribute(media_desc, payload_type_get_number(pt), avpf_params.trr_interval);
149 		}
150 
151 		/* Add rtcp-fb attributes according to the AVPF features of the payload types. */
152 		if (avpf_params.features & PAYLOAD_TYPE_AVPF_PLI) {
153 			add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_PLI);
154 		}
155 		if (avpf_params.features & PAYLOAD_TYPE_AVPF_SLI) {
156 			add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_SLI);
157 		}
158 		if (avpf_params.features & PAYLOAD_TYPE_AVPF_RPSI) {
159 			if (avpf_params.rpsi_compatibility == TRUE) {
160 				add_rtcp_fb_nack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
161 			} else {
162 				add_rtcp_fb_ack_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_RPSI);
163 			}
164 		}
165 		if (avpf_params.features & PAYLOAD_TYPE_AVPF_FIR) {
166 			add_rtcp_fb_ccm_attribute(media_desc, payload_type_get_number(pt), BELLE_SDP_RTCP_FB_FIR);
167 		}
168 	}
169 }
170 
create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration * config)171 static belle_sdp_attribute_t * create_rtcp_xr_attribute(const OrtpRtcpXrConfiguration *config) {
172 	belle_sdp_rtcp_xr_attribute_t *attribute = belle_sdp_rtcp_xr_attribute_new();
173 	if (config->rcvr_rtt_mode != OrtpRtcpXrRcvrRttNone) {
174 		if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttAll) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "all");
175 		else if (config->rcvr_rtt_mode == OrtpRtcpXrRcvrRttSender) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(attribute, "sender");
176 		belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(attribute, config->rcvr_rtt_max_size);
177 	}
178 	belle_sdp_rtcp_xr_attribute_set_stat_summary(attribute, (config->stat_summary_enabled == TRUE));
179 	if (config->stat_summary_enabled == TRUE) {
180 		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryLoss) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "loss");
181 		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryDup) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "dup");
182 		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryJitt) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "jitt");
183 		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryTTL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "TTL");
184 		if (config->stat_summary_flags & OrtpRtcpXrStatSummaryHL) belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(attribute, "HL");
185 	}
186 	belle_sdp_rtcp_xr_attribute_set_voip_metrics(attribute, (config->voip_metrics_enabled == TRUE));
187 	return BELLE_SDP_ATTRIBUTE(attribute);
188 }
189 
stream_description_to_sdp(belle_sdp_session_description_t * session_desc,const SalMediaDescription * md,const SalStreamDescription * stream)190 static void stream_description_to_sdp ( belle_sdp_session_description_t *session_desc, const SalMediaDescription *md, const SalStreamDescription *stream ) {
191 	belle_sdp_mime_parameter_t* mime_param;
192 	belle_sdp_media_description_t* media_desc;
193 	int j;
194 	bctbx_list_t* pt_it;
195 	PayloadType* pt;
196 	char buffer[1024];
197 	char* dir=NULL;
198 	const char *rtp_addr;
199 	const char *rtcp_addr;
200 	int rtp_port;
201 	int rtcp_port;
202 	bool_t different_rtp_and_rtcp_addr;
203 
204 	rtp_addr=stream->rtp_addr;
205 	rtcp_addr=stream->rtcp_addr;
206 	rtp_port=stream->rtp_port;
207 	rtcp_port=stream->rtcp_port;
208 
209 	media_desc = belle_sdp_media_description_create ( sal_stream_description_get_type_as_string(stream)
210 				 ,stream->rtp_port
211 				 ,1
212 				 ,sal_media_proto_to_string ( stream->proto )
213 				 ,NULL );
214 	if (stream->payloads) {
215 		for ( pt_it=stream->payloads; pt_it!=NULL; pt_it=pt_it->next ) {
216 			pt= ( PayloadType* ) pt_it->data;
217 			mime_param= belle_sdp_mime_parameter_create ( pt->mime_type
218 					, payload_type_get_number ( pt )
219 					, pt->clock_rate
220 					, pt->channels>0 ? pt->channels : -1 );
221 			belle_sdp_mime_parameter_set_parameters ( mime_param,pt->recv_fmtp );
222 			if ( stream->ptime>0 ) {
223 				belle_sdp_mime_parameter_set_ptime ( mime_param,stream->ptime );
224 			}
225 			belle_sdp_media_description_append_values_from_mime_parameter ( media_desc,mime_param );
226 			belle_sip_object_unref ( mime_param );
227 		}
228 	} else {
229 		/* to comply with SDP we cannot have an empty payload type number list */
230 		/* as it happens only when mline is declined with a zero port, it does not matter to put whatever codec*/
231 		belle_sip_list_t* format = belle_sip_list_append(NULL,0);
232 		belle_sdp_media_set_media_formats(belle_sdp_media_description_get_media(media_desc),format);
233 	}
234 	/*only add a c= line within the stream description if address are differents*/
235 	if (rtp_addr[0]!='\0' && strcmp(rtp_addr,md->addr)!=0){
236 		bool_t inet6;
237 		belle_sdp_connection_t *connection;
238 		if (strchr(rtp_addr,':')!=NULL){
239 			inet6=TRUE;
240 		}else inet6=FALSE;
241 		connection = belle_sdp_connection_create("IN", inet6 ? "IP6" : "IP4", rtp_addr);
242 		if (ms_is_multicast(rtp_addr)) {
243 			/*remove session cline in case of multicast*/
244 			belle_sdp_session_description_set_connection(session_desc,NULL);
245 			if (inet6 == FALSE)
246 				belle_sdp_connection_set_ttl(connection,stream->ttl);
247 		}
248 		belle_sdp_media_description_set_connection(media_desc,connection);
249 	}
250 
251 	if ( stream->bandwidth>0 )
252 		belle_sdp_media_description_set_bandwidth ( media_desc,"AS",stream->bandwidth );
253 
254 	if (sal_stream_description_has_srtp(stream)) {
255 		/* add crypto lines */
256 		for ( j=0; j<SAL_CRYPTO_ALGO_MAX; j++ ) {
257 			MSCryptoSuiteNameParams desc;
258 			if (ms_crypto_suite_to_name_params(stream->crypto[j].algo,&desc)==0){
259 				if (desc.params)
260 					snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s %s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key,desc.params);
261 				else
262 					snprintf ( buffer, sizeof ( buffer )-1, "%d %s inline:%s", stream->crypto[j].tag, desc.name, stream->crypto[j].master_key );
263 
264 				belle_sdp_media_description_add_attribute( media_desc,belle_sdp_attribute_create ("crypto", buffer));
265 			}else break;
266 		}
267 	}
268 
269 	/* insert DTLS session attribute if needed */
270 	if ((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp)) {
271 		char* ssrc_attribute = ms_strdup_printf("%u cname:%s",stream->rtp_ssrc,stream->rtcp_cname);
272 		if ((stream->dtls_role != SalDtlsRoleInvalid) && (strlen(stream->dtls_fingerprint)>0)) {
273 			switch(stream->dtls_role) {
274 				case SalDtlsRoleIsClient:
275 					belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","active"));
276 					break;
277 				case SalDtlsRoleIsServer:
278 					belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","passive"));
279 					break;
280 				case SalDtlsRoleUnset:
281 				default:
282 					belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("setup","actpass"));
283 					break;
284 			}
285 			belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint));
286 		}
287 		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute));
288 		ms_free(ssrc_attribute);
289 	}
290 
291 	/* insert zrtp-hash attribute if needed */
292 	if (stream->haveZrtpHash == 1) {
293 		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("zrtp-hash", (const char *)(stream->zrtphash)));
294 	}
295 
296 	switch ( stream->dir ) {
297 		case SalStreamSendRecv:
298 			/*dir="sendrecv";*/
299 			dir=NULL;
300 			break;
301 		case SalStreamRecvOnly:
302 			dir="recvonly";
303 			break;
304 		case SalStreamSendOnly:
305 			dir="sendonly";
306 			break;
307 		case SalStreamInactive:
308 			dir="inactive";
309 			break;
310 	}
311 	if ( dir ) belle_sdp_media_description_add_attribute ( media_desc,belle_sdp_attribute_create ( dir,NULL ) );
312 
313 	if (stream->rtcp_mux){
314 		belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create ("rtcp-mux",NULL ) );
315 	}
316 
317 	if (rtp_port != 0) {
318 		different_rtp_and_rtcp_addr = (rtcp_addr[0] != '\0') && (strcmp(rtp_addr, rtcp_addr) != 0);
319 		if ((rtcp_port != (rtp_port + 1)) || (different_rtp_and_rtcp_addr == TRUE)) {
320 			if (different_rtp_and_rtcp_addr == TRUE) {
321 				snprintf(buffer, sizeof(buffer), "%u IN IP4 %s", rtcp_port, rtcp_addr);
322 			} else {
323 				snprintf(buffer, sizeof(buffer), "%u",rtcp_port);
324 			}
325 			belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("rtcp",buffer));
326 		}
327 	}
328 	if (stream->set_nortpproxy == TRUE) {
329 		belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("nortpproxy","yes"));
330 	}
331 	if (stream->ice_mismatch == TRUE) {
332 		belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-mismatch",NULL));
333 	} else {
334 		if (rtp_port != 0) {
335 			if (stream->ice_pwd[0] != '\0')
336 				belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-pwd",stream->ice_pwd));
337 			if (stream->ice_ufrag[0] != '\0')
338 				belle_sdp_media_description_add_attribute(media_desc,belle_sdp_attribute_create ("ice-ufrag",stream->ice_ufrag));
339 			add_ice_candidates(media_desc,stream);
340 			add_ice_remote_candidates(media_desc,stream);
341 		}
342 	}
343 
344 	if ((rtp_port != 0) && (sal_stream_description_has_avpf(stream) || sal_stream_description_has_implicit_avpf(stream))) {
345 		add_rtcp_fb_attributes(media_desc, md, stream);
346 	}
347 
348 	if ((rtp_port != 0) && (stream->rtcp_xr.enabled == TRUE)) {
349 		char sastr[1024] = {0};
350 		char mastr[1024] = {0};
351 		size_t saoff = 0;
352 		size_t maoff = 0;
353 		const belle_sdp_attribute_t *session_attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr");
354 		belle_sdp_attribute_t *media_attribute;
355 		if (session_attribute != NULL) {
356 			belle_sip_object_marshal((belle_sip_object_t*)session_attribute, sastr, sizeof(sastr), &saoff);
357 		}
358 		media_attribute = create_rtcp_xr_attribute(&stream->rtcp_xr);
359 		if (media_attribute != NULL) {
360 			belle_sip_object_marshal((belle_sip_object_t*)media_attribute, mastr, sizeof(mastr), &maoff);
361 		}
362 		if (strcmp(sastr, mastr) != 0) {
363 			belle_sdp_media_description_add_attribute(media_desc, media_attribute);
364 		} else {
365 			belle_sip_object_unref((belle_sip_object_t*)media_attribute);
366 		}
367 	}
368 
369 	if (stream->custom_sdp_attributes) {
370 		belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)stream->custom_sdp_attributes;
371 		belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc);
372 		belle_sip_list_t *elem;
373 		for (elem = l; elem != NULL; elem = elem->next) {
374 			belle_sdp_media_description_add_attribute(media_desc, (belle_sdp_attribute_t *)elem->data);
375 		}
376 	}
377 
378 	/*
379 	 * rfc5576
380 	 * 4.1.  The "ssrc" Media Attribute
381 	 * <ssrc-id> is the synchronization source (SSRC) ID of the
382 	 * source being described, interpreted as a 32-bit unsigned integer in
383 	 * network byte order and represented in decimal.*/
384 
385 
386 	belle_sdp_session_description_add_media_description(session_desc, media_desc);
387 }
388 
media_description_to_sdp(const SalMediaDescription * desc)389 belle_sdp_session_description_t * media_description_to_sdp ( const SalMediaDescription *desc ) {
390 	belle_sdp_session_description_t* session_desc=belle_sdp_session_description_new();
391 	bool_t inet6;
392 	belle_sdp_origin_t* origin;
393 	int i;
394 
395 	if ( strchr ( desc->addr,':' ) !=NULL ) {
396 		inet6=1;
397 	} else inet6=0;
398 	belle_sdp_session_description_set_version ( session_desc,belle_sdp_version_create ( 0 ) );
399 
400 	origin = belle_sdp_origin_create ( desc->username
401 									  ,desc->session_id
402 									  ,desc->session_ver
403 									  ,"IN"
404 									  , inet6 ? "IP6" :"IP4"
405 									  ,desc->addr );
406 
407 	belle_sdp_session_description_set_origin ( session_desc,origin );
408 
409 	belle_sdp_session_description_set_session_name ( session_desc,
410 		belle_sdp_session_name_create ( desc->name[0]!='\0' ? desc->name : "Talk" ) );
411 
412 	if ( !sal_media_description_has_dir ( desc,SalStreamInactive ) || desc->ice_ufrag[0] != '\0' ) {
413 		/*in case of sendonly, setting of the IP on cnx we give a chance to receive stun packets*/
414 		belle_sdp_session_description_set_connection ( session_desc
415 				,belle_sdp_connection_create ( "IN",inet6 ? "IP6" :"IP4",desc->addr ) );
416 
417 	} else 	{
418 		belle_sdp_session_description_set_connection ( session_desc
419 				,belle_sdp_connection_create ( "IN"
420 								,inet6 ? "IP6" :"IP4"
421 								,inet6 ? "::0" :"0.0.0.0" ) );
422 
423 	}
424 
425 	belle_sdp_session_description_set_time_description ( session_desc,belle_sdp_time_description_create ( 0,0 ) );
426 
427 	if ( desc->bandwidth>0 ) {
428 		belle_sdp_session_description_set_bandwidth ( session_desc,"AS",desc->bandwidth );
429 	}
430 
431 	if (desc->set_nortpproxy == TRUE) belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("nortpproxy","yes"));
432 	if (desc->ice_pwd[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-pwd",desc->ice_pwd));
433 	if (desc->ice_ufrag[0] != '\0') belle_sdp_session_description_add_attribute(session_desc, belle_sdp_attribute_create("ice-ufrag",desc->ice_ufrag));
434 
435 	if (desc->rtcp_xr.enabled == TRUE) {
436 		belle_sdp_session_description_add_attribute(session_desc, create_rtcp_xr_attribute(&desc->rtcp_xr));
437 	}
438 	if (desc->custom_sdp_attributes) {
439 		belle_sdp_session_description_t *custom_desc = (belle_sdp_session_description_t *)desc->custom_sdp_attributes;
440 		belle_sip_list_t *l = belle_sdp_session_description_get_attributes(custom_desc);
441 		belle_sip_list_t *elem;
442 		for (elem = l; elem != NULL; elem = elem->next) {
443 			belle_sdp_session_description_add_attribute(session_desc, (belle_sdp_attribute_t *)elem->data);
444 		}
445 	}
446 
447 	for ( i=0; i<desc->nb_streams; i++ ) {
448 		stream_description_to_sdp(session_desc, desc, &desc->streams[i]);
449 	}
450 	return session_desc;
451 }
452 
453 
sdp_parse_payload_types(belle_sdp_media_description_t * media_desc,SalStreamDescription * stream)454 static void sdp_parse_payload_types(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
455 	PayloadType *pt;
456 	PayloadTypeAvpfParams avpf_params;
457 	belle_sip_list_t* mime_param_it=NULL;
458 	belle_sdp_mime_parameter_t* mime_param;
459 	belle_sip_list_t* mime_params=belle_sdp_media_description_build_mime_parameters ( media_desc );
460 	memset(&avpf_params, 0, sizeof(avpf_params));
461 	for ( mime_param_it=mime_params
462 						; mime_param_it!=NULL
463 			; mime_param_it=mime_param_it->next ) {
464 		mime_param=BELLE_SDP_MIME_PARAMETER ( mime_param_it->data );
465 
466 		pt=payload_type_new();
467 		payload_type_set_number ( pt,belle_sdp_mime_parameter_get_media_format ( mime_param ) );
468 		pt->clock_rate=belle_sdp_mime_parameter_get_rate ( mime_param );
469 		pt->mime_type=ms_strdup ( belle_sdp_mime_parameter_get_type ( mime_param ) );
470 		pt->channels=belle_sdp_mime_parameter_get_channel_count ( mime_param );
471 		payload_type_set_send_fmtp ( pt,belle_sdp_mime_parameter_get_parameters ( mime_param ) );
472 		payload_type_set_avpf_params(pt, avpf_params);
473 		stream->payloads=bctbx_list_append ( stream->payloads,pt );
474 		stream->ptime=belle_sdp_mime_parameter_get_ptime ( mime_param );
475 		ms_message ( "Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate,
476 						pt->send_fmtp ? pt->send_fmtp : "" );
477 	}
478 	if ( mime_params ) belle_sip_list_free_with_data ( mime_params,belle_sip_object_unref );
479 }
480 
sdp_parse_media_crypto_parameters(belle_sdp_media_description_t * media_desc,SalStreamDescription * stream)481 static void sdp_parse_media_crypto_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
482 	belle_sip_list_t *attribute_it;
483 	belle_sdp_attribute_t *attribute;
484 	char tmp[256], tmp2[256], parameters[256]={0};
485 	int valid_count = 0;
486 	int nb;
487 
488 	memset ( &stream->crypto, 0, sizeof ( stream->crypto ) );
489 	for ( attribute_it=belle_sdp_media_description_get_attributes ( media_desc )
490 						; valid_count < SAL_CRYPTO_ALGO_MAX && attribute_it!=NULL;
491 			attribute_it=attribute_it->next ) {
492 		attribute=BELLE_SDP_ATTRIBUTE ( attribute_it->data );
493 
494 		if ( keywordcmp ( "crypto",belle_sdp_attribute_get_name ( attribute ) ) ==0 && belle_sdp_attribute_get_value ( attribute ) !=NULL ) {
495 			nb = sscanf ( belle_sdp_attribute_get_value ( attribute ), "%d %256s inline:%256s %256s",
496 							&stream->crypto[valid_count].tag,
497 							tmp,
498 							tmp2, parameters );
499 
500 			if ( nb >= 3 ) {
501 				MSCryptoSuite cs;
502 				MSCryptoSuiteNameParams np;
503 
504 				np.name=tmp;
505 				np.params=parameters[0]!='\0' ? parameters : NULL;
506 				cs=ms_crypto_suite_build_from_name_params(&np);
507 				if (cs==MS_CRYPTO_SUITE_INVALID){
508 					ms_warning ( "Failed to parse crypto-algo: '%s'", tmp );
509 					stream->crypto[valid_count].algo = 0;
510 				}else{
511 					char *sep;
512 					strncpy ( stream->crypto[valid_count].master_key, tmp2, sizeof(stream->crypto[valid_count].master_key)-1 );
513 					sep=strchr(stream->crypto[valid_count].master_key,'|');
514 					if (sep) *sep='\0';
515 					stream->crypto[valid_count].algo = cs;
516 					ms_message ( "Found valid crypto line (tag:%d algo:'%s' key:'%s'",
517 									stream->crypto[valid_count].tag,
518 									tmp,
519 									stream->crypto[valid_count].master_key );
520 					valid_count++;
521 				}
522 			}else{
523 				ms_warning ( "sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value ( attribute ),nb );
524 			}
525 		}
526 	}
527 	ms_message("Found: %d valid crypto lines", valid_count );
528 }
529 
sdp_parse_media_ice_parameters(belle_sdp_media_description_t * media_desc,SalStreamDescription * stream)530 static void sdp_parse_media_ice_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
531 	belle_sip_list_t *attribute_it;
532 	belle_sdp_attribute_t *attribute;
533 	const char *att_name;
534 	const char *value;
535 	int nb_ice_candidates = 0;
536 
537 	for (attribute_it = belle_sdp_media_description_get_attributes(media_desc); attribute_it != NULL; attribute_it=attribute_it->next) {
538 		attribute=BELLE_SDP_ATTRIBUTE(attribute_it->data);
539 		att_name = belle_sdp_attribute_get_name(attribute);
540 		value = belle_sdp_attribute_get_value(attribute);
541 
542 		if ((nb_ice_candidates < (int)(sizeof(stream->ice_candidates)/sizeof(SalIceCandidate)))
543 				&& (keywordcmp("candidate", att_name) == 0)
544 				&& (value != NULL)) {
545 			SalIceCandidate *candidate = &stream->ice_candidates[nb_ice_candidates];
546 			char proto[4];
547 			int nb = sscanf(value, "%s %u %3s %u %s %d typ %s raddr %s rport %d",
548 				candidate->foundation, &candidate->componentID, proto, &candidate->priority, candidate->addr, &candidate->port,
549 				candidate->type, candidate->raddr, &candidate->rport);
550 			if (strcasecmp("udp",proto)==0 && ((nb == 7) || (nb == 9))) nb_ice_candidates++;
551 			else {
552 				ms_error("ice: Failed parsing a=candidate SDP attribute");
553 				memset(candidate, 0, sizeof(*candidate));
554 			}
555 		} else if ((keywordcmp("remote-candidates", att_name) == 0) && (value != NULL)) {
556 			SalIceRemoteCandidate candidate;
557 			unsigned int componentID;
558 			int offset;
559 			const char *ptr = value;
560 			const char *endptr = value + strlen(ptr);
561 			while (3 == sscanf(ptr, "%u %s %u%n", &componentID, candidate.addr, &candidate.port, &offset)) {
562 				if ((componentID > 0) && (componentID <= SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES)) {
563 					SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[componentID - 1];
564 					strncpy(remote_candidate->addr, candidate.addr, sizeof(remote_candidate->addr)-1);
565 					remote_candidate->port = candidate.port;
566 				}
567 				ptr += offset;
568 				if (ptr < endptr) {
569 					if (ptr[offset] == ' ') ptr += 1;
570 				} else break;
571 			}
572 		} else if ((keywordcmp("ice-ufrag", att_name) == 0) && (value != NULL)) {
573 			strncpy(stream->ice_ufrag, value, sizeof(stream->ice_ufrag)-1);
574 		} else if ((keywordcmp("ice-pwd", att_name) == 0) && (value != NULL)) {
575 			strncpy(stream->ice_pwd, value, sizeof(stream->ice_pwd) -1);
576 		} else if (keywordcmp("ice-mismatch", att_name) == 0) {
577 			stream->ice_mismatch = TRUE;
578 		}
579 	}
580 }
581 
enable_avpf_for_stream(SalStreamDescription * stream)582 static void enable_avpf_for_stream(SalStreamDescription *stream) {
583 	bctbx_list_t *pt_it;
584 	for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
585 		PayloadType *pt = (PayloadType *)pt_it->data;
586 		payload_type_set_flag(pt, PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED);
587 	}
588 }
589 
apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t * fb_attribute,SalStreamDescription * stream,PayloadType * pt)590 static void apply_rtcp_fb_attribute_to_payload(belle_sdp_rtcp_fb_attribute_t *fb_attribute, SalStreamDescription *stream, PayloadType *pt) {
591 	PayloadTypeAvpfParams avpf_params = payload_type_get_avpf_params(pt);
592 	switch (belle_sdp_rtcp_fb_attribute_get_type(fb_attribute)) {
593 		case BELLE_SDP_RTCP_FB_ACK:
594 			if (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute) == BELLE_SDP_RTCP_FB_RPSI) {
595 				avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
596 			}
597 			break;
598 		case BELLE_SDP_RTCP_FB_NACK:
599 			switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
600 				case BELLE_SDP_RTCP_FB_PLI:
601 					avpf_params.features |= PAYLOAD_TYPE_AVPF_PLI;
602 					break;
603 				case BELLE_SDP_RTCP_FB_SLI:
604 					avpf_params.features |= PAYLOAD_TYPE_AVPF_SLI;
605 					break;
606 				case BELLE_SDP_RTCP_FB_RPSI:
607 					/* Linphone uses positive feeback for RPSI. However first versions handling
608 					 * AVPF wrongly declared RPSI as negative feedback, so this is kept for compatibility
609 					 * with these versions but will probably be removed at some point in time. */
610 					avpf_params.features |= PAYLOAD_TYPE_AVPF_RPSI;
611 					avpf_params.rpsi_compatibility = TRUE;
612 					break;
613 				case BELLE_SDP_RTCP_FB_NONE:
614 					stream->rtcp_fb.generic_nack_enabled = TRUE;
615 					break;
616 				default:
617 					break;
618 			}
619 			break;
620 		case BELLE_SDP_RTCP_FB_TRR_INT:
621 			avpf_params.trr_interval = belle_sdp_rtcp_fb_attribute_get_trr_int(fb_attribute);
622 			break;
623 		case BELLE_SDP_RTCP_FB_CCM:
624 			switch (belle_sdp_rtcp_fb_attribute_get_param(fb_attribute)) {
625 				case BELLE_SDP_RTCP_FB_FIR:
626 					avpf_params.features |= PAYLOAD_TYPE_AVPF_FIR;
627 					break;
628 				case BELLE_SDP_RTCP_FB_TMMBR:
629 					stream->rtcp_fb.tmmbr_enabled = TRUE;
630 					break;
631 				default:
632 					break;
633 			}
634 			break;
635 		default:
636 			break;
637 	}
638 	payload_type_set_avpf_params(pt, avpf_params);
639 }
640 
sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t * media_desc,SalStreamDescription * stream)641 static bool_t sdp_parse_rtcp_fb_parameters(belle_sdp_media_description_t *media_desc, SalStreamDescription *stream) {
642 	belle_sip_list_t *it;
643 	belle_sdp_attribute_t *attribute;
644 	belle_sdp_rtcp_fb_attribute_t *fb_attribute;
645 	bctbx_list_t *pt_it;
646 	PayloadType *pt;
647 	int8_t pt_num;
648 	bool_t retval = FALSE;
649 
650 	/* Handle rtcp-fb attributes that concern all payload types. */
651 	for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
652 		attribute = BELLE_SDP_ATTRIBUTE(it->data);
653 		if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
654 			fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
655 			if (belle_sdp_rtcp_fb_attribute_get_id(fb_attribute) == -1) {
656 				for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
657 					pt = (PayloadType *)pt_it->data;
658 					apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
659 					retval = TRUE;
660 				}
661 			}
662 		}
663 	}
664 
665 	/* Handle rtcp-fb attributes that are specefic to a payload type. */
666 	for (it = belle_sdp_media_description_get_attributes(media_desc); it != NULL; it = it->next) {
667 		attribute = BELLE_SDP_ATTRIBUTE(it->data);
668 		if (keywordcmp("rtcp-fb", belle_sdp_attribute_get_name(attribute)) == 0) {
669 			fb_attribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute);
670 			pt_num = belle_sdp_rtcp_fb_attribute_get_id(fb_attribute);
671 			for (pt_it = stream->payloads; pt_it != NULL; pt_it = pt_it->next) {
672 				pt = (PayloadType *)pt_it->data;
673 				retval = TRUE;
674 				if (payload_type_get_number(pt) == (int)pt_num) {
675 					apply_rtcp_fb_attribute_to_payload(fb_attribute, stream, pt);
676 				}
677 			}
678 		}
679 	}
680 	return retval;
681 }
682 
sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration * config)683 static void sal_init_rtcp_xr_description(OrtpRtcpXrConfiguration *config) {
684 	config->enabled = FALSE;
685 	config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttNone;
686 	config->rcvr_rtt_max_size = -1;
687 	config->stat_summary_flags = 0;
688 	config->voip_metrics_enabled = FALSE;
689 }
690 
sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t * attribute,OrtpRtcpXrConfiguration * config)691 static void sdp_parse_rtcp_xr_parameters(const belle_sdp_attribute_t *attribute, OrtpRtcpXrConfiguration *config) {
692 	if (attribute != NULL) {
693 		const belle_sdp_rtcp_xr_attribute_t *xr_attr;
694 		const char *rcvr_rtt_mode;
695 		sal_init_rtcp_xr_description(config);
696 		xr_attr = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute);
697 		rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(xr_attr);
698 		if (rcvr_rtt_mode != NULL) {
699 			if (strcasecmp(rcvr_rtt_mode, "all") == 0) {
700 				config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttAll;
701 			} else if (strcasecmp(rcvr_rtt_mode, "sender") == 0) {
702 				config->rcvr_rtt_mode = OrtpRtcpXrRcvrRttSender;
703 			}
704 			config->rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(xr_attr);
705 		}
706 		config->stat_summary_enabled = (belle_sdp_rtcp_xr_attribute_has_stat_summary(xr_attr) != 0);
707 		if (config->stat_summary_enabled) {
708 			const belle_sip_list_t *stat_summary_flag_it;
709 			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 ) {
710 				const char *flag = (const char *)stat_summary_flag_it->data;
711 				if (flag != NULL) {
712 					if (strcasecmp(flag, "loss") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryLoss;
713 					else if (strcasecmp(flag, "dup") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryDup;
714 					else if (strcasecmp(flag, "jitt") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryJitt;
715 					else if (strcasecmp(flag, "TTL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryTTL;
716 					else if (strcasecmp(flag, "HL") == 0) config->stat_summary_flags |= OrtpRtcpXrStatSummaryHL;
717 				}
718 			}
719 		}
720 		config->voip_metrics_enabled = (belle_sdp_rtcp_xr_attribute_has_voip_metrics(xr_attr) != 0);
721 		config->enabled = TRUE;
722 	}
723 }
724 
sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t * session_desc,OrtpRtcpXrConfiguration * config)725 static void sdp_parse_session_rtcp_xr_parameters(belle_sdp_session_description_t *session_desc, OrtpRtcpXrConfiguration *config) {
726 	const belle_sdp_attribute_t *attribute = belle_sdp_session_description_get_attribute(session_desc, "rtcp-xr");
727 	sdp_parse_rtcp_xr_parameters(attribute, config);
728 }
729 
sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t * media_desc,OrtpRtcpXrConfiguration * config)730 static void sdp_parse_media_rtcp_xr_parameters(belle_sdp_media_description_t *media_desc, OrtpRtcpXrConfiguration *config) {
731 	const belle_sdp_attribute_t *attribute = belle_sdp_media_description_get_attribute(media_desc, "rtcp-xr");
732 	sdp_parse_rtcp_xr_parameters(attribute, config);
733 }
734 
sdp_to_stream_description(SalMediaDescription * md,belle_sdp_media_description_t * media_desc)735 static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, belle_sdp_media_description_t *media_desc) {
736 	SalStreamDescription *stream;
737 	belle_sdp_connection_t* cnx;
738 	belle_sdp_media_t* media;
739 	belle_sdp_attribute_t* attribute;
740 	belle_sip_list_t *custom_attribute_it;
741 	const char* value;
742 	const char *mtype,*proto;
743 	bool_t has_avpf_attributes;
744 
745 	stream=&md->streams[md->nb_streams];
746 	media=belle_sdp_media_description_get_media ( media_desc );
747 
748 	proto = belle_sdp_media_get_protocol ( media );
749 	stream->proto=SalProtoOther;
750 	if ( proto ) {
751 		if (strcasecmp(proto, "RTP/AVP") == 0) {
752 			stream->proto = SalProtoRtpAvp;
753 		} else if (strcasecmp(proto, "RTP/SAVP") == 0) {
754 			stream->proto = SalProtoRtpSavp;
755 		} else if (strcasecmp(proto, "RTP/AVPF") == 0) {
756 			stream->proto = SalProtoRtpAvpf;
757 		} else if (strcasecmp(proto, "RTP/SAVPF") == 0) {
758 			stream->proto = SalProtoRtpSavpf;
759 		} else if (strcasecmp(proto, "UDP/TLS/RTP/SAVP") == 0) {
760 			stream->proto = SalProtoUdpTlsRtpSavp;
761 		} else if (strcasecmp(proto, "UDP/TLS/RTP/SAVPF") == 0) {
762 			stream->proto = SalProtoUdpTlsRtpSavpf;
763 		} else {
764 			strncpy(stream->proto_other,proto,sizeof(stream->proto_other)-1);
765 		}
766 	}
767 	if ( ( cnx=belle_sdp_media_description_get_connection ( media_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
768 		strncpy ( stream->rtp_addr,belle_sdp_connection_get_address ( cnx ), sizeof ( stream->rtp_addr ) -1 );
769 		stream->ttl=belle_sdp_connection_get_ttl(cnx);
770 	}
771 
772 	stream->rtp_port=belle_sdp_media_get_media_port ( media );
773 
774 	mtype = belle_sdp_media_get_media_type ( media );
775 	if ( strcasecmp ( "audio", mtype ) == 0 ) {
776 		stream->type=SalAudio;
777 	} else if ( strcasecmp ( "video", mtype ) == 0 ) {
778 		stream->type=SalVideo;
779 	} else if ( strcasecmp ( "text", mtype ) == 0 ) {
780 		stream->type=SalText;
781 	} else {
782 		stream->type=SalOther;
783 		strncpy ( stream->typeother,mtype,sizeof ( stream->typeother )-1 );
784 	}
785 
786 	if ( belle_sdp_media_description_get_bandwidth ( media_desc,"AS" ) >0 ) {
787 		stream->bandwidth=belle_sdp_media_description_get_bandwidth ( media_desc,"AS" );
788 	}
789 
790 	if ( belle_sdp_media_description_get_attribute ( media_desc,"sendrecv" ) ) {
791 		stream->dir=SalStreamSendRecv;
792 	} else if ( belle_sdp_media_description_get_attribute ( media_desc,"sendonly" ) ) {
793 		stream->dir=SalStreamSendOnly;
794 	} else if ( belle_sdp_media_description_get_attribute ( media_desc,"recvonly" ) ) {
795 		stream->dir=SalStreamRecvOnly;
796 	} else if ( belle_sdp_media_description_get_attribute ( media_desc,"inactive" ) ) {
797 		stream->dir=SalStreamInactive;
798 	} else {
799 		stream->dir=md->dir; /*takes default value if not present*/
800 	}
801 
802 	stream->rtcp_mux = belle_sdp_media_description_get_attribute(media_desc, "rtcp-mux") != NULL;
803 
804 	/* Get media payload types */
805 	sdp_parse_payload_types(media_desc, stream);
806 
807 	/* Get media specific RTCP attribute */
808 	stream->rtcp_port = stream->rtp_port + 1;
809 	snprintf(stream->rtcp_addr, sizeof(stream->rtcp_addr), "%s", stream->rtp_addr);
810 	attribute=belle_sdp_media_description_get_attribute(media_desc,"rtcp");
811 	if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){
812 		char tmp[256];
813 		int nb = sscanf(value, "%d IN IP4 %s", &stream->rtcp_port, tmp);
814 		if (nb == 1) {
815 			/* SDP rtcp attribute only contains the port */
816 		} else if (nb == 2) {
817 			strncpy(stream->rtcp_addr, tmp, sizeof(stream->rtcp_addr)-1);
818 		} else {
819 			ms_warning("sdp has a strange a=rtcp line (%s) nb=%i", value, nb);
820 		}
821 	}
822 
823 	/* 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) */
824 	if (((stream->proto == SalProtoUdpTlsRtpSavpf) || (stream->proto == SalProtoUdpTlsRtpSavp))) {
825 		attribute=belle_sdp_media_description_get_attribute(media_desc,"setup");
826 		if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){
827 			if (strncmp(value, "actpass", 7) == 0) {
828 				stream->dtls_role = SalDtlsRoleUnset;
829 			} else if (strncmp(value, "active", 6) == 0) {
830 				stream->dtls_role = SalDtlsRoleIsClient;
831 			} else if (strncmp(value, "passive", 7) == 0) {
832 				stream->dtls_role = SalDtlsRoleIsServer;
833 			}
834 		}
835 		if (stream->dtls_role != SalDtlsRoleInvalid && (attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"))) {
836 			strncpy(stream->dtls_fingerprint, belle_sdp_attribute_get_value(attribute),sizeof(stream->dtls_fingerprint));
837 		}
838 	}
839 
840 	/* Read crypto lines if any */
841 	if (sal_stream_description_has_srtp(stream)) {
842 		sdp_parse_media_crypto_parameters(media_desc, stream);
843 	}
844 
845 	/* Read zrtp-hash attribute */
846 	if ((attribute=belle_sdp_media_description_get_attribute(media_desc,"zrtp-hash"))!=NULL) {
847 		if ((value=belle_sdp_attribute_get_value(attribute))!=NULL) {
848 			strncpy((char *)(stream->zrtphash), belle_sdp_attribute_get_value(attribute),sizeof(stream->zrtphash));
849 			stream->haveZrtpHash = 1;
850 		}
851 	}
852 
853 	/* Get ICE candidate attributes if any */
854 	sdp_parse_media_ice_parameters(media_desc, stream);
855 
856 	has_avpf_attributes = sdp_parse_rtcp_fb_parameters(media_desc, stream);
857 
858 	/* Get RTCP-FB attributes if any */
859 	if (sal_stream_description_has_avpf(stream)) {
860 		enable_avpf_for_stream(stream);
861 	}else if (has_avpf_attributes ){
862 		enable_avpf_for_stream(stream);
863 		stream->implicit_rtcp_fb = TRUE;
864 	}
865 
866 	/* Get RTCP-XR attributes if any */
867 	stream->rtcp_xr = md->rtcp_xr;	// Use session parameters if no stream parameters are defined
868 	sdp_parse_media_rtcp_xr_parameters(media_desc, &stream->rtcp_xr);
869 
870 	/* Get the custom attributes */
871 	for (custom_attribute_it = belle_sdp_media_description_get_attributes(media_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) {
872 		belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data;
873 		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));
874 	}
875 
876 	md->nb_streams++;
877 	return stream;
878 }
879 
880 
sdp_to_media_description(belle_sdp_session_description_t * session_desc,SalMediaDescription * desc)881 int sdp_to_media_description ( belle_sdp_session_description_t  *session_desc, SalMediaDescription *desc ) {
882 	belle_sdp_connection_t* cnx;
883 	belle_sip_list_t* media_desc_it;
884 	belle_sdp_media_description_t* media_desc;
885 	belle_sdp_session_name_t *sname;
886 	belle_sip_list_t *custom_attribute_it;
887 	const char* value;
888 	SalDtlsRole session_role=SalDtlsRoleInvalid;
889 	int i;
890 
891 	desc->nb_streams = 0;
892 	desc->dir = SalStreamSendRecv;
893 
894 	if ( ( cnx=belle_sdp_session_description_get_connection ( session_desc ) ) && belle_sdp_connection_get_address ( cnx ) ) {
895 		strncpy ( desc->addr,belle_sdp_connection_get_address ( cnx ),sizeof ( desc->addr ) -1  );
896 	}
897 	if ( (sname=belle_sdp_session_description_get_session_name(session_desc)) && belle_sdp_session_name_get_value(sname) ){
898 		strncpy(desc->name,belle_sdp_session_name_get_value(sname),sizeof(desc->name) - 1);
899 	}
900 
901 	if ( belle_sdp_session_description_get_bandwidth ( session_desc,"AS" ) >0 ) {
902 		desc->bandwidth=belle_sdp_session_description_get_bandwidth ( session_desc,"AS" );
903 	}
904 
905 	/*in some very rare case, session attribute may set stream dir*/
906 	if ( belle_sdp_session_description_get_attribute ( session_desc,"sendrecv" ) ) {
907 		desc->dir=SalStreamSendRecv;
908 	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"sendonly" ) ) {
909 		desc->dir=SalStreamSendOnly;
910 	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"recvonly" ) ) {
911 		desc->dir=SalStreamRecvOnly;
912 	} else if ( belle_sdp_session_description_get_attribute ( session_desc,"inactive" ) ) {
913 		desc->dir=SalStreamInactive;
914 	}
915 
916 	/*DTLS attributes can be defined at session level.*/
917 	value=belle_sdp_session_description_get_attribute_value(session_desc,"setup");
918 	if (value){
919 		if (strncmp(value, "actpass", 7) == 0) {
920 			session_role = SalDtlsRoleUnset;
921 		} else if (strncmp(value, "active", 6) == 0) {
922 			session_role = SalDtlsRoleIsClient;
923 		} else if (strncmp(value, "passive", 7) == 0) {
924 			session_role = SalDtlsRoleIsServer;
925 		}
926 	}
927 	value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint");
928 	/*copy dtls attributes to every streams, might be overwritten stream by stream*/
929 	for (i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;i++) {
930 		if (value)
931 			strncpy(desc->streams[i].dtls_fingerprint, value, sizeof(desc->streams[i].dtls_fingerprint));
932 		desc->streams[i].dtls_role=session_role; /*set or reset value*/
933 	}
934 
935 	/* Get ICE remote ufrag and remote pwd, and ice_lite flag */
936 	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag");
937 	if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1);
938 
939 	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-pwd");
940 	if (value) strncpy(desc->ice_pwd, value, sizeof(desc->ice_pwd)-1);
941 
942 	value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-lite");
943 	if (value) desc->ice_lite = TRUE;
944 
945 	/* Get session RTCP-XR attributes if any */
946 	sdp_parse_session_rtcp_xr_parameters(session_desc, &desc->rtcp_xr);
947 
948 	/* Get the custom attributes */
949 	for (custom_attribute_it = belle_sdp_session_description_get_attributes(session_desc); custom_attribute_it != NULL; custom_attribute_it = custom_attribute_it->next) {
950 		belle_sdp_attribute_t *attr = (belle_sdp_attribute_t *)custom_attribute_it->data;
951 		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));
952 	}
953 
954 	for ( media_desc_it=belle_sdp_session_description_get_media_descriptions ( session_desc )
955 						; media_desc_it!=NULL
956 			; media_desc_it=media_desc_it->next ) {
957 		if (desc->nb_streams==SAL_MEDIA_DESCRIPTION_MAX_STREAMS){
958 			ms_warning("Cannot convert mline at position [%i] from SDP to SalMediaDescription",desc->nb_streams);
959 			break;
960 		}
961 		media_desc=BELLE_SDP_MEDIA_DESCRIPTION ( media_desc_it->data );
962 		sdp_to_stream_description(desc, media_desc);
963 	}
964 	return 0;
965 }
966