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:
@ -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 = [
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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
|
||||||
45
webrtc/modules/audio_mixer/audio_source_with_mix_status.h
Normal file
45
webrtc/modules/audio_mixer/audio_source_with_mix_status.h
Normal 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_
|
||||||
@ -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.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user