Commit 6e655d74 authored by François Grisez's avatar François Grisez

Add the ability to remove a participant from a remote conference

That new feature has not been tested yet neither with Telefonica's server nor
in liblinphone tester. That one does not still work because the LinphoneCore
instance simulating the conference server does not handle REFER with BYE requests.
parent 2fcf9d4f
......@@ -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);
......
......@@ -27,15 +27,34 @@
#include "conference.h"
#include <mediastreamer2/msvolume.h>
#include <typeinfo>
#include <list>
#include <algorithm>
namespace Linphone {
class Participant {
public:
Participant(LinphoneCall *call);
Participant(const Participant &src);
~Participant();
bool operator==(const Participant &src) const;
const LinphoneAddress *getUri() const {return m_uri;}
LinphoneCall *getCall() const {return m_call;}
void setCall(LinphoneCall *call) {m_call = call;}
private:
LinphoneAddress *m_uri;
LinphoneCall *m_call;
};
class Conference {
public:
Conference(LinphoneCore *core);
virtual ~Conference() {};
virtual int addCall(LinphoneCall *call) = 0;
virtual int removeCall(LinphoneCall *call) = 0;
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;
......@@ -47,7 +66,8 @@ public:
bool microphoneIsMuted() const {return m_isMuted;}
float getInputVolume() const;
virtual int getParticipantCount() const = 0;
virtual int getParticipantCount() const {return m_participants.size();}
const std::list<Participant> &getParticipants() const {return m_participants;}
virtual int startRecording(const char *path) = 0;
virtual int stopRecording() = 0;
......@@ -57,13 +77,118 @@ public:
virtual void onCallTerminating(LinphoneCall *call) {};
protected:
Participant *find_participant(const LinphoneAddress *uri);
LinphoneCore *m_core;
AudioStream *m_localParticipantStream;
bool m_isMuted;
std::list<Participant> m_participants;
};
class LocalConference: public Conference {
public:
LocalConference(LinphoneCore *core);
virtual ~LocalConference();
virtual int addParticipant(LinphoneCall *call);
virtual int removeParticipant(LinphoneCall *call);
virtual int removeParticipant(const LinphoneAddress *uri);
virtual int terminate();
virtual int enter();
virtual int leave();
virtual bool isIn() const {return m_localParticipantStream!=NULL;}
virtual int getParticipantCount() const;
virtual int startRecording(const char *path);
virtual int stopRecording();
void onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote);
void onCallStreamStopping(LinphoneCall *call);
void onCallTerminating(LinphoneCall *call);
private:
void addLocalEndpoint();
int remoteParticipantsCount();
void removeLocalEndpoint();
int removeFromConference(LinphoneCall *call, bool_t active);
int convertConferenceToCall();
static RtpProfile *sMakeDummyProfile(int samplerate);
MSAudioConference *m_conf;
MSAudioEndpoint *m_localEndpoint;
MSAudioEndpoint *m_recordEndpoint;
RtpProfile *m_localDummyProfile;
bool_t m_terminated;
};
class RemoteConference: public Conference {
public:
RemoteConference(LinphoneCore *core);
virtual ~RemoteConference();
virtual int addParticipant(LinphoneCall *call);
virtual int removeParticipant(LinphoneCall *call) {return -1;}
virtual int removeParticipant(const LinphoneAddress *uri);
virtual int terminate();
virtual int enter();
virtual int leave();
virtual bool isIn() const;
virtual int startRecording(const char *path) {return 0;}
virtual int stopRecording() {return 0;}
private:
enum State {
NotConnectedToFocus,
ConnectingToFocus,
ConnectedToFocus,
};
static const char *stateToString(State state);
void onFocusCallSateChanged(LinphoneCallState state);
void onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state);
static void callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message);
static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state);
const char *m_focusAddr;
char *m_focusContact;
LinphoneCall *m_focusCall;
State m_state;
LinphoneCoreVTable *m_vtable;
MSList *m_pendingCalls;
MSList *m_transferingCalls;
};
};
using namespace Linphone;
using namespace std;
Participant::Participant(LinphoneCall *call) {
m_uri = linphone_address_clone(linphone_call_get_remote_address(call));
m_call = linphone_call_ref(call);
}
Participant::Participant(const Participant &src) {
m_uri = linphone_address_clone(src.m_uri);
m_call = src.m_call ? linphone_call_ref(src.m_call) : NULL;
}
Participant::~Participant() {
linphone_address_unref(m_uri);
if(m_call) linphone_call_unref(m_call);
}
bool Participant::operator==(const Participant &src) const {
return linphone_address_equal(m_uri, src.m_uri);
}
Conference::Conference(LinphoneCore *core) :
m_core(core),
......@@ -71,16 +196,29 @@ Conference::Conference(LinphoneCore *core) :
m_isMuted(false) {
}
int Conference::addCall(LinphoneCall *call) {
int Conference::addParticipant(LinphoneCall *call) {
Participant participant(call);
m_participants.push_back(participant);
call->conf_ref = (LinphoneConference *)this;
return 0;
}
int Conference::removeCall(LinphoneCall *call) {
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);
......@@ -105,44 +243,17 @@ float Conference::getInputVolume() const {
return LINPHONE_VOLUME_DB_LOWEST;
}
Participant *Conference::find_participant(const LinphoneAddress *uri) {
list<Participant>::iterator it = m_participants.begin();
while(it!=m_participants.end()) {
if(linphone_address_equal(uri, it->getUri())) break;
it++;
}
if(it == m_participants.end()) return NULL;
else return &*it;
}
namespace Linphone {
class LocalConference: public Conference {
public:
LocalConference(LinphoneCore *core);
virtual ~LocalConference();
virtual int addCall(LinphoneCall *call);
virtual int removeCall(LinphoneCall *call);
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;
};
};
LocalConference::LocalConference(LinphoneCore *core): Conference(core),
m_conf(NULL),
......@@ -196,7 +307,7 @@ void LocalConference::addLocalEndpoint() {
ms_audio_conference_add_member(m_conf,m_localEndpoint);
}
int LocalConference::addCall(LinphoneCall *call) {
int LocalConference::addParticipant(LinphoneCall *call) {
if (call->current_params->in_conference){
ms_error("Already in conference");
return -1;
......@@ -227,7 +338,7 @@ int LocalConference::addCall(LinphoneCall *call) {
ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
return -1;
}
Conference::addCall(call);
Conference::addParticipant(call);
return 0;
}
......@@ -245,7 +356,7 @@ int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){
}
}
call->params->in_conference=FALSE;
Conference::removeCall(call);
Conference::removeParticipant(call);
str=linphone_call_get_remote_address_as_string(call);
ms_message("%s will be removed from conference", str);
......@@ -296,7 +407,7 @@ int LocalConference::convertConferenceToCall(){
return err;
}
int LocalConference::removeCall(LinphoneCall *call) {
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);
......@@ -316,6 +427,14 @@ int LocalConference::removeCall(LinphoneCall *call) {
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;
......@@ -427,47 +546,7 @@ void LocalConference::onCallTerminating(LinphoneCall *call) {
}
}
namespace Linphone {
class RemoteConference: public Conference {
public:
RemoteConference(LinphoneCore *core);
virtual ~RemoteConference();
virtual int addCall(LinphoneCall *call);
virtual int removeCall(LinphoneCall *call) {return 0;}
virtual int terminate();
virtual int enter();
virtual int leave();
virtual bool isIn() const;
virtual int getParticipantCount() const {return -1;}
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;
};
};
RemoteConference::RemoteConference(LinphoneCore *core):
Conference(core),
......@@ -492,12 +571,12 @@ RemoteConference::~RemoteConference() {
linphone_core_v_table_destroy(m_vtable);
}
int RemoteConference::addCall(LinphoneCall *call) {
int RemoteConference::addParticipant(LinphoneCall *call) {
LinphoneAddress *addr;
switch(m_state) {
case NotConnectedToFocus:
Conference::addCall(call);
Conference::addParticipant(call);
ms_message("Calling the conference focus (%s)", m_focusAddr);
addr = linphone_address_new(m_focusAddr);
if(addr) {
......@@ -506,16 +585,17 @@ int RemoteConference::addCall(LinphoneCall *call) {
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::addCall(call);
Conference::addParticipant(call);
m_pendingCalls = ms_list_append(m_pendingCalls, linphone_call_ref(call));
return 0;
case ConnectedToFocus:
Conference::addCall(call);
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;
......@@ -526,11 +606,39 @@ int RemoteConference::addCall(LinphoneCall *call) {
}
}
int RemoteConference::removeParticipant(const LinphoneAddress *uri) {
SalOp *op;
const char *from;
LinphoneAddress *refer_to;
int res;
switch(m_state) {
case ConnectedToFocus:
op = sal_op_new(m_core->sal);
from = sal_op_get_from(m_focusCall->op);
sal_op_set_from(op, from);
sal_op_set_to(op, m_focusContact);
refer_to = linphone_address_clone(uri);
linphone_address_set_header(refer_to, "method", "BYE");
res = sal_call_refer(op, linphone_address_as_string(refer_to));
linphone_address_unref(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::removeCall(m_focusCall);
Conference::removeParticipant(m_focusCall);
linphone_core_terminate_call(m_core, m_focusCall);
break;
default:
......@@ -615,7 +723,7 @@ void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) {
case LinphoneCallError:
case LinphoneCallEnd:
Conference::removeCall(m_focusCall);
Conference::removeParticipant(m_focusCall);
m_state = NotConnectedToFocus;
linphone_call_unref(m_focusCall);
m_focusCall = NULL;
......@@ -645,7 +753,7 @@ void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCal
case LinphoneCallError:
case LinphoneCallEnd:
Conference::removeCall(call);
Conference::removeParticipant(call);
m_pendingCalls = ms_list_remove(m_pendingCalls, call);
linphone_call_unref(call);
break;
......@@ -683,7 +791,6 @@ void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *tran
LinphoneConference *linphone_local_conference_new(LinphoneCore *core) {
return (LinphoneConference *) new LocalConference(core);
}
......@@ -696,27 +803,31 @@ void linphone_conference_free(LinphoneConference *obj) {
delete (Conference *)obj;
}
int linphone_conference_add_call(LinphoneConference *obj, LinphoneCall *call) {
return ((Conference *)obj)->addCall(call);
int linphone_conference_add_participant(LinphoneConference *obj, LinphoneCall *call) {
return ((Conference *)obj)->addParticipant(call);
}
int linphone_conference_remove_call(LinphoneConference *obj, LinphoneCall *call) {
return ((Conference *)obj)->removeCall(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_add_local_participant(LinphoneConference *obj) {
int linphone_conference_enter(LinphoneConference *obj) {
return ((Conference *)obj)->enter();
}
int linphone_conference_remove_local_participant(LinphoneConference *obj) {
int linphone_conference_leave(LinphoneConference *obj) {
return ((Conference *)obj)->leave();
}
bool_t linphone_conference_local_participant_is_in(const LinphoneConference *obj) {
bool_t linphone_conference_is_in(const LinphoneConference *obj) {
return ((Conference *)obj)->isIn() ? TRUE : FALSE;
}
......@@ -740,6 +851,16 @@ int linphone_conference_get_participant_count(const LinphoneConference *obj) {
return ((Conference *)obj)->getParticipantCount();
}
MSList *linphone_conference_get_participants(const LinphoneConference *obj) {
const list<Participant> participants = ((Conference *)obj)->getParticipants();
MSList *participants_list = NULL;
for(list<Participant>::const_iterator it=participants.begin();it!=participants.end();it++) {
LinphoneAddress *uri = linphone_address_clone(it->getUri());
participants_list = ms_list_append(participants_list, uri);
}
return participants_list;
}
int linphone_conference_start_recording(LinphoneConference *obj, const char *path) {
return ((Conference *)obj)->startRecording(path);
}
......
......@@ -39,19 +39,22 @@ LinphoneConference *linphone_local_conference_new(LinphoneCore *core);
LinphoneConference *linphone_remote_conference_new(LinphoneCore *core);
void linphone_conference_free(LinphoneConference *obj);
int linphone_conference_add_call(LinphoneConference *obj, LinphoneCall *call);
int linphone_conference_remove_call(LinphoneConference *obj, LinphoneCall *call);
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_add_local_participant(LinphoneConference *obj);
int linphone_conference_remove_local_participant(LinphoneConference *obj);
bool_t linphone_conference_local_participant_is_in(const 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);
......
......@@ -7410,7 +7410,7 @@ int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call) {
return -1;
}
}
return linphone_conference_add_call(lc->conf_ctx, call);
return linphone_conference_add_participant(lc->conf_ctx, call);
}
int linphone_core_add_all_to_conference(LinphoneCore *lc) {
......@@ -7427,7 +7427,7 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) {
}
int linphone_core_remove_from_conference(LinphoneCore *lc, LinphoneCall *call) {
if(lc->conf_ctx) return linphone_conference_remove_call(lc->conf_ctx, call);
if(lc->conf_ctx) return linphone_conference_remove_participant_with_call(lc->conf_ctx, call);
else return -1;
}
......@@ -7440,17 +7440,17 @@ int linphone_core_terminate_conference(LinphoneCore *lc) {
}
int linphone_core_enter_conference(LinphoneCore *lc) {
if(lc->conf_ctx) return linphone_conference_add_local_participant(lc->conf_ctx);
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_remove_local_participant(lc->conf_ctx);
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_local_participant_is_in(lc->conf_ctx);
if(lc->conf_ctx) return linphone_conference_is_in(lc->conf_ctx);
else return FALSE;
}
......
......@@ -3858,6 +3858,9 @@ 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.
*/
......@@ -3909,11 +3912,25 @@ LINPHONE_PUBLIC int linphone_core_start_conference_recording(LinphoneCore *lc, c
*/
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
*/
* 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);
/**
* @}
......
......@@ -178,7 +178,6 @@ static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_