Merge branch 'dev_conference_rfc4579'

This commit is contained in:
François Grisez 2016-01-07 10:13:04 +01:00
commit 38dfb9ab6f
34 changed files with 1670 additions and 554 deletions

View file

@ -47,7 +47,7 @@ LOCAL_SRC_FILES := \
call_params.c \
chat.c \
chat_file_transfer.c \
conference.c \
conference.cc \
content.c \
ec-calibrator.c \
enum.c \

View file

@ -2452,7 +2452,7 @@ static void lpc_display_call_states(LinphoneCore *lc){
const char *flag;
bool_t in_conference;
call=(LinphoneCall*)elem->data;
in_conference=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
in_conference=(linphone_call_get_conference(call) != NULL);
tmp=linphone_call_get_remote_address_as_string (call);
flag=in_conference ? "conferencing" : "";
flag=linphone_call_has_transfer_pending(call) ? "transfer pending" : flag;

View file

@ -75,7 +75,6 @@ set(LINPHONE_SOURCE_FILES_C
call_params.c
chat.c
chat_file_transfer.c
conference.c
contactprovider.c
content.c
dict.c
@ -115,7 +114,7 @@ set(LINPHONE_SOURCE_FILES_C
xmlrpc.c
vtables.c
)
set(LINPHONE_SOURCE_FILES_CXX )
set(LINPHONE_SOURCE_FILES_CXX conference.cc)
set(LINPHONE_SOURCE_FILES_OBJC)
if (IOS)

View file

@ -56,7 +56,7 @@ liblinphone_la_SOURCES=\
call_params.c \
chat.c \
chat_file_transfer.c \
conference.c \
conference.cc \
contactprovider.c contactprovider.h contact_providers_priv.h \
content.c \
dict.c \

View file

@ -129,6 +129,20 @@ LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri)
return (LinphoneTransportType)sal_address_get_transport(uri);
}
/**
* Set the value of the method parameter
**/
void linphone_address_set_method_param(LinphoneAddress *addr, const char *method) {
sal_address_set_method_param(addr, method);
}
/**
* Get the value of the method parameter
**/
const char *linphone_address_get_method_param(const LinphoneAddress *addr) {
return sal_address_get_method_param(addr);
}
/**
* Removes address's tags and uri headers so that it is displayable to the user.
**/

View file

@ -114,6 +114,15 @@ const char* sal_address_get_transport_name(const SalAddress* addr){
return NULL;
}
const char *sal_address_get_method_param(const SalAddress *addr) {
belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr);
if (uri) {
return belle_sip_uri_get_method_param(uri);
}
return NULL;
}
void sal_address_set_display_name(SalAddress *addr, const char *display_name){
belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr);
belle_sip_header_address_set_displayname(header_addr,display_name);
@ -213,6 +222,10 @@ void sal_address_set_transport_name(SalAddress* addr,const char *transport){
SAL_ADDRESS_SET(addr,transport_param,transport);
}
void sal_address_set_method_param(SalAddress *addr, const char *method) {
SAL_ADDRESS_SET(addr, method_param, method);
}
SalAddress *sal_address_ref(SalAddress *addr){
return (SalAddress*)belle_sip_object_ref(BELLE_SIP_HEADER_ADDRESS(addr));
}

View file

