From 3b0a9c00485972b13d472eb7c1664aff11af8ae3 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 15 May 2017 18:06:08 +0200 Subject: [PATCH] add new method to create a conference from a list of participants. --- coreapi/conference.cc | 52 ++++++++++++++++++++++++- coreapi/linphonecore.c | 44 ++++++++------------- include/linphone/conference.h | 8 ++++ include/linphone/core.h | 13 ++++++- tester/call_multi_tester.c | 72 +++++++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 31 deletions(-) diff --git a/coreapi/conference.cc b/coreapi/conference.cc index 51541ae78..273b2dfd0 100644 --- a/coreapi/conference.cc +++ b/coreapi/conference.cc @@ -33,6 +33,15 @@ namespace Linphone { +template +inline std::list<_type> toStd(const bctbx_list_t *l){ + std::list<_type> ret; + for(; l != NULL; l = l->next){ + ret.push_back(static_cast<_type>(l->data)); + } + return ret; +} + class Conference { public: class Participant { @@ -82,7 +91,7 @@ public: void enableVideo(bool enable) {m_enableVideo = enable;} bool videoRequested() const {return m_enableVideo;} void setStateChangedCallback(LinphoneConferenceStateChangedCb cb, void *userData) { - m_stateChangedCb =cb; + m_stateChangedCb = cb; m_userData = userData; } @@ -99,6 +108,7 @@ public: const Params &getCurrentParams() const {return m_currentParams;} + virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params) = 0; virtual int addParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(LinphoneCall *call) = 0; virtual int removeParticipant(const LinphoneAddress *uri) = 0; @@ -124,6 +134,9 @@ public: virtual void onCallTerminating(LinphoneCall *call) {}; LinphoneConferenceState getState() const {return m_state;} + LinphoneCore *getCore()const{ + return m_core; + } static const char *stateToString(LinphoneConferenceState state); protected: @@ -146,6 +159,7 @@ public: LocalConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL); virtual ~LocalConference(); + virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params); virtual int addParticipant(LinphoneCall *call); virtual int removeParticipant(LinphoneCall *call); virtual int removeParticipant(const LinphoneAddress *uri); @@ -183,6 +197,7 @@ public: RemoteConference(LinphoneCore *core, LinphoneConference *conf, const Params *params = NULL); virtual ~RemoteConference(); + virtual int inviteAddresses(const std::list &addresses, const LinphoneCallParams *params); virtual int addParticipant(LinphoneCall *call); virtual int removeParticipant(LinphoneCall *call) {return -1;} virtual int removeParticipant(const LinphoneAddress *uri); @@ -382,6 +397,31 @@ void LocalConference::addLocalEndpoint() { ms_audio_conference_add_member(m_conf,m_localEndpoint); } +int LocalConference::inviteAddresses(const std::list &addresses, const LinphoneCallParams *params){ + + for (auto it = addresses.begin(); it != addresses.end(); ++it){ + const LinphoneAddress *addr = *it; + LinphoneCall * call = linphone_core_get_call_by_remote_address2(m_core, addr); + if (!call){ + /*start a new call by indicating that it has to be put into the conference directlly*/ + LinphoneCallParams * new_params = params ? linphone_call_params_copy(params) : linphone_core_create_call_params(m_core, NULL); + LinphoneCall *call; + /*toggle this flag so the call is immediately added to the conference upon acceptance*/ + new_params->in_conference = TRUE; + call = linphone_core_invite_address_with_params(m_core, addr, new_params); + if (!call){ + ms_error("LocalConference::inviteAddresses(): could not invite participant"); + } + linphone_call_params_unref(new_params); + }else{ + /*there is already a call to this address, so simply join it to the local conference if not already done*/ + if (!call->current_params->in_conference) + addParticipant(call); + } + } + return 0; +} + int LocalConference::addParticipant(LinphoneCall *call) { if (call->current_params->in_conference){ ms_error("Already in conference"); @@ -590,6 +630,7 @@ int LocalConference::stopRecording() { void LocalConference::onCallStreamStarting(LinphoneCall *call, bool isPausedByRemote) { call->params->has_video = FALSE; call->camera_enabled = FALSE; + ms_message("LocalConference::onCallStreamStarting(): joining AudioStream [%p] of call [%p] into conference.", call->audiostream, call); 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); @@ -643,6 +684,11 @@ RemoteConference::~RemoteConference() { linphone_core_cbs_unref(m_coreCbs); } +int RemoteConference::inviteAddresses(const std::list &addresses, const LinphoneCallParams *params){ + ms_error("RemoteConference::inviteAddresses() not implemented"); + return -1; +} + int RemoteConference::addParticipant(LinphoneCall *call) { LinphoneAddress *addr; LinphoneCallParams *params; @@ -1116,3 +1162,7 @@ bool_t linphone_conference_check_class(LinphoneConference *obj, LinphoneConferen default: return FALSE; } } + +LinphoneStatus linphone_conference_invite_participants(LinphoneConference *obj, const bctbx_list_t *addresses, const LinphoneCallParams *params){ + return obj->conf->inviteAddresses(toStd(addresses), params); +} diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 782d9e7d4..bf51cd179 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3471,8 +3471,11 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const bool_t defer = FALSE; LinphoneCallParams *cp; - if (!(!linphone_call_params_audio_enabled(params) || linphone_call_params_get_audio_direction(params) == LinphoneMediaDirectionInactive) - && linphone_core_preempt_sound_resources(lc) == -1){ + if (!(!linphone_call_params_audio_enabled(params) || + linphone_call_params_get_audio_direction(params) == LinphoneMediaDirectionInactive || + linphone_call_params_get_local_conference_mode(params) == TRUE + ) + && linphone_core_preempt_sound_resources(lc) == -1) { ms_error("linphone_core_invite_address_with_params(): sound is required for this call but another call is already locking the sound resource. Call attempt is rejected."); return NULL; } @@ -3776,7 +3779,7 @@ static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *rad return !linphone_address_weak_equal (addr,raddr); } -LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){ +LinphoneCall *linphone_core_get_call_by_remote_address(const LinphoneCore *lc, const char *remote_address){ LinphoneCall *call=NULL; LinphoneAddress *raddr=linphone_address_new(remote_address); if (raddr) { @@ -3786,8 +3789,12 @@ LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const c return call; } -LinphoneCall *linphone_core_get_call_by_remote_address2(LinphoneCore *lc, const LinphoneAddress *raddr){ - bctbx_list_t *elem=bctbx_list_find_custom(lc->calls,(int (*)(const void*,const void *))remote_address_compare,raddr); +LinphoneCall *linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *remote_address){ + return linphone_core_get_call_by_remote_address(lc, remote_address); +} + +LinphoneCall *linphone_core_get_call_by_remote_address2(const LinphoneCore *lc, const LinphoneAddress *raddr){ + const bctbx_list_t *elem=bctbx_list_find_custom(lc->calls,(int (*)(const void*,const void *))remote_address_compare,raddr); if (elem) return (LinphoneCall*) elem->data; return NULL; @@ -6580,29 +6587,6 @@ const char *linphone_core_get_user_certificates_path(LinphoneCore *lc){ return lc->user_certificates_path; } -LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCore *lc, const char *uri) { - bctbx_list_t *calls; - LinphoneCall *c; - const LinphoneAddress *address; - char *current_uri; - - if (uri == NULL) return NULL; - calls=lc->calls; - while(calls) { - c=(LinphoneCall*)calls->data; - calls=calls->next; - address = linphone_call_get_remote_address(c); - current_uri=linphone_address_as_string_uri_only(address); - if (strcmp(uri,current_uri)==0) { - ms_free(current_uri); - return c; - } else { - ms_free(current_uri); - } - } - return NULL; -} - bool_t linphone_core_sound_resources_locked(LinphoneCore *lc){ bctbx_list_t *elem; for(elem=lc->calls;elem!=NULL;elem=elem->next) { @@ -7068,6 +7052,10 @@ LinphoneConference *linphone_core_create_conference_with_params(LinphoneCore *lc return lc->conf_ctx; } +LinphoneConferenceParams * linphone_core_create_conference_params(LinphoneCore *lc){ + return linphone_conference_params_new(lc); +} + LinphoneStatus linphone_core_add_to_conference(LinphoneCore *lc, LinphoneCall *call) { LinphoneConference *conference = linphone_core_get_conference(lc); if(conference == NULL) { diff --git a/include/linphone/conference.h b/include/linphone/conference.h index c7f7702aa..ab2ad2add 100644 --- a/include/linphone/conference.h +++ b/include/linphone/conference.h @@ -117,6 +117,14 @@ LINPHONE_PUBLIC LinphoneStatus linphone_conference_remove_participant(LinphoneCo */ LINPHONE_PUBLIC bctbx_list_t *linphone_conference_get_participants(const LinphoneConference *obj); +/** + * Invite participants to the conference, by supplying a list of LinphoneAddress + * @param obj The conference. + * @param addresses bctbx_list_t of #LinphoneAddress object + * @param params #LinphoneCallParams to use for inviting the participants. +**/ +LINPHONE_PUBLIC LinphoneStatus linphone_conference_invite_participants(LinphoneConference *conf, const bctbx_list_t *addresses, const LinphoneCallParams *params); + /** * @} */ diff --git a/include/linphone/core.h b/include/linphone/core.h index b4085350f..434993a1d 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -1300,7 +1300,7 @@ LINPHONE_PUBLIC LinphoneCallParams *linphone_core_create_call_params(LinphoneCor * @return The call if it has been found, NULL otherwise * @ingroup call_control */ -LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address); +LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address(const LinphoneCore *lc, const char *remote_address); /** * Get the call with the remote_address specified @@ -1310,7 +1310,7 @@ LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneC * * @ingroup call_control */ -LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address2(LinphoneCore *lc, const LinphoneAddress *remote_address); +LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address2(const LinphoneCore *lc, const LinphoneAddress *remote_address); /** @@ -4013,6 +4013,14 @@ LINPHONE_PUBLIC LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCor * @{ */ +/** + * Create some default conference parameters for instanciating a a conference with linphone_core_create_conference_with_params(). + * @param lc the core + * @return conference parameters. +**/ +LINPHONE_PUBLIC LinphoneConferenceParams * linphone_core_create_conference_params(LinphoneCore *lc); + + /** * Create a conference * @param lc The #LinphoneCore instance where the conference will be created inside. @@ -4022,6 +4030,7 @@ LINPHONE_PUBLIC LinphoneCall* linphone_core_find_call_from_uri(const LinphoneCor */ LINPHONE_PUBLIC LinphoneConference *linphone_core_create_conference_with_params(LinphoneCore *lc, const LinphoneConferenceParams *params); + /** * Add a participant to the conference. If no conference is going on * a new internal conference context is created and the participant is diff --git a/tester/call_multi_tester.c b/tester/call_multi_tester.c index beb3668d4..fed22b5a0 100644 --- a/tester/call_multi_tester.c +++ b/tester/call_multi_tester.c @@ -381,6 +381,77 @@ static void simple_conference(void) { linphone_core_manager_destroy(laure); } + +static void simple_conference_from_scratch(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_udp"); + LinphoneConference *conf; + LinphoneConferenceParams *conf_params; + LinphoneCall *pauline_call, *laure_call; + bctbx_list_t *participants = NULL; + bctbx_list_t *lcs = NULL; + + lcs = bctbx_list_append(lcs, marie->lc); + lcs = bctbx_list_append(lcs, pauline->lc); + lcs = bctbx_list_append(lcs, laure->lc); + + /*marie creates the conference*/ + conf_params = linphone_core_create_conference_params(marie->lc); + linphone_conference_params_enable_video(conf_params, FALSE); + conf = linphone_core_create_conference_with_params(marie->lc, conf_params); + linphone_conference_params_unref(conf_params); + + participants = bctbx_list_append(participants, pauline->identity); + participants = bctbx_list_append(participants, laure->identity); + + linphone_conference_invite_participants(conf, participants, NULL); + + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallOutgoingProgress,2,2000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallIncomingReceived,1,10000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallIncomingReceived,1,10000)); + + pauline_call = linphone_core_get_current_call(pauline->lc); + laure_call = linphone_core_get_current_call(laure->lc); + + BC_ASSERT_PTR_NOT_NULL(pauline_call); + BC_ASSERT_PTR_NOT_NULL(laure_call); + + if (pauline_call && laure_call){ + const bctbx_list_t *marie_calls, *it; + linphone_call_accept(pauline_call); + linphone_call_accept(laure_call); + + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallConnected,2,10000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallStreamsRunning,2,3000)); + + /*make sure that the two calls from Marie's standpoint are in conference*/ + marie_calls = linphone_core_get_calls(marie->lc); + BC_ASSERT_EQUAL(bctbx_list_size(marie_calls), 2, int, "%i"); + for (it = marie_calls; it != NULL; it = it->next){ + BC_ASSERT_TRUE(linphone_call_params_get_local_conference_mode(linphone_call_get_current_params((LinphoneCall*)it->data)) == TRUE); + } + /*wait a bit for the conference audio processing to run, despite we do not test it for the moment*/ + wait_for_list(lcs,NULL,0,5000); + + linphone_core_terminate_conference(marie->lc); + BC_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,2,5000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->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,&marie->stat.number_of_LinphoneCallReleased,2,1000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallReleased,1,1000)); + BC_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallReleased,1,1000)); + } + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(laure); + + bctbx_list_free(participants); + bctbx_list_free(lcs); +} + static void simple_encrypted_conference_with_ice(LinphoneMediaEncryption mode) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); @@ -1074,6 +1145,7 @@ test_t multi_call_tests[] = { TEST_NO_TAG("Incoming call accepted when outgoing call in outgoing ringing", incoming_call_accepted_when_outgoing_call_in_outgoing_ringing), TEST_NO_TAG("Incoming call accepted when outgoing call in outgoing ringing early media", incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_media), TEST_NO_TAG("Simple conference", simple_conference), + TEST_NO_TAG("Simple conference established from scractch", simple_conference_from_scratch), TEST_ONE_TAG("Simple conference with ICE", simple_conference_with_ice, "ICE"), TEST_ONE_TAG("Simple ZRTP conference with ICE", simple_zrtp_conference_with_ice, "ICE"), TEST_NO_TAG("Eject from 3 participants conference", eject_from_3_participants_local_conference),