4 Copyright (C) 2010 Belledonne Communications SARL
5 (simon.morlat@linphone.org)
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "linphonecore.h"
28 #include <ortp/event.h>
32 #include "mediastreamer2/mediastream.h"
33 #include "mediastreamer2/msvolume.h"
34 #include "mediastreamer2/msequalizer.h"
35 #include "mediastreamer2/msfileplayer.h"
36 #include "mediastreamer2/msjpegwriter.h"
37 #include "mediastreamer2/mseventqueue.h"
38 #include "mediastreamer2/mssndcard.h"
41 static MSWebCam *get_nowebcam_device(){
42 return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
46 static bool_t generate_b64_crypto_key(int key_length, char* key_out) {
48 uint8_t* tmp = (uint8_t*) malloc(key_length);
49 if (ortp_crypto_get_random(tmp, key_length)!=0) {
50 ms_error("Failed to generate random key");
55 b64_size = b64_encode((const char*)tmp, key_length, NULL, 0);
57 ms_error("Failed to b64 encode key");
61 key_out[b64_size] = '\0';
62 b64_encode((const char*)tmp, key_length, key_out, 40);
67 LinphoneCore *linphone_call_get_core(const LinphoneCall *call){
71 const char* linphone_call_get_authentication_token(LinphoneCall *call){
72 return call->auth_token;
75 bool_t linphone_call_get_authentication_token_verified(LinphoneCall *call){
76 return call->auth_token_verified;
79 static bool_t linphone_call_are_all_streams_encrypted(LinphoneCall *call) {
80 // Check ZRTP encryption in audiostream
81 if (!call->audiostream_encrypted) {
86 // If video enabled, check ZRTP encryption in videostream
87 const LinphoneCallParams *params=linphone_call_get_current_params(call);
88 if (params->has_video && !call->videostream_encrypted) {
96 void propagate_encryption_changed(LinphoneCall *call){
97 LinphoneCore *lc=call->core;
98 if (!linphone_call_are_all_streams_encrypted(call)) {
99 ms_message("Some streams are not encrypted");
100 call->current_params.media_encryption=LinphoneMediaEncryptionNone;
101 if (lc->vtable.call_encryption_changed)
102 lc->vtable.call_encryption_changed(call->core, call, FALSE, call->auth_token);
104 ms_message("All streams are encrypted");
105 call->current_params.media_encryption=LinphoneMediaEncryptionZRTP;
106 if (lc->vtable.call_encryption_changed)
107 lc->vtable.call_encryption_changed(call->core, call, TRUE, call->auth_token);
112 static void linphone_call_videostream_encryption_changed(void *data, bool_t encrypted){
113 ms_message("Video stream is %s", encrypted ? "encrypted" : "not encrypted");
115 LinphoneCall *call = (LinphoneCall *)data;
116 call->videostream_encrypted=encrypted;
117 propagate_encryption_changed(call);
121 static void linphone_call_audiostream_encryption_changed(void *data, bool_t encrypted) {
122 char status[255]={0};
123 ms_message("Audio stream is %s ", encrypted ? "encrypted" : "not encrypted");
125 LinphoneCall *call = (LinphoneCall *)data;
126 call->audiostream_encrypted=encrypted;
128 if (encrypted && call->core->vtable.display_status != NULL) {
129 snprintf(status,sizeof(status)-1,_("Authentication token is %s"),call->auth_token);
130 call->core->vtable.display_status(call->core, status);
133 propagate_encryption_changed(call);
137 // Enable video encryption
138 const LinphoneCallParams *params=linphone_call_get_current_params(call);
139 if (params->has_video) {
140 ms_message("Trying to enable encryption on video stream");
141 OrtpZrtpParams params;
142 params.zid_file=NULL; //unused
143 video_stream_enable_zrtp(call->videostream,call->audiostream,¶ms);
149 static void linphone_call_audiostream_auth_token_ready(void *data, const char* auth_token, bool_t verified) {
150 LinphoneCall *call=(LinphoneCall *)data;
151 if (call->auth_token != NULL)
152 ms_free(call->auth_token);
154 call->auth_token=ms_strdup(auth_token);
155 call->auth_token_verified=verified;
157 ms_message("Authentication token is %s (%s)", auth_token, verified?"verified":"unverified");
160 void linphone_call_set_authentication_token_verified(LinphoneCall *call, bool_t verified){
161 if (call->audiostream==NULL){
162 ms_error("linphone_call_set_authentication_token_verified(): No audio stream");
164 if (call->audiostream->ortpZrtpContext==NULL){
165 ms_error("linphone_call_set_authentication_token_verified(): No zrtp context.");
167 if (!call->auth_token_verified && verified){
168 ortp_zrtp_sas_verified(call->audiostream->ortpZrtpContext);
169 }else if (call->auth_token_verified && !verified){
170 ortp_zrtp_sas_reset_verified(call->audiostream->ortpZrtpContext);
172 call->auth_token_verified=verified;
173 propagate_encryption_changed(call);
176 static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){
179 if (max_sample_rate) *max_sample_rate=0;
180 for(it=codecs;it!=NULL;it=it->next){
181 PayloadType *pt=(PayloadType*)it->data;
182 if (pt->flags & PAYLOAD_TYPE_ENABLED){
183 if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){
184 ms_message("Codec %s/%i eliminated because of audio bandwidth constraint.",pt->mime_type,pt->clock_rate);
187 if (linphone_core_check_payload_type_usability(lc,pt)){
188 l=ms_list_append(l,payload_type_clone(pt));
189 if (max_sample_rate && payload_type_get_rate(pt)>*max_sample_rate) *max_sample_rate=payload_type_get_rate(pt);
196 static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){
198 strcpy(md->streams[0].rtp_addr,ac->addr);
199 md->streams[0].rtp_port=ac->port;
200 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0) || md->nstreams==1){
201 strcpy(md->addr,ac->addr);
205 strcpy(md->streams[1].rtp_addr,vc->addr);
206 md->streams[1].rtp_port=vc->port;
212 static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, LinphoneCall *call, unsigned int session_id, unsigned int session_ver){
216 const char *me=linphone_core_get_identity(lc);
217 LinphoneAddress *addr=linphone_address_new(me);
218 const char *username=linphone_address_get_username (addr);
219 SalMediaDescription *md=sal_media_description_new();
221 if (call->ping_time>0) {
222 linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
225 md->session_id=session_id;
226 md->session_ver=session_ver;
228 strncpy(md->addr,call->localip,sizeof(md->addr));
229 strncpy(md->username,username,sizeof(md->username));
231 if (call->params.down_bw)
232 md->bandwidth=call->params.down_bw;
233 else md->bandwidth=linphone_core_get_download_bandwidth(lc);
235 /*set audio capabilities */
236 strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr));
237 strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr));
238 md->streams[0].rtp_port=call->audio_port;
239 md->streams[0].rtcp_port=call->audio_port+1;
240 md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ?
241 SalProtoRtpSavp : SalProtoRtpAvp;
242 md->streams[0].type=SalAudio;
243 if (call->params.down_ptime)
244 md->streams[0].ptime=call->params.down_ptime;
246 md->streams[0].ptime=linphone_core_get_download_ptime(lc);
247 l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate);
248 pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
249 l=ms_list_append(l,pt);
250 md->streams[0].payloads=l;
254 if (call->params.has_video){
256 md->streams[1].rtp_port=call->video_port;
257 md->streams[1].rtcp_port=call->video_port+1;
258 md->streams[1].proto=md->streams[0].proto;
259 md->streams[1].type=SalVideo;
260 l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL);
261 md->streams[1].payloads=l;
264 for(i=0; i<md->nstreams; i++) {
265 if (md->streams[i].proto == SalProtoRtpSavp) {
266 md->streams[i].crypto[0].tag = 1;
267 md->streams[i].crypto[0].algo = AES_128_SHA1_80;
268 if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
269 md->streams[i].crypto[0].algo = 0;
270 md->streams[i].crypto[1].tag = 2;
271 md->streams[i].crypto[1].algo = AES_128_SHA1_32;
272 if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
273 md->streams[i].crypto[1].algo = 0;
274 md->streams[i].crypto[2].algo = 0;
277 update_media_description_from_stun(md,&call->ac,&call->vc);
278 if (call->ice_session != NULL) {
279 linphone_core_update_local_media_description_from_ice(md, call->ice_session);
280 linphone_core_update_ice_state_in_call_stats(call);
282 linphone_address_destroy(addr);
286 void update_local_media_description(LinphoneCore *lc, LinphoneCall *call){
287 SalMediaDescription *md=call->localdesc;
289 call->localdesc = create_local_media_description(lc,call);
291 call->localdesc = _create_local_media_description(lc,call,md->session_id,md->session_ver+1);
292 sal_media_description_unref(md);
296 SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call){
297 unsigned int id=rand() & 0xfff;
298 return _create_local_media_description(lc,call,id,id);
301 static int find_port_offset(LinphoneCore *lc, SalStreamType type){
306 bool_t already_used=FALSE;
307 for(offset=0;offset<100;offset+=2){
311 tried_port=linphone_core_get_audio_port (lc)+offset;
314 tried_port=linphone_core_get_video_port (lc)+offset;
318 for(elem=lc->calls;elem!=NULL;elem=elem->next){
319 LinphoneCall *call=(LinphoneCall*)elem->data;
323 existing_port = call->audio_port;
326 existing_port = call->video_port;
329 if (existing_port==tried_port) {
334 if (!already_used) break;
337 ms_error("Could not find any free port !");
343 static int select_random_port(LinphoneCore *lc, SalStreamType type) {
347 int existing_port = 0;
348 int min_port = 0, max_port = 0;
349 bool_t already_used = FALSE;
354 linphone_core_get_audio_port_range(lc, &min_port, &max_port);
357 linphone_core_get_video_port_range(lc, &min_port, &max_port);
360 tried_port = (rand() % (max_port - min_port) + min_port) & ~0x1;
361 if (tried_port < min_port) tried_port = min_port + 2;
362 for (nb_tries = 0; nb_tries < 100; nb_tries++) {
363 for (elem = lc->calls; elem != NULL; elem = elem->next) {
364 LinphoneCall *call = (LinphoneCall *)elem->data;
368 existing_port = call->audio_port;
371 existing_port = call->video_port;
374 if (existing_port == tried_port) {
379 if (!already_used) break;
381 if (nb_tries == 100) {
382 ms_error("Could not find any free port!");
388 static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
390 int min_port, max_port;
391 call->magic=linphone_call_magic;
393 call->state=LinphoneCallIdle;
394 call->transfer_state = LinphoneCallIdle;
395 call->start_time=time(NULL);
396 call->media_start_time=0;
397 call->log=linphone_call_log_new(call, from, to);
398 call->owns_call_log=TRUE;
399 linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
400 linphone_core_get_audio_port_range(call->core, &min_port, &max_port);
401 if (min_port == max_port) {
402 /* Used fixed RTP audio port. */
403 port_offset=find_port_offset (call->core, SalAudio);
404 if (port_offset==-1) return;
405 call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
407 /* Select random RTP audio port in the specified range. */
408 call->audio_port = select_random_port(call->core, SalAudio);
410 linphone_core_get_video_port_range(call->core, &min_port, &max_port);
411 if (min_port == max_port) {
412 /* Used fixed RTP video port. */
413 port_offset=find_port_offset (call->core, SalVideo);
414 if (port_offset==-1) return;
415 call->video_port=linphone_core_get_video_port(call->core)+port_offset;
417 /* Select random RTP video port in the specified range. */
418 call->video_port = select_random_port(call->core, SalVideo);
420 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
421 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
424 void linphone_call_init_stats(LinphoneCallStats *stats, int type) {
426 stats->received_rtcp = NULL;
427 stats->sent_rtcp = NULL;
428 stats->ice_state = LinphoneIceStateNotActivated;
432 static void discover_mtu(LinphoneCore *lc, const char *remote){
434 if (lc->net_conf.mtu==0 ){
435 /*attempt to discover mtu*/
436 mtu=ms_discover_mtu(remote);
439 ms_message("Discovered mtu is %i, RTP payload max size is %i",
440 mtu, ms_get_payload_max_size());
445 LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
447 LinphoneCall *call=ms_new0(LinphoneCall,1);
448 call->dir=LinphoneCallOutgoing;
449 call->op=sal_op_new(lc->sal);
450 sal_op_set_user_pointer(call->op,call);
452 linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
453 linphone_call_init_common(call,from,to);
454 call->params=*params;
455 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
456 call->ice_session = ice_session_new();
457 ice_session_set_role(call->ice_session, IR_Controlling);
459 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
460 call->ping_time=linphone_core_run_stun_tests(call->core,call);
462 call->camera_active=params->has_video;
464 discover_mtu(lc,linphone_address_get_domain (to));
465 if (params->referer){
466 sal_call_set_referer(call->op,params->referer->op);
467 call->referer=linphone_call_ref(params->referer);
472 LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
473 LinphoneCall *call=ms_new0(LinphoneCall,1);
476 call->dir=LinphoneCallIncoming;
477 sal_op_set_user_pointer(op,call);
481 if (lc->sip_conf.ping_with_options){
482 /*the following sends an option request back to the caller so that
483 we get a chance to discover our nat'd address before answering.*/
484 call->ping_op=sal_op_new(lc->sal);
485 from_str=linphone_address_as_string_uri_only(from);
486 sal_op_set_route(call->ping_op,sal_op_get_network_origin(op));
487 sal_op_set_user_pointer(call->ping_op,call);
488 sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str);
492 linphone_address_clean(from);
493 linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
494 linphone_call_init_common(call, from, to);
495 call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/
496 linphone_core_init_default_params(lc, &call->params);
497 call->params.has_video &= !!lc->video_policy.automatically_accept;
498 call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(op));
499 switch (linphone_core_get_firewall_policy(call->core)) {
500 case LinphonePolicyUseIce:
501 call->ice_session = ice_session_new();
502 ice_session_set_role(call->ice_session, IR_Controlled);
503 linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
504 if (call->ice_session != NULL) {
505 linphone_call_init_media_streams(call);
506 linphone_call_start_media_streams_for_ice_gathering(call);
507 if (linphone_core_gather_ice_candidates(call->core,call)<0) {
508 /* Ice candidates gathering failed, proceed with the call anyway. */
509 linphone_call_delete_ice_session(call);
510 linphone_call_stop_media_streams_for_ice_gathering(call);
514 case LinphonePolicyUseStun:
515 call->ping_time=linphone_core_run_stun_tests(call->core,call);
516 /* No break to also destroy ice session in this case. */
520 call->camera_active=call->params.has_video;
522 discover_mtu(lc,linphone_address_get_domain(from));
526 /* this function is called internally to get rid of a call.
527 It performs the following tasks:
528 - remove the call from the internal list of calls
529 - update the call logs accordingly
532 static void linphone_call_set_terminated(LinphoneCall *call){
533 LinphoneCore *lc=call->core;
535 linphone_core_update_allocated_audio_bandwidth(lc);
537 call->owns_call_log=FALSE;
538 linphone_call_log_completed(call);
541 if (call == lc->current_call){
542 ms_message("Resetting the current call");
543 lc->current_call=NULL;
546 if (linphone_core_del_call(lc,call) != 0){
547 ms_error("Could not remove the call from the list !!!");
550 if (ms_list_size(lc->calls)==0)
551 linphone_core_notify_all_friends(lc,lc->presence_mode);
553 linphone_core_conference_check_uninit(lc);
554 if (call->ringing_beep){
555 linphone_core_stop_dtmf(lc);
556 call->ringing_beep=FALSE;
559 linphone_call_unref(call->referer);
564 void linphone_call_fix_call_parameters(LinphoneCall *call){
565 call->params.has_video=call->current_params.has_video;
566 call->params.media_encryption=call->current_params.media_encryption;
569 const char *linphone_call_state_to_string(LinphoneCallState cs){
571 case LinphoneCallIdle:
572 return "LinphoneCallIdle";
573 case LinphoneCallIncomingReceived:
574 return "LinphoneCallIncomingReceived";
575 case LinphoneCallOutgoingInit:
576 return "LinphoneCallOutgoingInit";
577 case LinphoneCallOutgoingProgress:
578 return "LinphoneCallOutgoingProgress";
579 case LinphoneCallOutgoingRinging:
580 return "LinphoneCallOutgoingRinging";
581 case LinphoneCallOutgoingEarlyMedia:
582 return "LinphoneCallOutgoingEarlyMedia";
583 case LinphoneCallConnected:
584 return "LinphoneCallConnected";
585 case LinphoneCallStreamsRunning:
586 return "LinphoneCallStreamsRunning";
587 case LinphoneCallPausing:
588 return "LinphoneCallPausing";
589 case LinphoneCallPaused:
590 return "LinphoneCallPaused";
591 case LinphoneCallResuming:
592 return "LinphoneCallResuming";
593 case LinphoneCallRefered:
594 return "LinphoneCallRefered";
595 case LinphoneCallError:
596 return "LinphoneCallError";
597 case LinphoneCallEnd:
598 return "LinphoneCallEnd";
599 case LinphoneCallPausedByRemote:
600 return "LinphoneCallPausedByRemote";
601 case LinphoneCallUpdatedByRemote:
602 return "LinphoneCallUpdatedByRemote";
603 case LinphoneCallIncomingEarlyMedia:
604 return "LinphoneCallIncomingEarlyMedia";
605 case LinphoneCallUpdating:
606 return "LinphoneCallUpdating";
607 case LinphoneCallReleased:
608 return "LinphoneCallReleased";
610 return "undefined state";
613 void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
614 LinphoneCore *lc=call->core;
616 if (call->state!=cstate){
617 if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
618 if (cstate!=LinphoneCallReleased){
619 ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
620 linphone_call_state_to_string(cstate));
624 ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
625 linphone_call_state_to_string(cstate));
626 if (cstate!=LinphoneCallRefered){
627 /*LinphoneCallRefered is rather an event, not a state.
628 Indeed it does not change the state of the call (still paused or running)*/
631 if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
632 switch(call->reason){
633 case LinphoneReasonDeclined:
634 call->log->status=LinphoneCallDeclined;
636 case LinphoneReasonNotAnswered:
637 call->log->status=LinphoneCallMissed;
642 linphone_call_set_terminated (call);
644 if (cstate == LinphoneCallConnected) {
645 call->log->status=LinphoneCallSuccess;
646 call->media_start_time=time(NULL);
649 if (lc->vtable.call_state_changed)
650 lc->vtable.call_state_changed(lc,call,cstate,message);
651 if (cstate==LinphoneCallReleased){
652 if (call->op!=NULL) {
653 /* so that we cannot have anymore upcalls for SAL
654 concerning this call*/
655 sal_op_release(call->op);
658 linphone_call_unref(call);
663 static void linphone_call_destroy(LinphoneCall *obj)
665 linphone_call_delete_ice_session(obj);
667 sal_op_release(obj->op);
670 if (obj->resultdesc!=NULL) {
671 sal_media_description_unref(obj->resultdesc);
672 obj->resultdesc=NULL;
674 if (obj->localdesc!=NULL) {
675 sal_media_description_unref(obj->localdesc);
679 sal_op_release(obj->ping_op);
682 ms_free(obj->refer_to);
684 if (obj->owns_call_log)
685 linphone_call_log_destroy(obj->log);
686 if (obj->auth_token) {
687 ms_free(obj->auth_token);
694 * @addtogroup call_control
699 * Increments the call 's reference count.
700 * An application that wishes to retain a pointer to call object
701 * must use this function to unsure the pointer remains
702 * valid. Once the application no more needs this pointer,
703 * it must call linphone_call_unref().
705 LinphoneCall * linphone_call_ref(LinphoneCall *obj){
711 * Decrements the call object reference count.
712 * See linphone_call_ref().
714 void linphone_call_unref(LinphoneCall *obj){
717 linphone_call_destroy(obj);
722 * Returns current parameters associated to the call.
724 const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
725 return &call->current_params;
728 static bool_t is_video_active(const SalStreamDescription *sd){
729 return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
733 * Returns call parameters proposed by remote.
735 * This is useful when receiving an incoming call, to know whether the remote party
736 * supports video, encryption or whatever.
738 const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
739 LinphoneCallParams *cp=&call->remote_params;
740 memset(cp,0,sizeof(*cp));
742 SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
744 SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
746 asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
747 vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
748 secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
749 secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
751 cp->has_video=is_video_active(secure_vsd);
752 if (secure_asd || asd==NULL)
753 cp->media_encryption=LinphoneMediaEncryptionSRTP;
755 cp->has_video=is_video_active(vsd);
764 * Returns the remote address associated to this call
767 const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
768 return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
772 * Returns the remote address associated to this call as a string.
774 * The result string must be freed by user using ms_free().
776 char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
777 return linphone_address_as_string(linphone_call_get_remote_address(call));
781 * Retrieves the call's current state.
783 LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
788 * Returns the reason for a call termination (either error or normal termination)
790 LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
795 * Get the user_pointer in the LinphoneCall
797 * @ingroup call_control
799 * return user_pointer an opaque user pointer that can be retrieved at any time
801 void *linphone_call_get_user_pointer(LinphoneCall *call)
803 return call->user_pointer;
807 * Set the user_pointer in the LinphoneCall
809 * @ingroup call_control
811 * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
813 void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
815 call->user_pointer = user_pointer;
819 * Returns the call log associated to this call.
821 LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
826 * Returns the refer-to uri (if the call was transfered).
828 const char *linphone_call_get_refer_to(const LinphoneCall *call){
829 return call->refer_to;
833 * Returns direction of the call (incoming or outgoing).
835 LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
836 return call->log->dir;
840 * Returns the far end's user agent description string, if available.
842 const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
844 return sal_op_get_remote_ua (call->op);
850 * Returns true if this calls has received a transfer that has not been
852 * Pending transfers are executed when this call is being paused or closed,
853 * locally or by remote endpoint.
854 * If the call is already paused while receiving the transfer request, the
855 * transfer immediately occurs.
857 bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
858 return call->refer_pending;
862 * Returns call's duration in seconds.
864 int linphone_call_get_duration(const LinphoneCall *call){
865 if (call->media_start_time==0) return 0;
866 return time(NULL)-call->media_start_time;
870 * Returns the call object this call is replacing, if any.
871 * Call replacement can occur during call transfers.
872 * By default, the core automatically terminates the replaced call and accept the new one.
873 * This function allows the application to know whether a new incoming call is a one that replaces another one.
875 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
876 SalOp *op=sal_call_get_replaces(call->op);
878 return (LinphoneCall*)sal_op_get_user_pointer(op);
884 * Indicate whether camera input should be sent to remote end.
886 void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
888 if (call->videostream!=NULL && call->videostream->ticker!=NULL){
889 LinphoneCore *lc=call->core;
890 MSWebCam *nowebcam=get_nowebcam_device();
891 if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
892 video_stream_change_camera(call->videostream,
893 enable ? lc->video_conf.device : nowebcam);
896 call->camera_active=enable;
901 * Take a photo of currently received video and write it into a jpeg file.
903 int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
905 if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){
906 return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
908 ms_warning("Cannot take snapshot: no currently running video stream on this call.");
915 * Returns TRUE if camera pictures are sent to the remote party.
917 bool_t linphone_call_camera_enabled (const LinphoneCall *call){
918 return call->camera_active;
922 * Enable video stream.
924 void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
925 cp->has_video=enabled;
928 const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
929 return cp->audio_codec;
932 const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
933 return cp->video_codec;
936 bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
937 return cp->low_bandwidth;
940 * Returns whether video is enabled.
942 bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
943 return cp->has_video;
946 enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
947 return cp->media_encryption;
950 void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
951 cp->media_encryption = e;
956 * Enable sending of real early media (during outgoing calls).
958 void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
959 cp->real_early_media=enabled;
962 bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
963 return cp->real_early_media;
967 * Returns true if the call is part of the locally managed conference.
969 bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
970 return cp->in_conference;
974 * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
975 * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
977 void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
978 cp->audio_bw=bandwidth;
983 * Request remote side to send us a Video Fast Update.
985 void linphone_call_send_vfu_request(LinphoneCall *call)
987 if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
988 sal_call_send_vfu_request(call->op);
995 LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
996 LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
997 memcpy(ncp,cp,sizeof(LinphoneCallParams));
1004 void linphone_call_params_destroy(LinphoneCallParams *p){
1013 #ifdef TEST_EXT_RENDERER
1014 static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
1015 ms_message("rendercb, local buffer=%p, remote buffer=%p",
1016 local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
1020 #ifdef VIDEO_ENABLED
1021 static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
1022 LinphoneCall* call = (LinphoneCall*) user_pointer;
1023 ms_warning("In linphonecall.c: video_stream_event_cb");
1025 case MS_VIDEO_DECODER_DECODING_ERRORS:
1026 ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
1027 linphone_call_send_vfu_request(call);
1029 case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
1030 ms_message("First video frame decoded successfully");
1031 if (call->nextVideoFrameDecoded._func != NULL)
1032 call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
1035 ms_warning("Unhandled event %i", event_id);
1041 void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) {
1042 call->nextVideoFrameDecoded._func = cb;
1043 call->nextVideoFrameDecoded._user_data = user_data;
1044 #ifdef VIDEO_ENABLED
1045 ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
1049 void linphone_call_init_audio_stream(LinphoneCall *call){
1050 LinphoneCore *lc=call->core;
1051 AudioStream *audiostream;
1054 if (call->audiostream != NULL) return;
1055 call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc));
1056 dscp=linphone_core_get_audio_dscp(lc);
1058 audio_stream_set_dscp(audiostream,dscp);
1059 if (linphone_core_echo_limiter_enabled(lc)){
1060 const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
1061 if (strcasecmp(type,"mic")==0)
1062 audio_stream_enable_echo_limiter(audiostream,ELControlMic);
1063 else if (strcasecmp(type,"full")==0)
1064 audio_stream_enable_echo_limiter(audiostream,ELControlFull);
1066 audio_stream_enable_gain_control(audiostream,TRUE);
1067 if (linphone_core_echo_cancellation_enabled(lc)){
1068 int len,delay,framesize;
1069 const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
1070 len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
1071 delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
1072 framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
1073 audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
1074 if (statestr && audiostream->ec){
1075 ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
1078 audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
1080 int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
1081 audio_stream_enable_noise_gate(audiostream,enabled);
1084 audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc));
1087 RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port);
1088 RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1);
1089 rtp_session_set_transports(audiostream->session,artp,artcp);
1091 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1092 rtp_session_set_pktinfo(audiostream->session, TRUE);
1093 rtp_session_set_symmetric_rtp(audiostream->session, FALSE);
1094 if (ice_session_check_list(call->ice_session, 0) == NULL) {
1095 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1097 audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0);
1098 ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session);
1101 call->audiostream_app_evq = ortp_ev_queue_new();
1102 rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);
1105 void linphone_call_init_video_stream(LinphoneCall *call){
1106 #ifdef VIDEO_ENABLED
1107 LinphoneCore *lc=call->core;
1109 if (!call->params.has_video) {
1110 linphone_call_stop_video_stream(call);
1113 if (call->videostream != NULL) return;
1114 if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){
1115 int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
1116 int dscp=linphone_core_get_video_dscp(lc);
1118 call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc));
1120 video_stream_set_dscp(call->videostream,dscp);
1121 video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
1122 if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size);
1124 if( lc->video_conf.displaytype != NULL)
1125 video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
1126 video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
1128 RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
1129 RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
1130 rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
1132 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1133 rtp_session_set_pktinfo(call->videostream->session, TRUE);
1134 rtp_session_set_symmetric_rtp(call->videostream->session, FALSE);
1135 if (ice_session_check_list(call->ice_session, 1) == NULL) {
1136 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1138 call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1);
1139 ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session);
1141 call->videostream_app_evq = ortp_ev_queue_new();
1142 rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
1143 #ifdef TEST_EXT_RENDERER
1144 video_stream_set_render_callback(call->videostream,rendercb,NULL);
1148 call->videostream=NULL;
1152 void linphone_call_init_media_streams(LinphoneCall *call){
1153 linphone_call_init_audio_stream(call);
1154 linphone_call_init_video_stream(call);
1158 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
1160 static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){
1161 if (dtmf<0 || dtmf>15){
1162 ms_warning("Bad dtmf value %i",dtmf);
1165 if (lc->vtable.dtmf_received != NULL)
1166 lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
1169 static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
1171 MSFilter *f=st->equalizer;
1172 int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
1173 const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
1174 ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
1180 if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
1181 ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
1182 ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
1191 void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
1192 float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1);
1195 float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
1196 float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
1197 int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
1200 audio_stream_set_mic_gain(st,mic_gain);
1202 audio_stream_set_mic_gain(st,0);
1204 recv_gain = lc->sound_conf.soft_play_lev;
1205 if (recv_gain != 0) {
1206 linphone_core_set_playback_gain_db (lc,recv_gain);
1210 ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
1211 float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
1212 thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
1213 float force=lp_config_get_float(lc->config,"sound","el_force",-1);
1214 int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
1215 float transmit_thres=lp_config_get_float(lc->config,"sound","el_transmit_thres",-1);
1218 if (speed==-1) speed=0.03;
1219 if (force==-1) force=25;
1220 ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
1221 ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
1223 ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
1225 ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
1226 if (transmit_thres!=-1)
1227 ms_filter_call_method(f,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&transmit_thres);
1229 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1230 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
1233 /* parameters for a limited noise-gate effect, using echo limiter threshold */
1234 float floorgain = 1/mic_gain;
1235 int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0);
1236 ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc);
1237 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1238 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
1240 parametrize_equalizer(lc,st);
1243 static void post_configure_audio_streams(LinphoneCall*call){
1244 AudioStream *st=call->audiostream;
1245 LinphoneCore *lc=call->core;
1246 _post_configure_audio_stream(st,lc,call->audio_muted);
1247 if (lc->vtable.dtmf_received!=NULL){
1248 /* replace by our default action*/
1249 audio_stream_play_received_dtmfs(call->audiostream,FALSE);
1250 /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/
1254 static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
1257 RtpProfile *prof=rtp_profile_new("Call profile");
1260 LinphoneCore *lc=call->core;
1262 const LinphoneCallParams *params=&call->params;
1265 for(elem=desc->payloads;elem!=NULL;elem=elem->next){
1266 PayloadType *pt=(PayloadType*)elem->data;
1269 if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
1270 if (desc->type==SalAudio){
1271 linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
1272 if (params->up_ptime)
1273 up_ptime=params->up_ptime;
1274 else up_ptime=linphone_core_get_upload_ptime(lc);
1276 *used_pt=payload_type_get_number(pt);
1279 if (desc->bandwidth>0) remote_bw=desc->bandwidth;
1280 else if (md->bandwidth>0) {
1281 /*case where b=AS is given globally, not per stream*/
1282 remote_bw=md->bandwidth;
1283 if (desc->type==SalVideo){
1284 remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
1288 if (desc->type==SalAudio){
1289 int audio_bw=call->audio_bw;
1291 if (params->up_bw< audio_bw)
1292 audio_bw=params->up_bw;
1294 bw=get_min_bandwidth(audio_bw,remote_bw);
1295 }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
1296 if (bw>0) pt->normal_bitrate=bw*1000;
1297 else if (desc->type==SalAudio){
1298 pt->normal_bitrate=-1;
1301 up_ptime=desc->ptime;
1305 snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
1306 payload_type_append_send_fmtp(pt,tmp);
1308 number=payload_type_get_number(pt);
1309 if (rtp_profile_get_payload(prof,number)!=NULL){
1310 ms_warning("A payload type with number %i already exists in profile !",number);
1312 rtp_profile_set_payload(prof,number,pt);
1318 static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
1319 int pause_time=3000;
1320 audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
1321 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1324 static bool_t linphone_call_sound_resources_available(LinphoneCall *call){
1325 LinphoneCore *lc=call->core;
1326 LinphoneCall *current=linphone_core_get_current_call(lc);
1327 return !linphone_core_is_in_conference(lc) &&
1328 (current==NULL || current==call);
1330 static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
1332 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
1333 if (crypto[i].tag == tag) {
1339 static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
1340 LinphoneCore *lc=call->core;
1342 char rtcp_tool[128]={0};
1343 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1344 /* look for savp stream first */
1345 const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
1346 SalProtoRtpSavp,SalAudio);
1347 /* no savp audio stream, use avp */
1349 stream=sal_media_description_find_stream(call->resultdesc,
1350 SalProtoRtpAvp,SalAudio);
1352 if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
1353 MSSndCard *playcard=lc->sound_conf.lsd_card ?
1354 lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
1355 MSSndCard *captcard=lc->sound_conf.capt_sndcard;
1356 const char *playfile=lc->play_file;
1357 const char *recfile=lc->rec_file;
1358 call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
1362 call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
1363 if (playcard==NULL) {
1364 ms_warning("No card defined for playback !");
1366 if (captcard==NULL) {
1367 ms_warning("No card defined for capture !");
1369 /*Replace soundcard filters by inactive file players or recorders
1370 when placed in recvonly or sendonly mode*/
1371 if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){
1374 }else if (stream->dir==SalStreamSendOnly){
1378 /*And we will eventually play "playfile" if set by the user*/
1381 if (send_ringbacktone){
1383 playfile=NULL;/* it is setup later*/
1385 /*if playfile are supplied don't use soundcards*/
1386 if (lc->use_files) {
1390 if (call->params.in_conference){
1391 /* first create the graph without soundcard resources*/
1392 captcard=playcard=NULL;
1394 if (!linphone_call_sound_resources_available(call)){
1395 ms_message("Sound resources are used by another call, not using soundcard.");
1396 captcard=playcard=NULL;
1398 use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
1399 if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
1400 if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
1401 audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
1402 audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
1403 audio_stream_start_full(
1405 call->audio_profile,
1406 stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr,
1408 stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
1409 linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0,
1411 linphone_core_get_audio_jittcomp(lc),
1418 post_configure_audio_streams(call);
1419 if (muted && !send_ringbacktone){
1420 audio_stream_set_mic_gain(call->audiostream,0);
1422 if (stream->dir==SalStreamSendOnly && playfile!=NULL){
1424 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1426 if (send_ringbacktone){
1427 setup_ring_player(lc,call);
1429 audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
1431 /* valid local tags are > 0 */
1432 if (stream->proto == SalProtoRtpSavp) {
1433 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1434 SalProtoRtpSavp,SalAudio);
1435 int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
1437 if (crypto_idx >= 0) {
1438 audio_stream_enable_strp(
1440 stream->crypto[0].algo,
1441 local_st_desc->crypto[crypto_idx].master_key,
1442 stream->crypto[0].master_key);
1443 call->audiostream_encrypted=TRUE;
1445 ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
1446 call->audiostream_encrypted=FALSE;
1448 }else call->audiostream_encrypted=FALSE;
1449 if (call->params.in_conference){
1450 /*transform the graph to connect it to the conference filter */
1451 bool_t mute=stream->dir==SalStreamRecvOnly;
1452 linphone_call_add_to_conf(call, mute);
1454 call->current_params.in_conference=call->params.in_conference;
1455 call->current_params.low_bandwidth=call->params.low_bandwidth;
1456 }else ms_warning("No audio stream accepted ?");
1460 static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){
1461 #ifdef VIDEO_ENABLED
1462 LinphoneCore *lc=call->core;
1464 /* look for savp stream first */
1465 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1466 SalProtoRtpSavp,SalVideo);
1467 char rtcp_tool[128]={0};
1468 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1470 /* no savp audio stream, use avp */
1472 vstream=sal_media_description_find_stream(call->resultdesc,
1473 SalProtoRtpAvp,SalVideo);
1475 /* shutdown preview */
1476 if (lc->previewstream!=NULL) {
1477 video_preview_stop(lc->previewstream);
1478 lc->previewstream=NULL;
1481 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
1482 const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
1483 const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
1484 call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
1486 call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
1487 VideoStreamDir dir=VideoStreamSendRecv;
1488 MSWebCam *cam=lc->video_conf.device;
1489 bool_t is_inactive=FALSE;
1491 call->current_params.has_video=TRUE;
1493 video_stream_enable_adaptive_bitrate_control(call->videostream,
1494 linphone_core_adaptive_rate_control_enabled(lc));
1495 video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc));
1496 video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
1497 video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
1498 if (lc->video_window_id!=0)
1499 video_stream_set_native_window_id(call->videostream,lc->video_window_id);
1500 if (lc->preview_window_id!=0)
1501 video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id);
1502 video_stream_use_preview_video_window (call->videostream,lc->use_preview_window);
1504 if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
1505 cam=get_nowebcam_device();
1506 dir=VideoStreamSendOnly;
1507 }else if (vstream->dir==SalStreamRecvOnly && lc->video_conf.display ){
1508 dir=VideoStreamRecvOnly;
1509 }else if (vstream->dir==SalStreamSendRecv){
1510 if (lc->video_conf.display && lc->video_conf.capture)
1511 dir=VideoStreamSendRecv;
1512 else if (lc->video_conf.display)
1513 dir=VideoStreamRecvOnly;
1515 dir=VideoStreamSendOnly;
1517 ms_warning("video stream is inactive.");
1518 /*either inactive or incompatible with local capabilities*/
1521 if (call->camera_active==FALSE || all_inputs_muted){
1522 cam=get_nowebcam_device();
1525 call->log->video_enabled = TRUE;
1526 video_stream_set_direction (call->videostream, dir);
1527 ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
1528 video_stream_set_device_rotation(call->videostream, lc->device_rotation);
1529 video_stream_start(call->videostream,
1530 call->video_profile, rtp_addr, vstream->rtp_port,
1531 rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0,
1532 used_pt, linphone_core_get_video_jittcomp(lc), cam);
1533 video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
1536 if (vstream->proto == SalProtoRtpSavp) {
1537 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1538 SalProtoRtpSavp,SalVideo);
1540 video_stream_enable_strp(
1542 vstream->crypto[0].algo,
1543 local_st_desc->crypto[0].master_key,
1544 vstream->crypto[0].master_key
1546 call->videostream_encrypted=TRUE;
1548 call->videostream_encrypted=FALSE;
1550 }else ms_warning("No video stream accepted.");
1552 ms_warning("No valid video stream defined.");
1557 void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
1558 LinphoneCore *lc=call->core;
1560 call->current_params.audio_codec = NULL;
1561 call->current_params.video_codec = NULL;
1563 LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
1565 bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
1566 #ifdef VIDEO_ENABLED
1567 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1568 SalProtoRtpAvp,SalVideo);
1571 if ((call->audiostream == NULL) && (call->videostream == NULL)) {
1572 ms_fatal("start_media_stream() called without prior init !");
1575 cname=linphone_address_as_string_uri_only(me);
1577 #if defined(VIDEO_ENABLED)
1578 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
1579 /*when video is used, do not make adaptive rate control on audio, it is stupid.*/
1583 if (call->audiostream!=NULL) {
1584 linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
1586 call->current_params.has_video=FALSE;
1587 if (call->videostream!=NULL) {
1588 linphone_call_start_video_stream(call,cname,all_inputs_muted);
1591 call->all_muted=all_inputs_muted;
1592 call->playing_ringbacktone=send_ringbacktone;
1593 call->up_bw=linphone_core_get_upload_bandwidth(lc);
1595 if (call->params.media_encryption==LinphoneMediaEncryptionZRTP) {
1596 OrtpZrtpParams params;
1597 /*will be set later when zrtp is activated*/
1598 call->current_params.media_encryption=LinphoneMediaEncryptionNone;
1600 params.zid_file=lc->zrtp_secrets_cache;
1601 audio_stream_enable_zrtp(call->audiostream,¶ms);
1602 }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
1603 call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
1604 LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
1607 /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
1608 * further in the call, for example during pause,resume, conferencing reINVITEs*/
1609 linphone_call_fix_call_parameters(call);
1610 if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) {
1611 ice_session_start_connectivity_checks(call->ice_session);
1617 linphone_address_destroy(me);
1620 void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){
1621 audio_stream_prepare_sound(call->audiostream, NULL, NULL);
1622 #ifdef VIDEO_ENABLED
1623 if (call->videostream) {
1624 video_stream_prepare_video(call->videostream);
1629 void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){
1630 audio_stream_unprepare_sound(call->audiostream);
1631 #ifdef VIDEO_ENABLED
1632 if (call->videostream) {
1633 video_stream_unprepare_video(call->videostream);
1638 void linphone_call_delete_ice_session(LinphoneCall *call){
1639 if (call->ice_session != NULL) {
1640 ice_session_destroy(call->ice_session);
1641 call->ice_session = NULL;
1642 if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL;
1643 if (call->videostream != NULL) call->videostream->ice_check_list = NULL;
1644 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
1645 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
1649 static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
1650 audio_stream_get_local_rtp_stats (st,&log->local_stats);
1651 log->quality=audio_stream_get_average_quality_rating(st);
1654 void linphone_call_stop_audio_stream(LinphoneCall *call) {
1655 if (call->audiostream!=NULL) {
1656 rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq);
1657 ortp_ev_queue_flush(call->audiostream_app_evq);
1658 ortp_ev_queue_destroy(call->audiostream_app_evq);
1659 call->audiostream_app_evq=NULL;
1661 if (call->audiostream->ec){
1662 const char *state_str=NULL;
1663 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_STATE_STRING,&state_str);
1665 ms_message("Writing echo canceler state, %i bytes",(int)strlen(state_str));
1666 lp_config_set_string(call->core->config,"sound","ec_state",state_str);
1669 linphone_call_log_fill_stats (call->log,call->audiostream);
1670 if (call->endpoint){
1671 linphone_call_remove_from_conf(call);
1673 audio_stream_stop(call->audiostream);
1674 call->audiostream=NULL;
1678 void linphone_call_stop_video_stream(LinphoneCall *call) {
1679 #ifdef VIDEO_ENABLED
1680 if (call->videostream!=NULL){
1681 rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq);
1682 ortp_ev_queue_flush(call->videostream_app_evq);
1683 ortp_ev_queue_destroy(call->videostream_app_evq);
1684 call->videostream_app_evq=NULL;
1685 video_stream_stop(call->videostream);
1686 call->videostream=NULL;
1691 void linphone_call_stop_media_streams(LinphoneCall *call){
1692 linphone_call_stop_audio_stream(call);
1693 linphone_call_stop_video_stream(call);
1694 ms_event_queue_skip(call->core->msevq);
1696 if (call->audio_profile){
1697 rtp_profile_clear_all(call->audio_profile);
1698 rtp_profile_destroy(call->audio_profile);
1699 call->audio_profile=NULL;
1701 if (call->video_profile){
1702 rtp_profile_clear_all(call->video_profile);
1703 rtp_profile_destroy(call->video_profile);
1704 call->video_profile=NULL;
1710 void linphone_call_enable_echo_cancellation(LinphoneCall *call, bool_t enable) {
1711 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1712 bool_t bypass_mode = !enable;
1713 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_SET_BYPASS_MODE,&bypass_mode);
1716 bool_t linphone_call_echo_cancellation_enabled(LinphoneCall *call) {
1717 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1719 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_BYPASS_MODE,&val);
1722 return linphone_core_echo_cancellation_enabled(call->core);
1726 void linphone_call_enable_echo_limiter(LinphoneCall *call, bool_t val){
1727 if (call!=NULL && call->audiostream!=NULL ) {
1729 const char *type=lp_config_get_string(call->core->config,"sound","el_type","mic");
1730 if (strcasecmp(type,"mic")==0)
1731 audio_stream_enable_echo_limiter(call->audiostream,ELControlMic);
1732 else if (strcasecmp(type,"full")==0)
1733 audio_stream_enable_echo_limiter(call->audiostream,ELControlFull);
1735 audio_stream_enable_echo_limiter(call->audiostream,ELInactive);
1740 bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){
1741 if (call!=NULL && call->audiostream!=NULL ){
1742 return call->audiostream->el_type !=ELInactive ;
1744 return linphone_core_echo_limiter_enabled(call->core);
1749 * @addtogroup call_misc
1754 * Returns the measured sound volume played locally (received from remote).
1755 * It is expressed in dbm0.
1757 float linphone_call_get_play_volume(LinphoneCall *call){
1758 AudioStream *st=call->audiostream;
1759 if (st && st->volrecv){
1761 ms_filter_call_method(st->volrecv,MS_VOLUME_GET,&vol);
1765 return LINPHONE_VOLUME_DB_LOWEST;
1769 * Returns the measured sound volume recorded locally (sent to remote).
1770 * It is expressed in dbm0.
1772 float linphone_call_get_record_volume(LinphoneCall *call){
1773 AudioStream *st=call->audiostream;
1774 if (st && st->volsend && !call->audio_muted && call->state==LinphoneCallStreamsRunning){
1776 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
1780 return LINPHONE_VOLUME_DB_LOWEST;
1784 * Obtain real-time quality rating of the call
1786 * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
1787 * during all the duration of the call. This function returns its value at the time of the function call.
1788 * It is expected that the rating is updated at least every 5 seconds or so.
1789 * The rating is a floating point number comprised between 0 and 5.
1791 * 4-5 = good quality <br>
1792 * 3-4 = average quality <br>
1793 * 2-3 = poor quality <br>
1794 * 1-2 = very poor quality <br>
1795 * 0-1 = can't be worse, mostly unusable <br>
1797 * @returns The function returns -1 if no quality measurement is available, for example if no
1798 * active audio stream exist. Otherwise it returns the quality rating.
1800 float linphone_call_get_current_quality(LinphoneCall *call){
1801 if (call->audiostream){
1802 return audio_stream_get_quality_rating(call->audiostream);
1808 * Returns call quality averaged over all the duration of the call.
1810 * See linphone_call_get_current_quality() for more details about quality measurement.
1812 float linphone_call_get_average_quality(LinphoneCall *call){
1813 if (call->audiostream){
1814 return audio_stream_get_average_quality_rating(call->audiostream);
1820 * Access last known statistics for audio stream, for a given call.
1822 const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) {
1823 return &call->stats[LINPHONE_CALL_STATS_AUDIO];
1827 * Access last known statistics for video stream, for a given call.
1829 const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) {
1830 return &call->stats[LINPHONE_CALL_STATS_VIDEO];
1838 static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){
1839 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0;
1840 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0;
1841 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0;
1842 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0;
1843 ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
1844 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
1845 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth ,
1846 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
1847 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth
1851 static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
1855 from = linphone_call_get_remote_address_as_string(call);
1858 snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
1863 snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
1865 if (lc->vtable.display_warning!=NULL)
1866 lc->vtable.display_warning(lc,temp);
1867 linphone_core_terminate_call(lc,call);
1870 static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
1871 OrtpEventType evt=ortp_event_get_type(ev);
1872 OrtpEventData *evd=ortp_event_get_data(ev);
1875 if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) {
1876 switch (ice_session_state(call->ice_session)) {
1878 ice_session_select_candidates(call->ice_session);
1879 if (ice_session_role(call->ice_session) == IR_Controlling) {
1880 linphone_core_update_call(call->core, call, &call->current_params);
1884 if (ice_session_has_completed_check_list(call->ice_session) == TRUE) {
1885 ice_session_select_candidates(call->ice_session);
1886 if (ice_session_role(call->ice_session) == IR_Controlling) {
1887 /* At least one ICE session has succeeded, so perform a call update. */
1888 linphone_core_update_call(call->core, call, &call->current_params);
1895 linphone_core_update_ice_state_in_call_stats(call);
1896 } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) {
1898 if (evd->info.ice_processing_successful==TRUE) {
1899 ice_session_compute_candidates_foundations(call->ice_session);
1900 ice_session_eliminate_redundant_candidates(call->ice_session);
1901 ice_session_choose_default_candidates(call->ice_session);
1902 ping_time = ice_session_average_gathering_round_trip_time(call->ice_session);
1903 if (ping_time >=0) {
1904 call->ping_time=ping_time;
1907 ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core));
1908 linphone_call_delete_ice_session(call);
1910 switch (call->state) {
1911 case LinphoneCallUpdating:
1912 linphone_core_start_update_call(call->core, call);
1914 case LinphoneCallUpdatedByRemote:
1915 linphone_core_start_accept_call_update(call->core, call);
1917 case LinphoneCallOutgoingInit:
1918 linphone_call_stop_media_streams_for_ice_gathering(call);
1919 linphone_core_proceed_with_invite_if_ready(call->core, call, NULL);
1921 case LinphoneCallIdle:
1922 linphone_call_stop_media_streams_for_ice_gathering(call);
1923 linphone_core_notify_incoming_call(call->core, call);
1928 } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
1929 linphone_core_start_accept_call_update(call->core, call);
1930 linphone_core_update_ice_state_in_call_stats(call);
1931 } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
1932 ice_session_restart(call->ice_session);
1933 ice_session_set_role(call->ice_session, IR_Controlling);
1934 linphone_core_update_call(call->core, call, &call->current_params);
1938 void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
1939 LinphoneCore* lc = call->core;
1940 int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
1941 bool_t disconnected=FALSE;
1943 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed){
1944 RtpSession *as=NULL,*vs=NULL;
1945 float audio_load=0, video_load=0;
1946 if (call->audiostream!=NULL){
1947 as=call->audiostream->session;
1948 if (call->audiostream->ticker)
1949 audio_load=ms_ticker_get_average_load(call->audiostream->ticker);
1951 if (call->videostream!=NULL){
1952 if (call->videostream->ticker)
1953 video_load=ms_ticker_get_average_load(call->videostream->ticker);
1954 vs=call->videostream->session;
1956 report_bandwidth(call,as,vs);
1957 ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
1959 #ifdef VIDEO_ENABLED
1960 if (call->videostream!=NULL) {
1963 /* Ensure there is no dangling ICE check list. */
1964 if (call->ice_session == NULL) call->videostream->ice_check_list = NULL;
1966 // Beware that the application queue should not depend on treatments fron the
1967 // mediastreamer queue.
1968 video_stream_iterate(call->videostream);
1970 while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){
1971 OrtpEventType evt=ortp_event_get_type(ev);
1972 OrtpEventData *evd=ortp_event_get_data(ev);
1973 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
1974 linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
1975 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
1976 call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session);
1977 if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
1978 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
1979 call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
1981 if (lc->vtable.call_stats_updated)
1982 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
1983 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
1984 memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t));
1985 if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
1986 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
1987 call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet;
1989 if (lc->vtable.call_stats_updated)
1990 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
1991 } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
1992 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
1993 handle_ice_events(call, ev);
1995 ortp_event_destroy(ev);
1999 if (call->audiostream!=NULL) {
2002 /* Ensure there is no dangling ICE check list. */
2003 if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL;
2005 // Beware that the application queue should not depend on treatments fron the
2006 // mediastreamer queue.
2007 audio_stream_iterate(call->audiostream);
2009 while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){
2010 OrtpEventType evt=ortp_event_get_type(ev);
2011 OrtpEventData *evd=ortp_event_get_data(ev);
2012 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2013 linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2014 } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
2015 linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
2016 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2017 call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session);
2018 if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
2019 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
2020 call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
2022 if (lc->vtable.call_stats_updated)
2023 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2024 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2025 memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t));
2026 if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
2027 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
2028 call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet;
2030 if (lc->vtable.call_stats_updated)
2031 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2032 } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2033 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2034 handle_ice_events(call, ev);
2035 } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
2036 linphone_core_dtmf_received(lc,evd->info.telephone_event);
2038 ortp_event_destroy(ev);
2041 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
2042 disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
2044 linphone_core_disconnected(call->core,call);
2047 void linphone_call_log_completed(LinphoneCall *call){
2048 LinphoneCore *lc=call->core;
2050 call->log->duration=time(NULL)-call->start_time;
2052 if (call->log->status==LinphoneCallMissed){
2055 info=ortp_strdup_printf(ngettext("You have missed %i call.",
2056 "You have missed %i calls.", lc->missed_calls),
2058 if (lc->vtable.display_status!=NULL)
2059 lc->vtable.display_status(lc,info);
2062 lc->call_logs=ms_list_prepend(lc->call_logs,(void *)call->log);
2063 if (ms_list_size(lc->call_logs)>lc->max_call_logs){
2064 MSList *elem,*prevelem=NULL;
2065 /*find the last element*/
2066 for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
2070 linphone_call_log_destroy((LinphoneCallLog*)elem->data);
2071 lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
2073 if (lc->vtable.call_log_updated!=NULL){
2074 lc->vtable.call_log_updated(lc,call->log);
2076 call_logs_write_to_config_file(lc);
2079 LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
2080 return call->transfer_state;
2083 void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
2084 if (state != call->transfer_state) {
2085 LinphoneCore* lc = call->core;
2086 call->transfer_state = state;
2087 if (lc->vtable.transfer_state_changed)
2088 lc->vtable.transfer_state_changed(lc, call, state);
2092 bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
2093 return call->params.in_conference;
2098 * Perform a zoom of the video displayed during a call.
2099 * @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied.
2100 * @param cx a floating point number pointing the horizontal center of the zoom to be applied. This value should be between 0.0 and 1.0.
2101 * @param cy a floating point number pointing the vertical center of the zoom to be applied. This value should be between 0.0 and 1.0.
2103 * cx and cy are updated in return in case their coordinates were to excentrated for the requested zoom factor. The zoom ensures that all the screen is fullfilled with the video.
2105 void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) {
2106 VideoStream* vstream = call->videostream;
2110 if (zoom_factor < 1)
2112 float halfsize = 0.5 * 1.0 / zoom_factor;
2114 if ((*cx - halfsize) < 0)
2116 if ((*cx + halfsize) > 1)
2118 if ((*cy - halfsize) < 0)
2120 if ((*cy + halfsize) > 1)
2123 zoom[0] = zoom_factor;
2126 ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
2127 }else ms_warning("Could not apply zoom: video output wasn't activated.");