@ -40,7 +40,7 @@ static void sal_op_set_referred_by(SalOp* op,belle_sip_header_referred_by_t* ref
int sal_call_refer_to(SalOp *op, belle_sip_header_refer_to_t* refer_to, belle_sip_header_referred_by_t* referred_by){
char* tmp;
belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"REFER"):NULL; /*cannot create request if dialog not set yet*/
belle_sip_request_t* req=op->dialog?belle_sip_dialog_create_request(op->dialog,"REFER"):sal_op_build_request(op, "REFER");
if (!req) {
tmp=belle_sip_uri_to_string(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(refer_to)));
ms_error("Cannot refer to [%s] for op [%p]",tmp,op);

View file

@ -125,6 +125,10 @@ LINPHONE_PUBLIC const char *linphone_call_params_get_custom_header(const Linphon
/**
* Tell whether the call is part of the locally managed conference.
* @warning If a conference server is used to manage conferences,
* that function does not return TRUE even if the conference is running.<br/>
* If you want to test whether the conference is running, you should test
* whether linphone_core_get_conference() return a non-null pointer.
* @param[in] cp LinphoneCallParams object
* @return A boolean value telling whether the call is part of the locally managed conference.
**/

View file

@ -1065,7 +1065,15 @@ static void dtmf_received(SalOp *op, char dtmf){
static void refer_received(Sal *sal, SalOp *op, const char *referto){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
if (call){
LinphoneAddress *refer_to_addr = linphone_address_new(referto);
char method[20] = "";
if(refer_to_addr) {
const char *tmp = linphone_address_get_method_param(refer_to_addr);
if(tmp) strncpy(method, tmp, sizeof(method));
linphone_address_destroy(refer_to_addr);
}
if (call && (strlen(method) == 0 || strcmp(method, "INVITE") == 0)) {
if (call->refer_to!=NULL){
ms_free(call->refer_to);
}

View file

@ -1,430 +0,0 @@
/***************************************************************************
* conference.c
*
* Mon Sep 12, 2011
* Copyright 2011 Belledonne Communications
* Author: Simon Morlat
* Email simon dot morlat at linphone dot org
****************************************************************************/
/*
* 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.
*/
#include "private.h"
#include "lpconfig.h"
#include "mediastreamer2/msvolume.h"
/**
* @addtogroup conferencing
* @{
**/
static int convert_conference_to_call(LinphoneCore *lc);
static void conference_check_init(LinphoneConference *ctx, int samplerate){
if (ctx->conf==NULL){
MSAudioConferenceParams params;
params.samplerate=samplerate;
ctx->conf=ms_audio_conference_new(&params);
ctx->terminated=FALSE;
}
}
static void remove_local_endpoint(LinphoneConference *ctx){
if (ctx->local_endpoint){
ms_audio_conference_remove_member(ctx->conf,ctx->local_endpoint);
ms_audio_endpoint_release_from_stream(ctx->local_endpoint);
ctx->local_endpoint=NULL;
audio_stream_stop(ctx->local_participant);
ctx->local_participant=NULL;
rtp_profile_destroy(ctx->local_dummy_profile);
}
}
static int linphone_conference_get_size(LinphoneConference *conf){
if (conf->conf == NULL) {
return 0;
}
return ms_audio_conference_get_size(conf->conf) - (conf->record_endpoint ? 1 : 0);
}
static int remote_participants_count(LinphoneConference *ctx) {
int count=linphone_conference_get_size(ctx);
if (count==0) return 0;
if (!ctx->local_participant) return count;
return count -1;
}
void linphone_core_conference_check_uninit(LinphoneCore *lc){
LinphoneConference *ctx=&lc->conf_ctx;
if (ctx->conf){
int remote_count=remote_participants_count(ctx);
ms_message("conference_check_uninit(): size=%i",linphone_conference_get_size(ctx));
if (remote_count==1 && !ctx->terminated){
convert_conference_to_call(lc);
}
if (remote_count==0){
if (ctx->local_participant!=NULL)
remove_local_endpoint(ctx);
if (ctx->record_endpoint){
ms_audio_conference_remove_member(ctx->conf,ctx->record_endpoint);
ms_audio_endpoint_destroy(ctx->record_endpoint);
ctx->record_endpoint=NULL;
}
}
if (ms_audio_conference_get_size(ctx->conf)==0){
ms_audio_conference_destroy(ctx->conf);
ctx->conf=NULL;
}
}
}
void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted){
LinphoneCore *lc=call->core;
LinphoneConference *conf=&lc->conf_ctx;
MSAudioEndpoint *ep;
call->params->has_video = FALSE;
call->camera_enabled = FALSE;
ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
ms_audio_conference_add_member(conf->conf,ep);
ms_audio_conference_mute_member(conf->conf,ep,muted);
call->endpoint=ep;
}
void linphone_call_remove_from_conf(LinphoneCall *call){
LinphoneCore *lc=call->core;
LinphoneConference *conf=&lc->conf_ctx;
ms_audio_conference_remove_member(conf->conf,call->endpoint);
ms_audio_endpoint_release_from_stream(call->endpoint);
call->endpoint=NULL;
}
static RtpProfile *make_dummy_profile(int samplerate){
RtpProfile *prof=rtp_profile_new("dummy");
PayloadType *pt=payload_type_clone(&payload_type_l16_mono);
pt->clock_rate=samplerate;
rtp_profile_set_payload(prof,0,pt);
return prof;
}
static void add_local_endpoint(LinphoneConference *conf,LinphoneCore *lc){
/*create a dummy audiostream in order to extract the local part of it */
/* network address and ports have no meaning and are not used here. */
AudioStream *st=audio_stream_new(65000,65001,FALSE);
MSSndCard *playcard=lc->sound_conf.lsd_card ?
lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
MSSndCard *captcard=lc->sound_conf.capt_sndcard;
const MSAudioConferenceParams *params=ms_audio_conference_get_params(conf->conf);
conf->local_dummy_profile=make_dummy_profile(params->samplerate);
audio_stream_start_full(st, conf->local_dummy_profile,
"127.0.0.1",
65000,
"127.0.0.1",
65001,
0,
40,
NULL,
NULL,
playcard,
captcard,
linphone_core_echo_cancellation_enabled(lc)
);
_post_configure_audio_stream(st,lc,FALSE);
conf->local_participant=st;
conf->local_endpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
ms_audio_conference_add_member(conf->conf,conf->local_endpoint);
}
/**
* Returns the sound volume (mic input) of the local participant of the conference.
* @param lc the linphone core
* @return the measured input volume expressed in dbm0.
**/
float linphone_core_get_conference_local_input_volume(LinphoneCore *lc){
LinphoneConference *conf=&lc->conf_ctx;
AudioStream *st=conf->local_participant;
if (st && st->volsend && !conf->local_muted){
float vol=0;
ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
return vol;
}
return LINPHONE_VOLUME_DB_LOWEST;
}
/**
* Merge a call into a conference.
* @param lc the linphone core
* @param call an established call, either in LinphoneCallStreamsRunning or LinphoneCallPaused state.
*
* If this is the first call that enters the conference, the virtual conference will be created automatically.
* If the local user was actively part of the call (ie not in paused state), then the local user is automatically entered into the conference.
* If the call was in paused state, then it is automatically resumed when entering into the conference.
*
* @return 0 if successful, -1 otherwise.
**/
int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call){
LinphoneConference *conf=&lc->conf_ctx;
if (call->current_params->in_conference){
ms_error("Already in conference");
return -1;
}
conference_check_init(&lc->conf_ctx, lp_config_get_int(lc->config, "sound","conference_rate",16000));
if (call->state==LinphoneCallPaused){
call->params->in_conference=TRUE;
call->params->has_video=FALSE;
linphone_core_resume_call(lc,call);
}else if (call->state==LinphoneCallStreamsRunning){
LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
params->in_conference=TRUE;
params->has_video=FALSE;
if (call->audiostream || call->videostream){
linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
linphone_call_init_media_streams(call);
}
if (call==lc->current_call){
lc->current_call=NULL;
}
/*this will trigger a reINVITE that will later redraw the streams */
/*FIXME probably a bit too much to just redraw streams !*/
linphone_core_update_call(lc,call,params);
linphone_call_params_destroy(params);
add_local_endpoint(conf,lc);
}else{
ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
return -1;
}
return 0;
}
static int remove_from_conference(LinphoneCore *lc, LinphoneCall *call, bool_t active){
int err=0;
char *str;
if (!call->current_params->in_conference){
if (call->params->in_conference){
ms_warning("Not (yet) in conference, be patient");
return -1;
}else{
ms_error("Not in a conference.");
return -1;
}
}
call->params->in_conference=FALSE;
str=linphone_call_get_remote_address_as_string(call);
ms_message("%s will be removed from conference", str);
ms_free(str);
if (active){
LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
params->in_conference=FALSE;
// reconnect local audio with this call
if (linphone_core_is_in_conference(lc)){
ms_message("Leaving conference for reconnecting with unique call.");
linphone_core_leave_conference(lc);
}
ms_message("Updating call to actually remove from conference");
err=linphone_core_update_call(lc,call,params);
linphone_call_params_destroy(params);
} else{
ms_message("Pausing call to actually remove from conference");
err=_linphone_core_pause_call(lc,call);
}
return err;
}
static int convert_conference_to_call(LinphoneCore *lc){
int err=0;
MSList *calls=lc->calls;
if (remote_participants_count(&lc->conf_ctx)!=1){
ms_error("No unique call remaining in conference.");
return -1;
}
while (calls) {
LinphoneCall *rc=(LinphoneCall*)calls->data;
calls=calls->next;
if (rc->params->in_conference) { // not using current_param
bool_t active_after_removed=linphone_core_is_in_conference(lc);
err=remove_from_conference(lc, rc, active_after_removed);
break;
}
}
return err;
}
int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call){
int err;
char * str=linphone_call_get_remote_address_as_string(call);
ms_message("Removing call %s from the conference", str);
ms_free(str);
err=remove_from_conference(lc,call, FALSE);
if (err){
ms_error("Error removing participant from conference.");
return err;
}
if (remote_participants_count(&lc->conf_ctx)==1){
ms_message("conference size is 1: need to be converted to plain call");
err=convert_conference_to_call(lc);
} else {
ms_message("the conference need not to be converted as size is %i", remote_participants_count(&lc->conf_ctx));
}
return err;
}
bool_t linphone_core_is_in_conference(const LinphoneCore *lc){
return lc->conf_ctx.local_participant!=NULL;
}
/**
* Moves the local participant out of the conference.
* @param lc the linphone core
* When the local participant is out of the conference, the remote participants can continue to talk normally.
* @return 0 if successful, -1 otherwise.
**/
int linphone_core_leave_conference(LinphoneCore *lc){
LinphoneConference *conf=&lc->conf_ctx;
if (linphone_core_is_in_conference(lc))
remove_local_endpoint(conf);
return 0;
}
/**
* Moves the local participant inside the conference.
* @param lc the linphone core
*
* Makes the local participant to join the conference.
* Typically, the local participant is by default always part of the conference when joining an active call into a conference.
* However, by calling linphone_core_leave_conference() and linphone_core_enter_conference() the application can decide to temporarily
* move out and in the local participant from the conference.
*
* @return 0 if successful, -1 otherwise
**/
int linphone_core_enter_conference(LinphoneCore *lc){
LinphoneConference *conf;
if (linphone_core_sound_resources_locked(lc)) {
return -1;
}
if (lc->current_call != NULL) {
_linphone_core_pause_call(lc, lc->current_call);
}
conf=&lc->conf_ctx;
if (conf->local_participant==NULL) add_local_endpoint(conf,lc);
return 0;
}
/**
* Add all calls into a conference.
* @param lc the linphone core
*
* Merge all established calls (either in LinphoneCallStreamsRunning or LinphoneCallPaused) into a conference.
*
* @return 0 if successful, -1 otherwise
**/
int linphone_core_add_all_to_conference(LinphoneCore *lc) {
MSList *calls=lc->calls;
while (calls) {
LinphoneCall *call=(LinphoneCall*)calls->data;
calls=calls->next;
if (!call->current_params->in_conference) {
linphone_core_add_to_conference(lc, call);
}
}
linphone_core_enter_conference(lc);
return 0;
}
/**
* Terminates the conference and the calls associated with it.
* @param lc the linphone core
*
* All the calls that were merged to the conference are terminated, and the conference resources are destroyed.
*
* @return 0 if successful, -1 otherwise
**/
int linphone_core_terminate_conference(LinphoneCore *lc) {
MSList *calls=lc->calls;
LinphoneConference *conf=&lc->conf_ctx;
conf->terminated=TRUE;
while (calls) {
LinphoneCall *call=(LinphoneCall*)calls->data;
calls=calls->next;
if (call->current_params->in_conference) {
linphone_core_terminate_call(lc, call);
}
}
return 0;
}
/**
* Returns the number of participants to the conference, including the local participant.
* @param lc the linphone core
*
* Typically, after merging two calls into the conference, there is total of 3 participants:
* the local participant (or local user), and two remote participants that were the destinations of the two previously establised calls.
*
* @return the number of participants to the conference
**/
int linphone_core_get_conference_size(LinphoneCore *lc) {
LinphoneConference *conf=&lc->conf_ctx;
return linphone_conference_get_size(conf);
}
int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path){
LinphoneConference *conf=&lc->conf_ctx;
if (conf->conf == NULL) {
ms_warning("linphone_core_start_conference_recording(): no conference now.");
return -1;
}
if (conf->record_endpoint==NULL){
conf->record_endpoint=ms_audio_endpoint_new_recorder();
ms_audio_conference_add_member(conf->conf,conf->record_endpoint);
}
ms_audio_recorder_endpoint_start(conf->record_endpoint,path);
return 0;
}
int linphone_core_stop_conference_recording(LinphoneCore *lc){
LinphoneConference *conf=&lc->conf_ctx;
if (conf->conf == NULL) {
ms_warning("linphone_core_stop_conference_recording(): no conference now.");
return -1;
}
if (conf->record_endpoint==NULL){
ms_warning("linphone_core_stop_conference_recording(): no record active.");
return -1;
}
ms_audio_recorder_endpoint_stop(conf->record_endpoint);
return 0;
}
/**
* @}
**/

883
coreapi/conference.cc Normal file
View file

@ -0,0 +1,883 @@
/*******************************************************************************
* conference.cc
*
* Thu Nov 26, 2015
* Copyright 2015 Belledonne Communications
* Author: Linphone's team
* Email info@belledonne-communications.com
******************************************************************************/
/*
* 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.
*/
#include "private.h"
#include "conference.h"
#include <mediastreamer2/msvolume.h>
#include <typeinfo>
#include <list>
#include <algorithm>
namespace Linphone {
class Participant {
public:
Participant(LinphoneCall *call);
Participant(const Participant &src);
~Participant();
bool operator==(const Participant &src) const;
const LinphoneAddress *getUri() const {return m_uri;}
LinphoneCall *getCall() const {return m_call;}
void setCall(LinphoneCall *call) {m_call = call;}
private:
LinphoneAddress *m_uri;
LinphoneCall *m_call;
};
class Conference {
public:
Conference(LinphoneCore *core);
virtual ~Conference() {};
virtual int addParticipant(LinphoneCall *call) = 0;
virtual int removeParticipant(LinphoneCall *call) = 0;
virtual int removeParticipant(const LinphoneAddress *uri) = 0;
virtual int terminate() = 0;
virtual int enter() = 0;
virtual int leave() = 0;
virtual bool isIn() const = 0;
AudioStream *getAudioStream() const {return m_localParticipantStream;}
int muteMicrophone(bool val);
bool microphoneIsMuted() const {return m_isMuted;}
float getInputVolume() const;
virtual int getParticipantCount() const {return m_participants.size();}
const std::list<Participant> &getParticipants() const {return m_participants;}
virtual int startRecording(const char *path) = 0;
virtual int stopRecording() = 0;
virtual void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {};
virtual void onCallStreamStopping(LinphoneCall *call) {};
virtual void onCallTerminating(LinphoneCall *call) {};
protected:
Participant *find_participant(const LinphoneAddress *uri);
LinphoneCore *m_core;
AudioStream *m_localParticipantStream;
bool m_isMuted;
std::list<Participant> m_participants;
};
class LocalConference: public Conference {
public:
LocalConference(LinphoneCore *core);
virtual ~LocalConference();
virtual int addParticipant(LinphoneCall *call);
virtual int removeParticipant(LinphoneCall *call);
virtual int removeParticipant(const LinphoneAddress *uri);
virtual int terminate();
virtual int enter();
virtual int leave();
virtual bool isIn() const {return m_localParticipantStream!=NULL;}
virtual int getParticipantCount() const;
virtual int startRecording(const char *path);
virtual int stopRecording();
void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote);
void onCallStreamStopping(LinphoneCall *call);
void onCallTerminating(LinphoneCall *call);
private:
void addLocalEndpoint();
int remoteParticipantsCount();
void removeLocalEndpoint();
int removeFromConference(LinphoneCall *call, bool_t active);
int convertConferenceToCall();
static RtpProfile *sMakeDummyProfile(int samplerate);
MSAudioConference *m_conf;
MSAudioEndpoint *m_localEndpoint;
MSAudioEndpoint *m_recordEndpoint;
RtpProfile *m_localDummyProfile;
bool_t m_terminated;
};
class RemoteConference: public Conference {
public:
RemoteConference(LinphoneCore *core);
virtual ~RemoteConference();
virtual int addParticipant(LinphoneCall *call);
virtual int removeParticipant(LinphoneCall *call) {return -1;}
virtual int removeParticipant(const LinphoneAddress *uri);
virtual int terminate();
virtual int enter();
virtual int leave();
virtual bool isIn() const;
virtual int startRecording(const char *path) {return 0;}
virtual int stopRecording() {return 0;}
private:
enum State {
NotConnectedToFocus,
ConnectingToFocus,
ConnectedToFocus,
};
static const char *stateToString(State state);
void onFocusCallSateChanged(LinphoneCallState state);
void onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state);
static void callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message);
static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
const char *m_focusAddr;
char *m_focusContact;
LinphoneCall *m_focusCall;
State m_state;
LinphoneCoreVTable *m_vtable;
MSList *m_pendingCalls;
MSList *m_transferingCalls;
};
};
using namespace Linphone;
using namespace std;
Participant::Participant(LinphoneCall *call) {
m_uri = linphone_address_clone(linphone_call_get_remote_address(call));
m_call = linphone_call_ref(call);
}
Participant::Participant(const Participant &src) {
m_uri = linphone_address_clone(src.m_uri);
m_call = src.m_call ? linphone_call_ref(src.m_call) : NULL;
}
Participant::~Participant() {
linphone_address_unref(m_uri);
if(m_call) linphone_call_unref(m_call);
}
bool Participant::operator==(const Participant &src) const {
return linphone_address_equal(m_uri, src.m_uri);
}
Conference::Conference(LinphoneCore *core) :
m_core(core),
m_localParticipantStream(NULL),
m_isMuted(false) {
}
int Conference::addParticipant(LinphoneCall *call) {
Participant participant(call);
m_participants.push_back(participant);
call->conf_ref = (LinphoneConference *)this;
return 0;
}
int Conference::removeParticipant(LinphoneCall *call) {
Participant participant(call);
m_participants.remove(participant);
call->conf_ref = NULL;
return 0;
}
int Conference::removeParticipant(const LinphoneAddress *uri) {
Participant *participant = find_participant(uri);
if(participant == NULL) return -1;
LinphoneCall *call = participant->getCall();
if(call) call->conf_ref = NULL;
m_participants.remove(Participant(*participant));
return 0;
}
int Conference::muteMicrophone(bool val) {
if (val) {
audio_stream_set_mic_gain(m_localParticipantStream, 0);
} else {
audio_stream_set_mic_gain_db(m_localParticipantStream, m_core->sound_conf.soft_mic_lev);
}
if ( linphone_core_get_rtp_no_xmit_on_audio_mute(m_core) ){
audio_stream_mute_rtp(m_localParticipantStream, val);
}
m_isMuted=val;
return 0;
}
float Conference::getInputVolume() const {
AudioStream *st=m_localParticipantStream;
if (st && st->volsend && !m_isMuted){
float vol=0;
ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol);
return vol;
}
return LINPHONE_VOLUME_DB_LOWEST;
}
Participant *Conference::find_participant(const LinphoneAddress *uri) {
list<Participant>::iterator it = m_participants.begin();
while(it!=m_participants.end()) {
if(linphone_address_equal(uri, it->getUri())) break;
it++;
}
if(it == m_participants.end()) return NULL;
else return &*it;
}
LocalConference::LocalConference(LinphoneCore *core): Conference(core),
m_conf(NULL),
m_localEndpoint(NULL),
m_recordEndpoint(NULL),
m_localDummyProfile(NULL),
m_terminated(FALSE) {
MSAudioConferenceParams params;
params.samplerate = lp_config_get_int(m_core->config, "sound","conference_rate",16000);
m_conf=ms_audio_conference_new(&params);
}
LocalConference::~LocalConference() {
if(m_conf) terminate();
}
RtpProfile *LocalConference::sMakeDummyProfile(int samplerate){
RtpProfile *prof=rtp_profile_new("dummy");
PayloadType *pt=payload_type_clone(&payload_type_l16_mono);
pt->clock_rate=samplerate;
rtp_profile_set_payload(prof,0,pt);
return prof;
}
void LocalConference::addLocalEndpoint() {
/*create a dummy audiostream in order to extract the local part of it */
/* network address and ports have no meaning and are not used here. */
AudioStream *st=audio_stream_new(65000,65001,FALSE);
MSSndCard *playcard=m_core->sound_conf.lsd_card ?
m_core->sound_conf.lsd_card : m_core->sound_conf.play_sndcard;
MSSndCard *captcard=m_core->sound_conf.capt_sndcard;
const MSAudioConferenceParams *params=ms_audio_conference_get_params(m_conf);
m_localDummyProfile=sMakeDummyProfile(params->samplerate);
audio_stream_start_full(st, m_localDummyProfile,
"127.0.0.1",
65000,
"127.0.0.1",
65001,
0,
40,
NULL,
NULL,
playcard,
captcard,
linphone_core_echo_cancellation_enabled(m_core)
);
_post_configure_audio_stream(st,m_core,FALSE);
m_localParticipantStream=st;
m_localEndpoint=ms_audio_endpoint_get_from_stream(st,FALSE);
ms_audio_conference_add_member(m_conf,m_localEndpoint);
}
int LocalConference::addParticipant(LinphoneCall *call) {
if (call->current_params->in_conference){
ms_error("Already in conference");
return -1;
}
if (call->state==LinphoneCallPaused){
call->params->in_conference=TRUE;
call->params->has_video=FALSE;
linphone_core_resume_call(m_core,call);
}else if (call->state==LinphoneCallStreamsRunning){
LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
params->in_conference=TRUE;
params->has_video=FALSE;
if (call->audiostream || call->videostream){
linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
linphone_call_init_media_streams(call);
}
if (call==m_core->current_call){
m_core->current_call=NULL;
}
/*this will trigger a reINVITE that will later redraw the streams */
/*FIXME probably a bit too much to just redraw streams !*/
linphone_core_update_call(m_core,call,params);
linphone_call_params_destroy(params);
addLocalEndpoint();
}else{
ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
return -1;
}
Conference::addParticipant(call);
return 0;
}
int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){
int err=0;
char *str;
if (!call->current_params->in_conference){
if (call->params->in_conference){
ms_warning("Not (yet) in conference, be patient");
return -1;
}else{
ms_error("Not in a conference.");
return -1;
}
}
call->params->in_conference=FALSE;
Conference::removeParticipant(call);
str=linphone_call_get_remote_address_as_string(call);
ms_message("%s will be removed from conference", str);
ms_free(str);
if (active){
LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call));
params->in_conference=FALSE;
// reconnect local audio with this call
if (isIn()){
ms_message("Leaving conference for reconnecting with unique call.");
leave();
}
ms_message("Updating call to actually remove from conference");
err=linphone_core_update_call(m_core,call,params);
linphone_call_params_destroy(params);
} else{
ms_message("Pausing call to actually remove from conference");
err=_linphone_core_pause_call(m_core,call);
}
return err;
}
int LocalConference::remoteParticipantsCount() {
int count=getParticipantCount();
if (count==0) return 0;
if (!m_localParticipantStream) return count;
return count -1;
}
int LocalConference::convertConferenceToCall(){
int err=0;
MSList *calls=m_core->calls;
if (remoteParticipantsCount()!=1){
ms_error("No unique call remaining in conference.");
return -1;
}
while (calls) {
LinphoneCall *rc=(LinphoneCall*)calls->data;
calls=calls->next;
if (rc->params->in_conference) { // not using current_param
bool_t active_after_removed=isIn();
err=removeFromConference(rc, active_after_removed);
break;
}
}
return err;
}
int LocalConference::removeParticipant(LinphoneCall *call) {
int err;
char * str=linphone_call_get_remote_address_as_string(call);
ms_message("Removing call %s from the conference", str);
ms_free(str);
err=removeFromConference(call, FALSE);
if (err){
ms_error("Error removing participant from conference.");
return err;
}
if (remoteParticipantsCount()==1){
ms_message("conference size is 1: need to be converted to plain call");
err=convertConferenceToCall();
} else {
ms_message("the conference need not to be converted as size is %i", remoteParticipantsCount());
}
return err;
}
int LocalConference::removeParticipant(const LinphoneAddress *uri) {
Participant *participant = find_participant(uri);
if(participant == NULL) return -1;
LinphoneCall *call = participant->getCall();
if(call == NULL) return -1;
return removeParticipant(call);
}
int LocalConference::terminate() {
MSList *calls=m_core->calls;
m_terminated=TRUE;
while (calls) {
LinphoneCall *call=(LinphoneCall*)calls->data;
calls=calls->next;
if (call->current_params->in_conference) {
linphone_core_terminate_call(m_core, call);
}
}
return 0;
}
int LocalConference::enter() {
if (linphone_core_sound_resources_locked(m_core)) {
return -1;
}
if (m_core->current_call != NULL) {
_linphone_core_pause_call(m_core, m_core->current_call);
}
if (m_localParticipantStream==NULL) addLocalEndpoint();
return 0;
}
void LocalConference::removeLocalEndpoint(){
if (m_localEndpoint){
ms_audio_conference_remove_member(m_conf,m_localEndpoint);
ms_audio_endpoint_release_from_stream(m_localEndpoint);
m_localEndpoint=NULL;
audio_stream_stop(m_localParticipantStream);
m_localParticipantStream=NULL;
rtp_profile_destroy(m_localDummyProfile);
}
}
int LocalConference::leave() {
if (isIn())
removeLocalEndpoint();
return 0;
}
int LocalConference::getParticipantCount() const {
if (m_conf == NULL) {
return 0;
}
return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0);
}
int LocalConference::startRecording(const char *path) {
if (m_conf == NULL) {
ms_warning("linphone_core_start_conference_recording(): no conference now.");
return -1;
}
if (m_recordEndpoint==NULL){
m_recordEndpoint=ms_audio_endpoint_new_recorder();
ms_audio_conference_add_member(m_conf,m_recordEndpoint);
}
ms_audio_recorder_endpoint_start(m_recordEndpoint,path);
return 0;
}
int LocalConference::stopRecording() {
if (m_conf == NULL) {
ms_warning("linphone_core_stop_conference_recording(): no conference now.");
return -1;
}
if (m_recordEndpoint==NULL){
ms_warning("linphone_core_stop_conference_recording(): no record active.");
return -1;
}
ms_audio_recorder_endpoint_stop(m_recordEndpoint);
return 0;
}
void LocalConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) {
call->params->has_video = FALSE;
call->camera_enabled = FALSE;
MSAudioEndpoint *ep=ms_audio_endpoint_get_from_stream(call->audiostream,TRUE);
ms_audio_conference_add_member(m_conf,ep);
ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote);
call->endpoint=ep;
}
void LocalConference::onCallStreamStopping(LinphoneCall *call) {
ms_audio_conference_remove_member(m_conf,call->endpoint);
ms_audio_endpoint_release_from_stream(call->endpoint);
call->endpoint=NULL;
}
void LocalConference::onCallTerminating(LinphoneCall *call) {
int remote_count=remoteParticipantsCount();
ms_message("conference_check_uninit(): size=%i", getParticipantCount());
if (remote_count==1 && !m_terminated){
convertConferenceToCall();
}
if (remote_count==0){
if (m_localParticipantStream)
removeLocalEndpoint();
if (m_recordEndpoint){
ms_audio_conference_remove_member(m_conf, m_recordEndpoint);
ms_audio_endpoint_destroy(m_recordEndpoint);
}
}
if (ms_audio_conference_get_size(m_conf)==0){
ms_audio_conference_destroy(m_conf);
m_core->conf_ctx = NULL;
delete this;
}
}
RemoteConference::RemoteConference(LinphoneCore *core):
Conference(core),
m_focusAddr(NULL),
m_focusContact(NULL),
m_focusCall(NULL),
m_state(NotConnectedToFocus),
m_vtable(NULL),
m_pendingCalls(NULL),
m_transferingCalls(NULL) {
m_focusAddr = lp_config_get_string(m_core->config, "misc", "conference_focus_addr", "");
m_vtable = linphone_core_v_table_new();
m_vtable->call_state_changed = callStateChangedCb;
m_vtable->transfer_state_changed = transferStateChanged;
linphone_core_v_table_set_user_data(m_vtable, this);
_linphone_core_add_listener(m_core, m_vtable, FALSE, TRUE);
}
RemoteConference::~RemoteConference() {
if(m_state == ConnectedToFocus) terminate();
linphone_core_remove_listener(m_core, m_vtable);
linphone_core_v_table_destroy(m_vtable);
}
int RemoteConference::addParticipant(LinphoneCall *call) {
LinphoneAddress *addr;
switch(m_state) {
case NotConnectedToFocus:
Conference::addParticipant(call);
ms_message("Calling the conference focus (%s)", m_focusAddr);
addr = linphone_address_new(m_focusAddr);
if(addr) {
m_focusCall = linphone_call_ref(linphone_core_invite_address(m_core, addr));
m_localParticipantStream = m_focusCall->audiostream;
m_pendingCalls = ms_list_append(m_pendingCalls, linphone_call_ref(call));
m_state = ConnectingToFocus;
linphone_address_unref(addr);
addParticipant(m_focusCall);
return 0;
} else return -1;
case ConnectingToFocus:
Conference::addParticipant(call);
m_pendingCalls = ms_list_append(m_pendingCalls, linphone_call_ref(call));
return 0;
case ConnectedToFocus:
Conference::addParticipant(call);
m_transferingCalls = ms_list_append(m_transferingCalls, linphone_call_ref(call));
linphone_core_transfer_call(m_core, call, m_focusContact);
return 0;
default:
ms_error("Could not add call %p to the conference. Bad conference state (%s)", call, stateToString(m_state));
return -1;
}
}
int RemoteConference::removeParticipant(const LinphoneAddress *uri) {
char *tmp, *refer_to;
int res;
switch(m_state) {
case ConnectedToFocus:
tmp = linphone_address_as_string_uri_only(uri);
refer_to = ms_strdup_printf("%s;method=BYE", tmp);
res = sal_call_refer(m_focusCall->op, refer_to);
ms_free(tmp);
ms_free(refer_to);
if(res == 0) return Conference::removeParticipant(uri);
else return -1;
default:
ms_error("Cannot remove %s from conference: Bad conference state (%s)", linphone_address_as_string(uri), stateToString(m_state));
return -1;
}
}
int RemoteConference::terminate() {
switch(m_state) {
case ConnectingToFocus:
case ConnectedToFocus:
Conference::removeParticipant(m_focusCall);
linphone_core_terminate_call(m_core, m_focusCall);
break;
default:
break;
}
return 0;
}
int RemoteConference::enter() {
if(m_state != ConnectedToFocus) {
ms_error("Could not enter in the conference: bad conference state (%s)", stateToString(m_state));
return -1;
}
LinphoneCallState callState = linphone_call_get_state(m_focusCall);
switch(callState) {
case LinphoneCallStreamsRunning: break;
case LinphoneCallPaused:
linphone_core_resume_call(m_core, m_focusCall);
break;
default:
ms_error("Could not join the conference: bad focus call state (%s)", linphone_call_state_to_string(callState));
return -1;
}
return 0;
}
int RemoteConference::leave() {
if(m_state != ConnectedToFocus) {
ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state));
return -1;
}
LinphoneCallState callState = linphone_call_get_state(m_focusCall);
switch(callState) {
case LinphoneCallPaused: break;
case LinphoneCallStreamsRunning:
linphone_core_pause_call(m_core, m_focusCall);
break;
default:
ms_error("Could not leave the conference: bad focus call state (%s)", linphone_call_state_to_string(callState));
return -1;
}
return 0;
}
bool RemoteConference::isIn() const {
if(m_state != ConnectedToFocus) return false;
LinphoneCallState callState = linphone_call_get_state(m_focusCall);
return callState == LinphoneCallStreamsRunning;
}
const char *RemoteConference::stateToString(RemoteConference::State state) {
switch(state) {
case NotConnectedToFocus: return "NotConnectedToFocus";
case ConnectingToFocus: return "ConnectingToFocus";
case ConnectedToFocus: return "ConnectedToFocus";
default: return "Unknown";
}
}
void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) {
MSList *it;
switch (state) {
case LinphoneCallConnected:
m_state = ConnectedToFocus;
m_focusContact = ms_strdup(linphone_call_get_remote_contact(m_focusCall));
it = m_pendingCalls;
while (it) {
LinphoneCall *pendingCall = (LinphoneCall *)it->data;
LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall);
if(pendingCallState == LinphoneCallStreamsRunning || pendingCallState == LinphoneCallPaused) {
MSList *current_elem = it;
it = it->next;
m_pendingCalls = ms_list_remove_link(m_pendingCalls, current_elem);
m_transferingCalls = ms_list_append(m_transferingCalls, pendingCall);
linphone_core_transfer_call(m_core, pendingCall, m_focusContact);
} else {
it = it->next;
}
}
break;
case LinphoneCallError:
case LinphoneCallEnd:
Conference::removeParticipant(m_focusCall);
m_state = NotConnectedToFocus;
linphone_call_unref(m_focusCall);
m_focusCall = NULL;
m_localParticipantStream = NULL;
if(m_focusContact) {
ms_free(m_focusContact);
m_focusContact = NULL;
}
m_pendingCalls = ms_list_free_with_data(m_pendingCalls, (void (*)(void *))linphone_call_unref);
m_transferingCalls = ms_list_free_with_data(m_transferingCalls, (void (*)(void *))linphone_call_unref);
break;
default: break;
}
}
void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) {
switch(state) {
case LinphoneCallStreamsRunning:
case LinphoneCallPaused:
if(m_state == ConnectedToFocus) {
m_pendingCalls = ms_list_remove(m_pendingCalls, call);
m_transferingCalls = ms_list_append(m_transferingCalls, call);
linphone_core_transfer_call(m_core, call, m_focusContact);
}
break;
case LinphoneCallError:
case LinphoneCallEnd:
Conference::removeParticipant(call);
m_pendingCalls = ms_list_remove(m_pendingCalls, call);
linphone_call_unref(call);
break;
default: break;
}
}
void RemoteConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) {
LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc);
RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable);
if (call == conf->m_focusCall) {
conf->onFocusCallSateChanged(cstate);
} else if(ms_list_find(conf->m_pendingCalls, call)) {
conf->onPendingCallStateChanged(call, cstate);
}
}
void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) {
LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc);
RemoteConference *conf = (RemoteConference *)linphone_core_v_table_get_user_data(vtable);
if (ms_list_find(conf->m_transferingCalls, transfered)) {
switch (new_call_state) {
case LinphoneCallConnected:
case LinphoneCallError:
conf->m_transferingCalls = ms_list_remove(conf->m_transferingCalls, transfered);
linphone_call_unref(transfered);
break;
default:
break;
}
}
}
LinphoneConference *linphone_local_conference_new(LinphoneCore *core) {
return (LinphoneConference *) new LocalConference(core);
}
LinphoneConference *linphone_remote_conference_new(LinphoneCore *core) {
return (LinphoneConference *) new RemoteConference(core);
}
void linphone_conference_free(LinphoneConference *obj) {
delete (Conference *)obj;
}
int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call) {
return ((Conference *)obj)->addParticipant(call);
}
int linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri) {
return ((Conference *)obj)->removeParticipant(uri);
}
int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call) {
return ((Conference *)obj)->removeParticipant(call);
}
int linphone_conference_terminate(LinphoneConference *obj) {
return ((Conference *)obj)->terminate();
}
int linphone_conference_enter(LinphoneConference *obj) {
return ((Conference *)obj)->enter();
}
int linphone_conference_leave(LinphoneConference *obj) {
return ((Conference *)obj)->leave();
}
bool_t linphone_conference_is_in(const LinphoneConference *obj) {
return ((Conference *)obj)->isIn() ? TRUE : FALSE;
}
AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj) {
return ((Conference *)obj)->getAudioStream();
}
int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val) {
return ((Conference *)obj)->muteMicrophone(val);
}
bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj) {
return ((Conference *)obj)->microphoneIsMuted();
}
float linphone_conference_get_input_volume(const LinphoneConference *obj) {
return ((Conference *)obj)->getInputVolume();
}
int linphone_conference_get_participant_count(const LinphoneConference *obj) {
return ((Conference *)obj)->getParticipantCount();
}
MSList *linphone_conference_get_participants(const LinphoneConference *obj) {
const list<Participant> participants = ((Conference *)obj)->getParticipants();
MSList *participants_list = NULL;
for(list<Participant>::const_iterator it=participants.begin();it!=participants.end();it++) {
LinphoneAddress *uri = linphone_address_clone(it->getUri());
participants_list = ms_list_append(participants_list, uri);
}
return participants_list;
}
int linphone_conference_start_recording(LinphoneConference *obj, const char *path) {
return ((Conference *)obj)->startRecording(path);
}
int linphone_conference_stop_recording(LinphoneConference *obj) {
return ((Conference *)obj)->stopRecording();
}
void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote) {
((Conference *)obj)->onCallStreamStarting(call, is_paused_by_remote);
}
void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call) {
((Conference *)obj)->onCallStreamStopping(call);
}
void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call) {
((Conference *)obj)->onCallTerminating(call);
}
bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class) {
switch(_class) {
case LinphoneConferenceClassLocal: return typeid(obj) == typeid(LocalConference);
case LinphoneConferenceClassRemote: return typeid(obj) == typeid(RemoteConference);
default: return FALSE;
}
}

