linphonecall.c 49.3 KB
Newer Older
1 2 3

/*
linphone
4
Copyright (C) 2010  Belledonne Communications SARL
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 (simon.morlat@linphone.org)

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.
*/
21 22 23
#ifdef WIN32
#include <time.h>
#endif
24 25 26 27
#include "linphonecore.h"
#include "sipsetup.h"
#include "lpconfig.h"
#include "private.h"
28
#include <ortp/event.h>
29
#include <ortp/b64.h>
30 31


32 33 34
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msvolume.h"
#include "mediastreamer2/msequalizer.h"
35
#include "mediastreamer2/msfileplayer.h"
36
#include "mediastreamer2/msjpegwriter.h"
37
#include "mediastreamer2/mseventqueue.h"
38

Simon Morlat's avatar
Simon Morlat committed
39
#ifdef VIDEO_ENABLED
40 41 42
static MSWebCam *get_nowebcam_device(){
	return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
}
Simon Morlat's avatar
Simon Morlat committed
43
#endif
44

45 46 47
static bool_t generate_b64_crypto_key(int key_length, char* key_out) {
	int b64_size;
	uint8_t* tmp = (uint8_t*) malloc(key_length);			
48
	if (ortp_crypto_get_random(tmp, key_length)!=0) {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
		ms_error("Failed to generate random key");
		free(tmp);
		return FALSE;
	}
	
	b64_size = b64_encode((const char*)tmp, key_length, NULL, 0);
	if (b64_size == 0) {
		ms_error("Failed to b64 encode key");
		free(tmp);
		return FALSE;
	}
	key_out[b64_size] = '\0';
	b64_encode((const char*)tmp, key_length, key_out, 40);
	free(tmp);
	return TRUE;
}

66 67 68 69
LinphoneCore *linphone_call_get_core(const LinphoneCall *call){
	return call->core;
}

70
static const char* get_hexa_zrtp_identifier(LinphoneCore *lc){
71 72 73 74
	const char *confZid=lp_config_get_string(lc->config,"rtp","zid",NULL);
	if (confZid != NULL) {
		return confZid;
	} else {
75 76 77
        char zidstr[128];
        snprintf(zidstr,sizeof(zidstr),"%x-%x-%x",rand(),rand(),rand());
		lp_config_set_string(lc->config,"rtp","zid",zidstr);
78 79 80 81 82
		return lp_config_get_string(lc->config,"rtp","zid",NULL);
	}
}

const char* linphone_call_get_authentication_token(LinphoneCall *call){
83
	return call->auth_token;
84 85 86
}

bool_t linphone_call_get_authentication_token_verified(LinphoneCall *call){
87
	return call->auth_token_verified;
88 89 90
}
bool_t linphone_call_are_all_streams_encrypted(LinphoneCall *call) {
	// Check ZRTP encryption in audiostream
91
	if (!call->audiostream_encrypted) {
92 93 94 95 96 97
		return FALSE;
	}

#ifdef VIDEO_ENABLED
	// If video enabled, check ZRTP encryption in videostream
	const LinphoneCallParams *params=linphone_call_get_current_params(call);
98
	if (params->has_video && !call->videostream_encrypted) {
99 100 101 102 103 104 105
		return FALSE;
	}
#endif

	return TRUE;
}

106
void propagate_encryption_changed(LinphoneCall *call){
107 108 109
	if (call->core->vtable.call_encryption_changed == NULL) return;

	if (!linphone_call_are_all_streams_encrypted(call)) {
110 111
		ms_message("Some streams are not encrypted");
		call->core->vtable.call_encryption_changed(call->core, call, FALSE, call->auth_token);
112
	} else {
113 114
		ms_message("All streams are encrypted");
		call->core->vtable.call_encryption_changed(call->core, call, TRUE, call->auth_token);
115 116 117 118 119 120 121 122
	}
}

#ifdef VIDEO_ENABLED
static void linphone_call_videostream_encryption_changed(void *data, bool_t encrypted){
	ms_message("Video stream is %s", encrypted ? "encrypted" : "not encrypted");

	LinphoneCall *call = (LinphoneCall *)data;
123
	call->videostream_encrypted=encrypted;
124 125 126 127 128
	propagate_encryption_changed(call);
}
#endif

