Introduced the new locking scheme

BUG=webrtc:5099

Review URL: https://codereview.webrtc.org/1424663003

Cr-Commit-Position: refs/heads/master@{#10836}
This commit is contained in:
peah
2015-11-28 12:35:15 -08:00
committed by Commit bot
parent 3236b91f55
commit df3efa8c07
17 changed files with 1304 additions and 836 deletions

File diff suppressed because it is too large Load Diff

View File

@ -15,23 +15,32 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/thread_annotations.h" #include "webrtc/base/thread_annotations.h"
#include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/include/file_wrapper.h"
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
// Files generated at build-time by the protobuf compiler.
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "webrtc/audio_processing/debug.pb.h"
#endif
#endif // WEBRTC_AUDIOPROC_DEBUG_DUMP
namespace webrtc { namespace webrtc {
class AgcManagerDirect; class AgcManagerDirect;
class AudioBuffer;
class AudioConverter; class AudioConverter;
template<typename T> template<typename T>
class Beamformer; class Beamformer;
class CriticalSectionWrapper;
class EchoCancellationImpl; class EchoCancellationImpl;
class EchoControlMobileImpl; class EchoControlMobileImpl;
class FileWrapper;
class GainControlImpl; class GainControlImpl;
class GainControlForNewAgc; class GainControlForNewAgc;
class HighPassFilterImpl; class HighPassFilterImpl;
@ -42,23 +51,14 @@ class TransientSuppressor;
class VoiceDetectionImpl; class VoiceDetectionImpl;
class IntelligibilityEnhancer; class IntelligibilityEnhancer;
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
namespace audioproc {
class Event;
} // namespace audioproc
#endif
class AudioProcessingImpl : public AudioProcessing { class AudioProcessingImpl : public AudioProcessing {
public: public:
// Methods forcing APM to run in a single-threaded manner.
// Acquires both the render and capture locks.
explicit AudioProcessingImpl(const Config& config); explicit AudioProcessingImpl(const Config& config);
// AudioProcessingImpl takes ownership of beamformer. // AudioProcessingImpl takes ownership of beamformer.
AudioProcessingImpl(const Config& config, Beamformer<float>* beamformer); AudioProcessingImpl(const Config& config, Beamformer<float>* beamformer);
virtual ~AudioProcessingImpl(); virtual ~AudioProcessingImpl();
// AudioProcessing methods.
int Initialize() override; int Initialize() override;
int Initialize(int input_sample_rate_hz, int Initialize(int input_sample_rate_hz,
int output_sample_rate_hz, int output_sample_rate_hz,
@ -68,12 +68,14 @@ class AudioProcessingImpl : public AudioProcessing {
ChannelLayout reverse_layout) override; ChannelLayout reverse_layout) override;
int Initialize(const ProcessingConfig& processing_config) override; int Initialize(const ProcessingConfig& processing_config) override;
void SetExtraOptions(const Config& config) override; void SetExtraOptions(const Config& config) override;
int proc_sample_rate_hz() const override; void UpdateHistogramsOnCallEnd() override;
int proc_split_sample_rate_hz() const override; int StartDebugRecording(const char filename[kMaxFilenameSize]) override;
int num_input_channels() const override; int StartDebugRecording(FILE* handle) override;
int num_output_channels() const override; int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) override;
int num_reverse_channels() const override; int StopDebugRecording() override;
void set_output_will_be_muted(bool muted) override;
// Capture-side exclusive methods possibly running APM in a
// multi-threaded manner. Acquire the capture lock.
int ProcessStream(AudioFrame* frame) override; int ProcessStream(AudioFrame* frame) override;
int ProcessStream(const float* const* src, int ProcessStream(const float* const* src,
size_t samples_per_channel, size_t samples_per_channel,
@ -86,6 +88,14 @@ class AudioProcessingImpl : public AudioProcessing {
const StreamConfig& input_config, const StreamConfig& input_config,
const StreamConfig& output_config, const StreamConfig& output_config,
float* const* dest) override; float* const* dest) override;
void set_output_will_be_muted(bool muted) override;
int set_stream_delay_ms(int delay) override;
void set_delay_offset_ms(int offset) override;
int delay_offset_ms() const override;
void set_stream_key_pressed(bool key_pressed) override;
// Render-side exclusive methods possibly running APM in a
// multi-threaded manner. Acquire the render lock.
int AnalyzeReverseStream(AudioFrame* frame) override; int AnalyzeReverseStream(AudioFrame* frame) override;
int ProcessReverseStream(AudioFrame* frame) override; int ProcessReverseStream(AudioFrame* frame) override;
int AnalyzeReverseStream(const float* const* data, int AnalyzeReverseStream(const float* const* data,
@ -96,17 +106,24 @@ class AudioProcessingImpl : public AudioProcessing {
const StreamConfig& reverse_input_config, const StreamConfig& reverse_input_config,
const StreamConfig& reverse_output_config, const StreamConfig& reverse_output_config,
float* const* dest) override; float* const* dest) override;
int set_stream_delay_ms(int delay) override;
// Methods only accessed from APM submodules or
// from AudioProcessing tests in a single-threaded manner.
// Hence there is no need for locks in these.
int proc_sample_rate_hz() const override;
int proc_split_sample_rate_hz() const override;
int num_input_channels() const override;
int num_output_channels() const override;
int num_reverse_channels() const override;
int stream_delay_ms() const override; int stream_delay_ms() const override;
bool was_stream_delay_set() const override; bool was_stream_delay_set() const override
void set_delay_offset_ms(int offset) override; EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
int delay_offset_ms() const override;
void set_stream_key_pressed(bool key_pressed) override; // Methods returning pointers to APM submodules.
int StartDebugRecording(const char filename[kMaxFilenameSize]) override; // No locks are aquired in those, as those locks
int StartDebugRecording(FILE* handle) override; // would offer no protection (the submodules are
int StartDebugRecordingForPlatformFile(rtc::PlatformFile handle) override; // created only once in a single-treaded manner
int StopDebugRecording() override; // during APM creation).
void UpdateHistogramsOnCallEnd() override;
EchoCancellation* echo_cancellation() const override; EchoCancellation* echo_cancellation() const override;
EchoControlMobile* echo_control_mobile() const override; EchoControlMobile* echo_control_mobile() const override;
GainControl* gain_control() const override; GainControl* gain_control() const override;
@ -117,116 +134,209 @@ class AudioProcessingImpl : public AudioProcessing {
protected: protected:
// Overridden in a mock. // Overridden in a mock.
virtual int InitializeLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); virtual int InitializeLocked()
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
private: private:
struct ApmPublicSubmodules;
struct ApmPrivateSubmodules;
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
// State for the debug dump.
struct ApmDebugDumpThreadState {
ApmDebugDumpThreadState() : event_msg(new audioproc::Event()) {}
rtc::scoped_ptr<audioproc::Event> event_msg; // Protobuf message.
std::string event_str; // Memory for protobuf serialization.
// Serialized string of last saved APM configuration.
std::string last_serialized_config;
};
struct ApmDebugDumpState {
ApmDebugDumpState() : debug_file(FileWrapper::Create()) {}
rtc::scoped_ptr<FileWrapper> debug_file;
ApmDebugDumpThreadState render;
ApmDebugDumpThreadState capture;
};
#endif
// Method for modifying the formats struct that are called from both
// the render and capture threads. The check for whether modifications
// are needed is done while holding the render lock only, thereby avoiding
// that the capture thread blocks the render thread.
// The struct is modified in a single-threaded manner by holding both the
// render and capture locks.
int MaybeInitialize(const ProcessingConfig& config)
EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
int MaybeInitializeRender(const ProcessingConfig& processing_config)
EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
int MaybeInitializeCapture(const ProcessingConfig& processing_config)
EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
// Method for checking for the need of conversion. Accesses the formats
// structs in a read manner but the requirement for the render lock to be held
// was added as it currently anyway is always called in that manner.
bool rev_conversion_needed() const EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
bool render_check_rev_conversion_needed() const
EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
// Methods requiring APM running in a single-threaded manner.
// Are called with both the render and capture locks already
// acquired.
void InitializeExperimentalAgc()
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
void InitializeTransient()
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
void InitializeBeamformer()
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
void InitializeIntelligibility()
EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
int InitializeLocked(const ProcessingConfig& config) int InitializeLocked(const ProcessingConfig& config)
EXCLUSIVE_LOCKS_REQUIRED(crit_); EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
int MaybeInitializeLockedRender(const ProcessingConfig& config)
EXCLUSIVE_LOCKS_REQUIRED(crit_); // Capture-side exclusive methods possibly running APM in a multi-threaded
int MaybeInitializeLockedCapture(const ProcessingConfig& config) // manner that are called with the render lock already acquired.
EXCLUSIVE_LOCKS_REQUIRED(crit_); int ProcessStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
int MaybeInitializeLocked(const ProcessingConfig& config) bool output_copy_needed(bool is_data_processed) const
EXCLUSIVE_LOCKS_REQUIRED(crit_); EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
bool is_data_processed() const EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
bool synthesis_needed(bool is_data_processed) const
EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
bool analysis_needed(bool is_data_processed) const
EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
void MaybeUpdateHistograms() EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
// Render-side exclusive methods possibly running APM in a multi-threaded
// manner that are called with the render lock already acquired.
// TODO(ekm): Remove once all clients updated to new interface. // TODO(ekm): Remove once all clients updated to new interface.
int AnalyzeReverseStream(const float* const* src, int AnalyzeReverseStreamLocked(const float* const* src,
const StreamConfig& input_config, const StreamConfig& input_config,
const StreamConfig& output_config); const StreamConfig& output_config)
int ProcessStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
int ProcessReverseStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_); bool is_rev_processed() const EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
int ProcessReverseStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
bool is_data_processed() const; // Debug dump methods that are internal and called without locks.
bool output_copy_needed(bool is_data_processed) const; // TODO(peah): Make thread safe.
bool synthesis_needed(bool is_data_processed) const;
bool analysis_needed(bool is_data_processed) const;
bool is_rev_processed() const;
bool rev_conversion_needed() const;
// TODO(peah): Add EXCLUSIVE_LOCKS_REQUIRED for the method below.
bool render_check_rev_conversion_needed() const;
void InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void InitializeBeamformer() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void InitializeIntelligibility() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void MaybeUpdateHistograms() EXCLUSIVE_LOCKS_REQUIRED(crit_);
EchoCancellationImpl* echo_cancellation_;
EchoControlMobileImpl* echo_control_mobile_;
GainControlImpl* gain_control_;
HighPassFilterImpl* high_pass_filter_;
LevelEstimatorImpl* level_estimator_;
NoiseSuppressionImpl* noise_suppression_;
VoiceDetectionImpl* voice_detection_;
rtc::scoped_ptr<GainControlForNewAgc> gain_control_for_new_agc_;
std::list<ProcessingComponent*> component_list_;
CriticalSectionWrapper* crit_;
rtc::scoped_ptr<AudioBuffer> render_audio_;
rtc::scoped_ptr<AudioBuffer> capture_audio_;
rtc::scoped_ptr<AudioConverter> render_converter_;
#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP
// TODO(andrew): make this more graceful. Ideally we would split this stuff // TODO(andrew): make this more graceful. Ideally we would split this stuff
// out into a separate class with an "enabled" and "disabled" implementation. // out into a separate class with an "enabled" and "disabled" implementation.
int WriteMessageToDebugFile(); static int WriteMessageToDebugFile(FileWrapper* debug_file,
int WriteInitMessage(); rtc::CriticalSection* crit_debug,
ApmDebugDumpThreadState* debug_state);
int WriteInitMessage() EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
// Writes Config message. If not |forced|, only writes the current config if // Writes Config message. If not |forced|, only writes the current config if
// it is different from the last saved one; if |forced|, writes the config // it is different from the last saved one; if |forced|, writes the config
// regardless of the last saved. // regardless of the last saved.
int WriteConfigMessage(bool forced); int WriteConfigMessage(bool forced) EXCLUSIVE_LOCKS_REQUIRED(crit_capture_)
EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
rtc::scoped_ptr<FileWrapper> debug_file_; // Critical section.
rtc::scoped_ptr<audioproc::Event> event_msg_; // Protobuf message. mutable rtc::CriticalSection crit_debug_;
std::string event_str_; // Memory for protobuf serialization.
// Serialized string of last saved APM configuration. // Debug dump state.
std::string last_serialized_config_; ApmDebugDumpState debug_dump_;
#endif #endif
// State that is written to while holding both the render and capture locks // Critical sections.
// but can be read while holding only one of the locks. mutable rtc::CriticalSection crit_render_ ACQUIRED_BEFORE(crit_capture_);
struct SharedState { mutable rtc::CriticalSection crit_capture_;
SharedState()
: // Format of processing streams at input/output call sites.
api_format_({{{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false}}}) {}
ProcessingConfig api_format_;
} shared_state_;
// Structs containing the pointers to the submodules.
rtc::scoped_ptr<ApmPublicSubmodules> public_submodules_;
rtc::scoped_ptr<ApmPrivateSubmodules> private_submodules_
GUARDED_BY(crit_capture_);
// State that is written to while holding both the render and capture locks
// but can be read without any lock being held.
// As this is only accessed internally of APM, and all internal methods in APM
// either are holding the render or capture locks, this construct is safe as
// it is not possible to read the variables while writing them.
struct ApmFormatState {
ApmFormatState()
: // Format of processing streams at input/output call sites.
api_format({{{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false},
{kSampleRate16kHz, 1, false}}}),
rev_proc_format(kSampleRate16kHz, 1) {}
ProcessingConfig api_format;
StreamConfig rev_proc_format;
} formats_;
// APM constants.
const struct ApmConstants {
ApmConstants(int agc_startup_min_volume,
const std::vector<Point> array_geometry,
SphericalPointf target_direction,
bool use_new_agc,
bool intelligibility_enabled,
bool beamformer_enabled)
: // Format of processing streams at input/output call sites.
agc_startup_min_volume(agc_startup_min_volume),
array_geometry(array_geometry),
target_direction(target_direction),
use_new_agc(use_new_agc),
intelligibility_enabled(intelligibility_enabled),
beamformer_enabled(beamformer_enabled) {}
int agc_startup_min_volume;
std::vector<Point> array_geometry;
SphericalPointf target_direction;
bool use_new_agc;
bool intelligibility_enabled;
bool beamformer_enabled;
} constants_;
struct ApmCaptureState {
ApmCaptureState(bool transient_suppressor_enabled)
: aec_system_delay_jumps(-1),
delay_offset_ms(0),
was_stream_delay_set(false),
last_stream_delay_ms(0),
last_aec_system_delay_ms(0),
stream_delay_jumps(-1),
output_will_be_muted(false),
key_pressed(false),
transient_suppressor_enabled(transient_suppressor_enabled),
fwd_proc_format(kSampleRate16kHz),
split_rate(kSampleRate16kHz) {}
int aec_system_delay_jumps;
int delay_offset_ms;
bool was_stream_delay_set;
int last_stream_delay_ms;
int last_aec_system_delay_ms;
int stream_delay_jumps;
bool output_will_be_muted;
bool key_pressed;
bool transient_suppressor_enabled;
rtc::scoped_ptr<AudioBuffer> capture_audio;
// Only the rate and samples fields of fwd_proc_format_ are used because the // Only the rate and samples fields of fwd_proc_format_ are used because the
// forward processing number of channels is mutable and is tracked by the // forward processing number of channels is mutable and is tracked by the
// capture_audio_. // capture_audio_.
StreamConfig fwd_proc_format_; StreamConfig fwd_proc_format;
StreamConfig rev_proc_format_; int split_rate;
int split_rate_; } capture_ GUARDED_BY(crit_capture_);
int stream_delay_ms_; struct ApmCaptureNonLockedState {
int delay_offset_ms_; ApmCaptureNonLockedState()
bool was_stream_delay_set_; : fwd_proc_format(kSampleRate16kHz),
int last_stream_delay_ms_; split_rate(kSampleRate16kHz),
int last_aec_system_delay_ms_; stream_delay_ms(0) {}
int stream_delay_jumps_; // Only the rate and samples fields of fwd_proc_format_ are used because the
int aec_system_delay_jumps_; // forward processing number of channels is mutable and is tracked by the
// capture_audio_.
StreamConfig fwd_proc_format;
int split_rate;
int stream_delay_ms;
} capture_nonlocked_;
bool output_will_be_muted_ GUARDED_BY(crit_); struct ApmRenderState {
rtc::scoped_ptr<AudioConverter> render_converter;
bool key_pressed_; rtc::scoped_ptr<AudioBuffer> render_audio;
} render_ GUARDED_BY(crit_render_);
// Only set through the constructor's Config parameter.
const bool use_new_agc_;
rtc::scoped_ptr<AgcManagerDirect> agc_manager_ GUARDED_BY(crit_);
int agc_startup_min_volume_;
bool transient_suppressor_enabled_;
rtc::scoped_ptr<TransientSuppressor> transient_suppressor_;
const bool beamformer_enabled_;
rtc::scoped_ptr<Beamformer<float>> beamformer_;
const std::vector<Point> array_geometry_;
const SphericalPointf target_direction_;
bool intelligibility_enabled_;
rtc::scoped_ptr<IntelligibilityEnhancer> intelligibility_enhancer_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -30,41 +30,6 @@ namespace {
class AudioProcessingImplLockTest; class AudioProcessingImplLockTest;
// Sleeps a random time between 0 and max_sleep milliseconds.
void SleepRandomMs(int max_sleep, test::Random* rand_gen) {
int sleeptime = rand_gen->Rand(0, max_sleep);
SleepMs(sleeptime);
}
// Populates a float audio frame with random data.
void PopulateAudioFrame(float** frame,
float amplitude,
size_t num_channels,
size_t samples_per_channel,
test::Random* rand_gen) {
for (size_t ch = 0; ch < num_channels; ch++) {
for (size_t k = 0; k < samples_per_channel; k++) {
// Store random 16 bit quantized float number between +-amplitude.
frame[ch][k] = amplitude * (2 * rand_gen->Rand<float>() - 1);
}
}
}
// Populates an audioframe frame of AudioFrame type with random data.
void PopulateAudioFrame(AudioFrame* frame,
int16_t amplitude,
test::Random* rand_gen) {
ASSERT_GT(amplitude, 0);
ASSERT_LE(amplitude, 32767);
for (int ch = 0; ch < frame->num_channels_; ch++) {
for (int k = 0; k < static_cast<int>(frame->samples_per_channel_); k++) {
// Store random 16 bit number between -(amplitude+1) and
// amplitude.
frame->data_[k * ch] = rand_gen->Rand(2 * amplitude + 1) - amplitude - 1;
}
}
}
// Type of the render thread APM API call to use in the test. // Type of the render thread APM API call to use in the test.
enum class RenderApiImpl { enum class RenderApiImpl {
ProcessReverseStreamImpl1, ProcessReverseStreamImpl1,
@ -97,6 +62,31 @@ enum class AecType {
BasicWebRtcAecSettingsWithAecMobile BasicWebRtcAecSettingsWithAecMobile
}; };
// Thread-safe random number generator wrapper.
class RandomGenerator {
public:
RandomGenerator() : rand_gen_(42U) {}
int RandInt(int min, int max) {
rtc::CritScope cs(&crit_);
return rand_gen_.Rand(min, max);
}
int RandInt(int max) {
rtc::CritScope cs(&crit_);
return rand_gen_.Rand(max);
}
float RandFloat() {
rtc::CritScope cs(&crit_);
return rand_gen_.Rand<float>();
}
private:
rtc::CriticalSection crit_;
test::Random rand_gen_ GUARDED_BY(crit_);
};
// Variables related to the audio data and formats. // Variables related to the audio data and formats.
struct AudioFrameData { struct AudioFrameData {
explicit AudioFrameData(int max_frame_size) { explicit AudioFrameData(int max_frame_size) {
@ -331,7 +321,7 @@ class CaptureSideCalledChecker {
class CaptureProcessor { class CaptureProcessor {
public: public:
CaptureProcessor(int max_frame_size, CaptureProcessor(int max_frame_size,
test::Random* rand_gen, RandomGenerator* rand_gen,
FrameCounters* shared_counters_state, FrameCounters* shared_counters_state,
CaptureSideCalledChecker* capture_call_checker, CaptureSideCalledChecker* capture_call_checker,
AudioProcessingImplLockTest* test_framework, AudioProcessingImplLockTest* test_framework,
@ -348,7 +338,7 @@ class CaptureProcessor {
void CallApmCaptureSide(); void CallApmCaptureSide();
void ApplyRuntimeSettingScheme(); void ApplyRuntimeSettingScheme();
test::Random* rand_gen_ = nullptr; RandomGenerator* rand_gen_ = nullptr;
FrameCounters* frame_counters_ = nullptr; FrameCounters* frame_counters_ = nullptr;
CaptureSideCalledChecker* capture_call_checker_ = nullptr; CaptureSideCalledChecker* capture_call_checker_ = nullptr;
AudioProcessingImplLockTest* test_ = nullptr; AudioProcessingImplLockTest* test_ = nullptr;
@ -360,13 +350,13 @@ class CaptureProcessor {
// Class for handling the stats processing. // Class for handling the stats processing.
class StatsProcessor { class StatsProcessor {
public: public:
StatsProcessor(test::Random* rand_gen, StatsProcessor(RandomGenerator* rand_gen,
TestConfig* test_config, TestConfig* test_config,
AudioProcessing* apm); AudioProcessing* apm);
bool Process(); bool Process();
private: private:
test::Random* rand_gen_ = nullptr; RandomGenerator* rand_gen_ = nullptr;
TestConfig* test_config_ = nullptr; TestConfig* test_config_ = nullptr;
AudioProcessing* apm_ = nullptr; AudioProcessing* apm_ = nullptr;
}; };
@ -375,7 +365,7 @@ class StatsProcessor {
class RenderProcessor { class RenderProcessor {
public: public:
RenderProcessor(int max_frame_size, RenderProcessor(int max_frame_size,
test::Random* rand_gen, RandomGenerator* rand_gen,
FrameCounters* shared_counters_state, FrameCounters* shared_counters_state,
CaptureSideCalledChecker* capture_call_checker, CaptureSideCalledChecker* capture_call_checker,
AudioProcessingImplLockTest* test_framework, AudioProcessingImplLockTest* test_framework,
@ -392,7 +382,7 @@ class RenderProcessor {
void CallApmRenderSide(); void CallApmRenderSide();
void ApplyRuntimeSettingScheme(); void ApplyRuntimeSettingScheme();
test::Random* rand_gen_ = nullptr; RandomGenerator* rand_gen_ = nullptr;
FrameCounters* frame_counters_ = nullptr; FrameCounters* frame_counters_ = nullptr;
CaptureSideCalledChecker* capture_call_checker_ = nullptr; CaptureSideCalledChecker* capture_call_checker_ = nullptr;
AudioProcessingImplLockTest* test_ = nullptr; AudioProcessingImplLockTest* test_ = nullptr;
@ -459,7 +449,7 @@ class AudioProcessingImplLockTest
rtc::PlatformThread render_thread_; rtc::PlatformThread render_thread_;
rtc::PlatformThread capture_thread_; rtc::PlatformThread capture_thread_;
rtc::PlatformThread stats_thread_; rtc::PlatformThread stats_thread_;
mutable test::Random rand_gen_; mutable RandomGenerator rand_gen_;
rtc::scoped_ptr<AudioProcessing> apm_; rtc::scoped_ptr<AudioProcessing> apm_;
TestConfig test_config_; TestConfig test_config_;
@ -470,12 +460,47 @@ class AudioProcessingImplLockTest
StatsProcessor stats_thread_state_; StatsProcessor stats_thread_state_;
}; };
// Sleeps a random time between 0 and max_sleep milliseconds.
void SleepRandomMs(int max_sleep, RandomGenerator* rand_gen) {
int sleeptime = rand_gen->RandInt(0, max_sleep);
SleepMs(sleeptime);
}
// Populates a float audio frame with random data.
void PopulateAudioFrame(float** frame,
float amplitude,
size_t num_channels,
size_t samples_per_channel,
RandomGenerator* rand_gen) {
for (size_t ch = 0; ch < num_channels; ch++) {
for (size_t k = 0; k < samples_per_channel; k++) {
// Store random 16 bit quantized float number between +-amplitude.
frame[ch][k] = amplitude * (2 * rand_gen->RandFloat() - 1);
}
}
}
// Populates an audioframe frame of AudioFrame type with random data.
void PopulateAudioFrame(AudioFrame* frame,
int16_t amplitude,
RandomGenerator* rand_gen) {
ASSERT_GT(amplitude, 0);
ASSERT_LE(amplitude, 32767);
for (int ch = 0; ch < frame->num_channels_; ch++) {
for (int k = 0; k < static_cast<int>(frame->samples_per_channel_); k++) {
// Store random 16 bit number between -(amplitude+1) and
// amplitude.
frame->data_[k * ch] =
rand_gen->RandInt(2 * amplitude + 1) - amplitude - 1;
}
}
}
AudioProcessingImplLockTest::AudioProcessingImplLockTest() AudioProcessingImplLockTest::AudioProcessingImplLockTest()
: test_complete_(EventWrapper::Create()), : test_complete_(EventWrapper::Create()),
render_thread_(RenderProcessorThreadFunc, this, "render"), render_thread_(RenderProcessorThreadFunc, this, "render"),
capture_thread_(CaptureProcessorThreadFunc, this, "capture"), capture_thread_(CaptureProcessorThreadFunc, this, "capture"),
stats_thread_(StatsProcessorThreadFunc, this, "stats"), stats_thread_(StatsProcessorThreadFunc, this, "stats"),
rand_gen_(42U),
apm_(AudioProcessingImpl::Create()), apm_(AudioProcessingImpl::Create()),
render_thread_state_(kMaxFrameSize, render_thread_state_(kMaxFrameSize,
&rand_gen_, &rand_gen_,
@ -513,7 +538,7 @@ void AudioProcessingImplLockTest::SetUp() {
ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
ASSERT_EQ(apm_->kNoError, ASSERT_EQ(apm_->kNoError,
apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
@ -552,7 +577,7 @@ void AudioProcessingImplLockTest::TearDown() {
stats_thread_.Stop(); stats_thread_.Stop();
} }
StatsProcessor::StatsProcessor(test::Random* rand_gen, StatsProcessor::StatsProcessor(RandomGenerator* rand_gen,
TestConfig* test_config, TestConfig* test_config,
AudioProcessing* apm) AudioProcessing* apm)
: rand_gen_(rand_gen), test_config_(test_config), apm_(apm) {} : rand_gen_(rand_gen), test_config_(test_config), apm_(apm) {}
@ -586,7 +611,7 @@ const float CaptureProcessor::kCaptureInputFloatLevel = 0.03125f;
CaptureProcessor::CaptureProcessor( CaptureProcessor::CaptureProcessor(
int max_frame_size, int max_frame_size,
test::Random* rand_gen, RandomGenerator* rand_gen,
FrameCounters* shared_counters_state, FrameCounters* shared_counters_state,
CaptureSideCalledChecker* capture_call_checker, CaptureSideCalledChecker* capture_call_checker,
AudioProcessingImplLockTest* test_framework, AudioProcessingImplLockTest* test_framework,
@ -824,8 +849,6 @@ void CaptureProcessor::ApplyRuntimeSettingScheme() {
apm_->set_stream_key_pressed(true); apm_->set_stream_key_pressed(true);
apm_->set_delay_offset_ms(15); apm_->set_delay_offset_ms(15);
EXPECT_EQ(apm_->delay_offset_ms(), 15); EXPECT_EQ(apm_->delay_offset_ms(), 15);
EXPECT_GE(apm_->num_reverse_channels(), 0);
EXPECT_LE(apm_->num_reverse_channels(), 2);
} else { } else {
ASSERT_EQ(AudioProcessing::Error::kNoError, ASSERT_EQ(AudioProcessing::Error::kNoError,
apm_->set_stream_delay_ms(50)); apm_->set_stream_delay_ms(50));
@ -833,9 +856,6 @@ void CaptureProcessor::ApplyRuntimeSettingScheme() {
apm_->set_delay_offset_ms(20); apm_->set_delay_offset_ms(20);
EXPECT_EQ(apm_->delay_offset_ms(), 20); EXPECT_EQ(apm_->delay_offset_ms(), 20);
apm_->delay_offset_ms(); apm_->delay_offset_ms();
apm_->num_reverse_channels();
EXPECT_GE(apm_->num_reverse_channels(), 0);
EXPECT_LE(apm_->num_reverse_channels(), 2);
} }
break; break;
default: default:
@ -852,7 +872,7 @@ void CaptureProcessor::ApplyRuntimeSettingScheme() {
const float RenderProcessor::kRenderInputFloatLevel = 0.5f; const float RenderProcessor::kRenderInputFloatLevel = 0.5f;
RenderProcessor::RenderProcessor(int max_frame_size, RenderProcessor::RenderProcessor(int max_frame_size,
test::Random* rand_gen, RandomGenerator* rand_gen,
FrameCounters* shared_counters_state, FrameCounters* shared_counters_state,
CaptureSideCalledChecker* capture_call_checker, CaptureSideCalledChecker* capture_call_checker,
AudioProcessingImplLockTest* test_framework, AudioProcessingImplLockTest* test_framework,
@ -1104,7 +1124,7 @@ INSTANTIATE_TEST_CASE_P(
::testing::ValuesIn(TestConfig::GenerateExtensiveTestConfigs())); ::testing::ValuesIn(TestConfig::GenerateExtensiveTestConfigs()));
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
DISABLED_AudioProcessingImplLockBrief, AudioProcessingImplLockBrief,
AudioProcessingImplLockTest, AudioProcessingImplLockTest,
::testing::ValuesIn(TestConfig::GenerateBriefTestConfigs())); ::testing::ValuesIn(TestConfig::GenerateBriefTestConfigs()));

View File

@ -18,7 +18,6 @@ extern "C" {
} }
#include "webrtc/modules/audio_processing/aec/echo_cancellation.h" #include "webrtc/modules/audio_processing/aec/echo_cancellation.h"
#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
namespace webrtc { namespace webrtc {
@ -63,10 +62,12 @@ static const size_t kMaxNumFramesToBuffer = 100;
} // namespace } // namespace
EchoCancellationImpl::EchoCancellationImpl(const AudioProcessing* apm, EchoCancellationImpl::EchoCancellationImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture)
: ProcessingComponent(), : ProcessingComponent(),
apm_(apm), apm_(apm),
crit_(crit), crit_render_(crit_render),
crit_capture_(crit_capture),
drift_compensation_enabled_(false), drift_compensation_enabled_(false),
metrics_enabled_(false), metrics_enabled_(false),
suppression_level_(kModerateSuppression), suppression_level_(kModerateSuppression),
@ -76,19 +77,24 @@ EchoCancellationImpl::EchoCancellationImpl(const AudioProcessing* apm,
delay_logging_enabled_(false), delay_logging_enabled_(false),
extended_filter_enabled_(false), extended_filter_enabled_(false),
delay_agnostic_enabled_(false), delay_agnostic_enabled_(false),
render_queue_element_max_size_(0) {} render_queue_element_max_size_(0) {
RTC_DCHECK(apm);
RTC_DCHECK(crit_render);
RTC_DCHECK(crit_capture);
}
EchoCancellationImpl::~EchoCancellationImpl() {} EchoCancellationImpl::~EchoCancellationImpl() {}
int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) { int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
rtc::CritScope cs_render(crit_render_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == apm_->num_reverse_channels()); assert(audio->num_channels() == apm_->num_reverse_channels());
int err = apm_->kNoError; int err = AudioProcessing::kNoError;
// The ordering convention must be followed to pass to the correct AEC. // The ordering convention must be followed to pass to the correct AEC.
size_t handle_index = 0; size_t handle_index = 0;
@ -102,7 +108,7 @@ int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
my_handle, audio->split_bands_const_f(j)[kBand0To8kHz], my_handle, audio->split_bands_const_f(j)[kBand0To8kHz],
audio->num_frames_per_band()); audio->num_frames_per_band());
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return MapError(err); // TODO(ajm): warning possible? return MapError(err); // TODO(ajm): warning possible?
} }
@ -116,18 +122,20 @@ int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
// Insert the samples into the queue. // Insert the samples into the queue.
if (!render_signal_queue_->Insert(&render_queue_buffer_)) { if (!render_signal_queue_->Insert(&render_queue_buffer_)) {
// The data queue is full and needs to be emptied.
ReadQueuedRenderData(); ReadQueuedRenderData();
// Retry the insert (should always work). // Retry the insert (should always work).
RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true);
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
// Read chunks of data that were received and queued on the render side from // Read chunks of data that were received and queued on the render side from
// a queue. All the data chunks are buffered into the farend signal of the AEC. // a queue. All the data chunks are buffered into the farend signal of the AEC.
void EchoCancellationImpl::ReadQueuedRenderData() { void EchoCancellationImpl::ReadQueuedRenderData() {
rtc::CritScope cs_capture(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return; return;
} }
@ -152,22 +160,23 @@ void EchoCancellationImpl::ReadQueuedRenderData() {
} }
int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) { int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs_capture(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
if (!apm_->was_stream_delay_set()) { if (!apm_->was_stream_delay_set()) {
return apm_->kStreamParameterNotSetError; return AudioProcessing::kStreamParameterNotSetError;
} }
if (drift_compensation_enabled_ && !was_stream_drift_set_) { if (drift_compensation_enabled_ && !was_stream_drift_set_) {
return apm_->kStreamParameterNotSetError; return AudioProcessing::kStreamParameterNotSetError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == apm_->num_output_channels()); assert(audio->num_channels() == apm_->num_output_channels());
int err = apm_->kNoError; int err = AudioProcessing::kNoError;
// The ordering convention must be followed to pass to the correct AEC. // The ordering convention must be followed to pass to the correct AEC.
size_t handle_index = 0; size_t handle_index = 0;
@ -175,26 +184,22 @@ int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
for (int i = 0; i < audio->num_channels(); i++) { for (int i = 0; i < audio->num_channels(); i++) {
for (int j = 0; j < apm_->num_reverse_channels(); j++) { for (int j = 0; j < apm_->num_reverse_channels(); j++) {
Handle* my_handle = handle(handle_index); Handle* my_handle = handle(handle_index);
err = WebRtcAec_Process( err = WebRtcAec_Process(my_handle, audio->split_bands_const_f(i),
my_handle, audio->num_bands(), audio->split_bands_f(i),
audio->split_bands_const_f(i),
audio->num_bands(),
audio->split_bands_f(i),
audio->num_frames_per_band(), audio->num_frames_per_band(),
apm_->stream_delay_ms(), apm_->stream_delay_ms(), stream_drift_samples_);
stream_drift_samples_);
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
err = MapError(err); err = MapError(err);
// TODO(ajm): Figure out how to return warnings properly. // TODO(ajm): Figure out how to return warnings properly.
if (err != apm_->kBadStreamParameterWarning) { if (err != AudioProcessing::kBadStreamParameterWarning) {
return err; return err;
} }
} }
int status = 0; int status = 0;
err = WebRtcAec_get_echo_status(my_handle, &status); err = WebRtcAec_get_echo_status(my_handle, &status);
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return MapError(err); return MapError(err);
} }
@ -207,77 +212,92 @@ int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) {
} }
was_stream_drift_set_ = false; was_stream_drift_set_ = false;
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int EchoCancellationImpl::Enable(bool enable) { int EchoCancellationImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); // Run in a single-threaded manner.
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
// Ensure AEC and AECM are not both enabled. // Ensure AEC and AECM are not both enabled.
// The is_enabled call is safe from a deadlock perspective
// as both locks are already held in the correct order.
if (enable && apm_->echo_control_mobile()->is_enabled()) { if (enable && apm_->echo_control_mobile()->is_enabled()) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
return EnableComponent(enable); return EnableComponent(enable);
} }
bool EchoCancellationImpl::is_enabled() const { bool EchoCancellationImpl::is_enabled() const {
rtc::CritScope cs(crit_capture_);
return is_component_enabled(); return is_component_enabled();
} }
int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) { int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) {
CriticalSectionScoped crit_scoped(crit_); {
if (MapSetting(level) == -1) { if (MapSetting(level) == -1) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
rtc::CritScope cs(crit_capture_);
suppression_level_ = level; suppression_level_ = level;
}
return Configure(); return Configure();
} }
EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level() EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level()
const { const {
rtc::CritScope cs(crit_capture_);
return suppression_level_; return suppression_level_;
} }
int EchoCancellationImpl::enable_drift_compensation(bool enable) { int EchoCancellationImpl::enable_drift_compensation(bool enable) {
CriticalSectionScoped crit_scoped(crit_); {
rtc::CritScope cs(crit_capture_);
drift_compensation_enabled_ = enable; drift_compensation_enabled_ = enable;
}
return Configure(); return Configure();
} }
bool EchoCancellationImpl::is_drift_compensation_enabled() const { bool EchoCancellationImpl::is_drift_compensation_enabled() const {
rtc::CritScope cs(crit_capture_);
return drift_compensation_enabled_; return drift_compensation_enabled_;
} }
void EchoCancellationImpl::set_stream_drift_samples(int drift) { void EchoCancellationImpl::set_stream_drift_samples(int drift) {
rtc::CritScope cs(crit_capture_);
was_stream_drift_set_ = true; was_stream_drift_set_ = true;
stream_drift_samples_ = drift; stream_drift_samples_ = drift;
} }
int EchoCancellationImpl::stream_drift_samples() const { int EchoCancellationImpl::stream_drift_samples() const {
rtc::CritScope cs(crit_capture_);
return stream_drift_samples_; return stream_drift_samples_;
} }
int EchoCancellationImpl::enable_metrics(bool enable) { int EchoCancellationImpl::enable_metrics(bool enable) {
CriticalSectionScoped crit_scoped(crit_); {
rtc::CritScope cs(crit_capture_);
metrics_enabled_ = enable; metrics_enabled_ = enable;
}
return Configure(); return Configure();
} }
bool EchoCancellationImpl::are_metrics_enabled() const { bool EchoCancellationImpl::are_metrics_enabled() const {
rtc::CritScope cs(crit_capture_);
return metrics_enabled_; return metrics_enabled_;
} }
// TODO(ajm): we currently just use the metrics from the first AEC. Think more // TODO(ajm): we currently just use the metrics from the first AEC. Think more
// aboue the best way to extend this to multi-channel. // aboue the best way to extend this to multi-channel.
int EchoCancellationImpl::GetMetrics(Metrics* metrics) { int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (metrics == NULL) { if (metrics == NULL) {
return apm_->kNullPointerError; return AudioProcessing::kNullPointerError;
} }
if (!is_component_enabled() || !metrics_enabled_) { if (!is_component_enabled() || !metrics_enabled_) {
return apm_->kNotEnabledError; return AudioProcessing::kNotEnabledError;
} }
AecMetrics my_metrics; AecMetrics my_metrics;
@ -286,7 +306,7 @@ int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
Handle* my_handle = static_cast<Handle*>(handle(0)); Handle* my_handle = static_cast<Handle*>(handle(0));
int err = WebRtcAec_GetMetrics(my_handle, &my_metrics); int err = WebRtcAec_GetMetrics(my_handle, &my_metrics);
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return MapError(err); return MapError(err);
} }
@ -310,63 +330,70 @@ int EchoCancellationImpl::GetMetrics(Metrics* metrics) {
metrics->a_nlp.maximum = my_metrics.aNlp.max; metrics->a_nlp.maximum = my_metrics.aNlp.max;
metrics->a_nlp.minimum = my_metrics.aNlp.min; metrics->a_nlp.minimum = my_metrics.aNlp.min;
return apm_->kNoError; return AudioProcessing::kNoError;
} }
bool EchoCancellationImpl::stream_has_echo() const { bool EchoCancellationImpl::stream_has_echo() const {
rtc::CritScope cs(crit_capture_);
return stream_has_echo_; return stream_has_echo_;
} }
int EchoCancellationImpl::enable_delay_logging(bool enable) { int EchoCancellationImpl::enable_delay_logging(bool enable) {
CriticalSectionScoped crit_scoped(crit_); {
rtc::CritScope cs(crit_capture_);
delay_logging_enabled_ = enable; delay_logging_enabled_ = enable;
}
return Configure(); return Configure();
} }
bool EchoCancellationImpl::is_delay_logging_enabled() const { bool EchoCancellationImpl::is_delay_logging_enabled() const {
rtc::CritScope cs(crit_capture_);
return delay_logging_enabled_; return delay_logging_enabled_;
} }
bool EchoCancellationImpl::is_delay_agnostic_enabled() const { bool EchoCancellationImpl::is_delay_agnostic_enabled() const {
rtc::CritScope cs(crit_capture_);
return delay_agnostic_enabled_; return delay_agnostic_enabled_;
} }
bool EchoCancellationImpl::is_extended_filter_enabled() const { bool EchoCancellationImpl::is_extended_filter_enabled() const {
rtc::CritScope cs(crit_capture_);
return extended_filter_enabled_; return extended_filter_enabled_;
} }
// TODO(bjornv): How should we handle the multi-channel case? // TODO(bjornv): How should we handle the multi-channel case?
int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) { int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
rtc::CritScope cs(crit_capture_);
float fraction_poor_delays = 0; float fraction_poor_delays = 0;
return GetDelayMetrics(median, std, &fraction_poor_delays); return GetDelayMetrics(median, std, &fraction_poor_delays);
} }
int EchoCancellationImpl::GetDelayMetrics(int* median, int* std, int EchoCancellationImpl::GetDelayMetrics(int* median, int* std,
float* fraction_poor_delays) { float* fraction_poor_delays) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (median == NULL) { if (median == NULL) {
return apm_->kNullPointerError; return AudioProcessing::kNullPointerError;
} }
if (std == NULL) { if (std == NULL) {
return apm_->kNullPointerError; return AudioProcessing::kNullPointerError;
} }
if (!is_component_enabled() || !delay_logging_enabled_) { if (!is_component_enabled() || !delay_logging_enabled_) {
return apm_->kNotEnabledError; return AudioProcessing::kNotEnabledError;
} }
Handle* my_handle = static_cast<Handle*>(handle(0)); Handle* my_handle = static_cast<Handle*>(handle(0));
const int err = const int err =
WebRtcAec_GetDelayMetrics(my_handle, median, std, fraction_poor_delays); WebRtcAec_GetDelayMetrics(my_handle, median, std, fraction_poor_delays);
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return MapError(err); return MapError(err);
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
struct AecCore* EchoCancellationImpl::aec_core() const { struct AecCore* EchoCancellationImpl::aec_core() const {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return NULL; return NULL;
} }
@ -376,13 +403,16 @@ struct AecCore* EchoCancellationImpl::aec_core() const {
int EchoCancellationImpl::Initialize() { int EchoCancellationImpl::Initialize() {
int err = ProcessingComponent::Initialize(); int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError || !is_component_enabled()) { {
rtc::CritScope cs(crit_capture_);
if (err != AudioProcessing::kNoError || !is_component_enabled()) {
return err; return err;
} }
}
AllocateRenderQueue(); AllocateRenderQueue();
return apm_->kNoError; return AudioProcessing::kNoError;
} }
void EchoCancellationImpl::AllocateRenderQueue() { void EchoCancellationImpl::AllocateRenderQueue() {
@ -390,6 +420,9 @@ void EchoCancellationImpl::AllocateRenderQueue() {
static_cast<size_t>(1), static_cast<size_t>(1),
kMaxAllowedValuesOfSamplesPerFrame * num_handles_required()); kMaxAllowedValuesOfSamplesPerFrame * num_handles_required());
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
// Reallocate the queue if the queue item size is too small to fit the // Reallocate the queue if the queue item size is too small to fit the
// data to put in the queue. // data to put in the queue.
if (render_queue_element_max_size_ < new_render_queue_element_max_size) { if (render_queue_element_max_size_ < new_render_queue_element_max_size) {
@ -410,8 +443,11 @@ void EchoCancellationImpl::AllocateRenderQueue() {
} }
void EchoCancellationImpl::SetExtraOptions(const Config& config) { void EchoCancellationImpl::SetExtraOptions(const Config& config) {
{
rtc::CritScope cs(crit_capture_);
extended_filter_enabled_ = config.Get<ExtendedFilter>().enabled; extended_filter_enabled_ = config.Get<ExtendedFilter>().enabled;
delay_agnostic_enabled_ = config.Get<DelayAgnostic>().enabled; delay_agnostic_enabled_ = config.Get<DelayAgnostic>().enabled;
}
Configure(); Configure();
} }
@ -425,23 +461,25 @@ void EchoCancellationImpl::DestroyHandle(void* handle) const {
} }
int EchoCancellationImpl::InitializeHandle(void* handle) const { int EchoCancellationImpl::InitializeHandle(void* handle) const {
// Not locked as it only relies on APM public API which is threadsafe.
assert(handle != NULL); assert(handle != NULL);
// TODO(ajm): Drift compensation is disabled in practice. If restored, it // TODO(ajm): Drift compensation is disabled in practice. If restored, it
// should be managed internally and not depend on the hardware sample rate. // should be managed internally and not depend on the hardware sample rate.
// For now, just hardcode a 48 kHz value. // For now, just hardcode a 48 kHz value.
return WebRtcAec_Init(static_cast<Handle*>(handle), return WebRtcAec_Init(static_cast<Handle*>(handle),
apm_->proc_sample_rate_hz(), apm_->proc_sample_rate_hz(), 48000);
48000);
} }
int EchoCancellationImpl::ConfigureHandle(void* handle) const { int EchoCancellationImpl::ConfigureHandle(void* handle) const {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
assert(handle != NULL); assert(handle != NULL);
AecConfig config; AecConfig config;
config.metricsMode = metrics_enabled_; config.metricsMode = metrics_enabled_;
config.nlpMode = MapSetting(suppression_level_); config.nlpMode = MapSetting(suppression_level_);
config.skewMode = drift_compensation_enabled_; config.skewMode = drift_compensation_enabled_;
config.delay_logging = delay_logging_enabled_; config.delay_logging = delay_logging_enabled_;
WebRtcAec_enable_extended_filter( WebRtcAec_enable_extended_filter(
WebRtcAec_aec_core(static_cast<Handle*>(handle)), WebRtcAec_aec_core(static_cast<Handle*>(handle)),
extended_filter_enabled_ ? 1 : 0); extended_filter_enabled_ ? 1 : 0);
@ -452,11 +490,13 @@ int EchoCancellationImpl::ConfigureHandle(void* handle) const {
} }
int EchoCancellationImpl::num_handles_required() const { int EchoCancellationImpl::num_handles_required() const {
// Not locked as it only relies on APM public API which is threadsafe.
return apm_->num_output_channels() * return apm_->num_output_channels() *
apm_->num_reverse_channels(); apm_->num_reverse_channels();
} }
int EchoCancellationImpl::GetHandleError(void* handle) const { int EchoCancellationImpl::GetHandleError(void* handle) const {
// Not locked as it does not rely on anything in the state.
assert(handle != NULL); assert(handle != NULL);
return AudioProcessing::kUnspecifiedError; return AudioProcessing::kUnspecifiedError;
} }

View File

@ -11,6 +11,7 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/swap_queue.h" #include "webrtc/common_audio/swap_queue.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
@ -19,13 +20,13 @@
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class EchoCancellationImpl : public EchoCancellation, class EchoCancellationImpl : public EchoCancellation,
public ProcessingComponent { public ProcessingComponent {
public: public:
EchoCancellationImpl(const AudioProcessing* apm, EchoCancellationImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit); rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture);
virtual ~EchoCancellationImpl(); virtual ~EchoCancellationImpl();
int ProcessRenderAudio(const AudioBuffer* audio); int ProcessRenderAudio(const AudioBuffer* audio);
@ -40,11 +41,11 @@ class EchoCancellationImpl : public EchoCancellation,
// ProcessingComponent implementation. // ProcessingComponent implementation.
int Initialize() override; int Initialize() override;
void SetExtraOptions(const Config& config) override; void SetExtraOptions(const Config& config) override;
bool is_delay_agnostic_enabled() const; bool is_delay_agnostic_enabled() const;
bool is_extended_filter_enabled() const; bool is_extended_filter_enabled() const;
// Reads render side data that has been queued on the render call. // Reads render side data that has been queued on the render call.
// Called holding the capture lock.
void ReadQueuedRenderData(); void ReadQueuedRenderData();
private: private:
@ -63,6 +64,7 @@ class EchoCancellationImpl : public EchoCancellation,
int GetDelayMetrics(int* median, int GetDelayMetrics(int* median,
int* std, int* std,
float* fraction_poor_delays) override; float* fraction_poor_delays) override;
struct AecCore* aec_core() const override; struct AecCore* aec_core() const override;
// ProcessingComponent implementation. // ProcessingComponent implementation.
@ -75,22 +77,28 @@ class EchoCancellationImpl : public EchoCancellation,
void AllocateRenderQueue(); void AllocateRenderQueue();
// Not guarded as its public API is thread safe.
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
bool drift_compensation_enabled_; rtc::CriticalSection* const crit_render_ ACQUIRED_BEFORE(crit_capture_);
bool metrics_enabled_; rtc::CriticalSection* const crit_capture_;
SuppressionLevel suppression_level_;
int stream_drift_samples_;
bool was_stream_drift_set_;
bool stream_has_echo_;
bool delay_logging_enabled_;
bool extended_filter_enabled_;
bool delay_agnostic_enabled_;
size_t render_queue_element_max_size_; bool drift_compensation_enabled_ GUARDED_BY(crit_capture_);
std::vector<float> render_queue_buffer_; bool metrics_enabled_ GUARDED_BY(crit_capture_);
std::vector<float> capture_queue_buffer_; SuppressionLevel suppression_level_ GUARDED_BY(crit_capture_);
int stream_drift_samples_ GUARDED_BY(crit_capture_);
bool was_stream_drift_set_ GUARDED_BY(crit_capture_);
bool stream_has_echo_ GUARDED_BY(crit_capture_);
bool delay_logging_enabled_ GUARDED_BY(crit_capture_);
bool extended_filter_enabled_ GUARDED_BY(crit_capture_);
bool delay_agnostic_enabled_ GUARDED_BY(crit_capture_);
size_t render_queue_element_max_size_ GUARDED_BY(crit_render_)
GUARDED_BY(crit_capture_);
std::vector<float> render_queue_buffer_ GUARDED_BY(crit_render_);
std::vector<float> capture_queue_buffer_ GUARDED_BY(crit_capture_);
// Lock protection not needed.
rtc::scoped_ptr<SwapQueue<std::vector<float>, RenderQueueItemVerifier<float>>> rtc::scoped_ptr<SwapQueue<std::vector<float>, RenderQueueItemVerifier<float>>>
render_signal_queue_; render_signal_queue_;
}; };

View File

@ -15,7 +15,6 @@
#include "webrtc/modules/audio_processing/aecm/echo_control_mobile.h" #include "webrtc/modules/audio_processing/aecm/echo_control_mobile.h"
#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
#include "webrtc/system_wrappers/include/logging.h" #include "webrtc/system_wrappers/include/logging.h"
namespace webrtc { namespace webrtc {
@ -69,14 +68,20 @@ size_t EchoControlMobile::echo_path_size_bytes() {
} }
EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture)
: ProcessingComponent(), : ProcessingComponent(),
apm_(apm), apm_(apm),
crit_(crit), crit_render_(crit_render),
crit_capture_(crit_capture),
routing_mode_(kSpeakerphone), routing_mode_(kSpeakerphone),
comfort_noise_enabled_(true), comfort_noise_enabled_(true),
external_echo_path_(NULL), external_echo_path_(NULL),
render_queue_element_max_size_(0) {} render_queue_element_max_size_(0) {
RTC_DCHECK(apm);
RTC_DCHECK(crit_render);
RTC_DCHECK(crit_capture);
}
EchoControlMobileImpl::~EchoControlMobileImpl() { EchoControlMobileImpl::~EchoControlMobileImpl() {
if (external_echo_path_ != NULL) { if (external_echo_path_ != NULL) {
@ -86,15 +91,16 @@ EchoControlMobileImpl::~EchoControlMobileImpl() {
} }
int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
rtc::CritScope cs_render(crit_render_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == apm_->num_reverse_channels()); assert(audio->num_channels() == apm_->num_reverse_channels());
int err = apm_->kNoError; int err = AudioProcessing::kNoError;
// The ordering convention must be followed to pass to the correct AECM. // The ordering convention must be followed to pass to the correct AECM.
size_t handle_index = 0; size_t handle_index = 0;
render_queue_buffer_.clear(); render_queue_buffer_.clear();
@ -105,7 +111,7 @@ int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
my_handle, audio->split_bands_const(j)[kBand0To8kHz], my_handle, audio->split_bands_const(j)[kBand0To8kHz],
audio->num_frames_per_band()); audio->num_frames_per_band());
if (err != apm_->kNoError) if (err != AudioProcessing::kNoError)
return MapError(err); // TODO(ajm): warning possible?); return MapError(err); // TODO(ajm): warning possible?);
// Buffer the samples in the render queue. // Buffer the samples in the render queue.
@ -120,18 +126,21 @@ int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
// Insert the samples into the queue. // Insert the samples into the queue.
if (!render_signal_queue_->Insert(&render_queue_buffer_)) { if (!render_signal_queue_->Insert(&render_queue_buffer_)) {
// The data queue is full and needs to be emptied.
ReadQueuedRenderData(); ReadQueuedRenderData();
// Retry the insert (should always work). // Retry the insert (should always work).
RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true);
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
// Read chunks of data that were received and queued on the render side from // Read chunks of data that were received and queued on the render side from
// a queue. All the data chunks are buffered into the farend signal of the AEC. // a queue. All the data chunks are buffered into the farend signal of the AEC.
void EchoControlMobileImpl::ReadQueuedRenderData() { void EchoControlMobileImpl::ReadQueuedRenderData() {
rtc::CritScope cs_capture(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return; return;
} }
@ -156,18 +165,20 @@ void EchoControlMobileImpl::ReadQueuedRenderData() {
} }
int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs_capture(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
if (!apm_->was_stream_delay_set()) { if (!apm_->was_stream_delay_set()) {
return apm_->kStreamParameterNotSetError; return AudioProcessing::kStreamParameterNotSetError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == apm_->num_output_channels()); assert(audio->num_channels() == apm_->num_output_channels());
int err = apm_->kNoError; int err = AudioProcessing::kNoError;
// The ordering convention must be followed to pass to the correct AECM. // The ordering convention must be followed to pass to the correct AECM.
size_t handle_index = 0; size_t handle_index = 0;
@ -190,86 +201,99 @@ int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) {
audio->num_frames_per_band(), audio->num_frames_per_band(),
apm_->stream_delay_ms()); apm_->stream_delay_ms());
if (err != apm_->kNoError) if (err != AudioProcessing::kNoError)
return MapError(err); return MapError(err);
handle_index++; handle_index++;
} }
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int EchoControlMobileImpl::Enable(bool enable) { int EchoControlMobileImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_);
// Ensure AEC and AECM are not both enabled. // Ensure AEC and AECM are not both enabled.
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
// The is_enabled call is safe from a deadlock perspective
// as both locks are allready held in the correct order.
if (enable && apm_->echo_cancellation()->is_enabled()) { if (enable && apm_->echo_cancellation()->is_enabled()) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
return EnableComponent(enable); return EnableComponent(enable);
} }
bool EchoControlMobileImpl::is_enabled() const { bool EchoControlMobileImpl::is_enabled() const {
rtc::CritScope cs(crit_capture_);
return is_component_enabled(); return is_component_enabled();
} }
int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) {
CriticalSectionScoped crit_scoped(crit_);
if (MapSetting(mode) == -1) { if (MapSetting(mode) == -1) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
{
rtc::CritScope cs(crit_capture_);
routing_mode_ = mode; routing_mode_ = mode;
}
return Configure(); return Configure();
} }
EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode()
const { const {
rtc::CritScope cs(crit_capture_);
return routing_mode_; return routing_mode_;
} }
int EchoControlMobileImpl::enable_comfort_noise(bool enable) { int EchoControlMobileImpl::enable_comfort_noise(bool enable) {
CriticalSectionScoped crit_scoped(crit_); {
rtc::CritScope cs(crit_capture_);
comfort_noise_enabled_ = enable; comfort_noise_enabled_ = enable;
}
return Configure(); return Configure();
} }
bool EchoControlMobileImpl::is_comfort_noise_enabled() const { bool EchoControlMobileImpl::is_comfort_noise_enabled() const {
rtc::CritScope cs(crit_capture_);
return comfort_noise_enabled_; return comfort_noise_enabled_;
} }
int EchoControlMobileImpl::SetEchoPath(const void* echo_path, int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
size_t size_bytes) { size_t size_bytes) {
CriticalSectionScoped crit_scoped(crit_); {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
if (echo_path == NULL) { if (echo_path == NULL) {
return apm_->kNullPointerError; return AudioProcessing::kNullPointerError;
} }
if (size_bytes != echo_path_size_bytes()) { if (size_bytes != echo_path_size_bytes()) {
// Size mismatch // Size mismatch
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
if (external_echo_path_ == NULL) { if (external_echo_path_ == NULL) {
external_echo_path_ = new unsigned char[size_bytes]; external_echo_path_ = new unsigned char[size_bytes];
} }
memcpy(external_echo_path_, echo_path, size_bytes); memcpy(external_echo_path_, echo_path, size_bytes);
}
return Initialize(); return Initialize();
} }
int EchoControlMobileImpl::GetEchoPath(void* echo_path, int EchoControlMobileImpl::GetEchoPath(void* echo_path,
size_t size_bytes) const { size_t size_bytes) const {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (echo_path == NULL) { if (echo_path == NULL) {
return apm_->kNullPointerError; return AudioProcessing::kNullPointerError;
} }
if (size_bytes != echo_path_size_bytes()) { if (size_bytes != echo_path_size_bytes()) {
// Size mismatch // Size mismatch
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNotEnabledError; return AudioProcessing::kNotEnabledError;
} }
// Get the echo path from the first channel // Get the echo path from the first channel
@ -278,27 +302,30 @@ int EchoControlMobileImpl::GetEchoPath(void* echo_path,
if (err != 0) if (err != 0)
return MapError(err); return MapError(err);
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int EchoControlMobileImpl::Initialize() { int EchoControlMobileImpl::Initialize() {
{
rtc::CritScope cs_capture(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
}
} }
if (apm_->proc_sample_rate_hz() > apm_->kSampleRate16kHz) { if (apm_->proc_sample_rate_hz() > AudioProcessing::kSampleRate16kHz) {
LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates"; LOG(LS_ERROR) << "AECM only supports 16 kHz or lower sample rates";
return apm_->kBadSampleRateError; return AudioProcessing::kBadSampleRateError;
} }
int err = ProcessingComponent::Initialize(); int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return err; return err;
} }
AllocateRenderQueue(); AllocateRenderQueue();
return apm_->kNoError; return AudioProcessing::kNoError;
} }
void EchoControlMobileImpl::AllocateRenderQueue() { void EchoControlMobileImpl::AllocateRenderQueue() {
@ -306,6 +333,9 @@ void EchoControlMobileImpl::AllocateRenderQueue() {
static_cast<size_t>(1), static_cast<size_t>(1),
kMaxAllowedValuesOfSamplesPerFrame * num_handles_required()); kMaxAllowedValuesOfSamplesPerFrame * num_handles_required());
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
// Reallocate the queue if the queue item size is too small to fit the // Reallocate the queue if the queue item size is too small to fit the
// data to put in the queue. // data to put in the queue.
if (render_queue_element_max_size_ < new_render_queue_element_max_size) { if (render_queue_element_max_size_ < new_render_queue_element_max_size) {
@ -330,10 +360,14 @@ void* EchoControlMobileImpl::CreateHandle() const {
} }
void EchoControlMobileImpl::DestroyHandle(void* handle) const { void EchoControlMobileImpl::DestroyHandle(void* handle) const {
// This method is only called in a non-concurrent manner during APM
// destruction.
WebRtcAecm_Free(static_cast<Handle*>(handle)); WebRtcAecm_Free(static_cast<Handle*>(handle));
} }
int EchoControlMobileImpl::InitializeHandle(void* handle) const { int EchoControlMobileImpl::InitializeHandle(void* handle) const {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
assert(handle != NULL); assert(handle != NULL);
Handle* my_handle = static_cast<Handle*>(handle); Handle* my_handle = static_cast<Handle*>(handle);
if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) {
@ -347,10 +381,12 @@ int EchoControlMobileImpl::InitializeHandle(void* handle) const {
} }
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int EchoControlMobileImpl::ConfigureHandle(void* handle) const { int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
AecmConfig config; AecmConfig config;
config.cngMode = comfort_noise_enabled_; config.cngMode = comfort_noise_enabled_;
config.echoMode = MapSetting(routing_mode_); config.echoMode = MapSetting(routing_mode_);
@ -359,11 +395,13 @@ int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
} }
int EchoControlMobileImpl::num_handles_required() const { int EchoControlMobileImpl::num_handles_required() const {
// Not locked as it only relies on APM public API which is threadsafe.
return apm_->num_output_channels() * return apm_->num_output_channels() *
apm_->num_reverse_channels(); apm_->num_reverse_channels();
} }
int EchoControlMobileImpl::GetHandleError(void* handle) const { int EchoControlMobileImpl::GetHandleError(void* handle) const {
// Not locked as it does not rely on anything in the state.
assert(handle != NULL); assert(handle != NULL);
return AudioProcessing::kUnspecifiedError; return AudioProcessing::kUnspecifiedError;
} }

View File

@ -11,6 +11,7 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CONTROL_MOBILE_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/swap_queue.h" #include "webrtc/common_audio/swap_queue.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
@ -19,13 +20,14 @@
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class EchoControlMobileImpl : public EchoControlMobile, class EchoControlMobileImpl : public EchoControlMobile,
public ProcessingComponent { public ProcessingComponent {
public: public:
EchoControlMobileImpl(const AudioProcessing* apm, EchoControlMobileImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit); rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture);
virtual ~EchoControlMobileImpl(); virtual ~EchoControlMobileImpl();
int ProcessRenderAudio(const AudioBuffer* audio); int ProcessRenderAudio(const AudioBuffer* audio);
@ -51,6 +53,7 @@ class EchoControlMobileImpl : public EchoControlMobile,
int GetEchoPath(void* echo_path, size_t size_bytes) const override; int GetEchoPath(void* echo_path, size_t size_bytes) const override;
// ProcessingComponent implementation. // ProcessingComponent implementation.
// Called holding both the render and capture locks.
void* CreateHandle() const override; void* CreateHandle() const override;
int InitializeHandle(void* handle) const override; int InitializeHandle(void* handle) const override;
int ConfigureHandle(void* handle) const override; int ConfigureHandle(void* handle) const override;
@ -60,15 +63,24 @@ class EchoControlMobileImpl : public EchoControlMobile,
void AllocateRenderQueue(); void AllocateRenderQueue();
// Not guarded as its public API is thread safe.
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
RoutingMode routing_mode_;
bool comfort_noise_enabled_;
unsigned char* external_echo_path_;
size_t render_queue_element_max_size_; rtc::CriticalSection* const crit_render_ ACQUIRED_BEFORE(crit_capture_);
std::vector<int16_t> render_queue_buffer_; rtc::CriticalSection* const crit_capture_;
std::vector<int16_t> capture_queue_buffer_;
RoutingMode routing_mode_ GUARDED_BY(crit_capture_);
bool comfort_noise_enabled_ GUARDED_BY(crit_capture_);
unsigned char* external_echo_path_ GUARDED_BY(crit_render_)
GUARDED_BY(crit_capture_);
size_t render_queue_element_max_size_ GUARDED_BY(crit_render_)
GUARDED_BY(crit_capture_);
std::vector<int16_t> render_queue_buffer_ GUARDED_BY(crit_render_);
std::vector<int16_t> capture_queue_buffer_ GUARDED_BY(crit_capture_);
// Lock protection not needed.
rtc::scoped_ptr< rtc::scoped_ptr<
SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>> SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>>
render_signal_queue_; render_signal_queue_;

View File

@ -14,7 +14,6 @@
#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h" #include "webrtc/modules/audio_processing/agc/legacy/gain_control.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
namespace webrtc { namespace webrtc {
@ -44,10 +43,12 @@ static const size_t kMaxNumFramesToBuffer = 100;
} // namespace } // namespace
GainControlImpl::GainControlImpl(const AudioProcessing* apm, GainControlImpl::GainControlImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture)
: ProcessingComponent(), : ProcessingComponent(),
apm_(apm), apm_(apm),
crit_(crit), crit_render_(crit_render),
crit_capture_(crit_capture),
mode_(kAdaptiveAnalog), mode_(kAdaptiveAnalog),
minimum_capture_level_(0), minimum_capture_level_(0),
maximum_capture_level_(255), maximum_capture_level_(255),
@ -57,13 +58,18 @@ GainControlImpl::GainControlImpl(const AudioProcessing* apm,
analog_capture_level_(0), analog_capture_level_(0),
was_analog_level_set_(false), was_analog_level_set_(false),
stream_is_saturated_(false), stream_is_saturated_(false),
render_queue_element_max_size_(0) {} render_queue_element_max_size_(0) {
RTC_DCHECK(apm);
RTC_DCHECK(crit_render);
RTC_DCHECK(crit_capture);
}
GainControlImpl::~GainControlImpl() {} GainControlImpl::~GainControlImpl() {}
int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) { int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
rtc::CritScope cs(crit_render_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
@ -74,7 +80,7 @@ int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
int err = int err =
WebRtcAgc_GetAddFarendError(my_handle, audio->num_frames_per_band()); WebRtcAgc_GetAddFarendError(my_handle, audio->num_frames_per_band());
if (err != apm_->kNoError) if (err != AudioProcessing::kNoError)
return GetHandleError(my_handle); return GetHandleError(my_handle);
// Buffer the samples in the render queue. // Buffer the samples in the render queue.
@ -85,18 +91,21 @@ int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) {
// Insert the samples into the queue. // Insert the samples into the queue.
if (!render_signal_queue_->Insert(&render_queue_buffer_)) { if (!render_signal_queue_->Insert(&render_queue_buffer_)) {
// The data queue is full and needs to be emptied.
ReadQueuedRenderData(); ReadQueuedRenderData();
// Retry the insert (should always work). // Retry the insert (should always work).
RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true);
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
// Read chunks of data that were received and queued on the render side from // Read chunks of data that were received and queued on the render side from
// a queue. All the data chunks are buffered into the farend signal of the AGC. // a queue. All the data chunks are buffered into the farend signal of the AGC.
void GainControlImpl::ReadQueuedRenderData() { void GainControlImpl::ReadQueuedRenderData() {
rtc::CritScope cs(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return; return;
} }
@ -116,14 +125,16 @@ void GainControlImpl::ReadQueuedRenderData() {
} }
int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == num_handles()); assert(audio->num_channels() == num_handles());
int err = apm_->kNoError; int err = AudioProcessing::kNoError;
if (mode_ == kAdaptiveAnalog) { if (mode_ == kAdaptiveAnalog) {
capture_levels_.assign(num_handles(), analog_capture_level_); capture_levels_.assign(num_handles(), analog_capture_level_);
@ -135,7 +146,7 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
audio->num_bands(), audio->num_bands(),
audio->num_frames_per_band()); audio->num_frames_per_band());
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return GetHandleError(my_handle); return GetHandleError(my_handle);
} }
} }
@ -155,23 +166,25 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
capture_levels_[i] = capture_level_out; capture_levels_[i] = capture_level_out;
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return GetHandleError(my_handle); return GetHandleError(my_handle);
} }
} }
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs(crit_capture_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) {
return apm_->kStreamParameterNotSetError; return AudioProcessing::kStreamParameterNotSetError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
@ -183,6 +196,8 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
int32_t capture_level_out = 0; int32_t capture_level_out = 0;
uint8_t saturation_warning = 0; uint8_t saturation_warning = 0;
// The call to stream_has_echo() is ok from a deadlock perspective
// as the capture lock is allready held.
int err = WebRtcAgc_Process( int err = WebRtcAgc_Process(
my_handle, my_handle,
audio->split_bands_const(i), audio->split_bands_const(i),
@ -194,7 +209,7 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
apm_->echo_cancellation()->stream_has_echo(), apm_->echo_cancellation()->stream_has_echo(),
&saturation_warning); &saturation_warning);
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return GetHandleError(my_handle); return GetHandleError(my_handle);
} }
@ -215,22 +230,24 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) {
} }
was_analog_level_set_ = false; was_analog_level_set_ = false;
return apm_->kNoError; return AudioProcessing::kNoError;
} }
// TODO(ajm): ensure this is called under kAdaptiveAnalog. // TODO(ajm): ensure this is called under kAdaptiveAnalog.
int GainControlImpl::set_stream_analog_level(int level) { int GainControlImpl::set_stream_analog_level(int level) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
was_analog_level_set_ = true; was_analog_level_set_ = true;
if (level < minimum_capture_level_ || level > maximum_capture_level_) { if (level < minimum_capture_level_ || level > maximum_capture_level_) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
analog_capture_level_ = level; analog_capture_level_ = level;
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int GainControlImpl::stream_analog_level() { int GainControlImpl::stream_analog_level() {
rtc::CritScope cs(crit_capture_);
// TODO(ajm): enable this assertion? // TODO(ajm): enable this assertion?
//assert(mode_ == kAdaptiveAnalog); //assert(mode_ == kAdaptiveAnalog);
@ -238,18 +255,21 @@ int GainControlImpl::stream_analog_level() {
} }
int GainControlImpl::Enable(bool enable) { int GainControlImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
return EnableComponent(enable); return EnableComponent(enable);
} }
bool GainControlImpl::is_enabled() const { bool GainControlImpl::is_enabled() const {
rtc::CritScope cs(crit_capture_);
return is_component_enabled(); return is_component_enabled();
} }
int GainControlImpl::set_mode(Mode mode) { int GainControlImpl::set_mode(Mode mode) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
if (MapSetting(mode) == -1) { if (MapSetting(mode) == -1) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
mode_ = mode; mode_ = mode;
@ -257,22 +277,23 @@ int GainControlImpl::set_mode(Mode mode) {
} }
GainControl::Mode GainControlImpl::mode() const { GainControl::Mode GainControlImpl::mode() const {
rtc::CritScope cs(crit_capture_);
return mode_; return mode_;
} }
int GainControlImpl::set_analog_level_limits(int minimum, int GainControlImpl::set_analog_level_limits(int minimum,
int maximum) { int maximum) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (minimum < 0) { if (minimum < 0) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
if (maximum > 65535) { if (maximum > 65535) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
if (maximum < minimum) { if (maximum < minimum) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
minimum_capture_level_ = minimum; minimum_capture_level_ = minimum;
@ -282,21 +303,24 @@ int GainControlImpl::set_analog_level_limits(int minimum,
} }
int GainControlImpl::analog_level_minimum() const { int GainControlImpl::analog_level_minimum() const {
rtc::CritScope cs(crit_capture_);
return minimum_capture_level_; return minimum_capture_level_;
} }
int GainControlImpl::analog_level_maximum() const { int GainControlImpl::analog_level_maximum() const {
rtc::CritScope cs(crit_capture_);
return maximum_capture_level_; return maximum_capture_level_;
} }
bool GainControlImpl::stream_is_saturated() const { bool GainControlImpl::stream_is_saturated() const {
rtc::CritScope cs(crit_capture_);
return stream_is_saturated_; return stream_is_saturated_;
} }
int GainControlImpl::set_target_level_dbfs(int level) { int GainControlImpl::set_target_level_dbfs(int level) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (level > 31 || level < 0) { if (level > 31 || level < 0) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
target_level_dbfs_ = level; target_level_dbfs_ = level;
@ -304,13 +328,14 @@ int GainControlImpl::set_target_level_dbfs(int level) {
} }
int GainControlImpl::target_level_dbfs() const { int GainControlImpl::target_level_dbfs() const {
rtc::CritScope cs(crit_capture_);
return target_level_dbfs_; return target_level_dbfs_;
} }
int GainControlImpl::set_compression_gain_db(int gain) { int GainControlImpl::set_compression_gain_db(int gain) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
if (gain < 0 || gain > 90) { if (gain < 0 || gain > 90) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
compression_gain_db_ = gain; compression_gain_db_ = gain;
@ -318,31 +343,35 @@ int GainControlImpl::set_compression_gain_db(int gain) {
} }
int GainControlImpl::compression_gain_db() const { int GainControlImpl::compression_gain_db() const {
rtc::CritScope cs(crit_capture_);
return compression_gain_db_; return compression_gain_db_;
} }
int GainControlImpl::enable_limiter(bool enable) { int GainControlImpl::enable_limiter(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_capture_);
limiter_enabled_ = enable; limiter_enabled_ = enable;
return Configure(); return Configure();
} }
bool GainControlImpl::is_limiter_enabled() const { bool GainControlImpl::is_limiter_enabled() const {
rtc::CritScope cs(crit_capture_);
return limiter_enabled_; return limiter_enabled_;
} }
int GainControlImpl::Initialize() { int GainControlImpl::Initialize() {
int err = ProcessingComponent::Initialize(); int err = ProcessingComponent::Initialize();
if (err != apm_->kNoError || !is_component_enabled()) { if (err != AudioProcessing::kNoError || !is_component_enabled()) {
return err; return err;
} }
AllocateRenderQueue(); AllocateRenderQueue();
rtc::CritScope cs_capture(crit_capture_);
const int n = num_handles(); const int n = num_handles();
RTC_CHECK_GE(n, 0) << "Bad number of handles: " << n; RTC_CHECK_GE(n, 0) << "Bad number of handles: " << n;
capture_levels_.assign(n, analog_capture_level_); capture_levels_.assign(n, analog_capture_level_);
return apm_->kNoError; return AudioProcessing::kNoError;
} }
void GainControlImpl::AllocateRenderQueue() { void GainControlImpl::AllocateRenderQueue() {
@ -350,6 +379,9 @@ void GainControlImpl::AllocateRenderQueue() {
std::max<size_t>(static_cast<size_t>(1), std::max<size_t>(static_cast<size_t>(1),
kMaxAllowedValuesOfSamplesPerFrame * num_handles()); kMaxAllowedValuesOfSamplesPerFrame * num_handles());
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
if (render_queue_element_max_size_ < new_render_queue_element_max_size) { if (render_queue_element_max_size_ < new_render_queue_element_max_size) {
render_queue_element_max_size_ = new_render_queue_element_max_size; render_queue_element_max_size_ = new_render_queue_element_max_size;
std::vector<int16_t> template_queue_element(render_queue_element_max_size_); std::vector<int16_t> template_queue_element(render_queue_element_max_size_);
@ -375,6 +407,9 @@ void GainControlImpl::DestroyHandle(void* handle) const {
} }
int GainControlImpl::InitializeHandle(void* handle) const { int GainControlImpl::InitializeHandle(void* handle) const {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
return WebRtcAgc_Init(static_cast<Handle*>(handle), return WebRtcAgc_Init(static_cast<Handle*>(handle),
minimum_capture_level_, minimum_capture_level_,
maximum_capture_level_, maximum_capture_level_,
@ -383,6 +418,8 @@ int GainControlImpl::InitializeHandle(void* handle) const {
} }
int GainControlImpl::ConfigureHandle(void* handle) const { int GainControlImpl::ConfigureHandle(void* handle) const {
rtc::CritScope cs_render(crit_render_);
rtc::CritScope cs_capture(crit_capture_);
WebRtcAgcConfig config; WebRtcAgcConfig config;
// TODO(ajm): Flip the sign here (since AGC expects a positive value) if we // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we
// change the interface. // change the interface.
@ -397,6 +434,7 @@ int GainControlImpl::ConfigureHandle(void* handle) const {
} }
int GainControlImpl::num_handles_required() const { int GainControlImpl::num_handles_required() const {
// Not locked as it only relies on APM public API which is threadsafe.
return apm_->num_output_channels(); return apm_->num_output_channels();
} }
@ -404,6 +442,6 @@ int GainControlImpl::GetHandleError(void* handle) const {
// The AGC has no get_error() function. // The AGC has no get_error() function.
// (Despite listing errors in its interface...) // (Despite listing errors in its interface...)
assert(handle != NULL); assert(handle != NULL);
return apm_->kUnspecifiedError; return AudioProcessing::kUnspecifiedError;
} }
} // namespace webrtc } // namespace webrtc

View File

@ -13,7 +13,9 @@
#include <vector> #include <vector>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/common_audio/swap_queue.h" #include "webrtc/common_audio/swap_queue.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/processing_component.h" #include "webrtc/modules/audio_processing/processing_component.h"
@ -21,13 +23,13 @@
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class GainControlImpl : public GainControl, class GainControlImpl : public GainControl,
public ProcessingComponent { public ProcessingComponent {
public: public:
GainControlImpl(const AudioProcessing* apm, GainControlImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit); rtc::CriticalSection* crit_render,
rtc::CriticalSection* crit_capture);
virtual ~GainControlImpl(); virtual ~GainControlImpl();
int ProcessRenderAudio(AudioBuffer* audio); int ProcessRenderAudio(AudioBuffer* audio);
@ -71,22 +73,29 @@ class GainControlImpl : public GainControl,
void AllocateRenderQueue(); void AllocateRenderQueue();
// Not guarded as its public API is thread safe.
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
Mode mode_;
int minimum_capture_level_;
int maximum_capture_level_;
bool limiter_enabled_;
int target_level_dbfs_;
int compression_gain_db_;
std::vector<int> capture_levels_;
int analog_capture_level_;
bool was_analog_level_set_;
bool stream_is_saturated_;
size_t render_queue_element_max_size_; rtc::CriticalSection* const crit_render_ ACQUIRED_BEFORE(crit_capture_);
std::vector<int16_t> render_queue_buffer_; rtc::CriticalSection* const crit_capture_;
std::vector<int16_t> capture_queue_buffer_;
Mode mode_ GUARDED_BY(crit_capture_);
int minimum_capture_level_ GUARDED_BY(crit_capture_);
int maximum_capture_level_ GUARDED_BY(crit_capture_);
bool limiter_enabled_ GUARDED_BY(crit_capture_);
int target_level_dbfs_ GUARDED_BY(crit_capture_);
int compression_gain_db_ GUARDED_BY(crit_capture_);
std::vector<int> capture_levels_ GUARDED_BY(crit_capture_);
int analog_capture_level_ GUARDED_BY(crit_capture_);
bool was_analog_level_set_ GUARDED_BY(crit_capture_);
bool stream_is_saturated_ GUARDED_BY(crit_capture_);
size_t render_queue_element_max_size_ GUARDED_BY(crit_render_)
GUARDED_BY(crit_capture_);
std::vector<int16_t> render_queue_buffer_ GUARDED_BY(crit_render_);
std::vector<int16_t> capture_queue_buffer_ GUARDED_BY(crit_capture_);
// Lock protection not needed.
rtc::scoped_ptr< rtc::scoped_ptr<
SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>> SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>>
render_signal_queue_; render_signal_queue_;

View File

@ -100,18 +100,20 @@ int Filter(FilterState* hpf, int16_t* data, size_t length) {
typedef FilterState Handle; typedef FilterState Handle;
HighPassFilterImpl::HighPassFilterImpl(const AudioProcessing* apm, HighPassFilterImpl::HighPassFilterImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit)
: ProcessingComponent(), : ProcessingComponent(), apm_(apm), crit_(crit) {
apm_(apm), RTC_DCHECK(apm);
crit_(crit) {} RTC_DCHECK(crit);
}
HighPassFilterImpl::~HighPassFilterImpl() {} HighPassFilterImpl::~HighPassFilterImpl() {}
int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) { int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) {
int err = apm_->kNoError; rtc::CritScope cs(crit_);
int err = AudioProcessing::kNoError;
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
@ -122,20 +124,21 @@ int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) {
audio->split_bands(i)[kBand0To8kHz], audio->split_bands(i)[kBand0To8kHz],
audio->num_frames_per_band()); audio->num_frames_per_band());
if (err != apm_->kNoError) { if (err != AudioProcessing::kNoError) {
return GetHandleError(my_handle); return GetHandleError(my_handle);
} }
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int HighPassFilterImpl::Enable(bool enable) { int HighPassFilterImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
return EnableComponent(enable); return EnableComponent(enable);
} }
bool HighPassFilterImpl::is_enabled() const { bool HighPassFilterImpl::is_enabled() const {
rtc::CritScope cs(crit_);
return is_component_enabled(); return is_component_enabled();
} }
@ -148,12 +151,15 @@ void HighPassFilterImpl::DestroyHandle(void* handle) const {
} }
int HighPassFilterImpl::InitializeHandle(void* handle) const { int HighPassFilterImpl::InitializeHandle(void* handle) const {
// TODO(peah): Remove dependency on apm for the
// capture side sample rate.
rtc::CritScope cs(crit_);
return InitializeFilter(static_cast<Handle*>(handle), return InitializeFilter(static_cast<Handle*>(handle),
apm_->proc_sample_rate_hz()); apm_->proc_sample_rate_hz());
} }
int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const { int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const {
return apm_->kNoError; // Not configurable. return AudioProcessing::kNoError; // Not configurable.
} }
int HighPassFilterImpl::num_handles_required() const { int HighPassFilterImpl::num_handles_required() const {
@ -163,6 +169,6 @@ int HighPassFilterImpl::num_handles_required() const {
int HighPassFilterImpl::GetHandleError(void* handle) const { int HighPassFilterImpl::GetHandleError(void* handle) const {
// The component has no detailed errors. // The component has no detailed errors.
assert(handle != NULL); assert(handle != NULL);
return apm_->kUnspecifiedError; return AudioProcessing::kUnspecifiedError;
} }
} // namespace webrtc } // namespace webrtc

View File

@ -11,18 +11,18 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_HIGH_PASS_FILTER_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/processing_component.h" #include "webrtc/modules/audio_processing/processing_component.h"
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class HighPassFilterImpl : public HighPassFilter, class HighPassFilterImpl : public HighPassFilter,
public ProcessingComponent { public ProcessingComponent {
public: public:
HighPassFilterImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); HighPassFilterImpl(const AudioProcessing* apm, rtc::CriticalSection* crit);
virtual ~HighPassFilterImpl(); virtual ~HighPassFilterImpl();
int ProcessCaptureAudio(AudioBuffer* audio); int ProcessCaptureAudio(AudioBuffer* audio);
@ -43,7 +43,8 @@ class HighPassFilterImpl : public HighPassFilter,
int GetHandleError(void* handle) const override; int GetHandleError(void* handle) const override;
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
rtc::CriticalSection* const crit_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -18,13 +18,17 @@
namespace webrtc { namespace webrtc {
LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessing* apm, LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit)
: ProcessingComponent(), : ProcessingComponent(), crit_(crit) {
crit_(crit) {} RTC_DCHECK(apm);
RTC_DCHECK(crit);
}
LevelEstimatorImpl::~LevelEstimatorImpl() {} LevelEstimatorImpl::~LevelEstimatorImpl() {}
int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) { int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) {
rtc::CritScope cs(crit_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return AudioProcessing::kNoError; return AudioProcessing::kNoError;
} }
@ -39,15 +43,17 @@ int LevelEstimatorImpl::ProcessStream(AudioBuffer* audio) {
} }
int LevelEstimatorImpl::Enable(bool enable) { int LevelEstimatorImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
return EnableComponent(enable); return EnableComponent(enable);
} }
bool LevelEstimatorImpl::is_enabled() const { bool LevelEstimatorImpl::is_enabled() const {
rtc::CritScope cs(crit_);
return is_component_enabled(); return is_component_enabled();
} }
int LevelEstimatorImpl::RMS() { int LevelEstimatorImpl::RMS() {
rtc::CritScope cs(crit_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return AudioProcessing::kNotEnabledError; return AudioProcessing::kNotEnabledError;
} }
@ -67,6 +73,7 @@ void LevelEstimatorImpl::DestroyHandle(void* handle) const {
} }
int LevelEstimatorImpl::InitializeHandle(void* handle) const { int LevelEstimatorImpl::InitializeHandle(void* handle) const {
rtc::CritScope cs(crit_);
static_cast<RMSLevel*>(handle)->Reset(); static_cast<RMSLevel*>(handle)->Reset();
return AudioProcessing::kNoError; return AudioProcessing::kNoError;
} }

View File

@ -11,6 +11,7 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_LEVEL_ESTIMATOR_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/processing_component.h" #include "webrtc/modules/audio_processing/processing_component.h"
#include "webrtc/modules/audio_processing/rms_level.h" #include "webrtc/modules/audio_processing/rms_level.h"
@ -18,13 +19,11 @@
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class LevelEstimatorImpl : public LevelEstimator, class LevelEstimatorImpl : public LevelEstimator,
public ProcessingComponent { public ProcessingComponent {
public: public:
LevelEstimatorImpl(const AudioProcessing* apm, LevelEstimatorImpl(const AudioProcessing* apm, rtc::CriticalSection* crit);
CriticalSectionWrapper* crit);
virtual ~LevelEstimatorImpl(); virtual ~LevelEstimatorImpl();
int ProcessStream(AudioBuffer* audio); int ProcessStream(AudioBuffer* audio);
@ -45,7 +44,7 @@ class LevelEstimatorImpl : public LevelEstimator,
int num_handles_required() const override; int num_handles_required() const override;
int GetHandleError(void* handle) const override; int GetHandleError(void* handle) const override;
CriticalSectionWrapper* crit_; rtc::CriticalSection* const crit_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -18,7 +18,6 @@
#elif defined(WEBRTC_NS_FIXED) #elif defined(WEBRTC_NS_FIXED)
#include "webrtc/modules/audio_processing/ns/noise_suppression_x.h" #include "webrtc/modules/audio_processing/ns/noise_suppression_x.h"
#endif #endif
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
namespace webrtc { namespace webrtc {
@ -47,18 +46,18 @@ int MapSetting(NoiseSuppression::Level level) {
} // namespace } // namespace
NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessing* apm, NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit)
: ProcessingComponent(), : ProcessingComponent(), apm_(apm), crit_(crit), level_(kModerate) {
apm_(apm), RTC_DCHECK(apm);
crit_(crit), RTC_DCHECK(crit);
level_(kModerate) {} }
NoiseSuppressionImpl::~NoiseSuppressionImpl() {} NoiseSuppressionImpl::~NoiseSuppressionImpl() {}
int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
#if defined(WEBRTC_NS_FLOAT) #if defined(WEBRTC_NS_FLOAT)
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == num_handles()); assert(audio->num_channels() == num_handles());
@ -69,12 +68,13 @@ int NoiseSuppressionImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
WebRtcNs_Analyze(my_handle, audio->split_bands_const_f(i)[kBand0To8kHz]); WebRtcNs_Analyze(my_handle, audio->split_bands_const_f(i)[kBand0To8kHz]);
} }
#endif #endif
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) { int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs(crit_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return AudioProcessing::kNoError;
} }
assert(audio->num_frames_per_band() <= 160); assert(audio->num_frames_per_band() <= 160);
assert(audio->num_channels() == num_handles()); assert(audio->num_channels() == num_handles());
@ -93,22 +93,23 @@ int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
audio->split_bands(i)); audio->split_bands(i));
#endif #endif
} }
return apm_->kNoError; return AudioProcessing::kNoError;
} }
int NoiseSuppressionImpl::Enable(bool enable) { int NoiseSuppressionImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
return EnableComponent(enable); return EnableComponent(enable);
} }
bool NoiseSuppressionImpl::is_enabled() const { bool NoiseSuppressionImpl::is_enabled() const {
rtc::CritScope cs(crit_);
return is_component_enabled(); return is_component_enabled();
} }
int NoiseSuppressionImpl::set_level(Level level) { int NoiseSuppressionImpl::set_level(Level level) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
if (MapSetting(level) == -1) { if (MapSetting(level) == -1) {
return apm_->kBadParameterError; return AudioProcessing::kBadParameterError;
} }
level_ = level; level_ = level;
@ -116,10 +117,12 @@ int NoiseSuppressionImpl::set_level(Level level) {
} }
NoiseSuppression::Level NoiseSuppressionImpl::level() const { NoiseSuppression::Level NoiseSuppressionImpl::level() const {
rtc::CritScope cs(crit_);
return level_; return level_;
} }
float NoiseSuppressionImpl::speech_probability() const { float NoiseSuppressionImpl::speech_probability() const {
rtc::CritScope cs(crit_);
#if defined(WEBRTC_NS_FLOAT) #if defined(WEBRTC_NS_FLOAT)
float probability_average = 0.0f; float probability_average = 0.0f;
for (int i = 0; i < num_handles(); i++) { for (int i = 0; i < num_handles(); i++) {
@ -129,7 +132,7 @@ float NoiseSuppressionImpl::speech_probability() const {
return probability_average / num_handles(); return probability_average / num_handles();
#elif defined(WEBRTC_NS_FIXED) #elif defined(WEBRTC_NS_FIXED)
// Currently not available for the fixed point implementation. // Currently not available for the fixed point implementation.
return apm_->kUnsupportedFunctionError; return AudioProcessing::kUnsupportedFunctionError;
#endif #endif
} }
@ -160,6 +163,7 @@ int NoiseSuppressionImpl::InitializeHandle(void* handle) const {
} }
int NoiseSuppressionImpl::ConfigureHandle(void* handle) const { int NoiseSuppressionImpl::ConfigureHandle(void* handle) const {
rtc::CritScope cs(crit_);
#if defined(WEBRTC_NS_FLOAT) #if defined(WEBRTC_NS_FLOAT)
return WebRtcNs_set_policy(static_cast<Handle*>(handle), return WebRtcNs_set_policy(static_cast<Handle*>(handle),
MapSetting(level_)); MapSetting(level_));
@ -176,6 +180,6 @@ int NoiseSuppressionImpl::num_handles_required() const {
int NoiseSuppressionImpl::GetHandleError(void* handle) const { int NoiseSuppressionImpl::GetHandleError(void* handle) const {
// The NS has no get_error() function. // The NS has no get_error() function.
assert(handle != NULL); assert(handle != NULL);
return apm_->kUnspecifiedError; return AudioProcessing::kUnspecifiedError;
} }
} // namespace webrtc } // namespace webrtc

View File

@ -11,19 +11,18 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/processing_component.h" #include "webrtc/modules/audio_processing/processing_component.h"
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class NoiseSuppressionImpl : public NoiseSuppression, class NoiseSuppressionImpl : public NoiseSuppression,
public ProcessingComponent { public ProcessingComponent {
public: public:
NoiseSuppressionImpl(const AudioProcessing* apm, NoiseSuppressionImpl(const AudioProcessing* apm, rtc::CriticalSection* crit);
CriticalSectionWrapper* crit);
virtual ~NoiseSuppressionImpl(); virtual ~NoiseSuppressionImpl();
int AnalyzeCaptureAudio(AudioBuffer* audio); int AnalyzeCaptureAudio(AudioBuffer* audio);
@ -47,9 +46,12 @@ class NoiseSuppressionImpl : public NoiseSuppression,
int num_handles_required() const override; int num_handles_required() const override;
int GetHandleError(void* handle) const override; int GetHandleError(void* handle) const override;
// Not guarded as its public API is thread safe.
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
Level level_; rtc::CriticalSection* const crit_;
Level level_ GUARDED_BY(crit_);
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -12,9 +12,10 @@
#include <assert.h> #include <assert.h>
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/common_audio/vad/include/webrtc_vad.h" #include "webrtc/common_audio/vad/include/webrtc_vad.h"
#include "webrtc/modules/audio_processing/audio_buffer.h" #include "webrtc/modules/audio_processing/audio_buffer.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
namespace webrtc { namespace webrtc {
@ -38,7 +39,7 @@ int MapSetting(VoiceDetection::Likelihood likelihood) {
} // namespace } // namespace
VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessing* apm, VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessing* apm,
CriticalSectionWrapper* crit) rtc::CriticalSection* crit)
: ProcessingComponent(), : ProcessingComponent(),
apm_(apm), apm_(apm),
crit_(crit), crit_(crit),
@ -46,11 +47,15 @@ VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessing* apm,
using_external_vad_(false), using_external_vad_(false),
likelihood_(kLowLikelihood), likelihood_(kLowLikelihood),
frame_size_ms_(10), frame_size_ms_(10),
frame_size_samples_(0) {} frame_size_samples_(0) {
RTC_DCHECK(apm);
RTC_DCHECK(crit);
}
VoiceDetectionImpl::~VoiceDetectionImpl() {} VoiceDetectionImpl::~VoiceDetectionImpl() {}
int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) { int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
rtc::CritScope cs(crit_);
if (!is_component_enabled()) { if (!is_component_enabled()) {
return apm_->kNoError; return apm_->kNoError;
} }
@ -81,28 +86,31 @@ int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) {
} }
int VoiceDetectionImpl::Enable(bool enable) { int VoiceDetectionImpl::Enable(bool enable) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
return EnableComponent(enable); return EnableComponent(enable);
} }
bool VoiceDetectionImpl::is_enabled() const { bool VoiceDetectionImpl::is_enabled() const {
rtc::CritScope cs(crit_);
return is_component_enabled(); return is_component_enabled();
} }
int VoiceDetectionImpl::set_stream_has_voice(bool has_voice) { int VoiceDetectionImpl::set_stream_has_voice(bool has_voice) {
rtc::CritScope cs(crit_);
using_external_vad_ = true; using_external_vad_ = true;
stream_has_voice_ = has_voice; stream_has_voice_ = has_voice;
return apm_->kNoError; return apm_->kNoError;
} }
bool VoiceDetectionImpl::stream_has_voice() const { bool VoiceDetectionImpl::stream_has_voice() const {
rtc::CritScope cs(crit_);
// TODO(ajm): enable this assertion? // TODO(ajm): enable this assertion?
//assert(using_external_vad_ || is_component_enabled()); //assert(using_external_vad_ || is_component_enabled());
return stream_has_voice_; return stream_has_voice_;
} }
int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) { int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
if (MapSetting(likelihood) == -1) { if (MapSetting(likelihood) == -1) {
return apm_->kBadParameterError; return apm_->kBadParameterError;
} }
@ -112,11 +120,12 @@ int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) {
} }
VoiceDetection::Likelihood VoiceDetectionImpl::likelihood() const { VoiceDetection::Likelihood VoiceDetectionImpl::likelihood() const {
rtc::CritScope cs(crit_);
return likelihood_; return likelihood_;
} }
int VoiceDetectionImpl::set_frame_size_ms(int size) { int VoiceDetectionImpl::set_frame_size_ms(int size) {
CriticalSectionScoped crit_scoped(crit_); rtc::CritScope cs(crit_);
assert(size == 10); // TODO(ajm): remove when supported. assert(size == 10); // TODO(ajm): remove when supported.
if (size != 10 && if (size != 10 &&
size != 20 && size != 20 &&
@ -130,11 +139,14 @@ int VoiceDetectionImpl::set_frame_size_ms(int size) {
} }
int VoiceDetectionImpl::frame_size_ms() const { int VoiceDetectionImpl::frame_size_ms() const {
rtc::CritScope cs(crit_);
return frame_size_ms_; return frame_size_ms_;
} }
int VoiceDetectionImpl::Initialize() { int VoiceDetectionImpl::Initialize() {
int err = ProcessingComponent::Initialize(); int err = ProcessingComponent::Initialize();
rtc::CritScope cs(crit_);
if (err != apm_->kNoError || !is_component_enabled()) { if (err != apm_->kNoError || !is_component_enabled()) {
return err; return err;
} }
@ -160,6 +172,7 @@ int VoiceDetectionImpl::InitializeHandle(void* handle) const {
} }
int VoiceDetectionImpl::ConfigureHandle(void* handle) const { int VoiceDetectionImpl::ConfigureHandle(void* handle) const {
rtc::CritScope cs(crit_);
return WebRtcVad_set_mode(static_cast<Handle*>(handle), return WebRtcVad_set_mode(static_cast<Handle*>(handle),
MapSetting(likelihood_)); MapSetting(likelihood_));
} }

View File

@ -11,18 +11,18 @@
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_ #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_ #define WEBRTC_MODULES_AUDIO_PROCESSING_VOICE_DETECTION_IMPL_H_
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/processing_component.h" #include "webrtc/modules/audio_processing/processing_component.h"
namespace webrtc { namespace webrtc {
class AudioBuffer; class AudioBuffer;
class CriticalSectionWrapper;
class VoiceDetectionImpl : public VoiceDetection, class VoiceDetectionImpl : public VoiceDetection,
public ProcessingComponent { public ProcessingComponent {
public: public:
VoiceDetectionImpl(const AudioProcessing* apm, CriticalSectionWrapper* crit); VoiceDetectionImpl(const AudioProcessing* apm, rtc::CriticalSection* crit);
virtual ~VoiceDetectionImpl(); virtual ~VoiceDetectionImpl();
int ProcessCaptureAudio(AudioBuffer* audio); int ProcessCaptureAudio(AudioBuffer* audio);
@ -51,13 +51,16 @@ class VoiceDetectionImpl : public VoiceDetection,
int num_handles_required() const override; int num_handles_required() const override;
int GetHandleError(void* handle) const override; int GetHandleError(void* handle) const override;
// Not guarded as its public API is thread safe.
const AudioProcessing* apm_; const AudioProcessing* apm_;
CriticalSectionWrapper* crit_;
bool stream_has_voice_; rtc::CriticalSection* const crit_;
bool using_external_vad_;
Likelihood likelihood_; bool stream_has_voice_ GUARDED_BY(crit_);
int frame_size_ms_; bool using_external_vad_ GUARDED_BY(crit_);
size_t frame_size_samples_; Likelihood likelihood_ GUARDED_BY(crit_);
int frame_size_ms_ GUARDED_BY(crit_);
size_t frame_size_samples_ GUARDED_BY(crit_);
}; };
} // namespace webrtc } // namespace webrtc