diff --git a/console/commands.c b/console/commands.c index d32e626..9bed3de 100644 --- a/console/commands.c +++ b/console/commands.c @@ -68,20 +68,21 @@ static int lpc_cmd_soundcard(LinphoneCore *, char *); static int lpc_cmd_play(LinphoneCore *, char *); static int lpc_cmd_record(LinphoneCore *, char *); static int lpc_cmd_register(LinphoneCore *, char *); static int lpc_cmd_unregister(LinphoneCore *, char *); static int lpc_cmd_duration(LinphoneCore *lc, char *args); static int lpc_cmd_status(LinphoneCore *lc, char *args); static int lpc_cmd_ports(LinphoneCore *lc, char *args); static int lpc_cmd_speak(LinphoneCore *lc, char *args); static int lpc_cmd_codec(LinphoneCore *lc, char *args); static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args); +static int lpc_cmd_hold(LinphoneCore *lc, char *args); /* Command handler helpers */ static void linphonec_proxy_add(LinphoneCore *lc); static void linphonec_proxy_display(LinphoneProxyConfig *lc); static void linphonec_proxy_list(LinphoneCore *lc); static void linphonec_proxy_remove(LinphoneCore *lc, int index); static int linphonec_proxy_use(LinphoneCore *lc, int index); static void linphonec_proxy_show(LinphoneCore *lc,int index); static void linphonec_friend_display(LinphoneFriend *fr); static int linphonec_friend_list(LinphoneCore *lc, char *arg); @@ -113,24 +114,28 @@ void linphonec_out(const char *fmt,...); /* * Commands table. */ 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." }, - { "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 the cid \n" + "'terminate ' : Terminate all the current calls\n" + }, { "answer", lpc_cmd_answer, "Answer a call", - "Accept an incoming call." + "'answer' : Answer the current call\n" + "'answer ' : Answer the call with the cid \n" }, { "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode", "'autoanswer' \t: show current autoanswer mode\n" "'autoanswer enable'\t: enable autoanswer mode\n" "'autoanswer disable'\t: disable autoanswer modeĀ \n"}, { "proxy", lpc_cmd_proxy, "Manage proxies", "'proxy list' : list all proxy setups.\n" "'proxy add' : add a new proxy setup.\n" "'proxy remove ' : remove proxy setup with number index.\n" "'proxy use ' : use proxy with number index as default proxy.\n" @@ -202,20 +207,24 @@ LPC_COMMAND commands[] = { "Example for english voice: 'speak default Hello my friend !'" }, { "codec", lpc_cmd_codec, "Codec configuration", "'codec list' : list codecs\n" "'codec enable ' : enable available codec\n" "'codec disable ' : disable codecs" }, { "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" }, + { "hold", lpc_cmd_hold, "Hold a call", + "'hold on' : hold on the current call\n" + "'hold off ' : hold off the call with cid \n" + "'hold show' : show hold call status\n" }, { (char *)NULL, (lpc_cmd_handler)NULL, (char *)NULL, (char *)NULL } }; /*************************************************************************** * * Public interface * ***************************************************************************/ /* @@ -353,35 +362,27 @@ lpc_cmd_help(LinphoneCore *lc, char *arg) static char callee_name[256]={0}; static char caller_name[256]={0}; static int lpc_cmd_call(LinphoneCore *lc, char *args) { if ( ! args || ! *args ) { return 0; } - - if ( lc->call != NULL ) + if ( -1 == linphone_core_invite(lc, args) ) { - linphonec_out("Terminate current call first.\n"); + linphonec_out("Error from linphone_core_invite.\n"); } else { - if ( -1 == linphone_core_invite(lc, args) ) - { - linphonec_out("Error from linphone_core_invite.\n"); - } - else - { - snprintf(callee_name,sizeof(callee_name),"%s",args); - } + snprintf(callee_name,sizeof(callee_name),"%s",args); } return 1; } const char *linphonec_get_callee(){ return callee_name; } const char *linphonec_get_caller(){ return caller_name; @@ -398,35 +399,116 @@ lpc_cmd_refer(LinphoneCore *lc, char *args) linphone_core_refer(lc, args); else{ linphonec_out("refer needs an argument\n"); } return 1; } static int lpc_cmd_terminate(LinphoneCore *lc, char *args) { - if ( -1 == linphone_core_terminate_call(lc, NULL) ) + char *arg1 = args; + char *arg2 = NULL; + char *ptr = args; + int the_cid,n; + + if (!args) + { + if ( -1 == linphone_core_terminate_call(lc, NULL) ) + { + linphonec_out("No active call.\n"); + } + return 1; + } + + /* Isolate first and second arg */ + while(*ptr && !isspace(*ptr)) ++ptr; + if ( *ptr ) { - linphonec_out("No active call.\n"); + *ptr='\0'; + arg2=ptr+1; + while(*arg2 && isspace(*arg2)) ++arg2; } - return 1; + if (arg1 != 0) + { + if(strcmp(arg1,"all")==0) + { + int i; + linphonec_out("We are going to stop all the calls.\n"); + for( i = (linphone_core_get_calls_nb(lc)-1); i >=0 ;i--) + { + the_cid=lc->calls[i]->cid; + if ( -1 == linphone_core_terminate_call(lc, &the_cid) ) + { + linphonec_out("Cannot stop the call with cid <%d>.\n",the_cid); + } + else + { + linphonec_out("Terminate the call with cid <%d>.\n",the_cid); + } + } + return 1; + } + else + { + n = sscanf(arg1, "%d", &the_cid); + if (n == 1) + { + if ( -1 == linphone_core_terminate_call(lc, &the_cid) ) + { + linphonec_out("Cannot stop the call with cid <%d>.\n",the_cid); + } + return 1; + } + } + } + return 0; } static int lpc_cmd_answer(LinphoneCore *lc, char *args) { - if ( -1 == linphone_core_accept_call(lc, NULL) ) + char *arg1 = args; + char *arg2 = NULL; + char *ptr = args; + int the_cid,n; + + if (!args) { - linphonec_out("No incoming call.\n"); + if ( -1 == linphone_core_accept_call(lc, NULL) ) + { + linphonec_out("you have to specify the call cid that you want to answer to.\n"); + } + return 1; } - 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) + { + n = sscanf(arg1, "%d", &the_cid); + if (n == 1) + { + if ( -1 == linphone_core_accept_call(lc, &the_cid) ) + { + linphonec_out("Cannot answer the call with cid <%d>.\n",the_cid); + } + return 1; + } + } + return 0; } static int lpc_cmd_autoanswer(LinphoneCore *lc, char *args) { if ( ! args ) { if ( linphonec_get_autoanswer() ) { linphonec_out("Auto answer is enabled. Use 'autoanswer disable' to disable.\n"); } else { @@ -1676,20 +1758,78 @@ static int lpc_cmd_echocancellation(LinphoneCore *lc, char *args){ lp_config_get_int(lc->config,"sound","ec_tail_len",0), lp_config_get_int(lc->config,"sound","ec_framesize",0)); } else { return 0; } return 1; } +static int lpc_cmd_hold(LinphoneCore *lc, char *args){ + char *arg1 = args; + char *arg2 = NULL; + char *ptr = args; + int the_cid,n; + + if (!args) return 0; + + /* Isolate first and second arg */ + while(*ptr && !isspace(*ptr)) ++ptr; + if ( *ptr ) + { + *ptr='\0'; + arg2=ptr+1; + while(*arg2 && isspace(*arg2)) ++arg2; + } + + if (strcmp(arg1,"on")==0){ + linphone_core_hold(lc,HOLD_ON,NULL); + } + else if (strcmp(arg1,"off")==0){ + if (arg2 != 0) { + n = sscanf(arg2, "%d", &the_cid); + if (n == 1) + { + linphone_core_hold(lc,HOLD_OFF,&the_cid); + } + } + else + { + linphonec_out("show off with the cid needed\n"); + } + } + else if (strcmp(arg1,"show")==0){ + if(linphone_core_get_calls_nb(lc)) + { + int i; + linphonec_out("\t\t\t\t\t\t\t\t\t\r\n"); + for(i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i] != NULL) + linphonec_out(" %d\t%s\t%s\t\t %d\r\n",lc->calls[i]->cid,linphone_address_as_string(lc->calls[i]->log->from),linphone_address_as_string(lc->calls[i]->log->to),lc->calls[i]->currently_used); + else + linphonec_out("wrong number from linphone_core_get_calls_nb\r\n"); + } + } + else + { + linphonec_out("no call in process\r\n"); + } + } + else { + return 0; + } + + return 1; +} + /*************************************************************************** * * Command table management funx * ***************************************************************************/ /* * Find a command given its name */ static LPC_COMMAND * diff --git a/console/linphonec.c b/console/linphonec.c index 2e0ca83..c172d86 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -725,21 +725,24 @@ void linphonec_main_loop_exit(void){ /* * Close linphonec, cleanly terminating * any pending call */ void linphonec_finish(int exit_status) { printf("Terminating...\n"); /* Terminate any pending call */ - linphonec_parse_command_line(linphonec, "terminate"); + char buf[256]; + printf("Terminating...\n"); + snprintf (buf, sizeof(buf),"terminate all"); + linphonec_parse_command_line(linphonec, buf); linphonec_command_finished(); #ifdef HAVE_READLINE linphonec_finish_readline(); #endif #if !defined(_WIN32_WCE) if (pipe_reader_run) stop_pipe_reader(); #endif /*_WIN32_WCE*/ linphone_core_destroy (linphonec); diff --git a/coreapi/exevents.c b/coreapi/exevents.c index 4dcee01..973fa3b 100644 --- a/coreapi/exevents.c +++ b/coreapi/exevents.c @@ -20,177 +20,181 @@ 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); + LinphoneCall *the_call = linphone_core_get_call_by_cid(lc,ev->cid); + if (the_call == NULL ) { + ms_warning("This call has been canceled: ev->cid=%i",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; + the_call->cid=ev->cid; + the_call->did=ev->did; + the_call->tid=ev->tid; } -static void linphone_connect_incoming(LinphoneCore *lc){ +static void linphone_connect_incoming(LinphoneCore *lc, eXosip_event_t *ev){ + LinphoneCall *the_call=linphone_core_get_call_by_cid(lc,ev->cid); lc->vtable.show(lc); lc->vtable.display_status(lc,_("Connected.")); - lc->call->state=LCStateAVRunning; + the_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); + if(!linphone_core_is_in_conversation(lc)) + { + linphone_core_start_media_streams(lc,the_call); + } + else + { + char temp[256]; + snprintf(temp,sizeof(temp),"hold on the incoming call with cid <%d> because we are already in call",ev->cid); + lc->vtable.display_status(lc,temp); + linphone_core_hold(lc,HOLD_ON,&ev->cid);//hold on the incoming call during a conversation } - linphone_core_start_media_streams(lc,lc->call); } int linphone_call_accepted(LinphoneCore *lc, eXosip_event_t *ev) { - LinphoneCall *call=lc->call; + LinphoneCall *call; sdp_message_t *sdp; const char *sdpanswer=NULL; osip_message_t *msg=NULL; int err; - if (call==NULL){ + if(linphone_core_get_calls_nb(lc)==0) + { ms_warning("No call to accept."); return 0; } linphone_call_proceeding(lc,ev); - if (!linphone_call_matches_event(lc->call,ev)) return 0; + call = linphone_core_get_call_by_cid(lc,ev->cid); + if(call == NULL) 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); + linphone_connect_incoming(lc,ev); } /*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; - } + char temp[256]; + LinphoneCall *call; + call = linphone_core_get_call_by_cid(lc,ev->cid); + if(call == NULL) + { + snprintf(temp,sizeof(temp),"the call <%d> was not in use.",ev->cid); + ms_message("%s",temp); + lc->vtable.display_warning(lc,temp); + } + else + { + snprintf(temp,sizeof(temp),"the call with cid <%d> is going to stop...\r\n",ev->cid); + ms_message("%s",temp); + lc->vtable.display_status(lc,temp); } - - ms_message("Current call terminated..."); if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc); + linphone_core_stop_media_streams(lc,call); lc->vtable.show(lc); - lc->vtable.display_status(lc,_("Call terminated.")); + snprintf(temp,sizeof(temp),"Call with cid <%d> terminated.",ev->cid); + lc->vtable.display_status(lc,temp); 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; - } + linphone_core_clear_call_by_cid(lc,ev->cid); return 0; } int linphone_call_released(LinphoneCore *lc, int cid){ - LinphoneCall *call=lc->call; + LinphoneCall *call=linphone_core_get_call_by_cid(lc,cid); if (call!=NULL && call->cid==cid){ - - linphone_call_destroy(lc->call); - lc->call=NULL; + linphone_core_clear_call_by_cid(lc,cid); 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; + LinphoneCall *call=linphone_core_get_call_by_cid(lc,ev->cid); - 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(call !=NULL) + { + 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) + if (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: @@ -234,57 +238,56 @@ int linphone_call_failure(LinphoneCore *lc, eXosip_event_t *ev) 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); + linphone_core_stop_media_streams(lc,call); if (call!=NULL) { - linphone_call_destroy(call); + linphone_core_clear_call_by_cid(lc,ev->cid); if (code!=487) 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; + LinphoneCall *the_call = linphone_core_get_call_by_cid(lc,ev->cid); + ctx=the_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; + LinphoneCall *the_call; 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); @@ -308,191 +311,214 @@ int linphone_inc_new_call(LinphoneCore *lc, eXosip_event_t *ev) 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*/ + if(!linphone_core_can_we_add_call(lc)) {//we have too much call send 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); - + the_call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),ev); + linphone_core_get_a_new_call(lc,the_call); + if(linphone_core_get_current_call(lc)!=NULL) //TODO useful ? + {//we already in call just put the incoming call in hold + char temp[256]; + snprintf(temp,sizeof(temp),"new incoming call with cid <%d> during call",ev->cid); + lc->vtable.display_status(lc,temp); + } 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.")); + barmesg=ortp_strdup_printf("%s %s%d%s",tmp,_("is contacting you with cid <"),ev->cid,_(">.")); lc->vtable.show(lc); lc->vtable.display_status(lc,barmesg); - /* play the ring */ - if (lc->sound_conf.ring_sndcard!=NULL){ + /* play the ring if we do not have a current call*/ + if (lc->sound_conf.ring_sndcard!=NULL && !linphone_core_is_in_conversation(lc)){ 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); + else + { + ms_message("avoid to ring because we are in conversation or we do not have a sndcard to ring with"); + } + linphone_call_set_state(the_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; + linphone_core_clear_call_by_cid(lc,ev->cid); } 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){ + LinphoneCall *the_call = linphone_core_get_call_by_cid(lc,ev->cid); 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_context_read_answer(the_call->sdpctx,sdp); + linphone_connect_incoming(lc,ev); 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; + LinphoneCall *call=linphone_core_get_call_by_cid(lc,ev->cid); 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); + lc->vtable.display_status(lc,_("we have been reinvite")); 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); + linphone_core_stop_media_streams(lc,call); 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(); + if((linphone_core_is_in_conversation(lc) && (linphone_core_get_cid_of_the_current_call(lc) == call->cid))) 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){ +void linphone_do_automatic_redirect(LinphoneCore *lc, const char *contact, eXosip_event_t *ev){ 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_clear_call_by_cid(lc,ev->cid); 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; + linphone_core_clear_call_by_cid(lc,ev->cid); break; case 302: - linphone_do_automatic_redirect(lc,contact); + linphone_do_automatic_redirect(lc,contact,ev); 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; - + if(ctx->holdon!=HOLD_ON) + { elem=lc->codecs_conf.audio_codecs; while(elem!=NULL){ codec=(PayloadType*) elem->data; if (linphone_core_check_payload_type_usability(lc,codec) && payload_type_enabled(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; + payload.localport=get_payload_audio_localport(lc,call); 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); + } + if(ctx->holdon==HOLD_ON) + { + sdp_payload_init(&payload); + payload.holdon=HOLD_ON; + payload.localport=get_payload_audio_localport(lc,call); + sdp_context_add_audio_payload(ctx,&payload); + } + else + { + sdp_payload_init(&payload); + payload.holdon=HOLD_OFF; + payload.localport=get_payload_audio_localport(lc,call); + 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) ){ @@ -529,41 +555,59 @@ static int find_payload_type_number_best_match(RtpProfile *prof, const char *rtp 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) && payload_type_enabled(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; + if(ctx->holdon!=HOLD_ON) + { + 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) && payload_type_enabled(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=get_payload_video_localport(lc,call); + 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); } - sdp_context_add_video_payload(ctx,&payload); - ms_free(payload.a_rtpmap); } } + if(ctx->holdon) + { + sdp_payload_t payload; + sdp_payload_init(&payload); + payload.holdon=ctx->holdon; + payload.localport=get_payload_video_localport(lc,call); + sdp_context_add_video_payload(ctx,&payload); + + } + else + { + sdp_payload_t payload; + sdp_payload_init(&payload); + payload.holdon=HOLD_OFF; + payload.localport=get_payload_video_localport(lc,call); + sdp_context_add_video_payload(ctx,&payload); + } 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) @@ -661,23 +705,22 @@ int linphone_accept_audio_offer(sdp_context_t *ctx,sdp_payload_t *payload) 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->localport=get_audio_localport(lc,call); + payload->localport=get_payload_audio_localport(lc,call); 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; @@ -725,22 +768,22 @@ int linphone_accept_video_offer(sdp_context_t *ctx,sdp_payload_t *payload) 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->localport=get_video_localport(lc,call); + payload->localport=get_payload_video_localport(lc,call); 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; @@ -768,21 +811,21 @@ int linphone_read_audio_answer(sdp_context_t *ctx,sdp_payload_t *payload) 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->localport=get_audio_localport(lc,call); 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; @@ -807,93 +850,98 @@ int linphone_read_video_answer(sdp_context_t *ctx,sdp_payload_t *payload) /* 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->localport=get_video_localport(lc,call); 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; + LinphoneCall *call=linphone_core_get_call_by_cid(lc,ev->cid); 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); + if(!linphone_core_is_in_conversation(lc)) + lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); + else + ms_message("avoid to play the ring during a call"); } }else{ /*accept early media */ StreamParams *audio_params; if (call==NULL){ ms_error("No call ?"); goto end; } - if (lc->audiostream->ticker!=NULL){ + if (call->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); + sdp_context_read_answer(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); + linphone_core_start_media_streams(lc,call);//??? Do not really know when this is used and could be a problem ??? } 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); + LinphoneCall *call=linphone_core_get_call_by_cid(lc,ev->cid); + if(call == NULL) return ; + if (call->videostream) + video_stream_send_vfu(call->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); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 62adf68..dcb66d7 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -96,20 +96,51 @@ static void discover_mtu(LinphoneCore *lc, const char *remote){ /*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()); } } } +int get_audio_localport(LinphoneCore *lc,LinphoneCall *call) +{ + int returned_value; + returned_value = (lc->rtp_conf.audio_rtp_port + call->repere); + return returned_value; +} + +int get_video_localport(LinphoneCore *lc,LinphoneCall *call) +{ + int returned_value; + returned_value = (lc->rtp_conf.video_rtp_port + call->repere); + return returned_value; +} +int get_payload_audio_localport(LinphoneCore *lc,LinphoneCall *call) +{ + int returned_value; + StreamParams *params; + params=&call->audio_params; + returned_value = params->natd_port>0 ?(params->natd_port + call->repere) : get_audio_localport(lc,call); + return returned_value; +} + +int get_payload_video_localport(LinphoneCore *lc,LinphoneCall *call) +{ + int returned_value; + StreamParams *params; + params=&call->video_params; + returned_value = params->natd_port>0 ? (params->natd_port + call->repere) : get_video_localport(lc,call); + return returned_value; +} + LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to) { LinphoneCall *call=ms_new0(LinphoneCall,1); call->dir=LinphoneCallOutgoing; call->cid=-1; call->did=-1; call->tid=-1; call->core=lc; linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); @@ -140,22 +171,22 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro call->sdpctx=sdp_handler_create_context(&linphone_sdphandler, call->audio_params.natd_port>0 ? call->audio_params.natd_addr : call->localip, linphone_address_get_username (me),NULL); sdp_context_set_user_pointer(call->sdpctx,(void*)call); discover_mtu(lc,linphone_address_get_domain(from)); linphone_address_destroy(me); osip_message_header_get_byname(ev->request,"Session-expires",0,&h); if (h) call->supports_session_timers=TRUE; return call; } - -void linphone_call_destroy(LinphoneCall *obj) +//should not be used anymore with multicall +void linphone_call_destroyed(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->profile!=NULL) rtp_profile_destroy(obj->profile); if (obj->sdpctx!=NULL) sdp_context_free(obj->sdpctx); ms_free(obj); } /*prevent a gcc bug with %c*/ @@ -368,29 +399,194 @@ const char *linphone_call_log_get_ref_key(const LinphoneCallLog *cl){ /** @} */ void linphone_call_log_destroy(LinphoneCallLog *cl){ if (cl->from!=NULL) linphone_address_destroy(cl->from); if (cl->to!=NULL) linphone_address_destroy(cl->to); if (cl->refkey!=NULL) ms_free(cl->refkey); ms_free(cl); } +int linphone_core_get_calls_nb(const LinphoneCore *lc) +{ + return lc->calls_nb; +} + +bool_t linphone_core_can_we_add_call(LinphoneCore *lc) +{ + if(linphone_core_get_calls_nb(lc) < NB_MAX_CALLS) + return 1; + return 0; +} + +int linphone_core_get_a_new_call( LinphoneCore *lc, LinphoneCall *call) +{ + if(linphone_core_can_we_add_call(lc)) + { + lc->calls_nb++; + lc->calls[(lc->calls_nb-1)] = call; + lc->calls[(lc->calls_nb-1)]->repere = (lc->calls_nb-1); + call->sdpctx->session_id = (lc->calls[(lc->calls_nb-1)]->repere + 1);//avoiding to be 0 + return 0; + } + return -1; +} + +int linphone_core_get_repere_from_call( LinphoneCall *call) +{ + if(call == NULL) + { + ms_warning("you try to get a repere from a NULL call"); + return -1; + } + return call->repere; +} + +int linphone_core_get_call_cid( LinphoneCall *call) +{ + if(call == NULL) + { + ms_warning("you try to get a cid from a NULL call"); + return -1; + } + return call->cid; +} + +LinphoneCall *linphone_core_get_current_call_with_const(const LinphoneCore *lc) +{ + int i; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i]!= NULL) + { + if(lc->calls[i]->currently_used) + return lc->calls[i]; + } + } + return NULL; +} + +LinphoneCall *linphone_core_get_current_call( LinphoneCore *lc) +{ + int i; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i]!= NULL) + { + if(lc->calls[i]->currently_used) + return lc->calls[i]; + } + } + return NULL; +} + +int linphone_core_get_cid_of_the_current_call( LinphoneCore *lc) +{ + int i; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i]!= NULL) + { + if(lc->calls[i]->currently_used) + return lc->calls[i]->cid; + } + } + return -1; +} + +LinphoneCall *linphone_core_get_call_by_cid( LinphoneCore *lc,int cid) +{ + int i; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i]!= NULL) + { + if(lc->calls[i]->cid == cid) + return lc->calls[i]; + } + } + return NULL; +} + +int linphone_core_clear_call_by_cid(LinphoneCore *lc,int cid) +{ + int i,j; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i] != NULL) + { + if(lc->calls[i]->cid == cid) + { + linphone_call_destroyed(lc->calls[i]); + lc->calls[i] = NULL; + lc->calls_nb--; + ms_message("DEBUG : finish destroying still %d calls\r\n",lc->calls_nb); + for(j = (i+1); j < (linphone_core_get_calls_nb(lc)+1); j++) + { + lc->calls[j-1] = lc->calls[j]; + } + return 0; + } + } + } + return -1; +} + +bool_t linphone_core_is_in_conversation(LinphoneCore *lc) +{ + int i; + + for (i = 0; i < linphone_core_get_calls_nb(lc); i++) + { + if(lc->calls[i] != NULL) + { + if(lc->calls[i]->currently_used) + { + return TRUE; + } + } + } + return FALSE; +} + +int linphone_core_set_current_call_by_cid( LinphoneCore *lc, int cid) +{ + LinphoneCall *lcall; + if(linphone_core_get_current_call(lc)!=NULL) + { + lc->vtable.display_status(lc,_("Already one call active")); + return -1; + } + lcall = linphone_core_get_call_by_cid(lc,cid); + if(lcall == NULL) + { + return -2; + } + lcall->currently_used = TRUE; + return 0; +} + int linphone_core_get_current_call_duration(const LinphoneCore *lc){ - LinphoneCall *call=lc->call; + LinphoneCall *call; + call = linphone_core_get_current_call_with_const(lc); 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; + LinphoneCall *call=linphone_core_get_current_call(lc); if (call==NULL) return 0; return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to; } void _osip_trace_func(char *fi, int li, osip_trace_level_t level, char *chfr, va_list ap){ int ortp_level=ORTP_DEBUG; switch(level){ case OSIP_INFO1: case OSIP_INFO2: case OSIP_INFO3: @@ -561,20 +757,21 @@ static void sound_config_read(LinphoneCore *lc) tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING; tmpbuf=lp_config_get_string(lc->config,"sound","remote_ring",tmpbuf); if (ortp_file_exist(tmpbuf)==-1){ tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING; } if (strstr(tmpbuf,".wav")==NULL){ /* it currently uses old sound files, so replace them */ tmpbuf=PACKAGE_SOUND_DIR "/" REMOTE_RING; } linphone_core_set_ringback(lc,tmpbuf); + check_sound_device(lc); lc->sound_conf.latency=0; linphone_core_enable_echo_cancellation(lc, lp_config_get_int(lc->config,"sound","echocancelation",0) | lp_config_get_int(lc->config,"sound","echocancellation",0) ); linphone_core_enable_echo_limiter(lc, lp_config_get_int(lc->config,"sound","echolimiter",0)); @@ -1525,20 +1722,21 @@ static void linphone_core_do_plugin_tasks(LinphoneCore *lc){ * - handles timers and timeout * - performs registration to proxies * - authentication retries * The application MUST call this function from periodically, in its main loop. * Be careful that this function must be call from the same thread as * other liblinphone methods. In not the case make sure all liblinphone calls are * serialized with a mutex. **/ void linphone_core_iterate(LinphoneCore *lc) { + LinphoneCall *call; eXosip_event_t *ev; bool_t disconnected=FALSE; int disconnect_timeout = linphone_core_get_nortp_timeout(lc); time_t curtime=time(NULL); int elapsed; bool_t one_second_elapsed=FALSE; if (curtime-lc->prevtime>=1){ lc->prevtime=curtime; one_second_elapsed=TRUE; @@ -1558,59 +1756,59 @@ void linphone_core_iterate(LinphoneCore *lc) if (lc->automatic_action==0) { eXosip_lock(); eXosip_automatic_action(); eXosip_unlock(); } } if (lc->auto_net_state_mon) monitor_network_state(lc,curtime); proxy_update(lc); - - if (lc->call!=NULL){ - LinphoneCall *call=lc->call; + + call=linphone_core_get_current_call(lc); + if (call!=NULL){ 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; + if (call->audiostream!=NULL) + as=call->audiostream->session; + if (call->videostream!=NULL) + vs=call->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) toggle_video_preview(lc,TRUE); #ifdef VIDEO_ENABLED else video_stream_iterate(lc->previewstream); #endif }else{ if (lc->previewstream!=NULL) toggle_video_preview(lc,FALSE); } - if (disconnected) + if (disconnected && linphone_core_is_in_conversation(lc)) linphone_core_disconnected(lc); linphone_core_do_plugin_tasks(lc); if (one_second_elapsed && lp_config_needs_commit(lc->config)){ lp_config_sync(lc->config); } } @@ -1819,43 +2017,121 @@ static void fix_contact(LinphoneCore *lc, osip_message_t *msg, const char *local osip_free(ctt->url->port); ctt->url->port=osip_strdup(tmp); osip_contact_to_str(ctt,&str); ms_message("Contact has been fixed to %s",str); osip_free(str); } } } /** + * Send a re-Invite used to hold the current call + * + * @ingroup call_control + * @param lc the LinphoneCore object + * @param url the destination of the call (sip address). +**/ +int linphone_core_send_hold(LinphoneCore *lc, int hold, int *cid) +{ + int err=0; + char *sdpmesg=NULL; + osip_message_t *reinvite=NULL; + char *real_url=NULL; + char *route=NULL; + LinphoneCall *call; + sdp_context_t *ctx=NULL; + LinphoneProxyConfig *dest_proxy=NULL; + char tmp_cseq_number[5]; + + if(cid ==NULL) + { + ms_error("cannot send hold without cid"); + return -1; + } + call=linphone_core_get_call_by_cid(lc,*cid); + if (call==NULL){ + ms_error("cannot find the call with cid <%d>",*cid); + return -1; + } + err=eXosip_call_build_initial_invite( &reinvite,linphone_address_as_string(call->log->to),linphone_address_as_string(call->log->from),route,"Phone call Hold"); + + if (err<0){ + ms_warning("Could not build initial invite"); + goto end; + } + /* This is probably useless for other messages */ + osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); + //wrong cseq sent + //osip_cseq_free(reinvite->cseq); + call->nb_cseq++; + snprintf(tmp_cseq_number,sizeof(tmp_cseq_number),"%d",(atoi(osip_cseq_get_number(reinvite->cseq))+(call->nb_cseq))); + osip_cseq_set_number(reinvite->cseq, osip_strdup(tmp_cseq_number)); + /* setting the previous tag and the previous call-ID from the first invite*/ + ms_message("setting the previous tag %s",call->tag_transaction_value); + ms_message("setting the previous call-id %s",call->the_callid); + { + osip_uri_param_t *p_tag; + osip_from_get_tag(reinvite->from,&p_tag); + osip_generic_param_set_value(p_tag,osip_strdup(call->tag_transaction_value)); + osip_call_id_set_number(reinvite->call_id,osip_strdup(call->the_callid)); + } + if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ + osip_message_set_header(reinvite, "Session-expires", "200"); + osip_message_set_supported(reinvite, "timer"); + } + /*try to be best-effort in giving real local or routable contact address, + except when the user choosed to override the ipaddress */ + if (linphone_core_get_firewall_policy(lc)!=LINPHONE_POLICY_USE_NAT_ADDRESS) + fix_contact(lc,reinvite,call->localip,dest_proxy); + /* make sdp message */ + + if (!lc->sip_conf.sdp_200_ack){ + ctx=call->sdpctx; + ctx->holdon = hold; + sdpmesg=sdp_context_get_offer(ctx); + linphone_set_sdp(reinvite,sdpmesg); + } + eXosip_lock(); + err=eXosip_call_send_request(call->did, reinvite); + eXosip_unlock(); +end: + if (real_url!=NULL) ms_free(real_url); + if (route!=NULL) ms_free(route); + return (err<0) ? -1 : 0; +} + +/** * Initiates an outgoing call * * @ingroup call_control * @param lc the LinphoneCore object * @param url the destination of the call (sip address). **/ int linphone_core_invite(LinphoneCore *lc, const char *url) { char *barmsg; int err=0; char *sdpmesg=NULL; char *route=NULL; const char *from=NULL; osip_message_t *invite=NULL; sdp_context_t *ctx=NULL; LinphoneProxyConfig *proxy=NULL; LinphoneAddress *parsed_url2=NULL; LinphoneAddress *real_parsed_url=NULL; char *real_url=NULL; LinphoneProxyConfig *dest_proxy=NULL; - - if (lc->call!=NULL){ - lc->vtable.display_warning(lc,_("Sorry, having multiple simultaneous calls is not supported yet !")); + LinphoneCall *the_call; + + if(!linphone_core_can_we_add_call(lc)) + { + lc->vtable.display_warning(lc,_("Sorry, we have too much calls in process")); return -1; } linphone_core_get_default_proxy(lc,&proxy); if (!linphone_core_interpret_url(lc,url,&real_parsed_url,&route)){ return -1; } real_url=linphone_address_as_string(real_parsed_url); dest_proxy=linphone_core_lookup_known_proxy(lc,real_parsed_url); @@ -1871,54 +2147,68 @@ int linphone_core_invite(LinphoneCore *lc, const char *url) /* if no proxy or no identity defined for this proxy, default to primary contact*/ if (from==NULL) from=linphone_core_get_primary_contact(lc); err=eXosip_call_build_initial_invite(&invite,real_url,from, route,"Phone call"); if (err<0){ ms_warning("Could not build initial invite cause [%i]",err); goto end; } + /* This is probably useless for other messages */ + osip_message_set_allow(invite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ osip_message_set_header(invite, "Session-expires", "200"); osip_message_set_supported(invite, "timer"); } + /* make sdp message */ parsed_url2=linphone_address_new(from); - lc->call=linphone_call_new_outgoing(lc,parsed_url2,real_parsed_url); + the_call=linphone_call_new_outgoing(lc,parsed_url2,real_parsed_url); + //we already checked if we could add a call + linphone_core_get_a_new_call(lc,the_call); + /* get the tag and call-ID balises for futures transactions */ + { + osip_generic_param_t *tag_param_local; + osip_from_get_tag(invite->from,&tag_param_local); + snprintf(the_call->tag_transaction_value,sizeof(the_call->tag_transaction_value),"%s",tag_param_local->gvalue); + snprintf(the_call->the_callid,sizeof(the_call->the_callid),"%s",osip_call_id_get_number(invite->call_id)); + ms_message("getting the tag %s",the_call->tag_transaction_value); + ms_message("getting the call-id :%s",the_call->the_callid); + } /*try to be best-effort in giving real local or routable contact address, except when the user choosed to override the ipaddress */ if (linphone_core_get_firewall_policy(lc)!=LINPHONE_POLICY_USE_NAT_ADDRESS) - fix_contact(lc,invite,lc->call->localip,dest_proxy); + fix_contact(lc,invite,the_call->localip,dest_proxy); - barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); - lc->vtable.display_status(lc,barmsg); - ms_free(barmsg); if (!lc->sip_conf.sdp_200_ack){ - ctx=lc->call->sdpctx; + ctx=the_call->sdpctx; sdpmesg=sdp_context_get_offer(ctx); linphone_set_sdp(invite,sdpmesg); - linphone_core_init_media_streams(lc); } eXosip_lock(); err=eXosip_call_send_initial_invite(invite); - lc->call->cid=err; + the_call->cid=err; eXosip_unlock(); + + barmsg=ortp_strdup_printf("%s %s with cid <%d>", _("Contacting"), real_url, linphone_core_get_call_cid(the_call)); + 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_call_destroy(lc->call); - lc->call=NULL; - linphone_core_stop_media_streams(lc); + linphone_core_stop_media_streams(lc,the_call); + linphone_core_clear_call_by_cid(lc,the_call->cid); + the_call=NULL; }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, url); goto end; end: if (real_url!=NULL) ms_free(real_url); if (route!=NULL) ms_free(route); return (err<0) ? -1 : 0; } int linphone_core_refer(LinphoneCore *lc, const char *url) @@ -1926,42 +2216,43 @@ int linphone_core_refer(LinphoneCore *lc, const char *url) char *real_url=NULL; LinphoneAddress *real_parsed_url=NULL; LinphoneCall *call; osip_message_t *msg=NULL; char *route; if (!linphone_core_interpret_url(lc,url,&real_parsed_url, &route)){ /* bad url */ return -1; } if (route!=NULL) ms_free(route); - call=lc->call; + call = linphone_core_get_current_call(lc); if (call==NULL){ ms_warning("No established call to refer."); return -1; } - lc->call=NULL; + linphone_core_clear_call_by_cid(lc,call->cid); real_url=linphone_address_as_string (real_parsed_url); eXosip_call_build_refer(call->did, real_url, &msg); ms_free(real_url); eXosip_lock(); eXosip_call_send_request(call->did, msg); eXosip_unlock(); 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){ + LinphoneCall *thecall = linphone_core_get_current_call(lc); + if (thecall!=NULL && thecall->dir==LinphoneCallIncoming){ return TRUE; } return FALSE; } #ifdef VINCENT_MAURY_RSVP /* on=1 for RPC_ENABLE=1...*/ int linphone_core_set_rpc_mode(LinphoneCore *lc, int on) { if (on==1) @@ -2026,50 +2317,50 @@ int linphone_core_change_qos(LinphoneCore *lc, int answer) } else { /* decline offer (603) */ linphone_core_terminate_call(lc, NULL); } return 0; } #endif -void linphone_core_init_media_streams(LinphoneCore *lc){ - lc->audiostream=audio_stream_new(linphone_core_get_audio_port(lc),linphone_core_ipv6_enabled(lc)); +void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call){ + call->audiostream=audio_stream_new(get_audio_localport(lc,call),linphone_core_ipv6_enabled(lc));//AB should be incremented by the call repere . . . 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); + audio_stream_enable_echo_limiter(call->audiostream,ELControlMic); else if (strcasecmp(type,"speaker")==0) - audio_stream_enable_echo_limiter(lc->audiostream,ELControlSpeaker); + audio_stream_enable_echo_limiter(call->audiostream,ELControlSpeaker); } - audio_stream_enable_gain_control(lc->audiostream,TRUE); + audio_stream_enable_gain_control(call->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_set_echo_canceller_params(call->audiostream,len,delay,framesize); } - audio_stream_enable_automatic_gain_control(lc->audiostream,linphone_core_agc_enabled(lc)); + audio_stream_enable_automatic_gain_control(call->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); + audio_stream_enable_noise_gate(call->audiostream,enabled); } if (lc->a_rtp) - rtp_session_set_transports(lc->audiostream->session,lc->a_rtp,lc->a_rtcp); + rtp_session_set_transports(call->audiostream->session,lc->a_rtp,lc->a_rtcp); #ifdef VIDEO_ENABLED if (lc->video_conf.display || lc->video_conf.capture) - lc->videostream=video_stream_new(linphone_core_get_video_port(lc),linphone_core_ipv6_enabled(lc)); + call->videostream=video_stream_new(get_video_localport(lc,call) ,linphone_core_ipv6_enabled(lc)); #else - lc->videostream=NULL; + call->videostream=NULL; #endif } static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){ LinphoneCore* lc = (LinphoneCore*)user_data; if (lc->vtable.dtmf_received != NULL) lc->vtable.dtmf_received(lc, dtmf); } static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ @@ -2087,22 +2378,22 @@ static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ 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; +static void post_configure_audio_streams(LinphoneCore *lc, LinphoneCall *call){ + AudioStream *st=call->audiostream; float gain=lp_config_get_float(lc->config,"sound","mic_gain",-1); if (gain!=-1) audio_stream_set_mic_gain(st,gain); if (linphone_core_echo_limiter_enabled(lc)){ float speed=lp_config_get_float(lc->config,"sound","el_speed",-1); float 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==ELControlMic){ @@ -2127,27 +2418,48 @@ static void post_configure_audio_streams(LinphoneCore *lc){ } if (st->volsend){ 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); 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); } 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); + 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); } } -void linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){ - LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); +int linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){ + int err; + LinphoneAddress *me; + + if(call == NULL) + { + lc->vtable.display_warning(lc,_("cannot start a NULL call")); + } + if(linphone_core_is_in_conversation(lc)) + { + lc->vtable.display_warning(lc,"linphone is already in communication, stop or hold the current one first\r\n"); + return -1; + } + err = linphone_core_set_current_call_by_cid(lc,call->cid); + if(err != 0) + { + char temp[256]; + snprintf(temp,sizeof(temp),"linphone cannot set current call(weird error %d!)",err); + lc->vtable.display_warning(lc,temp); + return -1; + } + linphone_core_init_media_streams(lc, call); + me=linphone_core_get_primary_contact_parsed(lc); const char *tool="linphone-" LINPHONE_VERSION; char *cname; /* 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=ortp_strdup_printf("address@hidden",me->url->username,me->url->host); { StreamParams *audio_params=&call->audio_params; @@ -2156,138 +2468,156 @@ void linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){ 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; } if (audio_params->relay_session_id!=NULL) - audio_stream_set_relay_session_id(lc->audiostream,audio_params->relay_session_id); - audio_stream_start_now( - lc->audiostream, - call->profile, - audio_params->remoteaddr, - audio_params->remoteport, - audio_params->remotertcpport, - audio_params->pt, - jitt_comp, - playcard, - captcard, - linphone_core_echo_cancellation_enabled(lc)); + audio_stream_set_relay_session_id(call->audiostream,audio_params->relay_session_id); + audio_stream_start_now( + call->audiostream, + call->profile, + audio_params->remoteaddr, + audio_params->remoteport, + audio_params->remotertcpport, + audio_params->pt, + jitt_comp, + playcard, + captcard, + linphone_core_echo_cancellation_enabled(lc)); }else{ audio_stream_start_with_files( - lc->audiostream, + call->audiostream, call->profile, audio_params->remoteaddr, audio_params->remoteport, audio_params->remotertcpport, audio_params->pt, 100, lc->play_file, lc->rec_file); } - post_configure_audio_streams(lc); - audio_stream_set_rtcp_information(lc->audiostream, cname, tool); + post_configure_audio_streams(lc,call); + audio_stream_set_rtcp_information(call->audiostream, cname, tool); } #ifdef VIDEO_ENABLED { /* shutdown preview */ if (lc->previewstream!=NULL) { video_preview_stop(lc->previewstream); lc->previewstream=NULL; } if (lc->video_conf.display || lc->video_conf.capture) { StreamParams *video_params=&call->video_params; if (video_params->remoteport>0){ if (video_params->relay_session_id!=NULL) - video_stream_set_relay_session_id(lc->videostream,video_params->relay_session_id); - 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); + { + video_stream_set_relay_session_id(call->videostream,video_params->relay_session_id); + } + 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 (lc->video_conf.display && lc->video_conf.capture) - video_stream_start(lc->videostream, + video_stream_start(call->videostream, call->profile, video_params->remoteaddr, video_params->remoteport, video_params->remotertcpport, video_params->pt, jitt_comp, lc->video_conf.device); else if (lc->video_conf.display) - video_stream_recv_only_start(lc->videostream, + video_stream_recv_only_start(call->videostream, call->profile, video_params->remoteaddr, video_params->remoteport, video_params->pt, jitt_comp); else if (lc->video_conf.capture) - video_stream_send_only_start(lc->videostream, + video_stream_send_only_start(call->videostream, call->profile, video_params->remoteaddr, video_params->remoteport, video_params->remotertcpport, video_params->pt, jitt_comp, lc->video_conf.device); - video_stream_set_rtcp_information(lc->videostream, cname,tool); + video_stream_set_rtcp_information(call->videostream, cname,tool); } } } #endif goto end; end: ms_free(cname); linphone_address_destroy(me); - lc->call->state=LCStateAVRunning; + call->state=LCStateAVRunning; + return 0; } -void linphone_core_stop_media_streams(LinphoneCore *lc){ - if (lc->audiostream!=NULL) { - audio_stream_stop(lc->audiostream); - lc->audiostream=NULL; +void linphone_core_stop_media_streams(LinphoneCore *lc, LinphoneCall *call){ + if(call == NULL) + { + lc->vtable.display_warning(lc,_("cannot stop a NULL call")); + return ; + } + if (call->audiostream!=NULL) { + audio_stream_stop(call->audiostream); + call->audiostream=NULL; } #ifdef VIDEO_ENABLED - if (lc->videostream!=NULL){ + if (call->videostream!=NULL){ if (lc->video_conf.display && lc->video_conf.capture) - video_stream_stop(lc->videostream); + video_stream_stop(call->videostream); else if (lc->video_conf.display) - video_stream_recv_only_stop(lc->videostream); + video_stream_recv_only_stop(call->videostream); else if (lc->video_conf.capture) - video_stream_send_only_stop(lc->videostream); - lc->videostream=NULL; + video_stream_send_only_stop(call->videostream); + call->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 + call->currently_used = FALSE; + } /** * 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 * 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. **/ -int linphone_core_accept_call(LinphoneCore *lc, const char *url) +int linphone_core_accept_call(LinphoneCore *lc, int *cid) { char *sdpmesg; osip_message_t *msg=NULL; - LinphoneCall *call=lc->call; + LinphoneCall *call; + if(cid == NULL) + { + call=linphone_core_get_current_call(lc); + } + else + { + call=linphone_core_get_call_by_cid(lc,*cid); + } int err; bool_t offering=FALSE; if (call==NULL){ return -1; } - if (lc->call->state==LCStateAVRunning){ + if (call->state==LCStateAVRunning){ /*call already accepted*/ return -1; } /*stop ringing */ if (lc->ringstream!=NULL) { ms_message("stop ringing"); ring_stop(lc->ringstream); ms_message("ring stopped"); lc->ringstream=NULL; @@ -2310,74 +2640,99 @@ int linphone_core_accept_call(LinphoneCore *lc, const char *url) if (sdpmesg==NULL){ offering=TRUE; ms_message("generating sdp offer"); sdpmesg=sdp_context_get_offer(call->sdpctx); if (sdpmesg==NULL){ ms_error("fail to generate sdp offer !"); return -1; } linphone_set_sdp(msg,sdpmesg); - linphone_core_init_media_streams(lc); }else{ linphone_set_sdp(msg,sdpmesg); } eXosip_lock(); eXosip_call_send_answer(call->tid,200,msg); eXosip_unlock(); lc->vtable.display_status(lc,_("Connected.")); gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); - if (!offering) linphone_core_start_media_streams(lc, lc->call); + if (!offering) + { + if(!linphone_core_is_in_conversation(lc)) + { + linphone_core_start_media_streams(lc, call); + } + else + { + lc->vtable.display_status(lc,_("the call answer is hold because we are currently in communication")); + linphone_core_send_hold(lc,HOLD_ON,cid); + } + + + } 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). + * @param cid permit to determine which call to terminate when simultanous **/ -int linphone_core_terminate_call(LinphoneCore *lc, const char *url) +int linphone_core_terminate_call(LinphoneCore *lc, int *cid) { - LinphoneCall *call=lc->call; + int the_cid = 0; + char temp[256]; + LinphoneCall *call; + if(cid == NULL) + { + call = linphone_core_get_current_call(lc); + if(call != NULL) + the_cid = call->cid; + } + else + { + call = linphone_core_get_call_by_cid(lc,*cid); + the_cid = *cid; + } if (call==NULL){ + lc->vtable.display_warning(lc,_("the call asked to stop do not exists")); return -1; } - lc->call=NULL; eXosip_lock(); eXosip_call_terminate(call->cid,call->did); eXosip_unlock(); /*stop ringing*/ if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc); - lc->vtable.display_status(lc,_("Call ended") ); + linphone_core_stop_media_streams(lc, call); + snprintf(temp,sizeof(temp),"Call ended with cid <%d>",the_cid); + lc->vtable.display_status(lc,temp ); gstate_new_state(lc, GSTATE_CALL_END, NULL); - linphone_call_destroy(call); + linphone_core_clear_call_by_cid(lc,call->cid); return 0; } /** * 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_with_const(lc)!=NULL); } int linphone_core_send_publish(LinphoneCore *lc, LinphoneOnlineStatus presence_mode) { const MSList *elem; for (elem=linphone_core_get_proxy_config_list(lc);elem!=NULL;elem=ms_list_next(elem)){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; if (cfg->publish) linphone_proxy_config_send_publish(cfg,presence_mode); } @@ -2745,20 +3100,81 @@ void linphone_core_set_ringback(LinphoneCore *lc, const char *path){ /** * Returns the path to the wav file used for ringing back. * * @ingroup media_parameters **/ const char * linphone_core_get_ringback(const LinphoneCore *lc){ return lc->sound_conf.remote_ring; } /** + * Permits to hold on or hold off a call with the cid *cid + * + * @ingroup media_parameters +**/ +int linphone_core_hold(LinphoneCore *lc, bool_t val, int *cid){ + char text[256]; + int cid_used; + LinphoneCall *lcall; + + if(val==HOLD_ON) + { + if(cid == NULL) + { + if(!linphone_core_is_in_conversation(lc)) + { + lc->vtable.display_warning(lc,_("we are not in conversation cannot hold on")); + return -4; + } + cid_used = linphone_core_get_call_cid(linphone_core_get_current_call(lc)); + } + else + { + cid_used = *cid; + } + } + else + { //hold off + if(linphone_core_is_in_conversation(lc)) + { + lc->vtable.display_warning(lc,_("cannot hold off we are already in communication")); + return -3; + } + if(cid == NULL) + { + lc->vtable.display_warning(lc,_("cannot hold a call without the cid specify")); + return -2; + } + cid_used = *cid; + } + lcall = linphone_core_get_call_by_cid(lc,cid_used); + if(lcall == NULL) + { + snprintf(text,sizeof(text),"the call with cid <%d> do no exist",cid_used); + lc->vtable.display_status(lc,text); + return -1; + } + if(val==HOLD_ON) + snprintf(text,sizeof(text),"hold on the call with cid <%d>",cid_used); + else + snprintf(text,sizeof(text),"hold off the call with cid <%d>",cid_used); + lc->vtable.display_status(lc,text); + linphone_core_send_hold(lc,val,&cid_used); + if(val==HOLD_ON) + linphone_core_stop_media_streams(lc,lcall); + else + linphone_core_start_media_streams(lc,lcall); + + return 0; +} + +/** * Enables or disable echo cancellation. * * @ingroup media_parameters **/ void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val){ lc->sound_conf.ec=val; if (lc->ready) lp_config_set_int(lc->config,"sound","echocancellation",val); } @@ -2778,22 +3194,28 @@ void linphone_core_enable_echo_limiter(LinphoneCore *lc, bool_t val){ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){ return lc->sound_conf.ea; } /** * Mutes or unmutes the local microphone. * * @ingroup media_parameters **/ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ - if (lc->audiostream!=NULL){ - audio_stream_set_mic_gain(lc->audiostream, + LinphoneCall *call = linphone_core_get_current_call(lc); + if(call == NULL) + { + lc->vtable.display_warning(lc,_("we cannot mute we are not in conversation")); + return ; + } + if (call->audiostream!=NULL){ + audio_stream_set_mic_gain(call->audiostream, (val==TRUE) ? 0 : 1.0); } } void linphone_core_enable_agc(LinphoneCore *lc, bool_t val){ lc->sound_conf.agc=val; } bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ return lc->sound_conf.agc; @@ -2803,39 +3225,45 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ * Send the specified dtmf. * * @ingroup media_parameters * This function only works during calls. The dtmf is automatically played to the user. * @param lc The LinphoneCore object * @param dtmf The dtmf name specified as a char, such as '0', '#' etc... * **/ void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf) { + LinphoneCall *call = linphone_core_get_current_call(lc); + if(call == NULL) + { + lc->vtable.display_warning(lc,_("we are not in conversation to send DTMF")); + 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 { ms_error("we cannot send RFC2833 dtmf when we are not in communication"); } } if (linphone_core_get_use_info_for_dtmf(lc)!=0) { char dtmf_body[1000]; char clen[10]; osip_message_t *msg=NULL; /* Out of Band DTMF (use INFO method) */ - LinphoneCall *call=lc->call; + LinphoneCall *call=linphone_core_get_current_call(lc); if (call==NULL){ return; } eXosip_call_build_info(call->did,&msg); snprintf(dtmf_body, 999, "Signal=%c\r\nDuration=250\r\n", dtmf); osip_message_set_body(msg,dtmf_body,strlen(dtmf_body)); osip_message_set_content_type(msg,"application/dtmf-relay"); snprintf(clen,sizeof(clen),"%lu",(unsigned long)strlen(dtmf_body)); osip_message_set_content_length(msg,clen); @@ -2983,31 +3411,32 @@ const MSList * linphone_core_get_call_logs(LinphoneCore *lc){ **/ void linphone_core_clear_call_logs(LinphoneCore *lc){ lc->missed_calls=0; ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); lc->call_logs=ms_list_free(lc->call_logs); call_logs_write_to_config_file(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; - } + LinphoneCall *call=linphone_core_get_current_call(lc); + if(call != NULL) + if (call->videostream != NULL) return; + 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 } /** * Enables video globally. * * @ingroup media_parameters * This function does not have any effect during calls. It just indicates LinphoneCore to @@ -3064,22 +3493,24 @@ bool_t linphone_core_video_preview_enabled(const LinphoneCore *lc){ * Enables or disable self view during calls. * * @ingroup media_parameters * Self-view refers to having local webcam image inserted in corner * of the video window during calls. * 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); + if(call == NULL) return; + if (call->videostream){ + video_stream_enable_self_view(call->videostream,val); } #endif } /** * Returns TRUE if self-view is enabled, FALSE otherwise. * * @ingroup media_parameters * * Refer to linphone_core_enable_self_view() for details. @@ -3128,22 +3559,24 @@ const char *linphone_core_get_video_device(const LinphoneCore *lc){ return NULL; } /** * Returns the native window handle of the video window, casted as an unsigned long. * * @ingroup media_parameters **/ 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((LinphoneCore *)lc); + if(call == NULL) return -1; + if (call->videostream) + return video_stream_get_native_window_id(call->videostream); if (lc->previewstream) return video_stream_get_native_window_id(lc->previewstream); #endif return 0; } static MSVideoSizeDef supported_resolutions[]={ { {MS_VIDEO_SIZE_SVGA_W,MS_VIDEO_SIZE_SVGA_H} , "svga" }, { {MS_VIDEO_SIZE_4CIF_W,MS_VIDEO_SIZE_4CIF_H} , "4cif" }, { {MS_VIDEO_SIZE_VGA_W,MS_VIDEO_SIZE_VGA_H} , "vga" }, @@ -3232,40 +3665,52 @@ void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char **/ MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc){ return lc->video_conf.vsize; } void linphone_core_use_files(LinphoneCore *lc, bool_t yesno){ lc->use_files=yesno; } 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(call == NULL) + { + lc->vtable.display_warning(lc,_("we cannot play file without conversation in progress")); + return; + } if (file!=NULL) { lc->play_file=ms_strdup(file); - if (lc->audiostream) - audio_stream_play(lc->audiostream,file); + if (call->audiostream) + audio_stream_play(call->audiostream,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(call == NULL) + { + lc->vtable.display_warning(lc,_("we cannot record into a file without conversation in progress")); + return; + } if (file!=NULL) { lc->rec_file=ms_strdup(file); - if (lc->audiostream) - audio_stream_record(lc->audiostream,file); + if (call->audiostream) + audio_stream_record(call->audiostream,file); } } /** * Retrieves the user pointer that was given to linphone_core_new() * * @ingroup initializing **/ void *linphone_core_get_user_data(LinphoneCore *lc){ return lc->data; @@ -3464,21 +3909,22 @@ void ui_config_uninit(LinphoneCore* lc) * The application can use the LpConfig object to insert its own private * sections and pairs of key=value in the configuration file. * **/ LpConfig *linphone_core_get_config(LinphoneCore *lc){ return lc->config; } static void linphone_core_uninit(LinphoneCore *lc) { - if (lc->call){ + if (linphone_core_get_current_call(lc)) + { int i; linphone_core_terminate_call(lc,NULL); for(i=0;i<10;++i){ #ifndef WIN32 usleep(50000); #else Sleep(50); #endif linphone_core_iterate(lc); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 6c8f0e2..ec14e88 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -23,22 +23,27 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ortp/payloadtype.h" #include "mediastreamer2/mscommon.h" #include "mediastreamer2/msvideo.h" #ifdef IN_LINPHONE #include "sipsetup.h" #else #include "linphone/sipsetup.h" #endif -#define LINPHONE_IPADDR_SIZE 64 -#define LINPHONE_HOSTNAME_SIZE 128 +#define LINPHONE_SESSION_ID_SIZE (15) +#define LINPHONE_IPADDR_SIZE (64) +#define LINPHONE_HOSTNAME_SIZE (128) +#define LINPHONE_CALL_ID_SIZE (255) + +#define HOLD_OFF (0) +#define HOLD_ON (1) #ifdef __cplusplus extern "C" { #endif struct _MSSndCard; struct _LinphoneCore; bool_t payload_type_enabled(struct _PayloadType *pt); void payload_type_set_enable(struct _PayloadType *pt,int value); @@ -588,50 +593,53 @@ typedef enum _LinphoneFirewallPolicy{ LINPHONE_POLICY_USE_STUN } LinphoneFirewallPolicy; typedef enum _LinphoneWaitingState{ LinphoneWaitingStart, LinphoneWaitingProgress, LinphoneWaitingFinished } LinphoneWaitingState; typedef void * (*LinphoneWaitingCallback)(struct _LinphoneCore *lc, void *context, LinphoneWaitingState ws, const char *purpose, float progress); +#ifndef NB_MAX_CALLS +#define NB_MAX_CALLS (10) +#endif typedef struct _LinphoneCore { LinphoneCoreVTable vtable; struct _LpConfig *config; net_config_t net_conf; sip_config_t sip_conf; rtp_config_t rtp_conf; sound_config_t sound_conf; video_config_t video_conf; codecs_config_t codecs_conf; ui_config_t ui_conf; autoreplier_config_t autoreplier_conf; LinphoneProxyConfig *default_proxy; MSList *friends; MSList *auth_info; struct _RingStream *ringstream; LCCallbackObj preview_finished_cb; bool_t preview_finished; struct _LinphoneCall *call; /* the current call, in the future it will be a list of calls (conferencing)*/ + int calls_nb; + struct _LinphoneCall *calls[NB_MAX_CALLS]; /* the current call, in the future it will be a list of calls (conferencing)*/ int rid; /*registration id*/ 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; - RtpTransport *a_rtp,*a_rtcp; + RtpTransport *a_rtp,*a_rtcp; //AB should it be in calls ??? struct _RtpProfile *local_profile; MSList *bl_reqs; MSList *subscribers; /* unknown subscribers */ int minutes_away; LinphoneOnlineStatus presence_mode; LinphoneOnlineStatus prev_mode; char *alt_contact; void *data; ms_mutex_t lock; char *play_file; @@ -683,23 +691,23 @@ LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable, void linphone_core_iterate(LinphoneCore *lc); int linphone_core_invite(LinphoneCore *lc, const char *url); int linphone_core_refer(LinphoneCore *lc, const char *url); bool_t linphone_core_inc_invite_pending(LinphoneCore*lc); bool_t linphone_core_in_call(const LinphoneCore *lc); -int linphone_core_accept_call(LinphoneCore *lc, const char *url); +int linphone_core_accept_call(LinphoneCore *lc, int *cid); -int linphone_core_terminate_call(LinphoneCore *lc, const char *url); +int linphone_core_terminate_call(LinphoneCore *lc, int *cid); void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf); int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact); const char *linphone_core_get_primary_contact(LinphoneCore *lc); void linphone_core_set_guess_hostname(LinphoneCore *lc, bool_t val); bool_t linphone_core_get_guess_hostname(LinphoneCore *lc); @@ -835,20 +843,21 @@ const char * linphone_core_get_capture_device(LinphoneCore *lc); int linphone_core_set_ringer_device(LinphoneCore *lc, const char * devid); int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid); int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid); char linphone_core_get_sound_source(LinphoneCore *lc); void linphone_core_set_sound_source(LinphoneCore *lc, char source); void linphone_core_set_ring(LinphoneCore *lc, const char *path); const char *linphone_core_get_ring(const LinphoneCore *lc); void linphone_core_set_ringback(LinphoneCore *lc, const char *path); const char * linphone_core_get_ringback(const LinphoneCore *lc); int linphone_core_preview_ring(LinphoneCore *lc, const char *ring,LinphoneCoreCbFunc func,void * userdata); +int linphone_core_hold(LinphoneCore *lc, bool_t val, int *cid); void linphone_core_enable_echo_cancellation(LinphoneCore *lc, bool_t val); bool_t linphone_core_echo_cancellation_enabled(LinphoneCore *lc); void linphone_core_enable_echo_limiter(LinphoneCore *lc, bool_t val); bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc); void linphone_core_enable_agc(LinphoneCore *lc, bool_t val); bool_t linphone_core_agc_enabled(const LinphoneCore *lc); void linphone_core_mute_mic(LinphoneCore *lc, bool_t muted); @@ -949,25 +958,24 @@ const MSList * linphone_core_get_sip_setups(LinphoneCore *lc); void linphone_core_destroy(LinphoneCore *lc); /*for advanced users:*/ void linphone_core_set_audio_transports(LinphoneCore *lc, RtpTransport *rtp, RtpTransport *rtcp); /* end of lecacy api */ /*internal use only */ #define linphone_core_lock(lc) ms_mutex_lock(&(lc)->lock) #define linphone_core_unlock(lc) ms_mutex_unlock((&lc)->lock) -void linphone_core_start_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); -void linphone_core_stop_media_streams(LinphoneCore *lc); const char * linphone_core_get_identity(LinphoneCore *lc); const char * linphone_core_get_route(LinphoneCore *lc); bool_t linphone_core_interpret_url(LinphoneCore *lc, const char *url, LinphoneAddress **real_parsed_url, char **route); void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); void linphone_core_stop_waiting(LinphoneCore *lc); +bool_t linphone_core_is_in_conversation(LinphoneCore *lc); #ifdef __cplusplus } #endif #endif diff --git a/coreapi/private.h b/coreapi/private.h index 93b1c77..1bc7563 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -72,49 +72,56 @@ typedef struct _StreamParams typedef enum _LCState{ LCStateInit, LCStateRinging, LCStateAVRunning }LCState; typedef struct _LinphoneCall { struct _LinphoneCore *core; + struct _AudioStream *audiostream; /**/ + struct _VideoStream *videostream; StreamParams audio_params; StreamParams video_params; LinphoneCallDir dir; struct _RtpProfile *profile; /*points to the local_profile or to the remote "guessed" profile*/ struct _LinphoneCallLog *log; int cid; /*call id */ int did; /*dialog id */ int tid; /*last transaction id*/ char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ struct _sdp_context *sdpctx; 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; bool_t auth_pending; bool_t supports_session_timers; + bool_t currently_used; + int nb_cseq; + int repere; + char tag_transaction_value[LINPHONE_CALL_ID_SIZE]; + char the_callid[LINPHONE_CALL_ID_SIZE]; } LinphoneCall; LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to); LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, eXosip_event_t *ev); #define linphone_call_set_state(lcall,st) (lcall)->state=(st) -void linphone_call_destroy(struct _LinphoneCall *obj); +void linphone_call_destroyed(struct _LinphoneCall *obj); /* 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_destroy(LinphoneCallLog *cl); -void linphone_core_init_media_streams(LinphoneCore *lc); +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_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphoneOnlineStatus os); int linphone_online_status_to_eXosip(LinphoneOnlineStatus os); @@ -187,11 +194,28 @@ LinphoneFriend * linphone_friend_new_from_config_file(struct _LinphoneCore *lc, void linphone_proxy_config_update(LinphoneProxyConfig *cfg); void linphone_proxy_config_get_contact(LinphoneProxyConfig *cfg, const char **ip, int *port); LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri); int linphone_core_get_local_ip_for(const char *dest, char *result); LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(struct _LpConfig *config, int index); void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,LinphoneProxyConfig *obj, int index); int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char *username, char *result, size_t result_len); +int linphone_core_get_calls_nb(const LinphoneCore *lc); +bool_t linphone_core_can_we_add_call(LinphoneCore *lc); +int linphone_core_clear_call_by_cid(LinphoneCore *lc,int cid); +LinphoneCall *linphone_core_get_call_by_cid( LinphoneCore *lc,int cid); +LinphoneCall *linphone_core_get_current_call( LinphoneCore *lc); +int linphone_core_get_cid_of_the_current_call( LinphoneCore *lc); +int linphone_core_get_a_new_call( LinphoneCore *lc, LinphoneCall *call); +int linphone_core_get_call_cid( LinphoneCall *call); +int linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_stop_media_streams(LinphoneCore *lc, LinphoneCall *call); + + +int get_audio_localport(LinphoneCore *lc,LinphoneCall *call); +int get_video_localport(LinphoneCore *lc,LinphoneCall *call); +int get_payload_audio_localport(LinphoneCore *lc,LinphoneCall *call); +int get_payload_video_localport(LinphoneCore *lc,LinphoneCall *call); + #endif /* _PRIVATE_H */ diff --git a/coreapi/sdphandler.c b/coreapi/sdphandler.c index bf622a9..b8253f7 100644 --- a/coreapi/sdphandler.c +++ b/coreapi/sdphandler.c @@ -135,65 +135,91 @@ void *sdp_context_get_user_pointer(sdp_context_t * ctx){ int sdp_context_get_status(sdp_context_t* ctx){ return ctx->negoc_status; } /* generate a template sdp */ sdp_message_t * sdp_context_generate_template (sdp_context_t * ctx) { sdp_message_t *local; int inet6; + char tmp_session_id[LINPHONE_SESSION_ID_SIZE]; + char tmp_session_version[LINPHONE_SESSION_ID_SIZE]; + char tmp_s_name[LINPHONE_CALL_ID_SIZE]; sdp_message_init (&local); if (strchr(ctx->localip,':')!=NULL){ inet6=1; }else inet6=0; + ctx->session_version++; + snprintf(tmp_session_id,sizeof(tmp_session_id),"%d",ctx->session_id); + snprintf(tmp_session_version,sizeof(tmp_session_version),"%d",ctx->session_version); + snprintf(tmp_s_name,sizeof(tmp_s_name),"A conversation with call %d",ctx->session_id); if (!inet6){ sdp_message_v_version_set (local, osip_strdup ("0")); sdp_message_o_origin_set (local, osip_strdup (ctx->username), - osip_strdup ("123456"), osip_strdup ("654321"), + osip_strdup (tmp_session_id), osip_strdup (tmp_session_version), osip_strdup ("IN"), osip_strdup ("IP4"), osip_strdup (ctx->localip)); - sdp_message_s_name_set (local, osip_strdup ("A conversation")); - sdp_message_c_connection_add (local, -1, - osip_strdup ("IN"), osip_strdup ("IP4"), - osip_strdup (ctx->localip), NULL, NULL); + sdp_message_s_name_set (local, osip_strdup (tmp_s_name)); + if(ctx->holdon == HOLD_OFF) + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), osip_strdup ("IP4"), + osip_strdup (ctx->localip), NULL, NULL); + } + else + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), osip_strdup ("IP4"), + osip_strdup ("0.0.0.0"), NULL, NULL); + } sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); }else{ sdp_message_v_version_set (local, osip_strdup ("0")); sdp_message_o_origin_set (local, osip_strdup (ctx->username), osip_strdup ("123456"), osip_strdup ("654321"), osip_strdup ("IN"), osip_strdup ("IP6"), osip_strdup (ctx->localip)); sdp_message_s_name_set (local, osip_strdup ("A conversation")); - sdp_message_c_connection_add (local, -1, + if(ctx->holdon == HOLD_OFF) + { + sdp_message_c_connection_add (local, -1, osip_strdup ("IN"), osip_strdup ("IP6"), osip_strdup (ctx->localip), NULL, NULL); + } + else + { + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), osip_strdup ("IP6"), + osip_strdup ("0.0.0.0"), NULL, NULL); + } sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); } return local; } static void add_relay_info(sdp_message_t *sdp, int mline, const char *relay, const char *relay_session_id){ if (relay) sdp_message_a_attribute_add(sdp, mline, osip_strdup ("relay-addr"),osip_strdup(relay)); if (relay_session_id) sdp_message_a_attribute_add(sdp, mline, osip_strdup ("relay-session-id"), osip_strdup(relay_session_id)); } /* to add payloads to the offer, must be called inside the write_offer callback */ void sdp_context_add_payload (sdp_context_t * ctx, sdp_payload_t * payload, char *media) { sdp_message_t *offer = ctx->offer; char *attr_field; + if (!ctx->incb) { eXosip_trace (OSIP_ERROR, ("You must not call sdp_context_add_*_payload outside the write_offer callback\n")); #if !defined(_WIN32_WCE) abort(); #else exit(-1); #endif /*_WIN32_WCE*/ @@ -222,20 +248,35 @@ sdp_context_add_payload (sdp_context_t * ctx, sdp_payload_t * payload, char *med osip_strdup ("rtpmap"), attr_field); } if (payload->a_fmtp != NULL) { attr_field = sstrdup_sprintf ("%i %s", payload->pt, payload->a_fmtp); sdp_message_a_attribute_add (offer, payload->line, osip_strdup ("fmtp"), attr_field); } + switch (payload->holdon) + { + case HOLD_ON : + /* we need to add a global attribute with a field set to "sendonly" when holding on */ + sdp_message_a_attribute_add (offer, payload->line, osip_strdup ("sendonly"),NULL); + break; + case HOLD_OFF : + /* we need to add a global attribute with a field set to "sendonly" when holding on */ + sdp_message_a_attribute_add (offer, payload->line, osip_strdup ("sendrecv"),NULL); + break; + default: + //ms_message("do not need to set sendonly or sendrecv"); + break; + } + if (payload->b_as_bandwidth != 0) { if (sdp_message_bandwidth_get(offer,payload->line,0)==NULL){ attr_field = sstrdup_sprintf ("%i", payload->b_as_bandwidth); sdp_message_b_bandwidth_add (offer, payload->line, osip_strdup ("AS"), attr_field); } } if (payload->a_ptime !=0) { diff --git a/coreapi/sdphandler.h b/coreapi/sdphandler.h index b3e811b..3bb7413 100644 --- a/coreapi/sdphandler.h +++ b/coreapi/sdphandler.h @@ -33,20 +33,21 @@ typedef struct _sdp_payload char *c_addrtype; char *c_addr; char *c_addr_multicast_ttl; char *c_addr_multicast_int; char *a_rtpmap; char *a_fmtp; char *relay_host; int relay_port; char *relay_session_id; int a_ptime; + int holdon; /* Infom that the callee will not receive anything from us */ } sdp_payload_t; typedef struct _sdp_context sdp_context_t; typedef int (*sdp_handler_read_codec_func_t) (struct _sdp_context *, sdp_payload_t *); typedef int (*sdp_handler_write_codec_func_t) (struct _sdp_context *); typedef struct _sdp_handler { @@ -73,20 +74,23 @@ struct _sdp_context char *username; void *reference; sdp_message_t *offer; /* the local sdp to be used for outgoing request */ char *offerstr; sdp_message_t *answer; /* the local sdp generated from an inc request */ char *answerstr; char *relay; char *relay_session_id; int negoc_status; /* in sip code */ int incb; + int holdon; /* which permit to say that the distant sip phone will just send and not receive anything */ + int session_id; + int session_version; sdp_context_state_t state; }; /* create a context for a sdp negociation: localip is the ip address to be used in the sdp message, can be a firewall address. It can be null when negociating for an incoming offer; In that case it will be guessed. */ sdp_context_t *sdp_handler_create_context(sdp_handler_t *handler, const char *localip, const char *username, const char *relay); void sdp_context_set_user_pointer(sdp_context_t * ctx, void* up); void *sdp_context_get_user_pointer(sdp_context_t * ctx); void sdp_context_add_audio_payload( sdp_context_t * ctx, sdp_payload_t * payload);