static void linphone_call_audiostream_encryption_changed(void *data, bool_t encrypted) {
129
	char status[255]={0};
130 131 132
	ms_message("Audio stream is %s ", encrypted ? "encrypted" : "not encrypted");

	LinphoneCall *call = (LinphoneCall *)data;
133
	call->audiostream_encrypted=encrypted;
134 135 136 137 138 139

	if (encrypted && call->core->vtable.display_status != NULL) {
		snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token);
		 call->core->vtable.display_status(call->core, status);
	}

140 141 142 143 144 145 146 147 148
	propagate_encryption_changed(call);


#ifdef VIDEO_ENABLED
	// Enable video encryption
	const LinphoneCallParams *params=linphone_call_get_current_params(call);
	if (params->has_video) {
		ms_message("Trying to enable encryption on video stream");
		OrtpZrtpParams params;
149
		params.zid=get_hexa_zrtp_identifier(call->core);
150 151 152 153 154 155 156 157 158
		params.zid_file=NULL; //unused
		video_stream_enable_zrtp(call->videostream,call->audiostream,&params);
	}
#endif
}


static void linphone_call_audiostream_auth_token_ready(void *data, const char* auth_token, bool_t verified) {
	LinphoneCall *call=(LinphoneCall *)data;
159 160
	if (call->auth_token != NULL)
		ms_free(call->auth_token);
161

162 163
	call->auth_token=ms_strdup(auth_token);
	call->auth_token_verified=verified;
164 165 166 167

	ms_message("Authentication token is %s (%s)", auth_token, verified?"verified":"unverified");
}

168

169
static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){
170 171 172 173
	MSList *l=NULL;
	const MSList *it;
	for(it=codecs;it!=NULL;it=it->next){
		PayloadType *pt=(PayloadType*)it->data;
174 175 176 177 178 179 180 181
		if (pt->flags & PAYLOAD_TYPE_ENABLED){
			if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){
				ms_message("Codec %s/%i eliminated because of audio bandwidth constraint.",pt->mime_type,pt->clock_rate);
				continue;
			}
			if (linphone_core_check_payload_type_usability(lc,pt)){
				l=ms_list_append(l,payload_type_clone(pt));
			}
182 183 184 185 186
		}
	}
	return l;
}

187
static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, LinphoneCall *call, unsigned int session_id, unsigned int session_ver){
188 189
	MSList *l;
	PayloadType *pt;
190
	int i;
191 192 193
	const char *me=linphone_core_get_identity(lc);
	LinphoneAddress *addr=linphone_address_new(me);
	const char *username=linphone_address_get_username (addr);
194
	SalMediaDescription *md=sal_media_description_new();
195

196 197
	md->session_id=session_id;
	md->session_ver=session_ver;
198
	md->nstreams=1;
199
	strncpy(md->addr,call->localip,sizeof(md->addr));
200 201
	strncpy(md->username,username,sizeof(md->username));
	md->bandwidth=linphone_core_get_download_bandwidth(lc);
202

203
	/*set audio capabilities */
204 205
	strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr));
	md->streams[0].port=call->audio_port;
206 207
	md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ? 
		SalProtoRtpSavp : SalProtoRtpAvp;
208 209
	md->streams[0].type=SalAudio;
	md->streams[0].ptime=lc->net_conf.down_ptime;
210
	l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw);
211 212 213
	pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
	l=ms_list_append(l,pt);
	md->streams[0].payloads=l;
214

215

Simon Morlat's avatar
Simon Morlat committed
216
	if (call->params.has_video){
217
		md->nstreams++;
218
		md->streams[1].port=call->video_port;
219
		md->streams[1].proto=md->streams[0].proto;
220
		md->streams[1].type=SalVideo;
221
		l=make_codec_list(lc,lc->codecs_conf.video_codecs,0);
222 223
		md->streams[1].payloads=l;
	}
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
	
	for(i=0; i<md->nstreams; i++) {
		if (md->streams[i].proto == SalProtoRtpSavp) {
			md->streams[i].crypto[0].tag = 1;
			md->streams[i].crypto[0].algo = AES_128_SHA1_80;
			if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
				md->streams[i].crypto[0].algo = 0;
			md->streams[i].crypto[1].tag = 2;
			md->streams[i].crypto[1].algo = AES_128_SHA1_32;
			if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
				md->streams[i].crypto[1].algo = 0;
			md->streams[i].crypto[2].algo = 0;
		}
	}
	
