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