/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 3 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, see . */ #include "streams.h" #include "mixers.h" #include "linphone/core.h" #include "private.h" #include "mediastreamer2/msvolume.h" LINPHONE_BEGIN_NAMESPACE MS2AudioMixer::MS2AudioMixer(MixerSession &session) : StreamMixer(session){ MSAudioConferenceParams ms_conf_params; ms_conf_params.samplerate = lp_config_get_int(mSession.getCCore()->config, "sound", "conference_rate", 16000); ms_conf_params.active_talker_callback = &MS2AudioMixer::sOnActiveTalkerChanged; ms_conf_params.user_data = this; mConference = ms_audio_conference_new(&ms_conf_params, mSession.getCCore()->factory); } MS2AudioMixer::~MS2AudioMixer(){ if (mTimer){ mSession.getCore().destroyTimer(mTimer); } if (mRecordEndpoint) { stopRecording(); } if (mLocalEndpoint){ removeLocalParticipant(); } ms_audio_conference_destroy(mConference); } void MS2AudioMixer::addListener(AudioMixerListener *listener){ if (mTimer == nullptr){ // Start the monitoring of the active talker since somebody wants this information. mTimer = mSession.getCore().createTimer([this]() -> bool{ ms_audio_conference_process_events(mConference); return true; }, 50, "AudioConference events timer"); } mListeners.push_back(listener); } void MS2AudioMixer::removeListener(AudioMixerListener *listener){ mListeners.remove(listener); } void MS2AudioMixer::sOnActiveTalkerChanged(MSAudioConference *audioconf, MSAudioEndpoint *ep){ const MSAudioConferenceParams *params = ms_audio_conference_get_params(audioconf); MS2AudioMixer *zis = static_cast(params->user_data); zis->onActiveTalkerChanged(ep); } void MS2AudioMixer::onActiveTalkerChanged(MSAudioEndpoint *ep){ StreamsGroup *sg = (StreamsGroup*)ms_audio_endpoint_get_user_data(ep); for (auto & l : mListeners){ l->onActiveTalkerChanged(sg); } } void MS2AudioMixer::connectEndpoint(Stream *as, MSAudioEndpoint *endpoint, bool muted){ ms_audio_endpoint_set_user_data(endpoint, &as->getGroup()); ms_audio_conference_add_member(mConference, endpoint); ms_audio_conference_mute_member(mConference, endpoint, muted); } void MS2AudioMixer::disconnectEndpoint(Stream *as, MSAudioEndpoint *endpoint){ ms_audio_endpoint_set_user_data(endpoint, nullptr); ms_audio_conference_remove_member(mConference, endpoint); } RtpProfile *MS2AudioMixer::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 MS2AudioMixer::addLocalParticipant(){ LinphoneCore *core = getSession().getCCore(); AudioStream *st = audio_stream_new(core->factory, 65000, 65001, FALSE); MSSndCard *playcard = core->sound_conf.lsd_card ? core->sound_conf.lsd_card : core->sound_conf.play_sndcard; MSSndCard *captcard = core->sound_conf.capt_sndcard; const MSAudioConferenceParams *params = ms_audio_conference_get_params(mConference); mLocalDummyProfile = sMakeDummyProfile(params->samplerate); audio_stream_start_full(st, mLocalDummyProfile, "127.0.0.1", 65000, "127.0.0.1", 65001, 0, 40, nullptr, nullptr, playcard, captcard, linphone_core_echo_cancellation_enabled(core) ); _post_configure_audio_stream(st, core, FALSE); mLocalParticipantStream = st; mLocalEndpoint = ms_audio_endpoint_get_from_stream(st, FALSE); ms_message("Conference: adding local endpoint"); ms_audio_conference_add_member(mConference, mLocalEndpoint); } void MS2AudioMixer::removeLocalParticipant(){ if (mLocalEndpoint) { ms_audio_conference_remove_member(mConference, mLocalEndpoint); ms_audio_endpoint_release_from_stream(mLocalEndpoint); mLocalEndpoint = nullptr; audio_stream_stop(mLocalParticipantStream); mLocalParticipantStream = nullptr; rtp_profile_destroy(mLocalDummyProfile); mLocalDummyProfile = nullptr; } } void MS2AudioMixer::enableLocalParticipant(bool value){ /* 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. */ if (value && !mLocalParticipantStream){ addLocalParticipant(); }else if (!value && mLocalParticipantStream){ removeLocalParticipant(); } } void MS2AudioMixer::setRecordPath(const std::string &path){ mRecordPath = path; } void MS2AudioMixer::enableMic(bool value){ mLocalMicEnabled = value; if (mLocalEndpoint) ms_audio_conference_mute_member(mConference, mLocalEndpoint, !value); } bool MS2AudioMixer::micEnabled()const{ return mLocalMicEnabled; } void MS2AudioMixer::enableSpeaker(bool value){ } bool MS2AudioMixer::speakerEnabled()const{ return false; } void MS2AudioMixer::startRecording(){ if (mRecordPath.empty()){ lError() << "MS2AudioMixer:startRecording(): no path set."; return; } if (!mRecordEndpoint) { mRecordEndpoint = ms_audio_endpoint_new_recorder(getSession().getCCore()->factory); ms_audio_conference_add_member(mConference, mRecordEndpoint); } ms_audio_recorder_endpoint_start(mRecordEndpoint, mRecordPath.c_str()); } void MS2AudioMixer::stopRecording(){ if (!mRecordEndpoint) { lWarning() << "MS2AudioMixer::stopRecording(): no record currently active"; return ; } ms_audio_recorder_endpoint_stop(mRecordEndpoint); ms_audio_conference_remove_member(mConference, mRecordEndpoint); ms_audio_endpoint_destroy(mRecordEndpoint); mRecordEndpoint = nullptr; } bool MS2AudioMixer::isRecording(){ return mRecordEndpoint != nullptr; } float MS2AudioMixer::getPlayVolume(){ AudioStream *st = mLocalParticipantStream; if (st && st->volrecv) { float vol = 0; ms_filter_call_method(st->volrecv, MS_VOLUME_GET, &vol); return vol; } return LINPHONE_VOLUME_DB_LOWEST; } float MS2AudioMixer::getRecordVolume(){ AudioStream *st = mLocalParticipantStream; if (st && st->volsend && mLocalMicEnabled) { float vol = 0; ms_filter_call_method(st->volsend, MS_VOLUME_GET, &vol); return vol; } return LINPHONE_VOLUME_DB_LOWEST; } float MS2AudioMixer::getMicGain(){ return 0.0; } void MS2AudioMixer::setMicGain(float value){ } float MS2AudioMixer::getSpeakerGain(){ return 0.0; } void MS2AudioMixer::setSpeakerGain(float value){ } void MS2AudioMixer::setRoute(LinphoneAudioRoute route){ } void MS2AudioMixer::sendDtmf(int dtmf){ } void MS2AudioMixer::enableEchoCancellation(bool value){ } bool MS2AudioMixer::echoCancellationEnabled()const{ return linphone_core_echo_cancellation_enabled(getSession().getCCore()); } AudioStream * MS2AudioMixer::getAudioStream(){ return mLocalParticipantStream; } LINPHONE_END_NAMESPACE