diff --git a/console/commands.c b/console/commands.c index 2f07bbdad..c3e1e472f 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 *); @@ -131,7 +132,10 @@ LPC_COMMAND commands[] = { { "help", lpc_cmd_help, "Print commands help", NULL }, { "call", lpc_cmd_call, "Call a SIP uri", "'call ' \t: initiate a call to the specified destination.\n" - "'call show' \t: show all the current calls status.\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\"' " @@ -139,12 +143,12 @@ LPC_COMMAND commands[] = { }, { "terminate", lpc_cmd_terminate, "Terminate a call", "'terminate' : Terminate the current call\n" - "'terminate ' : Terminate the call with remote address\n" + "'terminate ' : Terminate the call with supplied id\n" "'terminate ' : Terminate all the current calls\n" }, { "answer", lpc_cmd_answer, "Answer a call", "'answer' : Answer the current incoming call\n" - "'answer ' : Answer the call with remote address\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" @@ -177,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" @@ -245,7 +249,7 @@ LPC_COMMAND commands[] = { "'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 cid \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, @@ -398,6 +402,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) { @@ -405,36 +438,14 @@ lpc_cmd_call(LinphoneCore *lc, char *args) { return 0; } - if(!strcmp(args,"show")) - { - const MSList *calls = linphone_core_get_calls(lc); - if(calls) - { - const MSList *p_calls = calls; - linphonec_out("\t\t\t\t\r\n"); - while(p_calls != NULL) - { - char *tmp=linphone_call_get_remote_address_as_string(p_calls->data); - linphonec_out("%s\t\t\t%s\r\n", - tmp, - (((LinphoneCall *)p_calls->data)==linphone_core_get_current_call(lc))?"yes":"no"); - p_calls = p_calls->next; - ms_free(tmp); - } - } - else - { - linphonec_out("No active call.\n"); - } - } - else { + LinphoneCall *call; if ( linphone_core_in_call(lc) ) { linphonec_out("Terminate or hold on the current call first.\n"); return 1; } - if ( NULL == linphone_core_invite(lc, args) ) + if ( NULL == (call=linphone_core_invite(lc, args)) ) { linphonec_out("Error from linphone_core_invite.\n"); } @@ -446,6 +457,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) { @@ -488,12 +525,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, linphone_core_get_current_call(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; } @@ -501,96 +560,64 @@ lpc_cmd_refer(LinphoneCore *lc, char *args) static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { - char *arg1 = args; - char *arg2 = NULL; - char *ptr = args; - + if (linphone_core_get_calls(lc)==NULL){ + linphonec_out("No active calls"); + return 1; + } if (!args) { - if(linphone_core_in_call(lc)) - { - if ( -1 == linphone_core_terminate_call(lc, linphone_core_get_current_call(lc)) ) - { - linphonec_out("Could not stop the active call.\n"); - } - } - else - { - linphonec_out("No active call.\n"); + if ( -1 == linphone_core_terminate_call(lc, NULL) ){ + linphonec_out("Could not stop the active call.\n"); } return 1; } - /* Isolate first and second arg */ - while(*ptr && !isspace(*ptr)) ++ptr; - if ( *ptr ) - { - *ptr='\0'; - arg2=ptr+1; - while(*arg2 && isspace(*arg2)) ++arg2; - } - if (arg1 != 0) - { - if(strcmp(arg1,"all")==0) - { - linphonec_out("We are going to stop all the calls.\n"); - return (linphone_core_terminate_all_calls(lc)==0)?1:0; - } - else - { - char the_remote_address[255]; - int n = sscanf(arg1, "%s", the_remote_address); - if (n == 1) - { - if ( -1 == linphone_core_terminate_call(lc,linphone_core_get_call_by_remote_address(lc,the_remote_address))) - { - linphonec_out("Cannot stop the call with %s.\n",the_remote_address); - } - 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) -{ - char *arg1 = args; - char *arg2 = NULL; - char *ptr = args; - +lpc_cmd_answer(LinphoneCore *lc, char *args){ if (!args) { - //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("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; - } - - // Isolate first and second arg - while(*ptr && !isspace(*ptr)) ++ptr; - if ( *ptr ) - { - *ptr='\0'; - arg2=ptr+1; - while(*arg2 && isspace(*arg2)) ++arg2; - } - if (arg1 != 0) - { - char the_remote_address[256]; - int n = sscanf(arg1, "%s", the_remote_address); - if (n == 1) - { - if ( -1 == linphone_core_accept_call(lc, linphone_core_get_call_by_remote_address(lc,the_remote_address)) ) - { - linphonec_out("Cannot answer the call from %s.\n",the_remote_address); + }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); } - return 1; - } + }else return 0; + return 1; } return 0; } @@ -1209,40 +1236,42 @@ 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) { - char the_remote_address[255]; - int n = sscanf(args, "%s", the_remote_address); - if (n == 1) - { - if(linphone_core_resume_call(lc,linphone_core_get_call_by_remote_address(lc,the_remote_address)) < 0) - { - linphonec_out("There was a problem to resume the call check the remote address you gave %s\n",args); - return 0; - } - else + 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 1; } - } + }else return 0; } else { - int returned = 0; const MSList *calls = linphone_core_get_calls(lc); - if(ms_list_size(calls) == 1) + 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"); - returned = 0; + linphonec_out("There was a problem to resume the unique call.\n"); } - else - { - returned = 1; - } - return returned; + 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; diff --git a/console/linphonec.c b/console/linphonec.c index c00d87632..df86abb8f 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -118,7 +118,7 @@ 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 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, @@ -169,6 +169,24 @@ static ortp_pipe_t server_sock; #endif /*_WIN32_WCE*/ +void linphonec_call_identify(LinphoneCall* call){ + static long callid=1; + linphone_call_set_user_pointer (call,(void*)callid); + callid++; +} + +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; +} + /*************************************************************************** * * Linphone core callbacks @@ -252,13 +270,12 @@ 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)); } } @@ -291,27 +308,34 @@ linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, 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: - printf("Call with %s ended.\n", from); + linphonec_out("Call %i with %s ended.\n", id, from); break; case LinphoneCallResuming: - printf("Resuming call with %s.\n", from); + linphonec_out("Resuming call %i with %s.\n", id, from); break; case LinphoneCallStreamsRunning: - printf("Media streams established with %s.\n", from); + linphonec_out("Media streams established with %s for call %i.\n", from,id); break; case LinphoneCallPausing: - printf("Pausing call with %s.\n", from); + linphonec_out("Pausing call %i with %s.\n", id, from); break; case LinphoneCallPaused: - printf("Call with %s is now paused.\n", from); + 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; @@ -682,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/callbacks.c b/coreapi/callbacks.c index aa8bf3f30..6d0cd8b8f 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -95,8 +95,8 @@ static void call_received(SalOp *h){ if (lc->vtable.display_status) lc->vtable.display_status(lc,barmesg); - /* play the ring */ - if (lc->sound_conf.ring_sndcard!=NULL && !linphone_core_in_call(lc)){ + /* 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==NULL){ MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; ms_message("Starting local ring..."); @@ -106,6 +106,8 @@ static void call_received(SalOp *h){ { ms_message("the local ring is already started"); } + }else{ + /*TODO : play a tone within the context of the current call */ } sal_call_notify_ringing(h); #if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000) @@ -198,6 +200,16 @@ static void call_accepted(SalOp *op){ 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)"); } @@ -241,7 +253,7 @@ static void call_ack(SalOp *op){ } /* this callback is called when an incoming re-INVITE modifies the session*/ -static void call_updated(SalOp *op){ +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); if (call->resultdesc) @@ -254,6 +266,13 @@ static void call_updated(SalOp *op){ { 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)"); @@ -263,12 +282,18 @@ static void call_updated(SalOp *op){ 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){ @@ -459,12 +484,18 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); 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); @@ -479,10 +510,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){ @@ -525,7 +556,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/linphonecall.c b/coreapi/linphonecall.c index fbb4f0212..871d00e57 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -87,7 +87,7 @@ static int find_port_offset(LinphoneCore *lc){ MSList *elem; int audio_port; bool_t already_used=FALSE; - for(offset=0;offset<100;++offset){ + 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){ @@ -204,13 +204,17 @@ static void linphone_call_set_terminated(LinphoneCall *call){ } linphone_call_log_completed(call->log,call, status); - if (linphone_core_del_call(lc,call) != 0){ - ms_error("Could not remove the call from the list !!!"); - } + 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); @@ -226,7 +230,11 @@ static void linphone_call_set_terminated(LinphoneCall *call){ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message){ LinphoneCore *lc=call->core; if (call->state!=cstate){ - 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); } @@ -251,6 +259,9 @@ static void linphone_call_destroy(LinphoneCall *obj) if (obj->ping_op) { sal_op_release(obj->ping_op); } + if (obj->refer_to){ + ms_free(obj->refer_to); + } ms_free(obj); } @@ -336,7 +347,7 @@ LinphoneCallLog *linphone_call_get_call_log(const LinphoneCall *call){ } /** - * Returns the refer-to uri (if the call received was transfered). + * Returns the refer-to uri (if the call was transfered). **/ const char *linphone_call_get_refer_to(const LinphoneCall *call){ return call->refer_to; @@ -346,6 +357,18 @@ 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; +} + /** * @} **/ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 6cf8725f3..38f69d23d 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1671,25 +1671,22 @@ void linphone_core_iterate(LinphoneCore *lc){ call = linphone_core_get_current_call(lc); if(call) { - if (call->state==LinphoneCallConnected) + if (one_second_elapsed) { - if (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); - } -#ifdef VIDEO_ENABLED + RtpSession *as=NULL,*vs=NULL; + lc->prevtime=curtime; + if (call->audiostream!=NULL) + as=call->audiostream->session; if (call->videostream!=NULL) - video_stream_iterate(call->videostream); -#endif - if (call->audiostream!=NULL && disconnect_timeout>0) - disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout); + vs=call->videostream->session; + display_bandwidth(as,vs); } +#ifdef VIDEO_ENABLED + if (call->videostream!=NULL) + video_stream_iterate(call->videostream); +#endif + 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 && lc->calls==NULL) @@ -1835,6 +1832,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; @@ -2036,7 +2046,13 @@ LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddr return call; } -int linphone_core_refer(LinphoneCore *lc, LinphoneCall *call, 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); @@ -2053,6 +2069,7 @@ int linphone_core_refer(LinphoneCore *lc, LinphoneCall *call, 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; } @@ -2114,7 +2131,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) MSList *elem; for(elem=lc->calls;elem!=NULL;elem=elem->next){ LinphoneCall *c=(LinphoneCall*)elem->data; - if (c!=call && (c->state!=LinphoneCallPaused || c->state!=LinphoneCallPausing)){ + if (c!=call && (c->state!=LinphoneCallPaused)){ ms_warning("Cannot accept this call as another one is running, pause it before."); return -1; } @@ -2181,8 +2198,10 @@ int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *the_call) LinphoneCall *call; if (the_call == NULL){ call = linphone_core_get_current_call(lc); - if(call == NULL){ - ms_warning("No currently active call to terminate !"); + if (ms_list_size(lc->calls)==1){ + call=(LinphoneCall*)lc->calls->data; + }else{ + ms_warning("No unique call to terminate !"); return -1; } } @@ -2276,6 +2295,7 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *the_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; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index a1ed9d317..d0c680d92 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -172,6 +172,7 @@ 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); @@ -419,7 +420,7 @@ typedef void (*DisplayUrlCb)(struct _LinphoneCore *lc, const char *message, cons /** 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 */ @@ -509,7 +510,7 @@ LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url); LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr); -int linphone_core_refer(LinphoneCore *lc, LinphoneCall *call, const char *url); +int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *refer_to); bool_t linphone_core_inc_invite_pending(LinphoneCore*lc); diff --git a/coreapi/private.h b/coreapi/private.h index e5d0e9055..cb3ff78e2 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -76,6 +76,7 @@ struct _LinphoneCall struct _AudioStream *audiostream; /**/ struct _VideoStream *videostream; char *refer_to; + bool_t refer_pending; bool_t media_pending; bool_t audio_muted; }; @@ -184,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; diff --git a/coreapi/sal.h b/coreapi/sal.h index fd3f6f21b..7c6c7867b 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -187,7 +187,7 @@ 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, int code); typedef void (*SalOnAuthRequested)(SalOp *op, const char *realm, const char *username); @@ -210,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; @@ -273,6 +273,7 @@ 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); diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index ec0b7aff8..8e4afe22e 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) @@ -773,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){ @@ -820,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); diff --git a/mediastreamer2 b/mediastreamer2 index 236222b3f..6cc9076b9 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 236222b3f08baf502742b6c75633f50e3a14917f +Subproject commit 6cc9076b9cc4d6b88e7a0b93e6abdd1ad881e832 diff --git a/oRTP b/oRTP index a08462074..d2a8cb089 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit a084620745b1b1c81ec93501ffbb3de373f7c8c9 +Subproject commit d2a8cb0890c7547703a092c839c55db10449ef92 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 +