]> sjero.net Git - linphone/commitdiff
Merge branch 'dev_multicall'
authorSimon Morlat <simon.morlat@linphone.org>
Thu, 26 Aug 2010 14:01:10 +0000 (16:01 +0200)
committerSimon Morlat <simon.morlat@linphone.org>
Thu, 26 Aug 2010 14:01:10 +0000 (16:01 +0200)
and improve documentation

Conflicts:
coreapi/callbacks.c
coreapi/linphonecore.c
coreapi/linphonecore.h

1  2 
coreapi/help/Doxyfile.in
coreapi/help/Makefile.am
coreapi/help/doxygen.dox.in
coreapi/linphonecall.c
coreapi/linphonecore.c
coreapi/linphonecore.h
coreapi/sal_eXosip2.c
mediastreamer2

index abb4aef5b43d0c11e04d4a92e9fe595c5478d834,abb4aef5b43d0c11e04d4a92e9fe595c5478d834..b1ef17a4ebaef8362a7b45508ecf9374bd4cfa9e
@@@ -89,7 -89,7 +89,7 @@@ RECURSIVE              = N
  EXCLUDE                = 
  EXCLUDE_SYMLINKS       = NO
  EXCLUDE_PATTERNS       = 
--EXAMPLE_PATH           = ../
++EXAMPLE_PATH           = ../../ .
  EXAMPLE_PATTERNS       = 
  EXAMPLE_RECURSIVE      = NO
  IMAGE_PATH             = 
index 0b12592f49e59acf5af65667a8e8ec128af9e4b3,0b12592f49e59acf5af65667a8e8ec128af9e4b3..ae5e2ed251835de0698410d6eac6a5fda75a83a5
@@@ -31,3 -31,3 +31,22 @@@ endi
  
  clean-local:
        rm -rf doc
++
++noinst_PROGRAMS=helloworld
++
++helloworld_SOURCES=helloworld.c
++
++helloworld_LDADD=$(top_builddir)/coreapi/liblinphone.la
++
++INCLUDES=-I$(top_srcdir)/coreapi
++
++AM_CFLAGS=$(STRICT_OPTIONS)  -DIN_LINPHONE \
++      $(ORTP_CFLAGS) \
++      $(OSIP_CFLAGS) \
++      $(EXOSIP_CFLAGS) \
++      -DENABLE_TRACE  \
++      -DLOG_DOMAIN=\"LinphoneCore\" \
++       $(IPV6_CFLAGS) \
++       -DORTP_INET6 \
++       $(VIDEO_CFLAGS) 
++
index 13502d42424b7f601c4eea830b7ce70ff517ed8c,13502d42424b7f601c4eea830b7ce70ff517ed8c..cb05e05d71db1ce76a3567e9fb78047875aa8c4f
   */
  
  /**
-- * @defgroup tutorial_liblinphone Tutorial: Placing and receiving calls with liblinphone
-- * 
--
--<H1>Initialize liblinphone</H1>
--
--The first thing to do is to initialize the library passing it a set of callbacks functions to receive
--various notifications: incoming calls, progress of calls etc...
--These callbacks are all grouped in the LinphoneCoreVTable structure.
--All are optionnals (use NULL if you don't need them).
--The following code shows how initialize liblinphone:
--
--<PRE>
--      ##include <linphonecore.h>
--
--      //callback function for notification of incoming calls
--      static void on_invite_recv(LinphoneCore *lc, const char *from){
--              printf("Receiving a call from %s\n",from);
--      }
--
--      //callback function for notification end of calls (by remote)
--      static void on_bye_recv(LinphoneCore *lc, const char *from){
--              printf("Remote end hangup\n");
--      }
--
--      /
--      static void on_display_status(LinphoneCore *lc, const char *msg){
--              printf("%s",msg);
--      }
--
--      int main(int argc, char *argv[]){
--              LinphoneCoreVTable vtable;
--              
--              memset(&vtable,0,sizeof(vtable));
--              vtable.inv_recv=&on_invite_recv;
--              vtable.bye_recv=&on_bye_recv;
--              vtable.display_status=&on_display_status;
--              
--      }
--
--</PRE>
--
--
--
--/** 
-- * @defgroup initializing Initialization and destruction
-- *
++ * @defgroup initializing Initializing liblinphone
  **/
  
  /**
-- * @defgroup call_control Call control
-- *
-- * The application can initiate outgoing calls with linphone_core_invite().
-- * It is notified of incoming call thanks to the inv_recv callback of the LinphoneCoreVTable
-- * structure that is passed at creation of the LinphoneCore object.
-- * It can then answer calls with linphone_core_accept_call().
-- * Calls can be terminated or declined with linphone_core_terminate_call().
-- * The application is notified when the remote party hangups thanks to 
-- * bye_recv callback of the #LinphoneCoreVTable.
++ * @defgroup call_control Placing and receiving calls
  **/
  
  /**
   * @defgroup call_logs Managing call logs
  **/
  
++
  /**
   * @defgroup linphone_address SIP address parser API.
   * This api is useful for manipulating SIP addresses ('from' or 'to' headers).
  /**
   * @defgroup misc Miscenalleous: logs, version strings, config storage
  **/
++
++/**
++ * @defgroup tutorial_liblinphone Tutorial: Placing calls with liblinphone
++ *
++ * The minimalist program below illustrates how to initialize liblinphone and place and outgoing call.
++ * @include helloworld.c
++ *
++**/
++
++
++
index 0000000000000000000000000000000000000000,0899231260c704787d30c6352f8fbbba9e449b03..70023ac8df2c8a9da8cc7b204b06bcde3d0a840b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,723 +1,723 @@@
 - * @addtogroup calls