68
coreapi/conference.h Normal file
View file

@ -0,0 +1,68 @@
/*******************************************************************************
* conference.cc
*
* Thu Nov 26, 2015
* Copyright 2015 Belledonne Communications
* Author: Linphone's team
* Email info@belledonne-communications.com
******************************************************************************/
/*
* 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.
*/
#ifndef CONFERENCE_H
#define CONFERENCE_H
#include "linphonecore.h"
typedef struct _LinphoneConference LinphoneConference;
typedef enum {
LinphoneConferenceClassLocal,
LinphoneConferenceClassRemote
} LinphoneConferenceClass;
LinphoneConference *linphone_local_conference_new(LinphoneCore *core);
LinphoneConference *linphone_remote_conference_new(LinphoneCore *core);
void linphone_conference_free(LinphoneConference *obj);
int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call);
int linphone_conference_remove_participant_with_call(LinphoneConference *obj, LinphoneCall *call);
int linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri);
int linphone_conference_terminate(LinphoneConference *obj);
int linphone_conference_enter(LinphoneConference *obj);
int linphone_conference_leave(LinphoneConference *obj);
bool_t linphone_conference_is_in(const LinphoneConference *obj);
AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj);
int linphone_conference_mute_microphone(LinphoneConference *obj, bool_t val);
bool_t linphone_conference_microphone_is_muted(const LinphoneConference *obj);
float linphone_conference_get_input_volume(const LinphoneConference *obj);
int linphone_conference_get_participant_count(const LinphoneConference *obj);
MSList *linphone_conference_get_participants(const LinphoneConference *obj);
int linphone_conference_start_recording(LinphoneConference *obj, const char *path);
int linphone_conference_stop_recording(LinphoneConference *obj);
void linphone_conference_on_call_stream_starting(LinphoneConference *obj, LinphoneCall *call, bool_t is_paused_by_remote);
void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, LinphoneCall *call);
void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCall *call);
bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class);
#endif // CONFERENCE_H

