offeranswer.c 23.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
linphone
Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)

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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

Sylvain Berfini's avatar
Sylvain Berfini committed
20
#include "sal/sal.h"
21
#include "offeranswer.h"
22
#include "private.h"
23

Simon Morlat's avatar
Simon Morlat committed
24
static bool_t only_telephone_event(const MSList *l){
25 26 27 28 29
	for(;l!=NULL;l=l->next){
		PayloadType *p=(PayloadType*)l->data;
		if (strcasecmp(p->mime_type,"telephone-event")!=0){
			return FALSE;
		}
Simon Morlat's avatar
Simon Morlat committed
30 31 32
	}
	return TRUE;
}
33

34 35 36 37 38 39
typedef struct _PayloadTypeMatcher{
	const char *mime_type;
	PayloadType *(*match_func)(const MSList *l, const PayloadType *refpt);
}PayloadTypeMatcher;

static PayloadType * opus_match(const MSList *l, const PayloadType *refpt){
40 41 42 43 44 45
	PayloadType *pt;
	const MSList *elem;
	PayloadType *candidate=NULL;

	for (elem=l;elem!=NULL;elem=elem->next){
		pt=(PayloadType*)elem->data;
46 47
		
		/*workaround a bug in earlier versions of linphone where opus/48000/1 is offered, which is uncompliant with opus rtp draft*/
48 49 50 51 52 53 54
		if (strcasecmp(pt->mime_type,"opus")==0 ){
			if (refpt->channels==1){
				pt->channels=1; /*so that we respond with same number of channels */
				candidate=pt;
			}else if (refpt->channels==2){
				return pt;
			}
55
		}
56 57 58 59 60 61 62 63 64 65 66 67
	}
	return candidate;
}

/* the reason for this matcher is for some stupid uncompliant phone that offer G729a mime type !*/
static PayloadType * g729A_match(const MSList *l, const PayloadType *refpt){
	PayloadType *pt;
	const MSList *elem;
	PayloadType *candidate=NULL;

	for (elem=l;elem!=NULL;elem=elem->next){
		pt=(PayloadType*)elem->data;
68
		
69
		if (strcasecmp(pt->mime_type,"G729")==0 && refpt->channels==pt->channels){
70 71 72 73 74 75
			candidate=pt;
		}
	}
	return candidate;
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
static PayloadType * amr_match(const MSList *l, const PayloadType *refpt){
	PayloadType *pt;
	char value[10];
	const MSList *elem;
	PayloadType *candidate=NULL;

	for (elem=l;elem!=NULL;elem=elem->next){
		pt=(PayloadType*)elem->data;
		
		if ( pt->mime_type && refpt->mime_type 
			&& strcasecmp(pt->mime_type, refpt->mime_type)==0
			&& pt->clock_rate==refpt->clock_rate
			&& pt->channels==refpt->channels) {
			int octedalign1=0,octedalign2=0;
			if (pt->recv_fmtp!=NULL && fmtp_get_value(pt->recv_fmtp,"octet-align",value,sizeof(value))){
				octedalign1=atoi(value);
			}
			if (refpt->send_fmtp!=NULL && fmtp_get_value(refpt->send_fmtp,"octet-align",value,sizeof(value))){
				octedalign2=atoi(value);
			}
			if (octedalign1==octedalign2) {
				candidate=pt;
				break; /*exact match */
			}
		}
	}
	return candidate;
}

static PayloadType * generic_match(const MSList *l, const PayloadType *refpt){
	PayloadType *pt;
	const MSList *elem;

	for (elem=l;elem!=NULL;elem=elem->next){
		pt=(PayloadType*)elem->data;
		
		if ( pt->mime_type && refpt->mime_type 
			&& strcasecmp(pt->mime_type, refpt->mime_type)==0
			&& pt->clock_rate==refpt->clock_rate
			&& pt->channels==refpt->channels)
			return pt;
	}
	return NULL;
}

static PayloadTypeMatcher matchers[]={
	{"opus", opus_match},
	{"G729A", g729A_match},
	{"AMR", amr_match},
	{"AMR-WB", amr_match},
	{NULL, NULL}
};


130 131 132
/*
 * Returns a PayloadType from the local list that matches a PayloadType offered or answered in the remote list
*/
133 134 135 136 137 138 139 140 141 142 143
static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
	PayloadTypeMatcher *m;
	for(m=matchers;m->mime_type!=NULL;++m){
		if (refpt->mime_type && strcasecmp(m->mime_type,refpt->mime_type)==0){
			return m->match_func(l,refpt);
		}
	}
	return generic_match(l,refpt);
}


