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