+ /*
+ linphone
+ Copyright (C) 2010  Belledonne Communications SARL 
+  (simon.morlat@linphone.org)
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+ #ifdef WIN32
+ #include <time.h>
+ #endif
+ #include "linphonecore.h"
+ #include "sipsetup.h"
+ #include "lpconfig.h"
+ #include "private.h"
+ #include "mediastreamer2/mediastream.h"
+ #include "mediastreamer2/msvolume.h"
+ #include "mediastreamer2/msequalizer.h"
+ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, bool_t only_one_codec){
+       MSList *l=NULL;
+       const MSList *it;
+       for(it=codecs;it!=NULL;it=it->next){
+               PayloadType *pt=(PayloadType*)it->data;
+               if ((pt->flags & PAYLOAD_TYPE_ENABLED) && linphone_core_check_payload_type_usability(lc,pt)){
+                       l=ms_list_append(l,payload_type_clone(pt));
+                       if (only_one_codec) break;
+               }
+       }
+       return l;
+ }
+ static SalMediaDescription *create_local_media_description(LinphoneCore *lc, 
+               LinphoneCall *call, const char *username, bool_t only_one_codec){
+       MSList *l;
+       PayloadType *pt;
+       SalMediaDescription *md=sal_media_description_new();
+       md->nstreams=1;
+       strncpy(md->addr,call->localip,sizeof(md->addr));
+       strncpy(md->username,username,sizeof(md->username));
+       md->bandwidth=linphone_core_get_download_bandwidth(lc);
+       /*set audio capabilities */
+       strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr));
+       md->streams[0].port=call->audio_port;
+       md->streams[0].proto=SalProtoRtpAvp;
+       md->streams[0].type=SalAudio;
+       md->streams[0].ptime=lc->net_conf.down_ptime;
+       l=make_codec_list(lc,lc->codecs_conf.audio_codecs,only_one_codec);
+       pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
+       l=ms_list_append(l,pt);
+       md->streams[0].payloads=l;
+       
+       if (lc->dw_audio_bw>0)
+               md->streams[0].bandwidth=lc->dw_audio_bw;
+       if (linphone_core_video_enabled (lc)){
+               md->nstreams++;
+               md->streams[1].port=call->video_port;
+               md->streams[1].proto=SalProtoRtpAvp;
+               md->streams[1].type=SalVideo;
+               l=make_codec_list(lc,lc->codecs_conf.video_codecs,only_one_codec);
+               md->streams[1].payloads=l;
+               if (lc->dw_video_bw)
+                       md->streams[1].bandwidth=lc->dw_video_bw;
+       }
+       return md;
+ }
+ static int find_port_offset(LinphoneCore *lc){
+       int offset;
+       MSList *elem;
+       int audio_port;
+       bool_t already_used=FALSE;
+       for(offset=0;offset<100;offset+=2){
+               audio_port=linphone_core_get_audio_port (lc)+offset;
+               already_used=FALSE;
+               for(elem=lc->calls;elem!=NULL;elem=elem->next){
+                       LinphoneCall *call=(LinphoneCall*)elem->data;
+                       if (call->audio_port==audio_port) {
+                               already_used=TRUE;
+                               break;
+                       }
+               }
+               if (!already_used) break;
+       }
+       if (offset==100){
+               ms_error("Could not find any free port !");
+               return -1;
+       }
+       return offset;
+ }
+ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){
+       int port_offset;
+       call->refcnt=1;
+       call->state=LinphoneCallIdle;
+       call->start_time=time(NULL);
+       call->media_start_time=0;
+       call->log=linphone_call_log_new(call, from, to);
+       linphone_core_notify_all_friends(call->core,LinphoneStatusOnThePhone);
+       port_offset=find_port_offset (call->core);
+       if (port_offset==-1) return;
+       call->audio_port=linphone_core_get_audio_port(call->core)+port_offset;
+       call->video_port=linphone_core_get_video_port(call->core)+port_offset;
+       
+ }
+ static void discover_mtu(LinphoneCore *lc, const char *remote){
+       int mtu;
+       if (lc->net_conf.mtu==0 ){
+               /*attempt to discover mtu*/
+               mtu=ms_discover_mtu(remote);
+               if (mtu>0){
+                       ms_set_mtu(mtu);
+                       ms_message("Discovered mtu is %i, RTP payload max size is %i",
+                               mtu, ms_get_payload_max_size());
+               }
+       }
+ }
+ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to)
+ {
+       LinphoneCall *call=ms_new0(LinphoneCall,1);
+       call->dir=LinphoneCallOutgoing;
+       call->op=sal_op_new(lc->sal);
+       sal_op_set_user_pointer(call->op,call);
+       call->core=lc;
+       linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
+       linphone_call_init_common(call,from,to);
+       call->localdesc=create_local_media_description (lc,call,
+               linphone_address_get_username(from),FALSE);
+       if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
+               linphone_core_run_stun_tests(call->core,call);
+       discover_mtu(lc,linphone_address_get_domain (to));
+       return call;
+ }
+ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
+       LinphoneCall *call=ms_new0(LinphoneCall,1);
+       LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
+       char *to_str;
+       char *from_str;
+       call->dir=LinphoneCallIncoming;
+       sal_op_set_user_pointer(op,call);
+       call->op=op;
+       call->core=lc;
+       if (lc->sip_conf.ping_with_options){
+               /*the following sends an option request back to the caller so that
+                we get a chance to discover our nat'd address before answering.*/
+               call->ping_op=sal_op_new(lc->sal);
+               to_str=linphone_address_as_string(to);
+               from_str=linphone_address_as_string(from);
+               sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op));
+               sal_op_set_user_pointer(call->ping_op,call);
+               sal_ping(call->ping_op,to_str,from_str);
+               ms_free(to_str);
+               ms_free(from_str);
+       }
+       
+       linphone_address_clean(from);
+       linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
+       linphone_call_init_common(call, from, to);
+       call->localdesc=create_local_media_description (lc,call,
+           linphone_address_get_username(me),lc->sip_conf.only_one_codec);
+       if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
+               linphone_core_run_stun_tests(call->core,call);
+       discover_mtu(lc,linphone_address_get_domain(from));
+       linphone_address_destroy(me);
+       return call;
+ }
+ /* this function is called internally to get rid of a call.
+  It performs the following tasks:
+  - remove the call from the internal list of calls
+  - unref the LinphoneCall object
+  - update the call logs accordingly
+ */
+ static void linphone_call_set_terminated(LinphoneCall *call){
+       LinphoneCallStatus status=LinphoneCallAborted;
+       LinphoneCore *lc=call->core;
+       
+       linphone_core_update_allocated_audio_bandwidth(lc);
+       if (call->state==LinphoneCallEnd){
+               status=LinphoneCallSuccess;
+               
+       }
+       linphone_call_log_completed(call->log,call, status);
+       
+       if (call == lc->current_call){
+               ms_message("Resetting the current call");
+               lc->current_call=NULL;
+               linphone_core_start_pending_refered_calls(lc);
+       }
+       if (linphone_core_del_call(lc,call) != 0){
+               ms_error("Could not remove the call from the list !!!");
+       }
+       
+       if (ms_list_size(lc->calls)==0)
+               linphone_core_notify_all_friends(lc,lc->presence_mode);
+       
+       if (call->op!=NULL) {
+               /* so that we cannot have anymore upcalls for SAL
+                concerning this call*/
+               sal_op_release(call->op);
+               call->op=NULL;
+       }
+       linphone_call_unref(call);
+ }
+ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){
+       LinphoneCore *lc=call->core;
+       if (call->state!=cstate){
+               if (cstate!=LinphoneCallRefered){
+                       /*LinphoneCallRefered is rather an event, not a state.
+                        Indeed it does not change the state of the call (still paused or running)*/
+                       call->state=cstate;
+               }
+               if (lc->vtable.call_state_changed)
+                       lc->vtable.call_state_changed(lc,call,cstate,message);
+       }
+       if (call->state==LinphoneCallEnd || call->state==LinphoneCallError)
+               linphone_call_set_terminated (call);
+ }
+ static void linphone_call_destroy(LinphoneCall *obj)
+ {
+       if (obj->op!=NULL) {
+               sal_op_release(obj->op);
+               obj->op=NULL;
+       }
+       if (obj->resultdesc!=NULL) {
+               sal_media_description_unref(obj->resultdesc);
+               obj->resultdesc=NULL;
+       }
+       if (obj->localdesc!=NULL) {
+               sal_media_description_unref(obj->localdesc);
+               obj->localdesc=NULL;
+       }
+       if (obj->ping_op) {
+               sal_op_release(obj->ping_op);
+       }
+       if (obj->refer_to){
+               ms_free(obj->refer_to);
+       }
+       ms_free(obj);
+ }
+ /**
++ * @addtogroup call_control
+  * @{
+ **/
+ /**
+  * Increments the call 's reference count.
+  * An application that wishes to retain a pointer to call object
+  * must use this function to unsure the pointer remains
+  * valid. Once the application no more needs this pointer,
+  * it must call linphone_call_unref().
+ **/
+ void linphone_call_ref(LinphoneCall *obj){
+       obj->refcnt++;
+ }
+ /**
+  * Decrements the call object reference count.
+  * See linphone_call_ref().
+ **/
+ void linphone_call_unref(LinphoneCall *obj){
+       obj->refcnt--;
+       if (obj->refcnt==0)
+               linphone_call_destroy(obj);
+ }
+ /**
+  * Returns the remote address associated to this call
+  *
+ **/
+ const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call){
+       return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to;
+ }
+ /**
+  * Returns the remote address associated to this call as a string.
+  *
+  * The result string must be freed by user using ms_free().
+ **/
+ char *linphone_call_get_remote_address_as_string(const LinphoneCall *call){
+       return linphone_address_as_string(linphone_call_get_remote_address(call));
+ }
+ /**
+  * Retrieves the call's current state.
+ **/
+ LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
+       return call->state;
+ }
+ /**
+  * Get the user_pointer in the LinphoneCall
+  *
+  * @ingroup call_control
+  *
+  * return user_pointer an opaque user pointer that can be retrieved at any time
+ **/
+ void *linphone_call_get_user_pointer(LinphoneCall *call)
+ {
+       return call->user_pointer;
+ }
+ /**
+  * Set the user_pointer in the LinphoneCall
+  *
+  * @ingroup call_control
+  *
+  * the user_pointer is an opaque user pointer that can be retrieved at any time in the LinphoneCall
+ **/
+ void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer)
+ {
+       call->user_pointer = user_pointer;
+ }
+ /**
+  * Returns the call log associated to this call.
+ **/
+ LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){
+       return call->log;
+ }
+ /**
+  * Returns the refer-to uri (if the call was transfered).
+ **/
+ const char *linphone_call_get_refer_to(const LinphoneCall *call){
+       return call->refer_to;
+ }
+ LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call){
+       return call->log->dir;
+ }
+ /**
+  * Returns true if this calls has received a transfer that has not been
+  * executed yet.
+  * Pending transfers are executed when this call is being paused or closed,
+  * locally or by remote endpoint.
+  * If the call is already paused while receiving the transfer request, the 
+  * transfer immediately occurs.
+ **/
+ bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){
+       return call->refer_pending;
+ }
+ /**
+  * @}
+ **/
+ #ifdef TEST_EXT_RENDERER
+ static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
+       ms_message("rendercb, local buffer=%p, remote buffer=%p",
+                  local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
+ }
+ #endif
+ void linphone_call_init_media_streams(LinphoneCall *call){
+       LinphoneCore *lc=call->core;
+       SalMediaDescription *md=call->localdesc;
+       AudioStream *audiostream;
+       
+       call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc));
+       if (linphone_core_echo_limiter_enabled(lc)){
+               const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
+               if (strcasecmp(type,"mic")==0)
+                       audio_stream_enable_echo_limiter(audiostream,ELControlMic);
+               else if (strcasecmp(type,"full")==0)
+                       audio_stream_enable_echo_limiter(audiostream,ELControlFull);
+       }
+       audio_stream_enable_gain_control(audiostream,TRUE);
+       if (linphone_core_echo_cancellation_enabled(lc)){
+               int len,delay,framesize;
+               len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
+               delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
+               framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
+               audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
+       }
+       audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
+       {
+               int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
+               audio_stream_enable_noise_gate(audiostream,enabled);
+       }
+       if (lc->a_rtp)
+               rtp_session_set_transports(audiostream->session,lc->a_rtp,lc->a_rtcp);
+ #ifdef VIDEO_ENABLED
+       if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){
+               call->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc));
+ #ifdef TEST_EXT_RENDERER
+               video_stream_set_render_callback(call->videostream,rendercb,NULL);
+ #endif
+       }
+ #else
+       call->videostream=NULL;
+ #endif
+ }
+ static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
+ static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){
+       LinphoneCore* lc = (LinphoneCore*)user_data;
+       if (dtmf<0 || dtmf>15){
+               ms_warning("Bad dtmf value %i",dtmf);
+               return;
+       }
+       if (lc->vtable.dtmf_received != NULL)
+               lc->vtable.dtmf_received(lc, linphone_core_get_current_call(lc), dtmf_tab[dtmf]);
+ }
+ static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
+       if (st->equalizer){
+               MSFilter *f=st->equalizer;
+               int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
+               const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
+               ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
+               if (enabled){
+                       if (gains){
+                               do{
+                                       int bytes;
+                                       MSEqualizerGain g;
+                                       if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
+                                               ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
+                                               ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
+                                               gains+=bytes;
+                                       }else break;
+                               }while(1);
+                       }
+               }
+       }
+ }
+ static void post_configure_audio_streams(LinphoneCall*call){
+       AudioStream *st=call->audiostream;
+       LinphoneCore *lc=call->core;
+       float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1);
+       float thres = 0;
+       float recv_gain;
+       float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
+       float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
+       int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
+       
+       if (mic_gain!=-1)
+               audio_stream_set_mic_gain(st,mic_gain);
+       call->audio_muted=FALSE;
+       recv_gain = lc->sound_conf.soft_play_lev;
+       if (recv_gain != 0) {
+               linphone_core_set_playback_gain_db (lc,recv_gain);
+       }
+       if (st->volsend){
+               ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
+       }
+       if (linphone_core_echo_limiter_enabled(lc)){
+               float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
+               thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
+               float force=lp_config_get_float(lc->config,"sound","el_force",-1);
+               int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
+               MSFilter *f=NULL;
+               if (st->el_type!=ELInactive){
+                       f=st->volsend;
+                       if (speed==-1) speed=0.03;
+                       if (force==-1) force=25;
+                       ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
+                       ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
+                       if (thres!=-1)
+                               ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
+                       if (sustain!=-1)
+                               ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
+               }
+       }
+               
+       if (st->volsend){
+               ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
+               ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
+       }
+       if (st->volrecv){
+               /* parameters for a limited noise-gate effect, using echo limiter threshold */
+               float floorgain = 1/mic_gain;
+               ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&thres);
+               ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
+       }
+       parametrize_equalizer(lc,st);
+       if (lc->vtable.dtmf_received!=NULL){
+               /* replace by our default action*/
+               audio_stream_play_received_dtmfs(call->audiostream,FALSE);
+               rtp_session_signal_connect(call->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);
+       }
+ }
+ static RtpProfile *make_profile(LinphoneCore *lc, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
+       int bw;
+       const MSList *elem;
+       RtpProfile *prof=rtp_profile_new("Call profile");
+       bool_t first=TRUE;
+       int remote_bw=0;
+       *used_pt=-1;
+       
+       for(elem=desc->payloads;elem!=NULL;elem=elem->next){
+               PayloadType *pt=(PayloadType*)elem->data;
+       
+               if (first) {
+                       if (desc->type==SalAudio){
+                               linphone_core_update_allocated_audio_bandwidth_in_call(lc,pt);
+                       }
+                       *used_pt=payload_type_get_number(pt);
+                       first=FALSE;
+               }
+               if (desc->bandwidth>0) remote_bw=desc->bandwidth;
+               else if (md->bandwidth>0) {
+                       /*case where b=AS is given globally, not per stream*/
+                       remote_bw=md->bandwidth;
+                       if (desc->type==SalVideo){
+                               remote_bw-=lc->audio_bw;
+                       }
+               }
+               
+               if (desc->type==SalAudio){                      
+                               bw=get_min_bandwidth(lc->up_audio_bw,remote_bw);
+               }else bw=get_min_bandwidth(lc->up_video_bw,remote_bw);
+               if (bw>0) pt->normal_bitrate=bw*1000;
+               else if (desc->type==SalAudio){
+                       pt->normal_bitrate=-1;
+               }
+               if (desc->ptime>0){
+                       char tmp[40];
+                       snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime);
+                       payload_type_append_send_fmtp(pt,tmp);
+               }
+               rtp_profile_set_payload(prof,payload_type_get_number(pt),pt);
+       }
+       return prof;
+ }
+ void linphone_call_start_media_streams(LinphoneCall *call){
+       LinphoneCore *lc=call->core;
+       LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
+       const char *tool="linphone-" LINPHONE_VERSION;
+       char *cname;
+       int used_pt=-1;
+       if(call->audiostream == NULL)
+       {
+               ms_fatal("start_media_stream() called without prior init !");
+               return;
+       }
+       /* adjust rtp jitter compensation. It must be at least the latency of the sound card */
+       int jitt_comp=MAX(lc->sound_conf.latency,lc->rtp_conf.audio_jitt_comp);
+       if (call->media_start_time==0) call->media_start_time=time(NULL);
+       cname=linphone_address_as_string_uri_only(me);
+       {
+               const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
+                                                       SalProtoRtpAvp,SalAudio);
+               if (stream){
+                       MSSndCard *playcard=lc->sound_conf.lsd_card ? 
+                               lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
+                       MSSndCard *captcard=lc->sound_conf.capt_sndcard;
+                       const char *playfile=lc->play_file;
+                       const char *recfile=lc->rec_file;
+                       call->audio_profile=make_profile(lc,call->resultdesc,stream,&used_pt);
+                       if (used_pt!=-1){
+                               if (playcard==NULL) {
+                                       ms_warning("No card defined for playback !");
+                               }
+                               if (captcard==NULL) {
+                                       ms_warning("No card defined for capture !");
+                               }
+                               ms_message("streamdir is %i",stream->dir);
+                               /*Replace soundcard filters by inactive file players or recorders
+                                when placed in recvonly or sendonly mode*/
+                               if (stream->port==0 || stream->dir==SalStreamRecvOnly){
+                                       captcard=NULL;
+                                       playfile=NULL;
+                               }else if (stream->dir==SalStreamSendOnly){
+                                       playcard=NULL;
+                                       captcard=NULL;
+                                       recfile=NULL;
+                               }
+                               /*if playfile are supplied don't use soundcards*/
+                               if (lc->use_files) {
+                                       captcard=NULL;
+                                       playcard=NULL;
+                               }
+                               audio_stream_start_full(
+                                       call->audiostream,
+                                       call->audio_profile,
+                                       stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr,
+                                       stream->port,
+                                       stream->port+1,
+                                       used_pt,
+                                       jitt_comp,
+                                       playfile,
+                                       recfile,
+                                       playcard,
+                                       captcard,
+                                       linphone_core_echo_cancellation_enabled(lc));
+                               post_configure_audio_streams(call);
+                               audio_stream_set_rtcp_information(call->audiostream, cname, tool);
+                       }else ms_warning("No audio stream accepted ?");
+               }
+       }
+ #ifdef VIDEO_ENABLED
+       {
+               const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
+                                                       SalProtoRtpAvp,SalVideo);
+               used_pt=-1;
+               /* shutdown preview */
+               if (lc->previewstream!=NULL) {
+                       video_preview_stop(lc->previewstream);
+                       lc->previewstream=NULL;
+               }
+               if (stream) {
+                       const char *addr=stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr;
+                       call->video_profile=make_profile(lc,call->resultdesc,stream,&used_pt);
+                       if (used_pt!=-1){
+                               VideoStreamDir dir=VideoStreamSendRecv;
+                               MSWebCam *cam=lc->video_conf.device;
+                               bool_t is_inactive=FALSE;
+                               
+                               video_stream_set_sent_video_size(call->videostream,linphone_core_get_preferred_video_size(lc));
+                               video_stream_enable_self_view(call->videostream,lc->video_conf.selfview);
+                                               
+                               if (stream->dir==SalStreamSendOnly && lc->video_conf.capture ){
+                                       cam=ms_web_cam_manager_get_cam(ms_web_cam_manager_get(),"Static picture");
+                                       dir=VideoStreamSendOnly;
+                               }else if (stream->dir==SalStreamRecvOnly && lc->video_conf.display ){
+                                       dir=VideoStreamRecvOnly;
+                               }else if (stream->dir==SalStreamSendRecv){
+                                       if (lc->video_conf.display && lc->video_conf.capture)
+                                               dir=VideoStreamSendRecv;
+                                       else if (lc->video_conf.display)
+                                               dir=VideoStreamRecvOnly;
+                                       else
+                                               dir=VideoStreamSendOnly;
+                               }else{
+                                       ms_warning("video stream is inactive.");
+                                       /*either inactive or incompatible with local capabilities*/
+                                       is_inactive=TRUE;
+                               }
+                               if (!is_inactive){
+                                       video_stream_set_direction (call->videostream, dir);
+                                       video_stream_start(call->videostream,
+                                               call->video_profile, addr, stream->port,
+                                               stream->port+1,
+                                               used_pt, jitt_comp, cam);
+                                       video_stream_set_rtcp_information(call->videostream, cname,tool);
+                               }
+                       }else ms_warning("No video stream accepted.");
+               }else{
+                       ms_warning("No valid video stream defined.");
+               }
+       }
+ #endif
+       goto end;
+       end:
+               ms_free(cname);
+               linphone_address_destroy(me);
+ }
+ static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
+       audio_stream_get_local_rtp_stats (st,&log->local_stats);
+ }
+ void linphone_call_stop_media_streams(LinphoneCall *call){
+       if (call->audiostream!=NULL) {
+               linphone_call_log_fill_stats (call->log,call->audiostream);
+               audio_stream_stop(call->audiostream);
+               call->audiostream=NULL;
+       }
+ #ifdef VIDEO_ENABLED
+       if (call->videostream!=NULL){
+               video_stream_stop(call->videostream);
+               call->videostream=NULL;
+       }
+       
+ #endif
+       if (call->audio_profile){
+               rtp_profile_clear_all(call->audio_profile);
+               rtp_profile_destroy(call->audio_profile);
+               call->audio_profile=NULL;
+       }
+       if (call->video_profile){
+               rtp_profile_clear_all(call->video_profile);
+               rtp_profile_destroy(call->video_profile);
+               call->video_profile=NULL;
+       }
+ }
index 83b69c94376b34c6bd7f1bda9914708e6e374c65,5f388860b1e7ebf4ee8ca58bf8169ae6da89bae0..af13e57a0f79ee86dcf0cdf19a2ec11c0bf368b6
@@@ -1137,7 -999,7 +999,9 @@@ static void linphone_core_init (Linphon
   * It should be unique within your application.
   * @param vtable a LinphoneCoreVTable structure holding your application callbacks
   * @param config_path a path to a config file. If it does not exists it will be created.
-- *        The config file is used to store all user settings, call logs, friends, proxies...
++ *        The config file is used to store all settings, call logs, friends, proxies... so that all these settings
++ *           become persistent over the life of the LinphoneCore object.
++ *           It is allowed to set a NULL config file. In that case LinphoneCore will not store any settings.
   * @param factory_config_path a path to a read-only config file that can be used to 
   *        to store hard-coded preference such as proxy settings or internal preferences.
   *        The settings in this factory file always override the one in the normal config file.
@@@ -2076,9 -1984,9 +1986,12 @@@ LinphoneCall * linphone_core_invite(Lin
   *
   * @ingroup call_control
   * @param lc the LinphoneCore object
-- * @param url the destination of the call (sip address).
++ * @param real_parsed_url the destination of the call (sip address).
++ * 
++ * The LinphoneAddress can be constructed directly using linphone_address_new(), or
++ * created by linphone_core_interpret_url().
  **/
int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_parsed_url)
LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_parsed_url)
  {
        int err=0;
        const char *route=NULL;
@@@ -2156,348 -2081,38 +2086,32 @@@ int linphone_core_transfer_call(Linphon
        return 0;
  }
  
--/**
-  * Returns true if in incoming call is pending, ie waiting for being answered or declined.
 - * Returns true if an incoming call is pending, ie waiting for being answered or declined.
-- *
-- * @ingroup call_control
--**/
  bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){
-       if (lc->call!=NULL && lc->call->dir==LinphoneCallIncoming){
-               return TRUE;
-       }
-       return FALSE;
- }
- #ifdef TEST_EXT_RENDERER
- static void rendercb(void *data, const MSPicture *local, const MSPicture *remote){
-       ms_message("rendercb, local buffer=%p, remote buffer=%p",
-                  local ? local->planes[0] : NULL, remote? remote->planes[0] : NULL);
- }
- #endif
- void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call){
-       SalMediaDescription *md=call->localdesc;
-       lc->audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc));
-       if (linphone_core_echo_limiter_enabled(lc)){
-               const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
-               if (strcasecmp(type,"mic")==0)
-                       audio_stream_enable_echo_limiter(lc->audiostream,ELControlMic);
-               else if (strcasecmp(type,"full")==0)
-                       audio_stream_enable_echo_limiter(lc->audiostream,ELControlFull);
-       }
-       audio_stream_enable_gain_control(lc->audiostream,TRUE);
-       if (linphone_core_echo_cancellation_enabled(lc)){
-               int len,delay,framesize;
-               len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
-               delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
-               framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
-               audio_stream_set_echo_canceller_params(lc->audiostream,len,delay,framesize);
-       }
-       audio_stream_enable_automatic_gain_control(lc->audiostream,linphone_core_agc_enabled(lc));
-       {
-               int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
-               audio_stream_enable_noise_gate(lc->audiostream,enabled);
-       }
-       if (lc->a_rtp)
-               rtp_session_set_transports(lc->audiostream->session,lc->a_rtp,lc->a_rtcp);
- #ifdef VIDEO_ENABLED
-       if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0){
-               lc->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc));
- #ifdef TEST_EXT_RENDERER
-               video_stream_set_render_callback(lc->videostream,rendercb,NULL);
- #endif
-       }
- #else
-       lc->videostream=NULL;
- #endif
- }
- static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
- static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){
-       LinphoneCore* lc = (LinphoneCore*)user_data;
-       if (dtmf<0 || dtmf>15){
-               ms_warning("Bad dtmf value %i",dtmf);
-               return;
-       }
-       if (lc->vtable.dtmf_received != NULL)
-               lc->vtable.dtmf_received(lc, dtmf_tab[dtmf]);
- }
- static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){
-       if (st->equalizer){
-               MSFilter *f=st->equalizer;
-               int enabled=lp_config_get_int(lc->config,"sound","eq_active",0);
-               const char *gains=lp_config_get_string(lc->config,"sound","eq_gains",NULL);
-               ms_filter_call_method(f,MS_EQUALIZER_SET_ACTIVE,&enabled);
-               if (enabled){
-                       if (gains){
-                               do{
-                                       int bytes;
-                                       MSEqualizerGain g;
-                                       if (sscanf(gains,"%f:%f:%f %n",&g.frequency,&g.gain,&g.width,&bytes)==3){
-                                               ms_message("Read equalizer gains: %f(~%f) --> %f",g.frequency,g.width,g.gain);
-                                               ms_filter_call_method(f,MS_EQUALIZER_SET_GAIN,&g);
-                                               gains+=bytes;
-                                       }else break;
-                               }while(1);
-                       }
-               }
-       }
- }
- static void post_configure_audio_streams(LinphoneCore *lc){
-       AudioStream *st=lc->audiostream;
-       float mic_gain=lp_config_get_float(lc->config,"sound","mic_gain",1);
-       float thres = 0;
-       float recv_gain;
-       float ng_thres=lp_config_get_float(lc->config,"sound","ng_thres",0.05);
-       float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0);
-       int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0);
-       
-       if (mic_gain!=-1)
-               audio_stream_set_mic_gain(st,mic_gain);
-       lc->audio_muted=FALSE;
-       recv_gain = lc->sound_conf.soft_play_lev;
-       if (recv_gain != 0) {
-               linphone_core_set_playback_gain_db (lc,recv_gain);
-       }
-       if (st->volsend){
-               ms_filter_call_method(st->volsend,MS_VOLUME_REMOVE_DC,&dc_removal);
-       }
-       if (linphone_core_echo_limiter_enabled(lc)){
-               float speed=lp_config_get_float(lc->config,"sound","el_speed",-1);
-               thres=lp_config_get_float(lc->config,"sound","el_thres",-1);
-               float force=lp_config_get_float(lc->config,"sound","el_force",-1);
-               int sustain=lp_config_get_int(lc->config,"sound","el_sustain",-1);
-               MSFilter *f=NULL;
-               if (st->el_type!=ELInactive){
-                       f=st->volsend;
-                       if (speed==-1) speed=0.03;
-                       if (force==-1) force=25;
-                       ms_filter_call_method(f,MS_VOLUME_SET_EA_SPEED,&speed);
-                       ms_filter_call_method(f,MS_VOLUME_SET_EA_FORCE,&force);
-                       if (thres!=-1)
-                               ms_filter_call_method(f,MS_VOLUME_SET_EA_THRESHOLD,&thres);
-                       if (sustain!=-1)
-                               ms_filter_call_method(f,MS_VOLUME_SET_EA_SUSTAIN,&sustain);
-               }
-       }
-               
-       if (st->volsend){
-               ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&ng_thres);
-               ms_filter_call_method(st->volsend,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&ng_floorgain);
-       }
-       if (st->volrecv){
-               /* parameters for a limited noise-gate effect, using echo limiter threshold */
-               float floorgain = 1/mic_gain;
-               ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_THRESHOLD,&thres);
-               ms_filter_call_method(st->volrecv,MS_VOLUME_SET_NOISE_GATE_FLOORGAIN,&floorgain);
-       }
-       parametrize_equalizer(lc,st);
-       if (lc->vtable.dtmf_received!=NULL){
-               /* replace by our default action*/
-               audio_stream_play_received_dtmfs(lc->audiostream,FALSE);
-               rtp_session_signal_connect(lc->audiostream->session,"telephone-event",(RtpCallback)linphone_core_dtmf_received,(unsigned long)lc);
-       }
- }
- static RtpProfile *make_profile(LinphoneCore *lc, const SalMediaDescription *md, const SalStreamDescription *desc, int *used_pt){
-       int bw;
-       const MSList *elem;
-       RtpProfile *prof=rtp_profile_new("Call profile");
-       bool_t first=TRUE;
-       int remote_bw=0;
-       *used_pt=-1;
-       
-       for(elem=desc->payloads;elem!=NULL;elem=elem->next){
-               PayloadType *pt=(PayloadType*)elem->data;
-       
-               if (first) {
-                       if (desc->type==SalAudio){
-                               linphone_core_update_allocated_audio_bandwidth_in_call(lc,pt);
-                       }
-                       *used_pt=payload_type_get_number(pt);
-                       first=FALSE;
-               }
-               if (desc->bandwidth>0) remote_bw=desc->bandwidth;
-               else if (md->bandwidth>0) {
-                       /*case where b=AS is given globally, not per stream*/
-                       remote_bw=md->bandwidth;
-                       if (desc->type==SalVideo){
-                               remote_bw-=lc->audio_bw;
-                       }
-               }
-               
-               if (desc->type==SalAudio){                      
-                               bw=get_min_bandwidth(lc->up_audio_bw,remote_bw);
-               }else bw=get_min_bandwidth(lc->up_video_bw,remote_bw);
-               if (bw>0) pt->normal_bitrate=bw*1000;
-               else if (desc->type==SalAudio){
-                       pt->normal_bitrate=-1;
-               }
-               if (desc->ptime>0){
-                       char tmp[40];
-                       snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime);
-                       payload_type_append_send_fmtp(pt,tmp);
-               }
-               rtp_profile_set_payload(prof,payload_type_get_number(pt),pt);
-       }
-       return prof;
- }
- void linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){
-       LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
-       const char *tool="linphone-" LINPHONE_VERSION;
-       char *cname;
-       int used_pt=-1;
-       /* adjust rtp jitter compensation. It must be at least the latency of the sound card */
-       int jitt_comp=MAX(lc->sound_conf.latency,lc->rtp_conf.audio_jitt_comp);
-       if (call->media_start_time==0) call->media_start_time=time(NULL);
-       cname=linphone_address_as_string_uri_only(me);
+       LinphoneCall *call = linphone_core_get_current_call(lc);
+       if(call != NULL)
        {
-               const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
-                                                       SalProtoRtpAvp,SalAudio);
-               if (stream && stream->port!=0){
-                       call->audio_profile=make_profile(lc,call->resultdesc,stream,&used_pt);
-                       if (!lc->use_files){
-                               MSSndCard *playcard=lc->sound_conf.play_sndcard;
-                               MSSndCard *captcard=lc->sound_conf.capt_sndcard;
-                               if (playcard==NULL) {
-                                       ms_warning("No card defined for playback !");
-                                       goto end;
-                               }
-                               if (captcard==NULL) {
-                                       ms_warning("No card defined for capture !");
-                                       goto end;
-                               }
-                               audio_stream_start_now(
-                                       lc->audiostream,
-                                       call->audio_profile,
-                                       stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr,
-                                       stream->port,
-                                       stream->port+1,
-                                       used_pt,
-                                       jitt_comp,
-                                       playcard,
-                                       captcard,
-                                       linphone_core_echo_cancellation_enabled(lc));
-                       }else{
-                               audio_stream_start_with_files(
-                                       lc->audiostream,
-                                       call->audio_profile,
-                                       stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr,
-                                       stream->port,
-                                       stream->port+1,
-                                       used_pt,
-                                       100,
-                                       lc->play_file,
-                                       lc->rec_file);
-                       }
-                       post_configure_audio_streams(lc);
-                       audio_stream_set_rtcp_information(lc->audiostream, cname, tool);
-               }else ms_warning("No audio stream defined ?");
+               if(call->dir==LinphoneCallIncoming)
+                       return TRUE;
        }
