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;
211 void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){
214 SalMediaDescription *old_md=call->localdesc;
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();
220 bool_t keep_srtp_keys=lp_config_get_int(lc->config,"sip","keep_srtp_keys",0);
222 if (call->ping_time>0) {
223 linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
226 md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff));
227 md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff));
229 strncpy(md->addr,call->localip,sizeof(md->addr));
230 strncpy(md->username,username,sizeof(md->username));
232 if (call->params.down_bw)
233 md->bandwidth=call->params.down_bw;
234 else md->bandwidth=linphone_core_get_download_bandwidth(lc);
236 /*set audio capabilities */
237 strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr));
238 strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr));
239 md->streams[0].rtp_port=call->audio_port;
240 md->streams[0].rtcp_port=call->audio_port+1;
241 md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ?
242 SalProtoRtpSavp : SalProtoRtpAvp;
243 md->streams[0].type=SalAudio;
244 if (call->params.down_ptime)
245 md->streams[0].ptime=call->params.down_ptime;
247 md->streams[0].ptime=linphone_core_get_download_ptime(lc);
248 l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate);
249 pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
250 l=ms_list_append(l,pt);
251 md->streams[0].payloads=l;
253 if (call->params.has_video){
255 md->streams[1].rtp_port=call->video_port;
256 md->streams[1].rtcp_port=call->video_port+1;
257 md->streams[1].proto=md->streams[0].proto;
258 md->streams[1].type=SalVideo;
259 l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL);
260 md->streams[1].payloads=l;
263 for(i=0; i<md->nstreams; i++) {
264 if (md->streams[i].proto == SalProtoRtpSavp) {
265 if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){
267 for(j=0;j<SAL_CRYPTO_ALGO_MAX;++j){
268 memcpy(&md->streams[i].crypto[j],&old_md->streams[i].crypto[j],sizeof(SalSrtpCryptoAlgo));
271 md->streams[i].crypto[0].tag = 1;
272 md->streams[i].crypto[0].algo = AES_128_SHA1_80;
273 if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
274 md->streams[i].crypto[0].algo = 0;
275 md->streams[i].crypto[1].tag = 2;
276 md->streams[i].crypto[1].algo = AES_128_SHA1_32;
277 if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
278 md->streams[i].crypto[1].algo = 0;
279 md->streams[i].crypto[2].algo = 0;
283 update_media_description_from_stun(md,&call->ac,&call->vc);
284 if (call->ice_session != NULL) {
285 linphone_core_update_local_media_description_from_ice(md, call->ice_session);
286 linphone_core_update_ice_state_in_call_stats(call);
288 linphone_address_destroy(addr);
290 if (old_md) sal_media_description_unref(old_md);
293 static int find_port_offset(LinphoneCore *lc, SalStreamType type){
298 bool_t already_used=FALSE;
299 for(offset=0;offset<100;offset+=2){
303 tried_port=linphone_core_get_audio_port (lc)+offset;
306 tried_port=linphone_core_get_video_port (lc)+offset;
310 for(elem=lc->calls;elem!=NULL;elem=elem->next){
311 LinphoneCall *call=(LinphoneCall*)elem->data;
315 existing_port = call->audio_port;
318 existing_port = call->video_port;
321 if (existing_port==tried_port) {
326 if (!already_used) break;
329 ms_error("Could not find any free port !");
335 static int select_random_port(LinphoneCore *lc, SalStreamType type) {
339 int existing_port = 0;
340 int min_port = 0, max_port = 0;
341 bool_t already_used = FALSE;
346 linphone_core_get_audio_port_range(lc, &min_port, &max_port);
349 linphone_core_get_video_port_range(lc, &min_port, &max_port);
352 tried_port = (rand() % (max_port - min_port) + min_port) & ~0x1;
353 if (tried_port < min_port) tried_port = min_port + 2;
354 for (nb_tries = 0; nb_tries < 100; nb_tries++) {
355 for (elem = lc->calls; elem != NULL; elem = elem->next) {
356 LinphoneCall *call = (LinphoneCall *)elem->data;
360 existing_port = call->audio_port;
363 existing_port = call->video_port;
366 if (existing_port == tried_port) {
371 if (!already_used) break;
373 if (nb_tries == 100) {
374 ms_error("Could not find any free port!");
380 static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
382 int min_port, max_port;
383 call->magic=linphone_call_magic;
385 call->state=LinphoneCallIdle;
386 call->transfer_state = LinphoneCallIdle;
387 call->start_time=time(NULL);
388 call->media_start_time=0;
389 call->log=linphone_call_log_new(call, from, to);
390 call->owns_call_log=TRUE;
391 linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
392 linphone_core_get_audio_port_range(call->core, &min_port, &max_port);
393 if (min_port == max_port) {
394 /* Used fixed RTP audio port. */
395 port_offset=find_port_offset (call->core, SalAudio);
396 if (port_offset==-1) return;
397 call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
399 /* Select random RTP audio port in the specified range. */
400 call->audio_port = select_random_port(call->core, SalAudio);
402 linphone_core_get_video_port_range(call->core, &min_port, &max_port);
403 if (min_port == max_port) {
404 /* Used fixed RTP video port. */
405 port_offset=find_port_offset (call->core, SalVideo);
406 if (port_offset==-1) return;
407 call->video_port=linphone_core_get_video_port(call->core)+port_offset;
409 /* Select random RTP video port in the specified range. */
410 call->video_port = select_random_port(call->core, SalVideo);
412 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
413 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
416 void linphone_call_init_stats(LinphoneCallStats *stats, int type) {
418 stats->received_rtcp = NULL;
419 stats->sent_rtcp = NULL;
420 stats->ice_state = LinphoneIceStateNotActivated;
424 static void discover_mtu(LinphoneCore *lc, const char *remote){
426 if (lc->net_conf.mtu==0 ){
427 /*attempt to discover mtu*/
428 mtu=ms_discover_mtu(remote);
431 ms_message("Discovered mtu is %i, RTP payload max size is %i",
432 mtu, ms_get_payload_max_size());
437 LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
439 LinphoneCall *call=ms_new0(LinphoneCall,1);
440 call->dir=LinphoneCallOutgoing;
441 call->op=sal_op_new(lc->sal);
442 sal_op_set_user_pointer(call->op,call);
444 linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
445 linphone_call_init_common(call,from,to);
446 call->params=*params;
447 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
448 call->ice_session = ice_session_new();
449 ice_session_set_role(call->ice_session, IR_Controlling);
451 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
452 call->ping_time=linphone_core_run_stun_tests(call->core,call);
454 call->camera_active=params->has_video;
456 discover_mtu(lc,linphone_address_get_domain (to));
457 if (params->referer){
458 sal_call_set_referer(call->op,params->referer->op);
459 call->referer=linphone_call_ref(params->referer);
464 LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
465 LinphoneCall *call=ms_new0(LinphoneCall,1);
468 call->dir=LinphoneCallIncoming;
469 sal_op_set_user_pointer(op,call);
473 if (lc->sip_conf.ping_with_options){
474 /*the following sends an option request back to the caller so that
475 we get a chance to discover our nat'd address before answering.*/
476 call->ping_op=sal_op_new(lc->sal);
477 from_str=linphone_address_as_string_uri_only(from);
478 sal_op_set_route(call->ping_op,sal_op_get_network_origin(op));
479 sal_op_set_user_pointer(call->ping_op,call);
480 sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str);
484 linphone_address_clean(from);
485 linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
486 linphone_call_init_common(call, from, to);
487 call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/
488 linphone_core_init_default_params(lc, &call->params);
489 call->params.has_video &= !!lc->video_policy.automatically_accept;
490 call->params.has_video &= linphone_core_media_description_contains_video_stream(sal_call_get_remote_media_description(op));
491 switch (linphone_core_get_firewall_policy(call->core)) {
492 case LinphonePolicyUseIce:
493 call->ice_session = ice_session_new();
494 ice_session_set_role(call->ice_session, IR_Controlled);
495 linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
496 if (call->ice_session != NULL) {
497 linphone_call_init_media_streams(call);
498 linphone_call_start_media_streams_for_ice_gathering(call);
499 if (linphone_core_gather_ice_candidates(call->core,call)<0) {
500 /* Ice candidates gathering failed, proceed with the call anyway. */
501 linphone_call_delete_ice_session(call);
502 linphone_call_stop_media_streams_for_ice_gathering(call);
506 case LinphonePolicyUseStun:
507 call->ping_time=linphone_core_run_stun_tests(call->core,call);
508 /* No break to also destroy ice session in this case. */
512 call->camera_active=call->params.has_video;
514 discover_mtu(lc,linphone_address_get_domain(from));
518 /* this function is called internally to get rid of a call.
519 It performs the following tasks:
520 - remove the call from the internal list of calls
521 - update the call logs accordingly
524 static void linphone_call_set_terminated(LinphoneCall *call){
525 LinphoneCore *lc=call->core;
527 linphone_core_update_allocated_audio_bandwidth(lc);
529 call->owns_call_log=FALSE;
530 linphone_call_log_completed(call);
533 if (call == lc->current_call){
534 ms_message("Resetting the current call");
535 lc->current_call=NULL;
538 if (linphone_core_del_call(lc,call) != 0){
539 ms_error("Could not remove the call from the list !!!");
542 if (ms_list_size(lc->calls)==0)
543 linphone_core_notify_all_friends(lc,lc->presence_mode);
545 linphone_core_conference_check_uninit(lc);
546 if (call->ringing_beep){
547 linphone_core_stop_dtmf(lc);
548 call->ringing_beep=FALSE;
551 linphone_call_unref(call->referer);
556 void linphone_call_fix_call_parameters(LinphoneCall *call){
557 call->params.has_video=call->current_params.has_video;
558 call->params.media_encryption=call->current_params.media_encryption;
561 const char *linphone_call_state_to_string(LinphoneCallState cs){
563 case LinphoneCallIdle:
564 return "LinphoneCallIdle";
565 case LinphoneCallIncomingReceived:
566 return "LinphoneCallIncomingReceived";
567 case LinphoneCallOutgoingInit:
568 return "LinphoneCallOutgoingInit";
569 case LinphoneCallOutgoingProgress:
570 return "LinphoneCallOutgoingProgress";
571 case LinphoneCallOutgoingRinging:
572 return "LinphoneCallOutgoingRinging";
573 case LinphoneCallOutgoingEarlyMedia:
574 return "LinphoneCallOutgoingEarlyMedia";
575 case LinphoneCallConnected:
576 return "LinphoneCallConnected";
577 case LinphoneCallStreamsRunning:
578 return "LinphoneCallStreamsRunning";
579 case LinphoneCallPausing:
580 return "LinphoneCallPausing";
581 case LinphoneCallPaused:
582 return "LinphoneCallPaused";
583 case LinphoneCallResuming:
584 return "LinphoneCallResuming";
585 case LinphoneCallRefered:
586 return "LinphoneCallRefered";
587 case LinphoneCallError:
588 return "LinphoneCallError";
589 case LinphoneCallEnd:
590 return "LinphoneCallEnd";
591 case LinphoneCallPausedByRemote:
592 return "LinphoneCallPausedByRemote";
593 case LinphoneCallUpdatedByRemote:
594 return "LinphoneCallUpdatedByRemote";
595 case LinphoneCallIncomingEarlyMedia:
596 return "LinphoneCallIncomingEarlyMedia";
597 case LinphoneCallUpdating:
598 return "LinphoneCallUpdating";
599 case LinphoneCallReleased:
600 return "LinphoneCallReleased";
602 return "undefined state";
605 void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
606 LinphoneCore *lc=call->core;
608 if (call->state!=cstate){
609 if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
610 if (cstate!=LinphoneCallReleased){
611 ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
612 linphone_call_state_to_string(cstate));
616 ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
617 linphone_call_state_to_string(cstate));
618 if (cstate!=LinphoneCallRefered){
619 /*LinphoneCallRefered is rather an event, not a state.
620 Indeed it does not change the state of the call (still paused or running)*/
623 if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
624 switch(call->reason){
625 case LinphoneReasonDeclined:
626 call->log->status=LinphoneCallDeclined;
628 case LinphoneReasonNotAnswered:
629 call->log->status=LinphoneCallMissed;
634 linphone_call_set_terminated (call);
636 if (cstate == LinphoneCallConnected) {
637 call->log->status=LinphoneCallSuccess;
638 call->media_start_time=time(NULL);
641 if (lc->vtable.call_state_changed)
642 lc->vtable.call_state_changed(lc,call,cstate,message);
643 if (cstate==LinphoneCallReleased){
644 if (call->op!=NULL) {
645 /* so that we cannot have anymore upcalls for SAL
646 concerning this call*/
647 sal_op_release(call->op);
650 linphone_call_unref(call);
655 static void linphone_call_destroy(LinphoneCall *obj)
657 linphone_call_delete_ice_session(obj);
659 sal_op_release(obj->op);
662 if (obj->resultdesc!=NULL) {
663 sal_media_description_unref(obj->resultdesc);
664 obj->resultdesc=NULL;
666 if (obj->localdesc!=NULL) {
667 sal_media_description_unref(obj->localdesc);
671 sal_op_release(obj->ping_op);
674 ms_free(obj->refer_to);
676 if (obj->owns_call_log)
677 linphone_call_log_destroy(obj->log);
678 if (obj->auth_token) {
679 ms_free(obj->auth_token);
686 * @addtogroup call_control
691 * Increments the call 's reference count.
692 * An application that wishes to retain a pointer to call object
693 * must use this function to unsure the pointer remains
694 * valid. Once the application no more needs this pointer,
695 * it must call linphone_call_unref().
697 LinphoneCall * linphone_call_ref(LinphoneCall *obj){
703 * Decrements the call object reference count.
704 * See linphone_call_ref().
706 void linphone_call_unref(LinphoneCall *obj){
709 linphone_call_destroy(obj);
714 * Returns current parameters associated to the call.
716 const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
717 return &call->current_params;
720 static bool_t is_video_active(const SalStreamDescription *sd){
721 return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
725 * Returns call parameters proposed by remote.
727 * This is useful when receiving an incoming call, to know whether the remote party
728 * supports video, encryption or whatever.
730 const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
731 LinphoneCallParams *cp=&call->remote_params;
732 memset(cp,0,sizeof(*cp));
734 SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
736 SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
738 asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
739 vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
740 secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
741 secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
743 cp->has_video=is_video_active(secure_vsd);
744 if (secure_asd || asd==NULL)
745 cp->media_encryption=LinphoneMediaEncryptionSRTP;
747 cp->has_video=is_video_active(vsd);
756 * Returns the remote address associated to this call
759 const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
760 return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
764 * Returns the remote address associated to this call as a string.
766 * The result string must be freed by user using ms_free().
768 char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
769 return linphone_address_as_string(linphone_call_get_remote_address(call));
773 * Retrieves the call's current state.
775 LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
780 * Returns the reason for a call termination (either error or normal termination)
782 LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
787 * Get the user_pointer in the LinphoneCall
789 * @ingroup call_control
791 * return user_pointer an opaque user pointer that can be retrieved at any time
793 void *linphone_call_get_user_pointer(LinphoneCall *call)
795 return call->user_pointer;
799 * Set the user_pointer in the LinphoneCall
801 * @ingroup call_control
803 * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
805 void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
807 call->user_pointer = user_pointer;
811 * Returns the call log associated to this call.
813 LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
818 * Returns the refer-to uri (if the call was transfered).
820 const char *linphone_call_get_refer_to(const LinphoneCall *call){
821 return call->refer_to;
825 * Returns direction of the call (incoming or outgoing).
827 LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
828 return call->log->dir;
832 * Returns the far end's user agent description string, if available.
834 const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
836 return sal_op_get_remote_ua (call->op);
842 * Returns true if this calls has received a transfer that has not been
844 * Pending transfers are executed when this call is being paused or closed,
845 * locally or by remote endpoint.
846 * If the call is already paused while receiving the transfer request, the
847 * transfer immediately occurs.
849 bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
850 return call->refer_pending;
854 * Returns call's duration in seconds.
856 int linphone_call_get_duration(const LinphoneCall *call){
857 if (call->media_start_time==0) return 0;
858 return time(NULL)-call->media_start_time;
862 * Returns the call object this call is replacing, if any.
863 * Call replacement can occur during call transfers.
864 * By default, the core automatically terminates the replaced call and accept the new one.
865 * This function allows the application to know whether a new incoming call is a one that replaces another one.
867 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
868 SalOp *op=sal_call_get_replaces(call->op);
870 return (LinphoneCall*)sal_op_get_user_pointer(op);
876 * Indicate whether camera input should be sent to remote end.
878 void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
880 if (call->videostream!=NULL && call->videostream->ticker!=NULL){
881 LinphoneCore *lc=call->core;
882 MSWebCam *nowebcam=get_nowebcam_device();
883 if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
884 video_stream_change_camera(call->videostream,
885 enable ? lc->video_conf.device : nowebcam);
888 call->camera_active=enable;
893 * Take a photo of currently received video and write it into a jpeg file.
895 int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
897 if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){
898 return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
900 ms_warning("Cannot take snapshot: no currently running video stream on this call.");
907 * Returns TRUE if camera pictures are sent to the remote party.
909 bool_t linphone_call_camera_enabled (const LinphoneCall *call){
910 return call->camera_active;
914 * Enable video stream.
916 void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
917 cp->has_video=enabled;
920 const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
921 return cp->audio_codec;
924 const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
925 return cp->video_codec;
928 bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
929 return cp->low_bandwidth;
932 * Returns whether video is enabled.
934 bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
935 return cp->has_video;
938 enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
939 return cp->media_encryption;
942 void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
943 cp->media_encryption = e;
948 * Enable sending of real early media (during outgoing calls).
950 void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
951 cp->real_early_media=enabled;
954 bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
955 return cp->real_early_media;
959 * Returns true if the call is part of the locally managed conference.
961 bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
962 return cp->in_conference;
966 * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
967 * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
969 void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
970 cp->audio_bw=bandwidth;
975 * Request remote side to send us a Video Fast Update.
977 void linphone_call_send_vfu_request(LinphoneCall *call)
979 if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
980 sal_call_send_vfu_request(call->op);
987 LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
988 LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
989 memcpy(ncp,cp,sizeof(LinphoneCallParams));
996 void linphone_call_params_destroy(LinphoneCallParams *p){
1005 #ifdef TEST_EXT_RENDERER
1006 static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
1007 ms_message("rendercb, local buffer=%p, remote buffer=%p",
1008 local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
1012 #ifdef VIDEO_ENABLED
1013 static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
1014 LinphoneCall* call = (LinphoneCall*) user_pointer;
1015 ms_warning("In linphonecall.c: video_stream_event_cb");
1017 case MS_VIDEO_DECODER_DECODING_ERRORS:
1018 ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
1019 linphone_call_send_vfu_request(call);
1021 case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
1022 ms_message("First video frame decoded successfully");
1023 if (call->nextVideoFrameDecoded._func != NULL)
1024 call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
1027 ms_warning("Unhandled event %i", event_id);
1033 void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) {
1034 call->nextVideoFrameDecoded._func = cb;
1035 call->nextVideoFrameDecoded._user_data = user_data;
1036 #ifdef VIDEO_ENABLED
1037 ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
1041 void linphone_call_init_audio_stream(LinphoneCall *call){
1042 LinphoneCore *lc=call->core;
1043 AudioStream *audiostream;
1046 if (call->audiostream != NULL) return;
1047 call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc));
1048 dscp=linphone_core_get_audio_dscp(lc);
1050 audio_stream_set_dscp(audiostream,dscp);
1051 if (linphone_core_echo_limiter_enabled(lc)){
1052 const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
1053 if (strcasecmp(type,"mic")==0)
1054 audio_stream_enable_echo_limiter(audiostream,ELControlMic);
1055 else if (strcasecmp(type,"full")==0)
1056 audio_stream_enable_echo_limiter(audiostream,ELControlFull);
1058 audio_stream_enable_gain_control(audiostream,TRUE);
1059 if (linphone_core_echo_cancellation_enabled(lc)){
1060 int len,delay,framesize;
1061 const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
1062 len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
1063 delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
1064 framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
1065 audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
1066 if (statestr && audiostream->ec){
1067 ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
1070 audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
1072 int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
1073 audio_stream_enable_noise_gate(audiostream,enabled);
1076 audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc));
1079 RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port);
1080 RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1);
1081 rtp_session_set_transports(audiostream->session,artp,artcp);
1083 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1084 rtp_session_set_pktinfo(audiostream->session, TRUE);
1085 rtp_session_set_symmetric_rtp(audiostream->session, FALSE);
1086 if (ice_session_check_list(call->ice_session, 0) == NULL) {
1087 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1089 audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0);
1090 ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session);
1093 call->audiostream_app_evq = ortp_ev_queue_new();
1094 rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);
1097 void linphone_call_init_video_stream(LinphoneCall *call){
1098 #ifdef VIDEO_ENABLED
1099 LinphoneCore *lc=call->core;
1101 if (!call->params.has_video) {
1102 linphone_call_stop_video_stream(call);
1105 if (call->videostream != NULL) return;
1106 if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){
1107 int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
1108 int dscp=linphone_core_get_video_dscp(lc);
1110 call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc));
1112 video_stream_set_dscp(call->videostream,dscp);
1113 video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
1114 if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size);
1116 if( lc->video_conf.displaytype != NULL)
1117 video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
1118 video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
1120 RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
1121 RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
1122 rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
1124 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1125 rtp_session_set_pktinfo(call->videostream->session, TRUE);
1126 rtp_session_set_symmetric_rtp(call->videostream->session, FALSE);
1127 if (ice_session_check_list(call->ice_session, 1) == NULL) {
1128 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1130 call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1);
1131 ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session);
1133 call->videostream_app_evq = ortp_ev_queue_new();
1134 rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
1135 #ifdef TEST_EXT_RENDERER
1136 video_stream_set_render_callback(call->videostream,rendercb,NULL);
1140 call->videostream=NULL;
1144 void linphone_call_init_media_streams(LinphoneCall *call){
1145 linphone_call_init_audio_stream(call);
1146 linphone_call_init_video_stream(call);
1150 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
1152 static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){
1153 if (dtmf<0 || dtmf>15){
1154 ms_warning("Bad dtmf value %i",dtmf);
1157 if (lc->vtable.dtmf_received != NULL)
1158 lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
1161 static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
1163 MSFilter *f=st->equalizer;
1164 int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
1165 const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
1166 ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
1172 if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
1173 ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
1174 ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
1183 void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
1184 float mic_gain=lc->sound_conf.soft_mic_lev;
1187 float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
1188 float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
1189 int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
1192 linphone_core_set_mic_gain_db (lc, mic_gain);
1194 audio_stream_set_mic_gain(st,0);
1196 recv_gain = lc->sound_conf.soft_play_lev;
1197 if (recv_gain != 0) {
1198 linphone_core_set_playback_gain_db (lc,recv_gain);
1202 ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
1203 float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
1204 thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
1205 float force=lp_config_get_float(lc->config,"sound","el_force",-1);
1206 int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
1207 float transmit_thres=lp_config_get_float(lc->config,"sound","el_transmit_thres",-1);
1210 if (speed==-1) speed=0.03;
1211 if (force==-1) force=25;
1212 ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
1213 ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
1215 ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
1217 ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
1218 if (transmit_thres!=-1)
1219 ms_filter_call_method(f,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&transmit_thres);
1221 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1222 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
1225 /* parameters for a limited noise-gate effect, using echo limiter threshold */
1226 float floorgain = 1/pow(10,(mic_gain)/10);
1227 int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0);
1228 ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc);
1229 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1230 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
1232 parametrize_equalizer(lc,st);
1235 static void post_configure_audio_streams(LinphoneCall*call){
1236 AudioStream *st=call->audiostream;
1237 LinphoneCore *lc=call->core;
1238 _post_configure_audio_stream(st,lc,call->audio_muted);
1239 if (lc->vtable.dtmf_received!=NULL){
1240 /* replace by our default action*/
1241 audio_stream_play_received_dtmfs(call->audiostream,FALSE);
1242 /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/
1246 static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
1249 RtpProfile *prof=rtp_profile_new("Call profile");
1252 LinphoneCore *lc=call->core;
1254 const LinphoneCallParams *params=&call->params;
1257 for(elem=desc->payloads;elem!=NULL;elem=elem->next){
1258 PayloadType *pt=(PayloadType*)elem->data;
1261 if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
1262 if (desc->type==SalAudio){
1263 linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
1264 if (params->up_ptime)
1265 up_ptime=params->up_ptime;
1266 else up_ptime=linphone_core_get_upload_ptime(lc);
1268 *used_pt=payload_type_get_number(pt);
1271 if (desc->bandwidth>0) remote_bw=desc->bandwidth;
1272 else if (md->bandwidth>0) {
1273 /*case where b=AS is given globally, not per stream*/
1274 remote_bw=md->bandwidth;
1275 if (desc->type==SalVideo){
1276 remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
1280 if (desc->type==SalAudio){
1281 int audio_bw=call->audio_bw;
1283 if (params->up_bw< audio_bw)
1284 audio_bw=params->up_bw;
1286 bw=get_min_bandwidth(audio_bw,remote_bw);
1287 }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
1288 if (bw>0) pt->normal_bitrate=bw*1000;
1289 else if (desc->type==SalAudio){
1290 pt->normal_bitrate=-1;
1293 up_ptime=desc->ptime;
1297 snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
1298 payload_type_append_send_fmtp(pt,tmp);
1300 number=payload_type_get_number(pt);
1301 if (rtp_profile_get_payload(prof,number)!=NULL){
1302 ms_warning("A payload type with number %i already exists in profile !",number);
1304 rtp_profile_set_payload(prof,number,pt);
1310 static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
1311 int pause_time=3000;
1312 audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
1313 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1316 static bool_t linphone_call_sound_resources_available(LinphoneCall *call){
1317 LinphoneCore *lc=call->core;
1318 LinphoneCall *current=linphone_core_get_current_call(lc);
1319 return !linphone_core_is_in_conference(lc) &&
1320 (current==NULL || current==call);
1322 static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
1324 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
1325 if (crypto[i].tag == tag) {
1331 static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
1332 LinphoneCore *lc=call->core;
1334 char rtcp_tool[128]={0};
1335 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1336 /* look for savp stream first */
1337 const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
1338 SalProtoRtpSavp,SalAudio);
1339 /* no savp audio stream, use avp */
1341 stream=sal_media_description_find_stream(call->resultdesc,
1342 SalProtoRtpAvp,SalAudio);
1344 if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
1345 MSSndCard *playcard=lc->sound_conf.lsd_card ?
1346 lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
1347 MSSndCard *captcard=lc->sound_conf.capt_sndcard;
1348 const char *playfile=lc->play_file;
1349 const char *recfile=lc->rec_file;
1350 call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
1354 call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
1355 if (playcard==NULL) {
1356 ms_warning("No card defined for playback !");
1358 if (captcard==NULL) {
1359 ms_warning("No card defined for capture !");
1361 /*Replace soundcard filters by inactive file players or recorders
1362 when placed in recvonly or sendonly mode*/
1363 if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){
1366 }else if (stream->dir==SalStreamSendOnly){
1370 /*And we will eventually play "playfile" if set by the user*/
1373 if (send_ringbacktone){
1375 playfile=NULL;/* it is setup later*/
1377 /*if playfile are supplied don't use soundcards*/
1378 if (lc->use_files) {
1382 if (call->params.in_conference){
1383 /* first create the graph without soundcard resources*/
1384 captcard=playcard=NULL;
1386 if (!linphone_call_sound_resources_available(call)){
1387 ms_message("Sound resources are used by another call, not using soundcard.");
1388 captcard=playcard=NULL;
1390 use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
1391 if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
1392 if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
1393 audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
1394 audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
1395 audio_stream_start_full(
1397 call->audio_profile,
1398 stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr,
1400 stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
1401 linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0,
1403 linphone_core_get_audio_jittcomp(lc),
1410 post_configure_audio_streams(call);
1411 if (muted && !send_ringbacktone){
1412 audio_stream_set_mic_gain(call->audiostream,0);
1414 if (stream->dir==SalStreamSendOnly && playfile!=NULL){
1416 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1418 if (send_ringbacktone){
1419 setup_ring_player(lc,call);
1421 audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
1423 /* valid local tags are > 0 */
1424 if (stream->proto == SalProtoRtpSavp) {
1425 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1426 SalProtoRtpSavp,SalAudio);
1427 int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
1429 if (crypto_idx >= 0) {
1430 audio_stream_enable_strp(
1432 stream->crypto[0].algo,
1433 local_st_desc->crypto[crypto_idx].master_key,
1434 stream->crypto[0].master_key);
1435 call->audiostream_encrypted=TRUE;
1437 ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
1438 call->audiostream_encrypted=FALSE;
1440 }else call->audiostream_encrypted=FALSE;
1441 if (call->params.in_conference){
1442 /*transform the graph to connect it to the conference filter */
1443 bool_t mute=stream->dir==SalStreamRecvOnly;
1444 linphone_call_add_to_conf(call, mute);
1446 call->current_params.in_conference=call->params.in_conference;
1447 call->current_params.low_bandwidth=call->params.low_bandwidth;
1448 }else ms_warning("No audio stream accepted ?");
1452 static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){
1453 #ifdef VIDEO_ENABLED
1454 LinphoneCore *lc=call->core;
1456 /* look for savp stream first */
1457 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1458 SalProtoRtpSavp,SalVideo);
1459 char rtcp_tool[128]={0};
1460 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1462 /* no savp audio stream, use avp */
1464 vstream=sal_media_description_find_stream(call->resultdesc,
1465 SalProtoRtpAvp,SalVideo);
1467 /* shutdown preview */
1468 if (lc->previewstream!=NULL) {
1469 video_preview_stop(lc->previewstream);
1470 lc->previewstream=NULL;
1473 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
1474 const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
1475 const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
1476 call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
1478 call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
1479 VideoStreamDir dir=VideoStreamSendRecv;
1480 MSWebCam *cam=lc->video_conf.device;
1481 bool_t is_inactive=FALSE;
1483 call->current_params.has_video=TRUE;
1485 video_stream_enable_adaptive_bitrate_control(call->videostream,
1486 linphone_core_adaptive_rate_control_enabled(lc));
1487 video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc));
1488 video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
1489 video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
1490 if (lc->video_window_id!=0)
1491 video_stream_set_native_window_id(call->videostream,lc->video_window_id);
1492 if (lc->preview_window_id!=0)
1493 video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id);
1494 video_stream_use_preview_video_window (call->videostream,lc->use_preview_window);
1496 if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
1497 cam=get_nowebcam_device();
1498 dir=VideoStreamSendOnly;
1499 }else if (vstream->dir==SalStreamRecvOnly && lc->video_conf.display ){
1500 dir=VideoStreamRecvOnly;
1501 }else if (vstream->dir==SalStreamSendRecv){
1502 if (lc->video_conf.display && lc->video_conf.capture)
1503 dir=VideoStreamSendRecv;
1504 else if (lc->video_conf.display)
1505 dir=VideoStreamRecvOnly;
1507 dir=VideoStreamSendOnly;
1509 ms_warning("video stream is inactive.");
1510 /*either inactive or incompatible with local capabilities*/
1513 if (call->camera_active==FALSE || all_inputs_muted){
1514 cam=get_nowebcam_device();
1517 call->log->video_enabled = TRUE;
1518 video_stream_set_direction (call->videostream, dir);
1519 ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
1520 video_stream_set_device_rotation(call->videostream, lc->device_rotation);
1521 video_stream_start(call->videostream,
1522 call->video_profile, rtp_addr, vstream->rtp_port,
1523 rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0,
1524 used_pt, linphone_core_get_video_jittcomp(lc), cam);
1525 video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
1528 if (vstream->proto == SalProtoRtpSavp) {
1529 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1530 SalProtoRtpSavp,SalVideo);
1532 video_stream_enable_strp(
1534 vstream->crypto[0].algo,
1535 local_st_desc->crypto[0].master_key,
1536 vstream->crypto[0].master_key
1538 call->videostream_encrypted=TRUE;
1540 call->videostream_encrypted=FALSE;
1542 }else ms_warning("No video stream accepted.");
1544 ms_warning("No valid video stream defined.");
1549 void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
1550 LinphoneCore *lc=call->core;
1552 call->current_params.audio_codec = NULL;
1553 call->current_params.video_codec = NULL;
1555 LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
1557 bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
1558 #ifdef VIDEO_ENABLED
1559 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1560 SalProtoRtpAvp,SalVideo);
1563 if ((call->audiostream == NULL) && (call->videostream == NULL)) {
1564 ms_fatal("start_media_stream() called without prior init !");
1567 cname=linphone_address_as_string_uri_only(me);
1569 #if defined(VIDEO_ENABLED)
1570 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
1571 /*when video is used, do not make adaptive rate control on audio, it is stupid.*/
1575 if (call->audiostream!=NULL) {
1576 linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
1578 call->current_params.has_video=FALSE;
1579 if (call->videostream!=NULL) {
1580 linphone_call_start_video_stream(call,cname,all_inputs_muted);
1583 call->all_muted=all_inputs_muted;
1584 call->playing_ringbacktone=send_ringbacktone;
1585 call->up_bw=linphone_core_get_upload_bandwidth(lc);
1587 if (call->params.media_encryption==LinphoneMediaEncryptionZRTP) {
1588 OrtpZrtpParams params;
1589 /*will be set later when zrtp is activated*/
1590 call->current_params.media_encryption=LinphoneMediaEncryptionNone;
1592 params.zid_file=lc->zrtp_secrets_cache;
1593 audio_stream_enable_zrtp(call->audiostream,¶ms);
1594 }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
1595 call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
1596 LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
1599 /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
1600 * further in the call, for example during pause,resume, conferencing reINVITEs*/
1601 linphone_call_fix_call_parameters(call);
1602 if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) {
1603 ice_session_start_connectivity_checks(call->ice_session);
1609 linphone_address_destroy(me);
1612 void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){
1613 audio_stream_prepare_sound(call->audiostream, NULL, NULL);
1614 #ifdef VIDEO_ENABLED
1615 if (call->videostream) {
1616 video_stream_prepare_video(call->videostream);
1621 void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){
1622 audio_stream_unprepare_sound(call->audiostream);
1623 #ifdef VIDEO_ENABLED
1624 if (call->videostream) {
1625 video_stream_unprepare_video(call->videostream);
1630 void linphone_call_delete_ice_session(LinphoneCall *call){
1631 if (call->ice_session != NULL) {
1632 ice_session_destroy(call->ice_session);
1633 call->ice_session = NULL;
1634 if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL;
1635 if (call->videostream != NULL) call->videostream->ice_check_list = NULL;
1636 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
1637 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
1641 static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
1642 audio_stream_get_local_rtp_stats (st,&log->local_stats);
1643 log->quality=audio_stream_get_average_quality_rating(st);
1646 void linphone_call_stop_audio_stream(LinphoneCall *call) {
1647 if (call->audiostream!=NULL) {
1648 rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq);
1649 ortp_ev_queue_flush(call->audiostream_app_evq);
1650 ortp_ev_queue_destroy(call->audiostream_app_evq);
1651 call->audiostream_app_evq=NULL;
1653 if (call->audiostream->ec){
1654 const char *state_str=NULL;
1655 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_STATE_STRING,&state_str);
1657 ms_message("Writing echo canceler state, %i bytes",(int)strlen(state_str));
1658 lp_config_set_string(call->core->config,"sound","ec_state",state_str);
1661 linphone_call_log_fill_stats (call->log,call->audiostream);
1662 if (call->endpoint){
1663 linphone_call_remove_from_conf(call);
1665 audio_stream_stop(call->audiostream);
1666 call->audiostream=NULL;
1670 void linphone_call_stop_video_stream(LinphoneCall *call) {
1671 #ifdef VIDEO_ENABLED
1672 if (call->videostream!=NULL){
1673 rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq);
1674 ortp_ev_queue_flush(call->videostream_app_evq);
1675 ortp_ev_queue_destroy(call->videostream_app_evq);
1676 call->videostream_app_evq=NULL;
1677 video_stream_stop(call->videostream);
1678 call->videostream=NULL;
1683 void linphone_call_stop_media_streams(LinphoneCall *call){
1684 linphone_call_stop_audio_stream(call);
1685 linphone_call_stop_video_stream(call);
1686 ms_event_queue_skip(call->core->msevq);
1688 if (call->audio_profile){
1689 rtp_profile_clear_all(call->audio_profile);
1690 rtp_profile_destroy(call->audio_profile);
1691 call->audio_profile=NULL;
1693 if (call->video_profile){
1694 rtp_profile_clear_all(call->video_profile);
1695 rtp_profile_destroy(call->video_profile);
1696 call->video_profile=NULL;
1702 void linphone_call_enable_echo_cancellation(LinphoneCall *call, bool_t enable) {
1703 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1704 bool_t bypass_mode = !enable;
1705 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_SET_BYPASS_MODE,&bypass_mode);
1708 bool_t linphone_call_echo_cancellation_enabled(LinphoneCall *call) {
1709 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1711 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_BYPASS_MODE,&val);
1714 return linphone_core_echo_cancellation_enabled(call->core);
1718 void linphone_call_enable_echo_limiter(LinphoneCall *call, bool_t val){
1719 if (call!=NULL && call->audiostream!=NULL ) {
1721 const char *type=lp_config_get_string(call->core->config,"sound","el_type","mic");
1722 if (strcasecmp(type,"mic")==0)
1723 audio_stream_enable_echo_limiter(call->audiostream,ELControlMic);
1724 else if (strcasecmp(type,"full")==0)
1725 audio_stream_enable_echo_limiter(call->audiostream,ELControlFull);
1727 audio_stream_enable_echo_limiter(call->audiostream,ELInactive);
1732 bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){
1733 if (call!=NULL && call->audiostream!=NULL ){
1734 return call->audiostream->el_type !=ELInactive ;
1736 return linphone_core_echo_limiter_enabled(call->core);
1741 * @addtogroup call_misc
1746 * Returns the measured sound volume played locally (received from remote).
1747 * It is expressed in dbm0.
1749 float linphone_call_get_play_volume(LinphoneCall *call){
1750 AudioStream *st=call->audiostream;
1751 if (st && st->volrecv){
1753 ms_filter_call_method(st->volrecv,MS_VOLUME_GET,&vol);
1757 return LINPHONE_VOLUME_DB_LOWEST;
1761 * Returns the measured sound volume recorded locally (sent to remote).
1762 * It is expressed in dbm0.
1764 float linphone_call_get_record_volume(LinphoneCall *call){
1765 AudioStream *st=call->audiostream;
1766 if (st && st->volsend && !call->audio_muted && call->state==LinphoneCallStreamsRunning){
1768 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
1772 return LINPHONE_VOLUME_DB_LOWEST;
1776 * Obtain real-time quality rating of the call
1778 * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
1779 * during all the duration of the call. This function returns its value at the time of the function call.
1780 * It is expected that the rating is updated at least every 5 seconds or so.
1781 * The rating is a floating point number comprised between 0 and 5.
1783 * 4-5 = good quality <br>
1784 * 3-4 = average quality <br>
1785 * 2-3 = poor quality <br>
1786 * 1-2 = very poor quality <br>
1787 * 0-1 = can't be worse, mostly unusable <br>
1789 * @returns The function returns -1 if no quality measurement is available, for example if no
1790 * active audio stream exist. Otherwise it returns the quality rating.
1792 float linphone_call_get_current_quality(LinphoneCall *call){
1793 if (call->audiostream){
1794 return audio_stream_get_quality_rating(call->audiostream);
1800 * Returns call quality averaged over all the duration of the call.
1802 * See linphone_call_get_current_quality() for more details about quality measurement.
1804 float linphone_call_get_average_quality(LinphoneCall *call){
1805 if (call->audiostream){
1806 return audio_stream_get_average_quality_rating(call->audiostream);
1812 * Access last known statistics for audio stream, for a given call.
1814 const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) {
1815 return &call->stats[LINPHONE_CALL_STATS_AUDIO];
1819 * Access last known statistics for video stream, for a given call.
1821 const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) {
1822 return &call->stats[LINPHONE_CALL_STATS_VIDEO];
1830 static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){
1831 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0;
1832 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0;
1833 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0;
1834 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0;
1835 ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
1836 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
1837 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth ,
1838 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
1839 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth
1843 static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
1847 from = linphone_call_get_remote_address_as_string(call);
1850 snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
1855 snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
1857 if (lc->vtable.display_warning!=NULL)
1858 lc->vtable.display_warning(lc,temp);
1859 linphone_core_terminate_call(lc,call);
1862 static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
1863 OrtpEventType evt=ortp_event_get_type(ev);
1864 OrtpEventData *evd=ortp_event_get_data(ev);
1867 if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) {
1868 switch (ice_session_state(call->ice_session)) {
1870 ice_session_select_candidates(call->ice_session);
1871 if (ice_session_role(call->ice_session) == IR_Controlling) {
1872 linphone_core_update_call(call->core, call, &call->current_params);
1876 if (ice_session_has_completed_check_list(call->ice_session) == TRUE) {
1877 ice_session_select_candidates(call->ice_session);
1878 if (ice_session_role(call->ice_session) == IR_Controlling) {
1879 /* At least one ICE session has succeeded, so perform a call update. */
1880 linphone_core_update_call(call->core, call, &call->current_params);
1887 linphone_core_update_ice_state_in_call_stats(call);
1888 } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) {
1890 if (evd->info.ice_processing_successful==TRUE) {
1891 ice_session_compute_candidates_foundations(call->ice_session);
1892 ice_session_eliminate_redundant_candidates(call->ice_session);
1893 ice_session_choose_default_candidates(call->ice_session);
1894 ping_time = ice_session_average_gathering_round_trip_time(call->ice_session);
1895 if (ping_time >=0) {
1896 call->ping_time=ping_time;
1899 ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core));
1900 linphone_call_delete_ice_session(call);
1902 switch (call->state) {
1903 case LinphoneCallUpdating:
1904 linphone_core_start_update_call(call->core, call);
1906 case LinphoneCallUpdatedByRemote:
1907 linphone_core_start_accept_call_update(call->core, call);
1909 case LinphoneCallOutgoingInit:
1910 linphone_call_stop_media_streams_for_ice_gathering(call);
1911 linphone_core_proceed_with_invite_if_ready(call->core, call, NULL);
1913 case LinphoneCallIdle:
1914 linphone_call_stop_media_streams_for_ice_gathering(call);
1915 linphone_core_notify_incoming_call(call->core, call);
1920 } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
1921 linphone_core_start_accept_call_update(call->core, call);
1922 linphone_core_update_ice_state_in_call_stats(call);
1923 } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
1924 ice_session_restart(call->ice_session);
1925 ice_session_set_role(call->ice_session, IR_Controlling);
1926 linphone_core_update_call(call->core, call, &call->current_params);
1930 void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
1931 LinphoneCore* lc = call->core;
1932 int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
1933 bool_t disconnected=FALSE;
1935 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed){
1936 RtpSession *as=NULL,*vs=NULL;
1937 float audio_load=0, video_load=0;
1938 if (call->audiostream!=NULL){
1939 as=call->audiostream->session;
1940 if (call->audiostream->ticker)
1941 audio_load=ms_ticker_get_average_load(call->audiostream->ticker);
1943 if (call->videostream!=NULL){
1944 if (call->videostream->ticker)
1945 video_load=ms_ticker_get_average_load(call->videostream->ticker);
1946 vs=call->videostream->session;
1948 report_bandwidth(call,as,vs);
1949 ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
1951 #ifdef VIDEO_ENABLED
1952 if (call->videostream!=NULL) {
1955 /* Ensure there is no dangling ICE check list. */
1956 if (call->ice_session == NULL) call->videostream->ice_check_list = NULL;
1958 // Beware that the application queue should not depend on treatments fron the
1959 // mediastreamer queue.
1960 video_stream_iterate(call->videostream);
1962 while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){
1963 OrtpEventType evt=ortp_event_get_type(ev);
1964 OrtpEventData *evd=ortp_event_get_data(ev);
1965 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
1966 linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
1967 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
1968 call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session);
1969 if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
1970 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
1971 call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
1973 if (lc->vtable.call_stats_updated)
1974 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
1975 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
1976 memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t));
1977 if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
1978 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
1979 call->stats[LINPHONE_CALL_STATS_VIDEO].sent_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_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
1984 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
1985 handle_ice_events(call, ev);
1987 ortp_event_destroy(ev);
1991 if (call->audiostream!=NULL) {
1994 /* Ensure there is no dangling ICE check list. */
1995 if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL;
1997 // Beware that the application queue should not depend on treatments fron the
1998 // mediastreamer queue.
1999 audio_stream_iterate(call->audiostream);
2001 while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){
2002 OrtpEventType evt=ortp_event_get_type(ev);
2003 OrtpEventData *evd=ortp_event_get_data(ev);
2004 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2005 linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2006 } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
2007 linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
2008 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2009 call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session);
2010 if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
2011 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
2012 call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
2014 if (lc->vtable.call_stats_updated)
2015 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2016 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2017 memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t));
2018 if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
2019 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
2020 call->stats[LINPHONE_CALL_STATS_AUDIO].sent_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_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2025 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2026 handle_ice_events(call, ev);
2027 } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
2028 linphone_core_dtmf_received(lc,evd->info.telephone_event);
2030 ortp_event_destroy(ev);
2033 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
2034 disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
2036 linphone_core_disconnected(call->core,call);
2039 void linphone_call_log_completed(LinphoneCall *call){
2040 LinphoneCore *lc=call->core;
2042 call->log->duration=time(NULL)-call->start_time;
2044 if (call->log->status==LinphoneCallMissed){
2047 info=ortp_strdup_printf(ngettext("You have missed %i call.",
2048 "You have missed %i calls.", lc->missed_calls),
2050 if (lc->vtable.display_status!=NULL)
2051 lc->vtable.display_status(lc,info);
2054 lc->call_logs=ms_list_prepend(lc->call_logs,(void *)call->log);
2055 if (ms_list_size(lc->call_logs)>lc->max_call_logs){
2056 MSList *elem,*prevelem=NULL;
2057 /*find the last element*/
2058 for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
2062 linphone_call_log_destroy((LinphoneCallLog*)elem->data);
2063 lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
2065 if (lc->vtable.call_log_updated!=NULL){
2066 lc->vtable.call_log_updated(lc,call->log);
2068 call_logs_write_to_config_file(lc);
2071 LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
2072 return call->transfer_state;
2075 void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
2076 if (state != call->transfer_state) {
2077 LinphoneCore* lc = call->core;
2078 call->transfer_state = state;
2079 if (lc->vtable.transfer_state_changed)
2080 lc->vtable.transfer_state_changed(lc, call, state);
2084 bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
2085 return call->params.in_conference;
2090 * Perform a zoom of the video displayed during a call.
2091 * @param call the call.
2092 * @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied.
2093 * @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.
2094 * @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.
2096 * 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.
2098 void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) {
2099 VideoStream* vstream = call->videostream;
2100 if (vstream && vstream->output) {
2103 if (zoom_factor < 1)
2105 float halfsize = 0.5 * 1.0 / zoom_factor;
2107 if ((*cx - halfsize) < 0)
2109 if ((*cx + halfsize) > 1)
2111 if ((*cy - halfsize) < 0)
2113 if ((*cy + halfsize) > 1)
2116 zoom[0] = zoom_factor;
2119 ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
2120 }else ms_warning("Could not apply zoom: video output wasn't activated.");