mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-03 20:46:28 +00:00
Merge branch 'master' of git.linphone.org:linphone
This commit is contained in:
commit
3483b5de70
16 changed files with 402 additions and 88 deletions
8
NEWS
8
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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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; /**<Whether video shall be automatically proposed for outgoing calls.*/
|
||||
bool_t automatically_accept; /**<Whether video shall be automatically accepted for incoming calls*/
|
||||
bool_t unused[2];
|
||||
};
|
||||
|
||||
typedef struct _LinphoneVideoPolicy LinphoneVideoPolicy;
|
||||
|
||||
/**
|
||||
* The LinphoneCall object represents a call issued or received by the LinphoneCore
|
||||
**/
|
||||
|
|
@ -259,6 +270,7 @@ bool_t linphone_call_has_transfer_pending(const LinphoneCall *call);
|
|||
LinphoneCall *linphone_call_get_replaced_call(LinphoneCall *call);
|
||||
int linphone_call_get_duration(const LinphoneCall *call);
|
||||
const LinphoneCallParams * linphone_call_get_current_params(const LinphoneCall *call);
|
||||
const LinphoneCallParams * linphone_call_get_remote_params(LinphoneCall *call);
|
||||
void linphone_call_enable_camera(LinphoneCall *lc, bool_t enabled);
|
||||
bool_t linphone_call_camera_enabled(const LinphoneCall *lc);
|
||||
int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file);
|
||||
|
|
@ -690,6 +702,8 @@ LinphoneCall *linphone_core_get_current_call(const LinphoneCore *lc);
|
|||
|
||||
int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call);
|
||||
|
||||
int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
|
||||
|
||||
int linphone_core_terminate_call(LinphoneCore *lc, LinphoneCall *call);
|
||||
|
||||
int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri);
|
||||
|
|
@ -704,6 +718,10 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call);
|
|||
|
||||
int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
|
||||
|
||||
int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call);
|
||||
|
||||
int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
|
||||
|
||||
LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc);
|
||||
|
||||
LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address);
|
||||
|
|
@ -916,8 +934,11 @@ const MSList * linphone_core_get_call_logs(LinphoneCore *lc);
|
|||
void linphone_core_clear_call_logs(LinphoneCore *lc);
|
||||
|
||||
/* video support */
|
||||
bool_t linphone_core_video_supported(LinphoneCore *lc);
|
||||
void linphone_core_enable_video(LinphoneCore *lc, bool_t vcap_enabled, bool_t display_enabled);
|
||||
bool_t linphone_core_video_enabled(LinphoneCore *lc);
|
||||
void linphone_core_set_video_policy(LinphoneCore *lc, const LinphoneVideoPolicy *policy);
|
||||
const LinphoneVideoPolicy *linphone_core_get_video_policy(LinphoneCore *lc);
|
||||
|
||||
typedef struct MSVideoSizeDef{
|
||||
MSVideoSize vsize;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ struct _LinphoneCall
|
|||
char *refer_to;
|
||||
LinphoneCallParams params;
|
||||
LinphoneCallParams current_params;
|
||||
LinphoneCallParams remote_params;
|
||||
int up_bw; /*upload bandwidth setting at the time the call is started. Used to detect if it changes during a call */
|
||||
int audio_bw; /*upload bandwidth used by audio */
|
||||
bool_t refer_pending;
|
||||
|
|
@ -117,6 +118,7 @@ struct _LinphoneCall
|
|||
bool_t videostream_encrypted;
|
||||
bool_t audiostream_encrypted;
|
||||
bool_t auth_token_verified;
|
||||
bool_t defer_update;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -464,16 +466,20 @@ struct _LinphoneCore
|
|||
MSList *hooks;
|
||||
LinphoneConference conf_ctx;
|
||||
char* zrtp_secrets_cache;
|
||||
LinphoneVideoPolicy video_policy;
|
||||
bool_t use_files;
|
||||
bool_t apply_nat_settings;
|
||||
bool_t initial_subscribes_sent;
|
||||
bool_t bl_refresh;
|
||||
|
||||
bool_t preview_finished;
|
||||
bool_t auto_net_state_mon;
|
||||
bool_t network_reachable;
|
||||
bool_t use_preview_window;
|
||||
int device_rotation;
|
||||
|
||||
bool_t ringstream_autorelease;
|
||||
bool_t pad[3];
|
||||
int device_rotation;
|
||||
int max_calls;
|
||||
LinphoneTunnel *tunnel;
|
||||
};
|
||||
|
|
@ -492,7 +498,7 @@ 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);
|
||||
void update_local_media_description(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription **md);
|
||||
void update_local_media_description(LinphoneCore *lc, LinphoneCall *call);
|
||||
|
||||
void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md);
|
||||
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ int sal_call_notify_ringing(SalOp *h, bool_t early_media);
|
|||
int sal_call_accept(SalOp*h);
|
||||
int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/);
|
||||
int sal_call_update(SalOp *h, const char *subject);
|
||||
SalMediaDescription * sal_call_get_remote_media_description(SalOp *h);
|
||||
SalMediaDescription * sal_call_get_final_media_description(SalOp *h);
|
||||
int sal_call_refer(SalOp *h, const char *refer_to);
|
||||
int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h);
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@ static void set_sdp_from_desc(osip_message_t *sip, const SalMediaDescription *de
|
|||
}
|
||||
|
||||
static void sdp_process(SalOp *h){
|
||||
ms_message("Doing SDP offer/answer process");
|
||||
ms_message("Doing SDP offer/answer process of type %s",h->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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
74
gtk/main.c
74
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
|
||||
|
||||
|
|
|
|||
16
gtk/main.ui
16
gtk/main.ui
|
|
@ -293,7 +293,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<property name="layout_style">center</property>
|
||||
<property name="layout_style">spread</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="hold_call">
|
||||
<property name="label" translatable="yes">Pause</property>
|
||||
|
|
@ -308,6 +308,20 @@
|
|||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="video_button">
|
||||
<property name="label" translatable="yes">Video</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 4cee83249c8cfb1357c0dd3691a90c6ea9284ac2
|
||||
Subproject commit 7114083338b947014bc0a5e111c96018bb1272c2
|
||||
2
oRTP
2
oRTP
|
|
@ -1 +1 @@
|
|||
Subproject commit 81bc939428560a344999dbe13a30f3ed1b18837d
|
||||
Subproject commit c8cf8b73bd054978036837f4c7b90a78166bfe0e
|
||||
Loading…
Add table
Reference in a new issue