- #ifdef VIDEO_ENABLED
-       {
-               const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
-                                                       SalProtoRtpAvp,SalVideo);
-               /* shutdown preview */
-               if (lc->previewstream!=NULL) {
-                       video_preview_stop(lc->previewstream);
-                       lc->previewstream=NULL;
-               }
-               if (stream && stream->port!=0 && (lc->video_conf.display || lc->video_conf.capture)) {
-                       const char *addr=stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr;
-                       call->video_profile=make_profile(lc,call->resultdesc,stream,&used_pt);
-                       video_stream_set_sent_video_size(lc->videostream,linphone_core_get_preferred_video_size(lc));
-                       video_stream_enable_self_view(lc->videostream,lc->video_conf.selfview);
-                       if (lc->video_conf.display && lc->video_conf.capture)
-                               video_stream_start(lc->videostream,
-                               call->video_profile, addr, stream->port,
-                               stream->port+1,
-                               used_pt, jitt_comp, lc->video_conf.device);
-                       else if (lc->video_conf.display)
-                               video_stream_recv_only_start(lc->videostream,
-                               call->video_profile, addr, stream->port,
-                               used_pt, jitt_comp);
-                       else if (lc->video_conf.capture)
-                               video_stream_send_only_start(lc->videostream,
-                               call->video_profile, addr, stream->port,
-                               stream->port+1,
-                               used_pt, jitt_comp, lc->video_conf.device);
-                       video_stream_set_rtcp_information(lc->videostream, cname,tool);
-               }else{
-                       ms_warning("No valid video stream defined.");
-               }
-       }
- #endif
-       goto end;
-       end:
-               ms_free(cname);
-               linphone_address_destroy(me);
-               lc->call->state=LCStateAVRunning;
+       return FALSE;
  }
  
