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