Several lock acquisitions and one of the two lock members are removed. ENSURE_LOCKS_REQUIRED and CalledOnValidThread annotations are added.

NOTRY=True

Review-Url: https://codereview.webrtc.org/2286343002
Cr-Commit-Position: refs/heads/master@{#14106}
This commit is contained in:
aleloi 2016-09-07 06:13:12 -07:00 committed by Commit bot
parent 08b0351ddd
commit 311525e715
3 changed files with 175 additions and 113 deletions

View File

@ -12,7 +12,9 @@
#include <algorithm>
#include <functional>
#include <utility>
#include "webrtc/base/thread_annotations.h"
#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"
@ -120,105 +122,80 @@ void NewMixHistory::ResetMixedStatus() {
}
std::unique_ptr<AudioMixer> AudioMixer::Create(int id) {
AudioMixerImpl* mixer = new AudioMixerImpl(id);
if (!mixer->Init()) {
delete mixer;
return NULL;
}
return std::unique_ptr<AudioMixer>(mixer);
return AudioMixerImpl::Create(id);
}
AudioMixerImpl::AudioMixerImpl(int id)
AudioMixerImpl::AudioMixerImpl(int id, std::unique_ptr<AudioProcessing> limiter)
: 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) {
time_stamp_(0),
limiter_(std::move(limiter)) {
SetOutputFrequency(kDefaultFrequency);
thread_checker_.DetachFromThread();
}
AudioMixerImpl::~AudioMixerImpl() {}
bool AudioMixerImpl::Init() {
crit_.reset(CriticalSectionWrapper::CreateCriticalSection());
if (crit_.get() == NULL)
return false;
cb_crit_.reset(CriticalSectionWrapper::CreateCriticalSection());
if (cb_crit_.get() == NULL)
return false;
std::unique_ptr<AudioMixer> AudioMixerImpl::Create(int id) {
Config config;
config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
limiter_.reset(AudioProcessing::Create(config));
if (!limiter_.get())
return false;
std::unique_ptr<AudioProcessing> limiter(AudioProcessing::Create(config));
if (!limiter.get())
return nullptr;
if (SetOutputFrequency(kDefaultFrequency) == -1)
return false;
if (limiter_->gain_control()->set_mode(GainControl::kFixedDigital) !=
limiter_->kNoError)
return false;
if (limiter->gain_control()->set_mode(GainControl::kFixedDigital) !=
limiter->kNoError)
return nullptr;
// 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_target_level_dbfs(7) != limiter->kNoError)
return nullptr;
if (limiter_->gain_control()->set_compression_gain_db(0) !=
limiter_->kNoError)
return false;
if (limiter->gain_control()->set_compression_gain_db(0) != limiter->kNoError)
return nullptr;
if (limiter_->gain_control()->enable_limiter(true) != limiter_->kNoError)
return false;
if (limiter->gain_control()->enable_limiter(true) != limiter->kNoError)
return nullptr;
if (limiter_->gain_control()->Enable(true) != limiter_->kNoError)
return false;
if (limiter->gain_control()->Enable(true) != limiter->kNoError)
return nullptr;
return true;
return std::unique_ptr<AudioMixer>(
new AudioMixerImpl(id, std::move(limiter)));
}
void AudioMixerImpl::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());
RTC_DCHECK_RUN_ON(&thread_checker_);
std::map<int, MixerAudioSource*> mixedAudioSourcesMap;
if (sample_rate != kNbInHz && sample_rate != kWbInHz &&
sample_rate != kSwbInHz && sample_rate != kFbInHz) {
WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, id_,
"Invalid frequency: %d", sample_rate);
RTC_NOTREACHED();
return;
}
if (OutputFrequency() != sample_rate) {
SetOutputFrequency(static_cast<Frequency>(sample_rate));
}
AudioFrameList mixList;
AudioFrameList additionalFramesList;
std::map<int, MixerAudioSource*> mixedAudioSourcesMap;
int num_mixed_audio_sources;
{
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);
}
rtc::CritScope lock(&crit_);
mixList = UpdateToMix(kMaximumAmountOfMixedAudioSources);
GetAdditionalAudio(&additionalFramesList);
num_mixed_audio_sources = static_cast<int>(num_mixed_audio_sources_);
}
for (FrameAndMuteInfo& frame_and_mute : mixList) {
@ -234,24 +211,19 @@ void AudioMixerImpl::Mix(int sample_rate,
time_stamp_ += static_cast<uint32_t>(sample_size_);
use_limiter_ = num_mixed_audio_sources_ > 1;
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);
}
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.
@ -261,8 +233,7 @@ void AudioMixerImpl::Mix(int sample_rate,
}
int32_t AudioMixerImpl::SetOutputFrequency(const Frequency& frequency) {
CriticalSectionScoped cs(crit_.get());
RTC_DCHECK_RUN_ON(&thread_checker_);
output_frequency_ = frequency;
sample_size_ =
static_cast<size_t>((output_frequency_ * kFrameDurationInMs) / 1000);
@ -271,7 +242,7 @@ int32_t AudioMixerImpl::SetOutputFrequency(const Frequency& frequency) {
}
AudioMixer::Frequency AudioMixerImpl::OutputFrequency() const {
CriticalSectionScoped cs(crit_.get());
RTC_DCHECK_RUN_ON(&thread_checker_);
return output_frequency_;
}
@ -282,9 +253,8 @@ int32_t AudioMixerImpl::SetMixabilityStatus(MixerAudioSource* audio_source,
// audio source is in the _audioSourceList if it is being mixed.
SetAnonymousMixabilityStatus(audio_source, false);
}
size_t numMixedAudioSources;
{
CriticalSectionScoped cs(cb_crit_.get());
rtc::CritScope lock(&crit_);
const bool isMixed = IsAudioSourceInList(*audio_source, audio_source_list_);
// API must be called with a new state.
if (!(mixable ^ isMixed)) {
@ -309,27 +279,22 @@ int32_t AudioMixerImpl::SetMixabilityStatus(MixerAudioSource* audio_source,
if (numMixedNonAnonymous > kMaximumAmountOfMixedAudioSources) {
numMixedNonAnonymous = kMaximumAmountOfMixedAudioSources;
}
numMixedAudioSources =
num_mixed_audio_sources_ =
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 AudioMixerImpl::MixabilityStatus(
const MixerAudioSource& audio_source) const {
CriticalSectionScoped cs(cb_crit_.get());
rtc::CritScope lock(&crit_);
return IsAudioSourceInList(audio_source, audio_source_list_);
}
int32_t AudioMixerImpl::SetAnonymousMixabilityStatus(
MixerAudioSource* audio_source,
bool anonymous) {
CriticalSectionScoped cs(cb_crit_.get());
rtc::CritScope lock(&crit_);
if (IsAudioSourceInList(*audio_source, additional_audio_source_list_)) {
if (anonymous) {
return 0;
@ -363,11 +328,12 @@ int32_t AudioMixerImpl::SetAnonymousMixabilityStatus(
bool AudioMixerImpl::AnonymousMixabilityStatus(
const MixerAudioSource& audio_source) const {
CriticalSectionScoped cs(cb_crit_.get());
rtc::CritScope lock(&crit_);
return IsAudioSourceInList(audio_source, additional_audio_source_list_);
}
AudioFrameList AudioMixerImpl::UpdateToMix(size_t maxAudioFrameCounter) const {
RTC_DCHECK_RUN_ON(&thread_checker_);
AudioFrameList result;
std::vector<SourceFrame> audioSourceMixingDataList;
@ -426,6 +392,7 @@ AudioFrameList AudioMixerImpl::UpdateToMix(size_t maxAudioFrameCounter) const {
void AudioMixerImpl::GetAdditionalAudio(
AudioFrameList* additionalFramesList) const {
RTC_DCHECK_RUN_ON(&thread_checker_);
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"GetAdditionalAudio(additionalFramesList)");
// The GetAudioFrameWithMuted() callback may result in the audio source being
@ -533,6 +500,7 @@ int32_t AudioMixerImpl::MixFromList(AudioFrame* mixedAudio,
int32_t AudioMixerImpl::MixAnonomouslyFromList(
AudioFrame* mixedAudio,
const AudioFrameList& audioFrameList) const {
RTC_DCHECK_RUN_ON(&thread_checker_);
WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, id_,
"MixAnonomouslyFromList(mixedAudio, audioFrameList)");
@ -549,6 +517,7 @@ int32_t AudioMixerImpl::MixAnonomouslyFromList(
}
bool AudioMixerImpl::LimitMixedAudio(AudioFrame* mixedAudio) const {
RTC_DCHECK_RUN_ON(&thread_checker_);
if (!use_limiter_) {
return true;
}
@ -578,6 +547,7 @@ bool AudioMixerImpl::LimitMixedAudio(AudioFrame* mixedAudio) const {
}
int AudioMixerImpl::GetOutputAudioLevel() {
RTC_DCHECK_RUN_ON(&thread_checker_);
const int level = audio_level_.Level();
WEBRTC_TRACE(kTraceStateInfo, kTraceAudioMixerServer, id_,
"GetAudioOutputLevel() => level=%d", level);
@ -585,6 +555,7 @@ int AudioMixerImpl::GetOutputAudioLevel() {
}
int AudioMixerImpl::GetOutputAudioLevelFullRange() {
RTC_DCHECK_RUN_ON(&thread_checker_);
const int level = audio_level_.LevelFullRange();
WEBRTC_TRACE(kTraceStateInfo, kTraceAudioMixerServer, id_,
"GetAudioOutputLevelFullRange() => level=%d", level);

View File

@ -62,13 +62,10 @@ class AudioMixerImpl : public AudioMixer {
// AudioProcessing only accepts 10 ms frames.
static const int kFrameDurationInMs = 10;
explicit AudioMixerImpl(int id);
static std::unique_ptr<AudioMixer> Create(int id);
~AudioMixerImpl() override;
// Must be called after ctor.
bool Init();
// AudioMixer functions
int32_t SetMixabilityStatus(MixerAudioSource* audio_source,
bool mixable) override;
@ -82,6 +79,8 @@ class AudioMixerImpl : public AudioMixer {
const MixerAudioSource& audio_source) const override;
private:
AudioMixerImpl(int id, std::unique_ptr<AudioProcessing> limiter);
// Set/get mix frequency
int32_t SetOutputFrequency(const Frequency& frequency);
Frequency OutputFrequency() const;
@ -89,7 +88,8 @@ class AudioMixerImpl : public AudioMixer {
// Compute what audio sources to mix from audio_source_list_. Ramp in
// and out. Update mixed status. maxAudioFrameCounter specifies how
// many participants are allowed to be mixed.
AudioFrameList UpdateToMix(size_t maxAudioFrameCounter) const;
AudioFrameList UpdateToMix(size_t maxAudioFrameCounter) const
EXCLUSIVE_LOCKS_REQUIRED(crit_);
// Return the lowest mixing frequency that can be used without having to
// downsample any audio.
@ -98,7 +98,8 @@ class AudioMixerImpl : public AudioMixer {
const MixerAudioSourceList& mixList) const;
// Return the AudioFrames that should be mixed anonymously.
void GetAdditionalAudio(AudioFrameList* additionalFramesList) const;
void GetAdditionalAudio(AudioFrameList* additionalFramesList) const
EXCLUSIVE_LOCKS_REQUIRED(crit_);
// This function returns true if it finds the MixerAudioSource in the
// specified list of MixerAudioSources.
@ -131,36 +132,35 @@ class AudioMixerImpl : public AudioMixer {
int GetOutputAudioLevelFullRange() override;
std::unique_ptr<CriticalSectionWrapper> crit_;
std::unique_ptr<CriticalSectionWrapper> cb_crit_;
rtc::CriticalSection crit_;
int32_t id_;
const int32_t id_;
// The current sample frequency and sample size when mixing.
Frequency output_frequency_;
size_t sample_size_;
Frequency output_frequency_ ACCESS_ON(&thread_checker_);
size_t sample_size_ ACCESS_ON(&thread_checker_);
// List of all audio sources. Note all lists are disjunct
MixerAudioSourceList audio_source_list_; // May be mixed.
MixerAudioSourceList audio_source_list_ GUARDED_BY(crit_); // May be mixed.
// Always mixed, anonomously.
MixerAudioSourceList additional_audio_source_list_;
MixerAudioSourceList additional_audio_source_list_ GUARDED_BY(crit_);
size_t num_mixed_audio_sources_;
size_t num_mixed_audio_sources_ GUARDED_BY(crit_);
// Determines if we will use a limiter for clipping protection during
// mixing.
bool use_limiter_;
bool use_limiter_ ACCESS_ON(&thread_checker_);
uint32_t time_stamp_;
uint32_t time_stamp_ ACCESS_ON(&thread_checker_);
// Ensures that Mix is called from the same thread.
rtc::ThreadChecker thread_checker_;
// Used for inhibiting saturation in mixing.
std::unique_ptr<AudioProcessing> limiter_;
std::unique_ptr<AudioProcessing> limiter_ ACCESS_ON(&thread_checker_);
// Measures audio level for the combined signal.
voe::AudioLevel audio_level_;
voe::AudioLevel audio_level_ ACCESS_ON(&thread_checker_);
};
} // namespace webrtc

View File

@ -8,10 +8,14 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include <string.h>
#include <memory>
#include <utility>
#include "testing/gmock/include/gmock/gmock.h"
#include "webrtc/base/bind.h"
#include "webrtc/base/thread.h"
#include "webrtc/modules/audio_mixer/audio_mixer_defines.h"
#include "webrtc/modules/audio_mixer/audio_mixer.h"
@ -103,7 +107,7 @@ void MixAndCompare(
mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing);
for (int i = 0; i < num_audio_sources; i++) {
EXPECT_EQ(participants[i].IsMixed(), expected_status[i])
EXPECT_EQ(expected_status[i], participants[i].IsMixed())
<< "Mixed status of AudioSource #" << i << " wrong.";
}
}
@ -202,6 +206,63 @@ TEST(AudioMixer, LargestEnergyVadActiveMixed) {
}
}
TEST(AudioMixer, FrameNotModifiedForSingleParticipant) {
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId));
MockMixerAudioSource participant;
ResetFrame(participant.fake_frame());
const int n_samples = participant.fake_frame()->samples_per_channel_;
// Modify the frame so that it's not zero.
for (int j = 0; j < n_samples; j++) {
participant.fake_frame()->data_[j] = j;
}
EXPECT_EQ(0, mixer->SetMixabilityStatus(&participant, true));
EXPECT_CALL(participant, GetAudioFrameWithMuted(_, _)).Times(Exactly(2));
AudioFrame audio_frame;
// Two mix iteration to compare after the ramp-up step.
for (int i = 0; i < 2; i++) {
mixer->Mix(kDefaultSampleRateHz,
1, // number of channels
&audio_frame);
}
EXPECT_EQ(
0, memcmp(participant.fake_frame()->data_, audio_frame.data_, n_samples));
}
TEST(AudioMixer, FrameNotModifiedForSingleAnonymousParticipant) {
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId));
MockMixerAudioSource participant;
ResetFrame(participant.fake_frame());
const int n_samples = participant.fake_frame()->samples_per_channel_;
// Modify the frame so that it's not zero.
for (int j = 0; j < n_samples; j++) {
participant.fake_frame()->data_[j] = j;
}
EXPECT_EQ(0, mixer->SetMixabilityStatus(&participant, true));
EXPECT_EQ(0, mixer->SetAnonymousMixabilityStatus(&participant, true));
EXPECT_CALL(participant, GetAudioFrameWithMuted(_, _)).Times(Exactly(2));
AudioFrame audio_frame;
// Two mix iteration to compare after the ramp-up step.
for (int i = 0; i < 2; i++) {
mixer->Mix(kDefaultSampleRateHz,
1, // number of channels
&audio_frame);
}
EXPECT_EQ(
0, memcmp(participant.fake_frame()->data_, audio_frame.data_, n_samples));
}
TEST(AudioMixer, ParticipantSampleRate) {
const std::unique_ptr<AudioMixer> mixer(AudioMixer::Create(kId));
@ -332,11 +393,41 @@ TEST(AudioMixer, RampedOutSourcesShouldNotBeMarkedMixed) {
// The loudest participants should have been mixed.
for (int i = 1; i < kAudioSources; i++) {
EXPECT_EQ(participants[i].IsMixed(), true)
EXPECT_EQ(true, participants[i].IsMixed())
<< "Mixed status of AudioSource #" << i << " wrong.";
}
}
// This test checks that the initialization and participant addition
// can be done on a different thread.
TEST(AudioMixer, ConstructFromOtherThread) {
std::unique_ptr<rtc::Thread> init_thread = rtc::Thread::Create();
std::unique_ptr<rtc::Thread> participant_thread = rtc::Thread::Create();
init_thread->Start();
std::unique_ptr<AudioMixer> mixer(
init_thread->Invoke<std::unique_ptr<AudioMixer>>(
RTC_FROM_HERE, std::bind(&AudioMixer::Create, kId)));
MockMixerAudioSource participant;
ResetFrame(participant.fake_frame());
participant_thread->Start();
EXPECT_EQ(0, participant_thread->Invoke<int>(
RTC_FROM_HERE, rtc::Bind(&AudioMixer::SetMixabilityStatus,
mixer.get(), &participant, true)));
EXPECT_EQ(
0, participant_thread->Invoke<int>(
RTC_FROM_HERE, rtc::Bind(&AudioMixer::SetAnonymousMixabilityStatus,
mixer.get(), &participant, true)));
EXPECT_CALL(participant, GetAudioFrameWithMuted(_, kDefaultSampleRateHz))
.Times(Exactly(1));
// Do one mixer iteration
mixer->Mix(kDefaultSampleRateHz, 1, &frame_for_mixing);
}
TEST(AudioMixer, MutedShouldMixAfterUnmuted) {
constexpr int kAudioSources =
AudioMixer::kMaximumAmountOfMixedAudioSources + 1;