Made MixerAudioSource a pure interface.

This required quite a few small changes in the mixing algorithm
structure, the mixer interface and the mixer unit tests.

BUG=webrtc:6346

Review-Url: https://codereview.webrtc.org/2396483002
Cr-Commit-Position: refs/heads/master@{#14567}
This commit is contained in:
aleloi
2016-10-07 04:30:10 -07:00
committed by Commit bot
parent 2334b70851
commit 2ae5fdff86
10 changed files with 179 additions and 125 deletions

View File

@ -17,10 +17,11 @@ rtc_static_library("audio_mixer") {
"audio_frame_manipulator.cc", "audio_frame_manipulator.cc",
"audio_frame_manipulator.h", "audio_frame_manipulator.h",
"audio_mixer.h", "audio_mixer.h",
"audio_mixer_defines.cc",
"audio_mixer_defines.h", "audio_mixer_defines.h",
"audio_mixer_impl.cc", "audio_mixer_impl.cc",
"audio_mixer_impl.h", "audio_mixer_impl.h",
"audio_source_with_mix_status.cc",
"audio_source_with_mix_status.h",
] ]
public = [ public = [

View File

@ -30,11 +30,11 @@ const float kRampArray[] = {
const size_t kRampSize = sizeof(kRampArray) / sizeof(kRampArray[0]); const size_t kRampSize = sizeof(kRampArray) / sizeof(kRampArray[0]);
} // namespace } // namespace
uint32_t NewMixerCalculateEnergy(const AudioFrame& audio_frame) { uint32_t AudioMixerCalculateEnergy(const AudioFrame& audio_frame) {
uint32_t energy = 0; uint32_t energy = 0;
for (size_t position = 0; position < audio_frame.samples_per_channel_; for (size_t position = 0; position < audio_frame.samples_per_channel_;
position++) { position++) {
// TODO(andrew): this can easily overflow. // TODO(aleloi): This can overflow. Convert to floats.
energy += audio_frame.data_[position] * audio_frame.data_[position]; energy += audio_frame.data_[position] * audio_frame.data_[position];
} }
return energy; return energy;

View File

@ -17,7 +17,7 @@
namespace webrtc { namespace webrtc {
// Updates the audioFrame's energy (based on its samples). // Updates the audioFrame's energy (based on its samples).
uint32_t NewMixerCalculateEnergy(const AudioFrame& audio_frame); uint32_t AudioMixerCalculateEnergy(const AudioFrame& audio_frame);
// Apply linear step function that ramps in/out the audio samples in audio_frame // Apply linear step function that ramps in/out the audio samples in audio_frame
void NewMixerRampIn(AudioFrame* audio_frame); void NewMixerRampIn(AudioFrame* audio_frame);

View File

@ -22,10 +22,11 @@
'audio_frame_manipulator.cc', 'audio_frame_manipulator.cc',
'audio_frame_manipulator.h', 'audio_frame_manipulator.h',
'audio_mixer.h', 'audio_mixer.h',
'audio_mixer_defines.cc',
'audio_mixer_defines.h', 'audio_mixer_defines.h',
'audio_mixer_impl.cc', 'audio_mixer_impl.cc',
'audio_mixer_impl.h', 'audio_mixer_impl.h',
'audio_source_with_mix_status.cc',
'audio_source_with_mix_status.h',
], ],
}, },
], # targets ], # targets

View File

@ -35,6 +35,8 @@ class MixerAudioSource {
AudioFrameInfo audio_frame_info; AudioFrameInfo audio_frame_info;
}; };
virtual ~MixerAudioSource() = default;
// The implementation of GetAudioFrameWithMuted should update // The implementation of GetAudioFrameWithMuted should update
// audio_frame with new audio every time it's called. Implementing // audio_frame with new audio every time it's called. Implementing
// classes are allowed to return the same AudioFrame pointer on // classes are allowed to return the same AudioFrame pointer on
@ -43,25 +45,6 @@ class MixerAudioSource {
// mixer. // mixer.
virtual AudioFrameWithMuted GetAudioFrameWithMuted(int32_t id, virtual AudioFrameWithMuted GetAudioFrameWithMuted(int32_t id,
int sample_rate_hz) = 0; int sample_rate_hz) = 0;
// Returns true if the audio source was mixed this mix iteration.
bool IsMixed() const;
// Returns true if the audio source was mixed previous mix
// iteration.
bool WasMixed() const;
// Updates the mixed status.
int32_t SetIsMixed(bool mixed);
void ResetMixedStatus();
private:
bool is_mixed_;
protected:
MixerAudioSource();
virtual ~MixerAudioSource();
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -14,6 +14,7 @@
#include <functional> #include <functional>
#include <utility> #include <utility>
#include "webrtc/base/logging.h"
#include "webrtc/modules/audio_mixer/audio_frame_manipulator.h" #include "webrtc/modules/audio_mixer/audio_frame_manipulator.h"
#include "webrtc/modules/utility/include/audio_frame_operations.h" #include "webrtc/modules/utility/include/audio_frame_operations.h"
#include "webrtc/system_wrappers/include/trace.h" #include "webrtc/system_wrappers/include/trace.h"
@ -23,29 +24,26 @@ namespace {
class SourceFrame { class SourceFrame {
public: public:
SourceFrame(MixerAudioSource* p, AudioFrame* a, bool m, bool was_mixed_before) SourceFrame(AudioSourceWithMixStatus* audio_source,
: audio_source_(p), AudioFrame* audio_frame,
audio_frame_(a), bool muted)
muted_(m), : audio_source_(audio_source), audio_frame_(audio_frame), muted_(muted) {
was_mixed_before_(was_mixed_before) {
if (!muted_) { if (!muted_) {
energy_ = NewMixerCalculateEnergy(*a); energy_ = AudioMixerCalculateEnergy(*audio_frame);
} }
} }
SourceFrame(MixerAudioSource* p, SourceFrame(AudioSourceWithMixStatus* audio_source,
AudioFrame* a, AudioFrame* audio_frame,
bool m, bool muted,
bool was_mixed_before,
uint32_t energy) uint32_t energy)
: audio_source_(p), : audio_source_(audio_source),
audio_frame_(a), audio_frame_(audio_frame),
muted_(m), muted_(muted),
energy_(energy), energy_(energy) {}
was_mixed_before_(was_mixed_before) {}
// a.shouldMixBefore(b) is used to select mixer participants. // a.ShouldMixBefore(b) is used to select mixer sources.
bool shouldMixBefore(const SourceFrame& other) const { bool ShouldMixBefore(const SourceFrame& other) const {
if (muted_ != other.muted_) { if (muted_ != other.muted_) {
return other.muted_; return other.muted_;
} }
@ -60,11 +58,10 @@ class SourceFrame {
return energy_ > other.energy_; return energy_ > other.energy_;
} }
MixerAudioSource* audio_source_; AudioSourceWithMixStatus* audio_source_ = nullptr;
AudioFrame* audio_frame_; AudioFrame* audio_frame_ = nullptr;
bool muted_; bool muted_ = true;
uint32_t energy_; uint32_t energy_ = 0;
bool was_mixed_before_;
}; };
// Remixes a frame between stereo and mono. // Remixes a frame between stereo and mono.
@ -80,13 +77,13 @@ void RemixFrame(AudioFrame* frame, size_t number_of_channels) {
void Ramp(const std::vector<SourceFrame>& mixed_sources_and_frames) { void Ramp(const std::vector<SourceFrame>& mixed_sources_and_frames) {
for (const auto& source_frame : mixed_sources_and_frames) { for (const auto& source_frame : mixed_sources_and_frames) {
// Ramp in previously unmixed. // Ramp in previously unmixed.
if (!source_frame.was_mixed_before_) { if (!source_frame.audio_source_->WasMixed()) {
NewMixerRampIn(source_frame.audio_frame_); NewMixerRampIn(source_frame.audio_frame_);
} }
const bool is_mixed = source_frame.audio_source_->IsMixed(); const bool is_mixed = source_frame.audio_source_->IsMixed();
// Ramp out currently unmixed. // Ramp out currently unmixed.
if (source_frame.was_mixed_before_ && !is_mixed) { if (source_frame.audio_source_->WasMixed() && !is_mixed) {
NewMixerRampOut(source_frame.audio_frame_); NewMixerRampOut(source_frame.audio_frame_);
} }
} }
@ -133,6 +130,24 @@ int32_t MixFromList(AudioFrame* mixed_audio,
return 0; return 0;
} }
MixerAudioSourceList::const_iterator FindSourceInList(
MixerAudioSource const* audio_source,
MixerAudioSourceList const* audio_source_list) {
return std::find_if(audio_source_list->begin(), audio_source_list->end(),
[audio_source](const AudioSourceWithMixStatus& p) {
return p.audio_source() == audio_source;
});
}
MixerAudioSourceList::iterator FindSourceInList(
MixerAudioSource const* audio_source,
MixerAudioSourceList* audio_source_list) {
return std::find_if(audio_source_list->begin(), audio_source_list->end(),
[audio_source](const AudioSourceWithMixStatus& p) {
return p.audio_source() == audio_source;
});
}
} // namespace } // namespace
std::unique_ptr<AudioMixer> AudioMixer::Create(int id) { std::unique_ptr<AudioMixer> AudioMixer::Create(int id) {
@ -153,7 +168,7 @@ AudioMixerImpl::AudioMixerImpl(int id, std::unique_ptr<AudioProcessing> limiter)
AudioMixerImpl::~AudioMixerImpl() {} AudioMixerImpl::~AudioMixerImpl() {}
std::unique_ptr<AudioMixer> AudioMixerImpl::Create(int id) { std::unique_ptr<AudioMixerImpl> AudioMixerImpl::Create(int id) {
Config config; Config config;
config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
std::unique_ptr<AudioProcessing> limiter(AudioProcessing::Create(config)); std::unique_ptr<AudioProcessing> limiter(AudioProcessing::Create(config));
@ -179,7 +194,7 @@ std::unique_ptr<AudioMixer> AudioMixerImpl::Create(int id) {
if (limiter->gain_control()->Enable(true) != limiter->kNoError) if (limiter->gain_control()->Enable(true) != limiter->kNoError)
return nullptr; return nullptr;
return std::unique_ptr<AudioMixer>( return std::unique_ptr<AudioMixerImpl>(
new AudioMixerImpl(id, std::move(limiter))); new AudioMixerImpl(id, std::move(limiter)));
} }
@ -266,8 +281,8 @@ int32_t AudioMixerImpl::SetMixabilityStatus(MixerAudioSource* audio_source,
} }
{ {
rtc::CritScope lock(&crit_); rtc::CritScope lock(&crit_);
const bool is_mixed = const bool is_mixed = FindSourceInList(audio_source, &audio_source_list_) !=
IsAudioSourceInList(*audio_source, audio_source_list_); audio_source_list_.end();
// API must be called with a new state. // API must be called with a new state.
if (!(mixable ^ is_mixed)) { if (!(mixable ^ is_mixed)) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_,
@ -300,14 +315,16 @@ int32_t AudioMixerImpl::SetMixabilityStatus(MixerAudioSource* audio_source,
bool AudioMixerImpl::MixabilityStatus( bool AudioMixerImpl::MixabilityStatus(
const MixerAudioSource& audio_source) const { const MixerAudioSource& audio_source) const {
rtc::CritScope lock(&crit_); rtc::CritScope lock(&crit_);
return IsAudioSourceInList(audio_source, audio_source_list_); return FindSourceInList(&audio_source, &audio_source_list_) !=
audio_source_list_.end();
} }
int32_t AudioMixerImpl::SetAnonymousMixabilityStatus( int32_t AudioMixerImpl::SetAnonymousMixabilityStatus(
MixerAudioSource* audio_source, MixerAudioSource* audio_source,
bool anonymous) { bool anonymous) {
rtc::CritScope lock(&crit_); rtc::CritScope lock(&crit_);
if (IsAudioSourceInList(*audio_source, additional_audio_source_list_)) { if (FindSourceInList(audio_source, &additional_audio_source_list_) !=
additional_audio_source_list_.end()) {
if (anonymous) { if (anonymous) {
return 0; return 0;
} }
@ -341,10 +358,11 @@ int32_t AudioMixerImpl::SetAnonymousMixabilityStatus(
bool AudioMixerImpl::AnonymousMixabilityStatus( bool AudioMixerImpl::AnonymousMixabilityStatus(
const MixerAudioSource& audio_source) const { const MixerAudioSource& audio_source) const {
rtc::CritScope lock(&crit_); rtc::CritScope lock(&crit_);
return IsAudioSourceInList(audio_source, additional_audio_source_list_); return FindSourceInList(&audio_source, &additional_audio_source_list_) !=
additional_audio_source_list_.end();
} }
AudioFrameList AudioMixerImpl::GetNonAnonymousAudio() const { AudioFrameList AudioMixerImpl::GetNonAnonymousAudio() {
RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DCHECK_RUN_ON(&thread_checker_);
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"GetNonAnonymousAudio()"); "GetNonAnonymousAudio()");
@ -353,33 +371,33 @@ AudioFrameList AudioMixerImpl::GetNonAnonymousAudio() const {
std::vector<SourceFrame> ramp_list; std::vector<SourceFrame> ramp_list;
// Get audio source audio and put it in the struct vector. // Get audio source audio and put it in the struct vector.
for (auto* const audio_source : audio_source_list_) { for (auto& source_and_status : audio_source_list_) {
auto audio_frame_with_info = audio_source->GetAudioFrameWithMuted( auto audio_frame_with_info =
id_, static_cast<int>(OutputFrequency())); source_and_status.audio_source()->GetAudioFrameWithMuted(
id_, static_cast<int>(OutputFrequency()));
const auto audio_frame_info = audio_frame_with_info.audio_frame_info; const auto audio_frame_info = audio_frame_with_info.audio_frame_info;
AudioFrame* audio_source_audio_frame = audio_frame_with_info.audio_frame; AudioFrame* audio_source_audio_frame = audio_frame_with_info.audio_frame;
if (audio_frame_info == MixerAudioSource::AudioFrameInfo::kError) { if (audio_frame_info == MixerAudioSource::AudioFrameInfo::kError) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, id_,
"failed to GetAudioFrameWithMuted() from participant"); "failed to GetAudioFrameWithMuted() from source");
continue; continue;
} }
audio_source_mixing_data_list.emplace_back( audio_source_mixing_data_list.emplace_back(
audio_source, audio_source_audio_frame, &source_and_status, audio_source_audio_frame,
audio_frame_info == MixerAudioSource::AudioFrameInfo::kMuted, audio_frame_info == MixerAudioSource::AudioFrameInfo::kMuted);
audio_source->WasMixed());
} }
// Sort frames by sorting function. // Sort frames by sorting function.
std::sort(audio_source_mixing_data_list.begin(), std::sort(audio_source_mixing_data_list.begin(),
audio_source_mixing_data_list.end(), audio_source_mixing_data_list.end(),
std::mem_fn(&SourceFrame::shouldMixBefore)); std::mem_fn(&SourceFrame::ShouldMixBefore));
int max_audio_frame_counter = kMaximumAmountOfMixedAudioSources; int max_audio_frame_counter = kMaximumAmountOfMixedAudioSources;
// Go through list in order and put unmuted frames in result list. // Go through list in order and put unmuted frames in result list.
for (const SourceFrame& p : audio_source_mixing_data_list) { for (const auto& p : audio_source_mixing_data_list) {
// Filter muted. // Filter muted.
if (p.muted_) { if (p.muted_) {
p.audio_source_->SetIsMixed(false); p.audio_source_->SetIsMixed(false);
@ -391,8 +409,7 @@ AudioFrameList AudioMixerImpl::GetNonAnonymousAudio() const {
if (max_audio_frame_counter > 0) { if (max_audio_frame_counter > 0) {
--max_audio_frame_counter; --max_audio_frame_counter;
result.push_back(p.audio_frame_); result.push_back(p.audio_frame_);
ramp_list.emplace_back(p.audio_source_, p.audio_frame_, false, ramp_list.emplace_back(p.audio_source_, p.audio_frame_, false, -1);
p.was_mixed_before_, -1);
is_mixed = true; is_mixed = true;
} }
p.audio_source_->SetIsMixed(is_mixed); p.audio_source_->SetIsMixed(is_mixed);
@ -401,24 +418,16 @@ AudioFrameList AudioMixerImpl::GetNonAnonymousAudio() const {
return result; return result;
} }
AudioFrameList AudioMixerImpl::GetAnonymousAudio() const { AudioFrameList AudioMixerImpl::GetAnonymousAudio() {
RTC_DCHECK_RUN_ON(&thread_checker_); RTC_DCHECK_RUN_ON(&thread_checker_);
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"GetAnonymousAudio()"); "GetAnonymousAudio()");
// 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.
std::vector<SourceFrame> ramp_list; std::vector<SourceFrame> ramp_list;
MixerAudioSourceList additional_audio_sources_list;
AudioFrameList result; AudioFrameList result;
additional_audio_sources_list.insert(additional_audio_sources_list.begin(), for (auto& source_and_status : additional_audio_source_list_) {
additional_audio_source_list_.begin(),
additional_audio_source_list_.end());
for (const auto& audio_source : additional_audio_sources_list) {
const auto audio_frame_with_info = const auto audio_frame_with_info =
audio_source->GetAudioFrameWithMuted(id_, OutputFrequency()); source_and_status.audio_source()->GetAudioFrameWithMuted(
id_, OutputFrequency());
const auto ret = audio_frame_with_info.audio_frame_info; const auto ret = audio_frame_with_info.audio_frame_info;
AudioFrame* audio_frame = audio_frame_with_info.audio_frame; AudioFrame* audio_frame = audio_frame_with_info.audio_frame;
if (ret == MixerAudioSource::AudioFrameInfo::kError) { if (ret == MixerAudioSource::AudioFrameInfo::kError) {
@ -428,32 +437,20 @@ AudioFrameList AudioMixerImpl::GetAnonymousAudio() const {
} }
if (ret != MixerAudioSource::AudioFrameInfo::kMuted) { if (ret != MixerAudioSource::AudioFrameInfo::kMuted) {
result.push_back(audio_frame); result.push_back(audio_frame);
ramp_list.emplace_back(audio_source, audio_frame, false, ramp_list.emplace_back(&source_and_status, audio_frame, false, 0);
audio_source->IsMixed(), 0); source_and_status.SetIsMixed(true);
audio_source->SetIsMixed(true);
} }
} }
Ramp(ramp_list); Ramp(ramp_list);
return result; return result;
} }
bool AudioMixerImpl::IsAudioSourceInList(
const MixerAudioSource& audio_source,
const MixerAudioSourceList& audio_source_list) const {
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"IsAudioSourceInList(audio_source,audio_source_list)");
return std::find(audio_source_list.begin(), audio_source_list.end(),
&audio_source) != audio_source_list.end();
}
bool AudioMixerImpl::AddAudioSourceToList( bool AudioMixerImpl::AddAudioSourceToList(
MixerAudioSource* audio_source, MixerAudioSource* audio_source,
MixerAudioSourceList* audio_source_list) const { MixerAudioSourceList* audio_source_list) const {
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"AddAudioSourceToList(audio_source, audio_source_list)"); "AddAudioSourceToList(audio_source, audio_source_list)");
audio_source_list->push_back(audio_source); audio_source_list->emplace_back(audio_source);
// Make sure that the mixed status is correct for new MixerAudioSource.
audio_source->ResetMixedStatus();
return true; return true;
} }
@ -462,12 +459,9 @@ bool AudioMixerImpl::RemoveAudioSourceFromList(
MixerAudioSourceList* audio_source_list) const { MixerAudioSourceList* audio_source_list) const {
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_, WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"RemoveAudioSourceFromList(audio_source, audio_source_list)"); "RemoveAudioSourceFromList(audio_source, audio_source_list)");
const auto iter = std::find(audio_source_list->begin(), const auto iter = FindSourceInList(audio_source, audio_source_list);
audio_source_list->end(), audio_source);
if (iter != audio_source_list->end()) { if (iter != audio_source_list->end()) {
audio_source_list->erase(iter); audio_source_list->erase(iter);
// AudioSource is no longer mixed, reset to default.
audio_source->ResetMixedStatus();
return true; return true;
} else { } else {
return false; return false;
@ -519,4 +513,25 @@ int AudioMixerImpl::GetOutputAudioLevelFullRange() {
"GetAudioOutputLevelFullRange() => level=%d", level); "GetAudioOutputLevelFullRange() => level=%d", level);
return level; return level;
} }
bool AudioMixerImpl::GetAudioSourceMixabilityStatusForTest(
MixerAudioSource* audio_source) {
RTC_DCHECK_RUN_ON(&thread_checker_);
rtc::CritScope lock(&crit_);
const auto non_anonymous_iter =
FindSourceInList(audio_source, &audio_source_list_);
if (non_anonymous_iter != audio_source_list_.end()) {
return non_anonymous_iter->IsMixed();
}
const auto anonymous_iter =
FindSourceInList(audio_source, &additional_audio_source_list_);
if (anonymous_iter != audio_source_list_.end()) {
return anonymous_iter->IsMixed();
}
LOG_T_F(LS_ERROR) << "Audio source unknown";
return false;
}
} // namespace webrtc } // namespace webrtc

View File

@ -19,6 +19,7 @@
#include "webrtc/base/thread_checker.h" #include "webrtc/base/thread_checker.h"
#include "webrtc/modules/audio_mixer/audio_mixer.h" #include "webrtc/modules/audio_mixer/audio_mixer.h"
#include "webrtc/modules/audio_mixer/audio_mixer_defines.h" #include "webrtc/modules/audio_mixer/audio_mixer_defines.h"
#include "webrtc/modules/audio_mixer/audio_source_with_mix_status.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/include/module_common_types.h" #include "webrtc/modules/include/module_common_types.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
@ -28,14 +29,14 @@
namespace webrtc { namespace webrtc {
typedef std::vector<AudioFrame*> AudioFrameList; typedef std::vector<AudioFrame*> AudioFrameList;
typedef std::vector<MixerAudioSource*> MixerAudioSourceList; typedef std::vector<AudioSourceWithMixStatus> MixerAudioSourceList;
class AudioMixerImpl : public AudioMixer { class AudioMixerImpl : public AudioMixer {
public: public:
// AudioProcessing only accepts 10 ms frames. // AudioProcessing only accepts 10 ms frames.
static const int kFrameDurationInMs = 10; static const int kFrameDurationInMs = 10;
static std::unique_ptr<AudioMixer> Create(int id); static std::unique_ptr<AudioMixerImpl> Create(int id);
~AudioMixerImpl() override; ~AudioMixerImpl() override;
@ -51,6 +52,11 @@ class AudioMixerImpl : public AudioMixer {
bool AnonymousMixabilityStatus( bool AnonymousMixabilityStatus(
const MixerAudioSource& audio_source) const override; const MixerAudioSource& audio_source) const override;
// Returns true if the source was mixed last round. Returns
// false and logs an error if the source was never added to the
// mixer.
bool GetAudioSourceMixabilityStatusForTest(MixerAudioSource* audio_source);
private: private:
AudioMixerImpl(int id, std::unique_ptr<AudioProcessing> limiter); AudioMixerImpl(int id, std::unique_ptr<AudioProcessing> limiter);
@ -61,16 +67,11 @@ class AudioMixerImpl : public AudioMixer {
// Compute what audio sources to mix from audio_source_list_. Ramp // Compute what audio sources to mix from audio_source_list_. Ramp
// in and out. Update mixed status. Mixes up to // in and out. Update mixed status. Mixes up to
// kMaximumAmountOfMixedAudioSources audio sources. // kMaximumAmountOfMixedAudioSources audio sources.
AudioFrameList GetNonAnonymousAudio() const EXCLUSIVE_LOCKS_REQUIRED(crit_); AudioFrameList GetNonAnonymousAudio() EXCLUSIVE_LOCKS_REQUIRED(crit_);
// Return the AudioFrames that should be mixed anonymously. Ramp in // Return the AudioFrames that should be mixed anonymously. Ramp in
// and out. Update mixed status. // and out. Update mixed status.
AudioFrameList GetAnonymousAudio() const EXCLUSIVE_LOCKS_REQUIRED(crit_); AudioFrameList GetAnonymousAudio() EXCLUSIVE_LOCKS_REQUIRED(crit_);
// This function returns true if it finds the MixerAudioSource in the
// specified list of MixerAudioSources.
bool IsAudioSourceInList(const MixerAudioSource& audio_source,
const MixerAudioSourceList& audio_source_list) const;
// Add/remove the MixerAudioSource to the specified // Add/remove the MixerAudioSource to the specified
// MixerAudioSource list. // MixerAudioSource list.

View File

@ -8,31 +8,36 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include "webrtc/modules/audio_mixer/audio_mixer_defines.h" #include "webrtc/modules/audio_mixer/audio_source_with_mix_status.h"
namespace webrtc { namespace webrtc {
MixerAudioSource::MixerAudioSource() {} AudioSourceWithMixStatus::AudioSourceWithMixStatus(
MixerAudioSource* audio_source)
: audio_source_(audio_source) {}
MixerAudioSource::~MixerAudioSource() {} AudioSourceWithMixStatus::~AudioSourceWithMixStatus() {}
bool MixerAudioSource::IsMixed() const { bool AudioSourceWithMixStatus::IsMixed() const {
return is_mixed_; return is_mixed_;
} }
bool MixerAudioSource::WasMixed() const { bool AudioSourceWithMixStatus::WasMixed() const {
// Was mixed is the same as is mixed depending on perspective. This function // Was mixed is the same as is mixed depending on perspective. This function
// is for the perspective of AudioMixerImpl. // is for the perspective of AudioMixerImpl.
return IsMixed(); return IsMixed();
} }
int32_t MixerAudioSource::SetIsMixed(const bool mixed) { void AudioSourceWithMixStatus::SetIsMixed(const bool mixed) {
is_mixed_ = mixed; is_mixed_ = mixed;
return 0;
} }
void MixerAudioSource::ResetMixedStatus() { void AudioSourceWithMixStatus::ResetMixedStatus() {
is_mixed_ = false; is_mixed_ = false;
} }
MixerAudioSource* AudioSourceWithMixStatus::audio_source() const {
return audio_source_;
}
} // namespace webrtc } // namespace webrtc

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef WEBRTC_MODULES_AUDIO_MIXER_AUDIO_SOURCE_WITH_MIX_STATUS_H_
#define WEBRTC_MODULES_AUDIO_MIXER_AUDIO_SOURCE_WITH_MIX_STATUS_H_
#include "webrtc/modules/audio_mixer/audio_mixer_defines.h"
namespace webrtc {
// A class that holds a mixer participant and its mixing status.
class AudioSourceWithMixStatus {
public:
explicit AudioSourceWithMixStatus(MixerAudioSource* audio_source);
~AudioSourceWithMixStatus();
AudioSourceWithMixStatus(const AudioSourceWithMixStatus&) = default;
// Returns true if the audio source was mixed this mix iteration.
bool IsMixed() const;
// Returns true if the audio source was mixed previous mix
// iteration.
bool WasMixed() const;
// Updates the mixed status.
void SetIsMixed(const bool mixed);
void ResetMixedStatus();
MixerAudioSource* audio_source() const;
private:
MixerAudioSource* audio_source_ = nullptr;
bool is_mixed_ = false;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_MIXER_AUDIO_SOURCE_WITH_MIX_STATUS_H_

View File

@ -15,7 +15,7 @@
#include "webrtc/base/bind.h" #include "webrtc/base/bind.h"
#include "webrtc/base/thread.h" #include "webrtc/base/thread.h"
#include "webrtc/modules/audio_mixer/audio_mixer.h" #include "webrtc/modules/audio_mixer/audio_mixer_impl.h"
#include "webrtc/modules/audio_mixer/audio_mixer_defines.h" #include "webrtc/modules/audio_mixer/audio_mixer_defines.h"
#include "webrtc/test/gmock.h" #include "webrtc/test/gmock.h"
@ -89,7 +89,7 @@ void MixAndCompare(
RTC_DCHECK(frames.size() == frame_info.size()); RTC_DCHECK(frames.size() == frame_info.size());
RTC_DCHECK(frame_info.size() == expected_status.size()); RTC_DCHECK(frame_info.size() == expected_status.size());
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId)); const std::unique_ptr<AudioMixerImpl> mixer(AudioMixerImpl::Create(kId));
std::vector<MockMixerAudioSource> participants(num_audio_sources); std::vector<MockMixerAudioSource> participants(num_audio_sources);
for (int i = 0; i < num_audio_sources; i++) { for (int i = 0; i < num_audio_sources; i++) {
@ -107,7 +107,8 @@ void MixAndCompare(
mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing); mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing);
for (int i = 0; i < num_audio_sources; i++) { for (int i = 0; i < num_audio_sources; i++) {
EXPECT_EQ(expected_status[i], participants[i].IsMixed()) EXPECT_EQ(expected_status[i],
mixer->GetAudioSourceMixabilityStatusForTest(&participants[i]))
<< "Mixed status of AudioSource #" << i << " wrong."; << "Mixed status of AudioSource #" << i << " wrong.";
} }
} }
@ -167,7 +168,7 @@ TEST(AudioMixer, LargestEnergyVadActiveMixed) {
constexpr int kAudioSources = constexpr int kAudioSources =
AudioMixer::kMaximumAmountOfMixedAudioSources + 3; AudioMixer::kMaximumAmountOfMixedAudioSources + 3;
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId)); const std::unique_ptr<AudioMixerImpl> mixer(AudioMixerImpl::Create(kId));
MockMixerAudioSource participants[kAudioSources]; MockMixerAudioSource participants[kAudioSources];
@ -194,7 +195,8 @@ TEST(AudioMixer, LargestEnergyVadActiveMixed) {
&audio_frame); &audio_frame);
for (int i = 0; i < kAudioSources; ++i) { for (int i = 0; i < kAudioSources; ++i) {
bool is_mixed = participants[i].IsMixed(); bool is_mixed =
mixer->GetAudioSourceMixabilityStatusForTest(&participants[i]);
if (i == kAudioSources - 1 || if (i == kAudioSources - 1 ||
i < kAudioSources - 1 - AudioMixer::kMaximumAmountOfMixedAudioSources) { i < kAudioSources - 1 - AudioMixer::kMaximumAmountOfMixedAudioSources) {
EXPECT_FALSE(is_mixed) << "Mixing status of AudioSource #" << i EXPECT_FALSE(is_mixed) << "Mixing status of AudioSource #" << i
@ -351,7 +353,7 @@ TEST(AudioMixer, RampedOutSourcesShouldNotBeMarkedMixed) {
constexpr int kAudioSources = constexpr int kAudioSources =
AudioMixer::kMaximumAmountOfMixedAudioSources + 1; AudioMixer::kMaximumAmountOfMixedAudioSources + 1;
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId)); const std::unique_ptr<AudioMixerImpl> mixer(AudioMixerImpl::Create(kId));
MockMixerAudioSource participants[kAudioSources]; MockMixerAudioSource participants[kAudioSources];
for (int i = 0; i < kAudioSources; i++) { for (int i = 0; i < kAudioSources; i++) {
@ -374,8 +376,8 @@ TEST(AudioMixer, RampedOutSourcesShouldNotBeMarkedMixed) {
// All participants but the loudest should have been mixed. // All participants but the loudest should have been mixed.
for (int i = 0; i < kAudioSources - 1; i++) { for (int i = 0; i < kAudioSources - 1; i++) {
EXPECT_TRUE(participants[i].IsMixed()) << "Mixed status of AudioSource #" EXPECT_TRUE(mixer->GetAudioSourceMixabilityStatusForTest(&participants[i]))
<< i << " wrong."; << "Mixed status of AudioSource #" << i << " wrong.";
} }
// Add new participant with higher energy. // Add new participant with higher energy.
@ -390,12 +392,13 @@ TEST(AudioMixer, RampedOutSourcesShouldNotBeMarkedMixed) {
mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing); mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing);
// The most quiet participant should not have been mixed. // The most quiet participant should not have been mixed.
EXPECT_FALSE(participants[0].IsMixed()) EXPECT_FALSE(mixer->GetAudioSourceMixabilityStatusForTest(&participants[0]))
<< "Mixed status of AudioSource #0 wrong."; << "Mixed status of AudioSource #0 wrong.";
// The loudest participants should have been mixed. // The loudest participants should have been mixed.
for (int i = 1; i < kAudioSources; i++) { for (int i = 1; i < kAudioSources; i++) {
EXPECT_EQ(true, participants[i].IsMixed()) EXPECT_EQ(true,
mixer->GetAudioSourceMixabilityStatusForTest(&participants[i]))
<< "Mixed status of AudioSource #" << i << " wrong."; << "Mixed status of AudioSource #" << i << " wrong.";
} }
} }