239
	linphone_address_destroy(addr);
240 241 242
	return md;
}

243 244 245 246 247 248 249 250 251 252 253 254
void update_local_media_description(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription **md){
	if (*md == NULL) {
		*md = _create_local_media_description(lc,call,0,0);
	} else {
		unsigned int id = (*md)->session_id;
		unsigned int ver = (*md)->session_ver+1;
		sal_media_description_unref(*md);
		*md = _create_local_media_description(lc,call,id,ver);
	}
}

SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call){
255
	unsigned int id=rand() & 0xfff;
256 257 258
	return _create_local_media_description(lc,call,id,id);
}

259 260 261 262 263
static int find_port_offset(LinphoneCore *lc){
	int offset;
	MSList *elem;
	int audio_port;
	bool_t already_used=FALSE;
264
	for(offset=0;offset<100;offset+=2){
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
		audio_port=linphone_core_get_audio_port (lc)+offset;
		already_used=FALSE;
		for(elem=lc->calls;elem!=NULL;elem=elem->next){
			LinphoneCall *call=(LinphoneCall*)elem->data;
			if (call->audio_port==audio_port) {
				already_used=TRUE;
				break;
			}
		}
		if (!already_used) break;
	}
	if (offset==100){
		ms_error("Could not find any free port !");
		return -1;
	}
	return offset;
}

283
static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
284
	int port_offset;
285
	call->refcnt=1;
286
	call->state=LinphoneCallIdle;
287 288 289
	call->start_time=time(NULL);
	call->media_start_time=0;
	call->log=linphone_call_log_new(call, from, to);
290
	call->owns_call_log=TRUE;
291 292 293 294 295
	linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
	port_offset=find_port_offset (call->core);
	if (port_offset==-1) return;
	call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
	call->video_port=linphone_core_get_video_port(call->core)+port_offset;
296

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
}

static void discover_mtu(LinphoneCore *lc, const char *remote){
	int mtu;
	if (lc->net_conf.mtu==0	){
		/*attempt to discover mtu*/
		mtu=ms_discover_mtu(remote);
		if (mtu>0){
			ms_set_mtu(mtu);
			ms_message("Discovered mtu is %i, RTP payload max size is %i",
				mtu, ms_get_payload_max_size());
		}
	}
}

312
LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
313 314 315 316 317 318 319 320
{
	LinphoneCall *call=ms_new0(LinphoneCall,1);
	call->dir=LinphoneCallOutgoing;
	call->op=sal_op_new(lc->sal);
	sal_op_set_user_pointer(call->op,call);
	call->core=lc;
	linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
	linphone_call_init_common(call,from,to);
321
	call->params=*params;
Simon Morlat's avatar
Simon Morlat committed
322
	call->localdesc=create_local_media_description (lc,call);
323
	call->camera_active=params->has_video;
324 325
	if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
		linphone_core_run_stun_tests(call->core,call);
326
	discover_mtu(lc,linphone_address_get_domain (to));
327
	if (params->referer){
328
		sal_call_set_referer(call->op,params->referer->op);
329
	}
330 331 332 333 334
	return call;
}

LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
	LinphoneCall *call=ms_new0(LinphoneCall,1);
335
	char *from_str;
336 337 338 339 340 341 342 343 344 345

	call->dir=LinphoneCallIncoming;
	sal_op_set_user_pointer(op,call);
	call->op=op;
	call->core=lc;

	if (lc->sip_conf.ping_with_options){
		/*the following sends an option request back to the caller so that
		 we get a chance to discover our nat'd address before answering.*/
		call->ping_op=sal_op_new(lc->sal);
346
		from_str=linphone_address_as_string(from);
347
		sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op));
348
		sal_op_set_user_pointer(call->ping_op,call);
349 350
		sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str);
		ms_free(from_str);
351
	}
352

353 354 355
	linphone_address_clean(from);
	linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
	linphone_call_init_common(call, from, to);
356
	linphone_core_init_default_params(lc, &call->params);
