diff --git a/NEWS b/NEWS index a4b134ddf..10dc278dc 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,10 @@ -linphone-3.5.1 -- ?? +linphone-3.5.2 -- February 22, 2012 + * updated oRTP to 0.20.0 + * updated mediastreamer2 to 2.8.2 + * added ZRTP media encryption + * added SILK audio codec + +linphone-3.5.1 -- February 17, 2012 * gtk - implement friend search by typing into the friendlist, and friend sorting linphone-3.5.0 -- December 22, 2011 diff --git a/configure.ac b/configure.ac index 13f386bbc..67060be12 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.5.1],[linphone-developers@nongnu.org]) +AC_INIT([linphone],[3.5.2],[linphone-developers@nongnu.org]) AC_CANONICAL_SYSTEM AC_CONFIG_SRCDIR([coreapi/linphonecore.c]) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index a2dfffee3..d9296b367 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -404,50 +404,62 @@ static void call_ack(SalOp *op){ } } +static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){ + SalMediaDescription *md; + sal_call_accept(call->op); + md=sal_call_get_final_media_description(call->op); + if (md && !sal_media_description_empty(md)) + linphone_core_update_streams(lc,call,md); +} + +static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ + call_accept_update(lc,call); + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We have been resumed.")); + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); +} + +static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ + call_accept_update(lc,call); + /* we are being paused */ + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("We are paused by other party.")); + linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote"); +} + +static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){ + if(lc->vtable.display_status) + lc->vtable.display_status(lc,_("Call is updated by remote.")); + call->defer_update=FALSE; + linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); + if (call->defer_update==FALSE){ + linphone_core_accept_call_update(lc,call,NULL); + } +} /* this callback is called when an incoming re-INVITE modifies the session*/ static void call_updating(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); - LinphoneCallState prevstate=LinphoneCallIdle; - SalMediaDescription *md; - SalMediaDescription *old_md=call->resultdesc; + SalMediaDescription *rmd=sal_call_get_remote_media_description(op); - sal_media_description_ref(old_md); - - md=sal_call_get_final_media_description(op); - - /*accept the modification (sends a 200Ok)*/ - sal_call_accept(op); - - if (md && !sal_media_description_empty(md)) - { - linphone_core_update_streams (lc,call,md); - - if (sal_media_description_has_dir(call->localdesc,SalStreamSendRecv)){ - ms_message("Our local status is SalStreamSendRecv"); - if (sal_media_description_has_dir (md,SalStreamRecvOnly) || sal_media_description_has_dir(md,SalStreamInactive)){ - /* we are being paused */ - if(lc->vtable.display_status) - lc->vtable.display_status(lc,_("We are being paused...")); - linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote"); - }else if (!sal_media_description_has_dir(old_md,SalStreamSendRecv) && sal_media_description_has_dir(md,SalStreamSendRecv)){ - if(lc->vtable.display_status) - lc->vtable.display_status(lc,_("We have been resumed...")); - linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)"); - }else{ - prevstate=call->state; - if(lc->vtable.display_status) - lc->vtable.display_status(lc,_("Call has been updated by remote...")); - linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); + switch(call->state){ + case LinphoneCallPausedByRemote: + if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){ + call_resumed(lc,call); } - } - - if (prevstate!=LinphoneCallIdle){ - linphone_call_set_state (call,prevstate,"Connected (streams running)"); - } + break; + case LinphoneCallStreamsRunning: + case LinphoneCallConnected: + if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){ + call_paused_by_remote(lc,call); + }else{ + call_updated_by_remote(lc,call); + } + break; + default: + call_accept_update(lc,call); } - sal_media_description_unref(old_md); } static void call_terminated(SalOp *op, const char *from){ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 31c9cbfcb..a384a9299 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -246,14 +246,13 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li return md; } -void update_local_media_description(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription **md){ - if (*md == NULL) { - *md = _create_local_media_description(lc,call,0,0); +void update_local_media_description(LinphoneCore *lc, LinphoneCall *call){ + SalMediaDescription *md=call->localdesc; + if (md== NULL) { + call->localdesc = create_local_media_description(lc,call); } else { - unsigned int id = (*md)->session_id; - unsigned int ver = (*md)->session_ver+1; - sal_media_description_unref(*md); - *md = _create_local_media_description(lc,call,id,ver); + call->localdesc = _create_local_media_description(lc,call,md->session_id,md->session_ver+1); + sal_media_description_unref(md); } } @@ -360,6 +359,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); linphone_core_init_default_params(lc, &call->params); + call->params.has_video &= !!lc->video_policy.automatically_accept; call->localdesc=create_local_media_description (lc,call); call->camera_active=call->params.has_video; if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) @@ -402,6 +402,11 @@ static void linphone_call_set_terminated(LinphoneCall *call){ } } +void linphone_call_fix_call_parameters(LinphoneCall *call){ + call->params.has_video=call->current_params.has_video; + call->params.media_encryption=call->current_params.media_encryption; +} + const char *linphone_call_state_to_string(LinphoneCallState cs){ switch (cs){ case LinphoneCallIdle: @@ -553,6 +558,41 @@ const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall * return &call->current_params; } +static bool_t is_video_active(const SalStreamDescription *sd){ + return sd->port!=0 && sd->dir!=SalStreamInactive; +} + +/** + * Returns call parameters proposed by remote. + * + * This is useful when receiving an incoming call, to know whether the remote party + * supports video, encryption or whatever. +**/ +const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call){ + LinphoneCallParams *cp=&call->remote_params; + memset(cp,0,sizeof(*cp)); + if (call->op){ + SalMediaDescription *md=sal_call_get_remote_media_description(call->op); + if (md){ + SalStreamDescription *asd,*vsd,*secure_asd,*secure_vsd; + + asd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalAudio); + vsd=sal_media_description_find_stream(md,SalProtoRtpAvp,SalVideo); + secure_asd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalAudio); + secure_vsd=sal_media_description_find_stream(md,SalProtoRtpSavp,SalVideo); + if (secure_vsd){ + cp->has_video=is_video_active(secure_vsd); + if (secure_asd || asd==NULL) + cp->media_encryption=LinphoneMediaEncryptionSRTP; + }else if (vsd){ + cp->has_video=is_video_active(vsd); + } + return cp; + } + } + return NULL; +} + /** * Returns the remote address associated to this call * @@ -1293,11 +1333,12 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut }else if (call->params.media_encryption==LinphoneMediaEncryptionSRTP){ call->current_params.media_encryption=linphone_call_are_all_streams_encrypted(call) ? LinphoneMediaEncryptionSRTP : LinphoneMediaEncryptionNone; - /*also reflect the change if the "wished" params, in order to avoid to propose SAVP again - * further in the call, for example during pause,resume, conferencing reINVITEs*/ - call->params.media_encryption=call->current_params.media_encryption; } + /*also reflect the change if the "wished" params, in order to avoid to propose SAVP or video again + * further in the call, for example during pause,resume, conferencing reINVITEs*/ + linphone_call_fix_call_parameters(call); + goto end; end: ms_free(cname); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 3fddf65fe..dd41305b4 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -756,6 +756,7 @@ static void video_config_read(LinphoneCore *lc){ const char **devices; const MSList *elem; int i; + LinphoneVideoPolicy vpol; /* retrieve all video devices */ elem=ms_web_cam_manager_get_list(ms_web_cam_manager_get()); @@ -778,12 +779,15 @@ static void video_config_read(LinphoneCore *lc){ capture=lp_config_get_int(lc->config,"video","capture",1); display=lp_config_get_int(lc->config,"video","display",1); self_view=lp_config_get_int(lc->config,"video","self_view",1); + vpol.automatically_initiate=lp_config_get_int(lc->config,"video","automatically_initiate",1); + vpol.automatically_accept=lp_config_get_int(lc->config,"video","automatically_accept",1); lc->video_conf.displaytype=lp_config_get_string(lc->config,"video","displaytype",NULL); if(lc->video_conf.displaytype) ms_message("we are using a specific display:%s\n",lc->video_conf.displaytype); linphone_core_enable_video(lc,capture,display); linphone_core_enable_self_view(lc,self_view); + linphone_core_set_video_policy(lc,&vpol); #endif } @@ -1930,6 +1934,7 @@ const char * linphone_core_get_route(LinphoneCore *lc){ void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ if (call->refer_pending){ LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + cp->has_video &= !!lc->video_policy.automatically_initiate; cp->referer=call; ms_message("Starting new call to refered address %s",call->refer_to); call->refer_pending=FALSE; @@ -2082,6 +2087,7 @@ int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphonePro LinphoneCall * linphone_core_invite(LinphoneCore *lc, const char *url){ LinphoneCall *call; LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc); + p->has_video &= !!lc->video_policy.automatically_initiate; call=linphone_core_invite_with_params(lc,url,p); linphone_call_params_destroy(p); return call; @@ -2128,7 +2134,8 @@ LinphoneCall * linphone_core_invite_with_params(LinphoneCore *lc, const char *ur **/ LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr){ LinphoneCall *call; - LinphoneCallParams *p=linphone_core_create_default_call_parameters (lc); + LinphoneCallParams *p=linphone_core_create_default_call_parameters(lc); + p->has_video &= !!lc->video_policy.automatically_initiate; call=linphone_core_invite_address_with_params (lc,addr,p); linphone_call_params_destroy(p); return call; @@ -2285,9 +2292,8 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho if (params!=NULL){ const char *subject; call->params=*params; - update_local_media_description(lc,call,&call->localdesc); - call->camera_active=params->has_video; - + update_local_media_description(lc,call); + if (params->in_conference){ subject="Conference"; }else{ @@ -2311,6 +2317,50 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho return err; } +/** + * When receiving a LinphoneCallUpdatedByRemote state notification, prevent LinphoneCore from performing an automatic answer. + * + * When receiving a LinphoneCallUpdatedByRemote state notification (ie an incoming reINVITE), the default behaviour of + * LinphoneCore is to automatically answer the reINIVTE with call parameters unchanged. + * However when for example when the remote party updated the call to propose a video stream, it can be useful + * to prompt the user before answering. This can be achieved by calling linphone_core_defer_call_update() during + * the call state notifiacation, to deactivate the automatic answer that would just confirm the audio but reject the video. + * Then, when the user responds to dialog prompt, it becomes possible to call linphone_core_accept_call_update() to answer + * the reINVITE, with eventually video enabled in the LinphoneCallParams argument. + * + * @Returns 0 if successful, -1 if the linphone_core_defer_call_update() was done outside a LinphoneCallUpdatedByRemote notification, which is illegal. +**/ +int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){ + if (call->state==LinphoneCallUpdatedByRemote){ + call->defer_update=TRUE; + return 0; + } + ms_error("linphone_core_defer_call_update() not done in state LinphoneCallUpdatedByRemote"); + return -1; +} + +/** + * +**/ +int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ + SalMediaDescription *md; + if (call->state!=LinphoneCallUpdatedByRemote){ + ms_error("linphone_core_accept_update(): invalid state %s to call this function.", + linphone_call_state_to_string(call->state)); + return -1; + } + if (params){ + call->params=*params; + update_local_media_description(lc,call); + sal_call_set_local_media_description(call->op,call->localdesc); + } + sal_call_accept(call->op); + md=sal_call_get_final_media_description(call->op); + if (md && !sal_media_description_empty(md)) + linphone_core_update_streams (lc,call,md); + linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); + return 0; +} /** * Accept an incoming call. @@ -2325,7 +2375,25 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho * @param call the LinphoneCall object representing the call to be answered. * **/ -int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) +int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){ + return linphone_core_accept_call_with_params(lc,call,NULL); +} + +/** + * Accept an incoming call, with parameters. + * + * @ingroup call_control + * Basically the application is notified of incoming calls within the + * call_state_changed callback of the #LinphoneCoreVTable structure, where it will receive + * a LinphoneCallIncoming event with the associated LinphoneCall object. + * The application can later accept the call using + * this method. + * @param lc the LinphoneCore object + * @param call the LinphoneCall object representing the call to be answered. + * @param params the specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters. + * +**/ +int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) { LinphoneProxyConfig *cfg=NULL,*dest_proxy=NULL; const char *contact=NULL; @@ -2388,6 +2456,12 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) if (call->audiostream==NULL) linphone_call_init_media_streams(call); + if (params){ + call->params=*params; + update_local_media_description(lc,call); + sal_call_set_local_media_description(call->op,call->localdesc); + } + sal_call_accept(call->op); if (lc->vtable.display_status!=NULL) lc->vtable.display_status(lc,_("Connected.")); @@ -2540,7 +2614,7 @@ int linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) ms_warning("Cannot pause this call, it is not active."); return -1; } - update_local_media_description(lc,call,&call->localdesc); + update_local_media_description(lc,call); if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){ sal_media_description_set_dir(call->localdesc,SalStreamSendOnly); subject="Call on hold"; @@ -2613,11 +2687,11 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *the_call) ms_message("Resuming call %p",call); } - // Stop playing music immediately. If remote side is a conference it - // prevents the participants to hear it while the 200OK comes back. - audio_stream_play(call->audiostream, NULL); + /* Stop playing music immediately. If remote side is a conference it + prevents the participants to hear it while the 200OK comes back.*/ + if (call->audiostream) audio_stream_play(call->audiostream, NULL); - update_local_media_description(lc,the_call,&call->localdesc); + update_local_media_description(lc,the_call); sal_call_set_local_media_description(call->op,call->localdesc); sal_media_description_set_dir(call->localdesc,SalStreamSendRecv); if (call->params.in_conference && !call->current_params.in_conference) subject="Conference"; @@ -3354,6 +3428,14 @@ void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t di linphone_core_get_upload_bandwidth(lc)); } +bool_t linphone_core_video_supported(LinphoneCore *lc){ +#ifdef VIDEO_ENABLED + return TRUE; +#else + return FALSE; +#endif +} + /** * Returns TRUE if video is enabled, FALSE otherwise. * @ingroup media_parameters @@ -3362,6 +3444,28 @@ bool_t linphone_core_video_enabled(LinphoneCore *lc){ return (lc->video_conf.display || lc->video_conf.capture); } +/** + * Sets the default policy for video. + * This policy defines whether: + * - video shall be initiated by default for outgoing calls + * - video shall be accepter by default for incoming calls +**/ +void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy){ + lc->video_policy=*policy; + if (linphone_core_ready(lc)){ + lp_config_set_int(lc->config,"video","automatically_initiate",policy->automatically_initiate); + lp_config_set_int(lc->config,"video","automatically_accept",policy->automatically_accept); + } +} + +/** + * Get the default policy for video. + * See linphone_core_set_video_policy() for more details. +**/ +const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc){ + return &lc->video_policy; +} + /** * Controls video preview enablement. * diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 83edb4975..e2c46bf7b 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -209,6 +209,17 @@ typedef enum _LinphoneReason LinphoneReason; const char *linphone_reason_to_string(LinphoneReason err); +/** + * Structure describing policy regarding video streams establishments. +**/ +struct _LinphoneVideoPolicy{ + bool_t automatically_initiate; /**sdp_offering ? "outgoing" : "incoming"); if (h->result){ sal_media_description_unref(h->result); } @@ -517,6 +517,9 @@ static void sdp_process(SalOp *h){ offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result); }else{ int i; + if (h->sdp_answer){ + sdp_message_free(h->sdp_answer); + } 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); /*once we have generated the SDP answer, we modify the result description for processing by the upper layer. @@ -550,6 +553,15 @@ int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc){ if (h->base.local_media) sal_media_description_unref(h->base.local_media); h->base.local_media=desc; + if (h->base.remote_media){ + /*case of an incoming call where we modify the local capabilities between the time + * the call is ringing and it is accepted (for example if you want to accept without video*/ + /*reset the sdp answer so that it is computed again*/ + if (h->sdp_answer){ + sdp_message_free(h->sdp_answer); + h->sdp_answer=NULL; + } + } return 0; } @@ -606,18 +618,19 @@ int sal_call_notify_ringing(SalOp *h, bool_t early_media){ osip_message_t *msg; /*if early media send also 180 and 183 */ - if (early_media && h->sdp_answer){ + if (early_media){ msg=NULL; eXosip_lock(); eXosip_call_build_answer(h->tid,180,&msg); - if (msg){ - set_sdp(msg,h->sdp_answer); - eXosip_call_send_answer(h->tid,180,msg); - } - msg=NULL; + eXosip_call_build_answer(h->tid,183,&msg); if (msg){ - set_sdp(msg,h->sdp_answer); + sdp_process(h); + if (h->sdp_answer){ + set_sdp(msg,h->sdp_answer); + sdp_message_free(h->sdp_answer); + h->sdp_answer=NULL; + } eXosip_call_send_answer(h->tid,183,msg); } eXosip_unlock(); @@ -652,6 +665,7 @@ int sal_call_accept(SalOp * h){ if (h->sdp_offering) { set_sdp_from_desc(msg,h->base.local_media); }else{ + if (h->sdp_answer==NULL) sdp_process(h); if (h->sdp_answer){ set_sdp(msg,h->sdp_answer); sdp_message_free(h->sdp_answer); @@ -697,6 +711,10 @@ int sal_call_decline(SalOp *h, SalReason reason, const char *redirect){ return 0; } +SalMediaDescription * sal_call_get_remote_media_description(SalOp *h){ + return h->base.remote_media; +} + SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ if (h->base.local_media && h->base.remote_media && !h->result){ sdp_process(h); @@ -1032,15 +1050,19 @@ static void handle_ack(Sal *sal, eXosip_event_t *ev){ ms_warning("ack for non-existing call !"); return; } - sdp=eXosip_get_sdp_info(ev->ack); - if (sdp){ - op->base.remote_media=sal_media_description_new(); - sdp_to_media_description(sdp,op->base.remote_media); - sdp_process(op); - sdp_message_free(sdp); + + if (op->sdp_offering){ + sdp=eXosip_get_sdp_info(ev->ack); + if (sdp){ + if (op->base.remote_media) + sal_media_description_unref(op->base.remote_media); + op->base.remote_media=sal_media_description_new(); + sdp_to_media_description(sdp,op->base.remote_media); + sdp_process(op); + sdp_message_free(sdp); + } } if (op->reinvite){ - if (sdp) sal->callbacks.call_updating(op); op->reinvite=FALSE; }else{ sal->callbacks.call_ack(op); diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index f2577ee00..75dd7e58c 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -65,13 +65,13 @@ struct SalOp{ to retrieve the operation when receiving a response*/ char *replaces; char *referred_by; + const SalAuthInfo *auth_info; bool_t supports_session_timers; bool_t sdp_offering; bool_t reinvite; bool_t masquerade_via; bool_t auto_answer_asked; bool_t terminated; - const SalAuthInfo *auth_info; }; void sal_remove_out_subscribe(Sal *sal, SalOp *op); diff --git a/gtk/incall_view.c b/gtk/incall_view.c index 5674d5544..6ac443e98 100644 --- a/gtk/incall_view.c +++ b/gtk/incall_view.c @@ -219,6 +219,36 @@ void linphone_gtk_create_in_call_view(LinphoneCall *call){ GTK_BUTTON(linphone_gtk_get_widget(call_view,"incall_mute")),FALSE); } +static void video_button_clicked(GtkWidget *button, LinphoneCall *call){ + gboolean adding=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"adding_video")); + LinphoneCore *lc=linphone_call_get_core(call); + LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call)); + gtk_widget_set_sensitive(button,FALSE); + linphone_call_params_enable_video(params,adding); + linphone_core_update_call(lc,call,params); + linphone_call_params_destroy(params); +} + +void linphone_gtk_update_video_button(LinphoneCall *call){ + GtkWidget *call_view=(GtkWidget*)linphone_call_get_user_pointer(call); + GtkWidget *button=linphone_gtk_get_widget(call_view,"video_button"); + const LinphoneCallParams *params=linphone_call_get_current_params(call); + gboolean has_video=linphone_call_params_video_enabled(params); + + gtk_button_set_image(GTK_BUTTON(button), + gtk_image_new_from_stock(has_video ? GTK_STOCK_REMOVE : GTK_STOCK_ADD,GTK_ICON_SIZE_BUTTON)); + g_object_set_data(G_OBJECT(button),"adding_video",GINT_TO_POINTER(!has_video)); + if (!linphone_core_video_supported(linphone_call_get_core(call))){ + gtk_widget_set_sensitive(button,FALSE); + return; + } + if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),"signal_connected"))==0){ + g_signal_connect(G_OBJECT(button),"clicked",(GCallback)video_button_clicked,call); + g_object_set_data(G_OBJECT(button),"signal_connected",GINT_TO_POINTER(1)); + } + gtk_widget_set_sensitive(button,linphone_call_get_state(call)==LinphoneCallStreamsRunning); +} + void linphone_gtk_remove_in_call_view(LinphoneCall *call){ GtkWidget *w=(GtkWidget*)linphone_call_get_user_pointer (call); GtkWidget *main_window=linphone_gtk_get_main_window (); diff --git a/gtk/linphone.h b/gtk/linphone.h index 45c8b54aa..03410b1a5 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -114,6 +114,7 @@ void linphone_gtk_set_in_conference(LinphoneCall *call); void linphone_gtk_unset_from_conference(LinphoneCall *call); void linphone_gtk_terminate_conference_participant(LinphoneCall *call); void linphone_gtk_in_call_view_show_encryption(LinphoneCall *call); +void linphone_gtk_update_video_button(LinphoneCall *call); typedef float (*get_volume_t)(void *data); void linphone_gtk_init_audio_meter(GtkWidget *w, get_volume_t get_volume, void *data); diff --git a/gtk/main.c b/gtk/main.c index 76ead5053..0262c5e27 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -231,6 +231,7 @@ static void linphone_gtk_init_liblinphone(const char *config_file, linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); g_free(secrets_file); + linphone_core_enable_video(the_core,TRUE,TRUE); } @@ -694,6 +695,7 @@ static void linphone_gtk_update_call_buttons(LinphoneCall *call){ linphone_gtk_enable_transfer_button(lc,call_list_size>1); linphone_gtk_enable_conference_button(lc,call_list_size>1); update_video_title(); + if (call) linphone_gtk_update_video_button(call); } static gboolean linphone_gtk_start_call_do(GtkWidget *uri_bar){ @@ -766,7 +768,11 @@ void linphone_gtk_answer_clicked(GtkWidget *button){ void linphone_gtk_enable_video(GtkWidget *w){ gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); GtkWidget *selfview_item=linphone_gtk_get_widget(linphone_gtk_get_main_window(),"selfview_item"); - linphone_core_enable_video(linphone_gtk_get_core(),val,val); + LinphoneVideoPolicy policy={0}; + policy.automatically_initiate=policy.automatically_accept=val; + linphone_core_enable_video(linphone_gtk_get_core(),TRUE,TRUE); + linphone_core_set_video_policy(linphone_gtk_get_core(),&policy); + gtk_widget_set_sensitive(selfview_item,val); if (val){ linphone_core_enable_video_preview(linphone_gtk_get_core(), @@ -1022,6 +1028,52 @@ static void linphone_gtk_notify(LinphoneCall *call, const char *msg){ } } +static void on_call_updated_response(GtkWidget *dialog, gint responseid, LinphoneCall *call){ + if (linphone_call_get_state(call)==LinphoneCallUpdatedByRemote){ + LinphoneCore *lc=linphone_call_get_core(call); + LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call)); + linphone_call_params_enable_video(params,responseid==GTK_RESPONSE_YES); + linphone_core_accept_call_update(lc,call,params); + linphone_call_params_destroy(params); + } + linphone_call_unref(call); + g_source_remove_by_user_data(dialog); + gtk_widget_destroy(dialog); +} + +static void on_call_updated_timeout(GtkWidget *dialog){ + gtk_widget_destroy(dialog); +} + +static void linphone_gtk_call_updated_by_remote(LinphoneCall *call){ + LinphoneCore *lc=linphone_call_get_core(call); + const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(lc); + const LinphoneCallParams *rparams=linphone_call_get_remote_params(call); + const LinphoneCallParams *current_params=linphone_call_get_current_params(call); + gboolean video_requested=linphone_call_params_video_enabled(rparams); + gboolean video_used=linphone_call_params_video_enabled(current_params); + g_message("Video used=%i, video requested=%i, automatically_accept=%i", + video_used,video_requested,pol->automatically_accept); + if (video_used==FALSE && video_requested && !pol->automatically_accept){ + linphone_core_defer_call_update(lc,call); + { + const LinphoneAddress *addr=linphone_call_get_remote_address(call); + GtkWidget *dialog; + const char *dname=linphone_address_get_display_name(addr); + if (dname==NULL) dname=linphone_address_get_username(addr); + if (dname==NULL) dname=linphone_address_get_domain(addr); + dialog=gtk_message_dialog_new(GTK_WINDOW(linphone_gtk_get_main_window()), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_YES_NO, + _("%s proposed to start video. Do you accept ?"),dname); + g_signal_connect(G_OBJECT(dialog),"response",(GCallback)on_call_updated_response,linphone_call_ref(call)); + g_timeout_add(20000,(GSourceFunc)on_call_updated_timeout,dialog); + gtk_widget_show(dialog); + } + } +} + static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cs, const char *msg){ switch(cs){ case LinphoneCallOutgoingInit: @@ -1033,6 +1085,9 @@ static void linphone_gtk_call_state_changed(LinphoneCore *lc, LinphoneCall *call case LinphoneCallStreamsRunning: linphone_gtk_in_call_view_set_in_call(call); break; + case LinphoneCallUpdatedByRemote: + linphone_gtk_call_updated_by_remote(call); + break; case LinphoneCallError: linphone_gtk_in_call_view_terminate (call,msg); break; @@ -1265,7 +1320,7 @@ static void linphone_gtk_status_icon_set_blinking(gboolean val){ void linphone_gtk_options_activate(GtkWidget *item){ #ifndef HAVE_GTK_OSX gtk_widget_set_visible(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"quit_item"), - icon && !gtk_status_icon_is_embedded(icon)); + TRUE); #endif } @@ -1345,7 +1400,8 @@ static void linphone_gtk_connect_digits(void){ } static void linphone_gtk_check_menu_items(void){ - bool_t video_enabled=linphone_core_video_enabled(linphone_gtk_get_core()); + const LinphoneVideoPolicy *pol=linphone_core_get_video_policy(linphone_gtk_get_core()); + bool_t video_enabled=pol->automatically_accept && pol->automatically_initiate; bool_t selfview=linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT); GtkWidget *selfview_item=linphone_gtk_get_widget( linphone_gtk_get_main_window(),"selfview_item"); @@ -1491,13 +1547,13 @@ gboolean linphone_gtk_close(GtkWidget *mw){ #ifdef HAVE_GTK_OSX static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){ - if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){ - linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE); - }else{ - linphone_core_enable_video_preview(linphone_gtk_get_core(), + if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){ + linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE); + }else{ + linphone_core_enable_video_preview(linphone_gtk_get_core(), linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && linphone_core_video_enabled(linphone_gtk_get_core())); - } - return FALSE; + } + return FALSE; } #endif diff --git a/gtk/main.ui b/gtk/main.ui index d4c061a45..96647fe4d 100644 --- a/gtk/main.ui +++ b/gtk/main.ui @@ -293,7 +293,7 @@ True False True - center + spread Pause @@ -308,6 +308,20 @@ 0 + + + Video + True + True + True + False + + + False + True + 1 + + False diff --git a/mediastreamer2 b/mediastreamer2 index 4cee83249..711408333 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4cee83249c8cfb1357c0dd3691a90c6ea9284ac2 +Subproject commit 7114083338b947014bc0a5e111c96018bb1272c2 diff --git a/oRTP b/oRTP index 81bc93942..c8cf8b73b 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 81bc939428560a344999dbe13a30f3ed1b18837d +Subproject commit c8cf8b73bd054978036837f4c7b90a78166bfe0e