Simon Morlat's avatar
Simon Morlat committed
144
static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
145
	const MSList *e2,*e1;
146 147
	MSList *res=NULL;
	PayloadType *matched;
Simon Morlat's avatar
Simon Morlat committed
148
	bool_t found_codec=FALSE;
149

150 151 152 153
	for(e2=remote;e2!=NULL;e2=e2->next){
		PayloadType *p2=(PayloadType*)e2->data;
		matched=find_payload_type_best_match(local,p2);
		if (matched){
154 155 156
			PayloadType *newp;
			int local_number=payload_type_get_number(matched);
			int remote_number=payload_type_get_number(p2);
Simon Morlat's avatar
Simon Morlat committed
157 158 159 160 161 162 163 164

			if (one_matching_codec){
				if (strcasecmp(matched->mime_type,"telephone-event")!=0){
					if (found_codec){/* we have found a real codec already*/
						continue; /*this codec won't be added*/
					}else found_codec=TRUE;
				}
			}
165

166
			newp=payload_type_clone(matched);
167 168 169
			if (p2->send_fmtp){
				payload_type_append_send_fmtp(newp,p2->send_fmtp);
			}
170
			newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
171 172
			if (p2->flags & PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED) {
				newp->flags |= PAYLOAD_TYPE_RTCP_FEEDBACK_ENABLED;
173 174 175
				/* Negotiation of AVPF features (keep common features) */
				newp->avpf.features &= p2->avpf.features;
				newp->avpf.rpsi_compatibility = p2->avpf.rpsi_compatibility;
176 177 178 179
				/* Take bigger AVPF trr interval */
				if (p2->avpf.trr_interval < matched->avpf.trr_interval) {
					newp->avpf.trr_interval = matched->avpf.trr_interval;
				}
180
			}
181 182 183
			res=ms_list_append(res,newp);
			/* we should use the remote numbering even when parsing a response */
			payload_type_set_number(newp,remote_number);
184
			payload_type_set_flag(newp, PAYLOAD_TYPE_FROZEN_NUMBER);
185 186
			if (reading_response && remote_number!=local_number){
				ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
187
						  newp->mime_type, local_number, remote_number);
188 189 190 191 192
				/*
				 We must add this payload type with our local numbering in order to be able to receive it.
				 Indeed despite we must sent with the remote numbering, we must be able to receive with
				 our local one.
				*/
193
				newp=payload_type_clone(newp);
194
				payload_type_set_number(newp,local_number);
195
				payload_type_set_flag(newp, PAYLOAD_TYPE_FROZEN_NUMBER);
196 197
				res=ms_list_append(res,newp);
			}
198
		}else{
199 200 201
			if (p2->channels>0)
				ms_message("No match for %s/%i/%i",p2->mime_type,p2->clock_rate,p2->channels);
			else ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
202 203
		}
	}
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	if (reading_response){
		/* add remaning local payload as CAN_RECV only so that if we are in front of a non-compliant equipment we are still able to decode the RTP stream*/
		for(e1=local;e1!=NULL;e1=e1->next){
			PayloadType *p1=(PayloadType*)e1->data;
			bool_t found=FALSE;
			for(e2=res;e2!=NULL;e2=e2->next){
				PayloadType *p2=(PayloadType*)e2->data;
				if (payload_type_get_number(p2)==payload_type_get_number(p1)){
					found=TRUE;
					break;
				}
			}
			if (!found){
				ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
				p1=payload_type_clone(p1);
219 220
				payload_type_set_flag(p1, PAYLOAD_TYPE_FLAG_CAN_RECV);
				payload_type_set_flag(p1, PAYLOAD_TYPE_FROZEN_NUMBER);
221 222 223 224
				res=ms_list_append(res,p1);
			}
		}
	}