Simon Morlat's avatar
Simon Morlat committed
357
	call->localdesc=create_local_media_description (lc,call);
358
	call->camera_active=call->params.has_video;
359 360
	if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
		linphone_core_run_stun_tests(call->core,call);
361 362 363 364
	discover_mtu(lc,linphone_address_get_domain(from));
	return call;
}

Simon Morlat's avatar
Simon Morlat committed
365 366 367 368 369 370
/* this function is called internally to get rid of a call.
 It performs the following tasks:
 - remove the call from the internal list of calls
 - update the call logs accordingly
*/

371
static void linphone_call_set_terminated(LinphoneCall *call){
Simon Morlat's avatar
Simon Morlat committed
372
	LinphoneCore *lc=call->core;
373

Simon Morlat's avatar
Simon Morlat committed
374
	linphone_core_update_allocated_audio_bandwidth(lc);
375

376
	call->owns_call_log=FALSE;
377
	linphone_call_log_completed(call);
378 379


380 381 382
	if (call == lc->current_call){
		ms_message("Resetting the current call");
		lc->current_call=NULL;
383 384 385 386
	}

	if (linphone_core_del_call(lc,call) != 0){
		ms_error("Could not remove the call from the list !!!");
Simon Morlat's avatar
Simon Morlat committed
387
	}
388

389 390
	if (ms_list_size(lc->calls)==0)
		linphone_core_notify_all_friends(lc,lc->presence_mode);
391

392
	linphone_core_conference_check_uninit(&lc->conf_ctx);
393 394 395 396
	if (call->ringing_beep){
		linphone_core_stop_dtmf(lc);
		call->ringing_beep=FALSE;
	}
397 398
}

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
const char *linphone_call_state_to_string(LinphoneCallState cs){
	switch (cs){
		case LinphoneCallIdle:
			return "LinphoneCallIdle";
		case LinphoneCallIncomingReceived:
			return "LinphoneCallIncomingReceived";
		case LinphoneCallOutgoingInit:
			return "LinphoneCallOutgoingInit";
		case LinphoneCallOutgoingProgress:
			return "LinphoneCallOutgoingProgress";
		case LinphoneCallOutgoingRinging:
			return "LinphoneCallOutgoingRinging";
		case LinphoneCallOutgoingEarlyMedia:
			return "LinphoneCallOutgoingEarlyMedia";
		case LinphoneCallConnected:
			return "LinphoneCallConnected";
		case LinphoneCallStreamsRunning:
			return "LinphoneCallStreamsRunning";
		case LinphoneCallPausing:
			return "LinphoneCallPausing";
		case LinphoneCallPaused:
			return "LinphoneCallPaused";
		case LinphoneCallResuming:
			return "LinphoneCallResuming";
		case LinphoneCallRefered:
			return "LinphoneCallRefered";
		case LinphoneCallError:
Simon Morlat's avatar
Simon Morlat committed
426
			return "LinphoneCallError";
427 428 429 430
		case LinphoneCallEnd:
			return "LinphoneCallEnd";
		case LinphoneCallPausedByRemote:
			return "LinphoneCallPausedByRemote";
431 432
		case LinphoneCallUpdatedByRemote:
			return "LinphoneCallUpdatedByRemote";
433 434
		case LinphoneCallIncomingEarlyMedia:
			return "LinphoneCallIncomingEarlyMedia";
435 436
		case LinphoneCallUpdated:
			return "LinphoneCallUpdated";
437 438
		case LinphoneCallReleased:
			return "LinphoneCallReleased";
439 440 441 442
	}
	return "undefined state";
}

443 444
void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
	LinphoneCore *lc=call->core;
445

446
	if (call->state!=cstate){
447 448 449
		if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
			if (cstate!=LinphoneCallReleased){
				ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
450
				   linphone_call_state_to_string(cstate));
451 452 453
				return;
			}
		}
454 455
		ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
		           linphone_call_state_to_string(cstate));
456 457 458 459 460
		if (cstate!=LinphoneCallRefered){
			/*LinphoneCallRefered is rather an event, not a state.
			 Indeed it does not change the state of the call (still paused or running)*/
			call->state=cstate;
		}
461
		if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