- static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
-       audio_stream_get_local_rtp_stats (st,&log->local_stats);
- }
- void linphone_core_stop_media_streams(LinphoneCore *lc, LinphoneCall *call){
-       if (lc->audiostream!=NULL) {
-               linphone_call_log_fill_stats (call->log,lc->audiostream);
-               audio_stream_stop(lc->audiostream);
-               lc->audiostream=NULL;
-       }
- #ifdef VIDEO_ENABLED
-       if (lc->videostream!=NULL){
-               if (lc->video_conf.display && lc->video_conf.capture)
-                       video_stream_stop(lc->videostream);
-               else if (lc->video_conf.display)
-                       video_stream_recv_only_stop(lc->videostream);
-               else if (lc->video_conf.capture)
-                       video_stream_send_only_stop(lc->videostream);
-               lc->videostream=NULL;
-       }
-       if (linphone_core_video_preview_enabled(lc)){
-               if (lc->previewstream==NULL){
-                       lc->previewstream=video_preview_start(lc->video_conf.device, lc->video_conf.vsize);
-               }
-       }
- #endif
-       if (call->audio_profile){
-               rtp_profile_clear_all(call->audio_profile);
-               rtp_profile_destroy(call->audio_profile);
-               call->audio_profile=NULL;
-       }
-       if (call->video_profile){
-               rtp_profile_clear_all(call->video_profile);
-               rtp_profile_destroy(call->video_profile);
-               call->video_profile=NULL;
-       }
- }
  
  /**
   * Accept an incoming call.
   *
   * @ingroup call_control
   * Basically the application is notified of incoming calls within the
-- * invite_recv callback of the #LinphoneCoreVTable structure.
-- * The application can later respond positively to the call using
++ * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive
++ * a LinphoneCallIncoming event with the associated LinphoneCall object.
++ * The application can later accept the call using
   * this method.
   * @param lc the LinphoneCore object
-- * @param url the SIP address of the originator of the call, or NULL.
-- *            This argument is useful for managing multiple calls simulatenously,
-- *            however this feature is not supported yet.
-- *            Using NULL will accept the unique incoming call in progress.
++ * @param call the LinphoneCall object representing the call to be answered.
++ * 
  **/