View file

@ -1433,7 +1433,7 @@ static void linphone_call_set_terminated(LinphoneCall *call){
if (linphone_core_del_call(lc,call) != 0){
ms_error("Could not remove the call from the list !!!");
}
linphone_core_conference_check_uninit(lc);
if(lc->conf_ctx) linphone_conference_on_call_terminating(lc->conf_ctx, call);
if (call->ringing_beep){
linphone_core_stop_dtmf(lc);
call->ringing_beep=FALSE;
@ -3108,7 +3108,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, LinphoneCallSta
if (call->params->in_conference){
/*transform the graph to connect it to the conference filter */
mute = stream->dir==SalStreamRecvOnly;
linphone_call_add_to_conf(call, mute);
linphone_conference_on_call_stream_starting(lc->conf_ctx, call, mute);
}
call->current_params->in_conference=call->params->in_conference;
call->current_params->low_bandwidth=call->params->low_bandwidth;
@ -3600,6 +3600,7 @@ static void update_rtp_stats(LinphoneCall *call, int stream_index) {
}
static void linphone_call_stop_audio_stream(LinphoneCall *call) {
LinphoneCore *lc = call->core;
if (call->audiostream!=NULL) {
linphone_reporting_update_media_info(call, LINPHONE_CALL_STATS_AUDIO);
media_stream_reclaim_sessions(&call->audiostream->ms,&call->sessions[call->main_audio_stream_index]);
@ -3615,7 +3616,7 @@ static void linphone_call_stop_audio_stream(LinphoneCall *call) {
audio_stream_get_local_rtp_stats(call->audiostream,&call->log->local_stats);
linphone_call_log_fill_stats (call->log,(MediaStream*)call->audiostream);
if (call->endpoint){
linphone_call_remove_from_conf(call);
linphone_conference_on_call_stream_stopping(lc->conf_ctx, call);
}
update_rtp_stats(call, call->main_audio_stream_index);
audio_stream_stop(call->audiostream);
@ -4544,6 +4545,10 @@ bool_t linphone_call_is_in_conference(const LinphoneCall *call) {
return call->params->in_conference;
}
LinphoneConference *linphone_call_get_conference(const LinphoneCall *call) {
return call->conf_ref;
}
/**
* Perform a zoom of the video displayed during a call.
* @param call the call.
@ -4876,4 +4881,3 @@ void linphone_call_refresh_sockets(LinphoneCall *call){
}
}
}

View file

@ -1698,7 +1698,7 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab
internal_vtable->notify_received = linphone_core_internal_notify_received;
_linphone_core_add_listener(lc, internal_vtable, TRUE);
memcpy(local_vtable,vtable,sizeof(LinphoneCoreVTable));
_linphone_core_add_listener(lc, local_vtable, TRUE);
_linphone_core_add_listener(lc, local_vtable, TRUE, TRUE);
linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
ortp_init();
@ -4890,8 +4890,7 @@ void linphone_core_enable_mic(LinphoneCore *lc, bool_t enable) {
const MSList *elem;
if (linphone_core_is_in_conference(lc)){
lc->conf_ctx.local_muted=!enable;
linphone_core_mute_audio_stream(lc, lc->conf_ctx.local_participant, !enable);
linphone_conference_mute_microphone(lc->conf_ctx, !enable);
}
list = linphone_core_get_calls(lc);
for (elem = list; elem != NULL; elem = elem->next) {
@ -4904,7 +4903,7 @@ void linphone_core_enable_mic(LinphoneCore *lc, bool_t enable) {
bool_t linphone_core_mic_enabled(LinphoneCore *lc) {
LinphoneCall *call=linphone_core_get_current_call(lc);
if (linphone_core_is_in_conference(lc)){
return !lc->conf_ctx.local_muted;
return linphone_conference_microphone_is_muted(lc->conf_ctx);
}else if (call==NULL){
ms_warning("%s(): No current call!", __FUNCTION__);
return TRUE;
@ -5925,7 +5924,7 @@ static MSFilter *get_audio_resource(LinphoneCore *lc, LinphoneAudioResourceType
if (call){
stream=call->audiostream;
}else if (linphone_core_is_in_conference(lc)){
stream=lc->conf_ctx.local_participant;
stream=linphone_conference_get_audio_stream(lc->conf_ctx);
}
if (stream){
if (rtype==LinphoneToneGenerator) return stream->dtmfgen;
@ -7434,3 +7433,84 @@ const char *linphone_stream_type_to_string(const LinphoneStreamType type) {
LinphoneRingtonePlayer *linphone_core_get_ringtoneplayer(LinphoneCore *lc) {
return lc->ringtoneplayer;
}
int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call) {
const char *conf_method_name;
if(lc->conf_ctx == NULL) {
conf_method_name = lp_config_get_string(lc->config, "misc", "conference_type", "local");
if(strcasecmp(conf_method_name, "local") == 0) {
lc->conf_ctx = linphone_local_conference_new(lc);
} else if(strcasecmp(conf_method_name, "remote") == 0) {
lc->conf_ctx = linphone_remote_conference_new(lc);
} else {
ms_error("'%s' is not a valid conference method", conf_method_name);
return -1;
}
}
return linphone_conference_add_participant(lc->conf_ctx, call);
}
int linphone_core_add_all_to_conference(LinphoneCore *lc) {
MSList *calls=lc->calls;
while (calls) {
LinphoneCall *call=(LinphoneCall*)calls->data;
calls=calls->next;
linphone_core_add_to_conference(lc, call);
}
if(lc->conf_ctx && linphone_conference_check_class(lc->conf_ctx, LinphoneConferenceClassLocal)) {
linphone_core_enter_conference(lc);
}
return 0;
}
int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call) {
if(lc->conf_ctx) return linphone_conference_remove_participant_with_call(lc->conf_ctx, call);
else return -1;
}
int linphone_core_terminate_conference(LinphoneCore *lc) {
if(lc->conf_ctx == NULL) return -1;
linphone_conference_terminate(lc->conf_ctx);
linphone_conference_free(lc->conf_ctx);
lc->conf_ctx = NULL;
return 0;
}
int linphone_core_enter_conference(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_enter(lc->conf_ctx);
else return -1;
}
int linphone_core_leave_conference(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_leave(lc->conf_ctx);
else return -1;
}
bool_t linphone_core_is_in_conference(const LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_is_in(lc->conf_ctx);
else return FALSE;
}
int linphone_core_get_conference_size(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_get_participant_count(lc->conf_ctx);
return 0;
}
float linphone_core_get_conference_local_input_volume(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_get_input_volume(lc->conf_ctx);
else return -1.0;
}
int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path) {
if(lc->conf_ctx) return linphone_conference_start_recording(lc->conf_ctx, path);
return -1;
}
int linphone_core_stop_conference_recording(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_stop_recording(lc->conf_ctx);
return -1;
}
LinphoneConference *linphone_core_get_conference(LinphoneCore *lc) {
return lc->conf_ctx;
}

View file

@ -139,6 +139,13 @@ enum _LinphoneStreamType {
* @ingroup initializing
**/
typedef enum _LinphoneStreamType LinphoneStreamType;
/**
* Internal object of LinphoneCore representing a conference
* @ingroup call_control
*/
typedef struct _LinphoneConference LinphoneConference;
/**
* Function returning a human readable value for LinphoneStreamType.
* @ingroup initializing
@ -441,6 +448,8 @@ LINPHONE_PUBLIC void linphone_address_set_secure(LinphoneAddress *addr, bool_t e
LINPHONE_PUBLIC bool_t linphone_address_is_sip(const LinphoneAddress *uri);
LINPHONE_PUBLIC LinphoneTransportType linphone_address_get_transport(const LinphoneAddress *uri);
LINPHONE_PUBLIC void linphone_address_set_transport(LinphoneAddress *uri,LinphoneTransportType type);
LINPHONE_PUBLIC const char *linphone_address_get_method_param(const LinphoneAddress *addr);
LINPHONE_PUBLIC void linphone_address_set_method_param(LinphoneAddress *addr, const char *method);
LINPHONE_PUBLIC char *linphone_address_as_string(const LinphoneAddress *u);
LINPHONE_PUBLIC char *linphone_address_as_string_uri_only(const LinphoneAddress *u);
LINPHONE_PUBLIC bool_t linphone_address_weak_equal(const LinphoneAddress *a1, const LinphoneAddress *a2);
@ -888,10 +897,19 @@ LINPHONE_PUBLIC void linphone_call_set_native_video_window_id(LinphoneCall *call
* @param call #LinphoneCall
* @return TRUE if part of a conference.
*
* @deprecated Use linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call)) instead.
* @deprecated Use linphone_call_get_conference() instead.
* @ingroup call_control
*/
LINPHONE_PUBLIC LINPHONE_DEPRECATED bool_t linphone_call_is_in_conference(const LinphoneCall *call);
/**
* Return the associated conference object
* @param call #LinphoneCall
* @return A pointer on #LinphoneConference or NULL if the call is not part
* of any conference.
* @ingroup call_control
*/
LINPHONE_PUBLIC LinphoneConference *linphone_call_get_conference(const LinphoneCall *call);
/**
* Enables or disable echo cancellation for this call
* @param call
@ -3806,7 +3824,27 @@ LINPHONE_PUBLIC const char *linphone_core_get_user_certificates_path(LinphoneCor
*/
LINPHONE_PUBLIC LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri);
/**
* @addtogroup call_control
* @{
*/
/**
* Add a participant to the conference. If no conference is going on
* a new internal conference context is created and the participant is
* added to it.
* @param lc #LinphoneCore
* @param call The current call with the participant to add
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call);
/**
* Add all current calls into the conference. If no conference is running
* a new internal conference context is created and all current calls
* are added to it.
* @param lc #LinphoneCore
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_add_all_to_conference(LinphoneCore *lc);
/**
* Remove a call from the conference.
@ -3826,18 +3864,84 @@ LINPHONE_PUBLIC int linphone_core_add_all_to_conference(LinphoneCore *lc);
LINPHONE_PUBLIC int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call);
/**
* Indicates whether the local participant is part of a conference.
* @warning That function automatically fails in the case of conferences using a
* conferencet server (focus). If you use such a conference, you should use
* linphone_conference_remove_participant() instead.
* @param lc the linphone core
* @return TRUE if the local participant is in a conference, FALSE otherwise.
**/
*/
LINPHONE_PUBLIC bool_t linphone_core_is_in_conference(const LinphoneCore *lc);
/**
* Join the local participant to the running conference
* @param lc #LinphoneCore
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_enter_conference(LinphoneCore *lc);
/**
* Make the local participant leave the running conference
* @param lc #LinphoneCore
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_leave_conference(LinphoneCore *lc);
/**
* Get the set input volume of the local participant
* @param lc #LinphoneCore
* @return A value inside [0.0 ; 1.0]
*/
LINPHONE_PUBLIC float linphone_core_get_conference_local_input_volume(LinphoneCore *lc);
/**
* Terminate the running conference. If it is a local conference, all calls
* inside it will become back separate calls and will be put in #LinphoneCallPaused state.
* If it is a conference involving a focus server, all calls inside the conference
* will be terminated.
* @param lc #LinphoneCore
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_terminate_conference(LinphoneCore *lc);
/**
* Get the number of remote participant in the running conference
* @param lc #LinphoneCore
* @return The number of remote participant
*/
LINPHONE_PUBLIC int linphone_core_get_conference_size(LinphoneCore *lc);
/**
* Start recording the running conference
* @param lc #LinphoneCore
* @param path Path to the file where the recording will be written
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path);
/**
* Stop recording the running conference
* @param #LinphoneCore
* @return 0 if succeeded. Negative number if failed
*/
LINPHONE_PUBLIC int linphone_core_stop_conference_recording(LinphoneCore *lc);
/**
* Get a pointer on the internal conference object.
* @param lc #LinphoneCore
* @return A pointer on #LinphoneConference or NULL if no conference are going on
*/
LINPHONE_PUBLIC LinphoneConference *linphone_core_get_conference(LinphoneCore *lc);
/**
* Get URIs of all participants of one conference
* @param obj A #LinphoneConference
* @return A #MSList containing URIs of all participant. That list must be
* freed after utilisation and each URI must be unref with linphone_address_unref()
*/
LINPHONE_PUBLIC MSList *linphone_conference_get_participants(const LinphoneConference *obj);
/**
* Remove a participant from a conference
* @param obj A #LinphoneConference
* @param uri SIP URI of the participant to remove
* @return 0 if succeeded, -1 if failed
*/
LINPHONE_PUBLIC int linphone_conference_remove_participant(LinphoneConference *obj, const LinphoneAddress *uri);
/**
* @}
*/
/**
* Get the maximum number of simultaneous calls Linphone core can manage at a time. All new call above this limit are declined with a busy answer
* @ingroup initializing

View file

@ -1311,7 +1311,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeListener(JNIEnv* e
VTableReference *ref=(VTableReference*)(iterator->data);
LinphoneCoreVTable *vTable = ref->valid ? ref->vtable : NULL;
iterator = iterator->next; //Because linphone_core_remove_listener may change the list
if (vTable) {
if (vTable && !ref->internal) {
LinphoneCoreData *data = (LinphoneCoreData*) linphone_core_v_table_get_user_data(vTable);
if (data && env->IsSameObject(data->listener, jlistener)) {
linphone_core_remove_listener(core, vTable);
@ -4315,6 +4315,14 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv
return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore);
}
extern "C" jobject Jave_org_linphone_core_LinphoneCoreImpl_getConference(JNIEnv *env, jobject thiz, jlong pCore) {
jclass conference_class = env->FindClass("org/linphone/core/LinphoneConference");
jmethodID conference_constructor = env->GetMethodID(conference_class, "<init>", "(J)");
LinphoneConference *conf = linphone_core_get_conference((LinphoneCore *)pCore);
if(conf) return env->NewObject(conference_class, conference_constructor, conf);
else return NULL;
}
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startConferenceRecording(JNIEnv *env,jobject thiz,jlong pCore, jstring jpath){
int err=-1;
if (jpath){
@ -4449,6 +4457,14 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVe
linphone_call_set_authentication_token_verified(call, verified);
}
extern "C" jobject Java_org_linphnoe_core_LinphoneCallImpl_getConference(JNIEnv *env, jobject thiz, jlong ptr) {
jclass conference_class = env->FindClass("org/linphone/core/LinphoneConference");
jmethodID conference_constructor = env->GetMethodID(conference_class, "<init>", "(J)");
LinphoneConference *conf = linphone_call_get_conference((LinphoneCall *)ptr);
if(conf) return env->NewObject(conference_class, conference_constructor, conf);
return NULL;
}
extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getPlayVolume(JNIEnv* env, jobject thiz, jlong ptr) {
LinphoneCall *call = (LinphoneCall *) ptr;
return (jfloat)linphone_call_get_play_volume(call);
@ -6730,3 +6746,26 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getNortpTimeout(J
JNIEXPORT jobject JNICALL Java_org_linphone_core_LinphoneConferenceImpl_getParticipants(JNIEnv *env, jobject thiz, jlong pconference) {
MSList *participants, *it;
jclass addr_class = env->FindClass("org/linphone/core/LinphoneAddressImpl");
jclass addr_list_class = env->FindClass("[Lorg/linphone/core/LinphoneAddressImpl;");
jmethodID addr_constructor = env->GetMethodID(addr_class, "<init>", "(J)");
jmethodID addr_list_constructor = env->GetMethodID(addr_list_class, "<init>", "(V)");
jmethodID addr_list_append = env->GetMethodID(addr_list_class, "add", "(Lorg/linphone/core/LinphoneAddressImpl;)Z");
jobject jaddr_list = env->NewObject(addr_list_class, addr_list_constructor);
participants = linphone_conference_get_participants((LinphoneConference *)pconference);
for(it = participants; it; it = ms_list_next(it)) {
LinphoneAddress *addr = (LinphoneAddress *)it->data;
jobject jaddr = env->NewObject(addr_class, addr_constructor, addr);
env->CallBooleanMethod(jaddr_list, addr_list_append, jaddr);
}
return jaddr_list;
}
JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneConferenteImpl_removeParticipant(JNIEnv *env, jobject thiz, jlong pconference, jobject uri) {
jfieldID native_ptr_attr = env->GetFieldID(env->GetObjectClass(uri), "nativePtr", "J");
LinphoneAddress *addr = (LinphoneAddress *)env->GetLongField(uri, native_ptr_attr);
return linphone_conference_remove_participant((LinphoneConference *)pconference, addr);
}

View file

@ -33,6 +33,7 @@ extern "C" {
#include "friendlist.h"
#include "linphone_tunnel.h"
#include "linphonecore_utils.h"
#include "conference.h"
#include "sal/sal.h"
#include "sipsetup.h"
#include "quality_reporting.h"
@ -345,6 +346,8 @@ struct _LinphoneCall{
bool_t paused_by_app;
bool_t broken; /*set to TRUE when the call is in broken state due to network disconnection or transport */
LinphoneConference *conf_ref; /**> Point on the associated conference if this call is part of a conference. NULL instead. */
};
BELLE_SIP_DECLARE_VPTR(LinphoneCall);
@ -820,16 +823,6 @@ typedef struct autoreplier_config
const char *message; /* the path of the file to be played */
}autoreplier_config_t;
struct _LinphoneConference{
MSAudioConference *conf;
AudioStream *local_participant;
MSAudioEndpoint *local_endpoint;
MSAudioEndpoint *record_endpoint;
RtpProfile *local_dummy_profile;
bool_t local_muted;
bool_t terminated;
};
typedef struct _LinphoneToneDescription{
LinphoneReason reason;
@ -844,7 +837,6 @@ void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason)
void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile);
const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id);
int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info);
typedef struct _LinphoneConference LinphoneConference;
typedef struct _LinphoneTaskList{
MSList *hooks;
@ -906,7 +898,7 @@ struct _LinphoneCore
time_t netup_time; /*time when network went reachable */
struct _EcCalibrator *ecc;
LinphoneTaskList hooks; /*tasks periodically executed in linphone_core_iterate()*/
LinphoneConference conf_ctx;
LinphoneConference *conf_ctx;
char* zrtp_secrets_cache;
char* user_certificates_path;
LinphoneVideoPolicy video_policy;
@ -1040,13 +1032,6 @@ int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call);
/*conferencing subsystem*/
void _post_configure_audio_stream(AudioStream *st, LinphoneCore *lc, bool_t muted);
/* When a conference participant pause the conference he may send a music.
* We don't want to hear that music or to send it to the other participants.
* Use muted=yes to handle this case.
*/
void linphone_call_add_to_conf(LinphoneCall *call, bool_t muted);
void linphone_call_remove_from_conf(LinphoneCall *call);
void linphone_core_conference_check_uninit(LinphoneCore *lc);
bool_t linphone_core_sound_resources_available(LinphoneCore *lc);
void linphone_core_notify_refer_state(LinphoneCore *lc, LinphoneCall *referer, LinphoneCall *newcall);
unsigned int linphone_core_get_audio_features(LinphoneCore *lc);
@ -1420,17 +1405,18 @@ struct _VTableReference{
LinphoneCoreVTable *vtable;
bool_t valid;
bool_t autorelease;
bool_t internal;
};
typedef struct _VTableReference VTableReference;
void v_table_reference_destroy(VTableReference *ref);
void _linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable, bool_t autorelease);
LINPHONE_PUBLIC void linphone_core_v_table_set_internal(LinphoneCoreVTable *table, bool_t internal);
bool_t linphone_core_v_table_is_internal(LinphoneCoreVTable *table);
void _linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable, bool_t autorelease, bool_t internal);
#ifdef VIDEO_ENABLED
LINPHONE_PUBLIC MSWebCam *linphone_call_get_video_device(const LinphoneCall *call);
MSWebCam *get_nowebcam_device(void);

