diff --git a/configure.ac b/configure.ac index 93f4f1b00..3d67cbabc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([linphone],[3.3.99.9],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.3.99.10],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM AC_CONFIG_SRCDIR([coreapi/linphonecore.c]) diff --git a/console/commands.c b/console/commands.c index a7aa22f76..ab67569bc 100644 --- a/console/commands.c +++ b/console/commands.c @@ -144,7 +144,10 @@ static LPC_COMMAND commands[] = { }, { "call", lpc_cmd_call, "Call a SIP uri or number", #ifdef VIDEO_ENABLED - "'call [--audio-only]' \t: initiate a call to the specified destination.\n" + "'call [options]' \t: initiate a call to the specified destination.\n" + "Options can be:\n" + "--audio-only : initiate the call without video.\n" + "--early-media : sends audio and video stream immediately when remote proposes early media.\n" #else "'call ' \t: initiate a call to the specified destination.\n" #endif @@ -542,17 +545,22 @@ lpc_cmd_call(LinphoneCore *lc, char *args) { LinphoneCall *call; LinphoneCallParams *cp=linphone_core_create_default_call_parameters (lc); - char *opt; + char *opt1,*opt2; if ( linphone_core_in_call(lc) ) { linphonec_out("Terminate or hold on the current call first.\n"); return 1; } - opt=strstr(args,"--audio-only"); - if (opt){ - opt[0]='\0'; + opt1=strstr(args,"--audio-only"); + opt2=strstr(args,"--early-media"); + if (opt1){ + opt1[0]='\0'; linphone_call_params_enable_video (cp,FALSE); } + if (opt2){ + opt2[0]='\0'; + linphone_call_params_enable_early_media_sending(cp,TRUE); + } if ( NULL == (call=linphone_core_invite_with_params(lc, args,cp)) ) { linphonec_out("Error from linphone_core_invite.\n"); diff --git a/console/linphonec.c b/console/linphonec.c index 41b70a816..8373f1f63 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -356,10 +356,28 @@ static void linphonec_call_state_changed(LinphoneCore *lc, LinphoneCall *call, L break; case LinphoneCallOutgoingInit: linphonec_call_identify(call); + id=(long)linphone_call_get_user_pointer (call); + from=linphone_call_get_remote_address_as_string(call); + linphonec_out("Establishing call id to %s, assigned id %i\n", from,id); break; case LinphoneCallUpdatedByRemote: linphonec_call_updated(call); break; + case LinphoneCallOutgoingProgress: + linphonec_out("Call %i to %s in progress.\n", id, from); + break; + case LinphoneCallOutgoingRinging: + linphonec_out("Call %i to %s ringing.\n", id, from); + break; + case LinphoneCallConnected: + linphonec_out("Call %i with %s connected.\n", id, from); + break; + case LinphoneCallOutgoingEarlyMedia: + linphonec_out("Call %i with %s early media.\n", id, from); + break; + case LinphoneCallError: + linphonec_out("Call %i with %s error.\n", id, from); + break; default: break; } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index d4f5193f3..5fda3e638 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -27,12 +27,69 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details); -static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){ +static bool_t media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd){ + return !sal_media_description_equals(oldmd,newmd) || call->up_bw!=linphone_core_get_upload_bandwidth(call->core); +} + +void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){ + SalMediaDescription *oldmd=call->resultdesc; + if (lc->ringstream!=NULL){ ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_call_start_media_streams(call); + if (new_md!=NULL){ + sal_media_description_ref(new_md); + call->media_pending=FALSE; + }else{ + call->media_pending=TRUE; + } + call->resultdesc=new_md; + if (call->audiostream && call->audiostream->ticker){ + /* we already started media: check if we really need to restart it*/ + if (oldmd){ + if (!media_parameters_changed(call,oldmd,new_md) && !call->playing_ringbacktone){ + sal_media_description_unref(oldmd); + if (call->all_muted){ + ms_message("Early media finished, unmuting inputs..."); + /*we were in early media, now we want to enable real media */ + linphone_call_enable_camera (call,linphone_call_camera_enabled (call)); + if (call->audiostream) + linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc)); +#ifdef VIDEO_ENABLED + if (call->videostream && call->camera_active) + video_stream_change_camera(call->videostream,lc->video_conf.device ); +#endif + } + ms_message("No need to restart streams, SDP is unchanged."); + return; + }else{ + ms_message("Media descriptions are different, need to restart the streams."); + } + } + linphone_call_stop_media_streams (call); + linphone_call_init_media_streams (call); + } + if (oldmd) + sal_media_description_unref(oldmd); + + if (new_md) { + bool_t all_muted=FALSE; + bool_t send_ringbacktone=FALSE; + + if (call->audiostream==NULL){ + /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/ + linphone_call_init_media_streams (call); + } + if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){ + send_ringbacktone=TRUE; + } + if (call->state==LinphoneCallIncomingEarlyMedia || + (call->state==LinphoneCallOutgoingEarlyMedia && !call->params.real_early_media)){ + all_muted=TRUE; + } + linphone_call_start_media_streams(call,all_muted,send_ringbacktone); + } } static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){ @@ -55,7 +112,9 @@ static void call_received(SalOp *h){ char *tmp; LinphoneAddress *from_parsed; LinphoneAddress *from_addr, *to_addr; - const char * early_media=linphone_core_get_remote_ringback_tone (lc); + SalMediaDescription *md; + bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE); + const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc); /* first check if we can answer successfully to this invite */ if (lc->presence_mode==LinphoneStatusBusy || @@ -93,16 +152,14 @@ static void call_received(SalOp *h){ call=linphone_call_new_incoming(lc,from_addr,to_addr,h); sal_call_set_local_media_description(h,call->localdesc); - call->resultdesc=sal_call_get_final_media_description(h); - if (call->resultdesc) - sal_media_description_ref(call->resultdesc); - if (call->resultdesc && sal_media_description_empty(call->resultdesc)){ + md=sal_call_get_final_media_description(h); + + if (md && sal_media_description_empty(md)){ sal_call_decline(h,SalReasonMedia,NULL); linphone_call_unref(call); return; } - /* the call is acceptable so we can now add it to our list */ linphone_core_add_call(lc,call); @@ -124,7 +181,7 @@ static void call_received(SalOp *h){ lc->ringstream=NULL; lc->dmfs_playing_start_time=0; } - if(lc->ringstream==NULL){ + if(lc->ringstream==NULL && lc->sound_conf.local_ring){ MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard; ms_message("Starting local ring..."); lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard); @@ -136,17 +193,18 @@ static void call_received(SalOp *h){ }else{ /*TODO : play a tone within the context of the current call */ } - sal_call_notify_ringing(h,early_media!=NULL); -#if !(__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000) - linphone_call_init_media_streams(call); - if (early_media!=NULL){ - linphone_call_start_early_media (call); + linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + + sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL); + + if (propose_early_media || ringback_tone!=NULL){ + linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media"); + linphone_core_update_streams(lc,call,md); } -#endif ms_free(barmesg); ms_free(tmp); - linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call"); + if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){ linphone_core_accept_call(lc,call); } @@ -183,8 +241,6 @@ static void call_ringing(SalOp *h){ ms_message("Early media already started."); return; } - sal_media_description_ref(md); - call->resultdesc=md; if (lc->vtable.show) lc->vtable.show(lc); if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Early media.")); @@ -194,8 +250,7 @@ static void call_ringing(SalOp *h){ lc->ringstream=NULL; } ms_message("Doing early media..."); - linphone_call_start_media_streams(call); - call->media_pending=TRUE; + linphone_core_update_streams (lc,call,md); } } @@ -207,33 +262,23 @@ static void call_ringing(SalOp *h){ static void call_accepted(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - + SalMediaDescription *md; + if (call==NULL){ ms_warning("No call to accept."); return ; } - if ((call->audiostream!=NULL) && (call->audiostream->ticker!=NULL)){ - /*case where we accepted early media or already in call*/ - linphone_call_stop_media_streams(call); - } - if (call->audiostream==NULL){ - linphone_call_init_media_streams(call); - } - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc){ - sal_media_description_ref(call->resultdesc); - call->media_pending=FALSE; - } + + md=sal_call_get_final_media_description(op); + if (call->state==LinphoneCallOutgoingProgress || call->state==LinphoneCallOutgoingRinging || call->state==LinphoneCallOutgoingEarlyMedia){ linphone_call_set_state(call,LinphoneCallConnected,"Connected"); } - if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - if (sal_media_description_has_dir(call->resultdesc,SalStreamSendOnly) || - sal_media_description_has_dir(call->resultdesc,SalStreamInactive)){ + if (md && !sal_media_description_empty(md)){ + if (sal_media_description_has_dir(md,SalStreamSendOnly) || + sal_media_description_has_dir(md,SalStreamInactive)){ if (lc->vtable.display_status){ char *tmp=linphone_call_get_remote_address_as_string (call); char *msg=ms_strdup_printf(_("Call with %s is paused."),tmp); @@ -242,7 +287,7 @@ static void call_accepted(SalOp *op){ ms_free(msg); } linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); - }else if (sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly)){ + }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){ /*we are put on hold when the call is initially accepted */ if (lc->vtable.display_status){ char *tmp=linphone_call_get_remote_address_as_string (call); @@ -256,9 +301,14 @@ static void call_accepted(SalOp *op){ if (lc->vtable.display_status){ lc->vtable.display_status(lc,_("Call answered - connected.")); } + if (call->state==LinphoneCallStreamsRunning){ + /*media was running before, the remote as acceted a call modification (that is + a reinvite made by us. We must notify the application this reinvite was accepted*/ + linphone_call_set_state(call, LinphoneCallUpdated, "Call updated"); + } linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); } - linphone_connect_incoming (lc,call); + linphone_core_update_streams (lc,call,md); }else{ /*send a bye*/ ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); @@ -274,18 +324,14 @@ static void call_ack(SalOp *op){ return ; } if (call->media_pending){ - if (call->audiostream->ticker!=NULL){ - /*case where we accepted early media */ - linphone_call_stop_media_streams(call); - linphone_call_init_media_streams(call); - } - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc) - sal_media_description_ref(call->resultdesc); - if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ - linphone_connect_incoming(lc,call); + SalMediaDescription *md=sal_call_get_final_media_description(op); + if (md && !sal_media_description_empty(md)){ + if (call->state==LinphoneCallStreamsRunning){ + /*media was running before, the remote as acceted a call modification (that is + a reinvite made by us. We must notify the application this reinvite was accepted*/ + linphone_call_set_state(call, LinphoneCallUpdated, "Call updated"); + } + linphone_core_update_streams (lc,call,md); linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); }else{ /*send a bye*/ @@ -293,7 +339,6 @@ static void call_ack(SalOp *op){ linphone_core_abort_call(lc,call,"No codec intersection"); return; } - call->media_pending=FALSE; } } @@ -302,17 +347,14 @@ static void call_updating(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); LinphoneCallState prevstate=LinphoneCallIdle; + SalMediaDescription *md; - if (call->resultdesc) - sal_media_description_unref(call->resultdesc); - call->resultdesc=sal_call_get_final_media_description(op); - if (call->resultdesc) - sal_media_description_ref(call->resultdesc); - - if (call->resultdesc && !sal_media_description_empty(call->resultdesc)) + md=sal_call_get_final_media_description(op); + + if (md && !sal_media_description_empty(md)) { if ((call->state==LinphoneCallPausedByRemote || call->state==LinphoneCallPaused) && - sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv) && strcmp(call->resultdesc->addr,"0.0.0.0")!=0){ + sal_media_description_has_dir(md,SalStreamSendRecv) && strcmp(md->addr,"0.0.0.0")!=0){ /*make sure we can be resumed */ if (lc->current_call!=NULL && lc->current_call!=call){ ms_warning("Attempt to be resumed but already in call with somebody else!"); @@ -325,9 +367,9 @@ static void call_updating(SalOp *op){ linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); } else if(call->state==LinphoneCallStreamsRunning && - ( sal_media_description_has_dir(call->resultdesc,SalStreamRecvOnly) - || sal_media_description_has_dir(call->resultdesc,SalStreamInactive) - || strcmp(call->resultdesc->addr,"0.0.0.0")==0)){ + ( sal_media_description_has_dir(md,SalStreamRecvOnly) + || sal_media_description_has_dir(md,SalStreamInactive) + || strcmp(md->addr,"0.0.0.0")==0)){ if(lc->vtable.display_status) lc->vtable.display_status(lc,_("We are being paused...")); linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote"); @@ -340,9 +382,7 @@ static void call_updating(SalOp *op){ } /*accept the modification (sends a 200Ok)*/ sal_call_accept(op); - linphone_call_stop_media_streams (call); - linphone_call_init_media_streams (call); - linphone_call_start_media_streams (call); + linphone_core_update_streams (lc,call,md); if (prevstate!=LinphoneCallIdle){ linphone_call_set_state (call,prevstate,"Connected (streams running)"); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 56509c927..11b46c89e 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -40,21 +40,25 @@ static MSWebCam *get_nowebcam_device(){ #endif -static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, bool_t only_one_codec){ +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandwidth_limit){ MSList *l=NULL; const MSList *it; for(it=codecs;it!=NULL;it=it->next){ PayloadType *pt=(PayloadType*)it->data; - if ((pt->flags & PAYLOAD_TYPE_ENABLED) && linphone_core_check_payload_type_usability(lc,pt)){ - l=ms_list_append(l,payload_type_clone(pt)); - if (only_one_codec) break; + if (pt->flags & PAYLOAD_TYPE_ENABLED){ + if (bandwidth_limit>0 && !linphone_core_is_payload_type_usable_for_bandwidth(lc,pt,bandwidth_limit)){ + ms_message("Codec %s/%i eliminated because of audio bandwidth constraint.",pt->mime_type,pt->clock_rate); + continue; + } + if (linphone_core_check_payload_type_usability(lc,pt)){ + l=ms_list_append(l,payload_type_clone(pt)); + } } } return l; } -SalMediaDescription *create_local_media_description(LinphoneCore *lc, - LinphoneCall *call, bool_t with_video, bool_t only_one_codec){ +SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call){ MSList *l; PayloadType *pt; const char *me=linphone_core_get_identity(lc); @@ -72,7 +76,7 @@ SalMediaDescription *create_local_media_description(LinphoneCore *lc, md->streams[0].proto=SalProtoRtpAvp; md->streams[0].type=SalAudio; md->streams[0].ptime=lc->net_conf.down_ptime; - l=make_codec_list(lc,lc->codecs_conf.audio_codecs,only_one_codec); + l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw); pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event")); l=ms_list_append(l,pt); md->streams[0].payloads=l; @@ -80,12 +84,12 @@ SalMediaDescription *create_local_media_description(LinphoneCore *lc, if (lc->dw_audio_bw>0) md->streams[0].bandwidth=lc->dw_audio_bw; - if (with_video){ + if (call->params.has_video){ md->nstreams++; md->streams[1].port=call->video_port; md->streams[1].proto=SalProtoRtpAvp; md->streams[1].type=SalVideo; - l=make_codec_list(lc,lc->codecs_conf.video_codecs,only_one_codec); + l=make_codec_list(lc,lc->codecs_conf.video_codecs,0); md->streams[1].payloads=l; if (lc->dw_video_bw) md->streams[1].bandwidth=lc->dw_video_bw; @@ -156,7 +160,7 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); linphone_call_init_common(call,from,to); call->params=*params; - call->localdesc=create_local_media_description (lc,call,params->has_video,FALSE); + call->localdesc=create_local_media_description (lc,call); call->camera_active=params->has_video; if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) linphone_core_run_stun_tests(call->core,call); @@ -194,8 +198,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); linphone_call_init_common(call, from, to); call->params.has_video=linphone_core_video_enabled(lc); - call->localdesc=create_local_media_description (lc,call, - call->params.has_video,lc->sip_conf.only_one_codec); + call->localdesc=create_local_media_description (lc,call); call->camera_active=call->params.has_video; if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) linphone_core_run_stun_tests(call->core,call); @@ -279,6 +282,10 @@ const char *linphone_call_state_to_string(LinphoneCallState cs){ return "LinphoneCallPausedByRemote"; case LinphoneCallUpdatedByRemote: return "LinphoneCallUpdatedByRemote"; + case LinphoneCallIncomingEarlyMedia: + return "LinphoneCallIncomingEarlyMedia"; + case LinphoneCallUpdated: + return "LinphoneCallUpdated"; } return "undefined state"; } @@ -521,6 +528,25 @@ bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp){ return cp->has_video; } +/** + * Enable sending of real early media (during outgoing calls). +**/ +void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled){ + cp->real_early_media=enabled; +} + +bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp){ + return cp->real_early_media; +} + +/** + * Refine bandwidth settings for this call by setting a bandwidth limit for audio streams. + * As a consequence, codecs whose bitrates are not compatible with this limit won't be used. +**/ +void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bandwidth){ + cp->audio_bw=bandwidth; +} + /** * **/ @@ -638,9 +664,10 @@ static void post_configure_audio_streams(LinphoneCall*call){ float ng_floorgain=lp_config_get_float(lc->config,"sound","ng_floorgain",0); int dc_removal=lp_config_get_int(lc->config,"sound","dc_removal",0); - if (mic_gain!=-1) + if (!call->audio_muted) audio_stream_set_mic_gain(st,mic_gain); - call->audio_muted=FALSE; + else + audio_stream_set_mic_gain(st,0); recv_gain = lc->sound_conf.soft_play_lev; if (recv_gain != 0) { @@ -738,18 +765,21 @@ static RtpProfile *make_profile(LinphoneCore *lc, const SalMediaDescription *md, return prof; } + static void setup_ring_player(LinphoneCore *lc, LinphoneCall *call){ int pause_time=3000; audio_stream_play(call->audiostream,lc->sound_conf.ringback_tone); ms_filter_call_method(call->audiostream->soundread,MS_FILE_PLAYER_LOOP,&pause_time); } -static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_early_media){ + +void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone){ LinphoneCore *lc=call->core; LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); const char *tool="linphone-" LINPHONE_VERSION; char *cname; int used_pt=-1; + if(call->audiostream == NULL) { ms_fatal("start_media_stream() called without prior init !"); @@ -783,12 +813,15 @@ static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_e if (stream->port==0 || stream->dir==SalStreamRecvOnly){ captcard=NULL; playfile=NULL; - }else if (stream->dir==SalStreamSendOnly || send_early_media){ + }else if (stream->dir==SalStreamSendOnly){ playcard=NULL; captcard=NULL; recfile=NULL; - if (send_early_media) - playfile=NULL; + playfile=NULL; + } + if (send_ringbacktone){ + captcard=NULL; + playfile=NULL;/* it is setup later*/ } /*if playfile are supplied don't use soundcards*/ if (lc->use_files) { @@ -809,13 +842,18 @@ static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_e captcard, captcard==NULL ? FALSE : linphone_core_echo_cancellation_enabled(lc)); post_configure_audio_streams(call); - if (send_early_media) setup_ring_player(lc,call); + if (all_inputs_muted && !send_ringbacktone){ + audio_stream_set_mic_gain(call->audiostream,0); + } + if (send_ringbacktone){ + setup_ring_player(lc,call); + } audio_stream_set_rtcp_information(call->audiostream, cname, tool); }else ms_warning("No audio stream accepted ?"); } } #ifdef VIDEO_ENABLED - if (!send_early_media){ + { const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, SalProtoRtpAvp,SalVideo); used_pt=-1; @@ -859,7 +897,7 @@ static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_e /*either inactive or incompatible with local capabilities*/ is_inactive=TRUE; } - if (call->camera_active==FALSE){ + if (call->camera_active==FALSE || all_inputs_muted){ cam=get_nowebcam_device(); } if (!is_inactive){ @@ -876,21 +914,16 @@ static void _linphone_call_start_media_streams(LinphoneCall *call, bool_t send_e } } #endif + call->all_muted=all_inputs_muted; + call->playing_ringbacktone=send_ringbacktone; + call->up_bw=linphone_core_get_upload_bandwidth(lc); + goto end; end: ms_free(cname); linphone_address_destroy(me); } - -void linphone_call_start_media_streams(LinphoneCall *call){ - _linphone_call_start_media_streams(call,FALSE); -} - -void linphone_call_start_early_media(LinphoneCall *call){ - _linphone_call_start_media_streams(call,TRUE); -} - static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){ audio_stream_get_local_rtp_stats (st,&log->local_stats); } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index d06f3cf3b..16a624c6a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -486,7 +486,6 @@ static void sip_config_read(LinphoneCore *lc) sal_use_session_timers(lc->sal,200); } - tmp=lp_config_get_int(lc->config,"sip","use_rfc2833",0); linphone_core_set_use_rfc2833_for_dtmf(lc,tmp); @@ -562,21 +561,16 @@ static void sip_config_read(LinphoneCore *lc) break; } } - - - - - lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0); /*for tuning or test*/ lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0); - lc->sip_conf.only_one_codec=lp_config_get_int(lc->config,"sip","only_one_codec",0); lc->sip_conf.register_only_when_network_is_up= lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1); lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1); lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1); lc->sip_conf.keepalive_period=lp_config_get_int(lc->config,"sip","keepalive_period",10000); sal_set_keepalive_period(lc->sal,lc->sip_conf.keepalive_period); + sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0)); } static void rtp_config_read(LinphoneCore *lc) @@ -2193,8 +2187,9 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, LinphoneCall if (call->localdesc) sal_media_description_unref(call->localdesc); - call->localdesc=create_local_media_description (lc,call, - params->has_video,FALSE); + call->params=*params; + call->localdesc=create_local_media_description (lc,call); + call->camera_active=params->has_video; if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Modifying call parameters...")); sal_call_set_local_media_description (call->op,call->localdesc); @@ -2221,6 +2216,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; SalOp *replaced; + SalMediaDescription *new_md; if (call==NULL){ //if just one call is present answer the only one ... @@ -2276,26 +2272,21 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) contact=get_fixed_contact(lc,call,cfg); if (contact) sal_op_set_contact(call->op,contact); -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 - linphone_call_init_media_streams(call); -#else - if (call->audiostream!=NULL && call->audiostream->ticker!=NULL){ - /*case where we sent early media*/ - linphone_call_stop_media_streams (call); - linphone_call_init_media_streams (call); - } -#endif + + if (call->audiostream==NULL) + linphone_call_init_media_streams(call); + sal_call_accept(call->op); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Connected.")); lc->current_call=call; linphone_call_set_state(call,LinphoneCallConnected,"Connected"); - call->resultdesc=sal_call_get_final_media_description(call->op); - if (call->resultdesc){ - linphone_call_start_media_streams(call); + new_md=sal_call_get_final_media_description(call->op); + linphone_core_update_streams(lc, call, new_md); + if (new_md){ linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); - sal_media_description_ref(call->resultdesc); }else call->media_pending=TRUE; + ms_message("call answered."); return 0; } @@ -2830,7 +2821,7 @@ void linphone_core_set_sound_source(LinphoneCore *lc, char source) /** * Sets the path to a wav file used for ringing. * - * The file must be a wav 16bit linear. + * @param path The file must be a wav 16bit linear. Local ring is disabled if null * * @ingroup media_parameters **/ @@ -2946,17 +2937,12 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ } bool_t linphone_core_is_mic_muted(LinphoneCore *lc) { - float gain=1.0; LinphoneCall *call=linphone_core_get_current_call(lc); if (call==NULL){ ms_warning("linphone_core_is_mic_muted(): No current call !"); return FALSE; } - if (call->audiostream && call->audiostream->volsend){ - ms_filter_call_method(call->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain); - }else ms_warning("Could not get gain: gain control wasn't activated. "); - - return gain==0 || call->audio_muted; + return call->audio_muted; } // returns rtp transmission status for an active stream diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 012437f62..219b13a7e 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -168,8 +168,8 @@ char * linphone_call_log_to_str(LinphoneCallLog *cl); /** - * The LinphoneCallParams is an object contaning various call related parameters. - * It can be used to retrieve parameters from a currently running call or modify the call's characterisitcs + * The LinphoneCallParams is an object containing various call related parameters. + * It can be used to retrieve parameters from a currently running call or modify the call's characteristics * dynamically. **/ struct _LinphoneCallParams; @@ -178,6 +178,9 @@ typedef struct _LinphoneCallParams LinphoneCallParams; LinphoneCallParams * linphone_call_params_copy(const LinphoneCallParams *cp); void linphone_call_params_enable_video(LinphoneCallParams *cp, bool_t enabled); bool_t linphone_call_params_video_enabled(const LinphoneCallParams *cp); +void linphone_call_params_enable_early_media_sending(LinphoneCallParams *cp, bool_t enabled); +bool_t linphone_call_params_early_media_sending_enabled(const LinphoneCallParams *cp); +void linphone_call_params_set_audio_bandwidth_limit(LinphoneCallParams *cp, int bw); void linphone_call_params_destroy(LinphoneCallParams *cp); /** @@ -216,7 +219,9 @@ typedef enum _LinphoneCallState{ LinphoneCallError, /**GetStringUTFChars(jpath, NULL):NULL; + linphone_core_set_ring((LinphoneCore*)lc,path); + if (path) env->ReleaseStringUTFChars(jpath, path); +} +extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getRing(JNIEnv* env + ,jobject thiz + ,jlong lc + ) { + const char* path = linphone_core_get_ring((LinphoneCore*)lc); + if (path) { + return env->NewStringUTF(path); + } else { + return NULL; + } +} + //ProxyConfig @@ -973,5 +993,57 @@ extern "C" jstring Java_org_linphone_core_LinphoneCoreImpl_getStunServer(JNIEnv return jvalue; } +extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_enableVideo(JNIEnv *env, jobject thiz, jlong lcp, jboolean b){ + linphone_call_params_enable_video((LinphoneCallParams*)lcp, b); +} + +extern "C" jboolean Java_org_linphone_core_LinphoneCallParamsImpl_getVideoEnabled(JNIEnv *env, jobject thiz, jlong lcp){ + return linphone_call_params_video_enabled((LinphoneCallParams*)lcp); +} +extern "C" jlong Java_org_linphone_core_LinphoneCallParamsImpl_copy(JNIEnv *env, jobject thiz, jlong lcp){ + return (jlong) linphone_call_params_copy((LinphoneCallParams*)lcp); +} +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createDefaultCallParams(JNIEnv *env, jobject thiz, jlong lc){ + return (jlong) linphone_core_create_default_call_parameters((LinphoneCore*)lc); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getCurrentParams(JNIEnv *env, jobject thiz, jlong lc){ + return (jlong) linphone_call_get_current_params((LinphoneCall*)lc); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_inviteAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong addr, jlong params){ + return (jlong) linphone_core_invite_address_with_params((LinphoneCore *)lc, (const LinphoneAddress *)addr, (const LinphoneCallParams *)params); +} + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateAddressWithParams(JNIEnv *env, jobject thiz, jlong lc, jlong call, jlong params){ + return (jint) linphone_core_update_call((LinphoneCore *)lc, (LinphoneCall *)call, (LinphoneCallParams *)params); +} + +extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_updateCall(JNIEnv *env, jobject thiz, jlong lc, jlong call, jlong params){ + return (jint) linphone_core_update_call((LinphoneCore *)lc, (LinphoneCall *)call, (LinphoneCallParams *)params); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setPreferredVideoSize(JNIEnv *env, jobject thiz, jlong lc, jint width, jint height){ + MSVideoSize vsize; + vsize.width = (int)width; + vsize.height = (int)height; + linphone_core_set_preferred_video_size((LinphoneCore *)lc, vsize); +} + +extern "C" jintArray Java_org_linphone_core_LinphoneCoreImpl_getPreferredVideoSize(JNIEnv *env, jobject thiz, jlong lc){ + MSVideoSize vsize = linphone_core_get_preferred_video_size((LinphoneCore *)lc); + jintArray arr = env->NewIntArray(2); + int tVsize [2]= {vsize.width,vsize.height}; + env->SetIntArrayRegion(arr, 0, 2, tVsize); + return arr; +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setDownloadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){ + linphone_core_set_download_bandwidth((LinphoneCore *)lc, (int) bw); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setUploadBandwidth(JNIEnv *env, jobject thiz, jlong lc, jint bw){ + linphone_core_set_upload_bandwidth((LinphoneCore *)lc, (int) bw); +} + diff --git a/coreapi/misc.c b/coreapi/misc.c index 0ea84ca1d..a83cd8394 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -258,6 +258,39 @@ void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){ } } +bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit) +{ + double codec_band; + bool_t ret=FALSE; + + switch (pt->type){ + case PAYLOAD_AUDIO_CONTINUOUS: + case PAYLOAD_AUDIO_PACKETIZED: + codec_band=get_audio_payload_bandwidth(lc,pt); + ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band); + /*hack to avoid using uwb codecs when having low bitrate and video*/ + if (bandwidth_is_greater(199,bandwidth_limit)){ + if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){ + ret=FALSE; + } + } + //ms_message("Payload %s: %g",pt->mime_type,codec_band); + break; + case PAYLOAD_VIDEO: + if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/ + /*let the video use all the bandwidth minus the maximum bandwidth used by audio */ + if (bandwidth_limit>0) + pt->normal_bitrate=bandwidth_limit*1000; + else + pt->normal_bitrate=1500000; /*around 1.5 Mbit/s*/ + ret=TRUE; + } + else ret=FALSE; + break; + } + return ret; +} + /* return TRUE if codec can be used with bandwidth, FALSE else*/ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt) { diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index baf84287a..0db6b08a4 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -20,6 +20,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal.h" #include "offeranswer.h" +static bool_t only_telephone_event(const MSList *l){ + PayloadType *p=(PayloadType*)l->data; + if (strcasecmp(p->mime_type,"telephone-event")!=0){ + return FALSE; + } + return TRUE; +} static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ PayloadType *pt; @@ -53,10 +60,12 @@ static PayloadType * find_payload_type_best_match(const MSList *l, const Payload return candidate; } -static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response){ +static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){ const MSList *e2; MSList *res=NULL; PayloadType *matched; + bool_t found_codec=FALSE; + for(e2=remote;e2!=NULL;e2=e2->next){ PayloadType *p2=(PayloadType*)e2->data; matched=find_payload_type_best_match(local,p2); @@ -64,6 +73,14 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t PayloadType *newp; int local_number=payload_type_get_number(matched); int remote_number=payload_type_get_number(p2); + + if (one_matching_codec){ + if (strcasecmp(matched->mime_type,"telephone-event")!=0){ + if (found_codec){/* we have found a real codec already*/ + continue; /*this codec won't be added*/ + }else found_codec=TRUE; + } + } newp=payload_type_clone(matched); if (p2->send_fmtp) @@ -90,13 +107,7 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t return res; } -static bool_t only_telephone_event(const MSList *l){ - PayloadType *p=(PayloadType*)l->data; - if (strcasecmp(p->mime_type,"telephone-event")!=0){ - return FALSE; - } - return TRUE; -} + static SalStreamDir compute_dir(SalStreamDir local, SalStreamDir answered){ SalStreamDir res=local; @@ -117,7 +128,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, const SalStreamDescription *remote_answer, SalStreamDescription *result){ if (remote_answer->port!=0) - result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE); + result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE); result->proto=local_offer->proto; result->type=local_offer->type; result->dir=compute_dir(local_offer->dir,remote_answer->dir); @@ -135,8 +146,8 @@ static void initiate_outgoing(const SalStreamDescription *local_offer, static void initiate_incoming(const SalStreamDescription *local_cap, const SalStreamDescription *remote_offer, - SalStreamDescription *result){ - result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE); + SalStreamDescription *result, bool_t one_matching_codec){ + result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec); result->proto=local_cap->proto; result->type=local_cap->type; if (remote_offer->dir==SalStreamSendOnly) @@ -187,7 +198,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, **/ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, const SalMediaDescription *remote_offer, - SalMediaDescription *result){ + SalMediaDescription *result, bool_t one_matching_codec){ int i,j; const SalStreamDescription *ls,*rs; @@ -196,7 +207,7 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities ms_message("Processing for stream %i",i); ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type); if (ls){ - initiate_incoming(ls,rs,&result->streams[j]); + initiate_incoming(ls,rs,&result->streams[j],one_matching_codec); ++j; } } diff --git a/coreapi/offeranswer.h b/coreapi/offeranswer.h index 079f41c96..5aa658ae6 100644 --- a/coreapi/offeranswer.h +++ b/coreapi/offeranswer.h @@ -41,7 +41,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, **/ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, const SalMediaDescription *remote_offer, - SalMediaDescription *result); + SalMediaDescription *result, bool_t one_matching_codec); #endif diff --git a/coreapi/private.h b/coreapi/private.h index 969c337e9..1203fe0fa 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -58,8 +58,10 @@ struct _LinphoneCallParams{ LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */ + int audio_bw; /* bandwidth limit for audio stream */ bool_t has_video; - bool_t pad[3]; + bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/ + bool_t pad[2]; }; struct _LinphoneCall @@ -86,10 +88,13 @@ struct _LinphoneCall struct _VideoStream *videostream; char *refer_to; LinphoneCallParams params; + int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */ bool_t refer_pending; bool_t media_pending; bool_t audio_muted; bool_t camera_active; + bool_t all_muted; /*this flag is set during early medias*/ + bool_t playing_ringbacktone; }; @@ -187,8 +192,7 @@ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg); void linphone_call_init_media_streams(LinphoneCall *call); -void linphone_call_start_media_streams(LinphoneCall *call); -void linphone_call_start_early_media(LinphoneCall *call); +void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_muted, bool_t send_ringbacktone); void linphone_call_stop_media_streams(LinphoneCall *call); const char * linphone_core_get_identity(LinphoneCore *lc); @@ -278,7 +282,6 @@ typedef struct sip_config bool_t loopback_only; bool_t ipv6_enabled; bool_t sdp_200_ack; - bool_t only_one_codec; /*in SDP answers*/ bool_t register_only_when_network_is_up; bool_t ping_with_options; bool_t auto_net_state_mon; @@ -436,8 +439,11 @@ int linphone_core_get_calls_nb(const LinphoneCore *lc); void linphone_core_set_state(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); -SalMediaDescription *create_local_media_description(LinphoneCore *lc, - LinphoneCall *call, bool_t with_video, bool_t only_one_codec); +SalMediaDescription *create_local_media_description(LinphoneCore *lc, LinphoneCall *call); + +void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md); + +bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit); #define linphone_core_ready(lc) ((lc)->state!=LinphoneGlobalStartup) void _linphone_core_configure_resolver(); diff --git a/coreapi/sal.c b/coreapi/sal.c index ac44470a6..979969b24 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -94,6 +94,72 @@ bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir return found; } +/* +static bool_t fmtp_equals(const char *p1, const char *p2){ + if (p1 && p2 && strcmp(p1,p2)==0) return TRUE; + if (p1==NULL && p2==NULL) return TRUE; + return FALSE; +} +*/ + +static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){ + if (p1->type!=p2->type) return FALSE; + if (strcmp(p1->mime_type,p2->mime_type)!=0) return FALSE; + if (p1->clock_rate!=p2->clock_rate) return FALSE; + if (p1->channels!=p2->channels) return FALSE; + /* + Do not compare fmtp right now: they are modified internally when the call is started + */ + /* + if (!fmtp_equals(p1->recv_fmtp,p2->recv_fmtp) || + !fmtp_equals(p1->send_fmtp,p2->send_fmtp)) + return FALSE; + */ + return TRUE; +} + +static bool_t payload_list_equals(const MSList *l1, const MSList *l2){ + const MSList *e1,*e2; + for(e1=l1,e2=l2;e1!=NULL && e2!=NULL; e1=e1->next,e2=e2->next){ + PayloadType *p1=(PayloadType*)e1->data; + PayloadType *p2=(PayloadType*)e2->data; + if (!payload_type_equals(p1,p2)) + return FALSE; + } + if (e1!=NULL || e2!=NULL){ + /*means one list is longer than the other*/ + abort(); + return FALSE; + } + return TRUE; +} + +bool_t sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2){ + if (sd1->proto!=sd2->proto) return FALSE; + if (sd1->type!=sd2->type) return FALSE; + if (strcmp(sd1->addr,sd2->addr)!=0) return FALSE; + if (sd1->port!=sd2->port) return FALSE; + if (!payload_list_equals(sd1->payloads,sd2->payloads)) return FALSE; + if (sd1->bandwidth!=sd2->bandwidth) return FALSE; + if (sd1->ptime!=sd2->ptime) return FALSE; + /* compare candidates: TODO */ + if (sd1->dir!=sd2->dir) return FALSE; + return TRUE; +} + +bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2){ + int i; + + if (strcmp(md1->addr,md2->addr)!=0) return FALSE; + if (md1->nstreams!=md2->nstreams) return FALSE; + if (md1->bandwidth!=md2->bandwidth) return FALSE; + for(i=0;instreams;++i){ + if (!sal_stream_description_equals(&md1->streams[i],&md2->streams[i])) + return FALSE; + } + return TRUE; +} + static void assign_string(char **str, const char *arg){ if (*str){ ms_free(*str); diff --git a/coreapi/sal.h b/coreapi/sal.h index b0b3c7574..d98d5eb5c 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -127,6 +127,7 @@ SalMediaDescription *sal_media_description_new(); void sal_media_description_ref(SalMediaDescription *md); void sal_media_description_unref(SalMediaDescription *md); bool_t sal_media_description_empty(const SalMediaDescription *md); +bool_t sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2); bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir dir); SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md, SalMediaProto proto, SalStreamType type); @@ -245,6 +246,7 @@ void sal_set_user_agent(Sal *ctx, const char *user_agent); /*keepalive period in ms*/ void sal_set_keepalive_period(Sal *ctx,unsigned int value); void sal_use_session_timers(Sal *ctx, int expires); +void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec); int sal_iterate(Sal *sal); MSList * sal_get_pending_auths(Sal *sal); diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index 59467f379..fb0eb49ea 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -379,6 +379,10 @@ void sal_use_session_timers(Sal *ctx, int expires){ ctx->session_expires=expires; } +void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){ + ctx->one_matching_codec=one_matching_codec; +} + MSList *sal_get_pending_auths(Sal *sal){ return ms_list_copy(sal->pending_auths); } @@ -449,7 +453,7 @@ static void sdp_process(SalOp *h){ offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result); }else{ int i; - offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result); + offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec); h->sdp_answer=media_description_to_sdp(h->result); strcpy(h->result->addr,h->base.remote_media->addr); h->result->bandwidth=h->base.remote_media->bandwidth; @@ -1955,7 +1959,7 @@ int sal_call_hold(SalOp *h, bool_t holdon) osip_message_t *reinvite=NULL; if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL) return -1; - osip_message_set_subject(reinvite,osip_strdup("Phone Call Hold")); + osip_message_set_subject(reinvite,holdon ? "Phone call hold" : "Phone call resume" ); osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); if (h->base.root->session_expires!=0){ osip_message_set_header(reinvite, "Session-expires", "200"); diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index 10c8b46b7..ad6eeb7f4 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -40,6 +40,7 @@ struct Sal{ int session_expires; int keepalive_period; void *up; + bool_t one_matching_codec; }; struct SalOp{ diff --git a/gtk/main.c b/gtk/main.c index 584db85e7..f88f9cfcd 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -512,6 +512,7 @@ static gboolean linphone_gtk_iterate(LinphoneCore *lc){ static gboolean first_time=TRUE; unsigned long id; static unsigned long previd=0; + static unsigned long preview_previd=0; static gboolean in_iterate=FALSE; /*avoid reentrancy*/ @@ -534,6 +535,25 @@ static gboolean linphone_gtk_iterate(LinphoneCore *lc){ w=gdk_window_foreign_new(id); #else w=gdk_window_foreign_new((HANDLE)id); +#endif + if (w) { + set_video_window_decorations(w); + g_object_unref(G_OBJECT(w)); + } + else ms_error("gdk_window_foreign_new() failed"); + if (video_needs_update) video_needs_update=FALSE; + } + } + id=linphone_core_get_native_preview_window_id (lc); + if (id!=preview_previd ){ + GdkWindow *w; + preview_previd=id; + if (id!=0){ + ms_message("Updating window decorations for preview"); +#ifndef WIN32 + w=gdk_window_foreign_new(id); +#else + w=gdk_window_foreign_new((HANDLE)id); #endif if (w) { set_video_window_decorations(w); diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index 65923706c..9a1fe963d 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -89,10 +89,28 @@ public interface LinphoneCall { * Call end */ public final static State CallEnd = new State(13,"CallEnd"); + /** * Paused by remote */ public final static State PausedByRemote = new State(14,"PausedByRemote"); + + /** + * The call's parameters are updated, used for example when video is asked by remote + */ + public static final State CallUpdatedByRemote = new State(15, "CallUpdatedByRemote"); + + /** + * We are proposing early media to an incoming call + */ + public static final State CallIncomingEarlyMedia = new State(16,"CallIncomingEarlyMedia"); + + /** + * The remote accepted the call update initiated by us + */ + public static final State CallUpdated = new State(17, "CallUpdated"); + + private State(int value,String stringValue) { mValue = value; values.addElement(this); @@ -132,4 +150,9 @@ public interface LinphoneCall { **/ public LinphoneCallLog getCallLog(); + /** + * @return parameters for this call; read only, call copy() to get a read/write version. + */ + public LinphoneCallParams getCurrentParamsReadOnly(); + } diff --git a/java/common/org/linphone/core/LinphoneCallParams.java b/java/common/org/linphone/core/LinphoneCallParams.java new file mode 100644 index 000000000..30362a73b --- /dev/null +++ b/java/common/org/linphone/core/LinphoneCallParams.java @@ -0,0 +1,32 @@ +/* +LinphoneCallParameters.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +/** + * The LinphoneCallParams is an object containing various call related parameters. + * It can be used to retrieve parameters from a currently running call or modify the call's characteristics + * dynamically. + * @author Guillaume Beraudo + * + */ +public interface LinphoneCallParams { + void setVideoEnabled(boolean b); + boolean getVideoEnabled(); + LinphoneCallParams copy(); +} diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 0c8c0afeb..cbf3bdd34 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -115,7 +115,7 @@ public interface LinphoneCore { } } /** - * Describes proxy registration states. + * Describes firewall policy. * */ static public class FirewallPolicy { @@ -298,7 +298,12 @@ public interface LinphoneCore { * @param network state * */ - public void setNetworkStateReachable(boolean isReachable); + public void setNetworkReachable(boolean isReachable); + /** + * + * @return if false, there is no network connection. + */ + public boolean isNetworkReachable(); /** * destroy linphone core and free all underlying resources */ @@ -454,4 +459,30 @@ public interface LinphoneCore { * @return previously set firewall policy. */ public FirewallPolicy getFirewallPolicy(); + + public LinphoneCall inviteAddressWithParams(LinphoneAddress destination, LinphoneCallParams params) throws LinphoneCoreException ; + + public int updateCall(LinphoneCall call, LinphoneCallParams params); + + public LinphoneCallParams createDefaultCallParameters(); + + /** + * Sets the path to a wav file used for ringing. + * + * @param path The file must be a wav 16bit linear. Local ring is disabled if null + */ + public void setRing(String path); + /** + * gets the path to a wav file used for ringing. + * + * @param null if not set + */ + public String getRing(); + public void setUploadBandwidth(int bw); + + public void setDownloadBandwidth(int bw); + + public void setPreferredVideoSize(VideoSize vSize); + + public VideoSize getPreferredVideoSize(); } diff --git a/java/common/org/linphone/core/VideoSize.java b/java/common/org/linphone/core/VideoSize.java new file mode 100644 index 000000000..1d931624b --- /dev/null +++ b/java/common/org/linphone/core/VideoSize.java @@ -0,0 +1,84 @@ +/* +VideoSize.java +Copyright (C) 2010 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +package org.linphone.core; + +/** + * @author Guillaume Beraudo + */ +public final class VideoSize { + public static final int QCIF = 0; + public static final int CIF = 1; + public static final int HVGA = 2; + + private int width; + public int getWidth() {return width;} + public void setWidth(int width) {this.width = width;} + + private int height; + public int getHeight() {return height;} + public void setHeight(int height) {this.height = height;} + + public VideoSize() {} + private VideoSize(int width, int height) { + this.width = width; + this.height = height; + } + + public static final VideoSize createStandard(int code) { + switch (code) { + case QCIF: + return new VideoSize(176, 144); + case CIF: + return new VideoSize(352, 288); + case HVGA: + return new VideoSize(320, 480); + default: + return new VideoSize(); // Invalid one + } + } + + public boolean isValid() { + return width > 0 && height > 0; + } + + // Generated + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + height; + result = prime * result + width; + return result; + } + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VideoSize other = (VideoSize) obj; + if (height != other.height) + return false; + if (width != other.width) + return false; + return true; + } + + +} diff --git a/mediastreamer2 b/mediastreamer2 index c4d5e14dc..28a6e7f22 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit c4d5e14dca123596d1effb68bd7310124bfa1b11 +Subproject commit 28a6e7f22fbdd93a01676fc9cc47a2605c846d75