diff --git a/build/android/Android.mk b/build/android/Android.mk
index 5c99892f2..2abfb5eb0 100755
--- a/build/android/Android.mk
+++ b/build/android/Android.mk
@@ -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 \
diff --git a/console/commands.c b/console/commands.c
index f2c316325..cf462b990 100644
--- a/console/commands.c
+++ b/console/commands.c
@@ -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;
diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt
index 8ca65da17..f53b41c06 100644
--- a/coreapi/CMakeLists.txt
+++ b/coreapi/CMakeLists.txt
@@ -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)
diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am
index d9ce1d1f4..e24eba457 100644
--- a/coreapi/Makefile.am
+++ b/coreapi/Makefile.am
@@ -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 \
diff --git a/coreapi/address.c b/coreapi/address.c
index 1299a5351..79ed08d60 100644
--- a/coreapi/address.c
+++ b/coreapi/address.c
@@ -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.
**/
diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c
index 2546e7850..b6ed5b77e 100644
--- a/coreapi/bellesip_sal/sal_address_impl.c
+++ b/coreapi/bellesip_sal/sal_address_impl.c
@@ -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));
}
diff --git a/coreapi/bellesip_sal/sal_op_call_transfer.c b/coreapi/bellesip_sal/sal_op_call_transfer.c
index 84e529091..ffc4b945a 100644
--- a/coreapi/bellesip_sal/sal_op_call_transfer.c
+++ b/coreapi/bellesip_sal/sal_op_call_transfer.c
@@ -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);
diff --git a/coreapi/call_params.h b/coreapi/call_params.h
index e3e6d1ee4..e536280a4 100644
--- a/coreapi/call_params.h
+++ b/coreapi/call_params.h
@@ -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.
+ * 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.
**/
diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c
index 8b5f8eff4..af1cfb919 100644
--- a/coreapi/callbacks.c
+++ b/coreapi/callbacks.c
@@ -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);
}
diff --git a/coreapi/conference.c b/coreapi/conference.c
deleted file mode 100644
index 66898d9a2..000000000
--- a/coreapi/conference.c
+++ /dev/null
@@ -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(¶ms);
- 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;
-}
-
-/**
- * @}
-**/
-
diff --git a/coreapi/conference.cc b/coreapi/conference.cc
new file mode 100644
index 000000000..6cc888a2f
--- /dev/null
+++ b/coreapi/conference.cc
@@ -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
+#include
+#include
+#include
+
+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 &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 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::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(¶ms);
+}
+
+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 participants = ((Conference *)obj)->getParticipants();
+ MSList *participants_list = NULL;
+ for(list::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;
+ }
+}
diff --git a/coreapi/conference.h b/coreapi/conference.h
new file mode 100644
index 000000000..b2996ea89
--- /dev/null
+++ b/coreapi/conference.h
@@ -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
diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c
index 087d8fe27..a13c65237 100644
--- a/coreapi/linphonecall.c
+++ b/coreapi/linphonecall.c
@@ -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){
}
}
}
-
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 7d2440601..de27eaa38 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -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;
+}
diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h
index edcb6b751..f42930fc4 100644
--- a/coreapi/linphonecore.h
+++ b/coreapi/linphonecore.h
@@ -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
diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc
index b3547355e..de7408496 100644
--- a/coreapi/linphonecore_jni.cc
+++ b/coreapi/linphonecore_jni.cc
@@ -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, "", "(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, "", "(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, "", "(J)");
+ jmethodID addr_list_constructor = env->GetMethodID(addr_list_class, "", "(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);
+}
diff --git a/coreapi/private.h b/coreapi/private.h
index ca1fd76f7..3cbe173de 100644
--- a/coreapi/private.h
+++ b/coreapi/private.h
@@ -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);
diff --git a/coreapi/vtables.c b/coreapi/vtables.c
index dd146598c..9692240e2 100644
--- a/coreapi/vtables.c
+++ b/coreapi/vtables.c
@@ -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) {
diff --git a/gtk/conference.c b/gtk/conference.c
index 175cfc482..9073d629e 100644
--- a/gtk/conference.c
+++ b/gtk/conference.c
@@ -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);
+}
diff --git a/gtk/incall_view.c b/gtk/incall_view.c
index 5c7620840..d37602201 100644
--- a/gtk/incall_view.c
+++ b/gtk/incall_view.c
@@ -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)
diff --git a/gtk/linphone.h b/gtk/linphone.h
index 4e9d7dcf6..5ef4b2ea9 100644
--- a/gtk/linphone.h
+++ b/gtk/linphone.h
@@ -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);
diff --git a/gtk/main.c b/gtk/main.c
index f9f835d59..9ba13b2d9 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -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){
diff --git a/include/sal/sal.h b/include/sal/sal.h
index 21e311429..a5bb5e999 100644
--- a/include/sal/sal.h
+++ b/include/sal/sal.h
@@ -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);
diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java
index ad10b96b1..3ab28d437 100644
--- a/java/common/org/linphone/core/LinphoneCall.java
+++ b/java/common/org/linphone/core/LinphoneCall.java
@@ -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.
diff --git a/java/common/org/linphone/core/LinphoneConference.java b/java/common/org/linphone/core/LinphoneConference.java
new file mode 100644
index 000000000..9fb25d518
--- /dev/null
+++ b/java/common/org/linphone/core/LinphoneConference.java
@@ -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 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);
+}
diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java
index bb36434da..ef5112622 100644
--- a/java/common/org/linphone/core/LinphoneCore.java
+++ b/java/common/org/linphone/core/LinphoneCore.java
@@ -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.
diff --git a/java/impl/org/linphone/core/LinphoneCallImpl.java b/java/impl/org/linphone/core/LinphoneCallImpl.java
index f04ce86d8..28f6302d1 100644
--- a/java/impl/org/linphone/core/LinphoneCallImpl.java
+++ b/java/impl/org/linphone/core/LinphoneCallImpl.java
@@ -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);}
diff --git a/java/impl/org/linphone/core/LinphoneConferenceImpl.java b/java/impl/org/linphone/core/LinphoneConferenceImpl.java
new file mode 100644
index 000000000..2a3519302
--- /dev/null
+++ b/java/impl/org/linphone/core/LinphoneConferenceImpl.java
@@ -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 getParticipants(long nativePtr);
+ public List getParticipants() {
+ return getParticipants(nativePtr);
+ }
+
+ private native int removeParticipant(long nativePtr, LinphoneAddress uri);
+ public int removeParticipant(LinphoneAddress uri) {
+ return removeParticipant(nativePtr, uri);
+ }
+}
diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java
index eb23d6002..1147305b2 100644
--- a/java/impl/org/linphone/core/LinphoneCoreImpl.java
+++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java
@@ -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);
diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c
index bb6329556..80d21483f 100644
--- a/tester/flexisip_tester.c
+++ b/tester/flexisip_tester.c
@@ -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);
diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h
index b9aa9052d..d16134b4e 100644
--- a/tester/liblinphone_tester.h
+++ b/tester/liblinphone_tester.h
@@ -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);
diff --git a/tester/multi_call_tester.c b/tester/multi_call_tester.c
index c8021afc3..61135aecd 100644
--- a/tester/multi_call_tester.c
+++ b/tester/multi_call_tester.c
@@ -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,
diff --git a/tester/rcfiles/conference_focus_rc b/tester/rcfiles/conference_focus_rc
new file mode 100644
index 000000000..c0cdd5aaa
--- /dev/null
+++ b/tester/rcfiles/conference_focus_rc
@@ -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"
+reg_expires=3600
+reg_sendregister=1
+publish=0
+
+[auth_info_0]
+username=focus
+userid=focus
+passwd=secret
+realm=sip.example.org
+
diff --git a/tester/tester.c b/tester/tester.c
index ad3380993..40f1461e2 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -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);
+}