View file

@ -272,10 +272,11 @@ void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore
cleanup_dead_vtable_refs(lc);
}
static VTableReference * v_table_reference_new(LinphoneCoreVTable *vtable, bool_t autorelease){
static VTableReference * v_table_reference_new(LinphoneCoreVTable *vtable, bool_t autorelease, bool_t internal){
VTableReference *ref=ms_new0(VTableReference,1);
ref->valid=1;
ref->autorelease=autorelease;
ref->internal = internal;
ref->vtable=vtable;
return ref;
}
@ -285,13 +286,13 @@ void v_table_reference_destroy(VTableReference *ref){
ms_free(ref);
}
void _linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable, bool_t autorelease) {
void _linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable, bool_t autorelease, bool_t internal) {
ms_message("Vtable [%p] registered on core [%p]",lc,vtable);
lc->vtable_refs=ms_list_append(lc->vtable_refs,v_table_reference_new(vtable, autorelease));
lc->vtable_refs=ms_list_append(lc->vtable_refs,v_table_reference_new(vtable, autorelease, internal));
}
void linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable){
_linphone_core_add_listener(lc, vtable, FALSE);
_linphone_core_add_listener(lc, vtable, FALSE, FALSE);
}
void linphone_core_remove_listener(LinphoneCore *lc, const LinphoneCoreVTable *vtable) {

View file

@ -178,4 +178,6 @@ void linphone_gtk_unset_from_conference(LinphoneCall *call){
}
}
bool_t linphone_gtk_call_is_in_conference_view(LinphoneCall *call) {
return (find_conferencee_from_call(call) != NULL);
}

