From 99252460f478d0a1d002af9c3e4062e91ead101c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 30 Nov 2015 11:02:49 +0100 Subject: [PATCH 01/21] Create a C++ interface in order to abstract conference systems The actual conference system has been transposed as an implementation of the freshly created interface --- coreapi/CMakeLists.txt | 3 +- coreapi/conference.cc | 527 +++++++++++++++++++++++++++++++++++++++++ coreapi/conference.h | 65 +++++ coreapi/linphonecall.c | 8 +- coreapi/linphonecore.c | 84 ++++++- coreapi/private.h | 21 +- 6 files changed, 679 insertions(+), 29 deletions(-) create mode 100644 coreapi/conference.cc create mode 100644 coreapi/conference.h diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index 2f2d59712..4f7174ab5 100644 --- a/coreapi/CMakeLists.txt +++ b/coreapi/CMakeLists.txt @@ -73,7 +73,6 @@ set(LINPHONE_SOURCE_FILES_C call_params.c chat.c chat_file_transfer.c - conference.c contactprovider.c content.c dict.c @@ -111,7 +110,7 @@ set(LINPHONE_SOURCE_FILES_C xmlrpc.c vtables.c ) -set(LINPHONE_SOURCE_FILES_CXX ) +set(LINPHONE_SOURCE_FILES_CXX conference.cc) if(ENABLE_TUNNEL) list(APPEND LINPHONE_SOURCE_FILES_CXX diff --git a/coreapi/conference.cc b/coreapi/conference.cc new file mode 100644 index 000000000..676c61596 --- /dev/null +++ b/coreapi/conference.cc @@ -0,0 +1,527 @@ +/******************************************************************************* + * 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 + +namespace Linphone { +class Conference { +public: + enum Method { + Unknown = 0, + Media = 1, + Transport = 2 + }; + + Conference(LinphoneCore *core, Method method); + virtual ~Conference() {}; + + virtual int addCall(LinphoneCall *call) = 0; + virtual int removeCall(LinphoneCall *call) = 0; + virtual int terminate() = 0; + + virtual int addLocalParticipant() = 0; + virtual int removeLocalParticipant() = 0; + virtual bool localParticipantIsIn() const = 0; + virtual AudioStream *getAudioStream() const = 0; + + virtual int muteMicrophone(bool val) = 0; + virtual bool microphoneIsMuted() const = 0; + virtual float getInputVolume() const = 0; + virtual int getParticipantCount() const = 0; + Method getMethod() const {return m_method;} + + 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) {}; + + static Conference *make(LinphoneCore *core, Conference::Method method); + +protected: + LinphoneCore *m_core; + Method m_method; +}; +}; + +using namespace Linphone; + +Conference::Conference(LinphoneCore *core, Conference::Method type) : + m_core(core), + m_method(type) { +} + + +namespace Linphone { +class MediaConference: public Conference { +public: + MediaConference(LinphoneCore *core); + virtual ~MediaConference(); + + virtual int addCall(LinphoneCall *call); + virtual int addAllCalls(LinphoneCall &call); + virtual int removeCall(LinphoneCall *call); + virtual int terminate(); + + virtual int addLocalParticipant(); + virtual int removeLocalParticipant(); + virtual bool localParticipantIsIn() const {return m_localParticipant!=NULL;} + virtual AudioStream *getAudioStream() const {return m_localParticipant;} + + virtual int muteMicrophone(bool val); + virtual bool microphoneIsMuted() const {return m_localMuted;} + virtual float getInputVolume() const; + 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; + AudioStream *m_localParticipant; + MSAudioEndpoint *m_localEndpoint; + MSAudioEndpoint *m_recordEndpoint; + RtpProfile *m_localDummyProfile; + bool_t m_localMuted; + bool_t m_terminated; +}; +}; + +MediaConference::MediaConference(LinphoneCore *core): Conference(core, Media), + m_conf(NULL), + m_localParticipant(NULL), + m_localEndpoint(NULL), + m_recordEndpoint(NULL), + m_localDummyProfile(NULL), + m_localMuted(FALSE), + 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); +} + +MediaConference::~MediaConference() { + if(m_conf) terminate(); +} + +RtpProfile *MediaConference::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 MediaConference::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_localParticipant=st; + m_localEndpoint=ms_audio_endpoint_get_from_stream(st,FALSE); + ms_audio_conference_add_member(m_conf,m_localEndpoint); +} + +int MediaConference::addCall(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; + } + return 0; +} + +int MediaConference::addAllCalls(LinphoneCall &call) { + MSList *calls=m_core->calls; + while (calls) { + LinphoneCall *call=(LinphoneCall*)calls->data; + calls=calls->next; + if (!call->current_params->in_conference) addCall(call); + } + addLocalParticipant(); + return 0; +} + +int MediaConference::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; + + 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 (localParticipantIsIn()){ + ms_message("Leaving conference for reconnecting with unique call."); + removeLocalParticipant(); + } + 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 MediaConference::remoteParticipantsCount() { + int count=getParticipantCount(); + if (count==0) return 0; + if (!m_localParticipant) return count; + return count -1; +} + +int MediaConference::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=localParticipantIsIn(); + err=removeFromConference(rc, active_after_removed); + break; + } + } + return err; +} + +int MediaConference::removeCall(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 MediaConference::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 MediaConference::addLocalParticipant() { + 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_localParticipant==NULL) addLocalEndpoint(); + return 0; +} + +void MediaConference::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_localParticipant); + m_localParticipant=NULL; + rtp_profile_destroy(m_localDummyProfile); + } +} + +int MediaConference::removeLocalParticipant() { + if (localParticipantIsIn()) + removeLocalEndpoint(); + return 0; +} + +int MediaConference::muteMicrophone(bool val) { + if (val) { + audio_stream_set_mic_gain(m_localParticipant, 0); + } else { + audio_stream_set_mic_gain_db(m_localParticipant, m_core->sound_conf.soft_mic_lev); + } + if ( linphone_core_get_rtp_no_xmit_on_audio_mute(m_core) ){ + audio_stream_mute_rtp(m_localParticipant, val); + } + m_localMuted=val; + return 0; +} + +float MediaConference::getInputVolume() const { + AudioStream *st=m_localParticipant; + if (st && st->volsend && !m_localMuted){ + float vol=0; + ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol); + return vol; + + } + return LINPHONE_VOLUME_DB_LOWEST; +} + +int MediaConference::getParticipantCount() const { + if (m_conf == NULL) { + return 0; + } + return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0); +} + +int MediaConference::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 MediaConference::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; +} + +Conference *Conference::make(LinphoneCore *core, Conference::Method type) { + switch(type) { + case Media: + return new MediaConference(core); + default: + return NULL; + } +} + +void MediaConference::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 MediaConference::onCallStreamStopping(LinphoneCall *call) { + ms_audio_conference_remove_member(m_conf,call->endpoint); + ms_audio_endpoint_release_from_stream(call->endpoint); + call->endpoint=NULL; +} + +void MediaConference::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_localParticipant) + 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; + } +} + +LinphoneConference *linphone_conference_make(LinphoneCore *core, LinphoneConferenceType type) { + return Conference::make(core, (Conference::Method)type); +} + +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_remove_call(LinphoneConference *obj, LinphoneCall *call) { + return ((Conference *)obj)->removeCall(call); +} + +int linphone_conference_terminate(LinphoneConference *obj) { + return ((Conference *)obj)->terminate(); +} + +int linphone_conference_add_local_participant(LinphoneConference *obj) { + return ((Conference *)obj)->addLocalParticipant(); +} + +int linphone_conference_remove_local_participant(LinphoneConference *obj) { + return ((Conference *)obj)->removeLocalParticipant(); +} + +bool_t linphone_conference_local_participant_is_in(const LinphoneConference *obj) { + return ((Conference *)obj)->localParticipantIsIn() ? 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(); +} + +LinphoneConferenceType linphone_conference_get_method(const LinphoneConference *obj) { + return (LinphoneConferenceType)((Conference *)obj)->getMethod(); +} + +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); +} diff --git a/coreapi/conference.h b/coreapi/conference.h new file mode 100644 index 000000000..c592e8fb7 --- /dev/null +++ b/coreapi/conference.h @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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 enum _LinphoneConferenceType { + LinphoneConferenceTypeUnknown = 0, + LinphoneConferenceTypeMedia = 1, + LinphoneConferenceTypeTransport = 2 +} LinphoneConferenceType; + + +typedef void LinphoneConference; + +LinphoneConference *linphone_conference_make(LinphoneCore *core, LinphoneConferenceType type); +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_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); +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); +LinphoneConferenceType linphone_conference_get_method(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); + +#endif // CONFERENCE_H diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index a0b20ec63..35a2c2c6a 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1424,7 +1424,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; @@ -3091,7 +3091,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; @@ -3575,6 +3575,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]); @@ -3590,7 +3591,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); @@ -4851,4 +4852,3 @@ void linphone_call_refresh_sockets(LinphoneCall *call){ } } } - diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 38da0256e..64531e463 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -4853,8 +4853,7 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ const MSList *elem; if (linphone_core_is_in_conference(lc)){ - lc->conf_ctx.local_muted=val; - linphone_core_mute_audio_stream(lc, lc->conf_ctx.local_participant, val); + linphone_conference_mute_microphone(lc->conf_ctx, val); } list = linphone_core_get_calls(lc); for (elem = list; elem != NULL; elem = elem->next) { @@ -4867,7 +4866,7 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ bool_t linphone_core_is_mic_muted(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("linphone_core_is_mic_muted(): No current call !"); return FALSE; @@ -5896,7 +5895,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; @@ -7397,3 +7396,80 @@ const char *linphone_stream_type_to_string(const LinphoneStreamType type) { } return "INVALID"; } + +int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call) { + const char *conf_method_name; + LinphoneConferenceType method; + if(lc->conf_ctx == NULL) { + conf_method_name = lp_config_get_string(lc->config, "misc", "conference_method", "media"); + if(strcasecmp(conf_method_name, "media") == 0) { + method = LinphoneConferenceTypeMedia; + } else if(strcasecmp(conf_method_name, "transport") == 0) { + method = LinphoneConferenceTypeTransport; + } else { + ms_error("'%s' is not a valid conference method", conf_method_name); + return -1; + } + lc->conf_ctx = linphone_conference_make(lc, method); + } + return linphone_conference_add_call(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); + } + 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_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_add_local_participant(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); + 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); + 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; +} diff --git a/coreapi/private.h b/coreapi/private.h index 84d26357e..ddefa75cc 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -32,6 +32,7 @@ extern "C" { #include "linphonefriend.h" #include "linphone_tunnel.h" #include "linphonecore_utils.h" +#include "conference.h" #include "sal/sal.h" #include "sipsetup.h" #include "quality_reporting.h" @@ -783,16 +784,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; @@ -807,7 +798,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; @@ -869,7 +859,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; @@ -1000,13 +990,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); From 9d650f59bf2103e3f1c3fecb6496ccb5b1f79c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 1 Dec 2015 11:01:17 +0100 Subject: [PATCH 02/21] Remove conference.c --- coreapi/conference.c | 430 ------------------------------------------- 1 file changed, 430 deletions(-) delete mode 100644 coreapi/conference.c 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; -} - -/** - * @} -**/ - From c526791c63c8e8f31d07afbd6bc1f9936ea334ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 4 Dec 2015 13:45:04 +0100 Subject: [PATCH 03/21] Conference implementation based on focus server --- coreapi/conference.cc | 400 +++++++++++++++++++++++++++++++---------- coreapi/conference.h | 11 +- coreapi/linphonecore.c | 6 +- 3 files changed, 305 insertions(+), 112 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 676c61596..e931ebc52 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -30,29 +30,23 @@ namespace Linphone { class Conference { public: - enum Method { - Unknown = 0, - Media = 1, - Transport = 2 - }; - - Conference(LinphoneCore *core, Method method); + Conference(LinphoneCore *core); virtual ~Conference() {}; virtual int addCall(LinphoneCall *call) = 0; virtual int removeCall(LinphoneCall *call) = 0; virtual int terminate() = 0; - virtual int addLocalParticipant() = 0; - virtual int removeLocalParticipant() = 0; - virtual bool localParticipantIsIn() const = 0; - virtual AudioStream *getAudioStream() const = 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 muteMicrophone(bool val) = 0; - virtual bool microphoneIsMuted() const = 0; - virtual float getInputVolume() const = 0; virtual int getParticipantCount() const = 0; - Method getMethod() const {return m_method;} virtual int startRecording(const char *path) = 0; virtual int stopRecording() = 0; @@ -61,19 +55,43 @@ public: virtual void onCallStreamStopping(LinphoneCall *call) {}; virtual void onCallTerminating(LinphoneCall *call) {}; - static Conference *make(LinphoneCore *core, Conference::Method method); - protected: LinphoneCore *m_core; - Method m_method; + AudioStream *m_localParticipantStream; + bool m_isMuted; }; }; using namespace Linphone; -Conference::Conference(LinphoneCore *core, Conference::Method type) : +Conference::Conference(LinphoneCore *core) : m_core(core), - m_method(type) { + m_localParticipantStream(NULL), + m_isMuted(false) { +} + +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; } @@ -84,18 +102,12 @@ public: virtual ~MediaConference(); virtual int addCall(LinphoneCall *call); - virtual int addAllCalls(LinphoneCall &call); virtual int removeCall(LinphoneCall *call); virtual int terminate(); - virtual int addLocalParticipant(); - virtual int removeLocalParticipant(); - virtual bool localParticipantIsIn() const {return m_localParticipant!=NULL;} - virtual AudioStream *getAudioStream() const {return m_localParticipant;} - - virtual int muteMicrophone(bool val); - virtual bool microphoneIsMuted() const {return m_localMuted;} - virtual float getInputVolume() const; + virtual int enter(); + virtual int leave(); + virtual bool isIn() const {return m_localParticipantStream!=NULL;} virtual int getParticipantCount() const; virtual int startRecording(const char *path); @@ -114,22 +126,18 @@ private: static RtpProfile *sMakeDummyProfile(int samplerate); MSAudioConference *m_conf; - AudioStream *m_localParticipant; MSAudioEndpoint *m_localEndpoint; MSAudioEndpoint *m_recordEndpoint; RtpProfile *m_localDummyProfile; - bool_t m_localMuted; bool_t m_terminated; }; }; -MediaConference::MediaConference(LinphoneCore *core): Conference(core, Media), +MediaConference::MediaConference(LinphoneCore *core): Conference(core), m_conf(NULL), - m_localParticipant(NULL), m_localEndpoint(NULL), m_recordEndpoint(NULL), m_localDummyProfile(NULL), - m_localMuted(FALSE), m_terminated(FALSE) { MSAudioConferenceParams params; params.samplerate = lp_config_get_int(m_core->config, "sound","conference_rate",16000); @@ -172,7 +180,7 @@ void MediaConference::addLocalEndpoint() { linphone_core_echo_cancellation_enabled(m_core) ); _post_configure_audio_stream(st,m_core,FALSE); - m_localParticipant=st; + m_localParticipantStream=st; m_localEndpoint=ms_audio_endpoint_get_from_stream(st,FALSE); ms_audio_conference_add_member(m_conf,m_localEndpoint); } @@ -211,17 +219,6 @@ int MediaConference::addCall(LinphoneCall *call) { return 0; } -int MediaConference::addAllCalls(LinphoneCall &call) { - MSList *calls=m_core->calls; - while (calls) { - LinphoneCall *call=(LinphoneCall*)calls->data; - calls=calls->next; - if (!call->current_params->in_conference) addCall(call); - } - addLocalParticipant(); - return 0; -} - int MediaConference::removeFromConference(LinphoneCall *call, bool_t active){ int err=0; char *str; @@ -244,9 +241,9 @@ int MediaConference::removeFromConference(LinphoneCall *call, bool_t active){ LinphoneCallParams *params=linphone_call_params_copy(linphone_call_get_current_params(call)); params->in_conference=FALSE; // reconnect local audio with this call - if (localParticipantIsIn()){ + if (isIn()){ ms_message("Leaving conference for reconnecting with unique call."); - removeLocalParticipant(); + leave(); } ms_message("Updating call to actually remove from conference"); err=linphone_core_update_call(m_core,call,params); @@ -261,7 +258,7 @@ int MediaConference::removeFromConference(LinphoneCall *call, bool_t active){ int MediaConference::remoteParticipantsCount() { int count=getParticipantCount(); if (count==0) return 0; - if (!m_localParticipant) return count; + if (!m_localParticipantStream) return count; return count -1; } @@ -278,7 +275,7 @@ int MediaConference::convertConferenceToCall(){ LinphoneCall *rc=(LinphoneCall*)calls->data; calls=calls->next; if (rc->params->in_conference) { // not using current_param - bool_t active_after_removed=localParticipantIsIn(); + bool_t active_after_removed=isIn(); err=removeFromConference(rc, active_after_removed); break; } @@ -320,14 +317,14 @@ int MediaConference::terminate() { return 0; } -int MediaConference::addLocalParticipant() { +int MediaConference::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_localParticipant==NULL) addLocalEndpoint(); + if (m_localParticipantStream==NULL) addLocalEndpoint(); return 0; } @@ -336,42 +333,18 @@ void MediaConference::removeLocalEndpoint(){ ms_audio_conference_remove_member(m_conf,m_localEndpoint); ms_audio_endpoint_release_from_stream(m_localEndpoint); m_localEndpoint=NULL; - audio_stream_stop(m_localParticipant); - m_localParticipant=NULL; + audio_stream_stop(m_localParticipantStream); + m_localParticipantStream=NULL; rtp_profile_destroy(m_localDummyProfile); } } -int MediaConference::removeLocalParticipant() { - if (localParticipantIsIn()) +int MediaConference::leave() { + if (isIn()) removeLocalEndpoint(); return 0; } -int MediaConference::muteMicrophone(bool val) { - if (val) { - audio_stream_set_mic_gain(m_localParticipant, 0); - } else { - audio_stream_set_mic_gain_db(m_localParticipant, m_core->sound_conf.soft_mic_lev); - } - if ( linphone_core_get_rtp_no_xmit_on_audio_mute(m_core) ){ - audio_stream_mute_rtp(m_localParticipant, val); - } - m_localMuted=val; - return 0; -} - -float MediaConference::getInputVolume() const { - AudioStream *st=m_localParticipant; - if (st && st->volsend && !m_localMuted){ - float vol=0; - ms_filter_call_method(st->volsend,MS_VOLUME_GET,&vol); - return vol; - - } - return LINPHONE_VOLUME_DB_LOWEST; -} - int MediaConference::getParticipantCount() const { if (m_conf == NULL) { return 0; @@ -405,15 +378,6 @@ int MediaConference::stopRecording() { return 0; } -Conference *Conference::make(LinphoneCore *core, Conference::Method type) { - switch(type) { - case Media: - return new MediaConference(core); - default: - return NULL; - } -} - void MediaConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) { call->params->has_video = FALSE; call->camera_enabled = FALSE; @@ -436,7 +400,7 @@ void MediaConference::onCallTerminating(LinphoneCall *call) { convertConferenceToCall(); } if (remote_count==0){ - if (m_localParticipant) + if (m_localParticipantStream) removeLocalEndpoint(); if (m_recordEndpoint){ ms_audio_conference_remove_member(m_conf, m_recordEndpoint); @@ -450,8 +414,250 @@ void MediaConference::onCallTerminating(LinphoneCall *call) { } } -LinphoneConference *linphone_conference_make(LinphoneCore *core, LinphoneConferenceType type) { - return Conference::make(core, (Conference::Method)type); +namespace Linphone { +class TransportConference: public Conference { +public: + TransportConference(LinphoneCore *core); + virtual ~TransportConference(); + + 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; + const char *m_focusContact; + LinphoneCall *m_focusCall; + State m_state; + LinphoneCoreVTable *m_vtable; + MSList *m_pendingCalls; + MSList *m_transferingCalls; +}; +}; + +TransportConference::TransportConference(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); +} + +TransportConference::~TransportConference() { + linphone_core_remove_listener(m_core, m_vtable); + linphone_core_v_table_destroy(m_vtable); + if(m_pendingCalls) ms_list_free(m_pendingCalls); +} + +int TransportConference::addCall(LinphoneCall *call) { + LinphoneAddress *addr; + + switch(m_state) { + case NotConnectedToFocus: + ms_message("Calling the conference focus (%s)", m_focusAddr); + addr = linphone_address_new(m_focusAddr); + if(addr) { + m_focusCall = linphone_core_invite_address(m_core, addr); + m_localParticipantStream = m_focusCall->audiostream; + m_pendingCalls = ms_list_append(m_pendingCalls, call); + m_state = ConnectingToFocus; + linphone_address_unref(addr); + return 0; + } else return -1; + + case ConnectedToFocus: + linphone_core_transfer_call(m_core, call, m_focusContact); + m_transferingCalls = ms_list_append(m_transferingCalls, call); + return 0; + + default: + ms_error("Could not add call %p to the conference. Bad conference state (%s)", call, stateToString(m_state)); + return -1; + } +} + +int TransportConference::terminate() { + switch(m_state) { + case ConnectingToFocus: + case ConnectedToFocus: + linphone_core_terminate_call(m_core, m_focusCall); + break; + default: + break; + } + return 0; +} + +int TransportConference::enter() { + if(m_state != ConnectedToFocus) { + ms_error("Could not add local participant to 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 TransportConference::leave() { + if(m_state != ConnectedToFocus) { + ms_error("Could not remove local participant from 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 TransportConference::isIn() const { + if(m_state != ConnectedToFocus) return false; + LinphoneCallState callState = linphone_call_get_state(m_focusCall); + return callState == LinphoneCallStreamsRunning; +} + +const char *TransportConference::stateToString(TransportConference::State state) { + switch(state) { + case NotConnectedToFocus: return "NotConnectedToFocus"; + case ConnectingToFocus: return "ConnectingToFocus"; + case ConnectedToFocus: return "ConnectedToFocus"; + default: return "Unknown"; + } +} + +void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { + switch (state) { + case LinphoneCallConnected: + m_state = ConnectedToFocus; + m_focusContact = linphone_call_get_remote_contact(m_focusCall); + for (MSList *it = m_pendingCalls; it; it = it->next) { + LinphoneCall *pendingCall = (LinphoneCall *)it->data; + LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall); + if(pendingCallState == LinphoneCallStreamsRunning || pendingCallState == LinphoneCallPaused) { + MSList *current_elem = it; + it = it->next; + linphone_core_transfer_call(m_core, pendingCall, m_focusContact); + m_pendingCalls = ms_list_remove_link(m_pendingCalls, current_elem); + m_transferingCalls = ms_list_append_link(m_transferingCalls, current_elem); + continue; + } + } + break; + + case LinphoneCallError: + case LinphoneCallEnd: + m_state = NotConnectedToFocus; + m_focusCall = NULL; + m_localParticipantStream = NULL; + m_focusContact = NULL; + m_pendingCalls = ms_list_free(m_pendingCalls); + m_transferingCalls = ms_list_free(m_transferingCalls); + break; + + default: break; + } +} + +void TransportConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { + MSList *elem = ms_list_find(m_pendingCalls, call); + switch(state) { + case LinphoneCallStreamsRunning: + case LinphoneCallPaused: + if(m_state == ConnectedToFocus) { + linphone_core_transfer_call(m_core, call, m_focusContact); + m_pendingCalls = ms_list_remove_link(m_pendingCalls, elem); + m_transferingCalls = ms_list_append_link(m_transferingCalls, elem); + } + break; + + case LinphoneCallError: + case LinphoneCallEnd: + m_pendingCalls = ms_list_remove_link(m_pendingCalls, elem); + break; + + default: break; + } +} + +void TransportConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { + LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); + TransportConference *conf = (TransportConference *)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 TransportConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { + LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); + TransportConference *conf = (TransportConference *)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); + break; + + default: + break; + } + } +} + + + + +LinphoneConference *linphone_media_conference_new(LinphoneCore *core) { + return new MediaConference(core); +} + +LinphoneConference *linphone_transport_conference_new(LinphoneCore *core) { + return new TransportConference(core); } void linphone_conference_free(LinphoneConference *obj) { @@ -471,15 +677,15 @@ int linphone_conference_terminate(LinphoneConference *obj) { } int linphone_conference_add_local_participant(LinphoneConference *obj) { - return ((Conference *)obj)->addLocalParticipant(); + return ((Conference *)obj)->enter(); } int linphone_conference_remove_local_participant(LinphoneConference *obj) { - return ((Conference *)obj)->removeLocalParticipant(); + return ((Conference *)obj)->leave(); } bool_t linphone_conference_local_participant_is_in(const LinphoneConference *obj) { - return ((Conference *)obj)->localParticipantIsIn() ? TRUE : FALSE; + return ((Conference *)obj)->isIn() ? TRUE : FALSE; } AudioStream *linphone_conference_get_audio_stream(const LinphoneConference *obj) { @@ -502,10 +708,6 @@ int linphone_conference_get_participant_count(const LinphoneConference *obj) { return ((Conference *)obj)->getParticipantCount(); } -LinphoneConferenceType linphone_conference_get_method(const LinphoneConference *obj) { - return (LinphoneConferenceType)((Conference *)obj)->getMethod(); -} - int linphone_conference_start_recording(LinphoneConference *obj, const char *path) { return ((Conference *)obj)->startRecording(path); } diff --git a/coreapi/conference.h b/coreapi/conference.h index c592e8fb7..19b51d063 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -28,16 +28,10 @@ #include "linphonecore.h" -typedef enum _LinphoneConferenceType { - LinphoneConferenceTypeUnknown = 0, - LinphoneConferenceTypeMedia = 1, - LinphoneConferenceTypeTransport = 2 -} LinphoneConferenceType; - - typedef void LinphoneConference; -LinphoneConference *linphone_conference_make(LinphoneCore *core, LinphoneConferenceType type); +LinphoneConference *linphone_media_conference_new(LinphoneCore *core); +LinphoneConference *linphone_transport_conference_new(LinphoneCore *core); void linphone_conference_free(LinphoneConference *obj); int linphone_conference_add_call(LinphoneConference *obj, LinphoneCall *call); @@ -53,7 +47,6 @@ 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); -LinphoneConferenceType linphone_conference_get_method(const LinphoneConference *obj); int linphone_conference_start_recording(LinphoneConference *obj, const char *path); int linphone_conference_stop_recording(LinphoneConference *obj); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 64531e463..37cf31508 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7399,18 +7399,16 @@ const char *linphone_stream_type_to_string(const LinphoneStreamType type) { int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call) { const char *conf_method_name; - LinphoneConferenceType method; if(lc->conf_ctx == NULL) { conf_method_name = lp_config_get_string(lc->config, "misc", "conference_method", "media"); if(strcasecmp(conf_method_name, "media") == 0) { - method = LinphoneConferenceTypeMedia; + lc->conf_ctx = linphone_media_conference_new(lc); } else if(strcasecmp(conf_method_name, "transport") == 0) { - method = LinphoneConferenceTypeTransport; + lc->conf_ctx = linphone_transport_conference_new(lc); } else { ms_error("'%s' is not a valid conference method", conf_method_name); return -1; } - lc->conf_ctx = linphone_conference_make(lc, method); } return linphone_conference_add_call(lc->conf_ctx, call); } From 4e972721a7b8f79509b3563aebfe66d7ce171ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 4 Dec 2015 14:27:11 +0100 Subject: [PATCH 04/21] Fix crashes --- coreapi/conference.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index e931ebc52..95dcb351d 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -520,7 +520,7 @@ int TransportConference::terminate() { int TransportConference::enter() { if(m_state != ConnectedToFocus) { - ms_error("Could not add local participant to the conference: bad conference state (%s)", stateToString(m_state)); + 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); @@ -538,7 +538,7 @@ int TransportConference::enter() { int TransportConference::leave() { if(m_state != ConnectedToFocus) { - ms_error("Could not remove local participant from the conference: bad conference state (%s)", stateToString(m_state)); + ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state)); return -1; } LinphoneCallState callState = linphone_call_get_state(m_focusCall); @@ -582,7 +582,7 @@ void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { it = it->next; linphone_core_transfer_call(m_core, pendingCall, m_focusContact); m_pendingCalls = ms_list_remove_link(m_pendingCalls, current_elem); - m_transferingCalls = ms_list_append_link(m_transferingCalls, current_elem); + m_transferingCalls = ms_list_append(m_transferingCalls, pendingCall); continue; } } @@ -603,20 +603,19 @@ void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { } void TransportConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { - MSList *elem = ms_list_find(m_pendingCalls, call); switch(state) { case LinphoneCallStreamsRunning: case LinphoneCallPaused: if(m_state == ConnectedToFocus) { linphone_core_transfer_call(m_core, call, m_focusContact); - m_pendingCalls = ms_list_remove_link(m_pendingCalls, elem); - m_transferingCalls = ms_list_append_link(m_transferingCalls, elem); + m_pendingCalls = ms_list_remove(m_pendingCalls, call); + m_transferingCalls = ms_list_append(m_transferingCalls, call); } break; case LinphoneCallError: case LinphoneCallEnd: - m_pendingCalls = ms_list_remove_link(m_pendingCalls, elem); + m_pendingCalls = ms_list_remove(m_pendingCalls, call); break; default: break; From b675b29777e8e16b219c3434a1fb794894bfaf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 4 Dec 2015 14:48:42 +0100 Subject: [PATCH 05/21] Do not automatically enter the conference when the 'Transport' implementation is used --- coreapi/conference.cc | 9 +++++++++ coreapi/conference.h | 7 +++++++ coreapi/linphonecore.c | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 95dcb351d..237a703fc 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -26,6 +26,7 @@ #include "private.h" #include "conference.h" #include +#include namespace Linphone { class Conference { @@ -726,3 +727,11 @@ void linphone_conference_on_call_stream_stopping(LinphoneConference *obj, Linpho 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 LinphoneConferenceClassMedia: return typeid(obj) == typeid(MediaConference); + case LinphoneConferenceClassTransport: return typeid(obj) == typeid(TransportConference); + default: return FALSE; + } +} diff --git a/coreapi/conference.h b/coreapi/conference.h index 19b51d063..d10b78a56 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -30,6 +30,11 @@ typedef void LinphoneConference; +typedef enum { + LinphoneConferenceClassMedia, + LinphoneConferenceClassTransport +} LinphoneConferenceClass; + LinphoneConference *linphone_media_conference_new(LinphoneCore *core); LinphoneConference *linphone_transport_conference_new(LinphoneCore *core); void linphone_conference_free(LinphoneConference *obj); @@ -55,4 +60,6 @@ void linphone_conference_on_call_stream_starting(LinphoneConference *obj, Linpho 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/linphonecore.c b/coreapi/linphonecore.c index 37cf31508..e3176e590 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7420,7 +7420,9 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) { calls=calls->next; linphone_core_add_to_conference(lc, call); } - linphone_core_enter_conference(lc); + if(lc->conf_ctx && linphone_conference_check_class(lc->conf_ctx, LinphoneConferenceClassMedia)) { + linphone_core_enter_conference(lc); + } return 0; } From 0a4fab598726255c28d4a1c7877d373bf3ec0156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 7 Dec 2015 11:23:17 +0100 Subject: [PATCH 06/21] Add a method to LinphoneCore to get a pointer to the internal conference object --- coreapi/conference.cc | 4 ++-- coreapi/conference.h | 2 +- coreapi/linphonecore.c | 4 ++++ coreapi/linphonecore.h | 3 +++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 237a703fc..6bcf85841 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -653,11 +653,11 @@ void TransportConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *t LinphoneConference *linphone_media_conference_new(LinphoneCore *core) { - return new MediaConference(core); + return (LinphoneConference *) new MediaConference(core); } LinphoneConference *linphone_transport_conference_new(LinphoneCore *core) { - return new TransportConference(core); + return (LinphoneConference *) new TransportConference(core); } void linphone_conference_free(LinphoneConference *obj) { diff --git a/coreapi/conference.h b/coreapi/conference.h index d10b78a56..e18faf046 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -28,7 +28,7 @@ #include "linphonecore.h" -typedef void LinphoneConference; +typedef struct _LinphoneConference LinphoneConference; typedef enum { LinphoneConferenceClassMedia, diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e3176e590..4f4790f80 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7473,3 +7473,7 @@ 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; +} \ No newline at end of file diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index f8fcec461..41cc65346 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3803,6 +3803,8 @@ 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); +typedef struct _LinphoneConference LinphoneConference; + LINPHONE_PUBLIC int linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call); LINPHONE_PUBLIC int linphone_core_add_all_to_conference(LinphoneCore *lc); /** @@ -3835,6 +3837,7 @@ LINPHONE_PUBLIC int linphone_core_terminate_conference(LinphoneCore *lc); LINPHONE_PUBLIC int linphone_core_get_conference_size(LinphoneCore *lc); LINPHONE_PUBLIC int linphone_core_start_conference_recording(LinphoneCore *lc, const char *path); LINPHONE_PUBLIC int linphone_core_stop_conference_recording(LinphoneCore *lc); +LINPHONE_PUBLIC LinphoneConference *linphone_core_get_conference(LinphoneCore *lc); /** * 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 From f8a7f255586a84169180fb0792a0a8b2ba992c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 7 Dec 2015 14:49:24 +0100 Subject: [PATCH 07/21] Add a property to LinphoneCalls Add a property to LinphoneCalls storing the reference to the conference in which the call is part of. That property is NULL if the call is part of no conference That commit also add some documentation on several functions --- coreapi/call_params.h | 4 ++ coreapi/conference.cc | 14 +++++++ coreapi/linphonecall.c | 4 ++ coreapi/linphonecore.h | 91 +++++++++++++++++++++++++++++++++++++++--- coreapi/private.h | 2 + 5 files changed, 110 insertions(+), 5 deletions(-) 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/conference.cc b/coreapi/conference.cc index 6bcf85841..3087727c1 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -71,6 +71,16 @@ Conference::Conference(LinphoneCore *core) : m_isMuted(false) { } +int Conference::addCall(LinphoneCall *call) { + call->conf_ref = (LinphoneConference *)this; + return 0; +} + +int Conference::removeCall(LinphoneCall *call) { + call->conf_ref = NULL; + return 0; +} + int Conference::muteMicrophone(bool val) { if (val) { audio_stream_set_mic_gain(m_localParticipantStream, 0); @@ -386,12 +396,14 @@ void MediaConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRe ms_audio_conference_add_member(m_conf,ep); ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote); call->endpoint=ep; + Conference::addCall(call); } void MediaConference::onCallStreamStopping(LinphoneCall *call) { ms_audio_conference_remove_member(m_conf,call->endpoint); ms_audio_endpoint_release_from_stream(call->endpoint); call->endpoint=NULL; + Conference::removeCall(call); } void MediaConference::onCallTerminating(LinphoneCall *call) { @@ -573,6 +585,7 @@ const char *TransportConference::stateToString(TransportConference::State state) void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { switch (state) { case LinphoneCallConnected: + Conference::addCall(m_focusCall); m_state = ConnectedToFocus; m_focusContact = linphone_call_get_remote_contact(m_focusCall); for (MSList *it = m_pendingCalls; it; it = it->next) { @@ -591,6 +604,7 @@ void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { case LinphoneCallError: case LinphoneCallEnd: + Conference::removeCall(m_focusCall); m_state = NotConnectedToFocus; m_focusCall = NULL; m_localParticipantStream = NULL; diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 35a2c2c6a..d70f5247e 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4520,6 +4520,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. diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 41cc65346..a3d3425bd 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -139,13 +139,19 @@ 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 humain readable value for LinphoneStreamType. * @param LinphoneStreamType * @returns * @ingroup initializing **/ - LINPHONE_PUBLIC const char *linphone_stream_type_to_string(const LinphoneStreamType); /** * Object that represents a SIP address. @@ -890,10 +896,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 @@ -3803,9 +3818,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); -typedef struct _LinphoneConference LinphoneConference; +/** + * @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. @@ -3827,17 +3860,65 @@ LINPHONE_PUBLIC int linphone_core_add_all_to_conference(LinphoneCore *lc); * Indicates whether the local participant is part of a conference. * @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 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/private.h b/coreapi/private.h index ddefa75cc..23fcbd760 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -329,6 +329,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); From 20831d929e8b3a251e6706deff214c08dda82e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 8 Dec 2015 14:15:31 +0100 Subject: [PATCH 08/21] Display the conference view while conferences using a focus server --- console/commands.c | 2 +- gtk/conference.c | 4 +++- gtk/incall_view.c | 7 +++---- gtk/linphone.h | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) 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/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 e099b963d..68941d50a 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 f337770fb..9f430f5fc 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); From 18fde493f7bba7edfa6f9f5e4b989e856b913121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 8 Dec 2015 14:30:38 +0100 Subject: [PATCH 09/21] Rename the two implementations of the Conference interface --- coreapi/conference.cc | 90 +++++++++++++++++++++--------------------- coreapi/conference.h | 8 ++-- coreapi/linphonecore.c | 12 +++--- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 3087727c1..8bc7f4eef 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -107,10 +107,10 @@ float Conference::getInputVolume() const { namespace Linphone { -class MediaConference: public Conference { +class LocalConference: public Conference { public: - MediaConference(LinphoneCore *core); - virtual ~MediaConference(); + LocalConference(LinphoneCore *core); + virtual ~LocalConference(); virtual int addCall(LinphoneCall *call); virtual int removeCall(LinphoneCall *call); @@ -144,7 +144,7 @@ private: }; }; -MediaConference::MediaConference(LinphoneCore *core): Conference(core), +LocalConference::LocalConference(LinphoneCore *core): Conference(core), m_conf(NULL), m_localEndpoint(NULL), m_recordEndpoint(NULL), @@ -155,11 +155,11 @@ MediaConference::MediaConference(LinphoneCore *core): Conference(core), m_conf=ms_audio_conference_new(¶ms); } -MediaConference::~MediaConference() { +LocalConference::~LocalConference() { if(m_conf) terminate(); } -RtpProfile *MediaConference::sMakeDummyProfile(int samplerate){ +RtpProfile *LocalConference::sMakeDummyProfile(int samplerate){ RtpProfile *prof=rtp_profile_new("dummy"); PayloadType *pt=payload_type_clone(&payload_type_l16_mono); pt->clock_rate=samplerate; @@ -167,7 +167,7 @@ RtpProfile *MediaConference::sMakeDummyProfile(int samplerate){ return prof; } -void MediaConference::addLocalEndpoint() { +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); @@ -196,7 +196,7 @@ void MediaConference::addLocalEndpoint() { ms_audio_conference_add_member(m_conf,m_localEndpoint); } -int MediaConference::addCall(LinphoneCall *call) { +int LocalConference::addCall(LinphoneCall *call) { if (call->current_params->in_conference){ ms_error("Already in conference"); return -1; @@ -230,7 +230,7 @@ int MediaConference::addCall(LinphoneCall *call) { return 0; } -int MediaConference::removeFromConference(LinphoneCall *call, bool_t active){ +int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){ int err=0; char *str; @@ -266,14 +266,14 @@ int MediaConference::removeFromConference(LinphoneCall *call, bool_t active){ return err; } -int MediaConference::remoteParticipantsCount() { +int LocalConference::remoteParticipantsCount() { int count=getParticipantCount(); if (count==0) return 0; if (!m_localParticipantStream) return count; return count -1; } -int MediaConference::convertConferenceToCall(){ +int LocalConference::convertConferenceToCall(){ int err=0; MSList *calls=m_core->calls; @@ -294,7 +294,7 @@ int MediaConference::convertConferenceToCall(){ return err; } -int MediaConference::removeCall(LinphoneCall *call) { +int LocalConference::removeCall(LinphoneCall *call) { int err; char * str=linphone_call_get_remote_address_as_string(call); ms_message("Removing call %s from the conference", str); @@ -314,7 +314,7 @@ int MediaConference::removeCall(LinphoneCall *call) { return err; } -int MediaConference::terminate() { +int LocalConference::terminate() { MSList *calls=m_core->calls; m_terminated=TRUE; @@ -328,7 +328,7 @@ int MediaConference::terminate() { return 0; } -int MediaConference::enter() { +int LocalConference::enter() { if (linphone_core_sound_resources_locked(m_core)) { return -1; } @@ -339,7 +339,7 @@ int MediaConference::enter() { return 0; } -void MediaConference::removeLocalEndpoint(){ +void LocalConference::removeLocalEndpoint(){ if (m_localEndpoint){ ms_audio_conference_remove_member(m_conf,m_localEndpoint); ms_audio_endpoint_release_from_stream(m_localEndpoint); @@ -350,20 +350,20 @@ void MediaConference::removeLocalEndpoint(){ } } -int MediaConference::leave() { +int LocalConference::leave() { if (isIn()) removeLocalEndpoint(); return 0; } -int MediaConference::getParticipantCount() const { +int LocalConference::getParticipantCount() const { if (m_conf == NULL) { return 0; } return ms_audio_conference_get_size(m_conf) - (m_recordEndpoint ? 1 : 0); } -int MediaConference::startRecording(const char *path) { +int LocalConference::startRecording(const char *path) { if (m_conf == NULL) { ms_warning("linphone_core_start_conference_recording(): no conference now."); return -1; @@ -376,7 +376,7 @@ int MediaConference::startRecording(const char *path) { return 0; } -int MediaConference::stopRecording() { +int LocalConference::stopRecording() { if (m_conf == NULL) { ms_warning("linphone_core_stop_conference_recording(): no conference now."); return -1; @@ -389,7 +389,7 @@ int MediaConference::stopRecording() { return 0; } -void MediaConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) { +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); @@ -399,14 +399,14 @@ void MediaConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRe Conference::addCall(call); } -void MediaConference::onCallStreamStopping(LinphoneCall *call) { +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; Conference::removeCall(call); } -void MediaConference::onCallTerminating(LinphoneCall *call) { +void LocalConference::onCallTerminating(LinphoneCall *call) { int remote_count=remoteParticipantsCount(); ms_message("conference_check_uninit(): size=%i", getParticipantCount()); if (remote_count==1 && !m_terminated){ @@ -428,10 +428,10 @@ void MediaConference::onCallTerminating(LinphoneCall *call) { } namespace Linphone { -class TransportConference: public Conference { +class RemoteConference: public Conference { public: - TransportConference(LinphoneCore *core); - virtual ~TransportConference(); + RemoteConference(LinphoneCore *core); + virtual ~RemoteConference(); virtual int addCall(LinphoneCall *call); virtual int removeCall(LinphoneCall *call) {return 0;} @@ -469,7 +469,7 @@ private: }; }; -TransportConference::TransportConference(LinphoneCore *core): +RemoteConference::RemoteConference(LinphoneCore *core): Conference(core), m_focusAddr(NULL), m_focusContact(NULL), @@ -486,13 +486,13 @@ TransportConference::TransportConference(LinphoneCore *core): linphone_core_add_listener(m_core, m_vtable); } -TransportConference::~TransportConference() { +RemoteConference::~RemoteConference() { linphone_core_remove_listener(m_core, m_vtable); linphone_core_v_table_destroy(m_vtable); if(m_pendingCalls) ms_list_free(m_pendingCalls); } -int TransportConference::addCall(LinphoneCall *call) { +int RemoteConference::addCall(LinphoneCall *call) { LinphoneAddress *addr; switch(m_state) { @@ -519,7 +519,7 @@ int TransportConference::addCall(LinphoneCall *call) { } } -int TransportConference::terminate() { +int RemoteConference::terminate() { switch(m_state) { case ConnectingToFocus: case ConnectedToFocus: @@ -531,7 +531,7 @@ int TransportConference::terminate() { return 0; } -int TransportConference::enter() { +int RemoteConference::enter() { if(m_state != ConnectedToFocus) { ms_error("Could not enter in the conference: bad conference state (%s)", stateToString(m_state)); return -1; @@ -549,7 +549,7 @@ int TransportConference::enter() { return 0; } -int TransportConference::leave() { +int RemoteConference::leave() { if(m_state != ConnectedToFocus) { ms_error("Could not leave the conference: bad conference state (%s)", stateToString(m_state)); return -1; @@ -567,13 +567,13 @@ int TransportConference::leave() { return 0; } -bool TransportConference::isIn() const { +bool RemoteConference::isIn() const { if(m_state != ConnectedToFocus) return false; LinphoneCallState callState = linphone_call_get_state(m_focusCall); return callState == LinphoneCallStreamsRunning; } -const char *TransportConference::stateToString(TransportConference::State state) { +const char *RemoteConference::stateToString(RemoteConference::State state) { switch(state) { case NotConnectedToFocus: return "NotConnectedToFocus"; case ConnectingToFocus: return "ConnectingToFocus"; @@ -582,7 +582,7 @@ const char *TransportConference::stateToString(TransportConference::State state) } } -void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { +void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) { switch (state) { case LinphoneCallConnected: Conference::addCall(m_focusCall); @@ -617,7 +617,7 @@ void TransportConference::onFocusCallSateChanged(LinphoneCallState state) { } } -void TransportConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { +void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { switch(state) { case LinphoneCallStreamsRunning: case LinphoneCallPaused: @@ -637,9 +637,9 @@ void TransportConference::onPendingCallStateChanged(LinphoneCall *call, Linphone } } -void TransportConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { +void RemoteConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); - TransportConference *conf = (TransportConference *)linphone_core_v_table_get_user_data(vtable); + 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)) { @@ -647,9 +647,9 @@ void TransportConference::callStateChangedCb(LinphoneCore *lc, LinphoneCall *cal } } -void TransportConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { +void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state) { LinphoneCoreVTable *vtable = linphone_core_get_current_vtable(lc); - TransportConference *conf = (TransportConference *)linphone_core_v_table_get_user_data(vtable); + 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: @@ -666,12 +666,12 @@ void TransportConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *t -LinphoneConference *linphone_media_conference_new(LinphoneCore *core) { - return (LinphoneConference *) new MediaConference(core); +LinphoneConference *linphone_local_conference_new(LinphoneCore *core) { + return (LinphoneConference *) new LocalConference(core); } -LinphoneConference *linphone_transport_conference_new(LinphoneCore *core) { - return (LinphoneConference *) new TransportConference(core); +LinphoneConference *linphone_remote_conference_new(LinphoneCore *core) { + return (LinphoneConference *) new RemoteConference(core); } void linphone_conference_free(LinphoneConference *obj) { @@ -744,8 +744,8 @@ void linphone_conference_on_call_terminating(LinphoneConference *obj, LinphoneCa bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferenceClass _class) { switch(_class) { - case LinphoneConferenceClassMedia: return typeid(obj) == typeid(MediaConference); - case LinphoneConferenceClassTransport: return typeid(obj) == typeid(TransportConference); + 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 index e18faf046..aee2ffa8f 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -31,12 +31,12 @@ typedef struct _LinphoneConference LinphoneConference; typedef enum { - LinphoneConferenceClassMedia, - LinphoneConferenceClassTransport + LinphoneConferenceClassLocal, + LinphoneConferenceClassRemote } LinphoneConferenceClass; -LinphoneConference *linphone_media_conference_new(LinphoneCore *core); -LinphoneConference *linphone_transport_conference_new(LinphoneCore *core); +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); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 4f4790f80..b544a005b 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -7400,11 +7400,11 @@ const char *linphone_stream_type_to_string(const LinphoneStreamType type) { 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_method", "media"); - if(strcasecmp(conf_method_name, "media") == 0) { - lc->conf_ctx = linphone_media_conference_new(lc); - } else if(strcasecmp(conf_method_name, "transport") == 0) { - lc->conf_ctx = linphone_transport_conference_new(lc); + 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; @@ -7420,7 +7420,7 @@ int linphone_core_add_all_to_conference(LinphoneCore *lc) { calls=calls->next; linphone_core_add_to_conference(lc, call); } - if(lc->conf_ctx && linphone_conference_check_class(lc->conf_ctx, LinphoneConferenceClassMedia)) { + if(lc->conf_ctx && linphone_conference_check_class(lc->conf_ctx, LinphoneConferenceClassLocal)) { linphone_core_enter_conference(lc); } return 0; From a2874353bd4cf75d25ecac6e56747bdb146f4fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 17 Dec 2015 10:40:47 +0100 Subject: [PATCH 10/21] Fix several issues in the RemoteConference implementation --- coreapi/conference.cc | 88 ++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 8bc7f4eef..19140773b 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -227,6 +227,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); return 0; } @@ -244,6 +245,7 @@ int LocalConference::removeFromConference(LinphoneCall *call, bool_t active){ } } call->params->in_conference=FALSE; + Conference::removeCall(call); str=linphone_call_get_remote_address_as_string(call); ms_message("%s will be removed from conference", str); @@ -396,14 +398,12 @@ void LocalConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRe ms_audio_conference_add_member(m_conf,ep); ms_audio_conference_mute_member(m_conf,ep,isPausedByRemote); call->endpoint=ep; - Conference::addCall(call); } 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; - Conference::removeCall(call); } void LocalConference::onCallTerminating(LinphoneCall *call) { @@ -460,7 +460,7 @@ private: static void transferStateChanged(LinphoneCore *lc, LinphoneCall *transfered, LinphoneCallState new_call_state); const char *m_focusAddr; - const char *m_focusContact; + char *m_focusContact; LinphoneCall *m_focusCall; State m_state; LinphoneCoreVTable *m_vtable; @@ -487,9 +487,9 @@ RemoteConference::RemoteConference(LinphoneCore *core): } RemoteConference::~RemoteConference() { + if(m_state == ConnectedToFocus) terminate(); linphone_core_remove_listener(m_core, m_vtable); linphone_core_v_table_destroy(m_vtable); - if(m_pendingCalls) ms_list_free(m_pendingCalls); } int RemoteConference::addCall(LinphoneCall *call) { @@ -497,20 +497,27 @@ int RemoteConference::addCall(LinphoneCall *call) { switch(m_state) { case NotConnectedToFocus: + Conference::addCall(call); ms_message("Calling the conference focus (%s)", m_focusAddr); addr = linphone_address_new(m_focusAddr); if(addr) { - m_focusCall = linphone_core_invite_address(m_core, 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, call); + m_pendingCalls = ms_list_append(m_pendingCalls, linphone_call_ref(call)); m_state = ConnectingToFocus; linphone_address_unref(addr); return 0; } else return -1; + case ConnectingToFocus: + Conference::addCall(call); + m_pendingCalls = ms_list_append(m_pendingCalls, linphone_call_ref(call)); + return 0; + case ConnectedToFocus: + Conference::addCall(call); + m_transferingCalls = ms_list_append(m_transferingCalls, linphone_call_ref(call)); linphone_core_transfer_call(m_core, call, m_focusContact); - m_transferingCalls = ms_list_append(m_transferingCalls, call); return 0; default: @@ -523,6 +530,7 @@ int RemoteConference::terminate() { switch(m_state) { case ConnectingToFocus: case ConnectedToFocus: + Conference::removeCall(m_focusCall); linphone_core_terminate_call(m_core, m_focusCall); break; default: @@ -583,38 +591,45 @@ const char *RemoteConference::stateToString(RemoteConference::State state) { } void RemoteConference::onFocusCallSateChanged(LinphoneCallState state) { + MSList *it; + switch (state) { - case LinphoneCallConnected: - Conference::addCall(m_focusCall); - m_state = ConnectedToFocus; - m_focusContact = linphone_call_get_remote_contact(m_focusCall); - for (MSList *it = m_pendingCalls; it; it = it->next) { - LinphoneCall *pendingCall = (LinphoneCall *)it->data; - LinphoneCallState pendingCallState = linphone_call_get_state(pendingCall); - if(pendingCallState == LinphoneCallStreamsRunning || pendingCallState == LinphoneCallPaused) { - MSList *current_elem = it; - it = it->next; - linphone_core_transfer_call(m_core, pendingCall, m_focusContact); - m_pendingCalls = ms_list_remove_link(m_pendingCalls, current_elem); - m_transferingCalls = ms_list_append(m_transferingCalls, pendingCall); - continue; - } + 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; + } + break; - case LinphoneCallError: - case LinphoneCallEnd: - Conference::removeCall(m_focusCall); - m_state = NotConnectedToFocus; - m_focusCall = NULL; - m_localParticipantStream = NULL; + case LinphoneCallError: + case LinphoneCallEnd: + Conference::removeCall(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(m_pendingCalls); - m_transferingCalls = ms_list_free(m_transferingCalls); - break; + } + 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; - } + default: break; + } } void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCallState state) { @@ -622,15 +637,17 @@ void RemoteConference::onPendingCallStateChanged(LinphoneCall *call, LinphoneCal case LinphoneCallStreamsRunning: case LinphoneCallPaused: if(m_state == ConnectedToFocus) { - linphone_core_transfer_call(m_core, call, m_focusContact); 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::removeCall(call); m_pendingCalls = ms_list_remove(m_pendingCalls, call); + linphone_call_unref(call); break; default: break; @@ -655,6 +672,7 @@ void RemoteConference::transferStateChanged(LinphoneCore *lc, LinphoneCall *tran case LinphoneCallConnected: case LinphoneCallError: conf->m_transferingCalls = ms_list_remove(conf->m_transferingCalls, transfered); + linphone_call_unref(transfered); break; default: From 88d0c8ef9e7184981341dfaafea2b1aa8bc34384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 17 Dec 2015 10:42:58 +0100 Subject: [PATCH 11/21] Add a test testing the establishment of a conference based on a focus server --- tester/flexisip_tester.c | 24 ++++----- tester/liblinphone_tester.h | 14 ++++- tester/multi_call_tester.c | 87 ++++++++++++++++++++++++------ tester/rcfiles/conference_focus_rc | 26 +++++++++ tester/tester.c | 71 ++++++++++++++++++++---- 5 files changed, 180 insertions(+), 42 deletions(-) create mode 100644 tester/rcfiles/conference_focus_rc diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 2b60a0f80..a5fcd21bb 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -703,7 +703,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; @@ -712,13 +712,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); @@ -803,14 +803,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); @@ -827,14 +827,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 4cbe28046..1784ee54a 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -249,6 +249,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; @@ -258,11 +264,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); @@ -349,6 +356,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; diff --git a/tester/multi_call_tester.c b/tester/multi_call_tester.c index c8021afc3..de6899892 100644 --- a/tester/multi_call_tester.c +++ b/tester/multi_call_tester.c @@ -177,7 +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; @@ -187,9 +187,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,20 +210,35 @@ 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)); 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_EQUAL(linphone_core_get_conference_size(marie->lc),is_remote_conf?-1: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 +261,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 +272,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 +319,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"); @@ -695,22 +715,55 @@ 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); +} 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_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 } }; 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..c4bad5f9d 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,50 @@ 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; + } +} +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->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); +} From 2fcf9d4f2c6c5d8830f11169e7d1aa2aae45395b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 17 Dec 2015 16:08:49 +0100 Subject: [PATCH 12/21] Fix build on Android and autotools --- build/android/Android.mk | 2 +- coreapi/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/android/Android.mk b/build/android/Android.mk index 96fe57e16..d0b2b823b 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/coreapi/Makefile.am b/coreapi/Makefile.am index 02c7f442d..f1be7f86c 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -55,7 +55,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 \ From 6e655d74304f3215db6702c8b762ba0ceb03014b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 18 Dec 2015 18:08:20 +0100 Subject: [PATCH 13/21] 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. --- coreapi/bellesip_sal/sal_op_call_transfer.c | 2 +- coreapi/conference.cc | 339 +++++++++++++------- coreapi/conference.h | 13 +- coreapi/linphonecore.c | 10 +- coreapi/linphonecore.h | 25 +- tester/multi_call_tester.c | 117 +++++-- 6 files changed, 357 insertions(+), 149 deletions(-) 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/conference.cc b/coreapi/conference.cc index 19140773b..d917d7e01 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -27,15 +27,34 @@ #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 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 &getParticipants() const {return m_participants;} virtual int startRecording(const char *path) = 0; virtual int stopRecording() = 0; @@ -57,63 +77,22 @@ public: 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; }; -using namespace Linphone; - -Conference::Conference(LinphoneCore *core) : - m_core(core), - m_localParticipantStream(NULL), - m_isMuted(false) { -} - -int Conference::addCall(LinphoneCall *call) { - call->conf_ref = (LinphoneConference *)this; - return 0; -} - -int Conference::removeCall(LinphoneCall *call) { - call->conf_ref = NULL; - 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; -} - - -namespace Linphone { class LocalConference: public Conference { public: LocalConference(LinphoneCore *core); virtual ~LocalConference(); - virtual int addCall(LinphoneCall *call); - virtual int removeCall(LinphoneCall *call); + virtual int addParticipant(LinphoneCall *call); + virtual int removeParticipant(LinphoneCall *call); + virtual int removeParticipant(const LinphoneAddress *uri); virtual int terminate(); virtual int enter(); @@ -142,8 +121,140 @@ private: 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), @@ -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 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); } diff --git a/coreapi/conference.h b/coreapi/conference.h index aee2ffa8f..b2996ea89 100644 --- a/coreapi/conference.h +++ b/coreapi/conference.h @@ -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); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b544a005b..880271658 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -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; } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index a3d3425bd..97b89a089 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -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); /** * @} diff --git a/tester/multi_call_tester.c b/tester/multi_call_tester.c index de6899892..8ded2285d 100644 --- a/tester/multi_call_tester.c +++ b/tester/multi_call_tester.c @@ -178,7 +178,6 @@ static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_ } 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; @@ -232,7 +231,7 @@ static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManag BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,initial_marie_stat.number_of_LinphoneCallStreamsRunning+2,3000)); BC_ASSERT_TRUE(linphone_core_is_in_conference(marie->lc)); - BC_ASSERT_EQUAL(linphone_core_get_conference_size(marie->lc),is_remote_conf?-1:3, int, "%d"); + BC_ASSERT_EQUAL(linphone_core_get_conference_size(marie->lc),3, int, "%d"); if(!is_remote_conf) { BC_ASSERT_PTR_NULL(linphone_core_get_current_call(marie->lc)); @@ -566,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; @@ -578,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); @@ -597,11 +597,22 @@ 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_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_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)); @@ -610,25 +621,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+1,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+1,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); @@ -748,6 +783,37 @@ void simple_remote_conference(void) { 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 }, @@ -757,13 +823,14 @@ test_t multi_call_tests[] = { { "Simple conference", simple_conference }, { "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_conference }, + { "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 }, - { "Simple remote conference", simple_remote_conference } + { "Simple remote conference", simple_remote_conference }, + { "Eject from 3 participants 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, From ca05715af6fda57261069a0148c5af1680820790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 4 Jan 2016 16:45:18 +0100 Subject: [PATCH 14/21] Fix crash in JNI concerning the removeListener() function --- coreapi/conference.cc | 13 +++++++------ coreapi/linphonecore.c | 2 +- coreapi/linphonecore_jni.cc | 2 +- coreapi/private.h | 3 ++- coreapi/vtables.c | 9 +++++---- gtk/main.c | 2 +- tester/multi_call_tester.c | 4 +++- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index d917d7e01..df8a6c92f 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -562,7 +562,7 @@ RemoteConference::RemoteConference(LinphoneCore *core): 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); + _linphone_core_add_listener(m_core, m_vtable, FALSE, TRUE); } RemoteConference::~RemoteConference() { @@ -609,7 +609,7 @@ int RemoteConference::addParticipant(LinphoneCall *call) { int RemoteConference::removeParticipant(const LinphoneAddress *uri) { SalOp *op; const char *from; - LinphoneAddress *refer_to; + char *tmp, *refer_to; int res; switch(m_state) { @@ -620,10 +620,11 @@ int RemoteConference::removeParticipant(const LinphoneAddress *uri) { 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); + tmp = linphone_address_as_string_uri_only(uri); + refer_to = ms_strdup_printf("%s;method=BYE", tmp); + res = sal_call_refer(op, refer_to); + ms_free(tmp); + ms_free(refer_to); if(res == 0) return Conference::removeParticipant(uri); else return -1; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 880271658..a81293963 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1675,7 +1675,7 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_task_list_init(&lc->hooks); 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(); diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 433efcae4..f6361e389 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -1217,7 +1217,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); diff --git a/coreapi/private.h b/coreapi/private.h index 23fcbd760..57a924cc8 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1356,13 +1356,14 @@ 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); +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 b01a91f08..226f46be0 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -253,10 +253,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; } @@ -266,13 +267,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/main.c b/gtk/main.c index 7dc818271..8dd0f8453 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -823,7 +823,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); diff --git a/tester/multi_call_tester.c b/tester/multi_call_tester.c index 8ded2285d..a8747ba62 100644 --- a/tester/multi_call_tester.c +++ b/tester/multi_call_tester.c @@ -602,6 +602,7 @@ static void eject_from_3_participants_conference(LinphoneCoreManager *marie, Lin 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)); @@ -612,6 +613,7 @@ static void eject_from_3_participants_conference(LinphoneCoreManager *marie, Lin 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)); @@ -639,7 +641,7 @@ static void eject_from_3_participants_conference(LinphoneCoreManager *marie, Lin 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+1,5000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,initial_pauline_stat.number_of_LinphoneCallEnd+2,5000)); } if(!is_remote_conf) { From a49cd7566cfd7c74ca8ba9799884a85d4d396419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 10:03:14 +0100 Subject: [PATCH 15/21] Add a setter/getter for the method param to LinphoneAddress --- coreapi/address.c | 14 ++++++++++++++ coreapi/bellesip_sal/sal_address_impl.c | 13 +++++++++++++ coreapi/linphonecore.h | 2 ++ include/sal/sal.h | 2 ++ 4 files changed, 31 insertions(+) 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/linphonecore.h b/coreapi/linphonecore.h index 97b89a089..60d48fc9c 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -449,6 +449,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); diff --git a/include/sal/sal.h b/include/sal/sal.h index 6e1c4d068..a4e229ec8 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -102,6 +102,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); @@ -114,6 +115,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); From d7e761b09e132c0942d12e3497f0006d6c82415c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 10:07:40 +0100 Subject: [PATCH 16/21] Send REFER with BYE in dialog --- coreapi/conference.cc | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index df8a6c92f..6cc888a2f 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -607,22 +607,14 @@ int RemoteConference::addParticipant(LinphoneCall *call) { } int RemoteConference::removeParticipant(const LinphoneAddress *uri) { - SalOp *op; - const char *from; char *tmp, *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); - tmp = linphone_address_as_string_uri_only(uri); refer_to = ms_strdup_printf("%s;method=BYE", tmp); - res = sal_call_refer(op, refer_to); + res = sal_call_refer(m_focusCall->op, refer_to); ms_free(tmp); ms_free(refer_to); From 95c893dcc1de29a72836ea98e0915eceffc1e88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 13:54:22 +0100 Subject: [PATCH 17/21] Wrap LinphoneConference class into Java --- coreapi/linphonecore_jni.cc | 30 +++++++++++++ .../org/linphone/core/LinphoneCall.java | 11 ++++- .../org/linphone/core/LinphoneConference.java | 39 +++++++++++++++++ .../org/linphone/core/LinphoneCore.java | 7 +++ .../org/linphone/core/LinphoneCallImpl.java | 7 ++- .../linphone/core/LinphoneConferenceImpl.java | 43 +++++++++++++++++++ .../org/linphone/core/LinphoneCoreImpl.java | 4 ++ 7 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 java/common/org/linphone/core/LinphoneConference.java create mode 100644 java/impl/org/linphone/core/LinphoneConferenceImpl.java diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index f6361e389..7c3714d85 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -4135,6 +4135,10 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore); } +extern "C" jlong Jave_org_linphone_core_LinphoneCoreImpl_getConference(JNIEnv *env, jobject thiz, jlong pCore) { + return (jlong)linphone_core_get_conference((LinphoneCore *)pCore); +} + extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startConferenceRecording(JNIEnv *env,jobject thiz,jlong pCore, jstring jpath){ int err=-1; if (jpath){ @@ -4269,6 +4273,10 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVe linphone_call_set_authentication_token_verified(call, verified); } +extern "C" jlong Java_org_linphnoe_core_LinphoneCallImpl_getConference(JNIEnv *env, jobject thiz, jlong ptr) { + return (jlong)linphone_call_get_conference((LinphoneCall *)ptr); +} + 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); @@ -6570,4 +6578,26 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getSipTransportTi return linphone_core_get_sip_transport_timeout((LinphoneCore*)pcore); } +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/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 f2ce0ccab..0ff6c25bd 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1407,6 +1407,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. + **/ + long 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..4e77a3414 --- /dev/null +++ b/java/impl/org/linphone/core/LinphoneConferenceImpl.java @@ -0,0 +1,43 @@ +/* +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 void finalize(long nativePtr); + protected void finalize() { + finalize(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 87842c78e..c81100c5c 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -709,6 +709,10 @@ class LinphoneCoreImpl implements LinphoneCore { public synchronized int getConferenceSize() { return getConferenceSize(nativePtr); } + private native long getConference(long nativePtr); + public synchronized long getConference() { + return getConference(nativePtr); + } private native int getCallsNb(long nativePtr); public synchronized int getCallsNb() { return getCallsNb(nativePtr); From 88c0123983cbe7e21156daf2fd3ae1b0ce1c8265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 14:47:39 +0100 Subject: [PATCH 18/21] Fix JNI of getConference() methods of LinphoneCore and LinphoneCall --- coreapi/linphonecore_jni.cc | 16 ++++++++++++---- java/common/org/linphone/core/LinphoneCore.java | 2 +- .../impl/org/linphone/core/LinphoneCoreImpl.java | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 7c3714d85..6afa06741 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -4135,8 +4135,12 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore); } -extern "C" jlong Jave_org_linphone_core_LinphoneCoreImpl_getConference(JNIEnv *env, jobject thiz, jlong pCore) { - return (jlong)linphone_core_get_conference((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){ @@ -4273,8 +4277,12 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVe linphone_call_set_authentication_token_verified(call, verified); } -extern "C" jlong Java_org_linphnoe_core_LinphoneCallImpl_getConference(JNIEnv *env, jobject thiz, jlong ptr) { - return (jlong)linphone_call_get_conference((LinphoneCall *)ptr); +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) { diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 0ff6c25bd..913a7fe11 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1413,7 +1413,7 @@ public interface LinphoneCore { * That function can be used to test whether a conference is running. * @return A positive value if a conference is running, 0 if not. **/ - long getConference(); + LinphoneConference getConference(); /** * Request recording of the conference into a supplied file path. diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index c81100c5c..6b84e310c 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -709,8 +709,8 @@ class LinphoneCoreImpl implements LinphoneCore { public synchronized int getConferenceSize() { return getConferenceSize(nativePtr); } - private native long getConference(long nativePtr); - public synchronized long getConference() { + private native LinphoneConference getConference(long nativePtr); + public synchronized LinphoneConference getConference() { return getConference(nativePtr); } private native int getCallsNb(long nativePtr); From dc62411463b66139f301bf069cb8bfe86eae27b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 14:47:39 +0100 Subject: [PATCH 19/21] Fix JNI of getConference() methods of LinphoneCore and LinphoneCall --- coreapi/linphonecore_jni.cc | 17 +++++++++++++---- java/common/org/linphone/core/LinphoneCore.java | 2 +- .../linphone/core/LinphoneConferenceImpl.java | 7 +++---- .../org/linphone/core/LinphoneCoreImpl.java | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 7c3714d85..7559d2c8f 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -4135,8 +4135,12 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_getConferenceSize(JNIEnv return (jint)linphone_core_get_conference_size((LinphoneCore *) pCore); } -extern "C" jlong Jave_org_linphone_core_LinphoneCoreImpl_getConference(JNIEnv *env, jobject thiz, jlong pCore) { - return (jlong)linphone_core_get_conference((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){ @@ -4273,8 +4277,12 @@ extern "C" void Java_org_linphone_core_LinphoneCallImpl_setAuthenticationTokenVe linphone_call_set_authentication_token_verified(call, verified); } -extern "C" jlong Java_org_linphnoe_core_LinphoneCallImpl_getConference(JNIEnv *env, jobject thiz, jlong ptr) { - return (jlong)linphone_call_get_conference((LinphoneCall *)ptr); +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) { @@ -6578,6 +6586,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_LinphoneCoreImpl_getSipTransportTi return linphone_core_get_sip_transport_timeout((LinphoneCore*)pcore); } + 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"); diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 0ff6c25bd..913a7fe11 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1413,7 +1413,7 @@ public interface LinphoneCore { * That function can be used to test whether a conference is running. * @return A positive value if a conference is running, 0 if not. **/ - long getConference(); + LinphoneConference getConference(); /** * Request recording of the conference into a supplied file path. diff --git a/java/impl/org/linphone/core/LinphoneConferenceImpl.java b/java/impl/org/linphone/core/LinphoneConferenceImpl.java index 4e77a3414..2a3519302 100644 --- a/java/impl/org/linphone/core/LinphoneConferenceImpl.java +++ b/java/impl/org/linphone/core/LinphoneConferenceImpl.java @@ -25,17 +25,16 @@ import java.util.List; public class LinphoneConferenceImpl implements LinphoneConference { private final long nativePtr; + private LinphoneConferenceImpl(long nativePtr) { this.nativePtr = nativePtr; } - private native void finalize(long nativePtr); - protected void finalize() { - finalize(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 c81100c5c..6b84e310c 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -709,8 +709,8 @@ class LinphoneCoreImpl implements LinphoneCore { public synchronized int getConferenceSize() { return getConferenceSize(nativePtr); } - private native long getConference(long nativePtr); - public synchronized long getConference() { + private native LinphoneConference getConference(long nativePtr); + public synchronized LinphoneConference getConference() { return getConference(nativePtr); } private native int getCallsNb(long nativePtr); From fe6d7528f4efc3bd30b74897a536364689cf3978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 16:36:04 +0100 Subject: [PATCH 20/21] Makes Linphone ignore REFER requests with BYE method set --- coreapi/callbacks.c | 10 +++++++++- gtk/main.c | 23 ++++++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index a0035d2ed..a48422f1f 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/gtk/main.c b/gtk/main.c index 8dd0f8453..a5700c6ea 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1958,13 +1958,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){ From 3f1d66d10433d5c16a7ddb880cfd0ba3679eb84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 6 Jan 2016 16:38:19 +0100 Subject: [PATCH 21/21] Fix removing one participant tester --- tester/multi_call_tester.c | 4 ++-- tester/tester.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tester/multi_call_tester.c b/tester/multi_call_tester.c index a8747ba62..61135aecd 100644 --- a/tester/multi_call_tester.c +++ b/tester/multi_call_tester.c @@ -653,7 +653,7 @@ static void eject_from_3_participants_conference(LinphoneCoreManager *marie, Lin 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+1,3000)); + 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)); } @@ -832,7 +832,7 @@ test_t multi_call_tests[] = { { "Unattended call transfer with error", unattended_call_transfer_with_error }, { "Call transfer existing call outgoing call", call_transfer_existing_call_outgoing_call }, { "Simple remote conference", simple_remote_conference }, - { "Eject from 3 participants remote conference", eject_from_3_participants_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/tester.c b/tester/tester.c index c4bad5f9d..40f1461e2 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -700,12 +700,31 @@ void linphone_conference_server_call_state_changed(LinphoneCore *lc, LinphoneCal } } +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);