| Index: webrtc/modules/audio_mixer/new_audio_conference_mixer_impl.cc
|
| diff --git a/webrtc/modules/audio_mixer/new_audio_conference_mixer_impl.cc b/webrtc/modules/audio_mixer/new_audio_conference_mixer_impl.cc
|
| deleted file mode 100644
|
| index 96ce8ce41723b59dd072a22b3d565861adb4acbf..0000000000000000000000000000000000000000
|
| --- a/webrtc/modules/audio_mixer/new_audio_conference_mixer_impl.cc
|
| +++ /dev/null
|
| @@ -1,599 +0,0 @@
|
| -/*
|
| - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
| - *
|
| - * Use of this source code is governed by a BSD-style license
|
| - * that can be found in the LICENSE file in the root of the source
|
| - * tree. An additional intellectual property rights grant can be found
|
| - * in the file PATENTS. All contributing project authors may
|
| - * be found in the AUTHORS file in the root of the source tree.
|
| - */
|
| -
|
| -#include "webrtc/modules/audio_mixer/new_audio_conference_mixer_impl.h"
|
| -
|
| -#include <algorithm>
|
| -#include <functional>
|
| -
|
| -#include "webrtc/modules/audio_mixer/audio_frame_manipulator.h"
|
| -#include "webrtc/modules/audio_mixer/audio_mixer_defines.h"
|
| -#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
| -#include "webrtc/modules/utility/include/audio_frame_operations.h"
|
| -#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
|
| -#include "webrtc/system_wrappers/include/trace.h"
|
| -
|
| -namespace webrtc {
|
| -namespace {
|
| -
|
| -class SourceFrame {
|
| - public:
|
| - SourceFrame(MixerAudioSource* p, AudioFrame* a, bool m, bool was_mixed_before)
|
| - : audio_source_(p),
|
| - audio_frame_(a),
|
| - muted_(m),
|
| - was_mixed_before_(was_mixed_before) {
|
| - if (!muted_) {
|
| - energy_ = NewMixerCalculateEnergy(*a);
|
| - }
|
| - }
|
| -
|
| - // a.shouldMixBefore(b) is used to select mixer participants.
|
| - bool shouldMixBefore(const SourceFrame& other) const {
|
| - if (muted_ != other.muted_) {
|
| - return other.muted_;
|
| - }
|
| -
|
| - auto our_activity = audio_frame_->vad_activity_;
|
| - auto other_activity = other.audio_frame_->vad_activity_;
|
| -
|
| - if (our_activity != other_activity) {
|
| - return our_activity == AudioFrame::kVadActive;
|
| - }
|
| -
|
| - return energy_ > other.energy_;
|
| - }
|
| -
|
| - MixerAudioSource* audio_source_;
|
| - AudioFrame* audio_frame_;
|
| - bool muted_;
|
| - uint32_t energy_;
|
| - bool was_mixed_before_;
|
| -};
|
| -
|
| -// Remixes a frame between stereo and mono.
|
| -void RemixFrame(AudioFrame* frame, size_t number_of_channels) {
|
| - RTC_DCHECK(number_of_channels == 1 || number_of_channels == 2);
|
| - if (frame->num_channels_ == 1 && number_of_channels == 2) {
|
| - AudioFrameOperations::MonoToStereo(frame);
|
| - } else if (frame->num_channels_ == 2 && number_of_channels == 1) {
|
| - AudioFrameOperations::StereoToMono(frame);
|
| - }
|
| -}
|
| -
|
| -// Mix |frame| into |mixed_frame|, with saturation protection and upmixing.
|
| -// These effects are applied to |frame| itself prior to mixing. Assumes that
|
| -// |mixed_frame| always has at least as many channels as |frame|. Supports
|
| -// stereo at most.
|
| -//
|
| -void MixFrames(AudioFrame* mixed_frame, AudioFrame* frame, bool use_limiter) {
|
| - RTC_DCHECK_GE(mixed_frame->num_channels_, frame->num_channels_);
|
| - if (use_limiter) {
|
| - // Divide by two to avoid saturation in the mixing.
|
| - // This is only meaningful if the limiter will be used.
|
| - *frame >>= 1;
|
| - }
|
| - RTC_DCHECK_EQ(frame->num_channels_, mixed_frame->num_channels_);
|
| - *mixed_frame += *frame;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -MixerAudioSource::MixerAudioSource() : _mixHistory(new NewMixHistory()) {}
|
| -
|
| -MixerAudioSource::~MixerAudioSource() {
|
| - delete _mixHistory;
|
| -}
|
| -
|
| -bool MixerAudioSource::IsMixed() const {
|
| - return _mixHistory->IsMixed();
|
| -}
|
| -
|
| -NewMixHistory::NewMixHistory() : is_mixed_(0) {}
|
| -
|
| -NewMixHistory::~NewMixHistory() {}
|
| -
|
| -bool NewMixHistory::IsMixed() const {
|
| - return is_mixed_;
|
| -}
|
| -
|
| -bool NewMixHistory::WasMixed() const {
|
| - // Was mixed is the same as is mixed depending on perspective. This function
|
| - // is for the perspective of NewAudioConferenceMixerImpl.
|
| - return IsMixed();
|
| -}
|
| -
|
| -int32_t NewMixHistory::SetIsMixed(const bool mixed) {
|
| - is_mixed_ = mixed;
|
| - return 0;
|
| -}
|
| -
|
| -void NewMixHistory::ResetMixedStatus() {
|
| - is_mixed_ = false;
|
| -}
|
| -
|
| -NewAudioConferenceMixer* NewAudioConferenceMixer::Create(int id) {
|
| - NewAudioConferenceMixerImpl* mixer = new NewAudioConferenceMixerImpl(id);
|
| - if (!mixer->Init()) {
|
| - delete mixer;
|
| - return NULL;
|
| - }
|
| - return mixer;
|
| -}
|
| -
|
| -NewAudioConferenceMixerImpl::NewAudioConferenceMixerImpl(int id)
|
| - : id_(id),
|
| - output_frequency_(kDefaultFrequency),
|
| - sample_size_(0),
|
| - audio_source_list_(),
|
| - additional_audio_source_list_(),
|
| - num_mixed_audio_sources_(0),
|
| - use_limiter_(true),
|
| - time_stamp_(0) {
|
| - thread_checker_.DetachFromThread();
|
| -}
|
| -
|
| -NewAudioConferenceMixerImpl::~NewAudioConferenceMixerImpl() {}
|
| -
|
| -bool NewAudioConferenceMixerImpl::Init() {
|
| - crit_.reset(CriticalSectionWrapper::CreateCriticalSection());
|
| - if (crit_.get() == NULL)
|
| - return false;
|
| -
|
| - cb_crit_.reset(CriticalSectionWrapper::CreateCriticalSection());
|
| - if (cb_crit_.get() == NULL)
|
| - return false;
|
| -
|
| - Config config;
|
| - config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
|
| - limiter_.reset(AudioProcessing::Create(config));
|
| - if (!limiter_.get())
|
| - return false;
|
| -
|
| - if (SetOutputFrequency(kDefaultFrequency) == -1)
|
| - return false;
|
| -
|
| - if (limiter_->gain_control()->set_mode(GainControl::kFixedDigital) !=
|
| - limiter_->kNoError)
|
| - return false;
|
| -
|
| - // We smoothly limit the mixed frame to -7 dbFS. -6 would correspond to the
|
| - // divide-by-2 but -7 is used instead to give a bit of headroom since the
|
| - // AGC is not a hard limiter.
|
| - if (limiter_->gain_control()->set_target_level_dbfs(7) != limiter_->kNoError)
|
| - return false;
|
| -
|
| - if (limiter_->gain_control()->set_compression_gain_db(0) !=
|
| - limiter_->kNoError)
|
| - return false;
|
| -
|
| - if (limiter_->gain_control()->enable_limiter(true) != limiter_->kNoError)
|
| - return false;
|
| -
|
| - if (limiter_->gain_control()->Enable(true) != limiter_->kNoError)
|
| - return false;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void NewAudioConferenceMixerImpl::Mix(int sample_rate,
|
| - size_t number_of_channels,
|
| - AudioFrame* audio_frame_for_mixing) {
|
| - RTC_DCHECK(number_of_channels == 1 || number_of_channels == 2);
|
| - RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
| - AudioFrameList mixList;
|
| - AudioFrameList additionalFramesList;
|
| - std::map<int, MixerAudioSource*> mixedAudioSourcesMap;
|
| - {
|
| - CriticalSectionScoped cs(cb_crit_.get());
|
| - Frequency mixing_frequency;
|
| -
|
| - switch (sample_rate) {
|
| - case 8000:
|
| - mixing_frequency = kNbInHz;
|
| - break;
|
| - case 16000:
|
| - mixing_frequency = kWbInHz;
|
| - break;
|
| - case 32000:
|
| - mixing_frequency = kSwbInHz;
|
| - break;
|
| - case 48000:
|
| - mixing_frequency = kFbInHz;
|
| - break;
|
| - default:
|
| - RTC_NOTREACHED();
|
| - return;
|
| - }
|
| -
|
| - if (OutputFrequency() != mixing_frequency) {
|
| - SetOutputFrequency(mixing_frequency);
|
| - }
|
| -
|
| - mixList = UpdateToMix(kMaximumAmountOfMixedAudioSources);
|
| - GetAdditionalAudio(&additionalFramesList);
|
| - }
|
| -
|
| - for (FrameAndMuteInfo& frame_and_mute : mixList) {
|
| - RemixFrame(frame_and_mute.frame, number_of_channels);
|
| - }
|
| - for (FrameAndMuteInfo& frame_and_mute : additionalFramesList) {
|
| - RemixFrame(frame_and_mute.frame, number_of_channels);
|
| - }
|
| -
|
| - audio_frame_for_mixing->UpdateFrame(
|
| - -1, time_stamp_, NULL, 0, output_frequency_, AudioFrame::kNormalSpeech,
|
| - AudioFrame::kVadPassive, number_of_channels);
|
| -
|
| - time_stamp_ += static_cast<uint32_t>(sample_size_);
|
| -
|
| - use_limiter_ = num_mixed_audio_sources_ > 1;
|
| -
|
| - // We only use the limiter if it supports the output sample rate and
|
| - // we're actually mixing multiple streams.
|
| - MixFromList(audio_frame_for_mixing, mixList, id_, use_limiter_);
|
| -
|
| - {
|
| - CriticalSectionScoped cs(crit_.get());
|
| - MixAnonomouslyFromList(audio_frame_for_mixing, additionalFramesList);
|
| -
|
| - if (audio_frame_for_mixing->samples_per_channel_ == 0) {
|
| - // Nothing was mixed, set the audio samples to silence.
|
| - audio_frame_for_mixing->samples_per_channel_ = sample_size_;
|
| - audio_frame_for_mixing->Mute();
|
| - } else {
|
| - // Only call the limiter if we have something to mix.
|
| - LimitMixedAudio(audio_frame_for_mixing);
|
| - }
|
| - }
|
| -
|
| - // Pass the final result to the level indicator.
|
| - audio_level_.ComputeLevel(*audio_frame_for_mixing);
|
| -
|
| - return;
|
| -}
|
| -
|
| -int32_t NewAudioConferenceMixerImpl::SetOutputFrequency(
|
| - const Frequency& frequency) {
|
| - CriticalSectionScoped cs(crit_.get());
|
| -
|
| - output_frequency_ = frequency;
|
| - sample_size_ =
|
| - static_cast<size_t>((output_frequency_ * kProcessPeriodicityInMs) / 1000);
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -NewAudioConferenceMixer::Frequency
|
| -NewAudioConferenceMixerImpl::OutputFrequency() const {
|
| - CriticalSectionScoped cs(crit_.get());
|
| - return output_frequency_;
|
| -}
|
| -
|
| -int32_t NewAudioConferenceMixerImpl::SetMixabilityStatus(
|
| - MixerAudioSource* audio_source,
|
| - bool mixable) {
|
| - if (!mixable) {
|
| - // Anonymous audio sources are in a separate list. Make sure that the
|
| - // audio source is in the _audioSourceList if it is being mixed.
|
| - SetAnonymousMixabilityStatus(audio_source, false);
|
| - }
|
| - size_t numMixedAudioSources;
|
| - {
|
| - CriticalSectionScoped cs(cb_crit_.get());
|
| - const bool isMixed = IsAudioSourceInList(*audio_source, audio_source_list_);
|
| - // API must be called with a new state.
|
| - if (!(mixable ^ isMixed)) {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_,
|
| - "Mixable is aready %s", isMixed ? "ON" : "off");
|
| - return -1;
|
| - }
|
| - bool success = false;
|
| - if (mixable) {
|
| - success = AddAudioSourceToList(audio_source, &audio_source_list_);
|
| - } else {
|
| - success = RemoveAudioSourceFromList(audio_source, &audio_source_list_);
|
| - }
|
| - if (!success) {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, id_,
|
| - "failed to %s audio_source", mixable ? "add" : "remove");
|
| - RTC_NOTREACHED();
|
| - return -1;
|
| - }
|
| -
|
| - size_t numMixedNonAnonymous = audio_source_list_.size();
|
| - if (numMixedNonAnonymous > kMaximumAmountOfMixedAudioSources) {
|
| - numMixedNonAnonymous = kMaximumAmountOfMixedAudioSources;
|
| - }
|
| - numMixedAudioSources =
|
| - numMixedNonAnonymous + additional_audio_source_list_.size();
|
| - }
|
| - // A MixerAudioSource was added or removed. Make sure the scratch
|
| - // buffer is updated if necessary.
|
| - // Note: The scratch buffer may only be updated in Process().
|
| - CriticalSectionScoped cs(crit_.get());
|
| - num_mixed_audio_sources_ = numMixedAudioSources;
|
| - return 0;
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::MixabilityStatus(
|
| - const MixerAudioSource& audio_source) const {
|
| - CriticalSectionScoped cs(cb_crit_.get());
|
| - return IsAudioSourceInList(audio_source, audio_source_list_);
|
| -}
|
| -
|
| -int32_t NewAudioConferenceMixerImpl::SetAnonymousMixabilityStatus(
|
| - MixerAudioSource* audio_source,
|
| - bool anonymous) {
|
| - CriticalSectionScoped cs(cb_crit_.get());
|
| - if (IsAudioSourceInList(*audio_source, additional_audio_source_list_)) {
|
| - if (anonymous) {
|
| - return 0;
|
| - }
|
| - if (!RemoveAudioSourceFromList(audio_source,
|
| - &additional_audio_source_list_)) {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, id_,
|
| - "unable to remove audio_source from anonymous list");
|
| - RTC_NOTREACHED();
|
| - return -1;
|
| - }
|
| - return AddAudioSourceToList(audio_source, &audio_source_list_) ? 0 : -1;
|
| - }
|
| - if (!anonymous) {
|
| - return 0;
|
| - }
|
| - const bool mixable =
|
| - RemoveAudioSourceFromList(audio_source, &audio_source_list_);
|
| - if (!mixable) {
|
| - WEBRTC_TRACE(
|
| - kTraceWarning, kTraceAudioMixerServer, id_,
|
| - "audio_source must be registered before turning it into anonymous");
|
| - // Setting anonymous status is only possible if MixerAudioSource is
|
| - // already registered.
|
| - return -1;
|
| - }
|
| - return AddAudioSourceToList(audio_source, &additional_audio_source_list_)
|
| - ? 0
|
| - : -1;
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::AnonymousMixabilityStatus(
|
| - const MixerAudioSource& audio_source) const {
|
| - CriticalSectionScoped cs(cb_crit_.get());
|
| - return IsAudioSourceInList(audio_source, additional_audio_source_list_);
|
| -}
|
| -
|
| -AudioFrameList NewAudioConferenceMixerImpl::UpdateToMix(
|
| - size_t maxAudioFrameCounter) const {
|
| - AudioFrameList result;
|
| - std::vector<SourceFrame> audioSourceMixingDataList;
|
| -
|
| - // Get audio source audio and put it in the struct vector.
|
| - for (MixerAudioSource* audio_source : audio_source_list_) {
|
| - auto audio_frame_with_info = audio_source->GetAudioFrameWithMuted(
|
| - id_, static_cast<int>(output_frequency_));
|
| -
|
| - auto audio_frame_info = audio_frame_with_info.audio_frame_info;
|
| - AudioFrame* audio_source_audio_frame = audio_frame_with_info.audio_frame;
|
| -
|
| - if (audio_frame_info == MixerAudioSource::AudioFrameInfo::kError) {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_,
|
| - "failed to GetAudioFrameWithMuted() from participant");
|
| - continue;
|
| - }
|
| - audioSourceMixingDataList.emplace_back(
|
| - audio_source, audio_source_audio_frame,
|
| - audio_frame_info == MixerAudioSource::AudioFrameInfo::kMuted,
|
| - audio_source->_mixHistory->WasMixed());
|
| - }
|
| -
|
| - // Sort frames by sorting function.
|
| - std::sort(audioSourceMixingDataList.begin(), audioSourceMixingDataList.end(),
|
| - std::mem_fn(&SourceFrame::shouldMixBefore));
|
| -
|
| - // Go through list in order and put things in mixList.
|
| - for (SourceFrame& p : audioSourceMixingDataList) {
|
| - // Filter muted.
|
| - if (p.muted_) {
|
| - p.audio_source_->_mixHistory->SetIsMixed(false);
|
| - continue;
|
| - }
|
| -
|
| - // Add frame to result vector for mixing.
|
| - bool is_mixed = false;
|
| - if (maxAudioFrameCounter > 0) {
|
| - --maxAudioFrameCounter;
|
| - if (!p.was_mixed_before_) {
|
| - NewMixerRampIn(p.audio_frame_);
|
| - }
|
| - result.emplace_back(p.audio_frame_, false);
|
| - is_mixed = true;
|
| - }
|
| -
|
| - // Ramp out unmuted.
|
| - if (p.was_mixed_before_ && !is_mixed) {
|
| - NewMixerRampOut(p.audio_frame_);
|
| - result.emplace_back(p.audio_frame_, false);
|
| - }
|
| -
|
| - p.audio_source_->_mixHistory->SetIsMixed(is_mixed);
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -void NewAudioConferenceMixerImpl::GetAdditionalAudio(
|
| - AudioFrameList* additionalFramesList) const {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
|
| - "GetAdditionalAudio(additionalFramesList)");
|
| - // The GetAudioFrameWithMuted() callback may result in the audio source being
|
| - // removed from additionalAudioFramesList_. If that happens it will
|
| - // invalidate any iterators. Create a copy of the audio sources list such
|
| - // that the list of participants can be traversed safely.
|
| - MixerAudioSourceList additionalAudioSourceList;
|
| - additionalAudioSourceList.insert(additionalAudioSourceList.begin(),
|
| - additional_audio_source_list_.begin(),
|
| - additional_audio_source_list_.end());
|
| -
|
| - for (MixerAudioSourceList::const_iterator audio_source =
|
| - additionalAudioSourceList.begin();
|
| - audio_source != additionalAudioSourceList.end(); ++audio_source) {
|
| - auto audio_frame_with_info =
|
| - (*audio_source)->GetAudioFrameWithMuted(id_, output_frequency_);
|
| - auto ret = audio_frame_with_info.audio_frame_info;
|
| - AudioFrame* audio_frame = audio_frame_with_info.audio_frame;
|
| - if (ret == MixerAudioSource::AudioFrameInfo::kError) {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_,
|
| - "failed to GetAudioFrameWithMuted() from audio_source");
|
| - continue;
|
| - }
|
| - if (audio_frame->samples_per_channel_ == 0) {
|
| - // Empty frame. Don't use it.
|
| - continue;
|
| - }
|
| - additionalFramesList->push_back(FrameAndMuteInfo(
|
| - audio_frame, ret == MixerAudioSource::AudioFrameInfo::kMuted));
|
| - }
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::IsAudioSourceInList(
|
| - const MixerAudioSource& audio_source,
|
| - const MixerAudioSourceList& audioSourceList) const {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
|
| - "IsAudioSourceInList(audio_source,audioSourceList)");
|
| - return std::find(audioSourceList.begin(), audioSourceList.end(),
|
| - &audio_source) != audioSourceList.end();
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::AddAudioSourceToList(
|
| - MixerAudioSource* audio_source,
|
| - MixerAudioSourceList* audioSourceList) const {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
|
| - "AddAudioSourceToList(audio_source, audioSourceList)");
|
| - audioSourceList->push_back(audio_source);
|
| - // Make sure that the mixed status is correct for new MixerAudioSource.
|
| - audio_source->_mixHistory->ResetMixedStatus();
|
| - return true;
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::RemoveAudioSourceFromList(
|
| - MixerAudioSource* audio_source,
|
| - MixerAudioSourceList* audioSourceList) const {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
|
| - "RemoveAudioSourceFromList(audio_source, audioSourceList)");
|
| - auto iter =
|
| - std::find(audioSourceList->begin(), audioSourceList->end(), audio_source);
|
| - if (iter != audioSourceList->end()) {
|
| - audioSourceList->erase(iter);
|
| - // AudioSource is no longer mixed, reset to default.
|
| - audio_source->_mixHistory->ResetMixedStatus();
|
| - return true;
|
| - } else {
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -int32_t NewAudioConferenceMixerImpl::MixFromList(
|
| - AudioFrame* mixedAudio,
|
| - const AudioFrameList& audioFrameList,
|
| - int32_t id,
|
| - bool use_limiter) {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id,
|
| - "MixFromList(mixedAudio, audioFrameList)");
|
| - if (audioFrameList.empty())
|
| - return 0;
|
| -
|
| - uint32_t position = 0;
|
| -
|
| - if (audioFrameList.size() == 1) {
|
| - mixedAudio->timestamp_ = audioFrameList.front().frame->timestamp_;
|
| - mixedAudio->elapsed_time_ms_ =
|
| - audioFrameList.front().frame->elapsed_time_ms_;
|
| - } else {
|
| - // TODO(wu): Issue 3390.
|
| - // Audio frame timestamp is only supported in one channel case.
|
| - mixedAudio->timestamp_ = 0;
|
| - mixedAudio->elapsed_time_ms_ = -1;
|
| - }
|
| -
|
| - for (AudioFrameList::const_iterator iter = audioFrameList.begin();
|
| - iter != audioFrameList.end(); ++iter) {
|
| - if (!iter->muted) {
|
| - MixFrames(mixedAudio, iter->frame, use_limiter);
|
| - }
|
| -
|
| - position++;
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -// TODO(andrew): consolidate this function with MixFromList.
|
| -int32_t NewAudioConferenceMixerImpl::MixAnonomouslyFromList(
|
| - AudioFrame* mixedAudio,
|
| - const AudioFrameList& audioFrameList) const {
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
|
| - "MixAnonomouslyFromList(mixedAudio, audioFrameList)");
|
| -
|
| - if (audioFrameList.empty())
|
| - return 0;
|
| -
|
| - for (AudioFrameList::const_iterator iter = audioFrameList.begin();
|
| - iter != audioFrameList.end(); ++iter) {
|
| - if (!iter->muted) {
|
| - MixFrames(mixedAudio, iter->frame, use_limiter_);
|
| - }
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -bool NewAudioConferenceMixerImpl::LimitMixedAudio(
|
| - AudioFrame* mixedAudio) const {
|
| - if (!use_limiter_) {
|
| - return true;
|
| - }
|
| -
|
| - // Smoothly limit the mixed frame.
|
| - const int error = limiter_->ProcessStream(mixedAudio);
|
| -
|
| - // And now we can safely restore the level. This procedure results in
|
| - // some loss of resolution, deemed acceptable.
|
| - //
|
| - // It's possible to apply the gain in the AGC (with a target level of 0 dbFS
|
| - // and compression gain of 6 dB). However, in the transition frame when this
|
| - // is enabled (moving from one to two audio sources) it has the potential to
|
| - // create discontinuities in the mixed frame.
|
| - //
|
| - // Instead we double the frame (with addition since left-shifting a
|
| - // negative value is undefined).
|
| - *mixedAudio += *mixedAudio;
|
| -
|
| - if (error != limiter_->kNoError) {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, id_,
|
| - "Error from AudioProcessing: %d", error);
|
| - RTC_NOTREACHED();
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -int NewAudioConferenceMixerImpl::GetOutputAudioLevel() {
|
| - const int level = audio_level_.Level();
|
| - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioMixerServer, id_,
|
| - "GetAudioOutputLevel() => level=%d", level);
|
| - return level;
|
| -}
|
| -
|
| -int NewAudioConferenceMixerImpl::GetOutputAudioLevelFullRange() {
|
| - const int level = audio_level_.LevelFullRange();
|
| - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioMixerServer, id_,
|
| - "GetAudioOutputLevelFullRange() => level=%d", level);
|
| - return level;
|
| -}
|
| -} // namespace webrtc
|
|
|