462
             if (call->reason==LinphoneReasonDeclined){
463 464 465
				call->log->status=LinphoneCallDeclined;
			}
			linphone_call_set_terminated (call);
466
		}
467 468
		if (cstate == LinphoneCallConnected) {
			call->log->status=LinphoneCallSuccess;
469
			call->media_start_time=time(NULL);
470
		}
471

472 473
		if (lc->vtable.call_state_changed)
			lc->vtable.call_state_changed(lc,call,cstate,message);
474 475 476 477 478 479 480
		if (cstate==LinphoneCallReleased){
			if (call->op!=NULL) {
				/* so that we cannot have anymore upcalls for SAL
				 concerning this call*/
				sal_op_release(call->op);
				call->op=NULL;
			}
481
			linphone_call_unref(call);
482
		}
483 484 485
	}
}

486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
static void linphone_call_destroy(LinphoneCall *obj)
{
	if (obj->op!=NULL) {
		sal_op_release(obj->op);
		obj->op=NULL;
	}
	if (obj->resultdesc!=NULL) {
		sal_media_description_unref(obj->resultdesc);
		obj->resultdesc=NULL;
	}
	if (obj->localdesc!=NULL) {
		sal_media_description_unref(obj->localdesc);
		obj->localdesc=NULL;
	}
	if (obj->ping_op) {
		sal_op_release(obj->ping_op);
	}
503 504 505
	if (obj->refer_to){
		ms_free(obj->refer_to);
	}
506 507
	if (obj->owns_call_log)
		linphone_call_log_destroy(obj->log);
508 509 510 511
	if (obj->auth_token) {
		ms_free(obj->auth_token);
	}

512 513 514
	ms_free(obj);
}

Simon Morlat's avatar
Simon Morlat committed
515
/**
Simon Morlat's avatar
Simon Morlat committed
516
 * @addtogroup call_control
Simon Morlat's avatar
Simon Morlat committed
517 518 519 520 521 522 523 524 525 526
 * @{
**/

/**
 * Increments the call 's reference count.
 * An application that wishes to retain a pointer to call object
 * must use this function to unsure the pointer remains
 * valid. Once the application no more needs this pointer,
 * it must call linphone_call_unref().
**/
527
LinphoneCall * linphone_call_ref(LinphoneCall *obj){
528
	obj->refcnt++;
529
	return obj;
530 531
}

Simon Morlat's avatar
Simon Morlat committed
532 533 534 535
/**
 * Decrements the call object reference count.
 * See linphone_call_ref().
**/
536 537
void linphone_call_unref(LinphoneCall *obj){
	obj->refcnt--;
Simon Morlat's avatar
Simon Morlat committed
538
	if (obj->refcnt==0){
539
		linphone_call_destroy(obj);
Simon Morlat's avatar
Simon Morlat committed
540
	}
541 542
}

543 544 545 546
/**
 * Returns current parameters associated to the call.
**/
const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
547
	return &call->current_params;
548 549
}

Simon Morlat's avatar
Simon Morlat committed
550 551 552 553
/**
 * Returns the remote address associated to this call
 *
**/
554 555 556 557
const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
	return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
}

Simon Morlat's avatar
Simon Morlat committed
558 559 560 561 562
/**
 * Returns the remote address associated to this call as a string.
 *
 * The result string must be freed by user using ms_free().
**/
563 564 565 566
char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
	return linphone_address_as_string(linphone_call_get_remote_address(call));
}

Simon Morlat's avatar
Simon Morlat committed
567 568 569
/**
 * Retrieves the call's current state.
**/
570 571 572 573
LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
	return call->state;
}

574 575 576 577 578 579 580
/**
 * Returns the reason for a call termination (either error or normal termination)
**/
LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
	return call->reason;
}

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
/**
 * Get the user_pointer in the LinphoneCall
 *
 * @ingroup call_control
 *
 * return user_pointer an opaque user pointer that can be retrieved at any time
**/
void *linphone_call_get_user_pointer(LinphoneCall *call)
{
	return call->user_pointer;
}

/**
 * Set the user_pointer in the LinphoneCall
 *
 * @ingroup call_control
 *
 * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
**/
void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
{
	call->user_pointer = user_pointer;
}
Simon Morlat's avatar
Simon Morlat committed
604

