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 linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
224 md->session_id=(old_md ? old_md->session_id : (rand() & 0xfff));
225 md->session_ver=(old_md ? (old_md->session_ver+1) : (rand() & 0xfff));
227 strncpy(md->addr,call->localip,sizeof(md->addr));
228 strncpy(md->username,username,sizeof(md->username));
230 if (call->params.down_bw)
231 md->bandwidth=call->params.down_bw;
232 else md->bandwidth=linphone_core_get_download_bandwidth(lc);
234 /*set audio capabilities */
235 strncpy(md->streams[0].rtp_addr,call->localip,sizeof(md->streams[0].rtp_addr));
236 strncpy(md->streams[0].rtcp_addr,call->localip,sizeof(md->streams[0].rtcp_addr));
237 md->streams[0].rtp_port=call->audio_port;
238 md->streams[0].rtcp_port=call->audio_port+1;
239 md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ?
240 SalProtoRtpSavp : SalProtoRtpAvp;
241 md->streams[0].type=SalAudio;
242 if (call->params.down_ptime)
243 md->streams[0].ptime=call->params.down_ptime;
245 md->streams[0].ptime=linphone_core_get_download_ptime(lc);
246 l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw,&md->streams[0].max_rate);
247 pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
248 l=ms_list_append(l,pt);
249 md->streams[0].payloads=l;
251 if (call->params.has_video){
253 md->streams[1].rtp_port=call->video_port;
254 md->streams[1].rtcp_port=call->video_port+1;
255 md->streams[1].proto=md->streams[0].proto;
256 md->streams[1].type=SalVideo;
257 l=make_codec_list(lc,lc->codecs_conf.video_codecs,0,NULL);
258 md->streams[1].payloads=l;
261 for(i=0; i<md->nstreams; i++) {
262 if (md->streams[i].proto == SalProtoRtpSavp) {
263 if (keep_srtp_keys && old_md && old_md->streams[i].proto==SalProtoRtpSavp){
265 for(j=0;j<SAL_CRYPTO_ALGO_MAX;++j){
266 memcpy(&md->streams[i].crypto[j],&old_md->streams[i].crypto[j],sizeof(SalSrtpCryptoAlgo));
269 md->streams[i].crypto[0].tag = 1;
270 md->streams[i].crypto[0].algo = AES_128_SHA1_80;
271 if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
272 md->streams[i].crypto[0].algo = 0;
273 md->streams[i].crypto[1].tag = 2;
274 md->streams[i].crypto[1].algo = AES_128_SHA1_32;
275 if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
276 md->streams[i].crypto[1].algo = 0;
277 md->streams[i].crypto[2].algo = 0;
281 update_media_description_from_stun(md,&call->ac,&call->vc);
282 if (call->ice_session != NULL) {
283 linphone_core_update_local_media_description_from_ice(md, call->ice_session);
284 linphone_core_update_ice_state_in_call_stats(call);
286 linphone_address_destroy(addr);
288 if (old_md) sal_media_description_unref(old_md);
291 static int find_port_offset(LinphoneCore *lc, SalStreamType type){
296 bool_t already_used=FALSE;
297 for(offset=0;offset<100;offset+=2){
301 tried_port=linphone_core_get_audio_port (lc)+offset;
304 tried_port=linphone_core_get_video_port (lc)+offset;
308 for(elem=lc->calls;elem!=NULL;elem=elem->next){
309 LinphoneCall *call=(LinphoneCall*)elem->data;
313 existing_port = call->audio_port;
316 existing_port = call->video_port;
319 if (existing_port==tried_port) {
324 if (!already_used) break;
327 ms_error("Could not find any free port !");
333 static int select_random_port(LinphoneCore *lc, SalStreamType type) {
337 int existing_port = 0;
338 int min_port = 0, max_port = 0;
339 bool_t already_used = FALSE;
344 linphone_core_get_audio_port_range(lc, &min_port, &max_port);
347 linphone_core_get_video_port_range(lc, &min_port, &max_port);
350 tried_port = (rand() % (max_port - min_port) + min_port) & ~0x1;
351 if (tried_port < min_port) tried_port = min_port + 2;
352 for (nb_tries = 0; nb_tries < 100; nb_tries++) {
353 for (elem = lc->calls; elem != NULL; elem = elem->next) {
354 LinphoneCall *call = (LinphoneCall *)elem->data;
358 existing_port = call->audio_port;
361 existing_port = call->video_port;
364 if (existing_port == tried_port) {
369 if (!already_used) break;
371 if (nb_tries == 100) {
372 ms_error("Could not find any free port!");
378 static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
380 int min_port, max_port;
381 call->magic=linphone_call_magic;
383 call->state=LinphoneCallIdle;
384 call->transfer_state = LinphoneCallIdle;
385 call->start_time=time(NULL);
386 call->media_start_time=0;
387 call->log=linphone_call_log_new(call, from, to);
388 call->owns_call_log=TRUE;
389 linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
390 linphone_core_get_audio_port_range(call->core, &min_port, &max_port);
391 if (min_port == max_port) {
392 /* Used fixed RTP audio port. */
393 port_offset=find_port_offset (call->core, SalAudio);
394 if (port_offset==-1) return;
395 call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
397 /* Select random RTP audio port in the specified range. */
398 call->audio_port = select_random_port(call->core, SalAudio);
400 linphone_core_get_video_port_range(call->core, &min_port, &max_port);
401 if (min_port == max_port) {
402 /* Used fixed RTP video port. */
403 port_offset=find_port_offset (call->core, SalVideo);
404 if (port_offset==-1) return;
405 call->video_port=linphone_core_get_video_port(call->core)+port_offset;
407 /* Select random RTP video port in the specified range. */
408 call->video_port = select_random_port(call->core, SalVideo);
410 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_AUDIO], LINPHONE_CALL_STATS_AUDIO);
411 linphone_call_init_stats(&call->stats[LINPHONE_CALL_STATS_VIDEO], LINPHONE_CALL_STATS_VIDEO);
414 void linphone_call_init_stats(LinphoneCallStats *stats, int type) {
416 stats->received_rtcp = NULL;
417 stats->sent_rtcp = NULL;
418 stats->ice_state = LinphoneIceStateNotActivated;
422 static void discover_mtu(LinphoneCore *lc, const char *remote){
424 if (lc->net_conf.mtu==0 ){
425 /*attempt to discover mtu*/
426 mtu=ms_discover_mtu(remote);
429 ms_message("Discovered mtu is %i, RTP payload max size is %i",
430 mtu, ms_get_payload_max_size());
435 LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
437 LinphoneCall *call=ms_new0(LinphoneCall,1);
438 call->dir=LinphoneCallOutgoing;
439 call->op=sal_op_new(lc->sal);
440 sal_op_set_user_pointer(call->op,call);
442 linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
443 linphone_call_init_common(call,from,to);
444 call->params=*params;
445 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseIce) {
446 call->ice_session = ice_session_new();
447 ice_session_set_role(call->ice_session, IR_Controlling);
449 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
450 call->ping_time=linphone_core_run_stun_tests(call->core,call);
453 if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
454 call->upnp_session = upnp_session_new();
457 call->camera_active=params->has_video;
459 discover_mtu(lc,linphone_address_get_domain (to));
460 if (params->referer){
461 sal_call_set_referer(call->op,params->referer->op);
462 call->referer=linphone_call_ref(params->referer);
467 LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
468 LinphoneCall *call=ms_new0(LinphoneCall,1);
470 const SalMediaDescription *md;
472 call->dir=LinphoneCallIncoming;
473 sal_op_set_user_pointer(op,call);
477 if (lc->sip_conf.ping_with_options){
478 /*the following sends an option request back to the caller so that
479 we get a chance to discover our nat'd address before answering.*/
480 call->ping_op=sal_op_new(lc->sal);
481 from_str=linphone_address_as_string_uri_only(from);
482 sal_op_set_route(call->ping_op,sal_op_get_network_origin(op));
483 sal_op_set_user_pointer(call->ping_op,call);
484 sal_ping(call->ping_op,linphone_core_find_best_identity(lc,from,NULL),from_str);
488 linphone_address_clean(from);
489 linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
490 linphone_call_init_common(call, from, to);
491 call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/
492 linphone_core_init_default_params(lc, &call->params);
493 md=sal_call_get_remote_media_description(op);
494 call->params.has_video &= !!lc->video_policy.automatically_accept;
496 // It is licit to receive an INVITE without SDP
497 // In this case WE chose the media parameters according to policy.
498 call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
500 switch (linphone_core_get_firewall_policy(call->core)) {
501 case LinphonePolicyUseIce:
502 call->ice_session = ice_session_new();
503 ice_session_set_role(call->ice_session, IR_Controlled);
504 linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
505 if (call->ice_session != NULL) {
506 linphone_call_init_media_streams(call);
507 linphone_call_start_media_streams_for_ice_gathering(call);
508 if (linphone_core_gather_ice_candidates(call->core,call)<0) {
509 /* Ice candidates gathering failed, proceed with the call anyway. */
510 linphone_call_delete_ice_session(call);
511 linphone_call_stop_media_streams_for_ice_gathering(call);
515 case LinphonePolicyUseStun:
516 call->ping_time=linphone_core_run_stun_tests(call->core,call);
517 /* No break to also destroy ice session in this case. */
521 call->camera_active=call->params.has_video;
523 discover_mtu(lc,linphone_address_get_domain(from));
527 /* this function is called internally to get rid of a call.
528 It performs the following tasks:
529 - remove the call from the internal list of calls
530 - update the call logs accordingly
533 static void linphone_call_set_terminated(LinphoneCall *call){
534 LinphoneCore *lc=call->core;
536 linphone_core_update_allocated_audio_bandwidth(lc);
538 call->owns_call_log=FALSE;
539 linphone_call_log_completed(call);
542 if (call == lc->current_call){
543 ms_message("Resetting the current call");
544 lc->current_call=NULL;
547 if (linphone_core_del_call(lc,call) != 0){
548 ms_error("Could not remove the call from the list !!!");
551 if (ms_list_size(lc->calls)==0)
552 linphone_core_notify_all_friends(lc,lc->presence_mode);
554 linphone_core_conference_check_uninit(lc);
555 if (call->ringing_beep){
556 linphone_core_stop_dtmf(lc);
557 call->ringing_beep=FALSE;
560 linphone_call_unref(call->referer);
565 void linphone_call_fix_call_parameters(LinphoneCall *call){
566 call->params.has_video=call->current_params.has_video;
567 call->params.media_encryption=call->current_params.media_encryption;
570 const char *linphone_call_state_to_string(LinphoneCallState cs){
572 case LinphoneCallIdle:
573 return "LinphoneCallIdle";
574 case LinphoneCallIncomingReceived:
575 return "LinphoneCallIncomingReceived";
576 case LinphoneCallOutgoingInit:
577 return "LinphoneCallOutgoingInit";
578 case LinphoneCallOutgoingProgress:
579 return "LinphoneCallOutgoingProgress";
580 case LinphoneCallOutgoingRinging:
581 return "LinphoneCallOutgoingRinging";
582 case LinphoneCallOutgoingEarlyMedia:
583 return "LinphoneCallOutgoingEarlyMedia";
584 case LinphoneCallConnected:
585 return "LinphoneCallConnected";
586 case LinphoneCallStreamsRunning:
587 return "LinphoneCallStreamsRunning";
588 case LinphoneCallPausing:
589 return "LinphoneCallPausing";
590 case LinphoneCallPaused:
591 return "LinphoneCallPaused";
592 case LinphoneCallResuming:
593 return "LinphoneCallResuming";
594 case LinphoneCallRefered:
595 return "LinphoneCallRefered";
596 case LinphoneCallError:
597 return "LinphoneCallError";
598 case LinphoneCallEnd:
599 return "LinphoneCallEnd";
600 case LinphoneCallPausedByRemote:
601 return "LinphoneCallPausedByRemote";
602 case LinphoneCallUpdatedByRemote:
603 return "LinphoneCallUpdatedByRemote";
604 case LinphoneCallIncomingEarlyMedia:
605 return "LinphoneCallIncomingEarlyMedia";
606 case LinphoneCallUpdating:
607 return "LinphoneCallUpdating";
608 case LinphoneCallReleased:
609 return "LinphoneCallReleased";
611 return "undefined state";
614 void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
615 LinphoneCore *lc=call->core;
617 if (call->state!=cstate){
618 if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
619 if (cstate!=LinphoneCallReleased){
620 ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
621 linphone_call_state_to_string(cstate));
625 ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
626 linphone_call_state_to_string(cstate));
627 if (cstate!=LinphoneCallRefered){
628 /*LinphoneCallRefered is rather an event, not a state.
629 Indeed it does not change the state of the call (still paused or running)*/
632 if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
633 switch(call->reason){
634 case LinphoneReasonDeclined:
635 call->log->status=LinphoneCallDeclined;
637 case LinphoneReasonNotAnswered:
638 call->log->status=LinphoneCallMissed;
643 linphone_call_set_terminated (call);
645 if (cstate == LinphoneCallConnected) {
646 call->log->status=LinphoneCallSuccess;
647 call->media_start_time=time(NULL);
650 if (lc->vtable.call_state_changed)
651 lc->vtable.call_state_changed(lc,call,cstate,message);
652 if (cstate==LinphoneCallReleased){
653 if (call->op!=NULL) {
654 /* so that we cannot have anymore upcalls for SAL
655 concerning this call*/
656 sal_op_release(call->op);
659 linphone_call_unref(call);
664 static void linphone_call_destroy(LinphoneCall *obj)
666 linphone_call_delete_ice_session(obj);
668 sal_op_release(obj->op);
671 if (obj->resultdesc!=NULL) {
672 sal_media_description_unref(obj->resultdesc);
673 obj->resultdesc=NULL;
675 if (obj->localdesc!=NULL) {
676 sal_media_description_unref(obj->localdesc);
680 sal_op_release(obj->ping_op);
683 ms_free(obj->refer_to);
685 if (obj->owns_call_log)
686 linphone_call_log_destroy(obj->log);
687 if (obj->auth_token) {
688 ms_free(obj->auth_token);
695 * @addtogroup call_control
700 * Increments the call 's reference count.
701 * An application that wishes to retain a pointer to call object
702 * must use this function to unsure the pointer remains
703 * valid. Once the application no more needs this pointer,
704 * it must call linphone_call_unref().
706 LinphoneCall * linphone_call_ref(LinphoneCall *obj){
712 * Decrements the call object reference count.
713 * See linphone_call_ref().
715 void linphone_call_unref(LinphoneCall *obj){
718 linphone_call_destroy(obj);
723 * Returns current parameters associated to the call.
725 const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
726 return &call->current_params;
729 static bool_t is_video_active(const SalStreamDescription *sd){
730 return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
734 * Returns call parameters proposed by remote.
736 * This is useful when receiving an incoming call, to know whether the remote party
737 * supports video, encryption or whatever.
739 const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
740 LinphoneCallParams *cp=&call->remote_params;
741 memset(cp,0,sizeof(*cp));
743 SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
745 SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
747 asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
748 vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
749 secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
750 secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
752 cp->has_video=is_video_active(secure_vsd);
753 if (secure_asd || asd==NULL)
754 cp->media_encryption=LinphoneMediaEncryptionSRTP;
756 cp->has_video=is_video_active(vsd);
759 if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){
760 cp->low_bandwidth=TRUE;
770 * Returns the remote address associated to this call
773 const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
774 return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
778 * Returns the remote address associated to this call as a string.
780 * The result string must be freed by user using ms_free().
782 char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
783 return linphone_address_as_string(linphone_call_get_remote_address(call));
787 * Retrieves the call's current state.
789 LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
794 * Returns the reason for a call termination (either error or normal termination)
796 LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
801 * Get the user_pointer in the LinphoneCall
803 * @ingroup call_control
805 * return user_pointer an opaque user pointer that can be retrieved at any time
807 void *linphone_call_get_user_pointer(LinphoneCall *call)
809 return call->user_pointer;
813 * Set the user_pointer in the LinphoneCall
815 * @ingroup call_control
817 * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
819 void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
821 call->user_pointer = user_pointer;
825 * Returns the call log associated to this call.
827 LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
832 * Returns the refer-to uri (if the call was transfered).
834 const char *linphone_call_get_refer_to(const LinphoneCall *call){
835 return call->refer_to;
839 * Returns direction of the call (incoming or outgoing).
841 LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
842 return call->log->dir;
846 * Returns the far end's user agent description string, if available.
848 const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
850 return sal_op_get_remote_ua (call->op);
856 * Returns true if this calls has received a transfer that has not been
858 * Pending transfers are executed when this call is being paused or closed,
859 * locally or by remote endpoint.
860 * If the call is already paused while receiving the transfer request, the
861 * transfer immediately occurs.
863 bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
864 return call->refer_pending;
868 * Returns call's duration in seconds.
870 int linphone_call_get_duration(const LinphoneCall *call){
871 if (call->media_start_time==0) return 0;
872 return time(NULL)-call->media_start_time;
876 * Returns the call object this call is replacing, if any.
877 * Call replacement can occur during call transfers.
878 * By default, the core automatically terminates the replaced call and accept the new one.
879 * This function allows the application to know whether a new incoming call is a one that replaces another one.
881 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
882 SalOp *op=sal_call_get_replaces(call->op);
884 return (LinphoneCall*)sal_op_get_user_pointer(op);
890 * Indicate whether camera input should be sent to remote end.
892 void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
894 if (call->videostream!=NULL && call->videostream->ticker!=NULL){
895 LinphoneCore *lc=call->core;
896 MSWebCam *nowebcam=get_nowebcam_device();
897 if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
898 video_stream_change_camera(call->videostream,
899 enable ? lc->video_conf.device : nowebcam);
902 call->camera_active=enable;
907 * Take a photo of currently received video and write it into a jpeg file.
909 int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
911 if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){
912 return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
914 ms_warning("Cannot take snapshot: no currently running video stream on this call.");
921 * Returns TRUE if camera pictures are sent to the remote party.
923 bool_t linphone_call_camera_enabled (const LinphoneCall *call){
924 return call->camera_active;
928 * Enable video stream.
930 void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
931 cp->has_video=enabled;
934 const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
935 return cp->audio_codec;
938 const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
939 return cp->video_codec;
943 * @ingroup call_control
944 * Use to know if this call has been configured in low bandwidth mode.
945 * This mode can be automatically discovered thanks to a stun server when activate_edge_workarounds=1 in section [net] of configuration file.
946 * An application that would have reliable way to know network capacity may not use activate_edge_workarounds=1 but instead manually configure
947 * low bandwidth mode with linphone_call_params_enable_low_bandwidth().
948 * <br> When enabled, this param may transform a call request with video in audio only mode.
949 * @return TRUE if low bandwidth has been configured/detected
951 bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
952 return cp->low_bandwidth;
956 * @ingroup call_control
957 * Indicate low bandwith mode.
958 * Configuring a call to low bandwidth mode will result in the core to activate several settings for the call in order to ensure that bitrate usage
959 * is lowered to the minimum possible. Typically, ptime (packetization time) will be increased, audio codec's output bitrate will be targetted to 20kbit/s provided
960 * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled.
963 void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){
964 cp->low_bandwidth=enabled;
968 * Returns whether video is enabled.
970 bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
971 return cp->has_video;
974 enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
975 return cp->media_encryption;
978 void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
979 cp->media_encryption = e;
984 * Enable sending of real early media (during outgoing calls).
986 void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
987 cp->real_early_media=enabled;
990 bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
991 return cp->real_early_media;
995 * Returns true if the call is part of the locally managed conference.
997 bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
998 return cp->in_conference;
1002 * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
1003 * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
1005 void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
1006 cp->audio_bw=bandwidth;
1009 #ifdef VIDEO_ENABLED
1011 * Request remote side to send us a Video Fast Update.
1013 void linphone_call_send_vfu_request(LinphoneCall *call)
1015 if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
1016 sal_call_send_vfu_request(call->op);
1023 LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
1024 LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
1025 memcpy(ncp,cp,sizeof(LinphoneCallParams));
1032 void linphone_call_params_destroy(LinphoneCallParams *p){
1041 #ifdef TEST_EXT_RENDERER
1042 static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
1043 ms_message("rendercb, local buffer=%p, remote buffer=%p",
1044 local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
1048 #ifdef VIDEO_ENABLED
1049 static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
1050 LinphoneCall* call = (LinphoneCall*) user_pointer;
1051 ms_warning("In linphonecall.c: video_stream_event_cb");
1053 case MS_VIDEO_DECODER_DECODING_ERRORS:
1054 ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
1055 linphone_call_send_vfu_request(call);
1057 case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
1058 ms_message("First video frame decoded successfully");
1059 if (call->nextVideoFrameDecoded._func != NULL)
1060 call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
1063 ms_warning("Unhandled event %i", event_id);
1069 void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) {
1070 call->nextVideoFrameDecoded._func = cb;
1071 call->nextVideoFrameDecoded._user_data = user_data;
1072 #ifdef VIDEO_ENABLED
1073 ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
1077 void linphone_call_init_audio_stream(LinphoneCall *call){
1078 LinphoneCore *lc=call->core;
1079 AudioStream *audiostream;
1082 if (call->audiostream != NULL) return;
1083 call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc));
1084 dscp=linphone_core_get_audio_dscp(lc);
1086 audio_stream_set_dscp(audiostream,dscp);
1087 if (linphone_core_echo_limiter_enabled(lc)){
1088 const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
1089 if (strcasecmp(type,"mic")==0)
1090 audio_stream_enable_echo_limiter(audiostream,ELControlMic);
1091 else if (strcasecmp(type,"full")==0)
1092 audio_stream_enable_echo_limiter(audiostream,ELControlFull);
1094 audio_stream_enable_gain_control(audiostream,TRUE);
1095 if (linphone_core_echo_cancellation_enabled(lc)){
1096 int len,delay,framesize;
1097 const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
1098 len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
1099 delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
1100 framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
1101 audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
1102 if (statestr && audiostream->ec){
1103 ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
1106 audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
1108 int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
1109 audio_stream_enable_noise_gate(audiostream,enabled);
1112 audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc));
1115 RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port);
1116 RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1);
1117 rtp_session_set_transports(audiostream->session,artp,artcp);
1119 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1120 rtp_session_set_pktinfo(audiostream->session, TRUE);
1121 rtp_session_set_symmetric_rtp(audiostream->session, FALSE);
1122 if (ice_session_check_list(call->ice_session, 0) == NULL) {
1123 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1125 audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0);
1126 ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session);
1129 call->audiostream_app_evq = ortp_ev_queue_new();
1130 rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);
1133 void linphone_call_init_video_stream(LinphoneCall *call){
1134 #ifdef VIDEO_ENABLED
1135 LinphoneCore *lc=call->core;
1137 if (!call->params.has_video) {
1138 linphone_call_stop_video_stream(call);
1141 if (call->videostream != NULL) return;
1142 if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){
1143 int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
1144 int dscp=linphone_core_get_video_dscp(lc);
1146 call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc));
1148 video_stream_set_dscp(call->videostream,dscp);
1149 video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
1150 if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size);
1152 if( lc->video_conf.displaytype != NULL)
1153 video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
1154 video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
1156 RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
1157 RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
1158 rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
1160 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1161 rtp_session_set_pktinfo(call->videostream->session, TRUE);
1162 rtp_session_set_symmetric_rtp(call->videostream->session, FALSE);
1163 if (ice_session_check_list(call->ice_session, 1) == NULL) {
1164 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1166 call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1);
1167 ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session);
1169 call->videostream_app_evq = ortp_ev_queue_new();
1170 rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
1171 #ifdef TEST_EXT_RENDERER
1172 video_stream_set_render_callback(call->videostream,rendercb,NULL);
1176 call->videostream=NULL;
1180 void linphone_call_init_media_streams(LinphoneCall *call){
1181 linphone_call_init_audio_stream(call);
1182 linphone_call_init_video_stream(call);
1186 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
1188 static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){
1189 if (dtmf<0 || dtmf>15){
1190 ms_warning("Bad dtmf value %i",dtmf);
1193 if (lc->vtable.dtmf_received != NULL)
1194 lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
1197 static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
1199 MSFilter *f=st->equalizer;
1200 int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
1201 const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
1202 ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
1208 if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
1209 ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
1210 ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
1219 void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
1220 float mic_gain=lc->sound_conf.soft_mic_lev;
1223 float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
1224 float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
1225 int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
1228 linphone_core_set_mic_gain_db (lc, mic_gain);
1230 audio_stream_set_mic_gain(st,0);
1232 recv_gain = lc->sound_conf.soft_play_lev;
1233 if (recv_gain != 0) {
1234 linphone_core_set_playback_gain_db (lc,recv_gain);
1238 ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
1239 float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
1240 thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
1241 float force=lp_config_get_float(lc->config,"sound","el_force",-1);
1242 int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
1243 float transmit_thres=lp_config_get_float(lc->config,"sound","el_transmit_thres",-1);
1246 if (speed==-1) speed=0.03;
1247 if (force==-1) force=25;
1248 ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
1249 ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
1251 ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
1253 ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
1254 if (transmit_thres!=-1)
1255 ms_filter_call_method(f,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&transmit_thres);
1257 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1258 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
1261 /* parameters for a limited noise-gate effect, using echo limiter threshold */
1262 float floorgain = 1/pow(10,(mic_gain)/10);
1263 int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0);
1264 ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc);
1265 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1266 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
1268 parametrize_equalizer(lc,st);
1271 static void post_configure_audio_streams(LinphoneCall*call){
1272 AudioStream *st=call->audiostream;
1273 LinphoneCore *lc=call->core;
1274 _post_configure_audio_stream(st,lc,call->audio_muted);
1275 if (lc->vtable.dtmf_received!=NULL){
1276 /* replace by our default action*/
1277 audio_stream_play_received_dtmfs(call->audiostream,FALSE);
1278 /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/
1282 static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
1285 RtpProfile *prof=rtp_profile_new("Call profile");
1288 LinphoneCore *lc=call->core;
1290 const LinphoneCallParams *params=&call->params;
1293 for(elem=desc->payloads;elem!=NULL;elem=elem->next){
1294 PayloadType *pt=(PayloadType*)elem->data;
1297 if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
1298 if (desc->type==SalAudio){
1299 linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
1300 if (params->up_ptime)
1301 up_ptime=params->up_ptime;
1302 else up_ptime=linphone_core_get_upload_ptime(lc);
1304 *used_pt=payload_type_get_number(pt);
1307 if (desc->bandwidth>0) remote_bw=desc->bandwidth;
1308 else if (md->bandwidth>0) {
1309 /*case where b=AS is given globally, not per stream*/
1310 remote_bw=md->bandwidth;
1311 if (desc->type==SalVideo){
1312 remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
1316 if (desc->type==SalAudio){
1317 int audio_bw=call->audio_bw;
1319 if (params->up_bw< audio_bw)
1320 audio_bw=params->up_bw;
1322 bw=get_min_bandwidth(audio_bw,remote_bw);
1323 }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
1324 if (bw>0) pt->normal_bitrate=bw*1000;
1325 else if (desc->type==SalAudio){
1326 pt->normal_bitrate=-1;
1329 up_ptime=desc->ptime;
1333 snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
1334 payload_type_append_send_fmtp(pt,tmp);
1336 number=payload_type_get_number(pt);
1337 if (rtp_profile_get_payload(prof,number)!=NULL){
1338 ms_warning("A payload type with number %i already exists in profile !",number);
1340 rtp_profile_set_payload(prof,number,pt);
1346 static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
1347 int pause_time=3000;
1348 audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
1349 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1352 static bool_t linphone_call_sound_resources_available(LinphoneCall *call){
1353 LinphoneCore *lc=call->core;
1354 LinphoneCall *current=linphone_core_get_current_call(lc);
1355 return !linphone_core_is_in_conference(lc) &&
1356 (current==NULL || current==call);
1358 static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
1360 for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
1361 if (crypto[i].tag == tag) {
1367 static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
1368 LinphoneCore *lc=call->core;
1370 char rtcp_tool[128]={0};
1371 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1372 /* look for savp stream first */
1373 const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
1374 SalProtoRtpSavp,SalAudio);
1375 /* no savp audio stream, use avp */
1377 stream=sal_media_description_find_stream(call->resultdesc,
1378 SalProtoRtpAvp,SalAudio);
1380 if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
1381 MSSndCard *playcard=lc->sound_conf.lsd_card ?
1382 lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
1383 MSSndCard *captcard=lc->sound_conf.capt_sndcard;
1384 const char *playfile=lc->play_file;
1385 const char *recfile=lc->rec_file;
1386 call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
1390 call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
1391 if (playcard==NULL) {
1392 ms_warning("No card defined for playback !");
1394 if (captcard==NULL) {
1395 ms_warning("No card defined for capture !");
1397 /*Replace soundcard filters by inactive file players or recorders
1398 when placed in recvonly or sendonly mode*/
1399 if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){
1402 }else if (stream->dir==SalStreamSendOnly){
1406 /*And we will eventually play "playfile" if set by the user*/
1409 if (send_ringbacktone){
1411 playfile=NULL;/* it is setup later*/
1413 /*if playfile are supplied don't use soundcards*/
1414 if (lc->use_files) {
1418 if (call->params.in_conference){
1419 /* first create the graph without soundcard resources*/
1420 captcard=playcard=NULL;
1422 if (!linphone_call_sound_resources_available(call)){
1423 ms_message("Sound resources are used by another call, not using soundcard.");
1424 captcard=playcard=NULL;
1426 use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
1427 if (playcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
1428 if (captcard && stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
1429 audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
1430 audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
1431 audio_stream_start_full(
1433 call->audio_profile,
1434 stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr,
1436 stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
1437 linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0,
1439 linphone_core_get_audio_jittcomp(lc),
1446 post_configure_audio_streams(call);
1447 if (muted && !send_ringbacktone){
1448 audio_stream_set_mic_gain(call->audiostream,0);
1450 if (stream->dir==SalStreamSendOnly && playfile!=NULL){
1452 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1454 if (send_ringbacktone){
1455 setup_ring_player(lc,call);
1457 audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
1459 /* valid local tags are > 0 */
1460 if (stream->proto == SalProtoRtpSavp) {
1461 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1462 SalProtoRtpSavp,SalAudio);
1463 int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
1465 if (crypto_idx >= 0) {
1466 audio_stream_enable_strp(
1468 stream->crypto[0].algo,
1469 local_st_desc->crypto[crypto_idx].master_key,
1470 stream->crypto[0].master_key);
1471 call->audiostream_encrypted=TRUE;
1473 ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
1474 call->audiostream_encrypted=FALSE;
1476 }else call->audiostream_encrypted=FALSE;
1477 if (call->params.in_conference){
1478 /*transform the graph to connect it to the conference filter */
1479 bool_t mute=stream->dir==SalStreamRecvOnly;
1480 linphone_call_add_to_conf(call, mute);
1482 call->current_params.in_conference=call->params.in_conference;
1483 call->current_params.low_bandwidth=call->params.low_bandwidth;
1484 }else ms_warning("No audio stream accepted ?");
1488 static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){
1489 #ifdef VIDEO_ENABLED
1490 LinphoneCore *lc=call->core;
1492 /* look for savp stream first */
1493 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1494 SalProtoRtpSavp,SalVideo);
1495 char rtcp_tool[128]={0};
1496 snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1498 /* no savp audio stream, use avp */
1500 vstream=sal_media_description_find_stream(call->resultdesc,
1501 SalProtoRtpAvp,SalVideo);
1503 /* shutdown preview */
1504 if (lc->previewstream!=NULL) {
1505 video_preview_stop(lc->previewstream);
1506 lc->previewstream=NULL;
1509 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
1510 const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
1511 const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
1512 call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
1514 call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
1515 VideoStreamDir dir=VideoStreamSendRecv;
1516 MSWebCam *cam=lc->video_conf.device;
1517 bool_t is_inactive=FALSE;
1519 call->current_params.has_video=TRUE;
1521 video_stream_enable_adaptive_bitrate_control(call->videostream,
1522 linphone_core_adaptive_rate_control_enabled(lc));
1523 video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc));
1524 video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
1525 video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
1526 if (lc->video_window_id!=0)
1527 video_stream_set_native_window_id(call->videostream,lc->video_window_id);
1528 if (lc->preview_window_id!=0)
1529 video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id);
1530 video_stream_use_preview_video_window (call->videostream,lc->use_preview_window);
1532 if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
1533 cam=get_nowebcam_device();
1534 dir=VideoStreamSendOnly;
1535 }else if (vstream->dir==SalStreamRecvOnly && lc->video_conf.display ){
1536 dir=VideoStreamRecvOnly;
1537 }else if (vstream->dir==SalStreamSendRecv){
1538 if (lc->video_conf.display && lc->video_conf.capture)
1539 dir=VideoStreamSendRecv;
1540 else if (lc->video_conf.display)
1541 dir=VideoStreamRecvOnly;
1543 dir=VideoStreamSendOnly;
1545 ms_warning("video stream is inactive.");
1546 /*either inactive or incompatible with local capabilities*/
1549 if (call->camera_active==FALSE || all_inputs_muted){
1550 cam=get_nowebcam_device();
1553 call->log->video_enabled = TRUE;
1554 video_stream_set_direction (call->videostream, dir);
1555 ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
1556 video_stream_set_device_rotation(call->videostream, lc->device_rotation);
1557 video_stream_start(call->videostream,
1558 call->video_profile, rtp_addr, vstream->rtp_port,
1559 rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0,
1560 used_pt, linphone_core_get_video_jittcomp(lc), cam);
1561 video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
1564 if (vstream->proto == SalProtoRtpSavp) {
1565 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1566 SalProtoRtpSavp,SalVideo);
1568 video_stream_enable_strp(
1570 vstream->crypto[0].algo,
1571 local_st_desc->crypto[0].master_key,
1572 vstream->crypto[0].master_key
1574 call->videostream_encrypted=TRUE;
1576 call->videostream_encrypted=FALSE;
1578 }else ms_warning("No video stream accepted.");
1580 ms_warning("No valid video stream defined.");
1585 void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
1586 LinphoneCore *lc=call->core;
1588 call->current_params.audio_codec = NULL;
1589 call->current_params.video_codec = NULL;
1591 LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
1593 bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
1594 #ifdef VIDEO_ENABLED
1595 const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1596 SalProtoRtpAvp,SalVideo);
1599 if ((call->audiostream == NULL) && (call->videostream == NULL)) {
1600 ms_fatal("start_media_stream() called without prior init !");
1603 cname=linphone_address_as_string_uri_only(me);
1605 #if defined(VIDEO_ENABLED)
1606 if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
1607 /*when video is used, do not make adaptive rate control on audio, it is stupid.*/
1611 if (call->audiostream!=NULL) {
1612 linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
1614 call->current_params.has_video=FALSE;
1615 if (call->videostream!=NULL) {
1616 linphone_call_start_video_stream(call,cname,all_inputs_muted);
1619 call->all_muted=all_inputs_muted;
1620 call->playing_ringbacktone=send_ringbacktone;
1621 call->up_bw=linphone_core_get_upload_bandwidth(lc);
1623 if (call->params.media_encryption==LinphoneMediaEncryptionZRTP) {
1624 OrtpZrtpParams params;
1625 /*will be set later when zrtp is activated*/
1626 call->current_params.media_encryption=LinphoneMediaEncryptionNone;
1628 params.zid_file=lc->zrtp_secrets_cache;
1629 audio_stream_enable_zrtp(call->audiostream,¶ms);
1630 }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
1631 call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
1632 LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
1635 /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
1636 * further in the call, for example during pause,resume, conferencing reINVITEs*/
1637 linphone_call_fix_call_parameters(call);
1638 if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) {
1639 ice_session_start_connectivity_checks(call->ice_session);
1645 linphone_address_destroy(me);
1648 void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){
1649 audio_stream_prepare_sound(call->audiostream, NULL, NULL);
1650 #ifdef VIDEO_ENABLED
1651 if (call->videostream) {
1652 video_stream_prepare_video(call->videostream);
1657 void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){
1658 audio_stream_unprepare_sound(call->audiostream);
1659 #ifdef VIDEO_ENABLED
1660 if (call->videostream) {
1661 video_stream_unprepare_video(call->videostream);
1666 void linphone_call_delete_ice_session(LinphoneCall *call){
1667 if (call->ice_session != NULL) {
1668 ice_session_destroy(call->ice_session);
1669 call->ice_session = NULL;
1670 if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL;
1671 if (call->videostream != NULL) call->videostream->ice_check_list = NULL;
1672 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
1673 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
1677 static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
1678 audio_stream_get_local_rtp_stats (st,&log->local_stats);
1679 log->quality=audio_stream_get_average_quality_rating(st);
1682 void linphone_call_stop_audio_stream(LinphoneCall *call) {
1683 if (call->audiostream!=NULL) {
1684 rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq);
1685 ortp_ev_queue_flush(call->audiostream_app_evq);
1686 ortp_ev_queue_destroy(call->audiostream_app_evq);
1687 call->audiostream_app_evq=NULL;
1689 if (call->audiostream->ec){
1690 const char *state_str=NULL;
1691 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_STATE_STRING,&state_str);
1693 ms_message("Writing echo canceler state, %i bytes",(int)strlen(state_str));
1694 lp_config_set_string(call->core->config,"sound","ec_state",state_str);
1697 linphone_call_log_fill_stats (call->log,call->audiostream);
1698 if (call->endpoint){
1699 linphone_call_remove_from_conf(call);
1701 audio_stream_stop(call->audiostream);
1702 call->audiostream=NULL;
1706 void linphone_call_stop_video_stream(LinphoneCall *call) {
1707 #ifdef VIDEO_ENABLED
1708 if (call->videostream!=NULL){
1709 rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq);
1710 ortp_ev_queue_flush(call->videostream_app_evq);
1711 ortp_ev_queue_destroy(call->videostream_app_evq);
1712 call->videostream_app_evq=NULL;
1713 video_stream_stop(call->videostream);
1714 call->videostream=NULL;
1719 void linphone_call_stop_media_streams(LinphoneCall *call){
1720 linphone_call_stop_audio_stream(call);
1721 linphone_call_stop_video_stream(call);
1722 ms_event_queue_skip(call->core->msevq);
1724 if (call->audio_profile){
1725 rtp_profile_clear_all(call->audio_profile);
1726 rtp_profile_destroy(call->audio_profile);
1727 call->audio_profile=NULL;
1729 if (call->video_profile){
1730 rtp_profile_clear_all(call->video_profile);
1731 rtp_profile_destroy(call->video_profile);
1732 call->video_profile=NULL;
1738 void linphone_call_enable_echo_cancellation(LinphoneCall *call, bool_t enable) {
1739 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1740 bool_t bypass_mode = !enable;
1741 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_SET_BYPASS_MODE,&bypass_mode);
1744 bool_t linphone_call_echo_cancellation_enabled(LinphoneCall *call) {
1745 if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1747 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_BYPASS_MODE,&val);
1750 return linphone_core_echo_cancellation_enabled(call->core);
1754 void linphone_call_enable_echo_limiter(LinphoneCall *call, bool_t val){
1755 if (call!=NULL && call->audiostream!=NULL ) {
1757 const char *type=lp_config_get_string(call->core->config,"sound","el_type","mic");
1758 if (strcasecmp(type,"mic")==0)
1759 audio_stream_enable_echo_limiter(call->audiostream,ELControlMic);
1760 else if (strcasecmp(type,"full")==0)
1761 audio_stream_enable_echo_limiter(call->audiostream,ELControlFull);
1763 audio_stream_enable_echo_limiter(call->audiostream,ELInactive);
1768 bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){
1769 if (call!=NULL && call->audiostream!=NULL ){
1770 return call->audiostream->el_type !=ELInactive ;
1772 return linphone_core_echo_limiter_enabled(call->core);
1777 * @addtogroup call_misc
1782 * Returns the measured sound volume played locally (received from remote).
1783 * It is expressed in dbm0.
1785 float linphone_call_get_play_volume(LinphoneCall *call){
1786 AudioStream *st=call->audiostream;
1787 if (st && st->volrecv){
1789 ms_filter_call_method(st->volrecv,MS_VOLUME_GET,&vol);
1793 return LINPHONE_VOLUME_DB_LOWEST;
1797 * Returns the measured sound volume recorded locally (sent to remote).
1798 * It is expressed in dbm0.
1800 float linphone_call_get_record_volume(LinphoneCall *call){
1801 AudioStream *st=call->audiostream;
1802 if (st && st->volsend && !call->audio_muted && call->state==LinphoneCallStreamsRunning){
1804 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
1808 return LINPHONE_VOLUME_DB_LOWEST;
1812 * Obtain real-time quality rating of the call
1814 * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
1815 * during all the duration of the call. This function returns its value at the time of the function call.
1816 * It is expected that the rating is updated at least every 5 seconds or so.
1817 * The rating is a floating point number comprised between 0 and 5.
1819 * 4-5 = good quality <br>
1820 * 3-4 = average quality <br>
1821 * 2-3 = poor quality <br>
1822 * 1-2 = very poor quality <br>
1823 * 0-1 = can't be worse, mostly unusable <br>
1825 * @returns The function returns -1 if no quality measurement is available, for example if no
1826 * active audio stream exist. Otherwise it returns the quality rating.
1828 float linphone_call_get_current_quality(LinphoneCall *call){
1829 if (call->audiostream){
1830 return audio_stream_get_quality_rating(call->audiostream);
1836 * Returns call quality averaged over all the duration of the call.
1838 * See linphone_call_get_current_quality() for more details about quality measurement.
1840 float linphone_call_get_average_quality(LinphoneCall *call){
1841 if (call->audiostream){
1842 return audio_stream_get_average_quality_rating(call->audiostream);
1848 * Access last known statistics for audio stream, for a given call.
1850 const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) {
1851 return &call->stats[LINPHONE_CALL_STATS_AUDIO];
1855 * Access last known statistics for video stream, for a given call.
1857 const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) {
1858 return &call->stats[LINPHONE_CALL_STATS_VIDEO];
1866 static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){
1867 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0;
1868 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0;
1869 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0;
1870 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0;
1871 ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
1872 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
1873 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth ,
1874 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
1875 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth
1879 static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
1883 from = linphone_call_get_remote_address_as_string(call);
1886 snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
1891 snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
1893 if (lc->vtable.display_warning!=NULL)
1894 lc->vtable.display_warning(lc,temp);
1895 linphone_core_terminate_call(lc,call);
1898 static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
1899 OrtpEventType evt=ortp_event_get_type(ev);
1900 OrtpEventData *evd=ortp_event_get_data(ev);
1903 if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) {
1904 switch (ice_session_state(call->ice_session)) {
1906 ice_session_select_candidates(call->ice_session);
1907 if (ice_session_role(call->ice_session) == IR_Controlling) {
1908 linphone_core_update_call(call->core, call, &call->current_params);
1912 if (ice_session_has_completed_check_list(call->ice_session) == TRUE) {
1913 ice_session_select_candidates(call->ice_session);
1914 if (ice_session_role(call->ice_session) == IR_Controlling) {
1915 /* At least one ICE session has succeeded, so perform a call update. */
1916 linphone_core_update_call(call->core, call, &call->current_params);
1923 linphone_core_update_ice_state_in_call_stats(call);
1924 } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) {
1926 if (evd->info.ice_processing_successful==TRUE) {
1927 ice_session_compute_candidates_foundations(call->ice_session);
1928 ice_session_eliminate_redundant_candidates(call->ice_session);
1929 ice_session_choose_default_candidates(call->ice_session);
1930 ping_time = ice_session_average_gathering_round_trip_time(call->ice_session);
1931 if (ping_time >=0) {
1932 call->ping_time=ping_time;
1935 ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core));
1936 linphone_call_delete_ice_session(call);
1938 switch (call->state) {
1939 case LinphoneCallUpdating:
1940 linphone_core_start_update_call(call->core, call);
1942 case LinphoneCallUpdatedByRemote:
1943 linphone_core_start_accept_call_update(call->core, call);
1945 case LinphoneCallOutgoingInit:
1946 linphone_call_stop_media_streams_for_ice_gathering(call);
1947 linphone_core_proceed_with_invite_if_ready(call->core, call, NULL);
1949 case LinphoneCallIdle:
1950 linphone_call_stop_media_streams_for_ice_gathering(call);
1951 linphone_core_notify_incoming_call(call->core, call);
1956 } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
1957 linphone_core_start_accept_call_update(call->core, call);
1958 linphone_core_update_ice_state_in_call_stats(call);
1959 } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
1960 ice_session_restart(call->ice_session);
1961 ice_session_set_role(call->ice_session, IR_Controlling);
1962 linphone_core_update_call(call->core, call, &call->current_params);
1966 void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
1967 LinphoneCore* lc = call->core;
1968 int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
1969 bool_t disconnected=FALSE;
1971 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed){
1972 RtpSession *as=NULL,*vs=NULL;
1973 float audio_load=0, video_load=0;
1974 if (call->audiostream!=NULL){
1975 as=call->audiostream->session;
1976 if (call->audiostream->ticker)
1977 audio_load=ms_ticker_get_average_load(call->audiostream->ticker);
1979 if (call->videostream!=NULL){
1980 if (call->videostream->ticker)
1981 video_load=ms_ticker_get_average_load(call->videostream->ticker);
1982 vs=call->videostream->session;
1984 report_bandwidth(call,as,vs);
1985 ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
1987 #ifdef VIDEO_ENABLED
1988 if (call->videostream!=NULL) {
1991 /* Ensure there is no dangling ICE check list. */
1992 if (call->ice_session == NULL) call->videostream->ice_check_list = NULL;
1994 // Beware that the application queue should not depend on treatments fron the
1995 // mediastreamer queue.
1996 video_stream_iterate(call->videostream);
1998 while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){
1999 OrtpEventType evt=ortp_event_get_type(ev);
2000 OrtpEventData *evd=ortp_event_get_data(ev);
2001 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2002 linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2003 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2004 call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session);
2005 if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
2006 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
2007 call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
2009 if (lc->vtable.call_stats_updated)
2010 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
2011 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2012 memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t));
2013 if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
2014 freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
2015 call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet;
2017 if (lc->vtable.call_stats_updated)
2018 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
2019 } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2020 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2021 handle_ice_events(call, ev);
2023 ortp_event_destroy(ev);
2027 if (call->audiostream!=NULL) {
2030 /* Ensure there is no dangling ICE check list. */
2031 if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL;
2033 // Beware that the application queue should not depend on treatments fron the
2034 // mediastreamer queue.
2035 audio_stream_iterate(call->audiostream);
2037 while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){
2038 OrtpEventType evt=ortp_event_get_type(ev);
2039 OrtpEventData *evd=ortp_event_get_data(ev);
2040 if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2041 linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2042 } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
2043 linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
2044 } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2045 call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session);
2046 if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
2047 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
2048 call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
2050 if (lc->vtable.call_stats_updated)
2051 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2052 } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2053 memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t));
2054 if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
2055 freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
2056 call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet;
2058 if (lc->vtable.call_stats_updated)
2059 lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2060 } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2061 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2062 handle_ice_events(call, ev);
2063 } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
2064 linphone_core_dtmf_received(lc,evd->info.telephone_event);
2066 ortp_event_destroy(ev);
2069 if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
2070 disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
2072 linphone_core_disconnected(call->core,call);
2075 void linphone_call_log_completed(LinphoneCall *call){
2076 LinphoneCore *lc=call->core;
2078 call->log->duration=time(NULL)-call->start_time;
2080 if (call->log->status==LinphoneCallMissed){
2083 info=ortp_strdup_printf(ngettext("You have missed %i call.",
2084 "You have missed %i calls.", lc->missed_calls),
2086 if (lc->vtable.display_status!=NULL)
2087 lc->vtable.display_status(lc,info);
2090 lc->call_logs=ms_list_prepend(lc->call_logs,(void *)call->log);
2091 if (ms_list_size(lc->call_logs)>lc->max_call_logs){
2092 MSList *elem,*prevelem=NULL;
2093 /*find the last element*/
2094 for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
2098 linphone_call_log_destroy((LinphoneCallLog*)elem->data);
2099 lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
2101 if (lc->vtable.call_log_updated!=NULL){
2102 lc->vtable.call_log_updated(lc,call->log);
2104 call_logs_write_to_config_file(lc);
2107 LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
2108 return call->transfer_state;
2111 void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
2112 if (state != call->transfer_state) {
2113 LinphoneCore* lc = call->core;
2114 call->transfer_state = state;
2115 if (lc->vtable.transfer_state_changed)
2116 lc->vtable.transfer_state_changed(lc, call, state);
2121 * Returns true if the call is part of the conference.
2122 * @ingroup conferencing
2124 bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
2125 return call->params.in_conference;
2130 * Perform a zoom of the video displayed during a call.
2131 * @param call the call.
2132 * @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied.
2133 * @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.
2134 * @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.
2136 * 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.
2138 void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) {
2139 VideoStream* vstream = call->videostream;
2140 if (vstream && vstream->output) {
2143 if (zoom_factor < 1)
2145 float halfsize = 0.5 * 1.0 / zoom_factor;
2147 if ((*cx - halfsize) < 0)
2149 if ((*cx + halfsize) > 1)
2151 if ((*cy - halfsize) < 0)
2153 if ((*cy + halfsize) > 1)
2156 zoom[0] = zoom_factor;
2159 ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
2160 }else ms_warning("Could not apply zoom: video output wasn't activated.");