diff --git a/configure.in b/configure.in index e7edf835e..cda24af91 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([linphone],[3.3.2],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.3.99.1],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM dnl Source packaging numbers @@ -15,7 +15,12 @@ LINPHONE_VERSION=$LINPHONE_MAJOR_VERSION.$LINPHONE_MINOR_VERSION.${LINPHONE_MICR if test "$LINPHONE_EXTRA_VERSION" != "" ;then LINPHONE_VERSION=$LINPHONE_VERSION.${LINPHONE_EXTRA_VERSION} fi -LIBLINPHONE_SO_VERSION=`expr $LINPHONE_MINOR_VERSION + $LINPHONE_MAJOR_VERSION`:$LINPHONE_MICRO_VERSION:$LINPHONE_MINOR_VERSION + +LIBLINPHONE_SO_CURRENT=4 dnl increment this number when you add/change/remove an interface +LIBLINPHONE_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT +LIBLINPHONE_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface + +LIBLINPHONE_SO_VERSION=$LIBLINPHONE_SO_CURRENT:$LIBLINPHONE_SO_REVISION:$LIBLINPHONE_SO_AGE AC_SUBST(LIBLINPHONE_SO_VERSION, $LIBLINPHONE_SO_VERSION) AC_SUBST(LINPHONE_VERSION) diff --git a/console/commands.c b/console/commands.c index 7565f46d8..2fd9b5401 100644 --- a/console/commands.c +++ b/console/commands.c @@ -56,13 +56,14 @@ extern char *lpc_strip_blanks(char *input); static int lpc_cmd_help(LinphoneCore *, char *); static int lpc_cmd_proxy(LinphoneCore *, char *); static int lpc_cmd_call(LinphoneCore *, char *); +static int lpc_cmd_calls(LinphoneCore *, char *); static int lpc_cmd_chat(LinphoneCore *, char *); static int lpc_cmd_answer(LinphoneCore *, char *); static int lpc_cmd_autoanswer(LinphoneCore *, char *); static int lpc_cmd_terminate(LinphoneCore *, char *); static int lpc_cmd_call_logs(LinphoneCore *, char *); static int lpc_cmd_ipv6(LinphoneCore *, char *); -static int lpc_cmd_refer(LinphoneCore *, char *); +static int lpc_cmd_transfer(LinphoneCore *, char *); static int lpc_cmd_quit(LinphoneCore *, char *); static int lpc_cmd_nat(LinphoneCore *, char *); static int lpc_cmd_stun(LinphoneCore *, char *); @@ -83,6 +84,8 @@ static int lpc_cmd_acodec(LinphoneCore *lc, char *args); static int lpc_cmd_vcodec(LinphoneCore *lc, char *args); static int lpc_cmd_codec(int type, LinphoneCore *lc, char *args); static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args); +static int lpc_cmd_pause(LinphoneCore *lc, char *args); +static int lpc_cmd_resume(LinphoneCore *lc, char *args); static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args); static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args); @@ -128,17 +131,24 @@ void linphonec_out(const char *fmt,...); LPC_COMMAND commands[] = { { "help", lpc_cmd_help, "Print commands help", NULL }, { "call", lpc_cmd_call, "Call a SIP uri", - "'call ' " - ": initiate a call to the specified destination." + "'call ' \t: initiate a call to the specified destination.\n" + "'call show' \t: show all the current calls with their id and status.\n" + }, + { "calls", lpc_cmd_calls, "Show all the current calls with their id and status.\n", + NULL }, { "chat", lpc_cmd_chat, "Chat with a SIP uri", "'chat \"message\"' " ": send a chat message \"message\" to the specified destination." }, - { "terminate", lpc_cmd_terminate, "Terminate the current call", - NULL }, + { "terminate", lpc_cmd_terminate, "Terminate a call", + "'terminate' : Terminate the current call\n" + "'terminate ' : Terminate the call with supplied id\n" + "'terminate ' : Terminate all the current calls\n" + }, { "answer", lpc_cmd_answer, "Answer a call", - "Accept an incoming call." + "'answer' : Answer the current incoming call\n" + "'answer ' : Answer the call with given id\n" }, { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", "'autoanswer' \t: show current autoanswer mode\n" @@ -171,10 +181,10 @@ LPC_COMMAND commands[] = { "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, - { "refer", lpc_cmd_refer, - "Refer the current call to the specified destination.", - "'refer ' or 'r ' " - ": refer the current call to the specified destination." + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri" + "'transfer ': transfers the call with 'id' to the destination sip-uri" }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" @@ -231,19 +241,24 @@ LPC_COMMAND commands[] = { "'vcodec list' : list video codecs\n" "'vcodec enable ' : enable available video codec\n" "'vcodec disable ' : disable video codec" }, - { "ec", lpc_cmd_echocancellation, "Echo cancellation", - "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" - "'ec off' : turn echo cancellation (EC) off\n" - "'ec show' : show EC status" }, + { "ec", lpc_cmd_echocancellation, "Echo cancellation", + "'ec on [] [] []' : turn EC on with given delay, tail length and framesize\n" + "'ec off' : turn echo cancellation (EC) off\n" + "'ec show' : show EC status" }, + { "pause", lpc_cmd_pause, "pause a call", + "'pause' : pause the current call\n"}, + { "resume", lpc_cmd_resume, "resume a call", + "'resume' : resume the unique call\n" + "'resume ' : hold off the call with given id\n"}, { "mute", lpc_cmd_mute_mic, "Mute microphone and suspend voice transmission."}, { "unmute", lpc_cmd_unmute_mic, - "Unmute microphone and resume voice transmission."}, + "Unmute microphone and resume voice transmission."}, { "nortp-on-audio-mute", lpc_cmd_rtp_no_xmit_on_audio_mute, - "Set the rtp_no_xmit_on_audio_mute configuration parameter", - " If set to 1 then rtp transmission will be muted when\n" - " audio is muted , otherwise rtp is always sent."}, - { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } + "Set the rtp_no_xmit_on_audio_mute configuration parameter", + " If set to 1 then rtp transmission will be muted when\n" + " audio is muted , otherwise rtp is always sent."}, + { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } }; /*************************************************************************** @@ -388,6 +403,35 @@ lpc_cmd_help(LinphoneCore *lc, char *arg) static char callee_name[256]={0}; static char caller_name[256]={0}; +static const char *get_call_status(LinphoneCall *call){ + switch(linphone_call_get_state(call)){ + case LinphoneCallPaused: + if (linphone_call_get_refer_to (call)!=NULL){ + return "Paused (transfered)"; + }else{ + return "Paused"; + } + break; + case LinphoneCallIncomingReceived: + return "Pending"; + break; + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + return "Dialing out"; + break; + case LinphoneCallOutgoingEarlyMedia: + case LinphoneCallOutgoingRinging: + return "Remote ringing"; + break; + default: + if (linphone_call_has_transfer_pending(call)){ + return "Running (transfer pending)"; + }else + return "Running"; + } + return ""; +} + static int lpc_cmd_call(LinphoneCore *lc, char *args) { @@ -395,14 +439,14 @@ lpc_cmd_call(LinphoneCore *lc, char *args) { return 0; } - - if ( lc->call != NULL ) { - linphonec_out("Terminate current call first.\n"); - } - else - { - if ( -1 == linphone_core_invite(lc, args) ) + LinphoneCall *call; + if ( linphone_core_in_call(lc) ) + { + linphonec_out("Terminate or hold on the current call first.\n"); + return 1; + } + if ( NULL == (call=linphone_core_invite(lc, args)) ) { linphonec_out("Error from linphone_core_invite.\n"); } @@ -414,6 +458,32 @@ lpc_cmd_call(LinphoneCore *lc, char *args) return 1; } +static int +lpc_cmd_calls(LinphoneCore *lc, char *args){ + const MSList *calls = linphone_core_get_calls(lc); + if(calls) + { + const MSList *p_calls = calls; + linphonec_out("ID\t\tDestination\t\t\t\tStatus\n---------------------------------------------------------------------\n"); + while(p_calls != NULL) + { + LinphoneCall *call=(LinphoneCall*)p_calls->data; + char *tmp=linphone_call_get_remote_address_as_string(call); + linphonec_out("%li\t%s\t\t\t%s\r\n", + (long)linphone_call_get_user_pointer (call), + tmp, + get_call_status(call)); + p_calls = p_calls->next; + ms_free(tmp); + } + }else + { + linphonec_out("No active call.\n"); + } + return 1; +} + + static int lpc_cmd_chat(LinphoneCore *lc, char *args) { @@ -456,12 +526,34 @@ void linphonec_set_caller(const char *caller){ } static int -lpc_cmd_refer(LinphoneCore *lc, char *args) +lpc_cmd_transfer(LinphoneCore *lc, char *args) { - if (args) - linphone_core_refer(lc, args); - else{ - linphonec_out("refer needs an argument\n"); + if (args){ + LinphoneCall *call; + const char *refer_to=NULL; + char arg1[256]={0}; + char arg2[266]={0}; + int n=sscanf(args,"%s %s",arg1,arg2); + if (n==1 || isalpha(*arg1)){ + call=linphone_core_get_current_call(lc); + if (call==NULL && linphone_core_get_calls_nb (lc)==1){ + call=(LinphoneCall*)linphone_core_get_calls(lc)->data; + } + refer_to=args; + if (call==NULL){ + linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); + return 0; + } + }else{ + long id=atoi(arg1); + refer_to=args+strlen(arg1)+1; + call=linphonec_get_call(id); + if (call==NULL) return 0; + } + linphone_core_transfer_call(lc, call, refer_to); + }else{ + linphonec_out("Transfer command requires at least one argument\n"); + return 0; } return 1; } @@ -469,21 +561,66 @@ lpc_cmd_refer(LinphoneCore *lc, char *args) static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { - if ( -1 == linphone_core_terminate_call(lc, NULL) ) - { - linphonec_out("No active call.\n"); + if (linphone_core_get_calls(lc)==NULL){ + linphonec_out("No active calls"); + return 1; } - return 1; + if (!args) + { + if ( -1 == linphone_core_terminate_call(lc, NULL) ){ + linphonec_out("Could not stop the active call.\n"); + } + return 1; + } + + if(strcmp(args,"all")==0){ + linphonec_out("We are going to stop all the calls.\n"); + linphone_core_terminate_all_calls(lc); + return 1; + }else{ + /*the argument is a linphonec call id */ + long id=atoi(args); + LinphoneCall *call=linphonec_get_call(id); + if (call){ + if (linphone_core_terminate_call(lc,call)==-1){ + linphonec_out("Could not stop the call with id %li",id); + } + }else return 0; + return 1; + } + return 0; + } static int -lpc_cmd_answer(LinphoneCore *lc, char *args) -{ - if ( -1 == linphone_core_accept_call(lc, NULL) ) +lpc_cmd_answer(LinphoneCore *lc, char *args){ + if (!args) { - linphonec_out("No incoming call.\n"); + int nb=ms_list_size(linphone_core_get_calls(lc)); + if (nb==1){ + //if just one call is present answer the only one in passing NULL to the linphone_core_accept_call ... + if ( -1 == linphone_core_accept_call(lc, NULL) ) + { + linphonec_out("Fail to accept incoming call\n"); + } + }else if (nb==0){ + linphonec_out("There are no calls to answer.\n"); + }else{ + linphonec_out("Multiple calls in progress, please specify call id.\n"); + return 0; + } + return 1; + }else{ + long id; + if (sscanf(args,"%li",&id)==1){ + LinphoneCall *call=linphonec_get_call (id); + if (linphone_core_accept_call (lc,call)==-1){ + linphonec_out("Fail to accept call %i\n",id); + } + }else return 0; + return 1; } - return 1; + return 0; } static int @@ -531,7 +668,7 @@ lpc_cmd_nat(LinphoneCore *lc, char *args) } nat = linphone_core_get_nat_address(lc); - use = linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS; + use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress; linphonec_out("Nat address: %s%s\n", nat ? nat : "unspecified" , use ? "" : " (disabled - use 'firewall nat' to enable)"); return 1; @@ -552,7 +689,7 @@ lpc_cmd_stun(LinphoneCore *lc, char *args) } stun = linphone_core_get_stun_server(lc); - use = linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_STUN; + use = linphone_core_get_firewall_policy(lc)==LinphonePolicyUseStun; linphonec_out("Stun server: %s%s\n", stun ? stun : "unspecified" , use? "" : " (disabled - use 'firewall stun' to enable)"); return 1; @@ -569,7 +706,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) { if (strcmp(args,"none")==0) { - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_NO_FIREWALL); + linphone_core_set_firewall_policy(lc,LinphonePolicyNoFirewall); } else if (strcmp(args,"stun")==0) { @@ -579,7 +716,7 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) linphonec_out("No stun server address is defined, use 'stun
' first\n"); return 1; } - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_STUN); + linphone_core_set_firewall_policy(lc,LinphonePolicyUseStun); } else if (strcmp(args,"nat")==0) { @@ -589,19 +726,19 @@ lpc_cmd_firewall(LinphoneCore *lc, char *args) linphonec_out("No nat address is defined, use 'nat
' first"); return 1; } - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_USE_NAT_ADDRESS); + linphone_core_set_firewall_policy(lc,LinphonePolicyUseNatAddress); } } switch(linphone_core_get_firewall_policy(lc)) { - case LINPHONE_POLICY_NO_FIREWALL: + case LinphonePolicyNoFirewall: linphonec_out("No firewall\n"); break; - case LINPHONE_POLICY_USE_STUN: + case LinphonePolicyUseStun: linphonec_out("Using stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc)); break; - case LINPHONE_POLICY_USE_NAT_ADDRESS: + case LinphonePolicyUseNatAddress: linphonec_out("Using supplied nat address %s.\n", setting ? setting : linphone_core_get_nat_address(lc)); break; } @@ -1084,7 +1221,59 @@ lpc_cmd_staticpic(LinphoneCore *lc, char *args) return 0; /* Syntax error */ } +static int lpc_cmd_pause(LinphoneCore *lc, char *args){ + if(linphone_core_in_call(lc)) + { + linphone_core_pause_call(lc,linphone_core_get_current_call(lc)); + return 1; + } + linphonec_out("you can only pause when a call is in process\n"); + return 0; +} + +static int lpc_cmd_resume(LinphoneCore *lc, char *args){ + + if(linphone_core_in_call(lc)) + { + linphonec_out("There is already a call in process pause or stop it first"); + return 1; + } + if (args) + { + long id; + int n = sscanf(args, "%li", &id); + if (n == 1){ + LinphoneCall *call=linphonec_get_call (id); + if (call){ + if(linphone_core_resume_call(lc,call)==-1){ + linphonec_out("There was a problem to resume the call check the remote address you gave %s\n",args); + } + } + return 1; + }else return 0; + } + else + { + const MSList *calls = linphone_core_get_calls(lc); + int nbcalls=ms_list_size(calls); + if( nbcalls == 1) + { + if(linphone_core_resume_call(lc,calls->data) < 0) + { + linphonec_out("There was a problem to resume the unique call.\n"); + } + return 1; + }else if (nbcalls==0){ + linphonec_out("There is no calls at this time.\n"); + return 1; + }else{ + linphonec_out("There are %i calls at this time, please specify call id as given with 'calls' command.\n"); + } + } + return 0; + +} /*************************************************************************** * @@ -1612,31 +1801,31 @@ static int lpc_cmd_status(LinphoneCore *lc, char *args) } else if (strstr(args,"hook")) { - gstate_t call_state=linphone_core_get_state(lc,GSTATE_GROUP_CALL); -/* - if (!cfg || !linphone_proxy_config_is_registered(cfg)){ - linphonec_out("unregistered\n"); - return 1; - } - */ + LinphoneCall *call=linphone_core_get_current_call (lc); + LinphoneCallState call_state=LinphoneCallIdle; + if (call) call_state=linphone_call_get_state(call); + switch(call_state){ - case GSTATE_CALL_OUT_INVITE: + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: linphonec_out("hook=dialing\n"); break; - case GSTATE_CALL_IDLE: + case LinphoneCallIdle: linphonec_out("hook=offhook\n"); break; - case GSTATE_CALL_OUT_CONNECTED: - linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), + case LinphoneCallStreamsRunning: + case LinphoneCallConnected: + if (linphone_call_get_dir(call)==LinphoneCallOutgoing){ + linphonec_out("Call out, hook=%s duration=%i, muted=%s rtp-xmit-muted=%s\n", linphonec_get_callee(), linphone_core_get_current_call_duration(lc), - lc->audio_muted ? "yes" : "no", + linphone_core_is_mic_muted (lc) ? "yes" : "no", linphone_core_is_rtp_muted(lc) ? "yes" : "no"); - break; - case GSTATE_CALL_IN_CONNECTED: - linphonec_out("hook=answered duration=%i\n" , - linphone_core_get_current_call_duration(lc)); + }else{ + linphonec_out("hook=answered duration=%i\n" , + linphone_core_get_current_call_duration(lc)); + } break; - case GSTATE_CALL_IN_INVITE: + case LinphoneCallIncomingReceived: linphonec_out("Incoming call from %s\n",linphonec_get_caller()); break; default: @@ -1874,36 +2063,32 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ static int lpc_cmd_mute_mic(LinphoneCore *lc, char *args) { - if ( lc->call != NULL ) - linphone_core_mute_mic(lc, 1); - return 1; + linphone_core_mute_mic(lc, 1); + return 1; } -static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args) -{ - if ( lc->call != NULL ) - linphone_core_mute_mic(lc, 0); - return 1; +static int lpc_cmd_unmute_mic(LinphoneCore *lc, char *args){ + linphone_core_mute_mic(lc, 0); + return 1; } static int lpc_cmd_rtp_no_xmit_on_audio_mute(LinphoneCore *lc, char *args) { - bool_t rtp_xmit_off=FALSE; - char *status; - gstate_t call_state=linphone_core_get_state(lc,GSTATE_GROUP_CALL); + bool_t rtp_xmit_off=FALSE; + char *status; - if(args){ - if(strstr(args,"1"))rtp_xmit_off=TRUE; - if(call_state == GSTATE_CALL_IDLE) - linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); - else - linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); - } - rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); - if(rtp_xmit_off)status="off"; - else status="on"; - linphonec_out("rtp transmit %s when audio muted\n",status); - return 1; + if(args){ + if(strstr(args,"1"))rtp_xmit_off=TRUE; + if(linphone_core_get_current_call (lc)==NULL) + linphone_core_set_rtp_no_xmit_on_audio_mute(lc,rtp_xmit_off); + else + linphonec_out("nortp-on-audio-mute: call in progress - cannot change state\n"); + } + rtp_xmit_off=linphone_core_get_rtp_no_xmit_on_audio_mute(lc); + if (rtp_xmit_off) status="off"; + else status="on"; + linphonec_out("rtp transmit %s when audio muted\n",status); + return 1; } diff --git a/console/linphonec.c b/console/linphonec.c index 2d1ce98b3..df86abb8f 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -112,25 +112,24 @@ static char **linephonec_readline_completion(const char *text, #endif /* These are callback for linphone core */ -static void linphonec_call_received(LinphoneCore *lc, const char *from); static void linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *username); -static void linphonec_display_refer (LinphoneCore * lc,const char *refer_to); +static void linphonec_display_refer (LinphoneCore * lc, const char *refer_to); static void linphonec_display_something (LinphoneCore * lc, const char *something); static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); -static void stub () {} -static void linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg); +static void linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event); + static void linphonec_notify_presence_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); -static void linphonec_bye_received(LinphoneCore *lc, const char *from); + static void linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, const char *from, const char *msg); static void linphonec_display_status (LinphoneCore * lc, const char *something); -static void linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate); -static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf); +static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); static void print_prompt(LinphoneCore *opm); +void linphonec_out(const char *fmt,...); /*************************************************************************** * * Global variables @@ -170,34 +169,23 @@ static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ -LinphoneCoreVTable linphonec_vtable -#if !defined (_MSC_VER) -= { - .show =(ShowInterfaceCb) stub, - .inv_recv = linphonec_call_received, - .bye_recv = linphonec_bye_received, - .notify_recv = linphonec_notify_received, - .notify_presence_recv = linphonec_notify_presence_received, - .new_unknown_subscriber = linphonec_new_unknown_subscriber, - .auth_info_requested = linphonec_prompt_for_auth, - .display_status = linphonec_display_status, - .display_message=linphonec_display_something, -#ifdef VINCENT_MAURY_RSVP - /* the yes/no dialog box */ - .display_yes_no= (DisplayMessageCb) stub, -#endif - .display_warning=linphonec_display_warning, - .display_url=linphonec_display_url, - .display_question=(DisplayQuestionCb)stub, - .text_received=linphonec_text_received, - .general_state=linphonec_general_state, - .dtmf_received=linphonec_dtmf_received, - .refer_received=linphonec_display_refer +void linphonec_call_identify(LinphoneCall* call){ + static long callid=1; + linphone_call_set_user_pointer (call,(void*)callid); + callid++; } -#endif /*_WIN32_WCE*/ -; - +LinphoneCall *linphonec_get_call(long id){ + const MSList *elem=linphone_core_get_calls(linphonec); + for (;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall*)elem->data; + if (linphone_call_get_user_pointer (call)==(void*)id){ + return call; + } + } + linphonec_out("Sorry, no call with id %i exists at this time.",id); + return NULL; +} /*************************************************************************** * @@ -209,10 +197,9 @@ LinphoneCoreVTable linphonec_vtable * Linphone core callback */ static void -linphonec_display_refer (LinphoneCore * lc,const char *refer_to) +linphonec_display_refer (LinphoneCore * lc, const char *refer_to) { - fprintf (stdout, "The distant end point asked to transfer the call to %s,don't forget to terminate the call if not\n%s", refer_to,prompt); - fflush(stdout); + linphonec_out("Receiving out of call refer to %s", refer_to); } /* @@ -254,19 +241,6 @@ linphonec_display_url (LinphoneCore * lc, const char *something, const char *url fprintf (stdout, "%s : %s\n", something, url); } - -/* - * Linphone core callback - */ -static void -linphonec_call_received(LinphoneCore *lc, const char *from) -{ - linphonec_set_caller(from); - if ( auto_answer) { - answer_call=TRUE; - } -} - /* * Linphone core callback */ @@ -296,16 +270,16 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern * Linphone core callback */ static void -linphonec_notify_received(LinphoneCore *lc,const char *from,const char *msg) +linphonec_notify_received(LinphoneCore *lc, LinphoneCall *call, const char *from,const char *event) { - printf("Notify type %s from %s\n", msg, from); - if(!strcmp(msg,"refer")) + if(!strcmp(event,"refer")) { - printf("The distant SIP end point get the refer we can close the call\n"); - linphonec_parse_command_line(linphonec, "terminate"); + linphonec_out("The distand endpoint %s of call %li has been transfered, you can safely close the call.\n", + from,(long)linphone_call_get_user_pointer (call)); } } + /* * Linphone core callback */ @@ -332,17 +306,41 @@ linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, } -/* - * Linphone core callback - */ -static void -linphonec_bye_received(LinphoneCore *lc, const char *from) -{ - // Should change prompt back to original maybe - - // printing this is unneeded as we'd get a "Communication ended" - // message trough display_status callback anyway - //printf("Bye received from %s\n", from); +static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState st, const char *msg){ + char *from=linphone_call_get_remote_address_as_string(call); + long id=(long)linphone_call_get_user_pointer (call); + switch(st){ + case LinphoneCallEnd: + linphonec_out("Call %i with %s ended.\n", id, from); + break; + case LinphoneCallResuming: + linphonec_out("Resuming call %i with %s.\n", id, from); + break; + case LinphoneCallStreamsRunning: + linphonec_out("Media streams established with %s for call %i.\n", from,id); + break; + case LinphoneCallPausing: + linphonec_out("Pausing call %i with %s.\n", id, from); + break; + case LinphoneCallPaused: + linphonec_out("Call %i with %s is now paused.\n", id, from); + break; + case LinphoneCallIncomingReceived: + linphonec_call_identify(call); + id=(long)linphone_call_get_user_pointer (call); + linphonec_set_caller(from); + if ( auto_answer) { + answer_call=TRUE; + } + linphonec_out("Receiving new incoming call from %s, assigned id %i", from,id); + break; + case LinphoneCallOutgoingInit: + linphonec_call_identify(call); + break; + default: + break; + } + ms_free(from); } /* @@ -357,64 +355,11 @@ linphonec_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, } -static void linphonec_dtmf_received(LinphoneCore *lc, int dtmf){ - fprintf(stdout,"Receiving tone %c\n",dtmf); +static void linphonec_dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf){ + char *from=linphone_call_get_remote_address_as_string(call); + fprintf(stdout,"Receiving tone %c from %s\n",dtmf,from); fflush(stdout); -} - -static void -linphonec_general_state (LinphoneCore * lc, LinphoneGeneralState *gstate) -{ - if (show_general_state) { - switch(gstate->new_state) { - case GSTATE_POWER_OFF: - printf("GSTATE_POWER_OFF"); - break; - case GSTATE_POWER_STARTUP: - printf("GSTATE_POWER_STARTUP"); - break; - case GSTATE_POWER_ON: - printf("GSTATE_POWER_ON"); - break; - case GSTATE_POWER_SHUTDOWN: - printf("GSTATE_POWER_SHUTDOWN"); - break; - case GSTATE_REG_NONE: - printf("GSTATE_REG_NONE"); - break; - case GSTATE_REG_OK: - printf("GSTATE_REG_OK"); - break; - case GSTATE_REG_FAILED: - printf("GSTATE_REG_FAILED"); - break; - case GSTATE_CALL_IDLE: - printf("GSTATE_CALL_IDLE"); - break; - case GSTATE_CALL_OUT_INVITE: - printf("GSTATE_CALL_OUT_INVITE"); - break; - case GSTATE_CALL_OUT_CONNECTED: - printf("GSTATE_CALL_OUT_CONNECTED"); - break; - case GSTATE_CALL_IN_INVITE: - printf("GSTATE_CALL_IN_INVITE"); - break; - case GSTATE_CALL_IN_CONNECTED: - printf("GSTATE_CALL_IN_CONNECTED"); - break; - case GSTATE_CALL_END: - printf("GSTATE_CALL_END"); - break; - case GSTATE_CALL_ERROR: - printf("GSTATE_CALL_ERROR"); - break; - default: - printf("GSTATE_UNKNOWN_%d",gstate->new_state); - } - if (gstate->message) printf(" %s", gstate->message); - printf("\n"); - } + ms_free(from); } static char received_prompt[PROMPT_MAX_LEN]; @@ -596,6 +541,8 @@ bool_t linphonec_get_autoanswer(){ return auto_answer; } +LinphoneCoreVTable linphonec_vtable={0}; + /***************************************************************************/ /* * Main @@ -621,31 +568,24 @@ char **convert_args_to_ascii(int argc, _TCHAR **wargv){ int _tmain(int argc, _TCHAR* wargv[]) { char **argv=convert_args_to_ascii(argc,wargv); trace_level=6; - linphonec_vtable.show =(ShowInterfaceCb) stub; - linphonec_vtable.inv_recv = linphonec_call_received; - linphonec_vtable.bye_recv = linphonec_bye_received; - linphonec_vtable.notify_presence_recv = linphonec_notify_received; - linphonec_vtable.new_unknown_subscriber = linphonec_new_unknown_subscriber; - linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth; - linphonec_vtable.display_status = linphonec_display_status; - linphonec_vtable.display_message=linphonec_display_something; -#ifdef VINCENT_MAURY_RSVP - /* the yes/no dialog box */ - linphonec_vtable.display_yes_no= (DisplayMessageCb) stub; -#endif - linphonec_vtable.display_warning=linphonec_display_warning; - linphonec_vtable.display_url=linphonec_display_url; - linphonec_vtable.display_question=(DisplayQuestionCb)stub; - linphonec_vtable.text_received=linphonec_text_received; - linphonec_vtable.general_state=linphonec_general_state; - linphonec_vtable.dtmf_received=linphonec_dtmf_received; #else int main (int argc, char *argv[]) { #endif - - + linphonec_vtable.call_state_changed=linphonec_call_state_changed; + linphonec_vtable.notify_presence_recv = linphonec_notify_presence_received; + linphonec_vtable.new_unknown_subscriber = linphonec_new_unknown_subscriber; + linphonec_vtable.auth_info_requested = linphonec_prompt_for_auth; + linphonec_vtable.display_status = linphonec_display_status; + linphonec_vtable.display_message=linphonec_display_something; + linphonec_vtable.display_warning=linphonec_display_warning; + linphonec_vtable.display_url=linphonec_display_url; + linphonec_vtable.text_received=linphonec_text_received; + linphonec_vtable.dtmf_received=linphonec_dtmf_received; + linphonec_vtable.refer_received=linphonec_display_refer; + linphonec_vtable.notify_recv=linphonec_notify_received; + if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE); linphonec_main_loop (linphonec, sipAddr); @@ -766,11 +706,10 @@ void linphonec_main_loop_exit(void){ void linphonec_finish(int exit_status) { - printf("Terminating...\n"); + linphonec_out("Terminating...\n"); /* Terminate any pending call */ - linphonec_parse_command_line(linphonec, "terminate"); - linphonec_command_finished(); + linphone_core_terminate_all_calls(linphonec); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif diff --git a/console/linphonec.h b/console/linphonec.h index 90b6f6842..235aa5e97 100644 --- a/console/linphonec.h +++ b/console/linphonec.h @@ -112,6 +112,8 @@ void linphonec_set_autoanswer(bool_t enabled); bool_t linphonec_get_autoanswer(); void linphonec_command_finished(void); void linphonec_set_caller(const char *caller); +LinphoneCall *linphonec_get_call(long id); +void linphonec_call_identify(LinphoneCall* call); #endif /* def LINPHONEC_H */ diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 014ae308e..5ae7539e7 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST=linphonecore_jni.cc ## Process this file with automake to produce Makefile.in linphone_includedir=$(includedir)/linphone -linphone_include_HEADERS=linphonecore.h ../config.h lpconfig.h sipsetup.h +linphone_include_HEADERS=linphonecore.h linphonecore_utils.h ../config.h lpconfig.h sipsetup.h INCLUDES = \ -I$(top_srcdir)\ @@ -32,9 +32,10 @@ liblinphone_la_SOURCES=\ authentication.c \ lpconfig.c lpconfig.h \ chat.c \ - general_state.c \ + linphonecall.c \ sipsetup.c sipsetup.h \ - siplogin.c + siplogin.c \ + lsd.c linphonecore_utils.h liblinphone_la_LDFLAGS= -version-info $(LIBLINPHONE_SO_VERSION) -no-undefined @@ -48,6 +49,11 @@ if BUILD_WIN32 liblinphone_la_LIBADD+=$(top_builddir)/oRTP/src/libortp.la endif +noinst_PROGRAMS=test_lsd + +test_lsd_SOURCES=test_lsd.c + +test_lsd_LDADD=liblinphone.la AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE \ $(ORTP_CFLAGS) \ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 56bc2d4a0..aad80a60d 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -26,16 +26,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){ - if (lc->vtable.show) - lc->vtable.show(lc); - if (lc->vtable.display_status) - lc->vtable.display_status(lc,_("Connected.")); - call->state=LCStateAVRunning; if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_start_media_streams(lc,call); + linphone_call_start_media_streams(call); } static void call_received(SalOp *h){ @@ -47,26 +42,22 @@ static void call_received(SalOp *h){ LinphoneAddress *from_parsed; /* first check if we can answer successfully to this invite */ - if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){ - ms_message("Not present !! presence mode : %d\n",lc->presence_mode); - if (lc->presence_mode==LINPHONE_STATUS_BUSY) + if (lc->presence_mode==LinphoneStatusBusy || + lc->presence_mode==LinphoneStatusOffline || + lc->presence_mode==LinphoneStatusDoNotDisturb || + lc->presence_mode==LinphoneStatusMoved){ + if (lc->presence_mode==LinphoneStatusBusy ) sal_call_decline(h,SalReasonBusy,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_AWAY - ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK - ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE - ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH - ||lc->presence_mode==LINPHONE_STATUS_OFFLINE) + else if (lc->presence_mode==LinphoneStatusOffline) sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB) + else if (lc->presence_mode==LinphoneStatusDoNotDisturb) sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED) + else if (lc->alt_contact!=NULL && lc->presence_mode==LinphoneStatusMoved) sal_call_decline(h,SalReasonRedirect,lc->alt_contact); - else - sal_call_decline(h,SalReasonBusy,NULL); sal_op_release(h); return; } - if (lc->call!=NULL){/*busy*/ + if (!linphone_core_can_we_add_call(lc)){/*busy*/ sal_call_decline(h,SalReasonBusy,NULL); sal_op_release(h); return; @@ -75,56 +66,72 @@ static void call_received(SalOp *h){ to=sal_op_get_to(h); call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),h); - lc->call=call; + sal_call_set_local_media_description(h,call->localdesc); call->resultdesc=sal_call_get_final_media_description(h); if (call->resultdesc) sal_media_description_ref(call->resultdesc); if (call->resultdesc && sal_media_description_empty(call->resultdesc)){ sal_call_decline(h,SalReasonMedia,NULL); - linphone_call_destroy(call); - lc->call=NULL; + linphone_call_unref(call); + return; + } + /* the call is acceptable so we can now add it to our list */ + if(linphone_core_add_call(lc,call)!= 0) + { + ms_warning("we cannot handle anymore call\n"); + sal_call_decline(h,SalReasonMedia,NULL); + linphone_call_unref(call); return; } - from_parsed=linphone_address_new(sal_op_get_from(h)); linphone_address_clean(from_parsed); tmp=linphone_address_as_string(from_parsed); linphone_address_destroy(from_parsed); - gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp); + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"), (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_(".")); if (lc->vtable.show) lc->vtable.show(lc); if (lc->vtable.display_status) lc->vtable.display_status(lc,barmesg); - /* play the ring */ - if (lc->sound_conf.ring_sndcard!=NULL){ + /* play the ring if this is the only call*/ + if (lc->sound_conf.ring_sndcard!=NULL && ms_list_size(lc->calls)==1){ if (lc->ringstream && lc->dmfs_playing_start_time!=0){ ring_stop(lc->ringstream); lc->ringstream=NULL; lc->dmfs_playing_start_time=0; } - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard); + if(lc->ringstream==NULL){ + MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + ms_message("Starting local ring..."); + lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); + } + else + { + ms_message("the local ring is already started"); + } + }else{ + /*TODO : play a tone within the context of the current call */ } - linphone_call_set_state(call,LCStateRinging); sal_call_notify_ringing(h); #if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000) - linphone_core_init_media_streams(lc,lc->call); + linphone_call_init_media_streams(call); #endif - if (lc->vtable.inv_recv) lc->vtable.inv_recv(lc,tmp); ms_free(barmesg); ms_free(tmp); } static void call_ringing(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(h); SalMediaDescription *md; + if (call==NULL) return; + if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Remote ringing.")); + md=sal_call_get_final_media_description(h); if (md==NULL){ if (lc->ringstream && lc->dmfs_playing_start_time!=0){ @@ -134,13 +141,14 @@ static void call_ringing(SalOp *h){ } if (lc->ringstream!=NULL) return; /*already ringing !*/ if (lc->sound_conf.play_sndcard!=NULL){ + MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; ms_message("Remote ringing..."); - lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); - gstate_new_state(lc, GSTATE_CALL_OUT_RINGING, NULL); + lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard); + linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing"); } }else{ /*accept early media */ - if (lc->audiostream && lc->audiostream->ticker!=NULL){ + if (call->audiostream && call->audiostream->ticker!=NULL){ /*streams already started */ ms_message("Early media already started."); return; @@ -150,36 +158,34 @@ static void call_ringing(SalOp *h){ if (lc->vtable.show) lc->vtable.show(lc); if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Early media.")); - gstate_new_state(lc, GSTATE_CALL_OUT_RINGING, NULL); + linphone_call_set_state(call,LinphoneCallOutgoingEarlyMedia,"Early media"); if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } ms_message("Doing early media..."); - linphone_core_start_media_streams(lc,call); + linphone_call_start_media_streams(call); call->media_pending=TRUE; } - call->state=LCStateRinging; } +/* + * could be reach : + * - when the call is accepted + * - when a request is accepted (pause, resume) + */ static void call_accepted(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + if (call==NULL){ ms_warning("No call to accept."); return ; } - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_accepted: ignoring."); - return; - } - if (call->state==LCStateAVRunning){ - return ; /*already accepted*/ - } - if (lc->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); + if ((call->audiostream!=NULL) && (call->audiostream->ticker!=NULL)){ + /*case where we accepted early media or already in call*/ + linphone_call_stop_media_streams(call); + linphone_call_init_media_streams(call); } if (call->resultdesc) sal_media_description_unref(call->resultdesc); @@ -188,32 +194,55 @@ static void call_accepted(SalOp *op){ sal_media_description_ref(call->resultdesc); call->media_pending=FALSE; } + if (call->state==LinphoneCallOutgoingProgress || + call->state==LinphoneCallOutgoingRinging || + call->state==LinphoneCallOutgoingEarlyMedia){ + linphone_call_set_state(call,LinphoneCallConnected,"Connected"); + } if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - linphone_connect_incoming(lc,call); + if (sal_media_description_has_dir(call->resultdesc,SalStreamSendOnly)){ + /*we initiated a pause*/ + if (lc->vtable.display_status){ + char *tmp=linphone_call_get_remote_address_as_string (call); + char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp); + lc->vtable.display_status(lc,msg); + ms_free(tmp); + ms_free(msg); + } + linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + }else if (sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)){ + /*we are put on hold when the call is initially accepted */ + if (lc->vtable.display_status){ + char *tmp=linphone_call_get_remote_address_as_string (call); + char *msg=ms_strdup_printf(_("Call answered by %s - on hold."),tmp); + lc->vtable.display_status(lc,msg); + ms_free(tmp); + ms_free(msg); + } + linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + }else{ + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + } + linphone_connect_incoming (lc,call); }else{ /*send a bye*/ ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); - linphone_core_terminate_call(lc,NULL); + linphone_core_abort_call(lc,call,"No codec intersection"); } } static void call_ack(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (call==NULL){ ms_warning("No call to be ACK'd"); return ; } - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_ack: ignoring."); - return; - } if (call->media_pending){ - if (lc->audiostream->ticker!=NULL){ + if (call->audiostream->ticker!=NULL){ /*case where we accepted early media */ - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); + linphone_call_stop_media_streams(call); + linphone_call_init_media_streams(call); } if (call->resultdesc) sal_media_description_unref(call->resultdesc); @@ -221,62 +250,86 @@ static void call_ack(SalOp *op){ if (call->resultdesc) sal_media_description_ref(call->resultdesc); if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); linphone_connect_incoming(lc,call); + linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); }else{ /*send a bye*/ ms_error("Incompatible SDP response received in ACK, need to abort the call"); - linphone_core_terminate_call(lc,NULL); + linphone_core_abort_call(lc,call,"No codec intersection"); + return; } call->media_pending=FALSE; } } -static void call_updated(SalOp *op){ +/* this callback is called when an incoming re-INVITE modifies the session*/ +static void call_updating(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - linphone_core_stop_media_streams(lc,call); - linphone_core_init_media_streams(lc,call); if (call->resultdesc) sal_media_description_unref(call->resultdesc); call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc){ + if (call->resultdesc) sal_media_description_ref(call->resultdesc); - if (!sal_media_description_empty(call->resultdesc)){ - linphone_connect_incoming(lc,call); + + if (call->resultdesc && !sal_media_description_empty(call->resultdesc)) + { + if (call->state==LinphoneCallPaused && + sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv) && strcmp(call->resultdesc->addr,"0.0.0.0")!=0){ + /*make sure we can be resumed */ + if (lc->current_call!=NULL && lc->current_call!=call){ + ms_warning("Attempt to be resumed but already in call with somebody else!"); + /*we are actively running another call, reject with a busy*/ + sal_call_decline (op,SalReasonBusy,NULL); + return; + } + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We have been resumed...")); + linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); } + else if(call->state==LinphoneCallStreamsRunning && + sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly) && !strcmp(call->resultdesc->addr,"0.0.0.0")){ + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We are being paused...")); + linphone_call_set_state (call,LinphoneCallPaused,"Call paused"); + if (lc->current_call!=call){ + ms_error("Inconsitency detected: current call is %p but call %p is being paused !",lc->current_call,call); + } + lc->current_call=NULL; + } + /*accept the modification (sends a 200Ok)*/ + sal_call_accept(op); + linphone_call_stop_media_streams (call); + linphone_call_init_media_streams (call); + linphone_call_start_media_streams (call); } + if (lc->current_call==NULL) linphone_core_start_pending_refered_calls (lc); } static void call_terminated(SalOp *op, const char *from){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - if (sal_op_get_user_pointer(op)!=lc->call){ + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + + if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){ ms_warning("call_terminated: ignoring."); return; } ms_message("Current call terminated..."); - if (lc->ringstream!=NULL) { + //we stop the call only if we have this current call or if we are in call + if (lc->ringstream!=NULL && ( (ms_list_size(lc->calls) == 1) || linphone_core_in_call(lc) )) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,lc->call); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Call terminated.")); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - if (lc->vtable.bye_recv!=NULL){ - LinphoneAddress *addr=linphone_address_new(from); - char *tmp; - linphone_address_clean(addr); - tmp=linphone_address_as_string(addr); - lc->vtable.bye_recv(lc,tmp); - ms_free(tmp); - linphone_address_destroy(addr); - } - linphone_call_destroy(lc->call); - lc->call=NULL; + linphone_call_stop_media_streams(call); + if (lc->vtable.show!=NULL) + lc->vtable.show(lc); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Call terminated.")); + + linphone_call_set_state(call, LinphoneCallEnd,"Call ended"); } -static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details){ +static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); char *msg486=_("User is busy."); char *msg480=_("User is temporarily unavailable."); @@ -284,12 +337,13 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de char *msg600=_("User does not want to be disturbed."); char *msg603=_("Call declined."); const char *msg=details; - LinphoneCall *call=lc->call; + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - if (sal_op_get_user_pointer(op)!=lc->call){ - ms_warning("call_failure: ignoring."); - return; + if (call==NULL){ + ms_warning("Call faillure reported on already cleaned call ?"); + return ; } + if (lc->vtable.show) lc->vtable.show(lc); if (error==SalErrorNoResponse){ @@ -342,17 +396,15 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de lc->vtable.display_status(lc,_("Call failed.")); } } + if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,call); - if (call!=NULL) { - linphone_call_destroy(call); - if (sr!=SalReasonDeclined) gstate_new_state(lc, GSTATE_CALL_ERROR, msg); - else gstate_new_state(lc, GSTATE_CALL_END, msg); - lc->call=NULL; - } + linphone_call_stop_media_streams (call); + if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg); + else linphone_call_set_state(call,LinphoneCallEnd,"Call declined."); + } static void auth_requested(SalOp *h, const char *realm, const char *username){ @@ -387,42 +439,77 @@ static void register_success(SalOp *op, bool_t registered){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); char *msg; + cfg->registered=registered; - gstate_new_state(lc, GSTATE_REG_OK, NULL); - if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); - else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); - if (lc->vtable.display_status) + linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared , + registered ? "Registration sucessful" : "Unregistration done"); + if (lc->vtable.display_status){ + if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); + else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); lc->vtable.display_status(lc,msg); - ms_free(msg); + ms_free(msg); + } + } static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),(details!=NULL) ? details : _("no response timeout")); - if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); - gstate_new_state(lc, GSTATE_REG_FAILED, msg); - ms_free(msg); + LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); + + if (cfg==NULL){ + ms_warning("Registration failed for unknown proxy config."); + return ; + } + if (details==NULL) + details=_("no response timeout"); + + if (lc->vtable.display_status) { + char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),details ); + lc->vtable.display_status(lc,msg); + ms_free(msg); + } + linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,details); } static void vfu_request(SalOp *op){ #ifdef VIDEO_ENABLED - LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - if (lc->videostream) - video_stream_send_vfu(lc->videostream); + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); + if (call==NULL){ + ms_warning("VFU request but no call !"); + return ; + } + if (call->videostream) + video_stream_send_vfu(call->videostream); #endif } static void dtmf_received(SalOp *op, char dtmf){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, dtmf); + lc->vtable.dtmf_received(lc, call, dtmf); } static void refer_received(Sal *sal, SalOp *op, const char *referto){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); - if (lc->vtable.refer_received){ + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + if (call){ + if (call->refer_to!=NULL){ + ms_free(call->refer_to); + } + call->refer_to=ms_strdup(referto); + call->refer_pending=TRUE; + linphone_call_set_state(call,LinphoneCallRefered,"Refered"); + if (lc->vtable.display_status){ + char *msg=ms_strdup_printf(_("We are transferred to %s"),referto); + lc->vtable.display_status(lc,msg); + ms_free(msg); + } + if (lc->current_call==NULL) linphone_core_start_pending_refered_calls (lc); + sal_refer_accept(op); + }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - if (op) sal_refer_accept(op); + sal_refer_accept(op); } } @@ -433,10 +520,10 @@ static void text_received(Sal *sal, const char *from, const char *msg){ static void notify(SalOp *op, const char *from, const char *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); - + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer (op); ms_message("get a %s notify from %s",msg,from); if(lc->vtable.notify_recv) - lc->vtable.notify_recv(lc,from,msg); + lc->vtable.notify_recv(lc,call,from,msg); } static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){ @@ -464,10 +551,14 @@ static void ping_reply(SalOp *op){ LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); ms_message("ping reply !"); if (call){ - if (call->state==LCStatePreEstablishing){ + if (call->state==LinphoneCallOutgoingInit){ linphone_core_start_invite(call->core,call,NULL); } } + else + { + ms_warning("ping reply without call attached..."); + } } SalCallbacks linphone_sal_callbacks={ @@ -475,7 +566,7 @@ SalCallbacks linphone_sal_callbacks={ call_ringing, call_accepted, call_ack, - call_updated, + call_updating, call_terminated, call_failure, auth_requested, diff --git a/coreapi/chat.c b/coreapi/chat.c index 73a5f7c9a..9ee70482c 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -55,7 +55,7 @@ void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){ if(linphone_core_is_in_communication_with(cr->lc,cr->peer)) { ms_message("send SIP message into the call\n"); - op = cr->lc->call->op; + op = (linphone_core_get_current_call(cr->lc))->op; } else { diff --git a/coreapi/exevents.c b/coreapi/exevents.c deleted file mode 100644 index 24e830905..000000000 --- a/coreapi/exevents.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* -linphone -Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr) - -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. -*/ - -#include "exevents.h" -#include "linphonecore.h" -#include "private.h" -#include "mediastreamer2/mediastream.h" -#include -#include -#include - -static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp); - -static bool_t linphone_call_matches_event(LinphoneCall *call, eXosip_event_t *ev){ - return call->cid==ev->cid; -} - -static void linphone_call_proceeding(LinphoneCore *lc, eXosip_event_t *ev){ - if (lc->call==NULL || (lc->call->cid!=-1 && !linphone_call_matches_event(lc->call,ev)) ) { - ms_warning("This call has been canceled: call=%p, call->cid=%i, ev->cid=%i", - lc->call,lc->call?lc->call->cid:-1,ev->cid); - eXosip_lock(); - eXosip_call_terminate(ev->cid,ev->did); - eXosip_unlock(); - return; - } - lc->call->cid=ev->cid; - lc->call->did=ev->did; - lc->call->tid=ev->tid; -} - -static void linphone_connect_incoming(LinphoneCore *lc){ - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Connected.")); - lc->call->state=LCStateAVRunning; - if (lc->ringstream!=NULL){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - if (lc->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_core_stop_media_streams(lc); - linphone_core_init_media_streams(lc); - } - linphone_core_start_media_streams(lc,lc->call); -} - -int linphone_call_accepted(LinphoneCore *lc, eXosip_event_t *ev) -{ - LinphoneCall *call=lc->call; - sdp_message_t *sdp; - const char *sdpanswer=NULL; - osip_message_t *msg=NULL; - int err; - if (call==NULL){ - ms_warning("No call to accept."); - return 0; - } - linphone_call_proceeding(lc,ev); - if (!linphone_call_matches_event(lc->call,ev)) return 0; - call->auth_pending=FALSE; - if (call->state==LCStateAVRunning){ - return 0; /*already accepted*/ - } - linphone_call_init_media_params(call); - sdp=eXosip_get_sdp_info(ev->response); - if (!lc->sip_conf.sdp_200_ack){ - err=0; - sdp_context_read_answer(call->sdpctx,sdp); - }else{ - /*we receive a 200OK with an sdp offer*/ - err=linphone_answer_sdp(lc,ev,sdp); - if (err==0) sdpanswer=call->sdpctx->answerstr; - } - if (err==0){ - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - linphone_connect_incoming(lc); - } - /*send the ack once streams are started*/ - eXosip_call_build_ack(ev->did,&msg); - if (sdpanswer!=NULL) linphone_set_sdp(msg,sdpanswer); - eXosip_call_send_ack(ev->did,msg); - if (err!=0){ - /*send a bye*/ - ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); - linphone_core_terminate_call(lc,NULL); - } - sdp_message_free(sdp); - return 0; -} - - -int linphone_call_terminated(LinphoneCore *lc, eXosip_event_t *ev) -{ - /*stop ringing if necessary*/ - if (lc->call!=NULL){ - if (lc->call->cid!=ev->cid){ - /* this is not current call */ - ms_message("call %i terminated, this was not current call.",ev->cid); - return 0; - } - } - - ms_message("Current call terminated..."); - if (lc->ringstream!=NULL) { - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - linphone_core_stop_media_streams(lc); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Call terminated.")); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - if (lc->vtable.bye_recv!=NULL){ - char *from; - osip_from_to_str(ev->request->from,&from); - lc->vtable.bye_recv(lc,from); - osip_free(from); - } - if (lc->call!=NULL){ - linphone_call_destroy(lc->call); - lc->call=NULL; - } - return 0; -} - - -int linphone_call_released(LinphoneCore *lc, int cid){ - LinphoneCall *call=lc->call; - if (call!=NULL && call->cid==cid){ - - linphone_call_destroy(lc->call); - lc->call=NULL; - lc->vtable.display_status(lc,_("Could not reach destination.")); - gstate_new_state(lc, GSTATE_CALL_ERROR, NULL); - } - return 0; -} - -int linphone_call_failure(LinphoneCore *lc, eXosip_event_t *ev) -{ - const char *reason=""; - char *msg486=_("User is busy."); - char *msg480=_("User is temporarily unavailable."); - char *msg487=_("Request Cancelled."); - /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ - char *msg600=_("User does not want to be disturbed."); - char *msg603=_("Call declined."); - char* tmpmsg=msg486; - int code; - LinphoneCall *call=lc->call; - - if (call){ - /*check that the faillure is related to this call, not an old one*/ - if (!linphone_call_matches_event(call,ev)) { - ms_warning("Failure reported for an old call."); - return 0; - } - } - - if (ev->response){ - code=osip_message_get_status_code(ev->response); - reason=osip_message_get_reason_phrase(ev->response); - }else code=-110; - lc->vtable.show(lc); - - switch(code) - { - case 401: - case 407: - if (lc->call!=NULL) - linphone_process_authentication(lc,ev); - return 0; - break; - case 400: - lc->vtable.display_status(lc,_("Bad request")); - break; - case 404: - lc->vtable.display_status(lc,_("User cannot be found at given address.")); - break; - case 415: - lc->vtable.display_status(lc,_("Remote user cannot support any of proposed codecs.")); - break; - case 422: - /*ignore: eXosip_automatic_action will do the job of retrying with a greater Session-Expires*/ - return 0; - break; - case 480: - tmpmsg=msg480; - case 486: - /* - msg_header_getbyname(msg,"retry-after",0,&retry); - if (retry!=NULL) - { - umsg=g_malloc(strlen(tmpmsg)+strlen(retrymsg)+13); - sprintf(umsg,retrymsg,tmpmsg,atoi(retry->hvalue)/60); - lc->vtable.display_message(lc,umsg); - ms_free(umsg); - }*/ - lc->vtable.display_message(lc,tmpmsg); - break; - case 487: /*request terminated*/ - lc->vtable.display_status(lc,msg487); - break; - case 600: - lc->vtable.display_message(lc,msg600); - break; - case 603: - lc->vtable.display_status(lc,msg603); - break; - case -110: /* time out, call leg is lost */ - lc->vtable.display_status(lc,_("Timeout.")); - break; - case -111: - lc->vtable.display_status(lc,_("Remote host was found but refused connection.")); - break; - - default: - if (code>0) - { - lc->vtable.display_status(lc,reason); - } - else ms_warning("failure_cb unknown code=%i\n",code); - } - if (lc->ringstream!=NULL) { - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - linphone_core_stop_media_streams(lc); - if (call!=NULL) { - linphone_call_destroy(call); - gstate_new_state(lc, GSTATE_CALL_ERROR, NULL); - lc->call=NULL; - } - return 0; -} - -extern sdp_handler_t linphone_sdphandler; - -static int linphone_answer_sdp(LinphoneCore *lc, eXosip_event_t *ev, sdp_message_t *sdp){ - int status=200; - sdp_context_t *ctx=NULL; - - ctx=lc->call->sdpctx; - /* get the result of the negociation */ - sdp_context_get_answer(ctx,sdp); - status=sdp_context_get_status(ctx); - - if (status==200){ - linphone_core_init_media_streams(lc); - return 0; - }else{ - if (status==-1) status=415; - } - return -1; -} - -int linphone_inc_new_call(LinphoneCore *lc, eXosip_event_t *ev) -{ - sdp_message_t *sdp=NULL; - osip_from_t *from_url=ev->request->from; - char *barmesg; - char *from; - char *to; - int err; - - osip_from_to_str(ev->request->from,&from); - osip_to_to_str(ev->request->to,&to); - - /* first check if we can answer successfully to this invite */ - if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){ - ms_message("Not present !! presence mode : %d\n",lc->presence_mode); - eXosip_lock(); - if (lc->presence_mode==LINPHONE_STATUS_BUSY) - eXosip_call_send_answer(ev->tid,486,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_AWAY - ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK - ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE - ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH - ||lc->presence_mode==LINPHONE_STATUS_OFFLINE) - eXosip_call_send_answer(ev->tid,480,NULL); - else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB) - eXosip_call_send_answer(ev->tid,480,NULL); - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED) - { - osip_message_t *msg; - eXosip_call_build_answer(ev->tid,302,&msg); - osip_message_set_contact(msg,lc->alt_contact); - eXosip_call_send_answer(ev->tid,302,msg); - } - else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_ALT_SERVICE) - { - osip_message_t *msg; - eXosip_call_build_answer(ev->tid,380,&msg); - osip_message_set_contact(msg,lc->alt_contact); - eXosip_call_send_answer(ev->tid,380,msg); - } - else - eXosip_call_send_answer(ev->tid,486,NULL); - eXosip_unlock(); - goto end; - } - if (lc->call!=NULL){/*busy*/ - eXosip_lock(); - eXosip_call_send_answer(ev->tid,486,NULL); - eXosip_unlock(); - goto end; - } - lc->call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),ev); - - sdp=eXosip_get_sdp_info(ev->request); - if (sdp==NULL){ - ms_message("No sdp body in invite, 200-ack scheme"); - err=0; - }else{ - err=linphone_answer_sdp(lc,ev,sdp); - } - if (!err){ - char *tmp; - if (from_2char_without_params(from_url,&tmp)!=0){ - tmp=ms_strdup("Unknown user"); - } - gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp); - barmesg=ortp_strdup_printf("%s %s",tmp,_("is contacting you.")); - lc->vtable.show(lc); - lc->vtable.display_status(lc,barmesg); - - /* play the ring */ - if (lc->sound_conf.ring_sndcard!=NULL){ - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard); - } - linphone_call_set_state(lc->call,LCStateRinging); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,180,NULL); - eXosip_unlock(); - - lc->vtable.inv_recv(lc,tmp); - ms_free(barmesg); - osip_free(tmp); - }else{ - ms_error("Error during sdp negociation. "); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,415,NULL); - eXosip_unlock(); - linphone_call_destroy(lc->call); - lc->call=NULL; - } - end: - osip_free(from); - osip_free(to); - if (sdp) sdp_message_free(sdp); - return 0; -} - -void linphone_handle_ack(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->ack); - if (sdp){ - sdp_context_read_answer(lc->call->sdpctx,sdp); - linphone_connect_incoming(lc); - sdp_message_free(sdp); - } -} - -void linphone_handle_reinvite(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->request); - sdp_context_t *ctx; - LinphoneCall *call=lc->call; - char *answer; - int status; - if (sdp==NULL){ - ms_warning("No sdp in reinvite !"); - eXosip_lock(); - eXosip_call_send_answer(ev->tid,603,NULL); - eXosip_unlock(); - return; - } - ctx=call->sdpctx; - /* get the result of the negociation */ - linphone_call_init_media_params(call); - answer=sdp_context_get_answer(ctx,sdp); - status=sdp_context_get_status(ctx); - if (status==200){ - osip_message_t *msg=NULL; - linphone_core_stop_media_streams(lc); - linphone_core_init_media_streams(lc); - eXosip_lock(); - if (eXosip_call_build_answer(ev->tid,200,&msg)<0){ - ms_warning("Reinvite for closed call ?"); - eXosip_unlock(); - linphone_core_stop_media_streams(lc); - sdp_message_free(sdp); - return ; - } - answer=call->sdpctx->answerstr; /* takes the sdp already computed*/ - linphone_set_sdp(msg,answer); - eXosip_call_send_answer(ev->tid,200,msg); - eXosip_unlock(); - linphone_core_start_media_streams(lc,call); - }else{ - eXosip_lock(); - eXosip_call_send_answer(ev->tid,status,NULL); - eXosip_unlock(); - } - sdp_message_free(sdp); -} - -void linphone_do_automatic_redirect(LinphoneCore *lc, const char *contact){ - char *msg=ortp_strdup_printf(_("Redirected to %s..."),contact); - lc->vtable.display_status(lc,msg); - ms_free(msg); - if (lc->call!=NULL) linphone_call_destroy(lc->call); - lc->call=NULL; - linphone_core_invite(lc,contact); -} - -void linphone_call_redirected(LinphoneCore *lc, eXosip_event_t *ev){ - int code=osip_message_get_status_code(ev->response); - char *contact=NULL; - osip_contact_t *ct; - osip_message_get_contact(ev->response,0,&ct); - if (ct) osip_contact_to_str(ct,&contact); - switch(code){ - case 380: - lc->vtable.display_url(lc,_("User is not reachable at the moment but he invites you\nto contact him using the following alternate resource:"),contact); - if (lc->call!=NULL) linphone_call_destroy(lc->call); - lc->call=NULL; - break; - case 302: - linphone_do_automatic_redirect(lc,contact); - break; - } - if (contact) osip_free(contact); -} - - -/* these are the SdpHandler callbacks: we are called in to be aware of the content -of the SDP messages exchanged */ - -int linphone_set_audio_offer(sdp_context_t *ctx) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *codec; - MSList *elem; - sdp_payload_t payload; - - - elem=lc->codecs_conf.audio_codecs; - while(elem!=NULL){ - codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && - linphone_core_payload_type_enabled(lc,codec)){ - sdp_payload_init(&payload); - payload.a_rtpmap=ortp_strdup_printf("%s/%i/1",codec->mime_type,codec->clock_rate); - payload.pt=rtp_profile_get_payload_number_from_rtpmap(lc->local_profile,payload.a_rtpmap); - payload.localport=call->audio_params.natd_port > 0 ? - call->audio_params.natd_port : lc->rtp_conf.audio_rtp_port; - sdp_context_add_audio_payload(ctx,&payload); - ms_free(payload.a_rtpmap); - } - elem=ms_list_next(elem); - } - /* add telephone-event payload*/ - sdp_payload_init(&payload); - payload.pt=rtp_profile_get_payload_number_from_mime(lc->local_profile,"telephone-event"); - payload.a_rtpmap="telephone-event/8000"; - payload.a_fmtp="0-11"; - if (lc->dw_audio_bw>0) payload.b_as_bandwidth=lc->dw_audio_bw; - if (lc->down_ptime>0) { - payload.a_ptime=lc->down_ptime; - ms_message("ptime [%i]",payload.a_ptime); - } - sdp_context_add_audio_payload(ctx,&payload); - return 0; -} - -static int find_payload_type_number(RtpProfile *prof, PayloadType *pt){ - int candidate=-1,i; - PayloadType *it; - for(i=0;i<127;++i){ - it=rtp_profile_get_payload(prof,i); - if (it!=NULL && strcasecmp(pt->mime_type,it->mime_type)==0 - && (pt->clock_rate==it->clock_rate || pt->clock_rate<=0) ){ - if ( (pt->recv_fmtp && it->recv_fmtp && strcasecmp(pt->recv_fmtp,it->recv_fmtp)==0) || - (pt->recv_fmtp==NULL && it->recv_fmtp==NULL) ){ - /*exact match*/ - return i; - }else candidate=i; - } - } - if (candidate==-1) ms_fatal("Should not happen."); - return candidate; -} - -static int find_payload_type_number_best_match(RtpProfile *prof, const char *rtpmap, const char *fmtp){ - int localpt=rtp_profile_get_payload_number_from_rtpmap(prof,rtpmap); - PayloadType *pt; - char value[10]; - if (localpt<0) return -1; - pt=rtp_profile_get_payload(prof,localpt); - if (strcasecmp(pt->mime_type,"H264")==0){ - /*hack for H264: need to answer with same packetization-mode*/ - PayloadType tmp; - memset(&tmp,0,sizeof(tmp)); - tmp.mime_type="H264"; - tmp.clock_rate=pt->clock_rate; - if (fmtp && fmtp_get_value(fmtp,"packetization-mode",value,sizeof(value))){ - tmp.recv_fmtp=(atoi(value)==1) ? "packetization-mode=1" : NULL; - } - localpt=find_payload_type_number(prof,&tmp); - } - return localpt; -} - -int linphone_set_video_offer(sdp_context_t *ctx) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *codec; - MSList *elem; - bool_t firsttime=TRUE; - - if (!linphone_core_video_enabled(lc)) return -1; - - for(elem=lc->codecs_conf.video_codecs;elem!=NULL;elem=ms_list_next(elem)){ - codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && - linphone_core_payload_type_enabled(lc,codec)){ - sdp_payload_t payload; - sdp_payload_init(&payload); - payload.line=1; - payload.a_rtpmap=ortp_strdup_printf("%s/%i",codec->mime_type,codec->clock_rate); - payload.localport=call->video_params.natd_port>0 ? - call->video_params.natd_port : lc->rtp_conf.video_rtp_port; - payload.pt=find_payload_type_number(lc->local_profile,codec); - payload.a_fmtp=codec->recv_fmtp; - if(firsttime){ - firsttime=FALSE; - if (lc->dw_video_bw>0) - payload.b_as_bandwidth=lc->dw_video_bw; - } - sdp_context_add_video_payload(ctx,&payload); - ms_free(payload.a_rtpmap); - } - } - return 0; -} - -typedef enum { - Unsupported, - Supported, - SupportedAndValid /* valid= the presence of this codec is enough to make a call */ -}SupportLevel; - -SupportLevel linphone_payload_is_supported(LinphoneCore *lc, sdp_payload_t *payload,RtpProfile *local_profile,RtpProfile *dialog_profile, bool_t answering, PayloadType **local_payload_type) -{ - int localpt; - SupportLevel ret; - if (payload->a_rtpmap!=NULL){ - localpt=find_payload_type_number_best_match(local_profile,payload->a_rtpmap,payload->a_fmtp); - }else{ - localpt=payload->pt; - ms_warning("payload has no rtpmap."); - } - - if (localpt>=0 && localpt <128 ){ - /* this payload is understood, but does the user want to use it ?? */ - PayloadType *rtppayload; - rtppayload=rtp_profile_get_payload(local_profile,localpt); - if (rtppayload==NULL) { - ms_warning("strange error !!"); - return Unsupported; - } - *local_payload_type=rtppayload; - if (strcmp(rtppayload->mime_type,"telephone-event")!=0){ - if (answering && !linphone_core_check_payload_type_usability(lc,rtppayload) ){ - ms_warning("payload %s is not usable",rtppayload->mime_type); - return Unsupported; - } - if ( !linphone_core_payload_type_enabled(lc,rtppayload)) { - ms_warning("payload %s is not enabled.",rtppayload->mime_type); - return Unsupported; - } - ret=SupportedAndValid; - }else ret=Supported; - if (dialog_profile!=NULL){ - int dbw,ubw; - /* this payload is supported in our local rtp profile, so add it to the dialog rtp - profile */ - rtppayload=payload_type_clone(rtppayload); - if (rtp_profile_get_payload(dialog_profile,payload->pt)!=NULL){ - ms_error("Payload %s type already entered, should not happen !",rtppayload->mime_type); - } - rtp_profile_set_payload(dialog_profile,payload->pt,rtppayload); - /* add to the rtp payload type some other parameters (bandwidth) */ - if (rtppayload->type==PAYLOAD_VIDEO){ - dbw=lc->dw_video_bw; - ubw=lc->up_video_bw; - }else{ - dbw=lc->dw_audio_bw; - ubw=lc->up_audio_bw; - } - if (payload->b_as_bandwidth!=0){ - ms_message("Remote bandwidth constraint: %i",payload->b_as_bandwidth); - /*obey to remote bandwidth constraint AND our own upbandwidth constraint*/ - rtppayload->normal_bitrate=1000*get_min_bandwidth( - payload->b_as_bandwidth, ubw); - }else{ - /*limit to upload bandwidth if exist, else no limit*/ - if (ubw>0) rtppayload->normal_bitrate=1000*ubw; - else { - if (rtppayload->type!=PAYLOAD_VIDEO){ - rtppayload->normal_bitrate=-1; /*allow speex to use maximum bitrate*/ - } - } - } - if (payload->a_fmtp!=NULL){ - payload_type_set_send_fmtp(rtppayload,payload->a_fmtp); - } - payload->a_fmtp=rtppayload->recv_fmtp; - if (payload->a_ptime>0){ - char tmp[30]; - snprintf(tmp,sizeof(tmp),"ptime=%i",payload->a_ptime); - payload_type_append_send_fmtp(rtppayload,tmp); - ms_message("%s attribute added to fmtp",tmp); - } - } - return ret; - } - return Unsupported; -} - -int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - RtpProfile *remote_profile; - StreamParams *params; - SupportLevel supported; - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - PayloadType *lpt=NULL; - - params=&call->audio_params; - remote_profile=call->profile; - /* see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt); - if (supported==Unsupported) { - ms_message("Refusing audio codec %i (%s)",payload->pt,payload->a_rtpmap); - return -1; - } - if (lc->sip_conf.only_one_codec && params->initialized){ - ms_message("Only one codec has to be accepted."); - return -1; - } - if (supported==SupportedAndValid) { - if (params->initialized==0){ - /* this is the first codec we accept, it is going to be used*/ - params->localport=lc->rtp_conf.audio_rtp_port; - payload->localport=params->natd_port>0 ? - params->natd_port : lc->rtp_conf.audio_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - /* we can now update the allocated bandwidth for audio, and then video*/ - linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt); - /* give our download bandwidth constraint*/ - payload->b_as_bandwidth=(lc->dw_audio_bw>0) ? lc->dw_audio_bw : 0; - }else{ - /* refuse all other audio lines*/ - if(params->line!=payload->line) { - ms_message("Only one audio line can be accepted."); -#if !defined(_WIN32_WCE) - abort(); -#endif /*_WIN32_WCE*/ - return -1; - } - } - } - return 0; -} - -int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - RtpProfile *remote_profile; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - if (!linphone_core_video_enabled(lc)) return -1; - - if (payload->remoteport==0) { - ms_message("Video stream refused by remote."); - return 0; - } - - params=&call->video_params; - remote_profile=call->profile; - /* see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc,payload,lc->local_profile,remote_profile,TRUE,&lpt); - if (supported==Unsupported) { - ms_message("Refusing video codec %i (%s)",payload->pt,payload->a_rtpmap); - return -1; - } - if (lc->sip_conf.only_one_codec && params->initialized){ - return -1; - } - if (supported==SupportedAndValid){ - if (params->initialized==0){ - /* this is the first codec we may accept*/ - params->localport=lc->rtp_conf.video_rtp_port; - payload->localport=params->natd_port>0 ? params->natd_port : lc->rtp_conf.video_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=params->remoteport+1; - } - params->initialized=1; - payload->b_as_bandwidth=(lc->dw_video_bw>0) ? lc->dw_video_bw : 0; - }else{ - /* refuse all other video lines*/ - if(params->line!=payload->line) return -1; - } - } - return 0; -} - -int linphone_read_audio_answer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - /* paranoid check: see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt); - if (supported==Unsupported) { - ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap); - return 0; - } - if (supported==SupportedAndValid){ - params=&call->audio_params; - if (params->initialized==0){ - /* this is the first codec we accept, this is the one that is going to be used (at least for sending - data.*/ - params->localport=lc->rtp_conf.audio_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - /* we can now update the allocated bandwidth for audio, and then video*/ - linphone_core_update_allocated_audio_bandwidth_in_call(lc,lpt); - } - } - return 0; -} - -int linphone_read_video_answer(sdp_context_t *ctx,sdp_payload_t *payload) -{ - LinphoneCall *call=(LinphoneCall*)sdp_context_get_user_pointer(ctx); - LinphoneCore *lc=call->core; - StreamParams *params; - SupportLevel supported; - PayloadType *lpt=NULL; - - /* paranoid check: see if this codec is supported in our local rtp profile*/ - supported=linphone_payload_is_supported(lc, payload,lc->local_profile,call->profile,FALSE,&lpt); - if (supported==Unsupported) { - ms_warning("This remote sip phone did not answer properly to my sdp offer: rtpmap=%s",payload->a_rtpmap); - return 0; - } - if (supported==SupportedAndValid){ - params=&call->video_params; - if (params->initialized==0){ - /* this is the first codec we may accept*/ - params->localport=lc->rtp_conf.video_rtp_port; - params->line=payload->line; - params->pt=payload->pt; /* remember the first payload accepted */ - if (payload->relay_host!=NULL){ - strncpy(params->remoteaddr,payload->relay_host,sizeof(params->remoteaddr)-1); - params->remoteport=payload->relay_port; - params->remotertcpport=payload->relay_port; - params->relay_session_id=payload->relay_session_id; - }else{ - strncpy(params->remoteaddr,payload->c_addr,sizeof(params->remoteaddr)-1); - params->remoteport=payload->remoteport; - params->remotertcpport=payload->remoteport+1; - } - params->initialized=1; - } - } - return 0; -} - -void linphone_call_ringing(LinphoneCore *lc, eXosip_event_t *ev){ - sdp_message_t *sdp=eXosip_get_sdp_info(ev->response); - LinphoneCall *call=lc->call; - - lc->vtable.display_status(lc,_("Remote ringing.")); - linphone_call_proceeding(lc,ev); - if (call==NULL) return; - if (sdp==NULL){ - if (lc->ringstream!=NULL) return; /*already ringing !*/ - if (lc->sound_conf.play_sndcard!=NULL){ - ms_message("Remote ringing..."); - lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); - } - }else{ - /*accept early media */ - StreamParams *audio_params; - if (call==NULL){ - ms_error("No call ?"); - goto end; - } - if (lc->audiostream->ticker!=NULL){ - /*streams already started */ - ms_message("Early media already started."); - goto end; - } - audio_params=&call->audio_params; - sdp_context_read_answer(lc->call->sdpctx,sdp); - lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Early media.")); - gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); - if (lc->ringstream!=NULL){ - ring_stop(lc->ringstream); - lc->ringstream=NULL; - } - ms_message("Doing early media..."); - linphone_core_start_media_streams(lc,call); - } - call->state=LCStateRinging; - goto end; - end: - sdp_message_free(sdp); - -} - -static void linphone_process_media_control_xml(LinphoneCore *lc, eXosip_event_t *ev){ - osip_body_t *body=NULL; - osip_message_get_body(ev->request,0,&body); - if (body && body->body!=NULL && - strstr(body->body,"picture_fast_update")){ - osip_message_t *ans=NULL; - ms_message("Receiving VFU request !"); -#ifdef VIDEO_ENABLED - if (lc->videostream) - video_stream_send_vfu(lc->videostream); -#endif - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } -} - -static void linphone_process_dtmf_relay(LinphoneCore *lc, eXosip_event_t *ev){ - osip_body_t *body=NULL; - osip_message_get_body(ev->request,0,&body); - if (body && body->body!=NULL){ - osip_message_t *ans=NULL; - const char *name=strstr(body->body,"Signal"); - if (name==NULL) name=strstr(body->body,"signal"); - if (name==NULL) { - ms_warning("Could not extract the dtmf name from the SIP INFO."); - }else{ - char tmp[2]; - name+=strlen("signal"); - if (sscanf(name," = %1s",tmp)==1){ - ms_message("Receiving dtmf %s via SIP INFO.",tmp); - if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, tmp[0]); - } - } - - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } -} - -void linphone_call_message_new(LinphoneCore *lc, eXosip_event_t *ev){ - osip_message_t *ans=NULL; - if (ev->request){ - if (MSG_IS_INFO(ev->request)){ - osip_content_type_t *ct; - ct=osip_message_get_content_type(ev->request); - if (ct && ct->subtype){ - if (strcmp(ct->subtype,"media_control+xml")==0) - linphone_process_media_control_xml(lc,ev); - else if (strcmp(ct->subtype,"dtmf-relay")==0) - linphone_process_dtmf_relay(lc,ev); - else { - ms_message("Unhandled SIP INFO."); - /*send an "Not implemented" answer*/ - eXosip_call_build_answer(ev->tid,501,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,501,ans); - } - }else{ - /*empty SIP INFO, probably to test we are alive. Send an empty answer*/ - eXosip_call_build_answer(ev->tid,200,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,200,ans); - } - } - }else ms_warning("linphone_call_message_new: No request ?"); -} - -void linphone_registration_faillure(LinphoneCore *lc, eXosip_event_t *ev){ - int status_code=0; - char *msg; - const char *reason=NULL; - osip_uri_t *requri=osip_message_get_uri(ev->request); - char *ru; - LinphoneProxyConfig *cfg; - - if (ev->response){ - status_code=osip_message_get_status_code(ev->response); - reason=osip_message_get_reason_phrase(ev->response); - } - switch(status_code){ - case 401: - case 407: - linphone_process_authentication(lc,ev); - break; - default: - cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid); - /* if contact is up to date, process the failure, otherwise resend a new register with - updated contact first, just in case the faillure is due to incorrect contact */ - if (linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response)) - return; /*we are retrying with an updated contact*/ - linphone_proxy_config_process_authentication_failure(lc,status_code,ev); - osip_uri_to_str(requri,&ru); - msg=ortp_strdup_printf(_("Registration on %s failed: %s"),ru,(reason!=NULL) ? reason : _("no response timeout")); - lc->vtable.display_status(lc,msg); - gstate_new_state(lc, GSTATE_REG_FAILED, msg); - ms_free(msg); - osip_free(ru); - } -} - -void linphone_registration_success(LinphoneCore *lc,eXosip_event_t *ev){ - LinphoneProxyConfig *cfg; - osip_uri_t *requri=osip_message_get_uri(ev->request); - char *msg; - char *ru; - osip_header_t *h=NULL; - - cfg=linphone_core_get_proxy_config_from_rid(lc,ev->rid); - ms_return_if_fail(cfg!=NULL); - - osip_message_get_expires(ev->request,0,&h); - if (h!=NULL && atoi(h->hvalue)!=0){ - cfg->registered=TRUE; - linphone_proxy_config_register_again_with_updated_contact(cfg,ev->request,ev->response); - }else cfg->registered=FALSE; - - gstate_new_state(lc, GSTATE_REG_OK, NULL); - - osip_uri_to_str(requri,&ru); - if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),ru); - else msg=ms_strdup_printf(_("Unregistration on %s done."),ru); - lc->vtable.display_status(lc,msg); - ms_free(msg); - osip_free(ru); -} - -static bool_t comes_from_local_if(osip_message_t *msg){ - osip_via_t *via=NULL; - osip_message_get_via(msg,0,&via); - if (via){ - const char *host; - host=osip_via_get_host(via); - if (strcmp(host,"127.0.0.1")==0 || strcmp(host,"::1")==0){ - osip_generic_param_t *param=NULL; - osip_via_param_get_byname(via,"received",¶m); - if (param==NULL) return TRUE; - if (param->gvalue && - (strcmp(param->gvalue,"127.0.0.1")==0 || strcmp(param->gvalue,"::1")==0)){ - return TRUE; - } - } - } - return FALSE; -} - -static void linphone_inc_update(LinphoneCore *lc, eXosip_event_t *ev){ - osip_message_t *msg=NULL; - ms_message("Processing incoming UPDATE"); - eXosip_lock(); - eXosip_message_build_answer(ev->tid,200,&msg); - if (msg!=NULL) - eXosip_message_send_answer(ev->tid,200,msg); - eXosip_unlock(); -} - -static void linphone_other_request(LinphoneCore *lc, eXosip_event_t *ev){ - ms_message("in linphone_other_request"); - if (ev->request==NULL) return; - if (strcmp(ev->request->sip_method,"MESSAGE")==0){ - linphone_core_text_received(lc,ev); - eXosip_message_send_answer(ev->tid,200,NULL); - }else if (strcmp(ev->request->sip_method,"OPTIONS")==0){ - osip_message_t *options=NULL; - eXosip_options_build_answer(ev->tid,200,&options); - osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); - osip_message_set_accept(options,"application/sdp"); - eXosip_options_send_answer(ev->tid,200,options); - }else if (strcmp(ev->request->sip_method,"WAKEUP")==0 - && comes_from_local_if(ev->request)) { - eXosip_message_send_answer(ev->tid,200,NULL); - ms_message("Receiving WAKEUP request !"); - if (lc->vtable.show) - lc->vtable.show(lc); - }else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){ - ms_message("Receiving REFER request !"); - if (comes_from_local_if(ev->request)) { - osip_header_t *h=NULL; - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_message_send_answer(ev->tid,200,NULL); - if (h){ - if (lc->vtable.refer_received) - lc->vtable.refer_received(lc,h->hvalue); - } - - }else ms_warning("Ignored REFER not coming from this local loopback interface."); - }else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){ - linphone_inc_update(lc,ev); - }else { - char *tmp=NULL; - size_t msglen=0; - osip_message_to_str(ev->request,&tmp,&msglen); - if (tmp){ - ms_message("Unsupported request received:\n%s",tmp); - osip_free(tmp); - } - /*answer with a 501 Not implemented*/ - eXosip_message_send_answer(ev->tid,501,NULL); - } -} - -void linphone_core_process_event(LinphoneCore *lc,eXosip_event_t *ev) -{ - switch(ev->type){ - case EXOSIP_CALL_ANSWERED: - ms_message("CALL_ANSWERED\n"); - linphone_call_accepted(lc,ev); - linphone_authentication_ok(lc,ev); - break; - case EXOSIP_CALL_CLOSED: - case EXOSIP_CALL_CANCELLED: - ms_message("CALL_CLOSED or CANCELLED\n"); - linphone_call_terminated(lc,ev); - break; - case EXOSIP_CALL_TIMEOUT: - case EXOSIP_CALL_NOANSWER: - ms_message("CALL_TIMEOUT or NOANSWER\n"); - linphone_call_failure(lc,ev); - break; - case EXOSIP_CALL_REQUESTFAILURE: - case EXOSIP_CALL_GLOBALFAILURE: - case EXOSIP_CALL_SERVERFAILURE: - ms_message("CALL_REQUESTFAILURE or GLOBALFAILURE or SERVERFAILURE\n"); - linphone_call_failure(lc,ev); - break; - case EXOSIP_CALL_INVITE: - ms_message("CALL_NEW\n"); - /* CALL_NEW is used twice in qos mode : - * when you receive invite (textinfo = "With QoS" or "Without QoS") - * and when you receive update (textinfo = "New Call") */ - linphone_inc_new_call(lc,ev); - break; - case EXOSIP_CALL_REINVITE: - linphone_handle_reinvite(lc,ev); - break; - case EXOSIP_CALL_ACK: - ms_message("CALL_ACK"); - linphone_handle_ack(lc,ev); - break; - case EXOSIP_CALL_REDIRECTED: - ms_message("CALL_REDIRECTED"); - linphone_call_redirected(lc,ev); - break; - case EXOSIP_CALL_PROCEEDING: - ms_message("CALL_PROCEEDING"); - linphone_call_proceeding(lc,ev); - break; - case EXOSIP_CALL_RINGING: - ms_message("CALL_RINGING"); - linphone_call_ringing(lc,ev); - break; - case EXOSIP_CALL_MESSAGE_NEW: - ms_message("EXOSIP_CALL_MESSAGE_NEW"); - linphone_call_message_new(lc,ev); - break; - case EXOSIP_CALL_MESSAGE_REQUESTFAILURE: - if (ev->did<0 && ev->response && - (ev->response->status_code==407 || ev->response->status_code==401)){ - eXosip_default_action(ev); - } - break; - case EXOSIP_IN_SUBSCRIPTION_NEW: - ms_message("CALL_SUBSCRIPTION_NEW or UPDATE"); - linphone_subscription_new(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_UPDATE: - break; - case EXOSIP_SUBSCRIPTION_NOTIFY: - ms_message("CALL_SUBSCRIPTION_NOTIFY"); - linphone_notify_recv(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_ANSWERED: - ms_message("EXOSIP_SUBSCRIPTION_ANSWERED, ev->sid=%i\n",ev->sid); - linphone_subscription_answered(lc,ev); - break; - case EXOSIP_SUBSCRIPTION_CLOSED: - ms_message("EXOSIP_SUBSCRIPTION_CLOSED\n"); - linphone_subscription_closed(lc,ev); - break; - case EXOSIP_CALL_RELEASED: - ms_message("CALL_RELEASED\n"); - linphone_call_released(lc, ev->cid); - break; - case EXOSIP_REGISTRATION_FAILURE: - ms_message("REGISTRATION_FAILURE\n"); - linphone_registration_faillure(lc,ev); - break; - case EXOSIP_REGISTRATION_SUCCESS: - linphone_authentication_ok(lc,ev); - linphone_registration_success(lc,ev); - break; - case EXOSIP_MESSAGE_NEW: - linphone_other_request(lc,ev); - break; - case EXOSIP_MESSAGE_REQUESTFAILURE: - if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){ - /*the user is expected to have registered to the proxy, thus password is known*/ - eXosip_default_action(ev); - } - break; - default: - ms_message("Unhandled exosip event !"); - break; - } - eXosip_event_free(ev); -} diff --git a/coreapi/friend.c b/coreapi/friend.c index 7ba243741..1af7a4cb5 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -29,37 +29,37 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ const char *str=NULL; switch(ss){ - case LINPHONE_STATUS_ONLINE: + case LinphoneStatusOnline: str=_("Online"); break; - case LINPHONE_STATUS_BUSY: + case LinphoneStatusBusy: str=_("Busy"); break; - case LINPHONE_STATUS_BERIGHTBACK: + case LinphoneStatusBeRightBack: str=_("Be right back"); break; - case LINPHONE_STATUS_AWAY: + case LinphoneStatusAway: str=_("Away"); break; - case LINPHONE_STATUS_ONTHEPHONE: + case LinphoneStatusOnThePhone: str=_("On the phone"); break; - case LINPHONE_STATUS_OUTTOLUNCH: + case LinphoneStatusOutToLunch: str=_("Out to lunch"); break; - case LINPHONE_STATUS_NOT_DISTURB: + case LinphoneStatusDoNotDisturb: str=_("Do not disturb"); break; - case LINPHONE_STATUS_MOVED: + case LinphoneStatusMoved: str=_("Moved"); break; - case LINPHONE_STATUS_ALT_SERVICE: + case LinphoneStatusAltService: str=_("Using another messaging service"); break; - case LINPHONE_STATUS_OFFLINE: + case LinphoneStatusOffline: str=_("Offline"); break; - case LINPHONE_STATUS_PENDING: + case LinphoneStatusPending: str=_("Pending"); break; default: @@ -138,7 +138,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ }else from=linphone_core_get_primary_contact(fr->lc); if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ - fr->status=LINPHONE_STATUS_OFFLINE; + fr->status=LinphoneStatusOffline; /* if (fr->lc->vtable.notify_recv) fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); @@ -157,7 +157,7 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ LinphoneFriend * linphone_friend_new(){ LinphoneFriend *obj=ms_new0(LinphoneFriend,1); obj->pol=LinphoneSPAccept; - obj->status=LINPHONE_STATUS_OFFLINE; + obj->status=LinphoneStatusOffline; obj->subscribe=TRUE; return obj; } @@ -243,37 +243,37 @@ int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscri SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os){ switch(os){ - case LINPHONE_STATUS_OFFLINE: + case LinphoneStatusOffline: return SalPresenceOffline; break; - case LINPHONE_STATUS_ONLINE: + case LinphoneStatusOnline: return SalPresenceOnline; break; - case LINPHONE_STATUS_BUSY: + case LinphoneStatusBusy: return SalPresenceBusy; break; - case LINPHONE_STATUS_BERIGHTBACK: + case LinphoneStatusBeRightBack: return SalPresenceBerightback; break; - case LINPHONE_STATUS_AWAY: + case LinphoneStatusAway: return SalPresenceAway; break; - case LINPHONE_STATUS_ONTHEPHONE: + case LinphoneStatusOnThePhone: return SalPresenceOnthephone; break; - case LINPHONE_STATUS_OUTTOLUNCH: + case LinphoneStatusOutToLunch: return SalPresenceOuttolunch; break; - case LINPHONE_STATUS_NOT_DISTURB: + case LinphoneStatusDoNotDisturb: return SalPresenceDonotdisturb; break; - case LINPHONE_STATUS_MOVED: + case LinphoneStatusMoved: return SalPresenceMoved; break; - case LINPHONE_STATUS_ALT_SERVICE: + case LinphoneStatusAltService: return SalPresenceAltService; break; - case LINPHONE_STATUS_PENDING: + case LinphoneStatusPending: return SalPresenceOffline; break; default: @@ -347,7 +347,7 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ if (fr->inc_subscribe_pending){ switch(fr->pol){ case LinphoneSPWait: - linphone_friend_notify(fr,LINPHONE_STATUS_PENDING); + linphone_friend_notify(fr,LinphoneStatusPending); break; case LinphoneSPAccept: if (fr->lc!=NULL) @@ -356,7 +356,7 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ } break; case LinphoneSPDeny: - linphone_friend_notify(fr,LINPHONE_STATUS_OFFLINE); + linphone_friend_notify(fr,LinphoneStatusOffline); break; } fr->inc_subscribe_pending=FALSE; diff --git a/coreapi/general_state.c b/coreapi/general_state.c index 210037887..dc9e3e642 100644 --- a/coreapi/general_state.c +++ b/coreapi/general_state.c @@ -83,35 +83,36 @@ static void linphone_core_set_state(LinphoneCore *lc, gstate_group_t group, gsta void gstate_new_state(struct _LinphoneCore *lc, gstate_t new_state, + LinphoneGeneralStateContext gctx, const char *message) { - LinphoneGeneralState states_arg; - - /* determine the affected group */ - if (new_state < GSTATE_REG_NONE) - states_arg.group = GSTATE_GROUP_POWER; - else if (new_state < GSTATE_CALL_IDLE) - states_arg.group = GSTATE_GROUP_REG; - else - states_arg.group = GSTATE_GROUP_CALL; - - /* store the new state while remembering the old one */ - states_arg.new_state = new_state; - states_arg.old_state = linphone_core_get_state(lc,states_arg.group); - linphone_core_set_state(lc, states_arg.group,new_state); - states_arg.message = message; - - /*printf("gstate_new_state: %s\t-> %s\t(%s)\n", - _gstates_text[states_arg.old_state], - _gstates_text[states_arg.new_state], - message);*/ + LinphoneGeneralState states_arg; - /* call the virtual method */ - if (lc->vtable.general_state) - lc->vtable.general_state(lc, &states_arg); - - /* immediately proceed to idle state */ - if (new_state == GSTATE_CALL_END || - new_state == GSTATE_CALL_ERROR) - gstate_new_state(lc, GSTATE_CALL_IDLE, NULL); + /* determine the affected group */ + if (new_state < GSTATE_REG_NONE) + states_arg.group = GSTATE_GROUP_POWER; + else if (new_state < GSTATE_CALL_IDLE) + states_arg.group = GSTATE_GROUP_REG; + else + states_arg.group = GSTATE_GROUP_CALL; + + /* store the new state while remembering the old one */ + states_arg.new_state = new_state; + states_arg.old_state = linphone_core_get_state(lc,states_arg.group); + linphone_core_set_state(lc, states_arg.group,new_state); + states_arg.message = message; + + /*printf("gstate_new_state: %s\t-> %s\t(%s)\n", + _gstates_text[states_arg.old_state], + _gstates_text[states_arg.new_state], + message);*/ + + /* call the virtual method */ + if (lc->vtable.general_state) + lc->vtable.general_state(lc, &states_arg, gctx); + + /* immediately proceed to idle state */ + if (new_state == GSTATE_CALL_END || + new_state == GSTATE_CALL_ERROR) + gstate_new_state(lc, GSTATE_CALL_IDLE, gctx, NULL); } diff --git a/coreapi/help/Doxyfile.in b/coreapi/help/Doxyfile.in index abb4aef5b..b1ef17a4e 100644 --- a/coreapi/help/Doxyfile.in +++ b/coreapi/help/Doxyfile.in @@ -89,7 +89,7 @@ RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = -EXAMPLE_PATH = ../ +EXAMPLE_PATH = ../../ . EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = diff --git a/coreapi/help/Makefile.am b/coreapi/help/Makefile.am index 0b12592f4..ae5e2ed25 100644 --- a/coreapi/help/Makefile.am +++ b/coreapi/help/Makefile.am @@ -31,3 +31,22 @@ endif 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) + diff --git a/coreapi/help/doxygen.dox.in b/coreapi/help/doxygen.dox.in index 13502d424..cb05e05d7 100644 --- a/coreapi/help/doxygen.dox.in +++ b/coreapi/help/doxygen.dox.in @@ -29,64 +29,11 @@ */ /** - * @defgroup tutorial_liblinphone Tutorial: Placing and receiving calls with liblinphone - * - -

Initialize liblinphone

- -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: - -
-	##include 
-
-	//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;
-		
-	}
-
-
- - - -/** - * @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 **/ /** @@ -109,6 +56,7 @@ The following code shows how initialize liblinphone: * @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). @@ -117,3 +65,14 @@ The following code shows how initialize liblinphone: /** * @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 + * +**/ + + + diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c new file mode 100644 index 000000000..70023ac8d --- /dev/null +++ b/coreapi/linphonecall.c @@ -0,0 +1,723 @@ + +/* +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 +#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; + } +} + + diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 83b69c943..af13e57a0 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -21,14 +21,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sipsetup.h" #include "lpconfig.h" #include "private.h" + +#include #include "mediastreamer2/mediastream.h" +#include "mediastreamer2/mseventqueue.h" #include "mediastreamer2/msvolume.h" #include "mediastreamer2/msequalizer.h" #include "mediastreamer2/dtmfgen.h" -#include - - #ifdef INET6 #ifndef WIN32 #include @@ -63,151 +63,7 @@ int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){ return 0; } - -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, - const char *localip, const char *username, bool_t only_one_codec){ - MSList *l; - PayloadType *pt; - SalMediaDescription *md=sal_media_description_new(); - md->nstreams=1; - strncpy(md->addr,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,localip,sizeof(md->streams[0].addr)); - md->streams[0].port=linphone_core_get_audio_port(lc); - 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=linphone_core_get_video_port(lc); - 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 void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ - call->state=LCStateInit; - 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,LINPHONE_STATUS_ONTHEPHONE); - if (linphone_core_get_firewall_policy(call->core)==LINPHONE_POLICY_USE_STUN) - linphone_core_run_stun_tests(call->core,call); -} - -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); - call->localdesc=create_local_media_description (lc,call->localip, - linphone_address_get_username(from),FALSE); - linphone_call_init_common(call,from,to); - 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); - call->localdesc=create_local_media_description (lc,call->localip, - linphone_address_get_username(me),lc->sip_conf.only_one_codec); - linphone_call_init_common(call, from, to); - discover_mtu(lc,linphone_address_get_domain(from)); - linphone_address_destroy(me); - return call; -} - -void linphone_call_destroy(LinphoneCall *obj) -{ - linphone_core_notify_all_friends(obj->core,obj->core->prev_mode); - linphone_call_log_completed(obj->log,obj); - linphone_core_update_allocated_audio_bandwidth(obj->core); - 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); - } - ms_free(obj); -} - + /*prevent a gcc bug with %c*/ static size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm){ #if !defined(_WIN32_WCE) @@ -296,32 +152,21 @@ static void call_logs_read_from_config_file(LinphoneCore *lc){ } -void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call){ +void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call, LinphoneCallStatus status){ LinphoneCore *lc=call->core; calllog->duration=time(NULL)-call->start_time; - switch(call->state){ - case LCStateInit: - case LCStatePreEstablishing: - calllog->status=LinphoneCallAborted; - break; - case LCStateRinging: - if (calllog->dir==LinphoneCallIncoming){ - char *info; - calllog->status=LinphoneCallMissed; - lc->missed_calls++; - info=ortp_strdup_printf(ngettext("You have missed %i call.", - "You have missed %i calls.", lc->missed_calls), - lc->missed_calls); - lc->vtable.display_status(lc,info); - ms_free(info); - } - else calllog->status=LinphoneCallAborted; - break; - case LCStateAVRunning: - calllog->status=LinphoneCallSuccess; - break; - } + + if (status==LinphoneCallMissed){ + char *info; + lc->missed_calls++; + info=ortp_strdup_printf(ngettext("You have missed %i call.", + "You have missed %i calls.", lc->missed_calls), + lc->missed_calls); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,info); + ms_free(info); + }else calllog->status=status; lc->call_logs=ms_list_prepend(lc->call_logs,(void *)calllog); if (ms_list_size(lc->call_logs)>lc->max_call_logs){ MSList *elem,*prevelem=NULL; @@ -455,17 +300,21 @@ bool_t linphone_call_asked_to_autoanswer(LinphoneCall *call){ return FALSE; } -int linphone_core_get_current_call_duration(const LinphoneCore *lc){ - LinphoneCall *call=lc->call; +int linphone_core_get_call_duration(LinphoneCall *call){ if (call==NULL) return 0; if (call->media_start_time==0) return 0; return time(NULL)-call->media_start_time; } -const LinphoneAddress *linphone_core_get_remote_uri(LinphoneCore *lc){ - LinphoneCall *call=lc->call; - if (call==NULL) return 0; - return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to; +int linphone_core_get_current_call_duration(const LinphoneCore *lc){ + LinphoneCall *call=linphone_core_get_current_call((LinphoneCore *)lc); + return linphone_core_get_call_duration(call); +} + +const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _LinphoneCore *lc){ + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL) return NULL; + return linphone_call_get_remote_address(call); } /** @@ -632,9 +481,15 @@ static void sip_config_read(LinphoneCore *lc) LCSipTransports tr; int i,tmp; int ipv6; + tmp=lp_config_get_int(lc->config,"sip","use_info",0); linphone_core_set_use_info_for_dtmf(lc,tmp); + if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ + sal_use_session_timers(lc->sal,200); + } + + tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0); linphone_core_set_use_rfc2833_for_dtmf(lc,tmp); @@ -642,7 +497,8 @@ static void sip_config_read(LinphoneCore *lc) if (ipv6==-1){ ipv6=0; if (host_has_ipv6_network()){ - lc->vtable.display_message(lc,_("Your machine appears to be connected to an IPv6 network. By default linphone always uses IPv4. Please update your configuration if you want to use IPv6")); + if (lc->vtable.display_message) + lc->vtable.display_message(lc,_("Your machine appears to be connected to an IPv6 network. By default linphone always uses IPv4. Please update your configuration if you want to use IPv6")); } } linphone_core_enable_ipv6(lc,ipv6); @@ -1051,6 +907,12 @@ static void linphone_core_free_payload_types(void){ linphone_payload_types=NULL; } +void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message){ + if (lc->vtable.global_state_changed){ + lc->vtable.global_state_changed(lc,gstate,message); + } +} + static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path, const char *factory_config_path, void * userdata) { @@ -1059,9 +921,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta memcpy(&lc->vtable,vtable,sizeof(LinphoneCoreVTable)); - gstate_initialize(lc); - gstate_new_state(lc, GSTATE_POWER_STARTUP, NULL); - + linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up"); ortp_init(); linphone_core_assign_payload_type(&payload_type_pcmu8000,0,NULL); linphone_core_assign_payload_type(&payload_type_gsm,3,NULL); @@ -1099,6 +959,10 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta #endif ms_init(); + /* create a mediastreamer2 event queue and set it as global */ + /* This allows to run event's callback in linphone_core_iterate() */ + lc->msevq=ms_event_queue_new(); + ms_set_global_event_queue(lc->msevq); lc->config=lp_config_new(config_path); if (factory_config_path) @@ -1107,9 +971,7 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->sal=sal_init(); sal_set_user_pointer(lc->sal,lc); sal_set_callbacks(lc->sal,&linphone_sal_callbacks); - if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ - sal_use_session_timers(lc->sal,200); - } + sip_setup_register_all(); sound_config_read(lc); net_config_read(lc); @@ -1118,12 +980,12 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta sip_config_read(lc); /* this will start eXosip*/ video_config_read(lc); //autoreplier_config_init(&lc->autoreplier_conf); - lc->prev_mode=LINPHONE_STATUS_ONLINE; - lc->presence_mode=LINPHONE_STATUS_ONLINE; + lc->presence_mode=LinphoneStatusOnline; lc->max_call_logs=15; ui_config_read(lc); - lc->vtable.display_status(lc,_("Ready")); - gstate_new_state(lc, GSTATE_POWER_ON, NULL); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Ready")); + linphone_core_set_state(lc,LinphoneGlobalOn,"Ready"); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; lc->ready=TRUE; @@ -1137,7 +999,9 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta * 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. @@ -1208,7 +1072,7 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ - if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && linphone_core_get_nat_address(lc)!=NULL){ strncpy(result,linphone_core_get_nat_address(lc),LINPHONE_IPADDR_SIZE); return; @@ -1618,9 +1482,23 @@ static void display_bandwidth(RtpSession *as, RtpSession *vs){ (vs!=NULL) ? (rtp_session_compute_send_bandwidth(vs)*1e-3) : 0); } -static void linphone_core_disconnected(LinphoneCore *lc){ - lc->vtable.display_warning(lc,_("Remote end seems to have disconnected, the call is going to be closed.")); - linphone_core_terminate_call(lc,NULL); +static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){ + char temp[256]; + char *from=NULL; + if(call) + from = linphone_call_get_remote_address_as_string(call); + if(from) + { + snprintf(temp,sizeof(temp),"Remote end %s seems to have disconnected, the call is going to be closed.",from); + free(from); + } + else + { + snprintf(temp,sizeof(temp),"Remote end seems to have disconnected, the call is going to be closed."); + } + if (lc->vtable.display_warning!=NULL) + lc->vtable.display_warning(lc,temp); + linphone_core_terminate_call(lc,call); } static void monitor_network_state(LinphoneCore *lc, time_t curtime){ @@ -1746,6 +1624,8 @@ static void linphone_core_do_plugin_tasks(LinphoneCore *lc){ * serialized with a mutex. **/ void linphone_core_iterate(LinphoneCore *lc){ + MSList *calls; + LinphoneCall *call; int disconnect_timeout = linphone_core_get_nortp_timeout(lc); time_t curtime=time(NULL); int elapsed; @@ -1772,42 +1652,54 @@ void linphone_core_iterate(LinphoneCore *lc){ } sal_iterate(lc->sal); + ms_event_queue_pump(lc->msevq); if (lc->auto_net_state_mon) monitor_network_state(lc,curtime); proxy_update(lc); - if (lc->call!=NULL){ - LinphoneCall *call=lc->call; - if (call->state==LCStatePreEstablishing && (curtime-call->start_time>=2)){ - /*start the call even if the OPTIONS reply did not arrive*/ - linphone_core_start_invite(lc,call,NULL); + //we have to iterate for each call + calls= lc->calls; + while(calls!= NULL){ + call = (LinphoneCall *)calls->data; + /* get immediately a reference to next one in case the one + we are going to examine is destroy and removed during + linphone_core_start_invite() */ + calls=calls->next; + if (call->state==LinphoneCallOutgoingInit && (curtime-call->start_time>=2)){ + /*start the call even if the OPTIONS reply did not arrive*/ + linphone_core_start_invite(lc,call,NULL); + } + if (call->dir==LinphoneCallIncoming && call->state==LinphoneCallOutgoingRinging){ + elapsed=curtime-call->start_time; + ms_message("incoming call ringing for %i seconds",elapsed); + if (elapsed>lc->sip_conf.inc_timeout){ + call->log->status=LinphoneCallMissed; + linphone_core_terminate_call(lc,call); + } + } + } + call = linphone_core_get_current_call(lc); + if(call) + { + if (call->state==LinphoneCallStreamsRunning && one_second_elapsed) + { + RtpSession *as=NULL,*vs=NULL; + lc->prevtime=curtime; + if (call->audiostream!=NULL) + as=call->audiostream->session; + if (call->videostream!=NULL) + vs=call->videostream->session; + display_bandwidth(as,vs); } - if (call->dir==LinphoneCallIncoming && call->state==LCStateRinging){ - elapsed=curtime-call->start_time; - ms_message("incoming call ringing for %i seconds",elapsed); - if (elapsed>lc->sip_conf.inc_timeout){ - linphone_core_terminate_call(lc,NULL); - } - }else if (call->state==LCStateAVRunning){ - if (one_second_elapsed){ - RtpSession *as=NULL,*vs=NULL; - lc->prevtime=curtime; - if (lc->audiostream!=NULL) - as=lc->audiostream->session; - if (lc->videostream!=NULL) - vs=lc->videostream->session; - display_bandwidth(as,vs); - } #ifdef VIDEO_ENABLED - if (lc->videostream!=NULL) - video_stream_iterate(lc->videostream); + if (call->videostream!=NULL) + video_stream_iterate(call->videostream); #endif - if (lc->audiostream!=NULL && disconnect_timeout>0) - disconnected=!audio_stream_alive(lc->audiostream,disconnect_timeout); - } + if (call->audiostream!=NULL && disconnect_timeout>0) + disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout); } if (linphone_core_video_preview_enabled(lc)){ - if (lc->previewstream==NULL) + if (lc->previewstream==NULL && lc->calls==NULL) toggle_video_preview(lc,TRUE); #ifdef VIDEO_ENABLED else video_stream_iterate(lc->previewstream); @@ -1817,7 +1709,7 @@ void linphone_core_iterate(LinphoneCore *lc){ toggle_video_preview(lc,FALSE); } if (disconnected) - linphone_core_disconnected(lc); + linphone_core_disconnected(lc,call); linphone_core_do_plugin_tasks(lc); @@ -1848,9 +1740,11 @@ LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url) LinphoneAddress *uri; if (is_enum(url,&enum_domain)){ - lc->vtable.display_status(lc,_("Looking for telephone number destination...")); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Looking for telephone number destination...")); if (enum_lookup(enum_domain,&enumres)<0){ - lc->vtable.display_status(lc,_("Could not resolve this number.")); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Could not resolve this number.")); ms_free(enum_domain); return NULL; } @@ -1933,7 +1827,7 @@ bool_t linphone_core_is_in_communication_with(LinphoneCore *lc, const char *to) { char *tmp; bool_t returned; - const LinphoneAddress *la=linphone_core_get_remote_uri(lc); + const LinphoneAddress *la=linphone_core_get_current_call_remote_address(lc); if(la == NULL) { return FALSE; @@ -1948,6 +1842,19 @@ bool_t linphone_core_is_in_communication_with(LinphoneCore *lc, const char *to) return returned; } +void linphone_core_start_pending_refered_calls(LinphoneCore *lc){ + MSList *elem; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *call=(LinphoneCall*)elem->data; + if (call->refer_pending){ + ms_message("Starting new call to refered address %s",call->refer_to); + call->refer_pending=FALSE; + linphone_core_invite(lc,call->refer_to); + break; + } + } +} + LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri){ const MSList *elem; LinphoneProxyConfig *found_cfg=NULL; @@ -1967,7 +1874,7 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr const char *localip=call->localip; /* first use user's supplied ip address if asked*/ - if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){ + if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress){ ctt=linphone_core_get_primary_contact_parsed(lc); return ms_strdup_printf("sip:%s@%s",linphone_address_get_username(ctt), linphone_core_get_nat_address(lc)); @@ -1977,7 +1884,6 @@ static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphonePr if (call->op && sal_op_get_contact(call->op)!=NULL){ return NULL; } - /* if the ping OPTIONS request succeeded use the contact guessed from the received, rport*/ if (call->ping_op){ @@ -2017,14 +1923,16 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro char *contact; char *real_url,*barmsg; char *from; + /*try to be best-effort in giving real local or routable contact address */ contact=get_fixed_contact(lc,call,dest_proxy); if (contact){ sal_op_set_contact(call->op, contact); ms_free(contact); } - call->state=LCStateInit; - linphone_core_init_media_streams(lc,lc->call); + + //TODO : should probably not be done here + linphone_call_init_media_streams(call); if (!lc->sip_conf.sdp_200_ack){ call->media_pending=TRUE; sal_call_set_local_media_description(call->op,call->localdesc); @@ -2038,16 +1946,18 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro sal_call_set_local_media_description(call->op,call->localdesc); } barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); - lc->vtable.display_status(lc,barmsg); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,barmsg); ms_free(barmsg); if (err<0){ - ms_warning("Could not initiate call."); - lc->vtable.display_status(lc,_("could not call")); - linphone_core_stop_media_streams(lc,call); - linphone_call_destroy(call); - lc->call=NULL; - }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, real_url); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Could not call")); + linphone_call_stop_media_streams(call); + linphone_call_set_state(call,LinphoneCallError,"Call failed"); + }else { + linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress"); + } ms_free(real_url); ms_free(from); return err; @@ -2060,15 +1970,15 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro * @param lc the LinphoneCore object * @param url the destination of the call (sip address, or phone number). **/ -int linphone_core_invite(LinphoneCore *lc, const char *url){ +LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url){ LinphoneAddress *addr=linphone_core_interpret_url(lc,url); if (addr){ - int err; - err=linphone_core_invite_address(lc,addr); + LinphoneCall *call; + call=linphone_core_invite_address(lc,addr); linphone_address_destroy(addr); - return err; + return call; } - return -1; + return NULL; } /** @@ -2076,9 +1986,12 @@ int linphone_core_invite(LinphoneCore *lc, const char *url){ * * @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; @@ -2089,11 +2002,16 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p LinphoneProxyConfig *dest_proxy=NULL; LinphoneCall *call; - if (lc->call!=NULL){ - lc->vtable.display_warning(lc,_("Sorry, having multiple simultaneous calls is not supported yet !")); - return -1; + if (linphone_core_in_call(lc)){ + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("Sorry, you have to pause or stop the current call first !")); + return NULL; + } + if(!linphone_core_can_we_add_call(lc)){ + if (lc->vtable.display_warning) + lc->vtable.display_warning(lc,_("Sorry, we have reached the maximum number of simultaneous calls")); + return NULL; } - linphone_core_get_default_proxy(lc,&proxy); route=linphone_core_get_route(lc); @@ -2118,12 +2036,19 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(real_parsed_url)); sal_op_set_route(call->op,route); - lc->call=call; + if(linphone_core_add_call(lc,call)!= 0) + { + ms_warning("we had a problem in adding the call into the invite ... weird"); + linphone_call_unref(call); + return NULL; + } + /* this call becomes now the current one*/ + lc->current_call=call; + linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call"); if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){ err=linphone_core_start_invite(lc,call,dest_proxy); }else{ /*defer the start of the call after the OPTIONS ping*/ - call->state=LCStatePreEstablishing; call->ping_op=sal_op_new(lc->sal); sal_ping(call->ping_op,from,real_url); sal_op_set_user_pointer(call->ping_op,call); @@ -2131,20 +2056,24 @@ int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_p } if (real_url!=NULL) ms_free(real_url); - return err; + return call; } -int linphone_core_refer(LinphoneCore *lc, const char *url) +/** + * Performs a simple call transfer to the specified destination. + * + * The remote endpoint is expected to issue a new call to the specified destination. + * The current call remains active and thus can be later paused or terminated. +**/ +int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *url) { char *real_url=NULL; LinphoneAddress *real_parsed_url=linphone_core_interpret_url(lc,url); - LinphoneCall *call; if (!real_parsed_url){ /* bad url */ return -1; } - call=lc->call; if (call==NULL){ ms_warning("No established call to refer."); return -1; @@ -2153,361 +2082,67 @@ int linphone_core_refer(LinphoneCore *lc, const char *url) real_url=linphone_address_as_string (real_parsed_url); sal_refer(call->op,real_url); ms_free(real_url); + linphone_address_destroy(real_parsed_url); return 0; } -/** - * Returns true if in 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; + LinphoneCall *call = linphone_core_get_current_call(lc); + if(call != NULL) + { + if(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); - { - 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 ?"); - } -#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; -} - -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; if (call==NULL){ + //if just one call is present answer the only one ... + if(linphone_core_get_calls_nb (lc) != 1) + return -1; + else + call = (LinphoneCall*)linphone_core_get_calls(lc)->data; + } + + if (lc->current_call){ + ms_warning("Cannot accept this call, there is already one running."); + return -1; + } + + if (call->state==LinphoneCallConnected){ + /*call already accepted*/ return -1; } - if (call->state==LCStateAVRunning){ - /*call already accepted*/ - return -1; + /*can accept a new call only if others are on hold */ + { + MSList *elem; + for(elem=lc->calls;elem!=NULL;elem=elem->next){ + LinphoneCall *c=(LinphoneCall*)elem->data; + if (c!=call && (c->state!=LinphoneCallPaused)){ + ms_warning("Cannot accept this call as another one is running, pause it before."); + return -1; + } + } } /*stop ringing */ @@ -2517,42 +2152,31 @@ int linphone_core_accept_call(LinphoneCore *lc, const char *url) ms_message("ring stopped"); lc->ringstream=NULL; } - + linphone_core_get_default_proxy(lc,&cfg); /*try to be best-effort in giving real local or routable contact address*/ contact=get_fixed_contact(lc,call,cfg); if (contact) sal_op_set_contact(call->op,contact); #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 - linphone_core_init_media_streams(lc,call); + linphone_call_init_media_streams(call); #endif sal_call_accept(call->op); - lc->vtable.display_status(lc,_("Connected.")); - gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Connected.")); + lc->current_call=call; + linphone_call_set_state(call,LinphoneCallConnected,"Connected"); call->resultdesc=sal_call_get_final_media_description(call->op); if (call->resultdesc){ + linphone_call_start_media_streams(call); + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); sal_media_description_ref(call->resultdesc); - linphone_core_start_media_streams(lc, call); }else call->media_pending=TRUE; ms_message("call answered."); return 0; } -/** - * 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). -**/ -int linphone_core_terminate_call(LinphoneCore *lc, const char *url) -{ - LinphoneCall *call=lc->call; - if (call==NULL){ - return -1; - } - lc->call=NULL; +int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error){ sal_call_terminate(call->op); /*stop ringing*/ @@ -2560,20 +2184,88 @@ int linphone_core_terminate_call(LinphoneCore *lc, const char *url) ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc,call); - lc->vtable.display_status(lc,_("Call ended") ); - gstate_new_state(lc, GSTATE_CALL_END, NULL); - linphone_call_destroy(call); + linphone_call_stop_media_streams(call); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Call aborted") ); + linphone_call_set_state(call,LinphoneCallError,error); return 0; } + +/** + * Terminates a call. + * + * @ingroup call_control + * @param lc the LinphoneCore + * @param the_call the LinphoneCall object representing the call to be terminated. +**/ +int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) +{ + 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; + } + sal_call_terminate(call->op); + + /*stop ringing*/ + if (lc->ringstream!=NULL) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + linphone_call_stop_media_streams(call); + if (lc->vtable.display_status!=NULL) + lc->vtable.display_status(lc,_("Call ended") ); + linphone_call_set_state(call,LinphoneCallEnd,"Call terminated"); + return 0; +} + +/** + * Terminates all the calls. + * + * @ingroup call_control + * @param lc The LinphoneCore +**/ +int linphone_core_terminate_all_calls(LinphoneCore *lc){ + while(lc->calls) + { + LinphoneCall *the_call = lc->calls->data; + linphone_core_terminate_call(lc,the_call); + } + ms_list_free(lc->calls); + return -1; +} + +/** + * Returns the current list of calls. + * + * Note that this list is read-only and might be changed by the core after a function call to linphone_core_iterate(). + * Similarly the LinphoneCall objects inside it might be destroyed without prior notice. + * To hold references to LinphoneCall object into your program, you must use linphone_call_ref(). + * + * @ingroup call_control +**/ +const MSList *linphone_core_get_calls(LinphoneCore *lc) +{ + return lc->calls; +} + /** * Returns TRUE if there is a call running or pending. * * @ingroup call_control **/ bool_t linphone_core_in_call(const LinphoneCore *lc){ - return lc->call!=NULL; + return linphone_core_get_current_call((LinphoneCore *)lc)!=NULL; } /** @@ -2581,12 +2273,107 @@ bool_t linphone_core_in_call(const LinphoneCore *lc){ * * @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; +} + +/** + * 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; +} + +/** + * 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, @@ -2641,7 +2428,6 @@ void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, */ linphone_core_send_publish(lc,presence_mode); } - lc->prev_mode=lc->presence_mode; lc->presence_mode=presence_mode; } @@ -2697,10 +2483,15 @@ void linphone_core_set_ring_level(LinphoneCore *lc, int level){ **/ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){ float gain=gaindb; - lc->sound_conf.soft_play_lev=gaindb; - AudioStream *st=lc->audiostream; - if (!st) return; /*just return*/ + LinphoneCall *call=linphone_core_get_current_call (lc); + AudioStream *st; + lc->sound_conf.soft_play_lev=gaindb; + + if (call==NULL || (st=call->audiostream)==NULL){ + ms_message("linphone_core_set_playback_gain_db(): no active call."); + return; + } if (st->volrecv){ ms_filter_call_method(st->volrecv,MS_VOLUME_SET_DB_GAIN,&gain); }else ms_warning("Could not apply gain: gain control wasn't activated."); @@ -2712,13 +2503,7 @@ void linphone_core_set_playback_gain_db (LinphoneCore *lc, float gaindb){ * @ingroup media_parameters **/ float linphone_core_get_playback_gain_db(LinphoneCore *lc) { - float gain=0; - AudioStream *st=lc->audiostream; - if (st->volrecv){ - ms_filter_call_method(st->volrecv,MS_VOLUME_GET_GAIN_DB,&gain); - }else ms_warning("Could not get gain: gain control wasn't activated."); - - return gain; + return lc->sound_conf.soft_play_lev; } /** @@ -2959,7 +2744,8 @@ int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCb lc_callback_obj_init(&lc->preview_finished_cb,func,userdata); lc->preview_finished=0; if (lc->sound_conf.ring_sndcard!=NULL){ - lc->ringstream=ring_start_with_cb(ring,2000,lc->sound_conf.ring_sndcard,notify_end_of_ring,(void *)lc); + MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; + lc->ringstream=ring_start_with_cb(ring,2000,ringcard,notify_end_of_ring,(void *)lc); } return 0; } @@ -3022,39 +2808,46 @@ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){ * @ingroup media_parameters **/ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ - if (lc->audiostream!=NULL){ - audio_stream_set_mic_gain(lc->audiostream, - (val==TRUE) ? 0 : 1.0); // REVISIT: take mic_gain value - if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ - audio_stream_mute_rtp(lc->audiostream,val); - } - lc->audio_muted=val; + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_mute_mic(): No current call !"); + return; + } + if (call->audiostream!=NULL){ + audio_stream_set_mic_gain(call->audiostream, + (val==TRUE) ? 0 : lp_config_get_float(lc->config,"sound","mic_gain",1)); + if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ + audio_stream_mute_rtp(call->audiostream,val); + } + call->audio_muted=val; } } bool_t linphone_core_is_mic_muted(LinphoneCore *lc) { float gain=1.0; - if (lc->audiostream && lc->audiostream->volsend){ - ms_filter_call_method(lc->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain); + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_is_mic_muted(): No current call !"); + return FALSE; + } + if (call->audiostream && call->audiostream->volsend){ + ms_filter_call_method(call->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain); }else ms_warning("Could not get gain: gain control wasn't activated. "); - return gain==0; -} - -// returns audio mute status for active stream -bool_t linphone_core_is_audio_muted(LinphoneCore *lc){ - if( lc->audiostream != NULL ) - return (lc->audio_muted); - return FALSE; + return gain==0 || call->audio_muted; } // returns rtp transmission status for an active stream // if audio is muted and config parameter rtp_no_xmit_on_audio_mute // was set on then rtp transmission is also muted bool_t linphone_core_is_rtp_muted(LinphoneCore *lc){ - if( (lc->audiostream != NULL) && - linphone_core_get_rtp_no_xmit_on_audio_mute(lc)){ - return lc->audio_muted; + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_is_mic_muted(): No current call !"); + return FALSE; + } + if( linphone_core_get_rtp_no_xmit_on_audio_mute(lc)){ + return call->audio_muted; } return FALSE; } @@ -3078,12 +2871,17 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ **/ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) { + LinphoneCall *call=linphone_core_get_current_call(lc); + if (call==NULL){ + ms_warning("linphone_core_send_dtmf(): no active call"); + return; + } /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/ if (linphone_core_get_use_rfc2833_for_dtmf(lc)!=0 || linphone_core_get_use_info_for_dtmf(lc)==0) { /* In Band DTMF */ - if (lc->audiostream!=NULL){ - audio_stream_send_dtmf(lc->audiostream,dtmf); + if (call->audiostream!=NULL){ + audio_stream_send_dtmf(call->audiostream,dtmf); } else { @@ -3092,10 +2890,6 @@ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) } if (linphone_core_get_use_info_for_dtmf(lc)!=0){ /* Out of Band DTMF (use INFO method) */ - LinphoneCall *call=lc->call; - if (call==NULL){ - return; - } sal_call_send_dtmf(call->op,dtmf); } } @@ -3175,17 +2969,15 @@ void linphone_core_clear_call_logs(LinphoneCore *lc){ static void toggle_video_preview(LinphoneCore *lc, bool_t val){ #ifdef VIDEO_ENABLED - if (lc->videostream==NULL){ - if (val){ - if (lc->previewstream==NULL){ - lc->previewstream=video_preview_start(lc->video_conf.device, - lc->video_conf.vsize); - } - }else{ - if (lc->previewstream!=NULL){ - video_preview_stop(lc->previewstream); - lc->previewstream=NULL; - } + if (val){ + if (lc->previewstream==NULL){ + lc->previewstream=video_preview_start(lc->video_conf.device, + lc->video_conf.vsize); + } + }else{ + if (lc->previewstream!=NULL){ + video_preview_stop(lc->previewstream); + lc->previewstream=NULL; } } #endif @@ -3254,10 +3046,11 @@ bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc){ * This function works at any time, including during calls. **/ void linphone_core_enable_self_view(LinphoneCore *lc, bool_t val){ - lc->video_conf.selfview=val; #ifdef VIDEO_ENABLED - if (lc->videostream){ - video_stream_enable_self_view(lc->videostream,val); + LinphoneCall *call=linphone_core_get_current_call (lc); + lc->video_conf.selfview=val; + if (call && call->videostream){ + video_stream_enable_self_view(call->videostream,val); } #endif } @@ -3316,12 +3109,13 @@ const char *linphone_core_get_video_device(const LinphoneCore *lc){ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { #ifdef VIDEO_ENABLED VideoStream *vs = NULL; + LinphoneCall *call=linphone_core_get_current_call (lc); /* Select the video stream from the call in the first place */ - if (lc && lc->videostream) { - vs = lc->videostream; + if (call && call->videostream) { + vs = call->videostream; } /* If not in call, select the video stream from the preview */ - if (vs == NULL && lc && lc->previewstream) { + if (vs == NULL && lc->previewstream) { vs = lc->previewstream; } @@ -3351,8 +3145,9 @@ int linphone_core_set_static_picture(LinphoneCore *lc, const char *path) { **/ unsigned long linphone_core_get_native_video_window_id(const LinphoneCore *lc){ #ifdef VIDEO_ENABLED - if (lc->videostream) - return video_stream_get_native_window_id(lc->videostream); + LinphoneCall *call=linphone_core_get_current_call (lc); + if (call && call->videostream) + return video_stream_get_native_window_id(call->videostream); if (lc->previewstream) return video_stream_get_native_window_id(lc->previewstream); #endif @@ -3450,44 +3245,63 @@ MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc){ return lc->video_conf.vsize; } +/** + * Ask the core to stream audio from and to files, instead of using the soundcard. +**/ void linphone_core_use_files(LinphoneCore *lc, bool_t yesno){ lc->use_files=yesno; } +/** + * Sets a wav file to be played when putting somebody on hold, + * or when files are used instead of soundcards (see linphone_core_use_files()). + * + * The file must be a 16 bit linear wav file. +**/ void linphone_core_set_play_file(LinphoneCore *lc, const char *file){ + LinphoneCall *call=linphone_core_get_current_call(lc); if (lc->play_file!=NULL){ ms_free(lc->play_file); lc->play_file=NULL; } if (file!=NULL) { lc->play_file=ms_strdup(file); - if (lc->audiostream->ticker) - audio_stream_play(lc->audiostream,file); + if (call && call->audiostream && call->audiostream->ticker) + audio_stream_play(call->audiostream,file); } } + +/** + * Sets a wav file where incoming stream is to be recorded, + * when files are used instead of soundcards (see linphone_core_use_files()). + * + * The file must be a 16 bit linear wav file. +**/ void linphone_core_set_record_file(LinphoneCore *lc, const char *file){ + LinphoneCall *call=linphone_core_get_current_call(lc); if (lc->rec_file!=NULL){ ms_free(lc->rec_file); lc->rec_file=NULL; } if (file!=NULL) { lc->rec_file=ms_strdup(file); - if (lc->audiostream) - audio_stream_record(lc->audiostream,file); + if (call && call->audiostream) + audio_stream_record(call->audiostream,file); } } + static MSFilter *get_dtmf_gen(LinphoneCore *lc){ LinphoneCall *call=linphone_core_get_current_call (lc); if (call){ - AudioStream *stream=lc->audiostream; + AudioStream *stream=call->audiostream; if (stream){ return stream->dtmfgen; } } if (lc->ringstream==NULL){ - MSSndCard *ringcard= lc->sound_conf.ring_sndcard; + MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; lc->ringstream=ring_start(NULL,0,ringcard); lc->dmfs_playing_start_time=time(NULL); }else{ @@ -3592,9 +3406,9 @@ void linphone_core_set_audio_transports(LinphoneCore *lc, RtpTransport *rtp, Rtp int linphone_core_get_current_call_stats(LinphoneCore *lc, rtp_stats_t *local, rtp_stats_t *remote){ LinphoneCall *call=linphone_core_get_current_call (lc); if (call!=NULL){ - if (lc->audiostream!=NULL){ + if (call->audiostream!=NULL){ memset(remote,0,sizeof(*remote)); - audio_stream_get_local_rtp_stats (lc->audiostream,local); + audio_stream_get_local_rtp_stats (call->audiostream,local); return 0; } } @@ -3773,27 +3587,28 @@ LpConfig *linphone_core_get_config(LinphoneCore *lc){ static void linphone_core_uninit(LinphoneCore *lc) { - if (lc->call){ - int i; - linphone_core_terminate_call(lc,NULL); - for(i=0;i<10;++i){ -#ifndef WIN32 - usleep(50000); + while(lc->calls) + { + LinphoneCall *the_call = lc->calls->data; + linphone_core_terminate_call(lc,the_call); + linphone_core_iterate(lc); +#ifdef WIN32 + Sleep(50000); #else - Sleep(50); + usleep(50000); #endif - linphone_core_iterate(lc); - } } + if (lc->friends) ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions); - gstate_new_state(lc, GSTATE_POWER_SHUTDOWN, NULL); + linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down"); #ifdef VIDEO_ENABLED if (lc->previewstream!=NULL){ video_preview_stop(lc->previewstream); lc->previewstream=NULL; } #endif + ms_event_queue_destroy(lc->msevq); /* save all config */ net_config_uninit(lc); sip_config_uninit(lc); @@ -3813,7 +3628,7 @@ static void linphone_core_uninit(LinphoneCore *lc) linphone_core_free_payload_types(); ortp_exit(); - gstate_new_state(lc, GSTATE_POWER_OFF, NULL); + linphone_core_set_state(lc,LinphoneGlobalOff,"Off"); } static void set_network_reachable(LinphoneCore* lc,bool_t isReachable){ @@ -3857,6 +3672,57 @@ void linphone_core_destroy(LinphoneCore *lc){ linphone_core_uninit(lc); ms_free(lc); } +/** + * Get the number of Call + * + * @ingroup call_control +**/ +int linphone_core_get_calls_nb(const LinphoneCore *lc){ + return ms_list_size(lc->calls);; +} + +/** + * Check if we do not have exceed the number of simultaneous call + * + * @ingroup call_control +**/ +bool_t linphone_core_can_we_add_call(LinphoneCore *lc) +{ + if(linphone_core_get_calls_nb(lc) < NB_MAX_CALLS) + return TRUE; + ms_error("Maximum amount of simultaneous calls reached !"); + return FALSE; +} + + +int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call) +{ + if(linphone_core_can_we_add_call(lc)) + { + lc->calls = ms_list_append(lc->calls,call); + return 0; + } + return -1; +} + +int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call) +{ + MSList *it; + MSList *the_calls = lc->calls; + + it=ms_list_find(the_calls,call); + if (it) + { + the_calls = ms_list_remove_link(the_calls,it); + } + else + { + ms_warning("could not find the call into the list\n"); + return -1; + } + lc->calls = the_calls; + return 0; +} static PayloadType* find_payload_type_from_list(const char* type, int rate,const MSList* from) { const MSList *elem; @@ -3869,6 +3735,12 @@ static PayloadType* find_payload_type_from_list(const char* type, int rate,const return NULL; } +/** + * Get payload type from mime type and clock rate + * @ingroup media_parameters + * This function searches in audio and video codecs for the given payload type name and clockrate. + * Returns NULL if not found. + */ PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, int rate) { PayloadType* result = find_payload_type_from_list(type, rate, linphone_core_get_audio_codecs(lc)); if (result) { @@ -3879,6 +3751,6 @@ PayloadType* linphone_core_find_payload_type(LinphoneCore* lc, const char* type, return result; } } - //not found + /*not found*/ return NULL; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 9bfce8629..555d38cdf 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -97,14 +97,7 @@ void linphone_address_destroy(LinphoneAddress *u); struct _SipSetupContext; -/** - * The LinphoneCall object represents a call issued or received by the LinphoneCore -**/ -struct _LinphoneCall; -typedef struct _LinphoneCall LinphoneCall; -bool_t linphone_call_asked_to_autoanswer(struct _LinphoneCall *call); - /** * Enum representing the direction of a call. * @ingroup call_logs @@ -161,6 +154,28 @@ const rtp_stats_t *linphone_call_log_get_local_stats(const LinphoneCallLog *cl); const rtp_stats_t *linphone_call_log_get_remote_stats(const LinphoneCallLog *cl); char * linphone_call_log_to_str(LinphoneCallLog *cl); + +/** + * The LinphoneCall object represents a call issued or received by the LinphoneCore +**/ +struct _LinphoneCall; +typedef struct _LinphoneCall LinphoneCall; + + +enum _LinphoneCallState linphone_call_get_state(const LinphoneCall *call); +bool_t linphone_call_asked_to_autoanswer(LinphoneCall *call); +const LinphoneAddress * linphone_core_get_current_call_remote_address(struct _LinphoneCore *lc); +const LinphoneAddress * linphone_call_get_remote_address(const LinphoneCall *call); +char *linphone_call_get_remote_address_as_string(const LinphoneCall *call); +LinphoneCallDir linphone_call_get_dir(const LinphoneCall *call); +void linphone_call_ref(LinphoneCall *call); +void linphone_call_unref(LinphoneCall *call); +LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call); +const char *linphone_call_get_refer_to(const LinphoneCall *call); +bool_t linphone_call_has_transfer_pending(const LinphoneCall *call); +void *linphone_call_get_user_pointer(LinphoneCall *call); +void linphone_call_set_user_pointer(LinphoneCall *call, void *user_pointer); + typedef enum{ LinphoneSPWait, LinphoneSPDeny, @@ -168,18 +183,18 @@ typedef enum{ }LinphoneSubscribePolicy; typedef enum _LinphoneOnlineStatus{ - LINPHONE_STATUS_OFFLINE, - LINPHONE_STATUS_ONLINE, - LINPHONE_STATUS_BUSY, - LINPHONE_STATUS_BERIGHTBACK, - LINPHONE_STATUS_AWAY, - LINPHONE_STATUS_ONTHEPHONE, - LINPHONE_STATUS_OUTTOLUNCH, - LINPHONE_STATUS_NOT_DISTURB, - LINPHONE_STATUS_MOVED, - LINPHONE_STATUS_ALT_SERVICE, - LINPHONE_STATUS_PENDING, - LINPHONE_STATUS_END + LinphoneStatusOffline, + LinphoneStatusOnline, + LinphoneStatusBusy, + LinphoneStatusBeRightBack, + LinphoneStatusAway, + LinphoneStatusOnThePhone, + LinphoneStatusOutToLunch, + LinphoneStatusDoNotDisturb, + LinphoneStatusMoved, + LinphoneStatusAltService, + LinphoneStatusPending, + LinphoneStatusEnd }LinphoneOnlineStatus; const char *linphone_online_status_to_string(LinphoneOnlineStatus ss); @@ -350,72 +365,62 @@ void linphone_chat_room_destroy(LinphoneChatRoom *cr); void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud); void * linphone_chat_room_get_user_data(LinphoneChatRoom *cr); -/* describes the different groups of states */ -typedef enum _gstate_group { - GSTATE_GROUP_POWER, - GSTATE_GROUP_REG, - GSTATE_GROUP_CALL -} gstate_group_t; +typedef enum _LinphoneCallState{ + LinphoneCallIdle, + LinphoneCallIncomingReceived, + LinphoneCallOutgoingInit, + LinphoneCallOutgoingProgress, + LinphoneCallOutgoingRinging, + LinphoneCallOutgoingEarlyMedia, + LinphoneCallConnected, + LinphoneCallStreamsRunning, + LinphoneCallPausing, + LinphoneCallPaused, + LinphoneCallResuming, + LinphoneCallRefered, + LinphoneCallError, + LinphoneCallEnd, +} LinphoneCallState; -typedef enum _gstate { - /* states for GSTATE_GROUP_POWER */ - GSTATE_POWER_OFF = 0, /* initial state */ - GSTATE_POWER_STARTUP, - GSTATE_POWER_ON, - GSTATE_POWER_SHUTDOWN, - /* states for GSTATE_GROUP_REG */ - GSTATE_REG_NONE = 10, /* initial state */ - GSTATE_REG_OK, - GSTATE_REG_FAILED, - GSTATE_REG_PENDING, /* a registration request is ongoing*/ - /* states for GSTATE_GROUP_CALL */ - GSTATE_CALL_IDLE = 20, /* initial state */ - GSTATE_CALL_OUT_INVITE, - GSTATE_CALL_OUT_CONNECTED, - GSTATE_CALL_IN_INVITE, - GSTATE_CALL_IN_CONNECTED, - GSTATE_CALL_END, - GSTATE_CALL_ERROR, - GSTATE_INVALID, - GSTATE_CALL_OUT_RINGING /*remote ringing*/ -} gstate_t; +typedef enum _LinphoneGlobalState{ + LinphoneGlobalOff, + LinphoneGlobalStartup, + LinphoneGlobalOn, + LinphoneGlobalShutdown +}LinphoneGlobalState; -struct _LinphoneGeneralState { - gstate_t old_state; - gstate_t new_state; - gstate_group_t group; - const char *message; -}; -typedef struct _LinphoneGeneralState LinphoneGeneralState; - -/* private: set a new state */ -void gstate_new_state(struct _LinphoneCore *lc, gstate_t new_state, const char *message); -/*private*/ -void gstate_initialize(struct _LinphoneCore *lc) ; +typedef enum _LinphoneRegistrationState{ + LinphoneRegistrationNone, + LinphoneRegistrationProgress, + LinphoneRegistrationOk, + LinphoneRegistrationCleared, + LinphoneRegistrationFailed +}LinphoneRegistrationState; /** * @addtogroup initializing * @{ **/ + +/**Call state notification callback prototype*/ +typedef void (*LinphoneGlobalStateCb)(struct _LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); +/**Call state notification callback prototype*/ +typedef void (*LinphoneCallStateCb)(struct _LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message); +/**Registration state notification callback prototype*/ +typedef void (*LinphoneRegistrationStateCb)(struct _LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneCallState cstate, const char *message); /** Callback prototype */ typedef void (*ShowInterfaceCb)(struct _LinphoneCore *lc); /** Callback prototype */ -typedef void (*InviteReceivedCb)(struct _LinphoneCore *lc, const char *from); -/** Callback prototype */ -typedef void (*ByeReceivedCb)(struct _LinphoneCore *lc, const char *from); -/** Callback prototype */ typedef void (*DisplayStatusCb)(struct _LinphoneCore *lc, const char *message); /** Callback prototype */ typedef void (*DisplayMessageCb)(struct _LinphoneCore *lc, const char *message); /** Callback prototype */ typedef void (*DisplayUrlCb)(struct _LinphoneCore *lc, const char *message, const char *url); /** Callback prototype */ -typedef void (*DisplayQuestionCb)(struct _LinphoneCore *lc, const char *message); -/** Callback prototype */ typedef void (*LinphoneCoreCbFunc)(struct _LinphoneCore *lc,void * user_data); /** Callback prototype */ -typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, const char *from, const char *msg); +typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, LinphoneCall *call, const char *from, const char *event); /** Callback prototype */ typedef void (*NotifyPresenceReceivedCb)(struct _LinphoneCore *lc, LinphoneFriend * fid); /** Callback prototype */ @@ -427,9 +432,7 @@ typedef void (*CallLogUpdated)(struct _LinphoneCore *lc, struct _LinphoneCallLog /** Callback prototype */ typedef void (*TextMessageReceived)(struct _LinphoneCore *lc, LinphoneChatRoom *room, const char *from, const char *message); /** Callback prototype */ -typedef void (*GeneralStateChange)(struct _LinphoneCore *lc, LinphoneGeneralState *gstate); -/** Callback prototype */ -typedef void (*DtmfReceived)(struct _LinphoneCore* lc, int dtmf); +typedef void (*DtmfReceived)(struct _LinphoneCore* lc, LinphoneCall *call, int dtmf); /** Callback prototype */ typedef void (*ReferReceived)(struct _LinphoneCore *lc, const char *refer_to); /** Callback prototype */ @@ -437,28 +440,26 @@ typedef void (*BuddyInfoUpdated)(struct _LinphoneCore *lc, LinphoneFriend *lf); /** * This structure holds all callbacks that the application should implement. - * + * None is mandatory. **/ -typedef struct _LinphoneVTable -{ - ShowInterfaceCb show; /**< Notifies the application that it should show up*/ - InviteReceivedCb inv_recv; /**< Notifies incoming calls */ - ByeReceivedCb bye_recv; /**< Notify calls terminated by far end*/ +typedef struct _LinphoneVTable{ + LinphoneGlobalStateCb global_state_changed; /**data; + MSFilter *itcsink=ms_filter_new(MS_ITC_SINK_ID); + ms_filter_call_method(itcsink,MS_ITC_SINK_CONNECT,lsd->branches[0].player); + return itcsink; +} + +static MSSndCardDesc proxycard={ + "Linphone Sound Daemon", + /*detect*/ NULL, + /*init*/ NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + /*create_reader*/ NULL, + create_writer, + /*uninit,*/ +}; + +LsdPlayer *linphone_sound_daemon_get_player(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]; + MSFilter *p=b->player; + int state; + ms_filter_call_method(p,MS_PLAYER_GET_STATE,&state); + if (state==MSPlayerClosed){ + lsd_player_set_gain(b,1); + lsd_player_enable_loop (b,FALSE); + return b; + } + } + ms_warning("No more free players !"); + return NULL; +} + +void linphone_sound_daemon_release_player(LinphoneSoundDaemon *obj, LsdPlayer * player){ + int state; + ms_filter_call_method(player->player,MS_PLAYER_GET_STATE,&state); + if (state!=MSPlayerClosed){ + ms_filter_call_method(player->player,MS_PLAYER_CLOSE,&state); + } +} + +LinphoneSoundDaemon *lsd_player_get_daemon(const LsdPlayer *p){ + return p->lsd; +} + +int lsd_player_stop(LsdPlayer *p){ + ms_filter_call_method_noarg(p->player,MS_PLAYER_PAUSE); + return 0; +} + +static void lsd_player_init(LsdPlayer *p, MSConnectionPoint mixer, MSFilterId playerid, LinphoneSoundDaemon *lsd){ + MSConnectionHelper h; + p->player=ms_filter_new(playerid); + p->rateconv=ms_filter_new(MS_RESAMPLE_ID); + p->chanadapter=ms_filter_new(MS_CHANNEL_ADAPTER_ID); + + ms_connection_helper_start(&h); + ms_connection_helper_link(&h,p->player,-1,0); + ms_connection_helper_link(&h,p->rateconv,0,0); + ms_connection_helper_link(&h,p->chanadapter,0,0); + ms_connection_helper_link(&h,mixer.filter,mixer.pin,-1); + p->mixer_pin=mixer.pin; + p->loop=FALSE; + p->lsd=lsd; +} + +static void lsd_player_uninit(LsdPlayer *p, MSConnectionPoint mixer){ + MSConnectionHelper h; + + ms_connection_helper_start(&h); + ms_connection_helper_unlink (&h,p->player,-1,0); + ms_connection_helper_unlink(&h,p->rateconv,0,0); + ms_connection_helper_unlink(&h,p->chanadapter,0,0); + ms_connection_helper_unlink(&h,mixer.filter,mixer.pin,-1); + + ms_filter_destroy(p->player); + ms_filter_destroy(p->rateconv); + ms_filter_destroy(p->chanadapter); +} + +void lsd_player_set_callback(LsdPlayer *p, LsdEndOfPlayCallback cb){ + p->eop_cb=cb; +} + +void lsd_player_set_user_pointer(LsdPlayer *p, void *up){ + p->user_data=up; +} + +void *lsd_player_get_user_pointer(const LsdPlayer *p){ + return p->user_data; +} + +static void lsd_player_on_eop(void * userdata, unsigned int id, void *arg){ + LsdPlayer *p=(LsdPlayer *)userdata; + if (p->eop_cb!=NULL) + p->eop_cb(p); +} + +static void lsd_player_configure(LsdPlayer *b){ + int rate,chans; + LinphoneSoundDaemon *lsd=b->lsd; + + if (ms_filter_get_id(b->player)==MS_ITC_SOURCE_ID) + ms_message("Configuring branch coming from audio call..."); + + ms_filter_call_method(b->player,MS_FILTER_GET_SAMPLE_RATE,&rate); + ms_filter_call_method(b->player,MS_FILTER_GET_NCHANNELS,&chans); + + + ms_filter_call_method(b->rateconv,MS_FILTER_SET_SAMPLE_RATE,&rate); + ms_filter_call_method(b->rateconv,MS_FILTER_SET_NCHANNELS,&chans); + ms_filter_call_method(b->rateconv,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&lsd->out_rate); + + ms_filter_call_method(b->chanadapter,MS_FILTER_SET_NCHANNELS,&chans); + ms_filter_call_method(b->chanadapter,MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS,&lsd->out_nchans); + ms_message("player configured for rate=%i, channels=%i",rate,chans); +} + +int lsd_player_play(LsdPlayer *b, const char *filename ){ + int state; + + ms_filter_call_method(b->player,MS_PLAYER_GET_STATE,&state); + if (state!=MSPlayerClosed){ + ms_filter_call_method_noarg(b->player,MS_PLAYER_CLOSE); + } + + if (ms_filter_call_method(b->player,MS_PLAYER_OPEN,(void*)filename)!=0){ + ms_warning("Could not play %s",filename); + return -1; + } + ms_filter_set_notify_callback (b->player,lsd_player_on_eop,b); + lsd_player_configure(b); + ms_filter_call_method_noarg (b->player,MS_PLAYER_START); + return 0; +} + +void lsd_player_enable_loop(LsdPlayer *p, bool_t loopmode){ + if (ms_filter_get_id(p->player)==MS_FILE_PLAYER_ID){ + int arg=loopmode ? 0 : -1; + ms_filter_call_method(p->player,MS_FILE_PLAYER_LOOP,&arg); + p->loop=loopmode; + } +} + +bool_t lsd_player_loop_enabled(const LsdPlayer *p){ + return p->loop; +} + +void lsd_player_set_gain(LsdPlayer *p, float gain){ + MSAudioMixerCtl gainctl; + gainctl.pin=p->mixer_pin; + gainctl.gain=gain; + ms_filter_call_method(p->lsd->mixer,MS_AUDIO_MIXER_SET_INPUT_GAIN,&gainctl); +} + +LinphoneSoundDaemon * linphone_sound_daemon_new(const char *cardname, int rate, int nchannels){ + int i; + MSConnectionPoint mp; + LinphoneSoundDaemon *lsd; + MSSndCard *card=ms_snd_card_manager_get_card( + ms_snd_card_manager_get(), + cardname); + if (card==NULL){ + card=ms_snd_card_manager_get_default_playback_card ( + ms_snd_card_manager_get()); + if (card==NULL){ + ms_error("linphone_sound_daemon_new(): No playback soundcard available"); + return NULL; + } + } + + lsd=ms_new0(LinphoneSoundDaemon,1); + lsd->soundout=ms_snd_card_create_writer(card); + lsd->mixer=ms_filter_new(MS_AUDIO_MIXER_ID); + lsd->out_rate=rate; + lsd->out_nchans=nchannels; + ms_filter_call_method(lsd->soundout,MS_FILTER_SET_SAMPLE_RATE,&lsd->out_rate); + ms_filter_call_method(lsd->soundout,MS_FILTER_SET_NCHANNELS,&lsd->out_nchans); + ms_filter_call_method(lsd->mixer,MS_FILTER_SET_SAMPLE_RATE,&lsd->out_rate); + ms_filter_call_method(lsd->mixer,MS_FILTER_SET_NCHANNELS,&lsd->out_nchans); + + mp.filter=lsd->mixer; + mp.pin=0; + + lsd_player_init(&lsd->branches[0],mp,MS_ITC_SOURCE_ID,lsd); + ms_filter_set_notify_callback(lsd->branches[0].player,(MSFilterNotifyFunc)lsd_player_configure,&lsd->branches[0]); + ms_filter_enable_synchronous_notifcations (lsd->branches[0].player,TRUE); + for(i=1;ibranches[i],mp,MS_FILE_PLAYER_ID,lsd); + } + ms_filter_link(lsd->mixer,0,lsd->soundout,0); + lsd->ticker=ms_ticker_new(); + ms_ticker_attach(lsd->ticker,lsd->soundout); + + lsd->proxycard=ms_snd_card_new(&proxycard); + lsd->proxycard->data=lsd; + ms_message("LinphoneSoundDaemon started with rate=%i, nchannels=%i",rate,nchannels); + return lsd; +} + +void linphone_sound_daemon_stop_all_players(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]); + } +} + +void linphone_sound_daemon_release_all_players(LinphoneSoundDaemon *obj){ + int i; + for(i=1;ibranches[i]); + } +} + +void linphone_sound_daemon_destroy(LinphoneSoundDaemon *obj){ + int i; + MSConnectionPoint mp; + ms_ticker_detach(obj->ticker,obj->soundout); + mp.filter=obj->mixer; + for(i=0;ibranches[i]); + lsd_player_uninit (&obj->branches[i],mp); + } + ms_filter_unlink(obj->mixer,0,obj->soundout,0); + ms_ticker_destroy(obj->ticker); + ms_filter_destroy(obj->soundout); + ms_filter_destroy(obj->mixer); +} + +MSSndCard *linphone_sound_daemon_get_proxy_card(LinphoneSoundDaemon *lsd){ + return lsd->proxycard; +} + +void linphone_core_use_sound_daemon(LinphoneCore *lc, LinphoneSoundDaemon *lsd){ + if (lsd!=NULL){ + lc->sound_conf.lsd_card=linphone_sound_daemon_get_proxy_card (lsd); + }else { + lc->sound_conf.lsd_card=NULL; + } +} diff --git a/coreapi/misc.c b/coreapi/misc.c index 58f325d94..ced4f3396 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -497,10 +497,10 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ lc->vtable.display_status(lc,_("Stun lookup in progress...")); /*create the two audio and video RTP sockets, and send STUN message to our stun server */ - sock1=create_socket(linphone_core_get_audio_port(lc)); + sock1=create_socket(call->audio_port); if (sock1<0) return; if (video_enabled){ - sock2=create_socket(linphone_core_get_video_port(lc)); + sock2=create_socket(call->video_port); if (sock2<0) return ; } sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE); diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index d69a22ddd..6bd21863c 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -29,7 +29,10 @@ static PayloadType * find_payload_type_best_match(const MSList *l, const Payload for (elem=l;elem!=NULL;elem=elem->next){ pt=(PayloadType*)elem->data; - if (strcasecmp(pt->mime_type,refpt->mime_type)==0 && pt->clock_rate==refpt->clock_rate){ + /* the compare between G729 and G729A is for some stupid uncompliant phone*/ + if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0 || + (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 )) + && pt->clock_rate==refpt->clock_rate){ candidate=pt; /*good candidate, check fmtp for H264 */ if (strcasecmp(pt->mime_type,"H264")==0){ @@ -85,6 +88,8 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads); result->proto=local_offer->proto; result->type=local_offer->type; + result->dir=local_offer->dir; + if (result->payloads && !only_telephone_event(result->payloads)){ strcpy(result->addr,remote_answer->addr); result->port=remote_answer->port; @@ -102,6 +107,13 @@ static void initiate_incoming(const SalStreamDescription *local_cap, result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads); result->proto=local_cap->proto; result->type=local_cap->type; + if (remote_offer->dir==SalStreamSendOnly) + result->dir=SalStreamRecvOnly; + else if (remote_offer->dir==SalStreamRecvOnly){ + result->dir=SalStreamSendOnly; + }else if (remote_offer->dir==SalStreamInactive){ + result->dir=SalStreamInactive; + }else result->dir=SalStreamSendRecv; if (result->payloads && !only_telephone_event(result->payloads)){ strcpy(result->addr,local_cap->addr); result->port=local_cap->port; diff --git a/coreapi/presence.c b/coreapi/presence.c index 8d9d39474..57e764c38 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -92,36 +92,36 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, Sal char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; - LinphoneOnlineStatus estatus=LINPHONE_STATUS_OFFLINE; + LinphoneOnlineStatus estatus=LinphoneStatusOffline; switch(sal_status){ case SalPresenceOffline: - estatus=LINPHONE_STATUS_OFFLINE; + estatus=LinphoneStatusOffline; break; case SalPresenceOnline: - estatus=LINPHONE_STATUS_ONLINE; + estatus=LinphoneStatusOnline; break; case SalPresenceBusy: - estatus=LINPHONE_STATUS_BUSY; + estatus=LinphoneStatusBusy; break; case SalPresenceBerightback: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusBeRightBack; break; case SalPresenceAway: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusAway; break; case SalPresenceOnthephone: - estatus=LINPHONE_STATUS_ONTHEPHONE; + estatus=LinphoneStatusOnThePhone; break; case SalPresenceOuttolunch: - estatus=LINPHONE_STATUS_OUTTOLUNCH; + estatus=LinphoneStatusOutToLunch; break; case SalPresenceDonotdisturb: - estatus=LINPHONE_STATUS_BUSY; + estatus=LinphoneStatusDoNotDisturb; break; case SalPresenceMoved: case SalPresenceAltService: - estatus=LINPHONE_STATUS_AWAY; + estatus=LinphoneStatusMoved; break; } lf=linphone_find_friend_by_out_subscribe(lc->friends,op); @@ -130,7 +130,8 @@ void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, Sal tmp=linphone_address_as_string(friend); lf->status=estatus; lf->subscribe_active=TRUE; - lc->vtable.notify_presence_recv(lc,(LinphoneFriend*)lf); + if (lc->vtable.notify_presence_recv) + lc->vtable.notify_presence_recv(lc,(LinphoneFriend*)lf); ms_free(tmp); }else{ ms_message("But this person is not part of our friend list, so we don't care."); diff --git a/coreapi/private.h b/coreapi/private.h index 1faffaa67..bbb48d0cb 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -54,14 +54,6 @@ #endif #endif -typedef enum _LCState{ - LCStateInit, - LCStatePreEstablishing, - LCStateRinging, - LCStateAVRunning -}LCState; - - struct _LinphoneCall { struct _LinphoneCore *core; @@ -76,29 +68,36 @@ struct _LinphoneCall char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ time_t start_time; /*time at which the call was initiated*/ time_t media_start_time; /*time at which it was accepted, media streams established*/ - LCState state; + LinphoneCallState state; + int refcnt; + void * user_pointer; + int audio_port; + int video_port; + struct _AudioStream *audiostream; /**/ + struct _VideoStream *videostream; + char *refer_to; + bool_t refer_pending; bool_t media_pending; + bool_t audio_muted; }; LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to); LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op); -#define linphone_call_set_state(lcall,st) (lcall)->state=(st) -void linphone_call_destroy(struct _LinphoneCall *obj); +void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message); /* private: */ LinphoneCallLog * linphone_call_log_new(LinphoneCall *call, LinphoneAddress *local, LinphoneAddress * remote); -void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call); +void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call, LinphoneCallStatus status); void linphone_call_log_destroy(LinphoneCallLog *cl); - -void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call); - void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); void linphone_core_update_proxy_register(LinphoneCore *lc); void linphone_core_refresh_subscribes(LinphoneCore *lc); +int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *error); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphoneOnlineStatus os); +void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState rstate, const char *message); int linphone_online_status_to_eXosip(LinphoneOnlineStatus os); void linphone_friend_close_subscriptions(LinphoneFriend *lf); @@ -173,8 +172,11 @@ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg); -void linphone_core_start_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); -void linphone_core_stop_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); +void linphone_call_init_media_streams(LinphoneCall *call); +void linphone_call_start_media_streams(LinphoneCall *call); +void linphone_call_set_media_streams_dir(LinphoneCall *call, SalStreamDir dir); +void linphone_call_stop_media_streams(LinphoneCall *call); + const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); bool_t linphone_core_is_in_communication_with(LinphoneCore *lc, const char *to); @@ -183,7 +185,7 @@ void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float void linphone_core_stop_waiting(LinphoneCore *lc); int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); - +void linphone_core_start_pending_refered_calls(LinphoneCore *lc); extern SalCallbacks linphone_sal_callbacks; @@ -297,6 +299,7 @@ typedef struct sound_config struct _MSSndCard * ring_sndcard; /* the playback sndcard currently used */ struct _MSSndCard * play_sndcard; /* the playback sndcard currently used */ struct _MSSndCard * capt_sndcard; /* the capture sndcard currently used */ + struct _MSSndCard * lsd_card; /* dummy playback card for Linphone Sound Daemon extension */ const char **cards; int latency; /* latency in samples of the current used sound device */ char rec_lev; @@ -366,21 +369,20 @@ struct _LinphoneCore struct _RingStream *ringstream; time_t dmfs_playing_start_time; LCCallbackObj preview_finished_cb; - struct _LinphoneCall *call; /* the current call, in the future it will be a list of calls (conferencing)*/ + LinphoneCall *current_call; /* the current call */ + MSList *calls; /* all the processed calls */ MSList *queued_calls; /* used by the autoreplier */ MSList *call_logs; MSList *chatrooms; int max_call_logs; int missed_calls; - struct _AudioStream *audiostream; /**/ - struct _VideoStream *videostream; struct _VideoStream *previewstream; + struct _MSEventQueue *msevq; RtpTransport *a_rtp,*a_rtcp; MSList *bl_reqs; MSList *subscribers; /* unknown subscribers */ int minutes_away; LinphoneOnlineStatus presence_mode; - LinphoneOnlineStatus prev_mode; char *alt_contact; void *data; char *play_file; @@ -391,9 +393,6 @@ struct _LinphoneCore int dw_video_bw; int up_video_bw; int audio_bw; - gstate_t gstate_power; - gstate_t gstate_reg; - gstate_t gstate_call; LinphoneWaitingCallback wait_cb; void *wait_ctx; bool_t use_files; @@ -403,7 +402,21 @@ struct _LinphoneCore bool_t preview_finished; bool_t auto_net_state_mon; bool_t network_reachable; - bool_t audio_muted; }; +bool_t linphone_core_can_we_add_call(LinphoneCore *lc); +int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call); +int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call); +int linphone_core_set_as_current_call(LinphoneCore *lc, LinphoneCall *call); +int linphone_core_get_calls_nb(const LinphoneCore *lc); + +void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); + +#define HOLD_OFF (0) +#define HOLD_ON (1) + +#ifndef NB_MAX_CALLS +#define NB_MAX_CALLS (10) +#endif + #endif /* _PRIVATE_H */ diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 154ae06a2..848c8e4df 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -263,6 +263,7 @@ static char *guess_contact_for_register(LinphoneProxyConfig *obj){ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){ const char *id_str; + if (obj->reg_identity!=NULL) id_str=obj->reg_identity; else id_str=linphone_core_get_primary_contact(obj->lc); if (obj->reg_sendregister){ @@ -274,10 +275,10 @@ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){ sal_op_set_contact(obj->op,contact); ms_free(contact); sal_op_set_user_pointer(obj->op,obj); - if (!sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires)) { - gstate_new_state(obj->lc,GSTATE_REG_PENDING,NULL); + if (sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires)==0) { + linphone_proxy_config_set_state(obj,LinphoneRegistrationProgress,"Registration in progress"); } else { - gstate_new_state(obj->lc,GSTATE_REG_FAILED,NULL); + linphone_proxy_config_set_state(obj,LinphoneRegistrationFailed,"Registration failed"); } } } @@ -796,6 +797,12 @@ void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr) { return cr->user_data; } +void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrationState state, const char *message){ + LinphoneCore *lc=cfg->lc; + if (lc && lc->vtable.registration_state_changed){ + lc->vtable.registration_state_changed(lc,cfg,state,message); + } +} diff --git a/coreapi/sal.c b/coreapi/sal.c index 750555016..84f3d412b 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -62,11 +62,28 @@ const SalStreamDescription *sal_media_description_find_stream(const SalMediaDesc return NULL; } -bool_t sal_media_description_empty(SalMediaDescription *md){ +bool_t sal_media_description_empty(const SalMediaDescription *md){ + int i; + for(i=0;instreams;++i){ + const SalStreamDescription *ss=&md->streams[i]; + if (ss->port!=0) return FALSE; + } + return TRUE; +} + +void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir){ int i; for(i=0;instreams;++i){ SalStreamDescription *ss=&md->streams[i]; - if (ss->port!=0) return FALSE; + ss->dir=stream_dir; + } +} + +bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){ + int i; + for(i=0;instreams;++i){ + const SalStreamDescription *ss=&md->streams[i]; + if (ss->dir!=stream_dir) return FALSE; } return TRUE; } diff --git a/coreapi/sal.h b/coreapi/sal.h index d8d02edd9..7c6c7867b 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -86,6 +86,13 @@ typedef enum{ SalProtoRtpSavp }SalMediaProto; +typedef enum{ + SalStreamSendRecv, + SalStreamSendOnly, + SalStreamRecvOnly, + SalStreamInactive +}SalStreamDir; + typedef struct SalEndpointCandidate{ char addr[64]; int port; @@ -102,6 +109,7 @@ typedef struct SalStreamDescription{ int bandwidth; int ptime; SalEndpointCandidate candidates[SAL_ENDPOINT_CANDIDATE_MAX]; + SalStreamDir dir; } SalStreamDescription; #define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4 @@ -118,9 +126,11 @@ typedef struct SalMediaDescription{ SalMediaDescription *sal_media_description_new(); void sal_media_description_ref(SalMediaDescription *md); void sal_media_description_unref(SalMediaDescription *md); -bool_t sal_media_description_empty(SalMediaDescription *md); +bool_t sal_media_description_empty(const SalMediaDescription *md); +bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir dir); const SalStreamDescription *sal_media_description_find_stream(const SalMediaDescription *md, SalMediaProto proto, SalStreamType type); +void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir); /*this structure must be at the first byte of the SalOp structure defined by implementors*/ typedef struct SalOpBase{ @@ -177,9 +187,9 @@ typedef void (*SalOnCallReceived)(SalOp *op); typedef void (*SalOnCallRinging)(SalOp *op); typedef void (*SalOnCallAccepted)(SalOp *op); typedef void (*SalOnCallAck)(SalOp *op); -typedef void (*SalOnCallUpdated)(SalOp *op); +typedef void (*SalOnCallUpdating)(SalOp *op);/*< Called when a reINVITE is received*/ typedef void (*SalOnCallTerminated)(SalOp *op, const char *from); -typedef void (*SalOnCallFailure)(SalOp *op, SalError error, SalReason reason, const char *details); +typedef void (*SalOnCallFailure)(SalOp *op, SalError error, SalReason reason, const char *details, int code); typedef void (*SalOnAuthRequested)(SalOp *op, const char *realm, const char *username); typedef void (*SalOnAuthSuccess)(SalOp *op, const char *realm, const char *username); typedef void (*SalOnRegisterSuccess)(SalOp *op, bool_t registered); @@ -200,7 +210,7 @@ typedef struct SalCallbacks{ SalOnCallRinging call_ringing; SalOnCallAccepted call_accepted; SalOnCallAck call_ack; - SalOnCallUpdated call_updated; + SalOnCallUpdating call_updating; SalOnCallTerminated call_terminated; SalOnCallFailure call_failure; SalOnAuthRequested auth_requested; @@ -263,8 +273,10 @@ void *sal_op_get_user_pointer(const SalOp *op); int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc); int sal_call(SalOp *h, const char *from, const char *to); int sal_call_notify_ringing(SalOp *h); +/*accept an incoming call or, during a call accept a reINVITE*/ int sal_call_accept(SalOp*h); int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/); +int sal_call_hold(SalOp *h, bool_t holdon); SalMediaDescription * sal_call_get_final_media_description(SalOp *h); int sal_refer(SalOp *h, const char *refer_to); int sal_refer_accept(SalOp *h); @@ -306,5 +318,4 @@ void __sal_op_init(SalOp *b, Sal *sal); void __sal_op_set_network_origin(SalOp *op, const char *origin /*a sip uri*/); void __sal_op_free(SalOp *b); - #endif diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index 2d90a4731..25c79e582 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -284,8 +284,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){ ctx->callbacks.call_failure=(SalOnCallFailure)unimplemented_stub; if (ctx->callbacks.call_terminated==NULL) ctx->callbacks.call_terminated=(SalOnCallTerminated)unimplemented_stub; - if (ctx->callbacks.call_updated==NULL) - ctx->callbacks.call_updated=(SalOnCallUpdated)unimplemented_stub; + if (ctx->callbacks.call_updating==NULL) + ctx->callbacks.call_updating=(SalOnCallUpdating)unimplemented_stub; if (ctx->callbacks.auth_requested==NULL) ctx->callbacks.auth_requested=(SalOnAuthRequested)unimplemented_stub; if (ctx->callbacks.auth_success==NULL) @@ -654,9 +654,13 @@ int sal_call_send_dtmf(SalOp *h, char dtmf){ } int sal_call_terminate(SalOp *h){ + int err; eXosip_lock(); - eXosip_call_terminate(h->cid,h->did); + err=eXosip_call_terminate(h->cid,h->did); eXosip_unlock(); + if (err!=0){ + ms_warning("Exosip could not terminate the call: cid=%i did=%i", h->cid,h->did); + } sal_remove_call(h->base.root,h); h->cid=-1; return 0; @@ -769,35 +773,27 @@ static void handle_reinvite(Sal *sal, eXosip_event_t *ev){ sal_media_description_unref(op->base.remote_media); op->base.remote_media=NULL; } - eXosip_lock(); - eXosip_call_build_answer(ev->tid,200,&msg); - eXosip_unlock(); - if (msg==NULL) return; - if (op->base.root->session_expires!=0){ - if (op->supports_session_timers) osip_message_set_supported(msg, "timer"); - } - if (op->base.contact){ - _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); - osip_message_set_contact(msg,op->base.contact); + if (op->result){ + sal_media_description_unref(op->result); + op->result=NULL; } if (sdp){ op->sdp_offering=FALSE; op->base.remote_media=sal_media_description_new(); sdp_to_media_description(sdp,op->base.remote_media); sdp_message_free(sdp); - sdp_process(op); - if (op->sdp_answer!=NULL){ - set_sdp(msg,op->sdp_answer); - sdp_message_free(op->sdp_answer); - op->sdp_answer=NULL; - } + sal->callbacks.call_updating(op); }else { op->sdp_offering=TRUE; - set_sdp_from_desc(msg,op->base.local_media); + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&msg); + if (msg!=NULL){ + set_sdp_from_desc(msg,op->base.local_media); + eXosip_call_send_answer(ev->tid,200,msg); + } + eXosip_unlock(); } - eXosip_lock(); - eXosip_call_send_answer(ev->tid,200,msg); - eXosip_unlock(); + } static void handle_ack(Sal *sal, eXosip_event_t *ev){ @@ -816,7 +812,7 @@ static void handle_ack(Sal *sal, eXosip_event_t *ev){ sdp_message_free(sdp); } if (op->reinvite){ - sal->callbacks.call_updated(op); + if (sdp) sal->callbacks.call_updating(op); op->reinvite=FALSE; }else{ sal->callbacks.call_ack(op); @@ -855,7 +851,8 @@ static int call_proceeding(Sal *sal, eXosip_event_t *ev){ eXosip_unlock(); return -1; } - op->did=ev->did; + if (ev->did>0) + op->did=ev->did; op->tid=ev->tid; /* update contact if received and rport are set by the server @@ -938,7 +935,7 @@ static void call_released(Sal *sal, eXosip_event_t *ev){ } op->cid=-1; if (op->did==-1) - sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL); + sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL, 487); } static int get_auth_data_from_response(osip_message_t *resp, const char **realm, const char **username){ @@ -1100,7 +1097,7 @@ static bool_t call_failure(Sal *sal, eXosip_event_t *ev){ sr=SalReasonUnknown; }else error=SalErrorNoResponse; } - sal->callbacks.call_failure(op,error,sr,reason); + sal->callbacks.call_failure(op,error,sr,reason,code); if (computedReason != NULL){ ms_free(computedReason); } @@ -1777,3 +1774,32 @@ int sal_address_get_port_int(const SalAddress *uri) { } } +/* + * Send a re-Invite used to hold the current call +*/ +int sal_call_hold(SalOp *h, bool_t holdon) +{ + int err=0; + + osip_message_t *reinvite=NULL; + if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL) + return -1; + osip_message_set_subject(reinvite,osip_strdup("Phone Call Hold")); + osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + if (h->base.root->session_expires!=0){ + osip_message_set_header(reinvite, "Session-expires", "200"); + osip_message_set_supported(reinvite, "timer"); + } + //add something to say that the distant sip phone will be in sendonly/sendrecv mode + if (h->base.local_media){ + h->sdp_offering=TRUE; + sal_media_description_set_dir(h->base.local_media, holdon ? SalStreamSendOnly : SalStreamSendRecv); + set_sdp_from_desc(reinvite,h->base.local_media); + }else h->sdp_offering=FALSE; + eXosip_lock(); + err = eXosip_call_send_request(h->did, reinvite); + eXosip_unlock(); + + return err; +} + diff --git a/coreapi/sal_eXosip2_sdp.c b/coreapi/sal_eXosip2_sdp.c index 2165708b1..7d63148af 100644 --- a/coreapi/sal_eXosip2_sdp.c +++ b/coreapi/sal_eXosip2_sdp.c @@ -106,6 +106,23 @@ static int _sdp_message_get_a_ptime(sdp_message_t *sdp, int mline){ return 0; } +static int _sdp_message_get_mline_dir(sdp_message_t *sdp, int mline){ + int i; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,mline,i))!=NULL;i++){ + if (keywordcmp("sendrecv",attr->a_att_field)==0){ + return SalStreamSendRecv; + }else if (keywordcmp("sendonly",attr->a_att_field)==0){ + return SalStreamSendOnly; + }else if (keywordcmp("recvonly",attr->a_att_field)==0){ + return SalStreamSendOnly; + }else if (keywordcmp("inactive",attr->a_att_field)==0){ + return SalStreamInactive; + } + } + return SalStreamSendRecv; +} + static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) { sdp_message_t *local; @@ -121,12 +138,21 @@ static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) osip_strdup ("IN"), inet6 ? osip_strdup("IP6") : osip_strdup ("IP4"), osip_strdup (desc->addr)); sdp_message_s_name_set (local, osip_strdup ("A conversation")); - sdp_message_c_connection_add (local, -1, - osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), - osip_strdup (desc->addr), NULL, NULL); + if(!sal_media_description_has_dir (desc,SalStreamSendOnly)) + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (desc->addr), NULL, NULL); + } + else + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + inet6 ? osip_strdup ("::0") : osip_strdup ("0.0.0.0"), NULL, NULL); + } sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); if (desc->bandwidth>0) sdp_message_b_bandwidth_add (local, -1, osip_strdup ("AS"), - int_2char(desc->bandwidth)); + int_2char(desc->bandwidth)); return local; } @@ -158,6 +184,7 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription const char *mt=desc->type==SalAudio ? "audio" : "video"; const MSList *elem; const char *addr; + const char *dir="sendrecv"; int port; if (desc->candidates[0].addr[0]!='\0'){ addr=desc->candidates[0].addr; @@ -186,6 +213,21 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription for(elem=desc->payloads;elem!=NULL;elem=elem->next){ add_payload(msg, lineno, (PayloadType*)elem->data); } + switch(desc->dir){ + case SalStreamSendRecv: + dir="sendrecv"; + break; + case SalStreamRecvOnly: + dir="recvonly"; + break; + case SalStreamSendOnly: + dir="sendonly"; + break; + case SalStreamInactive: + dir="inactive"; + break; + } + sdp_message_a_attribute_add (msg, lineno, osip_strdup (dir),NULL); } sdp_message_t *media_description_to_sdp(const SalMediaDescription *desc){ @@ -272,6 +314,7 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ for(j=0;(sbw=sdp_message_bandwidth_get(msg,i,j))!=NULL;++j){ if (strcasecmp(sbw->b_bwtype,"AS")==0) stream->bandwidth=atoi(sbw->b_bandwidth); } + stream->dir=_sdp_message_get_mline_dir(msg,i); /* for each payload type */ for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){ const char *rtpmap,*fmtp; diff --git a/coreapi/test_lsd.c b/coreapi/test_lsd.c new file mode 100644 index 000000000..c362aa4bc --- /dev/null +++ b/coreapi/test_lsd.c @@ -0,0 +1,103 @@ +/* +linphone +Copyright (C) 2010 Simon MORLAT (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. +*/ + +/* Linphone Sound Daemon: is a lightweight utility to play sounds to speaker during a conversation. + This is useful for embedded platforms, where sound apis are not performant enough to allow + simultaneous sound access. + + This file is a test program that plays several sound files and places a call simultatenously. +*/ + +#include "linphonecore_utils.h" + +static void play_finished(LsdPlayer *p){ + const char *filename=(const char *)lsd_player_get_user_pointer (p); + ms_message("Playing of %s is finished.",filename); + if (!lsd_player_loop_enabled (p)){ + linphone_sound_daemon_release_player (lsd_player_get_daemon(p),p); + } +} + +static void wait_a_bit(LinphoneCore *lc, int seconds){ + time_t orig=time(NULL); + while(time(NULL)-orig1){ + linphone_core_invite(lc,argv[1]); + wait_a_bit(lc,10); + linphone_core_terminate_call(lc,NULL); + } + linphone_core_use_sound_daemon(lc,NULL); + linphone_sound_daemon_destroy(lsd); + linphone_core_destroy(lc); + + return 0; +} diff --git a/gtk-glade/friendlist.c b/gtk-glade/friendlist.c index d4fddf871..3805057c3 100644 --- a/gtk-glade/friendlist.c +++ b/gtk-glade/friendlist.c @@ -39,18 +39,18 @@ typedef struct _status_picture_tab_t{ } status_picture_tab_t; status_picture_tab_t status_picture_tab[]={ - { LINPHONE_STATUS_ONLINE, "status-green.png" }, - { LINPHONE_STATUS_BUSY, "status-orange.png" }, - { LINPHONE_STATUS_BERIGHTBACK, "status-orange.png" }, - { LINPHONE_STATUS_AWAY, "status-orange.png" }, - { LINPHONE_STATUS_ONTHEPHONE, "status-orange.png" }, - { LINPHONE_STATUS_OUTTOLUNCH, "status-orange.png" }, - { LINPHONE_STATUS_NOT_DISTURB, "status-red.png" }, - { LINPHONE_STATUS_MOVED, "status-orange.png" }, - { LINPHONE_STATUS_ALT_SERVICE, "status-orange.png" }, - { LINPHONE_STATUS_OFFLINE, "status-offline.png" }, - { LINPHONE_STATUS_PENDING, "status-offline.png" }, - { LINPHONE_STATUS_END, NULL }, + { LinphoneStatusOnline, "status-green.png" }, + { LinphoneStatusBusy, "status-orange.png" }, + { LinphoneStatusBeRightBack, "status-orange.png" }, + { LinphoneStatusAway, "status-orange.png" }, + { LinphoneStatusOnThePhone, "status-orange.png" }, + { LinphoneStatusOutToLunch, "status-orange.png" }, + { LinphoneStatusDoNotDisturb, "status-red.png" }, + { LinphoneStatusMoved, "status-orange.png" }, + { LinphoneStatusAltService, "status-orange.png" }, + { LinphoneStatusOffline, "status-offline.png" }, + { LinphoneStatusPending, "status-offline.png" }, + { LinphoneStatusEnd, NULL }, }; static GdkPixbuf *create_status_picture(LinphoneOnlineStatus ss){ @@ -132,7 +132,7 @@ static GtkWidget * create_presence_menu(){ GdkPixbuf *pbuf; status_picture_tab_t *t; for(t=status_picture_tab;t->img!=NULL;++t){ - if (t->status==LINPHONE_STATUS_PENDING){ + if (t->status==LinphoneStatusPending){ continue; } menu_item=gtk_image_menu_item_new_with_label(linphone_online_status_to_string(t->status)); @@ -317,7 +317,7 @@ void linphone_gtk_show_friends(void){ continue; } } - if (!online_only || (linphone_friend_get_status(lf)!=LINPHONE_STATUS_OFFLINE)){ + if (!online_only || (linphone_friend_get_status(lf)!=LinphoneStatusOffline)){ BuddyInfo *bi; if (name==NULL || name[0]=='\0') display=uri; gtk_list_store_append(store,&iter); diff --git a/gtk-glade/incall_view.c b/gtk-glade/incall_view.c index c0ddfcbbd..4d60d6309 100644 --- a/gtk-glade/incall_view.c +++ b/gtk-glade/incall_view.c @@ -124,7 +124,7 @@ void linphone_gtk_in_call_view_set_in_call(){ GtkWidget *duration=linphone_gtk_get_widget(main_window,"in_call_duration"); GtkWidget *animation=linphone_gtk_get_widget(main_window,"in_call_animation"); GdkPixbufAnimation *pbuf=create_pixbuf_animation("incall_anim.gif"); - const LinphoneAddress *uri=linphone_core_get_remote_uri(lc); + const LinphoneAddress *uri=linphone_core_get_current_call_remote_address(lc); char *tmp=linphone_address_as_string(uri); display_peer_name_in_label(callee,tmp); ms_free(tmp); @@ -137,7 +137,9 @@ void linphone_gtk_in_call_view_set_in_call(){ g_object_unref(G_OBJECT(pbuf)); }else gtk_image_set_from_stock(GTK_IMAGE(animation),GTK_STOCK_INFO,GTK_ICON_SIZE_DIALOG); linphone_gtk_enable_mute_button( - GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"incall_mute")),TRUE); + GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"incall_mute")),TRUE); + linphone_gtk_enable_hold_button( + GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"hold_call")),TRUE); } void linphone_gtk_in_call_view_update_duration(int duration){ @@ -175,6 +177,8 @@ void linphone_gtk_in_call_view_terminate(const char *error_msg){ } linphone_gtk_enable_mute_button( GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"incall_mute")),FALSE); + linphone_gtk_enable_hold_button( + GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window,"hold_call")),FALSE); g_timeout_add_seconds(2,(GSourceFunc)in_call_view_terminated,NULL); } @@ -202,7 +206,54 @@ void linphone_gtk_mute_toggled(GtkToggleButton *button){ linphone_gtk_draw_mute_button(button,active); } -void linphone_gtk_enable_mute_button(GtkToggleButton *button, gboolean sensitive){ +void linphone_gtk_enable_mute_button(GtkToggleButton *button, gboolean sensitive) +{ gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive); linphone_gtk_draw_mute_button(button,FALSE); } + +void linphone_gtk_draw_hold_button(GtkToggleButton *button, gboolean active){ + if (active){ + GtkWidget *image=create_pixmap("hold_off.png"); + gtk_button_set_label(GTK_BUTTON(button),_("HoldOff")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + }else{ + GtkWidget *image=create_pixmap("hold_on.png"); + gtk_button_set_label(GTK_BUTTON(button),_("HoldOn")); + if (image!=NULL) { + gtk_button_set_image(GTK_BUTTON(button),image); + gtk_widget_show(image); + } + } +} + +void linphone_gtk_hold_toggled(GtkToggleButton *button){ + gboolean active=gtk_toggle_button_get_active(button); + + if(active) + { + LinphoneCall *call=linphone_core_get_current_call (linphone_gtk_get_core()); + if (call==NULL) return; + linphone_core_pause_call(linphone_gtk_get_core(),call); + } + else + { + const MSList *calls=linphone_core_get_calls(linphone_gtk_get_core()); + if (calls==NULL) return; + if (ms_list_size(calls)>1){ + g_warning("Simultaneously calls not yet implemented in gtk ui."); + return; + } + /*we are supposed to have only one */ + linphone_core_resume_call(linphone_gtk_get_core(),(LinphoneCall*)calls->data); + } + linphone_gtk_draw_hold_button(button,active); +} + +void linphone_gtk_enable_hold_button(GtkToggleButton *button, gboolean sensitive){ + gtk_widget_set_sensitive(GTK_WIDGET(button),sensitive); + linphone_gtk_draw_hold_button(button,FALSE); +} diff --git a/gtk-glade/linphone.h b/gtk-glade/linphone.h index b6dcd5df1..850277155 100644 --- a/gtk-glade/linphone.h +++ b/gtk-glade/linphone.h @@ -92,6 +92,7 @@ void linphone_gtk_in_call_view_set_in_call(void); void linphone_gtk_in_call_view_update_duration(int duration); void linphone_gtk_in_call_view_terminate(const char *error_msg); void linphone_gtk_enable_mute_button(GtkToggleButton *button, gboolean sensitive); +void linphone_gtk_enable_hold_button(GtkToggleButton *button, gboolean sensitive); void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg); diff --git a/gtk-glade/main.c b/gtk-glade/main.c index 5fa9c6895..ddae9ff90 100644 --- a/gtk-glade/main.c +++ b/gtk-glade/main.c @@ -42,8 +42,7 @@ static LinphoneCore *the_core=NULL; static GtkWidget *the_ui=NULL; static void linphone_gtk_show(LinphoneCore *lc); -static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from); -static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from); +static void linphone_gtk_inv_recv(LinphoneCore *lc, LinphoneCall *call); static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid); static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username); @@ -51,30 +50,11 @@ static void linphone_gtk_display_status(LinphoneCore *lc, const char *status); static void linphone_gtk_display_message(LinphoneCore *lc, const char *msg); static void linphone_gtk_display_warning(LinphoneCore *lc, const char *warning); static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const char *url); -static void linphone_gtk_display_question(LinphoneCore *lc, const char *question); static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl); -static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate); -static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to); +static void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to); +static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg); static gboolean linphone_gtk_auto_answer(GtkWidget *incall_window); -static LinphoneCoreVTable vtable={ - .show=linphone_gtk_show, - .inv_recv=linphone_gtk_inv_recv, - .bye_recv=linphone_gtk_bye_recv, - .notify_presence_recv=linphone_gtk_notify_recv, - .new_unknown_subscriber=linphone_gtk_new_unknown_subscriber, - .auth_info_requested=linphone_gtk_auth_info_requested, - .display_status=linphone_gtk_display_status, - .display_message=linphone_gtk_display_message, - .display_warning=linphone_gtk_display_warning, - .display_url=linphone_gtk_display_url, - .display_question=linphone_gtk_display_question, - .call_log_updated=linphone_gtk_call_log_updated, - .text_received=linphone_gtk_text_received, - .general_state=linphone_gtk_general_state, - .refer_received=linphone_gtk_refer_received, - .buddy_info_updated=linphone_gtk_buddy_info_updated -}; static gboolean verbose=0; static gboolean auto_answer = 0; @@ -211,6 +191,22 @@ static const char *linphone_gtk_get_factory_config_file(){ static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file) { + LinphoneCoreVTable vtable={0}; + + vtable.call_state_changed=linphone_gtk_call_state_changed; + vtable.show=linphone_gtk_show; + vtable.notify_presence_recv=linphone_gtk_notify_recv; + vtable.new_unknown_subscriber=linphone_gtk_new_unknown_subscriber; + vtable.auth_info_requested=linphone_gtk_auth_info_requested; + vtable.display_status=linphone_gtk_display_status; + vtable.display_message=linphone_gtk_display_message; + vtable.display_warning=linphone_gtk_display_warning; + vtable.display_url=linphone_gtk_display_url; + vtable.call_log_updated=linphone_gtk_call_log_updated; + vtable.text_received=linphone_gtk_text_received; + vtable.refer_received=linphone_gtk_refer_received; + vtable.buddy_info_updated=linphone_gtk_buddy_info_updated; + linphone_core_set_user_agent("Linphone", LINPHONE_VERSION); the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); @@ -390,7 +386,7 @@ static void set_video_window_decorations(GdkWindow *w){ gdk_window_set_keep_above(w, FALSE); }else{ LinphoneAddress *uri = - linphone_address_clone(linphone_core_get_remote_uri(linphone_gtk_get_core())); + linphone_address_clone(linphone_core_get_current_call_remote_address(linphone_gtk_get_core())); char *display_name; linphone_address_clean(uri); @@ -588,7 +584,7 @@ static void linphone_gtk_call_started(GtkWidget *mw){ static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){ const char *entered=gtk_entry_get_text(GTK_ENTRY(uri_bar)); - if (linphone_core_invite(linphone_gtk_get_core(),entered)==0) { + if (linphone_core_invite(linphone_gtk_get_core(),entered)!=NULL) { completion_add_text(GTK_ENTRY(uri_bar),entered); }else{ linphone_gtk_call_terminated(NULL); @@ -643,7 +639,9 @@ void linphone_gtk_uri_bar_activate(GtkWidget *w){ void linphone_gtk_terminate_call(GtkWidget *button){ - linphone_core_terminate_call(linphone_gtk_get_core(),NULL); + const MSList *elem=linphone_core_get_calls(linphone_gtk_get_core()); + if (elem==NULL) return; + linphone_core_terminate_call(linphone_gtk_get_core(),(LinphoneCall*)elem->data); } void linphone_gtk_decline_call(GtkWidget *button){ @@ -704,10 +702,11 @@ static void linphone_gtk_show(LinphoneCore *lc){ linphone_gtk_show_main_window(); } -static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from){ +static void linphone_gtk_inv_recv(LinphoneCore *lc, LinphoneCall *call){ GtkWidget *w=linphone_gtk_create_window("incoming_call"); GtkWidget *label; gchar *msg; + char *from=linphone_call_get_remote_address_as_string(call); if (auto_answer){ g_timeout_add(2000,(GSourceFunc)linphone_gtk_auto_answer,w); @@ -727,10 +726,7 @@ static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from){ g_object_set_data(G_OBJECT(linphone_gtk_get_main_window()),"incoming_call",w); gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar")), from); -} - -static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from){ - + ms_free(from); } static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){ @@ -868,31 +864,29 @@ static void linphone_gtk_display_url(LinphoneCore *lc, const char *msg, const ch linphone_gtk_display_something(GTK_MESSAGE_INFO,richtext); } -static void linphone_gtk_display_question(LinphoneCore *lc, const char *question){ - linphone_gtk_display_something(GTK_MESSAGE_QUESTION,question); -} - static void linphone_gtk_call_log_updated(LinphoneCore *lc, LinphoneCallLog *cl){ GtkWidget *w=(GtkWidget*)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"call_logs"); if (w) linphone_gtk_call_log_update(w); } -static void linphone_gtk_general_state(LinphoneCore *lc, LinphoneGeneralState *gstate){ - switch(gstate->new_state){ - case GSTATE_CALL_OUT_CONNECTED: - case GSTATE_CALL_IN_CONNECTED: +static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){ + switch(cs){ + case LinphoneCallConnected: if (linphone_gtk_use_in_call_view()) linphone_gtk_in_call_view_set_in_call(); linphone_gtk_enable_mute_button( GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"main_mute")), TRUE); break; - case GSTATE_CALL_ERROR: - linphone_gtk_call_terminated(gstate->message); + case LinphoneCallError: + linphone_gtk_call_terminated(msg); break; - case GSTATE_CALL_END: + case LinphoneCallEnd: linphone_gtk_call_terminated(NULL); break; + case LinphoneCallIncomingReceived: + linphone_gtk_inv_recv (lc,call); + break; default: break; } @@ -1145,6 +1139,8 @@ static void linphone_gtk_init_main_window(){ "main_mute")),FALSE); linphone_gtk_enable_mute_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window, "incall_mute")),FALSE); + linphone_gtk_enable_hold_button(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(main_window, + "hold_call")),FALSE); if (!linphone_gtk_use_in_call_view()) { gtk_widget_show(linphone_gtk_get_widget(main_window, "main_mute")); } diff --git a/gtk-glade/main.glade b/gtk-glade/main.glade index b858892fb..67701fb23 100644 --- a/gtk-glade/main.glade +++ b/gtk-glade/main.glade @@ -1190,6 +1190,7 @@ Fiber Channel True + spread Mute @@ -1205,6 +1206,21 @@ Fiber Channel 0 + + + HoldOn + True + False + True + True + + + + False + False + 1 + + False diff --git a/gtk-glade/propertybox.c b/gtk-glade/propertybox.c index bab3e3af6..be4073fb9 100644 --- a/gtk-glade/propertybox.c +++ b/gtk-glade/propertybox.c @@ -119,17 +119,17 @@ void linphone_gtk_video_port_changed(GtkWidget *w){ void linphone_gtk_no_firewall_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_NO_FIREWALL); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyNoFirewall); } void linphone_gtk_use_nat_address_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_USE_NAT_ADDRESS); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseNatAddress); } void linphone_gtk_use_stun_toggled(GtkWidget *w){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - linphone_core_set_firewall_policy(linphone_gtk_get_core(),LINPHONE_POLICY_USE_STUN); + linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseStun); } void linphone_gtk_mtu_changed(GtkWidget *w){ @@ -762,13 +762,13 @@ void linphone_gtk_show_parameters(void){ if (tmp) gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(pb,"stun_server")),tmp); pol=linphone_core_get_firewall_policy(lc); switch(pol){ - case LINPHONE_POLICY_NO_FIREWALL: + case LinphonePolicyNoFirewall: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"no_nat")),TRUE); break; - case LINPHONE_POLICY_USE_NAT_ADDRESS: + case LinphonePolicyUseNatAddress: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_nat_address")),TRUE); break; - case LINPHONE_POLICY_USE_STUN: + case LinphonePolicyUseStun: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_stun")),TRUE); break; } diff --git a/mediastreamer2 b/mediastreamer2 index 332d79209..ea7ca97aa 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 332d79209b333bd8ded5b16a7b9dcfe2c0aedc63 +Subproject commit ea7ca97aaaa6f191dfec112f137fb048a4ef2139 diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am index bf2f03a8c..b462cf8e7 100644 --- a/pixmaps/Makefile.am +++ b/pixmaps/Makefile.am @@ -3,6 +3,7 @@ pixmapdir=$(datadir)/pixmaps/linphone pixmap_DATA= \ +hold_on.png hold_off.png \ mic_muted.png mic_active.png \ linphone-3-250x130.png linphone-3.png linphone2-57x57.png \ linphone.png linphone-banner.png \ diff --git a/pixmaps/hold_off.png b/pixmaps/hold_off.png new file mode 100644 index 000000000..61ab330c6 Binary files /dev/null and b/pixmaps/hold_off.png differ diff --git a/pixmaps/hold_on.png b/pixmaps/hold_on.png new file mode 100644 index 000000000..94469b2d6 Binary files /dev/null and b/pixmaps/hold_on.png differ diff --git a/po/POTFILES.in b/po/POTFILES.in index 2b0f2734f..3fcfbdd20 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -72,3 +72,5 @@ mediastreamer2/src/audiomixer.c mediastreamer2/src/chanadapt.c mediastreamer2/src/itc.c mediastreamer2/src/extdisplay.c +mediastreamer2/src/msiounit.c +