605 606 607 608 609 610 611
/**
 * Returns the call log associated to this call.
**/
LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
	return call->log;
}

Simon Morlat's avatar
Simon Morlat committed
612
/**
613
 * Returns the refer-to uri (if the call was transfered).
Simon Morlat's avatar
Simon Morlat committed
614 615 616 617 618
**/
const char *linphone_call_get_refer_to(const LinphoneCall *call){
	return call->refer_to;
}

619 620 621
/**
 * Returns direction of the call (incoming or outgoing).
**/
Simon Morlat's avatar
Simon Morlat committed
622 623 624
LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
	return call->log->dir;
}
625

626 627 628 629 630 631 632 633 634 635
/**
 * Returns the far end's user agent description string, if available.
**/
const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
	if (call->op){
		return sal_op_get_remote_ua (call->op);
	}
	return NULL;
}

636 637 638 639 640
/**
 * Returns true if this calls has received a transfer that has not been
 * executed yet.
 * Pending transfers are executed when this call is being paused or closed,
 * locally or by remote endpoint.
641
 * If the call is already paused while receiving the transfer request, the
642 643 644 645 646 647
 * transfer immediately occurs.
**/
bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
	return call->refer_pending;
}

648 649 650 651 652 653 654 655
/**
 * Returns call's duration in seconds.
**/
int linphone_call_get_duration(const LinphoneCall *call){
	if (call->media_start_time==0) return 0;
	return time(NULL)-call->media_start_time;
}

656 657 658 659 660 661 662 663 664 665 666 667 668 669
/**
 * Returns the call object this call is replacing, if any.
 * Call replacement can occur during call transfers.
 * By default, the core automatically terminates the replaced call and accept the new one.
 * This function allows the application to know whether a new incoming call is a one that replaces another one.
**/
LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
	SalOp *op=sal_call_get_replaces(call->op);
	if (op){
		return (LinphoneCall*)sal_op_get_user_pointer(op);
	}
	return NULL;
}

670 671 672 673
/**
 * Indicate whether camera input should be sent to remote end.
**/
void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
Simon Morlat's avatar
Simon Morlat committed
674
#ifdef VIDEO_ENABLED
675 676 677 678 679 680 681 682
	if (call->videostream!=NULL && call->videostream->ticker!=NULL){
		LinphoneCore *lc=call->core;
		MSWebCam *nowebcam=get_nowebcam_device();
		if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
			video_stream_change_camera(call->videostream,
			             enable ? lc->video_conf.device : nowebcam);
		}
	}
683
	call->camera_active=enable;
Simon Morlat's avatar
Simon Morlat committed
684
#endif
685 686
}

687 688 689 690 691 692 693 694 695 696 697 698 699 700
/**
 * Take a photo of currently received video and write it into a jpeg file.
**/
int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
#ifdef VIDEO_ENABLED
	if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){
		return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
	}
	ms_warning("Cannot take snapshot: no currently running video stream on this call.");
	return -1;
#endif
	return -1;
}

701
/**
702
 * Returns TRUE if camera pictures are sent to the remote party.
703
**/
704 705 706 707
bool_t linphone_call_camera_enabled (const LinphoneCall *call){
	return call->camera_active;
}

708
/**
709
 * Enable video stream.
710
**/
711 712 713 714
void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
	cp->has_video=enabled;
}

715
/**
716
 * Returns whether video is enabled.
717
**/
718 719 720 721
bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
	return cp->has_video;
}

722
enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(LinphoneCallParams *cp) {
723 724 725 726 727 728 729 730
	return cp->media_encryption;
}

void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
	cp->media_encryption = e;
}


731 732 733 734 735 736 737 738 739 740 741
/**
 * Enable sending of real early media (during outgoing calls).
**/
void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
	cp->real_early_media=enabled;
}

bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
	return cp->real_early_media;
}

Simon Morlat's avatar
Simon Morlat committed
742 743 744 745 746 747 748
/**
 * Returns true if the call is part of the locally managed conference.
**/
bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
	return cp->in_conference;
}

749 750 751 752 753 754 755 756
/**
 * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
 * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
**/
void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
	cp->audio_bw=bandwidth;
}

