Commit fdbf7871 authored by Ghislain MARY's avatar Ghislain MARY
Browse files

Fix audio conference.

parent 8aca567a
......@@ -35,6 +35,9 @@
#include "conference_private.h"
#include "c-wrapper/c-wrapper.h"
#include "call/call-p.h"
#include "conference/params/media-session-params-p.h"
#include "core/core-p.h"
// TODO: From coreapi. Remove me later.
#include "private.h"
......@@ -63,9 +66,8 @@ public:
~Participant() {
linphone_address_unref(m_uri);
#if 0
if(m_call) m_call->conf_ref = NULL;
#endif
if(m_call)
_linphone_call_set_conf_ref(m_call, nullptr);
}
const LinphoneAddress *getUri() const {
......@@ -208,7 +210,7 @@ private:
MSAudioEndpoint *m_localEndpoint;
MSAudioEndpoint *m_recordEndpoint;
RtpProfile *m_localDummyProfile;
//bool_t m_terminating;
bool_t m_terminating;
};
class RemoteConference: public Conference {
......@@ -262,124 +264,126 @@ using namespace Linphone;
using namespace std;
Conference::Conference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params):
Conference::Conference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params) :
m_core(core),
m_localParticipantStream(NULL),
m_localParticipantStream(nullptr),
m_isMuted(false),
m_currentParams(),
m_state(LinphoneConferenceStopped),
m_conference(conf) {
if(params) m_currentParams = *params;
if (params)
m_currentParams = *params;
}
int Conference::addParticipant(LinphoneCall *call) {
Participant *p =new Participant(call);
int Conference::addParticipant (LinphoneCall *call) {
Participant *p = new Participant(call);
m_participants.push_back(p);
#if 0
call->conf_ref = m_conference;
#endif
_linphone_call_set_conf_ref(call, m_conference);
return 0;
}
int Conference::removeParticipant(LinphoneCall *call) {
int Conference::removeParticipant (LinphoneCall *call) {
Participant *p = findParticipant(call);
if(p == NULL) return -1;
if (!p)
return -1;
delete p;
m_participants.remove(p);
return 0;
}
int Conference::removeParticipant(const LinphoneAddress *uri) {
int Conference::removeParticipant (const LinphoneAddress *uri) {
Participant *p = findParticipant(uri);
if(p == NULL) return -1;
if (!p)
return -1;
delete p;
m_participants.remove(p);
return 0;
}
int Conference::terminate() {
for(list<Participant *>::iterator it = m_participants.begin(); it!=m_participants.end(); it++) {
int Conference::terminate () {
for (auto it = m_participants.begin(); it != m_participants.end(); it++)
delete *it;
}
m_participants.clear();
return 0;
}
int Conference::muteMicrophone(bool val) {
if (val) {
int Conference::muteMicrophone (bool val) {
if (val)
audio_stream_set_mic_gain(m_localParticipantStream, 0);
} else {
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) ){
if (linphone_core_get_rtp_no_xmit_on_audio_mute(m_core))
audio_stream_mute_rtp(m_localParticipantStream, val);
}
m_isMuted=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);
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;
}
const char *Conference::stateToString(LinphoneConferenceState state) {
const char *Conference::stateToString (LinphoneConferenceState state) {
switch(state) {
case LinphoneConferenceStopped: return "Stopped";
case LinphoneConferenceStarting: return "Starting";
case LinphoneConferenceRunning: return "Ready";
case LinphoneConferenceStartingFailed: return "Startig failed";
default: return "Invalid state";
case LinphoneConferenceStopped:
return "Stopped";
case LinphoneConferenceStarting:
return "Starting";
case LinphoneConferenceRunning:
return "Ready";
case LinphoneConferenceStartingFailed:
return "Starting failed";
default:
return "Invalid state";
}
}
void Conference::setState(LinphoneConferenceState state) {
if(m_state != state) {
void Conference::setState (LinphoneConferenceState state) {
if (m_state != state) {
ms_message("Switching conference [%p] into state '%s'", this, stateToString(state));
m_state = state;
if(m_currentParams.m_stateChangedCb) {
if (m_currentParams.m_stateChangedCb)
m_currentParams.m_stateChangedCb(m_conference, state, m_currentParams.m_userData);
}
}
}
Conference::Participant *Conference::findParticipant(const LinphoneCall *call) const {
for(list<Participant *>::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
if((*it)->getCall() == call) return *it;
Conference::Participant *Conference::findParticipant (const LinphoneCall *call) const {
for (auto it = m_participants.begin(); it != m_participants.end(); it++) {
if ((*it)->getCall() == call)
return *it;
}
return NULL;
return nullptr;
}
Conference::Participant *Conference::findParticipant(const LinphoneAddress *uri) const {
for(list<Participant *>::const_iterator it=m_participants.begin(); it!=m_participants.end(); it++) {
if(linphone_address_equal((*it)->getUri(), uri)) return *it;
Conference::Participant *Conference::findParticipant (const LinphoneAddress *uri) const {
for (auto it = m_participants.begin(); it != m_participants.end(); it++) {
if (linphone_address_equal((*it)->getUri(), uri))
return *it;
}
return NULL;
return nullptr;
}
LocalConference::LocalConference(LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params):
LocalConference::LocalConference (LinphoneCore *core, LinphoneConference *conf, const Conference::Params *params) :
Conference(core, conf, params),
m_conf(NULL),
m_localEndpoint(NULL),
m_recordEndpoint(NULL),
m_localDummyProfile(NULL)/*,
m_terminating(FALSE)*/ {
m_conf(nullptr),
m_localEndpoint(nullptr),
m_recordEndpoint(nullptr),
m_localDummyProfile(nullptr),
m_terminating(FALSE) {
MSAudioConferenceParams ms_conf_params;
ms_conf_params.samplerate = lp_config_get_int(m_core->config, "sound","conference_rate",16000);
m_conf=ms_audio_conference_new(&ms_conf_params, core->factory);
m_state= LinphoneConferenceRunning;
ms_conf_params.samplerate = lp_config_get_int(m_core->config, "sound", "conference_rate", 16000);
m_conf = ms_audio_conference_new(&ms_conf_params, core->factory);
m_state = LinphoneConferenceRunning;
}
LocalConference::~LocalConference() {
......@@ -387,24 +391,24 @@ LocalConference::~LocalConference() {
ms_audio_conference_destroy(m_conf);
}
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);
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 */
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(m_core->factory, 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);
AudioStream *st = audio_stream_new(m_core->factory, 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,
......@@ -412,312 +416,294 @@ void LocalConference::addLocalEndpoint() {
65001,
0,
40,
NULL,
NULL,
nullptr,
nullptr,
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_message("conference: adding local endpoint");
ms_audio_conference_add_member(m_conf,m_localEndpoint);
_post_configure_audio_stream(st, m_core, FALSE);
m_localParticipantStream = st;
m_localEndpoint = ms_audio_endpoint_get_from_stream(st, FALSE);
ms_message("Conference: adding local endpoint");
ms_audio_conference_add_member(m_conf, m_localEndpoint);
}
int LocalConference::inviteAddresses (const list<const LinphoneAddress*> &addresses, const LinphoneCallParams *params) {
int LocalConference::inviteAddresses (const list<const LinphoneAddress *> &addresses, const LinphoneCallParams *params) {
for (const auto &address : addresses) {
LinphoneCall * call = linphone_core_get_call_by_remote_address2(m_core, address);
LinphoneCall *call = linphone_core_get_call_by_remote_address2(m_core, address);
if (!call) {
/*start a new call by indicating that it has to be put into the conference directlly*/
LinphoneCallParams * new_params = params ? linphone_call_params_copy(params) : linphone_core_create_call_params(m_core, NULL);
LinphoneCall *call;
/*toggle this flag so the call is immediately added to the conference upon acceptance*/
/* Start a new call by indicating that it has to be put into the conference directly */
LinphoneCallParams *new_params = params
? linphone_call_params_copy(params)
: linphone_core_create_call_params(m_core, nullptr);
/* Toggle this flag so the call is immediately added to the conference upon acceptance */
linphone_call_params_set_in_conference(new_params, TRUE);
linphone_call_params_enable_video(new_params, FALSE); /*turn off video as it is not supported for conferencing at this time*/
/* Turn off video as it is not supported for conferencing at this time */
linphone_call_params_enable_video(new_params, FALSE);
call = linphone_core_invite_address_with_params(m_core, address, new_params);
if (!call) {
if (!call)
ms_error("LocalConference::inviteAddresses(): could not invite participant");
}
linphone_call_params_unref(new_params);
}else{
/*there is already a call to this address, so simply join it to the local conference if not already done*/
} else {
/* There is already a call to this address, so simply join it to the local conference if not already done */
if (!linphone_call_params_get_in_conference(linphone_call_get_current_params(call)))
addParticipant(call);
}
/*if the local participant is not yet created, created it and it to the conference */
if (!m_localEndpoint) addLocalEndpoint();
/* If the local participant is not yet created, created it and it to the conference */
if (!m_localEndpoint)
addLocalEndpoint();
}
return 0;
}
int LocalConference::addParticipant(LinphoneCall *call) {
#if 0
if (linphone_call_params_get_in_conference(call->current_params)){
int LocalConference::addParticipant (LinphoneCall *call) {
if (linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) {
ms_error("Already in conference");
return -1;
}
if (call->state==LinphoneCallPaused){
linphone_call_params_set_in_conference(call->params, TRUE);
linphone_call_params_enable_video(call->params, FALSE);
if (linphone_call_get_state(call) == LinphoneCallPaused) {
const_cast<LinphonePrivate::MediaSessionParamsPrivate *>(
L_GET_PRIVATE(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams()))->setInConference(true);
const_cast<LinphonePrivate::MediaSessionParams *>(
L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams())->enableVideo(false);
linphone_call_resume(call);
}else if (call->state==LinphoneCallStreamsRunning){
} else if (linphone_call_get_state(call) == LinphoneCallStreamsRunning) {
LinphoneCallParams *params = linphone_core_create_call_params(m_core, call);
linphone_call_params_set_in_conference(params, TRUE);
linphone_call_params_enable_video(params, FALSE);
if (call->audiostream || call->videostream){
linphone_call_stop_media_streams(call); /*free the audio & video local resources*/
if (L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeAudio)
|| L_GET_PRIVATE_FROM_C_OBJECT(call)->getMediaStream(LinphoneStreamTypeVideo)) {
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_call_update(call,params);
if (call == linphone_core_get_current_call(m_core))
L_GET_PRIVATE_FROM_C_OBJECT(m_core)->setCurrentCall(nullptr);
/* This will trigger a reINVITE that will later redraw the streams */
/* FIXME: probably a bit too much to just redraw streams! */
linphone_call_update(call, params);
linphone_call_params_unref(params);
addLocalEndpoint();
}else{
ms_error("Call is in state %s, it cannot be added to the conference.",linphone_call_state_to_string(call->state));
} else {
ms_error("Call is in state %s, it cannot be added to the conference",
linphone_call_state_to_string(linphone_call_get_state(call)));
return -1;
}
return 0;
#else
return 0;
#endif
}
int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){
#if 0
int err=0;
char *str;
if (!linphone_call_params_get_in_conference(call->current_params)){
if (linphone_call_params_get_in_conference(call->params)){
int LocalConference::removeFromConference (LinphoneCall *call, bool_t active) {
if (!linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) {
if (linphone_call_params_get_in_conference(linphone_call_get_params(call))) {
ms_warning("Not (yet) in conference, be patient");
return -1;
}else{
ms_error("Not in a conference.");
} else {
ms_error("Not in a conference");
return -1;
}
}
linphone_call_params_set_in_conference(call->params, FALSE);
const_cast<LinphonePrivate::MediaSessionParamsPrivate *>(
L_GET_PRIVATE(L_GET_CPP_PTR_FROM_C_OBJECT(call)->getParams()))->setInConference(false);
str=linphone_call_get_remote_address_as_string(call);
char *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));
int err = 0;
if (active) {
LinphoneCallParams *params = linphone_call_params_copy(linphone_call_get_current_params(call));
linphone_call_params_set_in_conference(params, FALSE);
// reconnect local audio with this call
// Reconnect local audio with this call
if (isIn()){
ms_message("Leaving conference for reconnecting with unique call.");
ms_message("Leaving conference for reconnecting with unique call");
leave();
}
ms_message("Updating call to actually remove from conference");
err=linphone_call_update(call,params);
err = linphone_call_update(call, params);
linphone_call_params_unref(params);
} else{
} else {
ms_message("Pausing call to actually remove from conference");
err=_linphone_call_pause(call);
err = _linphone_call_pause(call);
}
return err;
#else
return 0;
#endif
}
int LocalConference::remoteParticipantsCount() {
int count=getSize();
if (count==0) return 0;
if (!m_localParticipantStream) return count;
return count -1;
int LocalConference::remoteParticipantsCount () {
int count = getSize();
if (count == 0)
return 0;
if (!m_localParticipantStream)
return count;
return count - 1;
}
int LocalConference::convertConferenceToCall(){
int err=0;
#if 0
bctbx_list_t *calls=m_core->calls;
if (remoteParticipantsCount()!=1){
ms_error("No unique call remaining in conference.");
int LocalConference::convertConferenceToCall () {
if (remoteParticipantsCount() != 1) {
ms_error("No unique call remaining in conference");
return -1;
}
while (calls) {
LinphoneCall *rc=(LinphoneCall*)calls->data;
calls=calls->next;
if (linphone_call_params_get_in_conference(linphone_call_get_params(rc))) { // not using current_param
bool_t active_after_removed=isIn();
err=removeFromConference(rc, active_after_removed);
break;
list<shared_ptr<LinphonePrivate::Call>> calls = L_GET_CPP_PTR_FROM_C_OBJECT(m_core)->getCalls();
for (auto it = calls.begin(); it != calls.end(); it++) {
shared_ptr<LinphonePrivate::Call> call(*it);
if (L_GET_PRIVATE(call->getParams())->getInConference()) {
bool_t active_after_removed = isIn();
return removeFromConference(L_GET_C_BACK_PTR(call), active_after_removed);
}
}
#endif
return err;
return 0;
}
int LocalConference::removeParticipant(LinphoneCall *call) {
int err;
char * str=linphone_call_get_remote_address_as_string(call);
int LocalConference::removeParticipant (LinphoneCall *call) {
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.");
int 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());
}
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) {
int LocalConference::removeParticipant (const LinphoneAddress *uri) {
const Participant *participant = findParticipant(uri);
if(participant == NULL) return -1;
if (!participant)
return -1;
LinphoneCall *call = participant->getCall();
if(call == NULL) return -1;
if (!call)
return -1;
return removeParticipant(call);
}
int LocalConference::terminate() {
#if 0
bctbx_list_t *calls=m_core->calls;
m_terminating =TRUE;
int LocalConference::terminate () {
m_terminating = TRUE;
while (calls) {
LinphoneCall *call=(LinphoneCall*)calls->data;
calls=calls->next;
if (linphone_call_params_get_in_conference(linphone_call_get_current_params(call))) {
linphone_call_terminate(call);
}
list<shared_ptr<LinphonePrivate::Call>> calls = L_GET_CPP_PTR_FROM_C_OBJECT(m_core)->getCalls();
for (auto it = calls.begin(); it != calls.end(); it++) {
shared_ptr<LinphonePrivate::Call> call(*it);
if (L_GET_PRIVATE(call->getCurrentParams())->getInConference())
call->terminate();
}
Conference::terminate();
m_terminating = FALSE;
#endif
return 0;
}
int LocalConference::enter() {
#if 0
if (linphone_core_sound_resources_locked(m_core)) {
int LocalConference::enter () {
if (linphone_core_sound_resources_locked(m_core))
return -1;
}
if (m_core->current_call != NULL) {
_linphone_call_pause(m_core->current_call);
}
if (m_localParticipantStream==NULL) addLocalEndpoint();
#endif
if (linphone_core_get_current_call(m_core))
_linphone_call_pause(linphone_core_get_current_call(m_core));
if (!m_localParticipantStream)
addLocalEndpoint();
return 0;
}
void LocalConference::removeLocalEndpoint(){
if (m_localEndpoint){
ms_audio_conference_remove_member(m_conf,m_localEndpoint);
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;
m_localEndpoint = nullptr;
audio_stream_stop(m_localParticipantStream);
m_localParticipantStream=NULL;
m_localParticipantStream = nullptr;
rtp_profile_destroy(m_localDummyProfile);
}
}
int LocalConference::leave() {
int LocalConference::leave () {
if (isIn())
removeLocalEndpoint();
return 0;
}
int LocalConference::getSize() const {
if (m_conf == NULL) {
int LocalConference::getSize () const {
if (!m_conf)
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.");
int LocalConference::startRecording (const char *path) {
if (!m_conf) {
ms_warning("linphone_core_start_conference_recording(): no conference now");
return -1;
}
if (m_recordEndpoint==NULL){
m_recordEndpoint=ms_audio_endpoint_new_recorder(m_core->factory);
ms_audio_conference_add_member(m_conf,m_recordEndpoint);
if (!m_recordEndpoint) {