]> sjero.net Git - linphone/blob - coreapi/linphonecall.c
uPnP in progress
[linphone] / coreapi / linphonecall.c
1
2 /*
3 linphone
4 Copyright (C) 2010  Belledonne Communications SARL
5  (simon.morlat@linphone.org)
6
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.
11
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.
16
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.
20 */
21 #ifdef WIN32
22 #include <time.h>
23 #endif
24 #include "linphonecore.h"
25 #include "sipsetup.h"
26 #include "lpconfig.h"
27 #include "private.h"
28 #include <ortp/event.h>
29 #include <ortp/b64.h>
30 #include <math.h>
31
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"
39
40 #ifdef VIDEO_ENABLED
41 static MSWebCam *get_nowebcam_device(){
42         return ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"StaticImage: Static picture");
43 }
44 #endif
45
46 static bool_t generate_b64_crypto_key(int key_length, char* key_out) {
47         int b64_size;
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");
51                 free(tmp);
52                 return FALSE;
53         }
54         
55         b64_size = b64_encode((const char*)tmp, key_length, NULL, 0);
56         if (b64_size == 0) {
57                 ms_error("Failed to b64 encode key");
58                 free(tmp);
59                 return FALSE;
60         }
61         key_out[b64_size] = '\0';
62         b64_encode((const char*)tmp, key_length, key_out, 40);
63         free(tmp);
64         return TRUE;
65 }
66
67 LinphoneCore *linphone_call_get_core(const LinphoneCall *call){
68         return call->core;
69 }
70
71 const char* linphone_call_get_authentication_token(LinphoneCall *call){
72         return call->auth_token;
73 }
74
75 bool_t linphone_call_get_authentication_token_verified(LinphoneCall *call){
76         return call->auth_token_verified;
77 }
78
79 static bool_t linphone_call_are_all_streams_encrypted(LinphoneCall *call) {
80         // Check ZRTP encryption in audiostream
81         if (!call->audiostream_encrypted) {
82                 return FALSE;
83         }
84
85 #ifdef VIDEO_ENABLED
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) {
89                 return FALSE;
90         }
91 #endif
92
93         return TRUE;
94 }
95
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);
103         } else {
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);
108         }
109 }
110
111 #ifdef VIDEO_ENABLED
112 static void linphone_call_videostream_encryption_changed(void *data, bool_t encrypted){
113         ms_message("Video stream is %s", encrypted ? "encrypted" : "not encrypted");
114
115         LinphoneCall *call = (LinphoneCall *)data;
116         call->videostream_encrypted=encrypted;
117         propagate_encryption_changed(call);
118 }
119 #endif
120
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");
124
125         LinphoneCall *call = (LinphoneCall *)data;
126         call->audiostream_encrypted=encrypted;
127         
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);
131         }
132
133         propagate_encryption_changed(call);
134
135
136 #ifdef VIDEO_ENABLED
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,&params);
144         }
145 #endif
146 }
147
148
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);
153
154         call->auth_token=ms_strdup(auth_token);
155         call->auth_token_verified=verified;
156
157         ms_message("Authentication token is %s (%s)", auth_token, verified?"verified":"unverified");
158 }
159
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");
163         }
164         if (call->audiostream->ortpZrtpContext==NULL){
165                 ms_error("linphone_call_set_authentication_token_verified(): No zrtp context.");
166         }
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);
171         }
172         call->auth_token_verified=verified;
173         propagate_encryption_changed(call);
174 }
175
176 static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit,int* max_sample_rate){
177         MSList *l=NULL;
178         const MSList *it;
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);
185                                 continue;
186                         }
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);
190                         }
191                 }
192         }
193         return l;
194 }
195
196 static void update_media_description_from_stun(SalMediaDescription *md, const StunCandidate *ac, const StunCandidate *vc){
197         if (ac->port!=0){
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);
202                 }
203         }
204         if (vc->port!=0){
205                 strcpy(md->streams[1].rtp_addr,vc->addr);
206                 md->streams[1].rtp_port=vc->port;
207         }
208         
209 }
210
211 void linphone_call_make_local_media_description(LinphoneCore *lc, LinphoneCall *call){
212         MSList *l;
213         PayloadType *pt;
214         SalMediaDescription *old_md=call->localdesc;
215         int i;
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);
221         
222         linphone_core_adapt_to_network(lc,call->ping_time,&call->params);
223
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));
226         md->nstreams=1;
227         strncpy(md->addr,call->localip,sizeof(md->addr));
228         strncpy(md->username,username,sizeof(md->username));
229         
230         if (call->params.down_bw)
231                 md->bandwidth=call->params.down_bw;
232         else md->bandwidth=linphone_core_get_download_bandwidth(lc);
233
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;
244         else
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;
250
251         if (call->params.has_video){
252                 md->nstreams++;
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;
259         }
260         
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){
264                                 int j;
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));
267                                 }
268                         }else{
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;
278                         }
279                 }
280         }
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);
285         }
286         linphone_address_destroy(addr);
287         call->localdesc=md;
288         if (old_md) sal_media_description_unref(old_md);
289 }
290
291 static int find_port_offset(LinphoneCore *lc, SalStreamType type){
292         int offset;
293         MSList *elem;
294         int tried_port;
295         int existing_port;
296         bool_t already_used=FALSE;
297         for(offset=0;offset<100;offset+=2){
298                 switch (type) {
299                         default:
300                         case SalAudio:
301                                 tried_port=linphone_core_get_audio_port (lc)+offset;
302                                 break;
303                         case SalVideo:
304                                 tried_port=linphone_core_get_video_port (lc)+offset;
305                                 break;
306                 }
307                 already_used=FALSE;
308                 for(elem=lc->calls;elem!=NULL;elem=elem->next){
309                         LinphoneCall *call=(LinphoneCall*)elem->data;
310                         switch (type) {
311                                 default:
312                                 case SalAudio:
313                                         existing_port = call->audio_port;
314                                         break;
315                                 case SalVideo:
316                                         existing_port = call->video_port;
317                                         break;
318                         }
319                         if (existing_port==tried_port) {
320                                 already_used=TRUE;
321                                 break;
322                         }
323                 }
324                 if (!already_used) break;
325         }
326         if (offset==100){
327                 ms_error("Could not find any free port !");
328                 return -1;
329         }
330         return offset;
331 }
332
333 static int select_random_port(LinphoneCore *lc, SalStreamType type) {
334         MSList *elem;
335         int nb_tries;
336         int tried_port = 0;
337         int existing_port = 0;
338         int min_port = 0, max_port = 0;
339         bool_t already_used = FALSE;
340
341         switch (type) {
342                 default:
343                 case SalAudio:
344                         linphone_core_get_audio_port_range(lc, &min_port, &max_port);
345                         break;
346                 case SalVideo:
347                         linphone_core_get_video_port_range(lc, &min_port, &max_port);
348                         break;
349         }
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;
355                         switch (type) {
356                                 default:
357                                 case SalAudio:
358                                         existing_port = call->audio_port;
359                                         break;
360                                 case SalVideo:
361                                         existing_port = call->video_port;
362                                         break;
363                         }
364                         if (existing_port == tried_port) {
365                                 already_used = TRUE;
366                                 break;
367                         }
368                 }
369                 if (!already_used) break;
370         }
371         if (nb_tries == 100) {
372                 ms_error("Could not find any free port!");
373                 return -1;
374         }
375         return tried_port;
376 }
377
378 static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
379         int port_offset;
380         int min_port, max_port;
381         call->magic=linphone_call_magic;
382         call->refcnt=1;
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;
396         } else {
397                 /* Select random RTP audio port in the specified range. */
398                 call->audio_port = select_random_port(call->core, SalAudio);
399         }
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;
406         } else {
407                 /* Select random RTP video port in the specified range. */
408                 call->video_port = select_random_port(call->core, SalVideo);
409         }
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);
412 }
413
414 void linphone_call_init_stats(LinphoneCallStats *stats, int type) {
415         stats->type = type;
416         stats->received_rtcp = NULL;
417         stats->sent_rtcp = NULL;
418         stats->ice_state = LinphoneIceStateNotActivated;
419 }
420
421
422 static void discover_mtu(LinphoneCore *lc, const char *remote){
423         int mtu;
424         if (lc->net_conf.mtu==0 ){
425                 /*attempt to discover mtu*/
426                 mtu=ms_discover_mtu(remote);
427                 if (mtu>0){
428                         ms_set_mtu(mtu);
429                         ms_message("Discovered mtu is %i, RTP payload max size is %i",
430                                 mtu, ms_get_payload_max_size());
431                 }
432         }
433 }
434
435 LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params)
436 {
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);
441         call->core=lc;
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);
448         }
449         if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
450                 call->ping_time=linphone_core_run_stun_tests(call->core,call);
451         }
452 #ifdef BUILD_UPNP
453         if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
454                 call->upnp_session = upnp_session_new(call);
455         }
456 #endif //BUILD_UPNP
457         call->camera_active=params->has_video;
458         
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);
463         }
464         return call;
465 }
466
467 LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
468         LinphoneCall *call=ms_new0(LinphoneCall,1);
469         char *from_str;
470         const SalMediaDescription *md;
471
472         call->dir=LinphoneCallIncoming;
473         sal_op_set_user_pointer(op,call);
474         call->op=op;
475         call->core=lc;
476
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);
485                 ms_free(from_str);
486         }
487
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;
495         if (md) {
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);
499         }
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);
512                                 }
513                         }
514                         break;
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. */
518                         break;
519                 case LinphonePolicyUseUpnp:
520 #ifdef BUILD_UPNP
521                 call->upnp_session = upnp_session_new(call);
522                 if (call->ice_session != NULL) {
523                         linphone_call_init_media_streams(call);
524                         if (linphone_core_update_upnp(call->core,call)<0) {
525                                 /* uPnP port mappings failed, proceed with the call anyway. */
526                                 linphone_call_delete_upnp_session(call);
527                         }
528                 }
529 #endif //BUILD_UPNP
530                         break;
531                 default:
532                         break;
533         }
534         call->camera_active=call->params.has_video;
535         
536         discover_mtu(lc,linphone_address_get_domain(from));
537         return call;
538 }
539
540 /* this function is called internally to get rid of a call.
541  It performs the following tasks:
542  - remove the call from the internal list of calls
543  - update the call logs accordingly
544 */
545
546 static void linphone_call_set_terminated(LinphoneCall *call){
547         LinphoneCore *lc=call->core;
548
549         linphone_core_update_allocated_audio_bandwidth(lc);
550
551         call->owns_call_log=FALSE;
552         linphone_call_log_completed(call);
553
554
555         if (call == lc->current_call){
556                 ms_message("Resetting the current call");
557                 lc->current_call=NULL;
558         }
559
560         if (linphone_core_del_call(lc,call) != 0){
561                 ms_error("Could not remove the call from the list !!!");
562         }
563
564         if (ms_list_size(lc->calls)==0)
565                 linphone_core_notify_all_friends(lc,lc->presence_mode);
566
567         linphone_core_conference_check_uninit(lc);
568         if (call->ringing_beep){
569                 linphone_core_stop_dtmf(lc);
570                 call->ringing_beep=FALSE;
571         }
572         if (call->referer){
573                 linphone_call_unref(call->referer);
574                 call->referer=NULL;
575         }
576 }
577
578 void linphone_call_fix_call_parameters(LinphoneCall *call){
579         call->params.has_video=call->current_params.has_video;
580         call->params.media_encryption=call->current_params.media_encryption;
581 }
582
583 const char *linphone_call_state_to_string(LinphoneCallState cs){
584         switch (cs){
585                 case LinphoneCallIdle:
586                         return "LinphoneCallIdle";
587                 case LinphoneCallIncomingReceived:
588                         return "LinphoneCallIncomingReceived";
589                 case LinphoneCallOutgoingInit:
590                         return "LinphoneCallOutgoingInit";
591                 case LinphoneCallOutgoingProgress:
592                         return "LinphoneCallOutgoingProgress";
593                 case LinphoneCallOutgoingRinging:
594                         return "LinphoneCallOutgoingRinging";
595                 case LinphoneCallOutgoingEarlyMedia:
596                         return "LinphoneCallOutgoingEarlyMedia";
597                 case LinphoneCallConnected:
598                         return "LinphoneCallConnected";
599                 case LinphoneCallStreamsRunning:
600                         return "LinphoneCallStreamsRunning";
601                 case LinphoneCallPausing:
602                         return "LinphoneCallPausing";
603                 case LinphoneCallPaused:
604                         return "LinphoneCallPaused";
605                 case LinphoneCallResuming:
606                         return "LinphoneCallResuming";
607                 case LinphoneCallRefered:
608                         return "LinphoneCallRefered";
609                 case LinphoneCallError:
610                         return "LinphoneCallError";
611                 case LinphoneCallEnd:
612                         return "LinphoneCallEnd";
613                 case LinphoneCallPausedByRemote:
614                         return "LinphoneCallPausedByRemote";
615                 case LinphoneCallUpdatedByRemote:
616                         return "LinphoneCallUpdatedByRemote";
617                 case LinphoneCallIncomingEarlyMedia:
618                         return "LinphoneCallIncomingEarlyMedia";
619                 case LinphoneCallUpdating:
620                         return "LinphoneCallUpdating";
621                 case LinphoneCallReleased:
622                         return "LinphoneCallReleased";
623         }
624         return "undefined state";
625 }
626
627 void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
628         LinphoneCore *lc=call->core;
629
630         if (call->state!=cstate){
631                 if (call->state==LinphoneCallEnd || call->state==LinphoneCallError){
632                         if (cstate!=LinphoneCallReleased){
633                                 ms_warning("Spurious call state change from %s to %s, ignored.",linphone_call_state_to_string(call->state),
634                                    linphone_call_state_to_string(cstate));
635                                 return;
636                         }
637                 }
638                 ms_message("Call %p: moving from state %s to %s",call,linphone_call_state_to_string(call->state),
639                            linphone_call_state_to_string(cstate));
640                 if (cstate!=LinphoneCallRefered){
641                         /*LinphoneCallRefered is rather an event, not a state.
642                          Indeed it does not change the state of the call (still paused or running)*/
643                         call->state=cstate;
644                 }
645                 if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
646                         switch(call->reason){
647                                 case LinphoneReasonDeclined:
648                                         call->log->status=LinphoneCallDeclined;
649                                         break;
650                                 case LinphoneReasonNotAnswered:
651                                         call->log->status=LinphoneCallMissed;
652                                 break;
653                                 default:
654                                 break;
655                         }
656                         linphone_call_set_terminated (call);
657                 }
658                 if (cstate == LinphoneCallConnected) {
659                         call->log->status=LinphoneCallSuccess;
660                         call->media_start_time=time(NULL);
661                 }
662
663                 if (lc->vtable.call_state_changed)
664                         lc->vtable.call_state_changed(lc,call,cstate,message);
665                 if (cstate==LinphoneCallReleased){
666                         if (call->op!=NULL) {
667                                 /* so that we cannot have anymore upcalls for SAL
668                                  concerning this call*/
669                                 sal_op_release(call->op);
670                                 call->op=NULL;
671                         }
672                         linphone_call_unref(call);
673                 }
674         }
675 }
676
677 static void linphone_call_destroy(LinphoneCall *obj)
678 {
679 #ifdef BUILD_UPNP
680         linphone_call_delete_upnp_session(obj);
681 #endif //BUILD_UPNP
682         linphone_call_delete_ice_session(obj);
683         if (obj->op!=NULL) {
684                 sal_op_release(obj->op);
685                 obj->op=NULL;
686         }
687         if (obj->resultdesc!=NULL) {
688                 sal_media_description_unref(obj->resultdesc);
689                 obj->resultdesc=NULL;
690         }
691         if (obj->localdesc!=NULL) {
692                 sal_media_description_unref(obj->localdesc);
693                 obj->localdesc=NULL;
694         }
695         if (obj->ping_op) {
696                 sal_op_release(obj->ping_op);
697         }
698         if (obj->refer_to){
699                 ms_free(obj->refer_to);
700         }
701         if (obj->owns_call_log)
702                 linphone_call_log_destroy(obj->log);
703         if (obj->auth_token) {
704                 ms_free(obj->auth_token);
705         }
706
707         ms_free(obj);
708 }
709
710 /**
711  * @addtogroup call_control
712  * @{
713 **/
714
715 /**
716  * Increments the call 's reference count.
717  * An application that wishes to retain a pointer to call object
718  * must use this function to unsure the pointer remains
719  * valid. Once the application no more needs this pointer,
720  * it must call linphone_call_unref().
721 **/
722 LinphoneCall * linphone_call_ref(LinphoneCall *obj){
723         obj->refcnt++;
724         return obj;
725 }
726
727 /**
728  * Decrements the call object reference count.
729  * See linphone_call_ref().
730 **/
731 void linphone_call_unref(LinphoneCall *obj){
732         obj->refcnt--;
733         if (obj->refcnt==0){
734                 linphone_call_destroy(obj);
735         }
736 }
737
738 /**
739  * Returns current parameters associated to the call.
740 **/
741 const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call){
742         return &call->current_params;
743 }
744
745 static bool_t is_video_active(const SalStreamDescription *sd){
746         return sd->rtp_port!=0 && sd->dir!=SalStreamInactive;
747 }
748
749 /**
750  * Returns call parameters proposed by remote.
751  * 
752  * This is useful when receiving an incoming call, to know whether the remote party
753  * supports video, encryption or whatever.
754 **/
755 const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){
756         LinphoneCallParams *cp=&call->remote_params;
757         memset(cp,0,sizeof(*cp));
758         if (call->op){
759                 SalMediaDescription *md=sal_call_get_remote_media_description(call->op);
760                 if (md){
761                         SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd;
762
763                         asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio);
764                         vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo);
765                         secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio);
766                         secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo);
767                         if (secure_vsd){
768                                 cp->has_video=is_video_active(secure_vsd);
769                                 if (secure_asd || asd==NULL)
770                                         cp->media_encryption=LinphoneMediaEncryptionSRTP;
771                         }else if (vsd){
772                                 cp->has_video=is_video_active(vsd);
773                         }
774                         if (!cp->has_video){
775                                 if (md->bandwidth>0 && md->bandwidth<=linphone_core_get_edge_bw(call->core)){
776                                         cp->low_bandwidth=TRUE;
777                                 }
778                         }
779                         return cp;
780                 }
781         }
782         return NULL;
783 }
784
785 /**
786  * Returns the remote address associated to this call
787  *
788 **/
789 const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
790         return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
791 }
792
793 /**
794  * Returns the remote address associated to this call as a string.
795  *
796  * The result string must be freed by user using ms_free().
797 **/
798 char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
799         return linphone_address_as_string(linphone_call_get_remote_address(call));
800 }
801
802 /**
803  * Retrieves the call's current state.
804 **/
805 LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
806         return call->state;
807 }
808
809 /**
810  * Returns the reason for a call termination (either error or normal termination)
811 **/
812 LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
813         return call->reason;
814 }
815
816 /**
817  * Get the user_pointer in the LinphoneCall
818  *
819  * @ingroup call_control
820  *
821  * return user_pointer an opaque user pointer that can be retrieved at any time
822 **/
823 void *linphone_call_get_user_pointer(LinphoneCall *call)
824 {
825         return call->user_pointer;
826 }
827
828 /**
829  * Set the user_pointer in the LinphoneCall
830  *
831  * @ingroup call_control
832  *
833  * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
834 **/
835 void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
836 {
837         call->user_pointer = user_pointer;
838 }
839
840 /**
841  * Returns the call log associated to this call.
842 **/
843 LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
844         return call->log;
845 }
846
847 /**
848  * Returns the refer-to uri (if the call was transfered).
849 **/
850 const char *linphone_call_get_refer_to(const LinphoneCall *call){
851         return call->refer_to;
852 }
853
854 /**
855  * Returns direction of the call (incoming or outgoing).
856 **/
857 LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
858         return call->log->dir;
859 }
860
861 /**
862  * Returns the far end's user agent description string, if available.
863 **/
864 const char *linphone_call_get_remote_user_agent(LinphoneCall *call){
865         if (call->op){
866                 return sal_op_get_remote_ua (call->op);
867         }
868         return NULL;
869 }
870
871 /**
872  * Returns true if this calls has received a transfer that has not been
873  * executed yet.
874  * Pending transfers are executed when this call is being paused or closed,
875  * locally or by remote endpoint.
876  * If the call is already paused while receiving the transfer request, the
877  * transfer immediately occurs.
878 **/
879 bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
880         return call->refer_pending;
881 }
882
883 /**
884  * Returns call's duration in seconds.
885 **/
886 int linphone_call_get_duration(const LinphoneCall *call){
887         if (call->media_start_time==0) return 0;
888         return time(NULL)-call->media_start_time;
889 }
890
891 /**
892  * Returns the call object this call is replacing, if any.
893  * Call replacement can occur during call transfers.
894  * By default, the core automatically terminates the replaced call and accept the new one.
895  * This function allows the application to know whether a new incoming call is a one that replaces another one.
896 **/
897 LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call){
898         SalOp *op=sal_call_get_replaces(call->op);
899         if (op){
900                 return (LinphoneCall*)sal_op_get_user_pointer(op);
901         }
902         return NULL;
903 }
904
905 /**
906  * Indicate whether camera input should be sent to remote end.
907 **/
908 void linphone_call_enable_camera (LinphoneCall *call, bool_t enable){
909 #ifdef VIDEO_ENABLED
910         if (call->videostream!=NULL && call->videostream->ticker!=NULL){
911                 LinphoneCore *lc=call->core;
912                 MSWebCam *nowebcam=get_nowebcam_device();
913                 if (call->camera_active!=enable && lc->video_conf.device!=nowebcam){
914                         video_stream_change_camera(call->videostream,
915                                      enable ? lc->video_conf.device : nowebcam);
916                 }
917         }
918         call->camera_active=enable;
919 #endif
920 }
921
922 /**
923  * Take a photo of currently received video and write it into a jpeg file.
924 **/
925 int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file){
926 #ifdef VIDEO_ENABLED
927         if (call->videostream!=NULL && call->videostream->jpegwriter!=NULL){
928                 return ms_filter_call_method(call->videostream->jpegwriter,MS_JPEG_WRITER_TAKE_SNAPSHOT,(void*)file);
929         }
930         ms_warning("Cannot take snapshot: no currently running video stream on this call.");
931         return -1;
932 #endif
933         return -1;
934 }
935
936 /**
937  * Returns TRUE if camera pictures are sent to the remote party.
938 **/
939 bool_t linphone_call_camera_enabled (const LinphoneCall *call){
940         return call->camera_active;
941 }
942
943 /**
944  * Enable video stream.
945 **/
946 void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled){
947         cp->has_video=enabled;
948 }
949
950 const PayloadType* linphone_call_params_get_used_audio_codec(const LinphoneCallParams *cp) {
951         return cp->audio_codec;
952 }
953
954 const PayloadType* linphone_call_params_get_used_video_codec(const LinphoneCallParams *cp) {
955         return cp->video_codec;
956 }
957
958 /**
959  * @ingroup call_control
960  * Use to know if this call has been configured in low bandwidth mode.
961  * This mode can be automatically discovered thanks to a stun server when activate_edge_workarounds=1 in section [net] of configuration file.
962  * An application that would have reliable way to know network capacity may not use activate_edge_workarounds=1 but instead manually configure
963  * low bandwidth mode with linphone_call_params_enable_low_bandwidth().
964  * <br> When enabled, this param may transform a call request with video in audio only mode.
965  * @return TRUE if low bandwidth has been configured/detected
966  */
967 bool_t linphone_call_params_low_bandwidth_enabled(const LinphoneCallParams *cp) {
968         return cp->low_bandwidth;
969 }
970
971 /**
972  * @ingroup call_control
973  * Indicate low bandwith mode. 
974  * 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
975  * 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
976  * that it is achievable by the codec selected after SDP handshake. Video is automatically disabled.
977  * 
978 **/
979 void linphone_call_params_enable_low_bandwidth(LinphoneCallParams *cp, bool_t enabled){
980         cp->low_bandwidth=enabled;
981 }
982
983 /**
984  * Returns whether video is enabled.
985 **/
986 bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){
987         return cp->has_video;
988 }
989
990 enum LinphoneMediaEncryption linphone_call_params_get_media_encryption(const LinphoneCallParams *cp) {
991         return cp->media_encryption;
992 }
993
994 void linphone_call_params_set_media_encryption(LinphoneCallParams *cp, enum LinphoneMediaEncryption e) {
995         cp->media_encryption = e;
996 }
997
998
999 /**
1000  * Enable sending of real early media (during outgoing calls).
1001 **/
1002 void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){
1003         cp->real_early_media=enabled;
1004 }
1005
1006 bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){
1007         return cp->real_early_media;
1008 }
1009
1010 /**
1011  * Returns true if the call is part of the locally managed conference.
1012 **/
1013 bool_t linphone_call_params_local_conference_mode(const LinphoneCallParams *cp){
1014         return cp->in_conference;
1015 }
1016
1017 /**
1018  * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams.
1019  * As a consequence, codecs whose bitrates are not compatible with this limit won't be used.
1020 **/
1021 void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){
1022         cp->audio_bw=bandwidth;
1023 }
1024
1025 #ifdef VIDEO_ENABLED
1026 /**
1027  * Request remote side to send us a Video Fast Update.
1028 **/
1029 void linphone_call_send_vfu_request(LinphoneCall *call)
1030 {
1031         if (LinphoneCallStreamsRunning == linphone_call_get_state(call))
1032                 sal_call_send_vfu_request(call->op);
1033 }
1034 #endif
1035
1036 /**
1037  *
1038 **/
1039 LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp){
1040         LinphoneCallParams *ncp=ms_new0(LinphoneCallParams,1);
1041         memcpy(ncp,cp,sizeof(LinphoneCallParams));
1042         return ncp;
1043 }
1044
1045 /**
1046  *
1047 **/
1048 void linphone_call_params_destroy(LinphoneCallParams *p){
1049         ms_free(p);
1050 }
1051
1052 /**
1053  * @}
1054 **/
1055
1056
1057 #ifdef TEST_EXT_RENDERER
1058 static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
1059         ms_message("rendercb, local buffer=%p, remote buffer=%p",
1060                    local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
1061 }
1062 #endif
1063
1064 #ifdef VIDEO_ENABLED
1065 static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const unsigned int event_id, const void *args){
1066     LinphoneCall* call = (LinphoneCall*) user_pointer;
1067         ms_warning("In linphonecall.c: video_stream_event_cb");
1068         switch (event_id) {
1069                 case MS_VIDEO_DECODER_DECODING_ERRORS:
1070                         ms_warning("Case is MS_VIDEO_DECODER_DECODING_ERRORS");
1071                         linphone_call_send_vfu_request(call);
1072                         break;
1073                 case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
1074                         ms_message("First video frame decoded successfully");
1075                         if (call->nextVideoFrameDecoded._func != NULL)
1076                         call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
1077                         break;
1078                 default:
1079                         ms_warning("Unhandled event %i", event_id);
1080                         break;
1081         }
1082 }
1083 #endif
1084
1085 void linphone_call_set_next_video_frame_decoded_callback(LinphoneCall *call, LinphoneCallCbFunc cb, void* user_data) {
1086         call->nextVideoFrameDecoded._func = cb;
1087         call->nextVideoFrameDecoded._user_data = user_data;
1088 #ifdef VIDEO_ENABLED
1089         ms_filter_call_method_noarg(call->videostream->decoder, MS_VIDEO_DECODER_RESET_FIRST_IMAGE_NOTIFICATION);
1090 #endif
1091 }
1092
1093 void linphone_call_init_audio_stream(LinphoneCall *call){
1094         LinphoneCore *lc=call->core;
1095         AudioStream *audiostream;
1096         int dscp;
1097
1098         if (call->audiostream != NULL) return;
1099         call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc));
1100         dscp=linphone_core_get_audio_dscp(lc);
1101         if (dscp!=-1)
1102                 audio_stream_set_dscp(audiostream,dscp);
1103         if (linphone_core_echo_limiter_enabled(lc)){
1104                 const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
1105                 if (strcasecmp(type,"mic")==0)
1106                         audio_stream_enable_echo_limiter(audiostream,ELControlMic);
1107                 else if (strcasecmp(type,"full")==0)
1108                         audio_stream_enable_echo_limiter(audiostream,ELControlFull);
1109         }
1110         audio_stream_enable_gain_control(audiostream,TRUE);
1111         if (linphone_core_echo_cancellation_enabled(lc)){
1112                 int len,delay,framesize;
1113                 const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
1114                 len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
1115                 delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
1116                 framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
1117                 audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
1118                 if (statestr && audiostream->ec){
1119                         ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
1120                 }
1121         }
1122         audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
1123         {
1124                 int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
1125                 audio_stream_enable_noise_gate(audiostream,enabled);
1126         }
1127
1128         audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc));
1129
1130         if (lc->rtptf){
1131                 RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->audio_port);
1132                 RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->audio_port+1);
1133                 rtp_session_set_transports(audiostream->session,artp,artcp);
1134         }
1135         if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1136                 rtp_session_set_pktinfo(audiostream->session, TRUE);
1137                 rtp_session_set_symmetric_rtp(audiostream->session, FALSE);
1138                 if (ice_session_check_list(call->ice_session, 0) == NULL) {
1139                         ice_session_add_check_list(call->ice_session, ice_check_list_new());
1140                 }
1141                 audiostream->ice_check_list = ice_session_check_list(call->ice_session, 0);
1142                 ice_check_list_set_rtp_session(audiostream->ice_check_list, audiostream->session);
1143         }
1144
1145         call->audiostream_app_evq = ortp_ev_queue_new();
1146         rtp_session_register_event_queue(audiostream->session,call->audiostream_app_evq);
1147 }
1148
1149 void linphone_call_init_video_stream(LinphoneCall *call){
1150 #ifdef VIDEO_ENABLED
1151         LinphoneCore *lc=call->core;
1152
1153         if (!call->params.has_video) {
1154                 linphone_call_stop_video_stream(call);
1155                 return;
1156         }
1157         if (call->videostream != NULL) return;
1158         if ((lc->video_conf.display || lc->video_conf.capture) && call->params.has_video){
1159                 int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0);
1160                 int dscp=linphone_core_get_video_dscp(lc);
1161                 
1162                 call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc));
1163                 if (dscp!=-1)
1164                         video_stream_set_dscp(call->videostream,dscp);
1165                 video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0));
1166                 if (video_recv_buf_size>0) rtp_session_set_recv_buf_size(call->videostream->session,video_recv_buf_size);
1167
1168                 if( lc->video_conf.displaytype != NULL)
1169                         video_stream_set_display_filter_name(call->videostream,lc->video_conf.displaytype);
1170                 video_stream_set_event_callback(call->videostream,video_stream_event_cb, call);
1171                 if (lc->rtptf){
1172                         RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->video_port);
1173                         RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->video_port+1);
1174                         rtp_session_set_transports(call->videostream->session,vrtp,vrtcp);
1175                 }
1176                 if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)){
1177                         rtp_session_set_pktinfo(call->videostream->session, TRUE);
1178                         rtp_session_set_symmetric_rtp(call->videostream->session, FALSE);
1179                         if (ice_session_check_list(call->ice_session, 1) == NULL) {
1180                                 ice_session_add_check_list(call->ice_session, ice_check_list_new());
1181                         }
1182                         call->videostream->ice_check_list = ice_session_check_list(call->ice_session, 1);
1183                         ice_check_list_set_rtp_session(call->videostream->ice_check_list, call->videostream->session);
1184                 }
1185                 call->videostream_app_evq = ortp_ev_queue_new();
1186                 rtp_session_register_event_queue(call->videostream->session,call->videostream_app_evq);
1187 #ifdef TEST_EXT_RENDERER
1188                 video_stream_set_render_callback(call->videostream,rendercb,NULL);
1189 #endif
1190         }
1191 #else
1192         call->videostream=NULL;
1193 #endif
1194 }
1195
1196 void linphone_call_init_media_streams(LinphoneCall *call){
1197         linphone_call_init_audio_stream(call);
1198         linphone_call_init_video_stream(call);
1199 }
1200
1201
1202 static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
1203
1204 static void linphone_core_dtmf_received(LinphoneCore *lc, int dtmf){
1205         if (dtmf<0 || dtmf>15){
1206                 ms_warning("Bad dtmf value %i",dtmf);
1207                 return;
1208         }
1209         if (lc->vtable.dtmf_received != NULL)
1210                 lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
1211 }
1212
1213 static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
1214         if (st->equalizer){
1215                 MSFilter *f=st->equalizer;
1216                 int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
1217                 const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
1218                 ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
1219                 if (enabled){
1220                         if (gains){
1221                                 do{
1222                                         int bytes;
1223                                         MSEqualizerGain g;
1224                                         if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
1225                                                 ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
1226                                                 ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
1227                                                 gains+=bytes;
1228                                         }else break;
1229                                 }while(1);
1230                         }
1231                 }
1232         }
1233 }
1234
1235 void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted){
1236         float mic_gain=lc->sound_conf.soft_mic_lev;
1237         float thres = 0;
1238         float recv_gain;
1239         float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
1240         float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
1241         int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
1242
1243         if (!muted)
1244                 linphone_core_set_mic_gain_db (lc, mic_gain);
1245         else
1246                 audio_stream_set_mic_gain(st,0);
1247
1248         recv_gain = lc->sound_conf.soft_play_lev;
1249         if (recv_gain != 0) {
1250                 linphone_core_set_playback_gain_db (lc,recv_gain);
1251         }
1252         
1253         if (st->volsend){
1254                 ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
1255                 float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
1256                 thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
1257                 float force=lp_config_get_float(lc->config,"sound","el_force",-1);
1258                 int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
1259                 float transmit_thres=lp_config_get_float(lc->config,"sound","el_transmit_thres",-1);
1260                 MSFilter *f=NULL;
1261                 f=st->volsend;
1262                 if (speed==-1) speed=0.03;
1263                 if (force==-1) force=25;
1264                 ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
1265                 ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
1266                 if (thres!=-1)
1267                         ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
1268                 if (sustain!=-1)
1269                         ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
1270                 if (transmit_thres!=-1)
1271                                 ms_filter_call_method(f,MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD,&transmit_thres);
1272
1273                 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1274                 ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
1275         }
1276         if (st->volrecv){
1277                 /* parameters for a limited noise-gate effect, using echo limiter threshold */
1278                 float floorgain = 1/pow(10,(mic_gain)/10);
1279                 int spk_agc=lp_config_get_int(lc->config,"sound","speaker_agc_enabled",0);
1280                 ms_filter_call_method(st->volrecv, MS_VOLUME_ENABLE_AGC, &spk_agc);
1281                 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
1282                 ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
1283         }
1284         parametrize_equalizer(lc,st);
1285 }
1286
1287 static void post_configure_audio_streams(LinphoneCall*call){
1288         AudioStream *st=call->audiostream;
1289         LinphoneCore *lc=call->core;
1290         _post_configure_audio_stream(st,lc,call->audio_muted);
1291         if (lc->vtable.dtmf_received!=NULL){
1292                 /* replace by our default action*/
1293                 audio_stream_play_received_dtmfs(call->audiostream,FALSE);
1294                 /*rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);*/
1295         }
1296 }
1297
1298 static RtpProfile *make_profile(LinphoneCall *call, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
1299         int bw;
1300         const MSList *elem;
1301         RtpProfile *prof=rtp_profile_new("Call profile");
1302         bool_t first=TRUE;
1303         int remote_bw=0;
1304         LinphoneCore *lc=call->core;
1305         int up_ptime=0;
1306         const LinphoneCallParams *params=&call->params;
1307         *used_pt=-1;
1308
1309         for(elem=desc->payloads;elem!=NULL;elem=elem->next){
1310                 PayloadType *pt=(PayloadType*)elem->data;
1311                 int number;
1312
1313                 if ((pt->flags & PAYLOAD_TYPE_FLAG_CAN_SEND) && first) {
1314                         if (desc->type==SalAudio){
1315                                 linphone_core_update_allocated_audio_bandwidth_in_call(call,pt);
1316                                 if (params->up_ptime)
1317                                         up_ptime=params->up_ptime;
1318                                 else up_ptime=linphone_core_get_upload_ptime(lc);
1319                         }
1320                         *used_pt=payload_type_get_number(pt);
1321                         first=FALSE;
1322                 }
1323                 if (desc->bandwidth>0) remote_bw=desc->bandwidth;
1324                 else if (md->bandwidth>0) {
1325                         /*case where b=AS is given globally, not per stream*/
1326                         remote_bw=md->bandwidth;
1327                         if (desc->type==SalVideo){
1328                                 remote_bw=get_video_bandwidth(remote_bw,call->audio_bw);
1329                         }
1330                 }
1331
1332                 if (desc->type==SalAudio){
1333                         int audio_bw=call->audio_bw;
1334                         if (params->up_bw){
1335                                 if (params->up_bw< audio_bw)
1336                                         audio_bw=params->up_bw;
1337                         }
1338                         bw=get_min_bandwidth(audio_bw,remote_bw);
1339                 }else bw=get_min_bandwidth(get_video_bandwidth(linphone_core_get_upload_bandwidth (lc),call->audio_bw),remote_bw);
1340                 if (bw>0) pt->normal_bitrate=bw*1000;
1341                 else if (desc->type==SalAudio){
1342                         pt->normal_bitrate=-1;
1343                 }
1344                 if (desc->ptime>0){
1345                         up_ptime=desc->ptime;
1346                 }
1347                 if (up_ptime>0){
1348                         char tmp[40];
1349                         snprintf(tmp,sizeof(tmp),"ptime=%i",up_ptime);
1350                         payload_type_append_send_fmtp(pt,tmp);
1351                 }
1352                 number=payload_type_get_number(pt);
1353                 if (rtp_profile_get_payload(prof,number)!=NULL){
1354                         ms_warning("A payload type with number %i already exists in profile !",number);
1355                 }else
1356                         rtp_profile_set_payload(prof,number,pt);
1357         }
1358         return prof;
1359 }
1360
1361
1362 static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){
1363         int pause_time=3000;
1364         audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone);
1365         ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1366 }
1367
1368 static bool_t linphone_call_sound_resources_available(LinphoneCall *call){
1369         LinphoneCore *lc=call->core;
1370         LinphoneCall *current=linphone_core_get_current_call(lc);
1371         return !linphone_core_is_in_conference(lc) && 
1372                 (current==NULL || current==call);
1373 }
1374 static int find_crypto_index_from_tag(const SalSrtpCryptoAlgo crypto[],unsigned char tag) {
1375     int i;
1376     for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
1377         if (crypto[i].tag == tag) {
1378             return i;
1379         }
1380     }
1381     return -1;
1382 }
1383 static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cname, bool_t muted, bool_t send_ringbacktone, bool_t use_arc){
1384         LinphoneCore *lc=call->core;
1385         int used_pt=-1;
1386         char rtcp_tool[128]={0};
1387         snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1388         /* look for savp stream first */
1389         const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
1390                                                 SalProtoRtpSavp,SalAudio);
1391         /* no savp audio stream, use avp */
1392         if (!stream)
1393                 stream=sal_media_description_find_stream(call->resultdesc,
1394                                                 SalProtoRtpAvp,SalAudio);
1395
1396         if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){
1397                 MSSndCard *playcard=lc->sound_conf.lsd_card ?
1398                         lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
1399                 MSSndCard *captcard=lc->sound_conf.capt_sndcard;
1400                 const char *playfile=lc->play_file;
1401                 const char *recfile=lc->rec_file;
1402                 call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt);
1403                 bool_t use_ec;
1404
1405                 if (used_pt!=-1){
1406                         call->current_params.audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt);
1407                         if (playcard==NULL) {
1408                                 ms_warning("No card defined for playback !");
1409                         }
1410                         if (captcard==NULL) {
1411                                 ms_warning("No card defined for capture !");
1412                         }
1413                         /*Replace soundcard filters by inactive file players or recorders
1414                          when placed in recvonly or sendonly mode*/
1415                         if (stream->rtp_port==0 || stream->dir==SalStreamRecvOnly){
1416                                 captcard=NULL;
1417                                 playfile=NULL;
1418                         }else if (stream->dir==SalStreamSendOnly){
1419                                 playcard=NULL;
1420                                 captcard=NULL;
1421                                 recfile=NULL;
1422                                 /*And we will eventually play "playfile" if set by the user*/
1423                                 /*playfile=NULL;*/
1424                         }
1425                         if (send_ringbacktone){
1426                                 captcard=NULL;
1427                                 playfile=NULL;/* it is setup later*/
1428                         }
1429                         /*if playfile are supplied don't use soundcards*/
1430                         if (lc->use_files) {
1431                                 captcard=NULL;
1432                                 playcard=NULL;
1433                         }
1434                         if (call->params.in_conference){
1435                                 /* first create the graph without soundcard resources*/
1436                                 captcard=playcard=NULL;
1437                         }
1438                         if (!linphone_call_sound_resources_available(call)){
1439                                 ms_message("Sound resources are used by another call, not using soundcard.");
1440                                 captcard=playcard=NULL;
1441                         }
1442                         use_ec=captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc);
1443                         if (playcard &&  stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(playcard, stream->max_rate);
1444                         if (captcard &&  stream->max_rate>0) ms_snd_card_set_preferred_sample_rate(captcard, stream->max_rate);
1445                         audio_stream_enable_adaptive_bitrate_control(call->audiostream,use_arc);
1446                         audio_stream_enable_adaptive_jittcomp(call->audiostream, linphone_core_audio_adaptive_jittcomp_enabled(lc));
1447                         audio_stream_start_full(
1448                                 call->audiostream,
1449                                 call->audio_profile,
1450                                 stream->rtp_addr[0]!='\0' ? stream->rtp_addr : call->resultdesc->addr,
1451                                 stream->rtp_port,
1452                                 stream->rtcp_addr[0]!='\0' ? stream->rtcp_addr : call->resultdesc->addr,
1453                                 linphone_core_rtcp_enabled(lc) ? (stream->rtcp_port) : 0,
1454                                 used_pt,
1455                                 linphone_core_get_audio_jittcomp(lc),
1456                                 playfile,
1457                                 recfile,
1458                                 playcard,
1459                                 captcard,
1460                                 use_ec
1461                                 );
1462                         post_configure_audio_streams(call);
1463                         if (muted && !send_ringbacktone){
1464                                 audio_stream_set_mic_gain(call->audiostream,0);
1465                         }
1466                         if (stream->dir==SalStreamSendOnly && playfile!=NULL){
1467                                 int pause_time=500;
1468                                 ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time);
1469                         }
1470                         if (send_ringbacktone){
1471                                 setup_ring_player(lc,call);
1472                         }
1473                         audio_stream_set_rtcp_information(call->audiostream, cname, rtcp_tool);
1474                         
1475             /* valid local tags are > 0 */
1476                         if (stream->proto == SalProtoRtpSavp) {
1477                 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1478                                                                                             SalProtoRtpSavp,SalAudio);
1479                 int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag);
1480                 
1481                 if (crypto_idx >= 0) {
1482                     audio_stream_enable_strp(
1483                                              call->audiostream, 
1484                                              stream->crypto[0].algo,
1485                                              local_st_desc->crypto[crypto_idx].master_key,
1486                                              stream->crypto[0].master_key);
1487                     call->audiostream_encrypted=TRUE;
1488                 } else {
1489                     ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag);
1490                     call->audiostream_encrypted=FALSE;
1491                 }
1492                         }else call->audiostream_encrypted=FALSE;
1493                         if (call->params.in_conference){
1494                                 /*transform the graph to connect it to the conference filter */
1495                                 bool_t mute=stream->dir==SalStreamRecvOnly;
1496                                 linphone_call_add_to_conf(call, mute);
1497                         }
1498                         call->current_params.in_conference=call->params.in_conference;
1499                         call->current_params.low_bandwidth=call->params.low_bandwidth;
1500                 }else ms_warning("No audio stream accepted ?");
1501         }
1502 }
1503
1504 static void linphone_call_start_video_stream(LinphoneCall *call, const char *cname,bool_t all_inputs_muted){
1505 #ifdef VIDEO_ENABLED
1506         LinphoneCore *lc=call->core;
1507         int used_pt=-1;
1508         /* look for savp stream first */
1509         const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1510                                                 SalProtoRtpSavp,SalVideo);
1511         char rtcp_tool[128]={0};
1512         snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version());
1513         
1514         /* no savp audio stream, use avp */
1515         if (!vstream)
1516                 vstream=sal_media_description_find_stream(call->resultdesc,
1517                                                 SalProtoRtpAvp,SalVideo);
1518                                                 
1519         /* shutdown preview */
1520         if (lc->previewstream!=NULL) {
1521                 video_preview_stop(lc->previewstream);
1522                 lc->previewstream=NULL;
1523         }
1524         
1525         if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->rtp_port!=0) {
1526                 const char *rtp_addr=vstream->rtp_addr[0]!='\0' ? vstream->rtp_addr : call->resultdesc->addr;
1527                 const char *rtcp_addr=vstream->rtcp_addr[0]!='\0' ? vstream->rtcp_addr : call->resultdesc->addr;
1528                 call->video_profile=make_profile(call,call->resultdesc,vstream,&used_pt);
1529                 if (used_pt!=-1){
1530                         call->current_params.video_codec = rtp_profile_get_payload(call->video_profile, used_pt);
1531                         VideoStreamDir dir=VideoStreamSendRecv;
1532                         MSWebCam *cam=lc->video_conf.device;
1533                         bool_t is_inactive=FALSE;
1534
1535                         call->current_params.has_video=TRUE;
1536
1537                         video_stream_enable_adaptive_bitrate_control(call->videostream,
1538                                                                   linphone_core_adaptive_rate_control_enabled(lc));
1539                         video_stream_enable_adaptive_jittcomp(call->videostream, linphone_core_video_adaptive_jittcomp_enabled(lc));
1540                         video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
1541                         video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
1542                         if (lc->video_window_id!=0)
1543                                 video_stream_set_native_window_id(call->videostream,lc->video_window_id);
1544                         if (lc->preview_window_id!=0)
1545                                 video_stream_set_native_preview_window_id (call->videostream,lc->preview_window_id);
1546                         video_stream_use_preview_video_window (call->videostream,lc->use_preview_window);
1547                         
1548                         if (vstream->dir==SalStreamSendOnly && lc->video_conf.capture ){
1549                                 cam=get_nowebcam_device();
1550                                 dir=VideoStreamSendOnly;
1551                         }else if (vstream->dir==SalStreamRecvOnly && lc->video_conf.display ){
1552                                 dir=VideoStreamRecvOnly;
1553                         }else if (vstream->dir==SalStreamSendRecv){
1554                                 if (lc->video_conf.display && lc->video_conf.capture)
1555                                         dir=VideoStreamSendRecv;
1556                                 else if (lc->video_conf.display)
1557                                         dir=VideoStreamRecvOnly;
1558                                 else
1559                                         dir=VideoStreamSendOnly;
1560                         }else{
1561                                 ms_warning("video stream is inactive.");
1562                                 /*either inactive or incompatible with local capabilities*/
1563                                 is_inactive=TRUE;
1564                         }
1565                         if (call->camera_active==FALSE || all_inputs_muted){
1566                                 cam=get_nowebcam_device();
1567                         }
1568                         if (!is_inactive){
1569                                 call->log->video_enabled = TRUE;
1570                                 video_stream_set_direction (call->videostream, dir);
1571                                 ms_message("%s lc rotation:%d\n", __FUNCTION__, lc->device_rotation);
1572                                 video_stream_set_device_rotation(call->videostream, lc->device_rotation);
1573                                 video_stream_start(call->videostream,
1574                                         call->video_profile, rtp_addr, vstream->rtp_port,
1575                                         rtcp_addr, linphone_core_rtcp_enabled(lc) ? (vstream->rtcp_port) : 0,
1576                                         used_pt, linphone_core_get_video_jittcomp(lc), cam);
1577                                 video_stream_set_rtcp_information(call->videostream, cname,rtcp_tool);
1578                         }
1579                         
1580                         if (vstream->proto == SalProtoRtpSavp) {
1581                                 const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
1582                                                 SalProtoRtpSavp,SalVideo);
1583                                                 
1584                                 video_stream_enable_strp(
1585                                         call->videostream, 
1586                                         vstream->crypto[0].algo,
1587                                         local_st_desc->crypto[0].master_key, 
1588                                         vstream->crypto[0].master_key
1589                                         );
1590                                 call->videostream_encrypted=TRUE;
1591                         }else{
1592                                 call->videostream_encrypted=FALSE;
1593                         }
1594                 }else ms_warning("No video stream accepted.");
1595         }else{
1596                 ms_warning("No valid video stream defined.");
1597         }
1598 #endif
1599 }
1600
1601 void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){
1602         LinphoneCore *lc=call->core;
1603
1604         call->current_params.audio_codec = NULL;
1605         call->current_params.video_codec = NULL;
1606
1607         LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
1608         char *cname;
1609         bool_t use_arc=linphone_core_adaptive_rate_control_enabled(lc);
1610 #ifdef VIDEO_ENABLED
1611         const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
1612                                                         SalProtoRtpAvp,SalVideo);
1613 #endif
1614
1615         if ((call->audiostream == NULL) && (call->videostream == NULL)) {
1616                 ms_fatal("start_media_stream() called without prior init !");
1617                 return;
1618         }
1619         cname=linphone_address_as_string_uri_only(me);
1620
1621 #if defined(VIDEO_ENABLED)
1622         if (vstream!=NULL && vstream->dir!=SalStreamInactive && vstream->payloads!=NULL){
1623                 /*when video is used, do not make adaptive rate control on audio, it is stupid.*/
1624                 use_arc=FALSE;
1625         }
1626 #endif
1627         if (call->audiostream!=NULL) {
1628                 linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc);
1629         }
1630         call->current_params.has_video=FALSE;
1631         if (call->videostream!=NULL) {
1632                 linphone_call_start_video_stream(call,cname,all_inputs_muted);
1633         }
1634
1635         call->all_muted=all_inputs_muted;
1636         call->playing_ringbacktone=send_ringbacktone;
1637         call->up_bw=linphone_core_get_upload_bandwidth(lc);
1638
1639         if (call->params.media_encryption==LinphoneMediaEncryptionZRTP) {
1640                 OrtpZrtpParams params;
1641                 /*will be set later when zrtp is activated*/
1642                 call->current_params.media_encryption=LinphoneMediaEncryptionNone;
1643                 
1644                 params.zid_file=lc->zrtp_secrets_cache;
1645                 audio_stream_enable_zrtp(call->audiostream,&params);
1646         }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){
1647                 call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ?
1648                         LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone;
1649         }
1650
1651         /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again
1652          * further in the call, for example during pause,resume, conferencing reINVITEs*/
1653         linphone_call_fix_call_parameters(call);
1654         if ((call->ice_session != NULL) && (ice_session_state(call->ice_session) != IS_Completed)) {
1655                 ice_session_start_connectivity_checks(call->ice_session);
1656         }
1657
1658         goto end;
1659         end:
1660                 ms_free(cname);
1661                 linphone_address_destroy(me);
1662 }
1663
1664 void linphone_call_start_media_streams_for_ice_gathering(LinphoneCall *call){
1665         audio_stream_prepare_sound(call->audiostream, NULL, NULL);
1666 #ifdef VIDEO_ENABLED
1667         if (call->videostream) {
1668                 video_stream_prepare_video(call->videostream);
1669         }
1670 #endif
1671 }
1672
1673 void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call){
1674         audio_stream_unprepare_sound(call->audiostream);
1675 #ifdef VIDEO_ENABLED
1676         if (call->videostream) {
1677                 video_stream_unprepare_video(call->videostream);
1678         }
1679 #endif
1680 }
1681
1682 void linphone_call_delete_ice_session(LinphoneCall *call){
1683         if (call->ice_session != NULL) {
1684                 ice_session_destroy(call->ice_session);
1685                 call->ice_session = NULL;
1686                 if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL;
1687                 if (call->videostream != NULL) call->videostream->ice_check_list = NULL;
1688                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
1689                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
1690         }
1691 }
1692
1693 #ifdef BUILD_UPNP
1694 void linphone_call_delete_upnp_session(LinphoneCall *call){
1695         if(call->upnp_session!=NULL) {
1696                 upnp_session_destroy(call->upnp_session);
1697                 call->upnp_session=NULL;
1698         }
1699 }
1700 #endif //BUILD_UPNP
1701
1702 static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
1703         audio_stream_get_local_rtp_stats (st,&log->local_stats);
1704         log->quality=audio_stream_get_average_quality_rating(st);
1705 }
1706
1707 void linphone_call_stop_audio_stream(LinphoneCall *call) {
1708         if (call->audiostream!=NULL) {
1709                 rtp_session_unregister_event_queue(call->audiostream->session,call->audiostream_app_evq);
1710                 ortp_ev_queue_flush(call->audiostream_app_evq);
1711                 ortp_ev_queue_destroy(call->audiostream_app_evq);
1712                 call->audiostream_app_evq=NULL;
1713
1714                 if (call->audiostream->ec){
1715                         const char *state_str=NULL;
1716                         ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_STATE_STRING,&state_str);
1717                         if (state_str){
1718                                 ms_message("Writing echo canceler state, %i bytes",(int)strlen(state_str));
1719                                 lp_config_set_string(call->core->config,"sound","ec_state",state_str);
1720                         }
1721                 }
1722                 linphone_call_log_fill_stats (call->log,call->audiostream);
1723                 if (call->endpoint){
1724                         linphone_call_remove_from_conf(call);
1725                 }
1726                 audio_stream_stop(call->audiostream);
1727                 call->audiostream=NULL;
1728         }
1729 }
1730
1731 void linphone_call_stop_video_stream(LinphoneCall *call) {
1732 #ifdef VIDEO_ENABLED
1733         if (call->videostream!=NULL){
1734                 rtp_session_unregister_event_queue(call->videostream->session,call->videostream_app_evq);
1735                 ortp_ev_queue_flush(call->videostream_app_evq);
1736                 ortp_ev_queue_destroy(call->videostream_app_evq);
1737                 call->videostream_app_evq=NULL;
1738                 video_stream_stop(call->videostream);
1739                 call->videostream=NULL;
1740         }
1741 #endif
1742 }
1743
1744 void linphone_call_stop_media_streams(LinphoneCall *call){
1745         linphone_call_stop_audio_stream(call);
1746         linphone_call_stop_video_stream(call);
1747         ms_event_queue_skip(call->core->msevq);
1748         
1749         if (call->audio_profile){
1750                 rtp_profile_clear_all(call->audio_profile);
1751                 rtp_profile_destroy(call->audio_profile);
1752                 call->audio_profile=NULL;
1753         }
1754         if (call->video_profile){
1755                 rtp_profile_clear_all(call->video_profile);
1756                 rtp_profile_destroy(call->video_profile);
1757                 call->video_profile=NULL;
1758         }
1759 }
1760
1761
1762
1763 void linphone_call_enable_echo_cancellation(LinphoneCall *call, bool_t enable) {
1764         if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1765                 bool_t bypass_mode = !enable;
1766                 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_SET_BYPASS_MODE,&bypass_mode);
1767         }
1768 }
1769 bool_t linphone_call_echo_cancellation_enabled(LinphoneCall *call) {
1770         if (call!=NULL && call->audiostream!=NULL && call->audiostream->ec){
1771                 bool_t val;
1772                 ms_filter_call_method(call->audiostream->ec,MS_ECHO_CANCELLER_GET_BYPASS_MODE,&val);
1773                 return !val;
1774         } else {
1775                 return linphone_core_echo_cancellation_enabled(call->core);
1776         }
1777 }
1778
1779 void linphone_call_enable_echo_limiter(LinphoneCall *call, bool_t val){
1780         if (call!=NULL && call->audiostream!=NULL ) {
1781                 if (val) {
1782                 const char *type=lp_config_get_string(call->core->config,"sound","el_type","mic");
1783                 if (strcasecmp(type,"mic")==0)
1784                         audio_stream_enable_echo_limiter(call->audiostream,ELControlMic);
1785                 else if (strcasecmp(type,"full")==0)
1786                         audio_stream_enable_echo_limiter(call->audiostream,ELControlFull);
1787                 } else {
1788                         audio_stream_enable_echo_limiter(call->audiostream,ELInactive);
1789                 }
1790         }
1791 }
1792
1793 bool_t linphone_call_echo_limiter_enabled(const LinphoneCall *call){
1794         if (call!=NULL && call->audiostream!=NULL ){
1795                 return call->audiostream->el_type !=ELInactive ;
1796         } else {
1797                 return linphone_core_echo_limiter_enabled(call->core);
1798         }
1799 }
1800
1801 /**
1802  * @addtogroup call_misc
1803  * @{
1804 **/
1805
1806 /**
1807  * Returns the measured sound volume played locally (received from remote).
1808  * It is expressed in dbm0.
1809 **/
1810 float linphone_call_get_play_volume(LinphoneCall *call){
1811         AudioStream *st=call->audiostream;
1812         if (st && st->volrecv){
1813                 float vol=0;
1814                 ms_filter_call_method(st->volrecv,MS_VOLUME_GET,&vol);
1815                 return vol;
1816
1817         }
1818         return LINPHONE_VOLUME_DB_LOWEST;
1819 }
1820
1821 /**
1822  * Returns the measured sound volume recorded locally (sent to remote).
1823  * It is expressed in dbm0.
1824 **/
1825 float linphone_call_get_record_volume(LinphoneCall *call){
1826         AudioStream *st=call->audiostream;
1827         if (st && st->volsend && !call->audio_muted && call->state==LinphoneCallStreamsRunning){
1828                 float vol=0;
1829                 ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
1830                 return vol;
1831
1832         }
1833         return LINPHONE_VOLUME_DB_LOWEST;
1834 }
1835
1836 /**
1837  * Obtain real-time quality rating of the call
1838  *
1839  * Based on local RTP statistics and RTCP feedback, a quality rating is computed and updated
1840  * during all the duration of the call. This function returns its value at the time of the function call.
1841  * It is expected that the rating is updated at least every 5 seconds or so.
1842  * The rating is a floating point number comprised between 0 and 5.
1843  *
1844  * 4-5 = good quality <br>
1845  * 3-4 = average quality <br>
1846  * 2-3 = poor quality <br>
1847  * 1-2 = very poor quality <br>
1848  * 0-1 = can't be worse, mostly unusable <br>
1849  *
1850  * @returns The function returns -1 if no quality measurement is available, for example if no
1851  * active audio stream exist. Otherwise it returns the quality rating.
1852 **/
1853 float linphone_call_get_current_quality(LinphoneCall *call){
1854         if (call->audiostream){
1855                 return audio_stream_get_quality_rating(call->audiostream);
1856         }
1857         return -1;
1858 }
1859
1860 /**
1861  * Returns call quality averaged over all the duration of the call.
1862  *
1863  * See linphone_call_get_current_quality() for more details about quality measurement.
1864 **/
1865 float linphone_call_get_average_quality(LinphoneCall *call){
1866         if (call->audiostream){
1867                 return audio_stream_get_average_quality_rating(call->audiostream);
1868         }
1869         return -1;
1870 }
1871
1872 /**
1873  * Access last known statistics for audio stream, for a given call.
1874 **/
1875 const LinphoneCallStats *linphone_call_get_audio_stats(const LinphoneCall *call) {
1876         return &call->stats[LINPHONE_CALL_STATS_AUDIO];
1877 }
1878
1879 /**
1880  * Access last known statistics for video stream, for a given call.
1881 **/
1882 const LinphoneCallStats *linphone_call_get_video_stats(const LinphoneCall *call) {
1883         return &call->stats[LINPHONE_CALL_STATS_VIDEO];
1884 }
1885
1886
1887 /**
1888  * @}
1889 **/
1890
1891 static void report_bandwidth(LinphoneCall *call, RtpSession *as, RtpSession *vs){
1892         call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (rtp_session_compute_recv_bandwidth(as)*1e-3) : 0;
1893         call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (rtp_session_compute_send_bandwidth(as)*1e-3) : 0;
1894         call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (rtp_session_compute_recv_bandwidth(vs)*1e-3) : 0;
1895         call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0;
1896         ms_message("bandwidth usage: audio=[d=%.1f,u=%.1f] video=[d=%.1f,u=%.1f] kbit/sec",
1897                 call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth,
1898                 call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth ,
1899                 call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth,
1900                 call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth
1901         );
1902 }
1903
1904 static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
1905         char temp[256];
1906         char *from=NULL;
1907         if(call)
1908                 from = linphone_call_get_remote_address_as_string(call);
1909         if (from)
1910         {
1911                 snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from);
1912                 free(from);
1913         }
1914         else
1915         {
1916                 snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed.");
1917         }
1918         if (lc->vtable.display_warning!=NULL)
1919                 lc->vtable.display_warning(lc,temp);
1920         linphone_core_terminate_call(lc,call);
1921 }
1922
1923 static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
1924         OrtpEventType evt=ortp_event_get_type(ev);
1925         OrtpEventData *evd=ortp_event_get_data(ev);
1926         int ping_time;
1927
1928         if (evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) {
1929                 switch (ice_session_state(call->ice_session)) {
1930                         case IS_Completed:
1931                                 ice_session_select_candidates(call->ice_session);
1932                                 if (ice_session_role(call->ice_session) == IR_Controlling) {
1933                                         linphone_core_update_call(call->core, call, &call->current_params);
1934                                 }
1935                                 break;
1936                         case IS_Failed:
1937                                 if (ice_session_has_completed_check_list(call->ice_session) == TRUE) {
1938                                         ice_session_select_candidates(call->ice_session);
1939                                         if (ice_session_role(call->ice_session) == IR_Controlling) {
1940                                                 /* At least one ICE session has succeeded, so perform a call update. */
1941                                                 linphone_core_update_call(call->core, call, &call->current_params);
1942                                         }
1943                                 }
1944                                 break;
1945                         default:
1946                                 break;
1947                 }
1948                 linphone_core_update_ice_state_in_call_stats(call);
1949         } else if (evt == ORTP_EVENT_ICE_GATHERING_FINISHED) {
1950
1951                 if (evd->info.ice_processing_successful==TRUE) {
1952                         ice_session_compute_candidates_foundations(call->ice_session);
1953                         ice_session_eliminate_redundant_candidates(call->ice_session);
1954                         ice_session_choose_default_candidates(call->ice_session);
1955                         ping_time = ice_session_average_gathering_round_trip_time(call->ice_session);
1956                         if (ping_time >=0) {
1957                                 call->ping_time=ping_time;
1958                         }
1959                 } else {
1960                         ms_warning("No STUN answer from [%s], disabling ICE",linphone_core_get_stun_server(call->core));
1961                         linphone_call_delete_ice_session(call);
1962                 }
1963                 switch (call->state) {
1964                         case LinphoneCallUpdating:
1965                                 linphone_core_start_update_call(call->core, call);
1966                                 break;
1967                         case LinphoneCallUpdatedByRemote:
1968                                 linphone_core_start_accept_call_update(call->core, call);
1969                                 break;
1970                         case LinphoneCallOutgoingInit:
1971                                 linphone_call_stop_media_streams_for_ice_gathering(call);
1972                                 linphone_core_proceed_with_invite_if_ready(call->core, call, NULL);
1973                                 break;
1974                         case LinphoneCallIdle:
1975                                 linphone_call_stop_media_streams_for_ice_gathering(call);
1976                                 linphone_core_notify_incoming_call(call->core, call);
1977                                 break;
1978                         default:
1979                                 break;
1980                 }
1981         } else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
1982                 linphone_core_start_accept_call_update(call->core, call);
1983                 linphone_core_update_ice_state_in_call_stats(call);
1984         } else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
1985                 ice_session_restart(call->ice_session);
1986                 ice_session_set_role(call->ice_session, IR_Controlling);
1987                 linphone_core_update_call(call->core, call, &call->current_params);
1988         }
1989 }
1990
1991 void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed){
1992         LinphoneCore* lc = call->core;
1993         int disconnect_timeout = linphone_core_get_nortp_timeout(call->core);
1994         bool_t disconnected=FALSE;
1995
1996         if (call->state==LinphoneCallStreamsRunning && one_second_elapsed){
1997                 RtpSession *as=NULL,*vs=NULL;
1998                 float audio_load=0, video_load=0;
1999                 if (call->audiostream!=NULL){
2000                         as=call->audiostream->session;
2001                         if (call->audiostream->ticker)
2002                                 audio_load=ms_ticker_get_average_load(call->audiostream->ticker);
2003                 }
2004                 if (call->videostream!=NULL){
2005                         if (call->videostream->ticker)
2006                                 video_load=ms_ticker_get_average_load(call->videostream->ticker);
2007                         vs=call->videostream->session;
2008                 }
2009                 report_bandwidth(call,as,vs);
2010                 ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
2011         }
2012
2013 #ifdef BUILD_UPNP
2014         upnp_call_process(call);
2015 #endif //BUILD_UPNP
2016
2017 #ifdef VIDEO_ENABLED
2018         if (call->videostream!=NULL) {
2019                 OrtpEvent *ev;
2020
2021                 /* Ensure there is no dangling ICE check list. */
2022                 if (call->ice_session == NULL) call->videostream->ice_check_list = NULL;
2023
2024                 // Beware that the application queue should not depend on treatments fron the
2025                 // mediastreamer queue.
2026                 video_stream_iterate(call->videostream);
2027
2028                 while (call->videostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->videostream_app_evq)))){
2029                         OrtpEventType evt=ortp_event_get_type(ev);
2030                         OrtpEventData *evd=ortp_event_get_data(ev);
2031                         if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2032                                 linphone_call_videostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2033                         } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2034                                 call->stats[LINPHONE_CALL_STATS_VIDEO].round_trip_delay = rtp_session_get_round_trip_propagation(call->videostream->session);
2035                                 if(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp != NULL)
2036                                         freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp);
2037                                 call->stats[LINPHONE_CALL_STATS_VIDEO].received_rtcp = evd->packet;
2038                                 evd->packet = NULL;
2039                                 if (lc->vtable.call_stats_updated)
2040                                         lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
2041                         } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2042                                 memcpy(&call->stats[LINPHONE_CALL_STATS_VIDEO].jitter_stats, rtp_session_get_jitter_stats(call->videostream->session), sizeof(jitter_stats_t));
2043                                 if(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp != NULL)
2044                                         freemsg(call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp);
2045                                 call->stats[LINPHONE_CALL_STATS_VIDEO].sent_rtcp = evd->packet;
2046                                 evd->packet = NULL;
2047                                 if (lc->vtable.call_stats_updated)
2048                                         lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_VIDEO]);
2049                         } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2050                                 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2051                                 handle_ice_events(call, ev);
2052                         }
2053                         ortp_event_destroy(ev);
2054                 }
2055         }
2056 #endif
2057         if (call->audiostream!=NULL) {
2058                 OrtpEvent *ev;
2059
2060                 /* Ensure there is no dangling ICE check list. */
2061                 if (call->ice_session == NULL) call->audiostream->ice_check_list = NULL;
2062
2063                 // Beware that the application queue should not depend on treatments fron the
2064                 // mediastreamer queue.
2065                 audio_stream_iterate(call->audiostream);
2066
2067                 while (call->audiostream_app_evq && (NULL != (ev=ortp_ev_queue_get(call->audiostream_app_evq)))){
2068                         OrtpEventType evt=ortp_event_get_type(ev);
2069                         OrtpEventData *evd=ortp_event_get_data(ev);
2070                         if (evt == ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED){
2071                                 linphone_call_audiostream_encryption_changed(call, evd->info.zrtp_stream_encrypted);
2072                         } else if (evt == ORTP_EVENT_ZRTP_SAS_READY) {
2073                                 linphone_call_audiostream_auth_token_ready(call, evd->info.zrtp_sas.sas, evd->info.zrtp_sas.verified);
2074                         } else if (evt == ORTP_EVENT_RTCP_PACKET_RECEIVED) {
2075                                 call->stats[LINPHONE_CALL_STATS_AUDIO].round_trip_delay = rtp_session_get_round_trip_propagation(call->audiostream->session);
2076                                 if(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp != NULL)
2077                                         freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp);
2078                                 call->stats[LINPHONE_CALL_STATS_AUDIO].received_rtcp = evd->packet;
2079                                 evd->packet = NULL;
2080                                 if (lc->vtable.call_stats_updated)
2081                                         lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2082                         } else if (evt == ORTP_EVENT_RTCP_PACKET_EMITTED) {
2083                                 memcpy(&call->stats[LINPHONE_CALL_STATS_AUDIO].jitter_stats, rtp_session_get_jitter_stats(call->audiostream->session), sizeof(jitter_stats_t));
2084                                 if(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp != NULL)
2085                                         freemsg(call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp);
2086                                 call->stats[LINPHONE_CALL_STATS_AUDIO].sent_rtcp = evd->packet;
2087                                 evd->packet = NULL;
2088                                 if (lc->vtable.call_stats_updated)
2089                                         lc->vtable.call_stats_updated(lc, call, &call->stats[LINPHONE_CALL_STATS_AUDIO]);
2090                         } else if ((evt == ORTP_EVENT_ICE_SESSION_PROCESSING_FINISHED) || (evt == ORTP_EVENT_ICE_GATHERING_FINISHED)
2091                                 || (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) || (evt == ORTP_EVENT_ICE_RESTART_NEEDED)) {
2092                                 handle_ice_events(call, ev);
2093                         } else if (evt==ORTP_EVENT_TELEPHONE_EVENT){
2094                                 linphone_core_dtmf_received(lc,evd->info.telephone_event);
2095                         }
2096                         ortp_event_destroy(ev);
2097                 }
2098         }
2099         if (call->state==LinphoneCallStreamsRunning && one_second_elapsed && call->audiostream!=NULL && disconnect_timeout>0 )
2100                 disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
2101         if (disconnected)
2102                 linphone_core_disconnected(call->core,call);
2103 }
2104
2105 void linphone_call_log_completed(LinphoneCall *call){
2106         LinphoneCore *lc=call->core;
2107
2108         call->log->duration=time(NULL)-call->start_time;
2109
2110         if (call->log->status==LinphoneCallMissed){
2111                 char *info;
2112                 lc->missed_calls++;
2113                 info=ortp_strdup_printf(ngettext("You have missed %i call.",
2114                                          "You have missed %i calls.", lc->missed_calls),
2115                                 lc->missed_calls);
2116         if (lc->vtable.display_status!=NULL)
2117             lc->vtable.display_status(lc,info);
2118                 ms_free(info);
2119         }
2120         lc->call_logs=ms_list_prepend(lc->call_logs,(void *)call->log);
2121         if (ms_list_size(lc->call_logs)>lc->max_call_logs){
2122                 MSList *elem,*prevelem=NULL;
2123                 /*find the last element*/
2124                 for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
2125                         prevelem=elem;
2126                 }
2127                 elem=prevelem;
2128                 linphone_call_log_destroy((LinphoneCallLog*)elem->data);
2129                 lc->call_logs=ms_list_remove_link(lc->call_logs,elem);
2130         }
2131         if (lc->vtable.call_log_updated!=NULL){
2132                 lc->vtable.call_log_updated(lc,call->log);
2133         }
2134         call_logs_write_to_config_file(lc);
2135 }
2136
2137 LinphoneCallState linphone_call_get_transfer_state(LinphoneCall *call) {
2138         return call->transfer_state;
2139 }
2140
2141 void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state) {
2142         if (state != call->transfer_state) {
2143                 LinphoneCore* lc = call->core;
2144                 call->transfer_state = state;
2145                 if (lc->vtable.transfer_state_changed)
2146                         lc->vtable.transfer_state_changed(lc, call, state);
2147         }
2148 }
2149
2150 /**
2151  * Returns true if the call is part of the conference.
2152  * @ingroup conferencing
2153 **/
2154 bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
2155         return call->params.in_conference;
2156 }
2157
2158
2159 /**
2160  * Perform a zoom of the video displayed during a call.
2161  * @param call the call.
2162  * @param zoom_factor a floating point number describing the zoom factor. A value 1.0 corresponds to no zoom applied.
2163  * @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.
2164  * @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.
2165  * 
2166  * 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.
2167 **/
2168 void linphone_call_zoom_video(LinphoneCall* call, float zoom_factor, float* cx, float* cy) {
2169         VideoStream* vstream = call->videostream;
2170         if (vstream && vstream->output) {
2171                 float zoom[3];
2172                 
2173                 if (zoom_factor < 1)
2174                         zoom_factor = 1;
2175                 float halfsize = 0.5 * 1.0 / zoom_factor;
2176
2177                 if ((*cx - halfsize) < 0)
2178                         *cx = 0 + halfsize;
2179                 if ((*cx + halfsize) > 1)
2180                         *cx = 1 - halfsize;
2181                 if ((*cy - halfsize) < 0)
2182                         *cy = 0 + halfsize;
2183                 if ((*cy + halfsize) > 1)
2184                         *cy = 1 - halfsize;
2185         
2186                 zoom[0] = zoom_factor;
2187                 zoom[1] = *cx;
2188                 zoom[2] = *cy;
2189                 ms_filter_call_method(vstream->output, MS_VIDEO_DISPLAY_ZOOM, &zoom);
2190         }else ms_warning("Could not apply zoom: video output wasn't activated.");
2191 }
2192