- int linphone_core_accept_call(LinphoneCore *lc, const char *url)
+ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call)
  {
-       LinphoneCall *call=lc->call;
        LinphoneProxyConfig *cfg=NULL;
        const char *contact=NULL;
        
@@@ -2542,17 -2197,26 +2196,25 @@@ int linphone_core_abort_call(LinphoneCo
   * Terminates a call.
   *
   * @ingroup call_control
-- * @param lc The LinphoneCore
-- * @param url the destination of the call to be terminated, use NULL if there is
-- *            only one call (which is case in this version of liblinphone).
++ * @param lc the LinphoneCore
++ * @param the_call the LinphoneCall object representing the call to be terminated.
  **/
- int linphone_core_terminate_call(LinphoneCore *lc, const char *url)
+ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call)
  {
-       LinphoneCall *call=lc->call;
-       if (call==NULL){
-               return -1;
+       LinphoneCall *call;
+       if (the_call == NULL){
+               call = linphone_core_get_current_call(lc);
+               if (ms_list_size(lc->calls)==1){
+                       call=(LinphoneCall*)lc->calls->data;
+               }else{
+                       ms_warning("No unique call to terminate !");
+                       return -1;
+               }
+       }
+       else
+       {
+               call = the_call;
        }
-       lc->call=NULL;
        sal_call_terminate(call->op);
  
        /*stop ringing*/
@@@ -2581,12 -2275,106 +2273,107 @@@ bool_t linphone_core_in_call(const Linp
   *
   * @ingroup call_control
  **/
struct _LinphoneCall *linphone_core_get_current_call(LinphoneCore *lc)
LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc)
  {
-       if(linphone_core_in_call(lc))
-               return lc->call;
-       else
-               return NULL;
+       return lc->current_call;
+ }
+ /**
 - * Permits to pause the call
++ * Pauses the call. If a music file has been setup using linphone_core_set_play_file(),
++ * this file will be played to the remote user.
+  *
+  * @ingroup call_control
+ **/
+ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *the_call)
+ {
+       LinphoneCall *call = the_call;
+       
+       if(linphone_core_get_current_call(lc) != call)
+       {
+               ms_error("The call asked to be paused was not the current on");
+               return -1;
+       }
+       if (sal_call_hold(call->op,TRUE) != 0)
+       {
+               if (lc->vtable.display_warning)
+                       lc->vtable.display_warning(lc,_("Could not pause the call"));
+       }
+       linphone_call_set_state(call,LinphoneCallPausing,"Pausing call");
+       if (lc->vtable.display_status)
+               lc->vtable.display_status(lc,_("Pausing the current call..."));
+       lc->current_call=NULL;
+       linphone_core_start_pending_refered_calls(lc);
+       return 0;
+ }
+ /**
 - * Permits to resume the call
++ * Resumes the call.
+  *
+  * @ingroup call_control
+ **/
+ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call)
+ {
+       char temp[255]={0};
+       LinphoneCall *call = the_call;
+       
+       if(call->state!=LinphoneCallPaused ){
+               ms_warning("we cannot resume a call when the communication is not established");
+               return -1;
+       }
+       if(linphone_core_get_current_call(lc) != NULL){
+               if (lc->vtable.display_warning)
+                       lc->vtable.display_warning(lc,_("There is already a call in process, pause or stop it first."));
+               return -1;
+       }
+       if(sal_call_hold(call->op,FALSE) != 0){
+               return -1;
+       }
+       linphone_call_set_state (call,LinphoneCallResuming,"Resuming");
+       snprintf(temp,sizeof(temp)-1,"Resuming the call with %s",linphone_call_get_remote_address_as_string(call));
+       if (lc->vtable.display_status) 
+               lc->vtable.display_status(lc,temp);
+       lc->current_call=call;
+       return 0;
+ }
+ /**
+  * Compare the remote address with the one in call
+  * 
+  * @param a the call
+  * @param b the remote address to compare with
+  * @return 0 if it's the good call else 1
+  */
+ static int linphone_call_remote_address_compare(const void * a, const void * b)
+ {
+       if(b == NULL || a ==NULL)
+               return 1;
+       char *the_remote_address = ((char *)b);
+       LinphoneCall *call = ((LinphoneCall *)a);
+ #ifdef DEBUG 
+       ms_message("the remote address:%s\n",the_remote_address);
+       ms_message("the call:%p => %s\n",call,linphone_call_get_remote_address_as_string(call));
+ #endif
+       if(!strcmp(linphone_call_get_remote_address_as_string(call),the_remote_address))
+       {
+               return 0;
+       }
+       return 1;
+ }
+ /**
+  * Get the call with the remote_address specified
+  * @param lc
+  * @param remote_address
+  * @return the LinphoneCall of the call if found
+  */
+ LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){
+       MSList *the_call = ms_list_find_custom(lc->calls,linphone_call_remote_address_compare,(void *)remote_address);
+       if(the_call != NULL)
+       {
+               return ((LinphoneCall *)the_call->data);
+       }
+       return NULL;
  }
  
  int linphone_core_send_publish(LinphoneCore *lc,
@@@ -3478,6 -3292,6 +3291,7 @@@ void linphone_core_set_record_file(Linp
        }
  }
  