757 758 759 760 761 762 763 764 765 766 767
#ifdef VIDEO_ENABLED
/**
 * Request remote side to send us a Video Fast Update.
**/
void linphone_call_send_vfu_request(LinphoneCall *call)
{
	if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
		sal_call_send_vfu_request(call->op);
}
#endif

768 769 770
/**
 *
**/
771 772 773 774 775 776
LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
	LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
	memcpy(ncp,cp,sizeof(LinphoneCallParams));
	return ncp;
}

777 778 779
/**
 *
**/
780 781 782 783
void linphone_call_params_destroy(LinphoneCallParams *p){
	ms_free(p);
}

Simon Morlat's avatar
Simon Morlat committed
784 785 786 787
/**
 * @}
**/

788 789 790 791 792 793 794 795

#ifdef TEST_EXT_RENDERER
static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
	ms_message("rendercb, local buffer=%p, remote buffer=%p",
	           local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
}
#endif

796 797 798 799 800
#ifdef VIDEO_ENABLED
static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
	ms_warning("In linphonecall.c: video_stream_event_cb");
	switch (event_id) {
		case MS_VIDEO_DECODER_DECODING_ERRORS:
Guillaume Beraudo's avatar
Guillaume Beraudo committed
801
			ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
802 803 804 805 806 807 808 809
			linphone_call_send_vfu_request((LinphoneCall*) user_pointer);
			break;
		default:
			ms_warning("Unhandled event %i", event_id);
			break;
	}
}
#endif
jehan's avatar
jehan committed
810

811 812 813 814
void linphone_call_init_media_streams(LinphoneCall *call){
	LinphoneCore *lc=call->core;
	SalMediaDescription *md=call->localdesc;
	AudioStream *audiostream;
815

816 817 818 819 820 821 822 823 824 825 826
	call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc));
	if (linphone_core_echo_limiter_enabled(lc)){
		const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
		if (strcasecmp(type,"mic")==0)
			audio_stream_enable_echo_limiter(audiostream,ELControlMic);
		else if (strcasecmp(type,"full")==0)
			audio_stream_enable_echo_limiter(audiostream,ELControlFull);
	}
	audio_stream_enable_gain_control(audiostream,TRUE);
	if (linphone_core_echo_cancellation_enabled(lc)){
		int len,delay,framesize;
827
		const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
828 829 830 831
		len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
		delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
		framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
		audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
832 833 834
		if (statestr && audiostream->ec){
			ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
		}
835 836 837 838 839 840
	}
	audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
	{
		int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
		audio_stream_enable_noise_gate(audiostream,enabled);
	}
841

842 843 844
	if (lc->a_rtp)
		rtp_session_set_transports(audiostream->session,lc->a_rtp,lc->a_rtcp);

845 846 847
	call->audiostream_app_evq = ortp_ev_queue_new();
	rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);

848
#ifdef VIDEO_ENABLED
849

850 851
	if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){
		call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc));
852 853
	if( lc->video_conf.displaytype != NULL)
		video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
854
	video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
855 856
	if (lc->v_rtp)
		rtp_session_set_transports(call->videostream->session,lc->v_rtp,lc->v_rtcp);
857 858
	call->videostream_app_evq = ortp_ev_queue_new();
	rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
859 860 861 862 863
#ifdef TEST_EXT_RENDERER
		video_stream_set_render_callback(call->videostream,rendercb,NULL);
#endif
	}
#else
864
	call->videostream=NULL;
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
#endif
}


static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};

static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){
	LinphoneCore* lc = (LinphoneCore*)user_data;
	if (dtmf<0 || dtmf>15){
		ms_warning("Bad dtmf value %i",dtmf);
		return;
	}
	if (lc->vtable.dtmf_received != NULL)
		lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
}

static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
	if (st->equalizer){
		MSFilter *f=st->equalizer;
		int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
		const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
		ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
		if (enabled){
			if (gains){
				do{
					int bytes;
					MSEqualizerGain g;
					if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
						ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
						ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
						gains+=bytes;
					}else break;
				}while(1);
			}
		}
	}
}

903
void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
904 905 906 907 908 909
	float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1);
	float thres = 0;
	float recv_gain;
	float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
	float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
	int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
910

