/* * core-call.cpp * Copyright (C) 2010-2018 Belledonne Communications SARL * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "core-p.h" #include "call/call-p.h" #include "conference/session/call-session-p.h" #include "logger/logger.h" // TODO: Remove me later. #include "c-wrapper/c-wrapper.h" #include // ============================================================================= using namespace std; LINPHONE_BEGIN_NAMESPACE int CorePrivate::addCall (const shared_ptr &call) { L_Q(); L_ASSERT(call); if (!canWeAddCall()) return -1; if (!hasCalls()) notifySoundcardUsage(true); calls.push_back(call); linphone_core_notify_call_created(q->getCCore(), L_GET_C_BACK_PTR(call)); return 0; } bool CorePrivate::canWeAddCall () const { L_Q(); if (q->getCallCount() < static_cast(q->getCCore()->max_calls)) return true; lInfo() << "Maximum amount of simultaneous calls reached!"; return false; } bool CorePrivate::inviteReplacesABrokenCall (SalCallOp *op) { CallSession *replacedSession = nullptr; SalCallOp *replacedOp = op->get_replaces(); if (replacedOp) replacedSession = reinterpret_cast(replacedOp->get_user_pointer()); for (const auto &call : calls) { shared_ptr session = call->getPrivate()->getActiveSession(); if (session && ((session->getPrivate()->isBroken() && op->compare_op(session->getPrivate()->getOp())) || ((replacedSession == session.get()) && (strcmp(op->get_from(), replacedOp->get_from()) == 0) && (strcmp(op->get_to(), replacedOp->get_to()) == 0))) ) { session->getPrivate()->replaceOp(op); return true; } } return false; } bool CorePrivate::isAlreadyInCallWithAddress (const Address &addr) const { for (const auto &call : calls) { if (call->getRemoteAddress().weakEqual(addr)) return true; } return false; } void CorePrivate::iterateCalls (time_t currentRealTime, bool oneSecondElapsed) const { for (const auto &call : calls) { call->getPrivate()->iterate(currentRealTime, oneSecondElapsed); } } void CorePrivate::notifySoundcardUsage (bool used) { L_Q(); MSSndCard *card = q->getCCore()->sound_conf.capt_sndcard; if (card && (ms_snd_card_get_capabilities(card) & MS_SND_CARD_CAP_IS_SLOW)) ms_snd_card_set_usage_hint(card, used); } int CorePrivate::removeCall (const shared_ptr &call) { L_ASSERT(call); auto iter = find(calls.begin(), calls.end(), call); if (iter == calls.end()) { lWarning() << "Could not find the call to remove"; return -1; } calls.erase(iter); return 0; } void CorePrivate::unsetVideoWindowId (bool preview, void *id) { #ifdef VIDEO_ENABLED for (const auto &call : calls) { VideoStream *vstream = reinterpret_cast(call->getPrivate()->getMediaStream(LinphoneStreamTypeVideo)); if (vstream) { if (preview) video_stream_set_native_preview_window_id(vstream, id); else video_stream_set_native_window_id(vstream, id); } } #endif } // ----------------------------------------------------------------------------- void CorePrivate::parameterizeEqualizer (AudioStream *stream) { L_Q(); LinphoneConfig *config = linphone_core_get_config(q->getCCore()); const char *eqActive = lp_config_get_string(config, "sound", "eq_active", nullptr); if (eqActive) lWarning() << "'eq_active' linphonerc parameter has no effect anymore. Please use 'mic_eq_active' or 'spk_eq_active' instead"; const char *eqGains = lp_config_get_string(config, "sound", "eq_gains", nullptr); if(eqGains) lWarning() << "'eq_gains' linphonerc parameter has no effect anymore. Please use 'mic_eq_gains' or 'spk_eq_gains' instead"; if (stream->mic_equalizer) { MSFilter *f = stream->mic_equalizer; bool enabled = !!lp_config_get_int(config, "sound", "mic_eq_active", 0); ms_filter_call_method(f, MS_EQUALIZER_SET_ACTIVE, &enabled); const char *gains = lp_config_get_string(config, "sound", "mic_eq_gains", nullptr); if (enabled && gains) { bctbx_list_t *gainsList = ms_parse_equalizer_string(gains); for (bctbx_list_t *it = gainsList; it; it = bctbx_list_next(it)) { MSEqualizerGain *g = reinterpret_cast(bctbx_list_get_data(it)); lInfo() << "Read microphone equalizer gains: " << g->frequency << "(~" << g->width << ") --> " << g->gain; ms_filter_call_method(f, MS_EQUALIZER_SET_GAIN, g); } if (gainsList) bctbx_list_free_with_data(gainsList, ms_free); } } if (stream->spk_equalizer) { MSFilter *f = stream->spk_equalizer; bool enabled = !!lp_config_get_int(config, "sound", "spk_eq_active", 0); ms_filter_call_method(f, MS_EQUALIZER_SET_ACTIVE, &enabled); const char *gains = lp_config_get_string(config, "sound", "spk_eq_gains", nullptr); if (enabled && gains) { bctbx_list_t *gainsList = ms_parse_equalizer_string(gains); for (bctbx_list_t *it = gainsList; it; it = bctbx_list_next(it)) { MSEqualizerGain *g = reinterpret_cast(bctbx_list_get_data(it)); lInfo() << "Read speaker equalizer gains: " << g->frequency << "(~" << g->width << ") --> " << g->gain; ms_filter_call_method(f, MS_EQUALIZER_SET_GAIN, g); } if (gainsList) bctbx_list_free_with_data(gainsList, ms_free); } } } void CorePrivate::postConfigureAudioStream (AudioStream *stream, bool muted) { L_Q(); float micGain = q->getCCore()->sound_conf.soft_mic_lev; if (muted) audio_stream_set_mic_gain(stream, 0); else audio_stream_set_mic_gain_db(stream, micGain); float recvGain = q->getCCore()->sound_conf.soft_play_lev; if (static_cast(recvGain)) setPlaybackGainDb(stream, recvGain); LinphoneConfig *config = linphone_core_get_config(q->getCCore()); float ngThres = lp_config_get_float(config, "sound", "ng_thres", 0.05f); float ngFloorGain = lp_config_get_float(config, "sound", "ng_floorgain", 0); if (stream->volsend) { int dcRemoval = lp_config_get_int(config, "sound", "dc_removal", 0); ms_filter_call_method(stream->volsend, MS_VOLUME_REMOVE_DC, &dcRemoval); float speed = lp_config_get_float(config, "sound", "el_speed", -1); float thres = lp_config_get_float(config, "sound", "el_thres", -1); float force = lp_config_get_float(config, "sound", "el_force", -1); int sustain = lp_config_get_int(config, "sound", "el_sustain", -1); float transmitThres = lp_config_get_float(config, "sound", "el_transmit_thres", -1); if (static_cast(speed) == -1) speed = 0.03f; if (static_cast(force) == -1) force = 25; MSFilter *f = stream->volsend; ms_filter_call_method(f, MS_VOLUME_SET_EA_SPEED, &speed); ms_filter_call_method(f, MS_VOLUME_SET_EA_FORCE, &force); if (static_cast(thres) != -1) ms_filter_call_method(f, MS_VOLUME_SET_EA_THRESHOLD, &thres); if (static_cast(sustain) != -1) ms_filter_call_method(f, MS_VOLUME_SET_EA_SUSTAIN, &sustain); if (static_cast(transmitThres) != -1) ms_filter_call_method(f, MS_VOLUME_SET_EA_TRANSMIT_THRESHOLD, &transmitThres); ms_filter_call_method(f, MS_VOLUME_SET_NOISE_GATE_THRESHOLD, &ngThres); ms_filter_call_method(f, MS_VOLUME_SET_NOISE_GATE_FLOORGAIN, &ngFloorGain); } if (stream->volrecv) { /* Parameters for a limited noise-gate effect, using echo limiter threshold */ float floorGain = (float)(1 / pow(10, micGain / 10)); int spkAgc = lp_config_get_int(config, "sound", "speaker_agc_enabled", 0); MSFilter *f = stream->volrecv; ms_filter_call_method(f, MS_VOLUME_ENABLE_AGC, &spkAgc); ms_filter_call_method(f, MS_VOLUME_SET_NOISE_GATE_THRESHOLD, &ngThres); ms_filter_call_method(f, MS_VOLUME_SET_NOISE_GATE_FLOORGAIN, &floorGain); } parameterizeEqualizer(stream); } void CorePrivate::setPlaybackGainDb (AudioStream *stream, float gain) { if (stream->volrecv) ms_filter_call_method(stream->volrecv, MS_VOLUME_SET_DB_GAIN, &gain); else lWarning() << "Could not apply playback gain: gain control wasn't activated"; } // ============================================================================= bool Core::areSoundResourcesLocked () const { L_D(); for (const auto &call : d->calls) { if (call->mediaInProgress()) return true; switch (call->getState()) { case CallSession::State::OutgoingInit: case CallSession::State::OutgoingProgress: case CallSession::State::OutgoingRinging: case CallSession::State::OutgoingEarlyMedia: case CallSession::State::Connected: case CallSession::State::Referred: case CallSession::State::IncomingEarlyMedia: case CallSession::State::Updating: lInfo() << "Call " << call << " is locking sound resources"; return true; default: break; } } return false; } shared_ptr Core::getCallByRemoteAddress (const Address &addr) const { L_D(); for (const auto &call : d->calls) { if (call->getRemoteAddress().weakEqual(addr)) return call; } return nullptr; } const list> &Core::getCalls () const { L_D(); return d->calls; } unsigned int Core::getCallCount () const { L_D(); return static_cast(d->calls.size()); } shared_ptr Core::getCurrentCall () const { L_D(); return d->currentCall; } LinphoneStatus Core::pauseAllCalls () { L_D(); for (const auto &call : d->calls) { if ((call->getState() == CallSession::State::StreamsRunning) || (call->getState() == CallSession::State::PausedByRemote)) call->pause(); } return 0; } void Core::soundcardHintCheck () { L_D(); bool noNeedForSound = true; // Check if the remaining calls are paused for (const auto &call : d->calls) { if ((call->getState() != CallSession::State::Pausing) && (call->getState() != CallSession::State::Paused) && (call->getState() != CallSession::State::End) && (call->getState() != CallSession::State::Error)) { noNeedForSound = false; break; } } // If no more calls or all calls are paused, we can free the soundcard LinphoneConfig *config = linphone_core_get_config(L_GET_C_BACK_PTR(this)); bool useRtpIo = !!lp_config_get_int(config, "sound", "rtp_io", FALSE); bool useRtpIoEnableLocalOutput = !!lp_config_get_int(config, "sound", "rtp_io_enable_local_output", FALSE); bool useFiles = L_GET_C_BACK_PTR(getSharedFromThis())->use_files; if ((!d->hasCalls() || noNeedForSound) && (!useFiles && (!useRtpIo || (useRtpIo && useRtpIoEnableLocalOutput)))) { lInfo() << "Notifying soundcard that we don't need it anymore for calls"; d->notifySoundcardUsage(false); } } LinphoneStatus Core::terminateAllCalls () { L_D(); while (!d->calls.empty()) { d->calls.front()->terminate(); } return 0; } LINPHONE_END_NAMESPACE