++
  static MSFilter *get_dtmf_gen(LinphoneCore *lc){
        LinphoneCall *call=linphone_core_get_current_call (lc);
        if (call){
index 9bfce862981ec8aed4e40f87790d2932d9e67472,e67da52099102b8945def26da34927bcccbdfb59..555d38cdff259ca18a4bbb51d608c50150e7433f
@@@ -776,9 -772,8 +772,9 @@@ void linphone_core_set_record_file(Linp
  void linphone_core_play_dtmf(LinphoneCore *lc, char dtmf, int duration_ms);
  void linphone_core_stop_dtmf(LinphoneCore *lc);
  
++
  int linphone_core_get_current_call_duration(const LinphoneCore *lc);
- const LinphoneAddress *linphone_core_get_remote_uri(LinphoneCore *lc);
+ const LinphoneAddress *linphone_core_get_remote_address(LinphoneCore *lc);
  
  int linphone_core_get_mtu(const LinphoneCore *lc);
  void linphone_core_set_mtu(LinphoneCore *lc, int mtu);
Simple merge
diff --cc mediastreamer2
index 332d79209b333bd8ded5b16a7b9dcfe2c0aedc63,332d79209b333bd8ded5b16a7b9dcfe2c0aedc63..ea7ca97aaaa6f191dfec112f137fb048a4ef2139
@@@ -1,1 -1,1 +1,1 @@@
--Subproject commit 332d79209b333bd8ded5b16a7b9dcfe2c0aedc63
++Subproject commit ea7ca97aaaa6f191dfec112f137fb048a4ef2139