225 226 227
	return res;
}

228
static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote,
229
	SalSrtpCryptoAlgo* result, unsigned int* choosen_local_tag, bool_t use_local_key) {
230 231 232 233
	int i,j;
	for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
		if (remote[i].algo == 0)
			break;
234

235
		/* Look for a local enabled crypto algo that matches one of the proposed by remote */
236 237 238
		for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
			if (remote[i].algo == local[j].algo) {
				result->algo = remote[i].algo;
239
				/* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
240
				if (use_local_key) {
241
					strncpy(result->master_key, local[j].master_key, sizeof(result->master_key) );
jehan's avatar
jehan committed
242
					result->tag = remote[i].tag;
243
					*choosen_local_tag = local[j].tag;
244 245
				}
				/* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
246
				else {
247
					strncpy(result->master_key, remote[i].master_key, sizeof(result->master_key));
248
					result->tag = local[j].tag;
249
					*choosen_local_tag = local[j].tag;
250 251 252 253 254 255 256 257
				}
				return TRUE;
			}
		}
	}
	return FALSE;
}

Simon Morlat's avatar
Simon Morlat committed
258

259

260
static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
Simon Morlat's avatar
Simon Morlat committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274
	SalStreamDir res=local;
	if (local==SalStreamSendRecv){
		if (answered==SalStreamRecvOnly){
			res=SalStreamSendOnly;
		}else if (answered==SalStreamSendOnly){
			res=SalStreamRecvOnly;
		}
	}
	if (answered==SalStreamInactive){
		res=SalStreamInactive;
	}
	return res;
}

275 276 277 278 279
static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
	SalStreamDir res=SalStreamSendRecv;
	if (local==SalStreamSendRecv){
		if (offered==SalStreamSendOnly)
			res=SalStreamRecvOnly;
Simon Morlat's avatar
Simon Morlat committed
280
		else if (offered==SalStreamRecvOnly)
281
			res=SalStreamSendOnly;
Simon Morlat's avatar
Simon Morlat committed
282
		else if (offered==SalStreamInactive)
283
			res=SalStreamInactive;
Simon Morlat's avatar
Simon Morlat committed
284 285
		else
			res=SalStreamSendRecv;
286
	}else if (local==SalStreamSendOnly){
Simon Morlat's avatar
Simon Morlat committed
287
		if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
288
			res=SalStreamSendOnly;
Simon Morlat's avatar
Simon Morlat committed
289
		else res=SalStreamInactive;
290
	}else if (local==SalStreamRecvOnly){
Simon Morlat's avatar
Simon Morlat committed
291
		if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
292
			res=SalStreamRecvOnly;
Simon Morlat's avatar
Simon Morlat committed
293
		else
294 295 296 297 298
			res=SalStreamInactive;
	}else res=SalStreamInactive;
	return res;
}

299
static void initiate_outgoing(const SalStreamDescription *local_offer,
300 301
						const SalStreamDescription *remote_answer,
						SalStreamDescription *result){
302
	if (remote_answer->rtp_port!=0)
Simon Morlat's avatar
Simon Morlat committed
303
		result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
jehan's avatar
jehan committed
304 305 306 307 308
	else {
		ms_message("Local stream description [%p] rejected by peer",local_offer);
		result->rtp_port=0;
		return;
	}
309
	result->proto=remote_answer->proto;
310
	result->type=local_offer->type;
jehan's avatar
jehan committed
311

312
	if (local_offer->rtp_addr[0]!='\0' && ms_is_multicast(local_offer->rtp_addr)) {
jehan's avatar
jehan committed
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
		/*6.2 Multicast Streams
		...
	   If a multicast stream is accepted, the address and port information
	   in the answer MUST match that of the offer.  Similarly, the
	   directionality information in the answer (sendonly, recvonly, or
	   sendrecv) MUST equal that of the offer.  This is because all
	   participants in a multicast session need to have equivalent views of
	   the parameters of the session, an underlying assumption of the
	   multicast bias of RFC 2327.*/
	  if (strcmp(local_offer->rtp_addr,remote_answer->rtp_addr) !=0 ) {
		  ms_message("Remote answered IP [%s] does not match offered [%s] for local stream description [%p]"
				  	  	  	  	  	  	  	  	  	  	  	  ,remote_answer->rtp_addr
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer->rtp_addr
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
		  result->rtp_port=0;
		  return;
	  }
	  if (local_offer->rtp_port!=remote_answer->rtp_port) {
		  ms_message("Remote answered rtp port [%i] does not match offered [%i] for local stream description [%p]"
				  	  	  	  	  	  	  	  	  	  	  	  ,remote_answer->rtp_port
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer->rtp_port
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
		  result->rtp_port=0;
		  return;
	  }
	  if (local_offer->dir!=remote_answer->dir) {
		  ms_message("Remote answered dir [%s] does not match offered [%s] for local stream description [%p]"
				  	  	  	  	  	  	  	  	  	  	  	  ,sal_stream_dir_to_string(remote_answer->dir)
				  	  	  	  	  	  	  	  	  	  	  	  ,sal_stream_dir_to_string(local_offer->dir)
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
		  result->rtp_port=0;
		  return;
	  }
	  if (local_offer->bandwidth!=remote_answer->bandwidth) {
		  ms_message("Remote answered bandwidth [%i] does not match offered [%i] for local stream description [%p]"
				  	  	  	  	  	  	  	  	  	  	  	  ,remote_answer->bandwidth
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer->bandwidth
				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
		  result->rtp_port=0;
		  return;
	  }
	  if (local_offer->ptime > 0 && local_offer->ptime!=remote_answer->ptime) {
	  		  ms_message("Remote answered ptime [%i] does not match offered [%i] for local stream description [%p]"
	  				  	  	  	  	  	  	  	  	  	  	  	  ,remote_answer->ptime
	  				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer->ptime
	  				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
	  	  result->rtp_port=0;
	  	  return;
	  }
	  if (local_offer->ttl > 0 && local_offer->ttl!=remote_answer->ttl) {
	  	  ms_message("Remote answered ttl [%i] does not match offered [%i] for local stream description [%p]"
	  	  				  	  	  	  	  	  	  	  	  	  	  	  ,remote_answer->ttl
	  	  				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer->ttl
	  	  				  	  	  	  	  	  	  	  	  	  	  	  ,local_offer);
	   	  result->rtp_port=0;
	   	  return;
	   }
	  result->ttl=local_offer->ttl;
	  result->dir=local_offer->dir;
372
	  result->multicast_role = SalMulticastSender;
jehan's avatar
jehan committed
373 374 375 376 377 378

	} else {
		result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
	}


Simon Morlat's avatar
Simon Morlat committed
379

380
	if (result->payloads && !only_telephone_event(result->payloads)){
381
		strcpy(result->rtp_addr,remote_answer->rtp_addr);
382
		strcpy(result->rtcp_addr,remote_answer->rtcp_addr);
383
		result->rtp_port=remote_answer->rtp_port;
384
		result->rtcp_port=remote_answer->rtcp_port;
385 386 387
		result->bandwidth=remote_answer->bandwidth;
		result->ptime=remote_answer->ptime;
	}else{
388
		result->rtp_port=0;
389
	}
390
	if (sal_stream_description_has_srtp(result) == TRUE) {
391 392
		/* verify crypto algo */
		memset(result->crypto, 0, sizeof(result->crypto));
393
		if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
394
			result->rtp_port = 0;
395
	}
396 397 398
	result->rtp_ssrc=local_offer->rtp_ssrc;
	strncpy(result->rtcp_cname,local_offer->rtcp_cname,sizeof(result->rtcp_cname));

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
	// Handle dtls session attribute: if both local and remote have a dtls fingerprint and a dtls setup, get the remote fingerprint into the result
	if ((local_offer->dtls_role!=SalDtlsRoleInvalid) && (remote_answer->dtls_role!=SalDtlsRoleInvalid)
			&&(strlen(local_offer->dtls_fingerprint)>0) && (strlen(remote_answer->dtls_fingerprint)>0)) {
		strncpy(result->dtls_fingerprint, remote_answer->dtls_fingerprint,sizeof(result->dtls_fingerprint));
		if (remote_answer->dtls_role==SalDtlsRoleIsClient) {
			result->dtls_role = SalDtlsRoleIsServer;
		} else {
			result->dtls_role = SalDtlsRoleIsClient;
		}
	} else {
		result->dtls_fingerprint[0] = '\0';
		result->dtls_role = SalDtlsRoleInvalid;
	}


414 415 416 417
}


static void initiate_incoming(const SalStreamDescription *local_cap,
418 419
						const SalStreamDescription *remote_offer,
						SalStreamDescription *result, bool_t one_matching_codec){
Simon Morlat's avatar
Simon Morlat committed
420
	result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
421
	result->proto=remote_offer->proto;
422
	result->type=local_cap->type;
423 424
	result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
	if (!result->payloads || only_telephone_event(result->payloads) || remote_offer->rtp_port==0 || remote_offer->dir==SalStreamRecvOnly){
jehan's avatar
jehan committed
425 426 427
		result->rtp_port=0;
		return;
	}
428
	if (remote_offer->rtp_addr[0]!='\0' && ms_is_multicast(remote_offer->rtp_addr)) {
jehan's avatar
jehan committed
429 430 431 432 433 434 435 436 437 438 439 440 441 442
		if (sal_stream_description_has_srtp(result) == TRUE) {
			ms_message("SAVP not supported for multicast address for remote stream [%p]",remote_offer);
			result->rtp_port=0;
			return;
		}
		result->dir=remote_offer->dir;
		strcpy(result->rtp_addr,remote_offer->rtp_addr);
		strcpy(result->rtcp_addr,remote_offer->rtcp_addr);
		result->rtp_port=remote_offer->rtp_port;
		/*result->rtcp_port=remote_offer->rtcp_port;*/
		result->rtcp_port=0; /* rtcp not supported yet*/
		result->bandwidth=remote_offer->bandwidth;
		result->ptime=remote_offer->ptime;
		result->ttl=remote_offer->ttl;
443
		result->multicast_role = SalMulticastReceiver;
jehan's avatar
jehan committed
444
	} else {
445
		strcpy(result->rtp_addr,local_cap->rtp_addr);
446
		strcpy(result->rtcp_addr,local_cap->rtcp_addr);
447
		result->rtp_port=local_cap->rtp_port;
448
		result->rtcp_port=local_cap->rtcp_port;
449
		result->bandwidth=local_cap->bandwidth;
450
		result->ptime=local_cap->ptime;
451
	}
452
	if (sal_stream_description_has_srtp(result) == TRUE) {
453 454
		/* select crypto algo */
		memset(result->crypto, 0, sizeof(result->crypto));
455
		if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
456
			result->rtp_port = 0;
457

458
	}
459 460 461
	strcpy(result->ice_pwd, local_cap->ice_pwd);
	strcpy(result->ice_ufrag, local_cap->ice_ufrag);
	result->ice_mismatch = local_cap->ice_mismatch;
462
	result->ice_completed = local_cap->ice_completed;
463 464
	memcpy(result->ice_candidates, local_cap->ice_candidates, sizeof(result->ice_candidates));
	memcpy(result->ice_remote_candidates, local_cap->ice_remote_candidates, sizeof(result->ice_remote_candidates));
465
	strcpy(result->name,local_cap->name);
466 467 468
	result->rtp_ssrc=local_cap->rtp_ssrc;
	strncpy(result->rtcp_cname,local_cap->rtcp_cname,sizeof(result->rtcp_cname));

469 470 471 472 473 474 475 476 477 478 479 480 481 482
	// Handle dtls stream attribute: if both local and remote have a dtls fingerprint and a dtls setup, add the local fingerprint to the answer
	// Note: local description usually stores dtls config at session level which means it apply to all streams, check this too
	if (((local_cap->dtls_role!=SalDtlsRoleInvalid)) && (remote_offer->dtls_role!=SalDtlsRoleInvalid)
			&& (strlen(local_cap->dtls_fingerprint)>0) && (strlen(remote_offer->dtls_fingerprint)>0)) {
		strncpy(result->dtls_fingerprint, local_cap->dtls_fingerprint,sizeof(result->dtls_fingerprint));
		if (remote_offer->dtls_role==SalDtlsRoleUnset) {
			result->dtls_role = SalDtlsRoleIsClient;
		}
	} else {
		result->dtls_fingerprint[0] = '\0';
		result->dtls_role = SalDtlsRoleInvalid;
	}


483 484
}

jehan's avatar
jehan committed
485

486 487 488 489 490
/**
 * Returns a media description to run the streams with, based on a local offer
 * and the returned response (remote).
**/
int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
491 492 493
					const SalMediaDescription *remote_answer,
					SalMediaDescription *result){
	int i,j;
494
	const SalStreamDescription *ls,*rs;
495

496
	for(i=0,j=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
497
		ls=&local_offer->streams[i];
498 499
		if (!sal_stream_description_active(ls)) continue;
		ms_message("Processing for stream %i",i);
500
		rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
501
		if (rs) {
502
			initiate_outgoing(ls,rs,&result->streams[j]);
503
			memcpy(&result->streams[i].rtcp_xr, &ls->rtcp_xr, sizeof(result->streams[i].rtcp_xr));
504
			if ((ls->rtcp_xr.enabled == TRUE) && (rs->rtcp_xr.enabled == FALSE)) {
505
				result->streams[i].rtcp_xr.enabled = FALSE;
506
			}
507 508
			result->streams[i].rtcp_fb.generic_nack_enabled = ls->rtcp_fb.generic_nack_enabled & rs->rtcp_fb.generic_nack_enabled;
			result->streams[i].rtcp_fb.tmmbr_enabled = ls->rtcp_fb.tmmbr_enabled & rs->rtcp_fb.tmmbr_enabled;
509 510 511
			++j;
		}
		else ms_warning("No matching stream for %i",i);
512
	}
513
	result->nb_streams=local_offer->nb_streams;
Simon Morlat's avatar
Simon Morlat committed
514
	result->bandwidth=remote_answer->bandwidth;
515
	strcpy(result->addr,remote_answer->addr);
516
	memcpy(&result->rtcp_xr, &local_offer->rtcp_xr, sizeof(result->rtcp_xr));
517
	if ((local_offer->rtcp_xr.enabled == TRUE) && (remote_answer->rtcp_xr.enabled == FALSE)) {
518
		result->rtcp_xr.enabled = FALSE;
519 520
	}

521 522 523
	return 0;
}

524 525
static bool_t local_stream_not_already_used(const SalMediaDescription *result, const SalStreamDescription *stream){
	int i;
526
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
527 528 529 530 531 532 533 534 535
		const SalStreamDescription *ss=&result->streams[i];
		if (strcmp(ss->name,stream->name)==0){
			ms_message("video stream already used in answer");
			return FALSE;
		}
	}
	return TRUE;
}

536
/*in answering mode, we consider that if we are able to make AVPF/SAVP/SAVPF, then we can do AVP as well*/
537 538
static bool_t proto_compatible(SalMediaProto local, SalMediaProto remote) {
	if (local == remote) return TRUE;
539
	if ((remote == SalProtoRtpAvp) && ((local == SalProtoRtpSavp) || (local == SalProtoRtpSavpf))) return TRUE;
johan's avatar
johan committed
540
	if ((remote == SalProtoRtpAvp) && ((local == SalProtoUdpTlsRtpSavp) || (local == SalProtoUdpTlsRtpSavpf))) return TRUE;
541
	if ((remote == SalProtoRtpAvpf) && (local == SalProtoRtpSavpf)) return TRUE;
johan's avatar
johan committed
542
	if ((remote == SalProtoRtpAvpf) && (local == SalProtoUdpTlsRtpSavpf)) return TRUE;
543 544 545 546
	return FALSE;
}

static const SalStreamDescription *find_local_matching_stream(const SalMediaDescription *result, const SalMediaDescription *local_capabilities, const SalStreamDescription *remote_stream){
547
		int i;
548
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
549
		const SalStreamDescription *ss=&local_capabilities->streams[i];
550
		if (!sal_stream_description_active(ss)) continue;
551 552 553 554 555 556
		if (ss->type==remote_stream->type && proto_compatible(ss->proto,remote_stream->proto)
			&& local_stream_not_already_used(result,ss)) return ss;
	}
	return NULL;
}

557 558 559 560 561 562
/**
 * Returns a media description to run the streams with, based on the local capabilities and
 * and the received offer.
 * The returned media description is an answer and should be sent to the offerer.
**/
int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
563 564
					const SalMediaDescription *remote_offer,
					SalMediaDescription *result, bool_t one_matching_codec){
565
	int i;
Yann Diorcet's avatar
Yann Diorcet committed
566
	const SalStreamDescription *ls=NULL,*rs;
567
	result->nb_streams = 0;
568

569
	for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;++i){
570
		rs=&remote_offer->streams[i];
571
		if (!sal_stream_description_active(rs)) continue;
572 573
		if (rs->proto!=SalProtoOther){
			ls=find_local_matching_stream(result,local_capabilities,rs);
574
		}else ms_warning("Unknown protocol for mline %i, declining",i);
575
		if (ls){
576
			initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
577 578 579
			// Handle global RTCP FB attributes
			result->streams[i].rtcp_fb.generic_nack_enabled = rs->rtcp_fb.generic_nack_enabled;
			result->streams[i].rtcp_fb.tmmbr_enabled = rs->rtcp_fb.tmmbr_enabled;
580 581 582
			// Handle media RTCP XR attribute
			memset(&result->streams[i].rtcp_xr, 0, sizeof(result->streams[i].rtcp_xr));
			if (rs->rtcp_xr.enabled == TRUE) {
583 584 585 586 587
				const OrtpRtcpXrConfiguration *rtcp_xr_conf = NULL;
				if (ls->rtcp_xr.enabled == TRUE) rtcp_xr_conf = &ls->rtcp_xr;
				else if (local_capabilities->rtcp_xr.enabled == TRUE) rtcp_xr_conf = &local_capabilities->rtcp_xr;
				if ((rtcp_xr_conf != NULL) && (ls->dir == SalStreamSendRecv)) {
					memcpy(&result->streams[i].rtcp_xr, rtcp_xr_conf, sizeof(result->streams[i].rtcp_xr));
588 589 590 591
				} else {
					result->streams[i].rtcp_xr.enabled = TRUE;
				}
			}
592
			result->nb_streams++;
593
		}else {
594
			ms_message("Declining mline %i, no corresponding stream in local capabilities description.",i);
595
			/* create an inactive stream for the answer, as there where no matching stream in local capabilities */
596
			result->streams[i].dir=SalStreamInactive;
597
			result->streams[i].rtp_port=0;
598
			result->streams[i].type=rs->type;
Simon Morlat's avatar
Simon Morlat committed
599
			result->streams[i].proto=rs->proto;
600 601 602
			if (rs->type==SalOther){
				strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
			}
603 604 605
			if (rs->proto==SalProtoOther){
				strncpy(result->streams[i].proto_other,rs->proto_other,sizeof(rs->proto_other)-1);
			}
606
		}
607
	}
608

609
	strcpy(result->username, local_capabilities->username);
610
	strcpy(result->addr,local_capabilities->addr);
Simon Morlat's avatar
Simon Morlat committed
611
	result->bandwidth=local_capabilities->bandwidth;
612 613
	result->session_ver=local_capabilities->session_ver;
	result->session_id=local_capabilities->session_id;
614 615 616
	strcpy(result->ice_pwd, local_capabilities->ice_pwd);
	strcpy(result->ice_ufrag, local_capabilities->ice_ufrag);
	result->ice_lite = local_capabilities->ice_lite;
617
	result->ice_completed = local_capabilities->ice_completed;
618

619
	strcpy(result->name,local_capabilities->name);
620 621 622 623

	// Handle session RTCP XR attribute
	memset(&result->rtcp_xr, 0, sizeof(result->rtcp_xr));
	if (remote_offer->rtcp_xr.enabled == TRUE) {
624
		if ((local_capabilities->rtcp_xr.enabled == TRUE) && (local_capabilities->dir == SalStreamSendRecv)) {
625 626 627 628 629 630
			memcpy(&result->rtcp_xr, &local_capabilities->rtcp_xr, sizeof(result->rtcp_xr));
		} else {
			result->rtcp_xr.enabled = TRUE;
		}
	}

631 632
	return 0;
}