View file

@ -509,11 +509,10 @@ 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 ();
GtkWidget *nb=linphone_gtk_get_widget(main_window,"viewswitch");
gboolean in_conf=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
int idx;
g_return_if_fail(w!=NULL);
idx=gtk_notebook_page_num(GTK_NOTEBOOK(nb),w);
if (in_conf){
if (linphone_gtk_call_is_in_conference_view(call)){
linphone_gtk_unset_from_conference(call);
}
linphone_call_set_user_pointer (call,NULL);
@ -776,7 +775,7 @@ void linphone_gtk_in_call_view_set_in_call(LinphoneCall *call){
GtkWidget *callee=linphone_gtk_get_widget(callview,"in_call_uri");
GtkWidget *duration=linphone_gtk_get_widget(callview,"in_call_duration");
guint taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
gboolean in_conf=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
gboolean in_conf=(linphone_call_get_conference(call) != NULL);
GtkWidget *call_stats=(GtkWidget*)g_object_get_data(G_OBJECT(callview),"call_stats");
linphone_gtk_in_call_show_video(call);
@ -854,7 +853,7 @@ void linphone_gtk_in_call_view_terminate(LinphoneCall *call, const char *error_m
video_window=(GtkWidget*)g_object_get_data(G_OBJECT(callview),"video_window");
status=linphone_gtk_get_widget(callview,"in_call_status");
taskid=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(callview),"taskid"));
in_conf=linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call));
in_conf=(linphone_call_get_conference(call) != NULL);
if (video_window) gtk_widget_destroy(video_window);
if (status==NULL) return;
if (error_msg==NULL)

View file