Simon Morlat's avatar
Simon Morlat committed
911
	if (!muted)
912
		audio_stream_set_mic_gain(st,mic_gain);
913
	else
914
		audio_stream_set_mic_gain(st,0);
915 916 917 918 919

	recv_gain = lc->sound_conf.soft_play_lev;
	if (recv_gain != 0) {
		linphone_core_set_playback_gain_db (lc,recv_gain);
	}
920
	
921 922 923 924 925 926
	if (st->volsend){
		ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
		float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
		thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
		float force=lp_config_get_float(lc->config,"sound","el_force",-1);
		int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
927
		float transmit_thres=lp_config_get_float(lc->config,"sound","el_transmit_thres",-1);
928
		MSFilter *f=NULL;
jehan's avatar
jehan committed
929 930 931 932 933 934 935 936 937 938
		f=st->volsend;
		if (speed==-1) speed=0.03;
		if (force==-1) force=25;
		ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
		ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
		if (thres!=-1)
			ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
		if (sustain!=-1)
			ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
		if (transmit_thres!=-1)
939
				ms_filter_call_method(f,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&transmit_thres);
jehan's avatar
jehan committed
940

941 942 943 944 945 946
		ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
		ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
	}
	if (st->volrecv){
		/* parameters for a limited noise-gate effect, using echo limiter threshold */
		float floorgain = 1/mic_gain;
947 948 949
		int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0);
		ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc);
		ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
950 951 952
		ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
	}
	parametrize_equalizer(lc,st);
Simon Morlat's avatar
Simon Morlat committed
953 954 955 956 957 958
}

static void post_configure_audio_streams(LinphoneCall*call){
	AudioStream *st=call->audiostream;
	LinphoneCore *lc=call->core;
	_post_configure_audio_stream(st,lc,call->audio_muted);
959 960 961 962 963 964 965
	if (lc->vtable.dtmf_received!=NULL){
		/* replace by our default action*/
		audio_stream_play_received_dtmfs(call->audiostream,FALSE);
		rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);
	}
}

Simon Morlat's avatar
Simon Morlat committed
966
static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
967 968 969 970 971
	int bw;
	const MSList *elem;
	RtpProfile *prof=rtp_profile_new("Call profile");
	bool_t first=TRUE;
	int remote_bw=0;
Simon Morlat's avatar
Simon Morlat committed
972
	LinphoneCore *lc=call->core;
973
	int up_ptime=0;
974
	*used_pt=-1;
975

976 977
	for(elem=desc->payloads;elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
978
		int number;
979

980
		if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
981
			if (desc->type==SalAudio){
Simon Morlat's avatar
Simon Morlat committed
982
				linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
983
				up_ptime=linphone_core_get_upload_ptime(lc);
984 985 986 987 988 989 990 991 992
			}
			*used_pt=payload_type_get_number(pt);
			first=FALSE;
		}
		if (desc->bandwidth>0) remote_bw=desc->bandwidth;
		else if (md->bandwidth>0) {
			/*case where b=AS is given globally, not per stream*/
			remote_bw=md->bandwidth;
			if (desc->type==SalVideo){
993
				remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
994 995
			}
		}
996 997

		if (desc->type==SalAudio){
Simon Morlat's avatar
Simon Morlat committed
998
				bw=get_min_bandwidth(call->audio_bw,remote_bw);
999
		}else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
1000 1001 1002 1003 1004
		if (bw>0) pt->normal_bitrate=bw*1000;
		else if (desc->type==SalAudio){
			pt->normal_bitrate=-1;
		}
		if (desc->ptime>0){
1005 1006 1007
			up_ptime=desc->ptime;
		}
		if (up_ptime>0){
1008
			char tmp[40];
1009
			snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
1010 1011
			payload_type_append_send_fmtp(pt,tmp);
		}
1012 1013 1014 1015 1016
		number=payload_type_get_number(pt);
		if (rtp_profile_get_payload(prof,number)!=NULL){
			ms_warning("A payload type with number %i already exists in profile !",number);
		}else
			rtp_profile_set_payload(prof,number,pt);
1017 1018 1019 1020
	}
	return prof;
}

1021

1022 1023
static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
	int pause_time=3000;
1024
	audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
Simon Morlat's avatar
Simon Morlat committed