@ -198,6 +198,7 @@ LINPHONE_PUBLIC void linphone_gtk_enable_transfer_button(LinphoneCore *lc, gbool
LINPHONE_PUBLIC void linphone_gtk_enable_conference_button(LinphoneCore *lc, gboolean value);
LINPHONE_PUBLIC void linphone_gtk_set_in_conference(LinphoneCall *call);
LINPHONE_PUBLIC void linphone_gtk_unset_from_conference(LinphoneCall *call);
LINPHONE_PUBLIC bool_t linphone_gtk_call_is_in_conference_view(LinphoneCall *call);
LINPHONE_PUBLIC void linphone_gtk_terminate_conference_participant(LinphoneCall *call);
LINPHONE_PUBLIC void linphone_gtk_in_call_view_show_encryption(LinphoneCall *call);
LINPHONE_PUBLIC void linphone_gtk_in_call_view_hide_encryption(LinphoneCall *call);

View file

@ -814,7 +814,7 @@ static void linphone_gtk_update_call_buttons(LinphoneCall *call){
conf_frame=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"conf_frame");
if(conf_frame==NULL){
linphone_gtk_enable_transfer_button(lc,call_list_size>1);
linphone_gtk_enable_conference_button(lc,call_list_size>1);
linphone_gtk_enable_conference_button(lc,call_list_size>0);
} else {
linphone_gtk_enable_transfer_button(lc,FALSE);
linphone_gtk_enable_conference_button(lc,FALSE);
@ -1929,13 +1929,22 @@ void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){
void linphone_gtk_refer_received(LinphoneCore *lc, const char *refer_to){
GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
linphone_gtk_get_main_window(), "uribar"));
char *text;
linphone_gtk_notify(NULL,NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
g_free(text);
gtk_entry_set_text(uri_bar, refer_to);
linphone_gtk_start_call(linphone_gtk_get_main_window());
char method[20] = "";
LinphoneAddress *addr = linphone_address_new(refer_to);
if(addr) {
const char *tmp = linphone_address_get_method_param(addr);
strncpy(method, tmp, sizeof(20));
linphone_address_destroy(addr);
}
if(strlen(method) == 0 || strcmp(method, "INVITE") == 0) {
GtkEntry * uri_bar =GTK_ENTRY(linphone_gtk_get_widget(
linphone_gtk_get_main_window(), "uribar"));
char *text;
linphone_gtk_notify(NULL,NULL,(text=ms_strdup_printf(_("We are transferred to %s"),refer_to)));
g_free(text);
gtk_entry_set_text(uri_bar, refer_to);
linphone_gtk_start_call(linphone_gtk_get_main_window());
}
}
static void linphone_gtk_check_soundcards(void){

View file

@ -106,6 +106,7 @@ void sal_address_set_secure(SalAddress *addr, bool_t enabled);
SalTransport sal_address_get_transport(const SalAddress* addr);
const char* sal_address_get_transport_name(const SalAddress* addr);
const char *sal_address_get_method_param(const SalAddress *addr);
void sal_address_set_display_name(SalAddress *addr, const char *display_name);
void sal_address_set_username(SalAddress *addr, const char *username);
@ -118,6 +119,7 @@ void sal_address_destroy(SalAddress *u);
void sal_address_set_param(SalAddress *u,const char* name,const char* value);
void sal_address_set_transport(SalAddress* addr,SalTransport transport);
void sal_address_set_transport_name(SalAddress* addr,const char* transport);
void sal_address_set_method_param(SalAddress *addr, const char *method);
void sal_address_set_params(SalAddress *addr, const char *params);
void sal_address_set_uri_params(SalAddress *addr, const char *params);
bool_t sal_address_is_ipv6(const SalAddress *addr);

View file

@ -273,8 +273,17 @@ public interface LinphoneCall {
* @param verified true when displayed SAS is correct
*/
void setAuthenticationTokenVerified(boolean verified);
/**
* Checks wether the call is part of a conferece.
* @return A boolean
*/
boolean isInConference();
/**
* Get the pointer on the C conference instance associated to that call.
* @return A positive value if the call is part of a conference, 0 if not.
*/
long getConference();
/**
* Indicates whether an operation is in progress at the media side.

View file

@ -0,0 +1,39 @@
/*
LinphoneConference.java
Copyright (C) 2015 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;
import org.linphone.core.LinphoneAddress;
import java.util.List;
/**
* Interface to manipulate a running conference
*/
public interface LinphoneConference {
/**
* Get the URIs of all participants of the conference
*/
public List<LinphoneAddress> getParticipants();
/**
* Remove a participant from the conference
* @param uri The URI of the participant to remove
* @return 0 if succeed, -1 if not.
*/
public int removeParticipant(LinphoneAddress uri);
}

View file

@ -1417,6 +1417,13 @@ public interface LinphoneCore {
* @returns the number of participants to the conference
**/
int getConferenceSize();
/**
* Return the value of the C pointer on the conference instance.
*
* That function can be used to test whether a conference is running.
* @return A positive value if a conference is running, 0 if not.
**/
LinphoneConference getConference();
/**
* Request recording of the conference into a supplied file path.

View file

@ -165,8 +165,11 @@ class LinphoneCallImpl implements LinphoneCall {
}
public boolean isInConference() {
LinphoneCallParamsImpl params = new LinphoneCallParamsImpl(getCurrentParamsCopy(nativePtr));
return params.localConferenceMode();
return getConference() != 0;
}
private native long getConference(long nativePtr);
public long getConference() {
return getConference(nativePtr);
}
public boolean mediaInProgress() { return mediaInProgress(nativePtr);}

View file

@ -0,0 +1,42 @@
/*
LinphoneConferenceImpl.java
Copyright (C) 2015 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;
import org.linphone.core.LinphoneConference;
import java.util.List;
public class LinphoneConferenceImpl implements LinphoneConference {
private final long nativePtr;
private LinphoneConferenceImpl(long nativePtr) {
this.nativePtr = nativePtr;
}
private native List<LinphoneAddress> getParticipants(long nativePtr);
public List<LinphoneAddress> getParticipants() {
return getParticipants(nativePtr);
}
private native int removeParticipant(long nativePtr, LinphoneAddress uri);
public int removeParticipant(LinphoneAddress uri) {
return removeParticipant(nativePtr, uri);
}
}

View file

@ -721,6 +721,10 @@ class LinphoneCoreImpl implements LinphoneCore {
public synchronized int getConferenceSize() {
return getConferenceSize(nativePtr);
}
private native LinphoneConference getConference(long nativePtr);
public synchronized LinphoneConference getConference() {
return getConference(nativePtr);
}
private native int getCallsNb(long nativePtr);
public synchronized int getCallsNb() {
return getCallsNb(nativePtr);

View file

@ -723,7 +723,7 @@ static void call_with_ipv6(void) {
static void file_transfer_message_rcs_to_external_body_client(void) {
if (transport_supported(LinphoneTransportTls)) {
LinphoneCoreManager* marie = linphone_core_manager_init( "marie_rc");
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneChatRoom* chat_room;
LinphoneChatMessage* message;
LinphoneChatMessageCbs *cbs;
@ -732,13 +732,13 @@ static void file_transfer_message_rcs_to_external_body_client(void) {
size_t file_size;
char *send_filepath = bc_tester_res("images/nowebcamCIF.jpg");
char *receive_filepath = bc_tester_file("receive_file.dump");
LinphoneCoreManager* pauline = linphone_core_manager_init( "pauline_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_proxy_config_set_custom_header(marie->lc->default_proxy, "Accept", "application/sdp");
linphone_core_manager_start(marie, "marie_rc", TRUE);
linphone_core_manager_start(marie, TRUE);
linphone_proxy_config_set_custom_header(pauline->lc->default_proxy, "Accept", "application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml");
linphone_core_manager_start(pauline, "pauline_rc", TRUE);
linphone_core_manager_start(pauline, TRUE);
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -823,14 +823,14 @@ void send_file_transfer_message_using_external_body_url(LinphoneCoreManager *mar
static void file_transfer_message_external_body_to_external_body_client(void) {
if (transport_supported(LinphoneTransportTls)) {
LinphoneCoreManager* marie = linphone_core_manager_init( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_init( "pauline_rc");
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_proxy_config_set_custom_header(marie->lc->default_proxy, "Accept", "application/sdp");
linphone_core_manager_start(marie, "marie_rc", TRUE);
linphone_core_manager_start(marie, TRUE);
linphone_proxy_config_set_custom_header(pauline->lc->default_proxy, "Accept", "application/sdp");
linphone_core_manager_start(pauline, "pauline_rc", TRUE);
linphone_core_manager_start(pauline, TRUE);
reset_counters(&marie->stat);
reset_counters(&pauline->stat);
@ -847,14 +847,14 @@ static void file_transfer_message_external_body_to_external_body_client(void) {
static void file_transfer_message_external_body_to_rcs_client(void) {
if (transport_supported(LinphoneTransportTls)) {
LinphoneCoreManager* marie = linphone_core_manager_init( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_init( "pauline_rc");
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
linphone_proxy_config_set_custom_header(marie->lc->default_proxy, "Accept", "application/sdp");
linphone_core_manager_start(marie, "marie_rc", TRUE);
linphone_core_manager_start(marie, TRUE);
linphone_proxy_config_set_custom_header(pauline->lc->default_proxy, "Accept", "application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml");
linphone_core_manager_start(pauline, "pauline_rc", TRUE);
linphone_core_manager_start(pauline, TRUE);
reset_counters(&marie->stat);
reset_counters(&pauline->stat);

View file

@ -256,6 +256,12 @@ typedef struct _LinphoneCoreManager {
int number_of_cunit_error_at_creation;
} LinphoneCoreManager;
typedef struct _LinphoneConferenceServer {
LinphoneCoreManager base;
LinphoneCall *first_call;
LinphoneCoreVTable *vtable;
} LinphoneConferenceServer;
typedef struct _LinphoneCallTestParams {
LinphoneCallParams *base;
bool_t sdp_removal;
@ -265,11 +271,12 @@ typedef struct _LinphoneCallTestParams {
void liblinphone_tester_add_suites(void);
LinphoneCoreManager* linphone_core_manager_init(const char* rc_file);
void linphone_core_manager_start(LinphoneCoreManager *mgr, const char* rc_file, int check_for_proxies);
void linphone_core_manager_init(LinphoneCoreManager *mgr, const char* rc_file);
void linphone_core_manager_start(LinphoneCoreManager *mgr, int check_for_proxies);
LinphoneCoreManager* linphone_core_manager_new2(const char* rc_file, int check_for_proxies);
LinphoneCoreManager* linphone_core_manager_new(const char* rc_file);
void linphone_core_manager_stop(LinphoneCoreManager *mgr);
void linphone_core_manager_uninit(LinphoneCoreManager *mgr);
void linphone_core_manager_destroy(LinphoneCoreManager* mgr);
void reset_counters( stats* counters);
@ -356,6 +363,9 @@ void liblinphone_tester_uninit(void);
int liblinphone_tester_set_log_file(const char *filename);
bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, LinphoneIceState state);
LinphoneConferenceServer* linphone_conference_server_new(const char *rc_file);
void linphone_conference_server_destroy(LinphoneConferenceServer *conf_srv);
extern const char *liblinphone_tester_mire_id;
FILE *sip_start(const char *senario, const char* dest_username, LinphoneAddress* dest_addres);

View file

@ -177,8 +177,7 @@ static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_
incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallOutgoingEarlyMedia);
}
static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, LinphoneCoreManager* laure) {
static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, LinphoneCoreManager* laure, LinphoneCoreManager *focus) {
stats initial_marie_stat;
stats initial_pauline_stat;
stats initial_laure_stat;
@ -187,9 +186,14 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag
LinphoneCall* pauline_called_by_marie;
LinphoneCall* marie_call_laure;
const MSList* calls;
bool_t is_remote_conf;
MSList* lcs=ms_list_append(NULL,marie->lc);
lcs=ms_list_append(lcs,pauline->lc);
lcs=ms_list_append(lcs,laure->lc);
if(focus) lcs=ms_list_append(lcs,focus->lc);
is_remote_conf = (strcmp(lp_config_get_string(marie->lc->config, "misc", "conference_type", "local"), "remote") == 0);
if(is_remote_conf) BC_ASSERT_PTR_NOT_NULL(focus);
BC_ASSERT_TRUE(call(marie,pauline));
marie_call_pauline=linphone_core_get_current_call(marie->lc);
@ -205,12 +209,23 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag
BC_ASSERT_PTR_NOT_NULL_FATAL(marie_call_laure);
linphone_core_add_to_conference(marie->lc,marie_call_laure);
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1,5000));
if(!is_remote_conf) {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1,5000));
} else {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,initial_marie_stat.number_of_LinphoneCallStreamsRunning+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallConnected,initial_marie_stat.number_of_LinphoneTransferCallConnected+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,initial_marie_stat.number_of_LinphoneCallEnd+1,5000));
}
linphone_core_add_to_conference(marie->lc,marie_call_pauline);
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallResuming,initial_marie_stat.number_of_LinphoneCallResuming+1,2000));
if(!is_remote_conf) {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallResuming,initial_marie_stat.number_of_LinphoneCallResuming+1,2000));
} else {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallConnected,initial_marie_stat.number_of_LinphoneTransferCallConnected+2,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,initial_marie_stat.number_of_LinphoneCallEnd+2,5000));
}
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,initial_pauline_stat.number_of_LinphoneCallStreamsRunning+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,initial_laure_stat.number_of_LinphoneCallStreamsRunning+1,2000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,initial_marie_stat.number_of_LinphoneCallStreamsRunning+2,3000));
@ -218,7 +233,11 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag
BC_ASSERT_TRUE(linphone_core_is_in_conference(marie->lc));
BC_ASSERT_EQUAL(linphone_core_get_conference_size(marie->lc),3, int, "%d");
BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc));
if(!is_remote_conf) {
BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc));
} else {
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc));
}
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(pauline->lc));
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(laure->lc));
@ -241,9 +260,9 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag
linphone_core_terminate_conference(marie->lc);
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,is_remote_conf?2:1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,is_remote_conf?3:1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,is_remote_conf?2:1,10000));
ms_list_free(lcs);
}
@ -252,7 +271,7 @@ static void simple_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
simple_conference_base(marie,pauline,laure);
simple_conference_base(marie,pauline,laure, NULL);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
@ -299,7 +318,7 @@ static void simple_encrypted_conference_with_ice(LinphoneMediaEncryption mode) {
linphone_core_set_media_encryption(pauline->lc,mode);
linphone_core_set_media_encryption(laure->lc,mode);
simple_conference_base(marie,pauline,laure);
simple_conference_base(marie,pauline,laure,NULL);
} else {
ms_warning("No [%s] support available",linphone_media_encryption_to_string(mode));
BC_PASS("Passed");
@ -546,11 +565,7 @@ static void call_transfer_existing_call_outgoing_call(void) {
ms_list_free(lcs);
}
static void eject_from_3_participants_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
static void eject_from_3_participants_conference(LinphoneCoreManager *marie, LinphoneCoreManager *pauline, LinphoneCoreManager *laure, LinphoneCoreManager *focus) {
stats initial_marie_stat;
stats initial_pauline_stat;
stats initial_laure_stat;
@ -558,10 +573,15 @@ static void eject_from_3_participants_conference(void) {
LinphoneCall* marie_call_pauline;
LinphoneCall* pauline_called_by_marie;
LinphoneCall* marie_call_laure;
bool_t is_remote_conf;
MSList* lcs=ms_list_append(NULL,marie->lc);
lcs=ms_list_append(lcs,pauline->lc);
lcs=ms_list_append(lcs,laure->lc);
if(focus) lcs=ms_list_append(lcs,focus->lc);
is_remote_conf = (strcmp(lp_config_get_string(marie->lc->config, "misc", "conference_type", "local"), "remote") == 0);
if(is_remote_conf) BC_ASSERT_PTR_NOT_NULL(focus);
BC_ASSERT_TRUE(call(marie,pauline));
marie_call_pauline=linphone_core_get_current_call(marie->lc);
pauline_called_by_marie=linphone_core_get_current_call(pauline->lc);
@ -577,11 +597,24 @@ static void eject_from_3_participants_conference(void) {
BC_ASSERT_PTR_NOT_NULL_FATAL(marie_call_laure);
linphone_core_add_to_conference(marie->lc,marie_call_laure);
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1,5000));
if(!is_remote_conf) BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallUpdating,initial_marie_stat.number_of_LinphoneCallUpdating+1,5000));
else {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallConnected,initial_marie_stat.number_of_LinphoneTransferCallConnected+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,initial_marie_stat.number_of_LinphoneCallEnd+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,initial_laure_stat.number_of_LinphoneCallEnd+1,5000));
}
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_conference(marie->lc));
linphone_core_add_to_conference(marie->lc,marie_call_pauline);
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallResuming,initial_marie_stat.number_of_LinphoneCallResuming+1,2000));
if(!is_remote_conf) BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallResuming,initial_marie_stat.number_of_LinphoneCallResuming+1,2000));
else {
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallConnected,initial_marie_stat.number_of_LinphoneTransferCallConnected+2,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,initial_marie_stat.number_of_LinphoneCallEnd+2,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,initial_pauline_stat.number_of_LinphoneCallEnd+1,5000));
}
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallStreamsRunning,initial_pauline_stat.number_of_LinphoneCallStreamsRunning+1,5000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,initial_laure_stat.number_of_LinphoneCallStreamsRunning+1,2000));
@ -590,25 +623,49 @@ static void eject_from_3_participants_conference(void) {
BC_ASSERT_TRUE(linphone_core_is_in_conference(marie->lc));
BC_ASSERT_EQUAL(linphone_core_get_conference_size(marie->lc),3, int, "%d");
BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc));
if(!is_remote_conf) BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc));
linphone_core_remove_from_conference(marie->lc, marie_call_pauline);
if(!is_remote_conf) linphone_core_remove_from_conference(marie->lc, marie_call_pauline);
else {
LinphoneConference *conference = linphone_core_get_conference(marie->lc);
const LinphoneAddress *uri = linphone_call_get_remote_address(marie_call_pauline);
linphone_conference_remove_participant(conference, uri);
}
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPausedByRemote,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,3,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,5,10000));
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc));
BC_ASSERT_EQUAL(ms_list_size(linphone_core_get_calls(marie->lc)), 2, int, "%d");
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(pauline->lc));
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(laure->lc));
end_call(laure, marie);
end_call(pauline, marie);
if(!is_remote_conf) {
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPausedByRemote,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallStreamsRunning,3,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,5,10000));
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(marie->lc));
BC_ASSERT_EQUAL(ms_list_size(linphone_core_get_calls(marie->lc)), 2, int, "%d");
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(pauline->lc));
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call(laure->lc));
} else {
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,initial_pauline_stat.number_of_LinphoneCallEnd+2,5000));
}
if(!is_remote_conf) {
end_call(laure, marie);
end_call(pauline, marie);
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,10000));
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,1,10000));
} else {
linphone_core_terminate_conference(marie->lc);
BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,initial_laure_stat.number_of_LinphoneCallEnd+2,3000));
BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,initial_marie_stat.number_of_LinphoneCallEnd+3,3000));
}
ms_list_free(lcs);
}
static void eject_from_3_participants_local_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
eject_from_3_participants_conference(marie, pauline, laure, NULL);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
@ -695,22 +752,87 @@ static void eject_from_4_participants_conference(void) {
linphone_core_manager_destroy(laure);
linphone_core_manager_destroy(michelle);
}
void simple_remote_conference(void) {
LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
LinphoneCoreManager *laure = linphone_core_manager_new("laure_rc");
LinphoneConferenceServer *focus = linphone_conference_server_new("conference_focus_rc");
LpConfig *marie_config = linphone_core_get_config(marie->lc);
LinphoneProxyConfig *focus_proxy_config = linphone_core_get_default_proxy_config(((LinphoneCoreManager *)focus)->lc);
LinphoneProxyConfig *laure_proxy_config = linphone_core_get_default_proxy_config(((LinphoneCoreManager *)laure)->lc);
const char *laure_proxy_uri = linphone_proxy_config_get_server_addr(laure_proxy_config);
const char *focus_uri = linphone_proxy_config_get_identity(focus_proxy_config);
int laure_n_register = laure->stat.number_of_LinphoneRegistrationOk;
MSList *lcs = NULL;
lp_config_set_string(marie_config, "misc", "conference_type", "remote");
lp_config_set_string(marie_config, "misc", "conference_focus_addr", focus_uri);
linphone_proxy_config_edit(laure_proxy_config);
linphone_proxy_config_set_route(laure_proxy_config, laure_proxy_uri);
linphone_proxy_config_done(laure_proxy_config);
lcs = ms_list_append(lcs, laure->lc);
BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneRegistrationOk, laure_n_register+1, 5000));
ms_list_free(lcs);
simple_conference_base(marie, pauline, laure, (LinphoneCoreManager *)focus);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
linphone_conference_server_destroy(focus);
}
void eject_from_3_participants_remote_conference(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
LinphoneConferenceServer *focus = linphone_conference_server_new("conference_focus_rc");
LpConfig *marie_config = linphone_core_get_config(marie->lc);
LinphoneProxyConfig *focus_proxy_config = linphone_core_get_default_proxy_config(((LinphoneCoreManager *)focus)->lc);
LinphoneProxyConfig *laure_proxy_config = linphone_core_get_default_proxy_config(((LinphoneCoreManager *)laure)->lc);
const char *laure_proxy_uri = linphone_proxy_config_get_server_addr(laure_proxy_config);
const char *focus_uri = linphone_proxy_config_get_identity(focus_proxy_config);
int laure_n_register = laure->stat.number_of_LinphoneRegistrationOk;
MSList *lcs = NULL;
lp_config_set_string(marie_config, "misc", "conference_type", "remote");
lp_config_set_string(marie_config, "misc", "conference_focus_addr", focus_uri);
linphone_proxy_config_edit(laure_proxy_config);
linphone_proxy_config_set_route(laure_proxy_config, laure_proxy_uri);
linphone_proxy_config_done(laure_proxy_config);
lcs = ms_list_append(lcs, laure->lc);
BC_ASSERT_TRUE(wait_for_list(lcs, &laure->stat.number_of_LinphoneRegistrationOk, laure_n_register+1, 5000));
ms_list_free(lcs);
eject_from_3_participants_conference(marie, pauline, laure, (LinphoneCoreManager *)focus);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
linphone_conference_server_destroy(focus);
}
test_t multi_call_tests[] = {
{ "Call waiting indication", call_waiting_indication },
{ "Call waiting indication with privacy", call_waiting_indication_with_privacy },
{ "Incoming call accepted when outgoing call in progress",incoming_call_accepted_when_outgoing_call_in_progress},
{ "Incoming call accepted when outgoing call in outgoing ringing",incoming_call_accepted_when_outgoing_call_in_outgoing_ringing},
{ "Incoming call accepted when outgoing call in outgoing ringing early media",incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_media},
{ "Incoming call accepted when outgoing call in progress", incoming_call_accepted_when_outgoing_call_in_progress},
{ "Incoming call accepted when outgoing call in outgoing ringing", incoming_call_accepted_when_outgoing_call_in_outgoing_ringing},
{ "Incoming call accepted when outgoing call in outgoing ringing early media", incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_media},
{ "Simple conference", simple_conference },
{ "Simple conference with ICE",simple_conference_with_ice},
{ "Simple conference with ICE", simple_conference_with_ice},
{ "Simple ZRTP conference with ICE",simple_zrtp_conference_with_ice},
{ "Eject from 3 participants conference", eject_from_3_participants_local_conference },
{ "Eject from 4 participants conference", eject_from_4_participants_conference },
{ "Simple call transfer", simple_call_transfer },
{ "Unattended call transfer", unattended_call_transfer },
{ "Unattended call transfer with error", unattended_call_transfer_with_error },
{ "Call transfer existing call outgoing call", call_transfer_existing_call_outgoing_call },
{ "Eject from 3 participants conference", eject_from_3_participants_conference },
{ "Eject from 4 participants conference", eject_from_4_participants_conference },
{ "Simple remote conference", simple_remote_conference },
{ "Eject from 3 participants in remote conference", eject_from_3_participants_remote_conference }
};
test_suite_t multi_call_test_suite = {"Multi call", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,

View file

@ -0,0 +1,26 @@
[sip]
default_proxy=0
sip_port=-1
sip_tcp_port=-1
sip_dtls_port=-1
sip_tls_port=-1
[rtp]
audio_rtp_port=18070-28000
text_rtp_port=39000-49000
video_rtp_port=28070-38000
[proxy_0]
reg_proxy=sip.example.org;transport=tcp
reg_route=sip.example.org;transport=tcp;lr
reg_identity="Focus" <sip:focus@sip.example.org>
reg_expires=3600
reg_sendregister=1
publish=0
[auth_info_0]
username=focus
userid=focus
passwd=secret
realm=sip.example.org

View file

@ -260,8 +260,7 @@ static void display_status(LinphoneCore *lc, const char *status){
ms_message("display_status(): %s",status);
}
LinphoneCoreManager* linphone_core_manager_init(const char* rc_file) {
LinphoneCoreManager* mgr= ms_new0(LinphoneCoreManager,1);
void linphone_core_manager_init(LinphoneCoreManager *mgr, const char* rc_file) {
char *rc_path = NULL;
char *hellopath = bc_tester_res("sounds/hello8000.wav");
mgr->number_of_cunit_error_at_creation = CU_get_number_of_failures();
@ -330,16 +329,14 @@ LinphoneCoreManager* linphone_core_manager_init(const char* rc_file) {
linphone_core_set_user_certificates_path(mgr->lc,bc_tester_get_writable_dir_prefix());
if (rc_path) ms_free(rc_path);
return mgr;
}
void linphone_core_manager_start(LinphoneCoreManager *mgr, const char* rc_file, int check_for_proxies) {
void linphone_core_manager_start(LinphoneCoreManager *mgr, int check_for_proxies) {
LinphoneProxyConfig* proxy;
int proxy_count;
/*BC_ASSERT_EQUAL(ms_list_size(linphone_core_get_proxy_config_list(lc)),proxy_count, int, "%d");*/
if (check_for_proxies && rc_file) /**/
if (check_for_proxies) /**/
proxy_count=ms_list_size(linphone_core_get_proxy_config_list(mgr->lc));
else
proxy_count=0;
@ -363,14 +360,16 @@ void linphone_core_manager_start(LinphoneCoreManager *mgr, const char* rc_file,
}
LinphoneCoreManager* linphone_core_manager_new( const char* rc_file) {
LinphoneCoreManager *manager = linphone_core_manager_init(rc_file);
linphone_core_manager_start(manager, rc_file, TRUE);
LinphoneCoreManager *manager = ms_new0(LinphoneCoreManager, 1);
linphone_core_manager_init(manager, rc_file);
linphone_core_manager_start(manager, TRUE);
return manager;
}
LinphoneCoreManager* linphone_core_manager_new2( const char* rc_file, int check_for_proxies) {
LinphoneCoreManager *manager = linphone_core_manager_init(rc_file);
linphone_core_manager_start(manager, rc_file, check_for_proxies);
LinphoneCoreManager *manager = ms_new0(LinphoneCoreManager, 1);
linphone_core_manager_init(manager, rc_file);
linphone_core_manager_start(manager, check_for_proxies);
return manager;
}
@ -381,7 +380,7 @@ void linphone_core_manager_stop(LinphoneCoreManager *mgr){
}
}
void linphone_core_manager_destroy(LinphoneCoreManager* mgr) {
void linphone_core_manager_uninit(LinphoneCoreManager *mgr) {
if (mgr->stat.last_received_chat_message) {
linphone_chat_message_unref(mgr->stat.last_received_chat_message);
}
@ -406,8 +405,12 @@ void linphone_core_manager_destroy(LinphoneCoreManager* mgr) {
if (mgr->identity) {
linphone_address_destroy(mgr->identity);
}
manager_count--;
}
void linphone_core_manager_destroy(LinphoneCoreManager* mgr) {
linphone_core_manager_uninit(mgr);
ms_free(mgr);
}
@ -668,4 +671,69 @@ bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, Linph
return video_enabled ? (realtime_text_enabled ? text_success && audio_success && video_success : audio_success && video_success) : realtime_text_enabled ? text_success && audio_success : audio_success;
}
void linphone_conference_server_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *msg) {
LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc);
LinphoneConferenceServer *conf_srv = (LinphoneConferenceServer *)vtable->user_data;
switch(cstate) {
case LinphoneCallIncomingReceived:
linphone_core_accept_call(lc, call);
break;
case LinphoneCallStreamsRunning:
if(linphone_call_get_conference(call) == NULL) {
linphone_core_add_to_conference(lc, call);
linphone_core_leave_conference(lc);
if(conf_srv->first_call == NULL) conf_srv->first_call = linphone_call_ref(call);
}
break;
case LinphoneCallEnd:
if(call == conf_srv->first_call) {
linphone_core_terminate_conference(lc);
linphone_call_unref(call);
conf_srv->first_call = NULL;
}
break;
default: break;
}
}
void linphone_conference_server_refer_received(LinphoneCore *core, const char *refer_to) {
char method[20];
LinphoneAddress *refer_to_addr = linphone_address_new(refer_to);
char *uri;
LinphoneCall *call;
if(refer_to_addr == NULL) return;
strncpy(method, linphone_address_get_method_param(refer_to_addr), sizeof(method));
if(strcmp(method, "BYE") == 0) {
linphone_address_clean(refer_to_addr);
uri = linphone_address_as_string_uri_only(refer_to_addr);
call = linphone_core_find_call_from_uri(core, uri);
if(call) linphone_core_terminate_call(core, call);
ms_free(uri);
}
linphone_address_destroy(refer_to_addr);
}
LinphoneConferenceServer* linphone_conference_server_new(const char *rc_file) {
LinphoneConferenceServer *conf_srv = (LinphoneConferenceServer *)ms_new0(LinphoneConferenceServer, 1);
LinphoneCoreManager *lm = (LinphoneCoreManager *)conf_srv;
conf_srv->vtable = linphone_core_v_table_new();
conf_srv->vtable->call_state_changed = linphone_conference_server_call_state_changed;
conf_srv->vtable->refer_received = linphone_conference_server_refer_received;
conf_srv->vtable->user_data = conf_srv;
linphone_core_manager_init(lm, rc_file);
linphone_core_add_listener(lm->lc, conf_srv->vtable);
linphone_core_manager_start(lm, TRUE);
return conf_srv;
}
void linphone_conference_server_destroy(LinphoneConferenceServer *conf_srv) {
linphone_core_manager_uninit((LinphoneCoreManager *)conf_srv);
linphone_core_v_table_destroy(conf_srv->vtable);
ms_free(conf_srv);
}