diff options
Diffstat (limited to 'webrtc/modules/audio_processing/echo_control_mobile_impl.cc')
-rw-r--r-- | webrtc/modules/audio_processing/echo_control_mobile_impl.cc | 245 |
1 files changed, 179 insertions, 66 deletions
diff --git a/webrtc/modules/audio_processing/echo_control_mobile_impl.cc b/webrtc/modules/audio_processing/echo_control_mobile_impl.cc index 954aac7d4a..f2df5f7984 100644 --- a/webrtc/modules/audio_processing/echo_control_mobile_impl.cc +++ b/webrtc/modules/audio_processing/echo_control_mobile_impl.cc @@ -13,9 +13,8 @@ #include <assert.h> #include <string.h> -#include "webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h" +#include "webrtc/modules/audio_processing/aecm/echo_control_mobile.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" namespace webrtc { @@ -56,6 +55,12 @@ AudioProcessing::Error MapError(int err) { return AudioProcessing::kUnspecifiedError; } } +// Maximum length that a frame of samples can have. +static const size_t kMaxAllowedValuesOfSamplesPerFrame = 160; +// Maximum number of frames to buffer in the render queue. +// TODO(peah): Decrease this once we properly handle hugely unbalanced +// reverse and forward call numbers. +static const size_t kMaxNumFramesToBuffer = 100; } // namespace size_t EchoControlMobile::echo_path_size_bytes() { @@ -63,13 +68,20 @@ size_t EchoControlMobile::echo_path_size_bytes() { } EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessing* apm, - CriticalSectionWrapper* crit) - : ProcessingComponent(), - apm_(apm), - crit_(crit), - routing_mode_(kSpeakerphone), - comfort_noise_enabled_(true), - external_echo_path_(NULL) {} + rtc::CriticalSection* crit_render, + rtc::CriticalSection* crit_capture) + : ProcessingComponent(), + apm_(apm), + crit_render_(crit_render), + crit_capture_(crit_capture), + routing_mode_(kSpeakerphone), + comfort_noise_enabled_(true), + external_echo_path_(NULL), + render_queue_element_max_size_(0) { + RTC_DCHECK(apm); + RTC_DCHECK(crit_render); + RTC_DCHECK(crit_capture); +} EchoControlMobileImpl::~EchoControlMobileImpl() { if (external_echo_path_ != NULL) { @@ -79,53 +91,98 @@ EchoControlMobileImpl::~EchoControlMobileImpl() { } int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { + rtc::CritScope cs_render(crit_render_); + if (!is_component_enabled()) { - return apm_->kNoError; + return AudioProcessing::kNoError; } assert(audio->num_frames_per_band() <= 160); 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. size_t handle_index = 0; - for (int i = 0; i < apm_->num_output_channels(); i++) { - for (int j = 0; j < audio->num_channels(); j++) { + render_queue_buffer_.clear(); + for (size_t i = 0; i < apm_->num_output_channels(); i++) { + for (size_t j = 0; j < audio->num_channels(); j++) { Handle* my_handle = static_cast<Handle*>(handle(handle_index)); - err = WebRtcAecm_BufferFarend( - my_handle, - audio->split_bands_const(j)[kBand0To8kHz], + err = WebRtcAecm_GetBufferFarendError( + my_handle, audio->split_bands_const(j)[kBand0To8kHz], audio->num_frames_per_band()); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } + if (err != AudioProcessing::kNoError) + return MapError(err); // TODO(ajm): warning possible?); + + // Buffer the samples in the render queue. + render_queue_buffer_.insert(render_queue_buffer_.end(), + audio->split_bands_const(j)[kBand0To8kHz], + (audio->split_bands_const(j)[kBand0To8kHz] + + audio->num_frames_per_band())); handle_index++; } } - return apm_->kNoError; + // Insert the samples into the queue. + if (!render_signal_queue_->Insert(&render_queue_buffer_)) { + // The data queue is full and needs to be emptied. + ReadQueuedRenderData(); + + // Retry the insert (should always work). + RTC_DCHECK_EQ(render_signal_queue_->Insert(&render_queue_buffer_), true); + } + + return AudioProcessing::kNoError; +} + +// 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. +void EchoControlMobileImpl::ReadQueuedRenderData() { + rtc::CritScope cs_capture(crit_capture_); + + if (!is_component_enabled()) { + return; + } + + while (render_signal_queue_->Remove(&capture_queue_buffer_)) { + size_t handle_index = 0; + size_t buffer_index = 0; + const size_t num_frames_per_band = + capture_queue_buffer_.size() / + (apm_->num_output_channels() * apm_->num_reverse_channels()); + for (size_t i = 0; i < apm_->num_output_channels(); i++) { + for (size_t j = 0; j < apm_->num_reverse_channels(); j++) { + Handle* my_handle = static_cast<Handle*>(handle(handle_index)); + WebRtcAecm_BufferFarend(my_handle, &capture_queue_buffer_[buffer_index], + num_frames_per_band); + + buffer_index += num_frames_per_band; + handle_index++; + } + } + } } int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { + rtc::CritScope cs_capture(crit_capture_); + if (!is_component_enabled()) { - return apm_->kNoError; + return AudioProcessing::kNoError; } if (!apm_->was_stream_delay_set()) { - return apm_->kStreamParameterNotSetError; + return AudioProcessing::kStreamParameterNotSetError; } assert(audio->num_frames_per_band() <= 160); 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. size_t handle_index = 0; - for (int i = 0; i < audio->num_channels(); i++) { + for (size_t i = 0; i < audio->num_channels(); i++) { // TODO(ajm): improve how this works, possibly inside AECM. // This is kind of hacked up. const int16_t* noisy = audio->low_pass_reference(i); @@ -134,7 +191,7 @@ int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { noisy = clean; clean = NULL; } - for (int j = 0; j < apm_->num_reverse_channels(); j++) { + for (size_t j = 0; j < apm_->num_reverse_channels(); j++) { Handle* my_handle = static_cast<Handle*>(handle(handle_index)); err = WebRtcAecm_Process( my_handle, @@ -144,109 +201,158 @@ int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { audio->num_frames_per_band(), apm_->stream_delay_ms()); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } + if (err != AudioProcessing::kNoError) + return MapError(err); handle_index++; } } - return apm_->kNoError; + return AudioProcessing::kNoError; } int EchoControlMobileImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(crit_); // 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()) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } return EnableComponent(enable); } bool EchoControlMobileImpl::is_enabled() const { + rtc::CritScope cs(crit_capture_); return is_component_enabled(); } int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { - CriticalSectionScoped crit_scoped(crit_); if (MapSetting(mode) == -1) { - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } - routing_mode_ = mode; + { + rtc::CritScope cs(crit_capture_); + routing_mode_ = mode; + } return Configure(); } EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() const { + rtc::CritScope cs(crit_capture_); return routing_mode_; } int EchoControlMobileImpl::enable_comfort_noise(bool enable) { - CriticalSectionScoped crit_scoped(crit_); - comfort_noise_enabled_ = enable; + { + rtc::CritScope cs(crit_capture_); + comfort_noise_enabled_ = enable; + } return Configure(); } bool EchoControlMobileImpl::is_comfort_noise_enabled() const { + rtc::CritScope cs(crit_capture_); return comfort_noise_enabled_; } int EchoControlMobileImpl::SetEchoPath(const void* echo_path, size_t size_bytes) { - CriticalSectionScoped crit_scoped(crit_); - if (echo_path == NULL) { - return apm_->kNullPointerError; - } - if (size_bytes != echo_path_size_bytes()) { - // Size mismatch - return apm_->kBadParameterError; - } + { + rtc::CritScope cs_render(crit_render_); + rtc::CritScope cs_capture(crit_capture_); + if (echo_path == NULL) { + return AudioProcessing::kNullPointerError; + } + if (size_bytes != echo_path_size_bytes()) { + // Size mismatch + return AudioProcessing::kBadParameterError; + } - if (external_echo_path_ == NULL) { - external_echo_path_ = new unsigned char[size_bytes]; + if (external_echo_path_ == NULL) { + 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(); } int EchoControlMobileImpl::GetEchoPath(void* echo_path, size_t size_bytes) const { - CriticalSectionScoped crit_scoped(crit_); + rtc::CritScope cs(crit_capture_); if (echo_path == NULL) { - return apm_->kNullPointerError; + return AudioProcessing::kNullPointerError; } if (size_bytes != echo_path_size_bytes()) { // Size mismatch - return apm_->kBadParameterError; + return AudioProcessing::kBadParameterError; } if (!is_component_enabled()) { - return apm_->kNotEnabledError; + return AudioProcessing::kNotEnabledError; } // Get the echo path from the first channel Handle* my_handle = static_cast<Handle*>(handle(0)); - if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) { - return GetHandleError(my_handle); - } + int32_t err = WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes); + if (err != 0) + return MapError(err); - return apm_->kNoError; + return AudioProcessing::kNoError; } int EchoControlMobileImpl::Initialize() { - if (!is_component_enabled()) { - return apm_->kNoError; + { + rtc::CritScope cs_capture(crit_capture_); + if (!is_component_enabled()) { + 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"; - return apm_->kBadSampleRateError; + return AudioProcessing::kBadSampleRateError; } - return ProcessingComponent::Initialize(); + int err = ProcessingComponent::Initialize(); + if (err != AudioProcessing::kNoError) { + return err; + } + + AllocateRenderQueue(); + + return AudioProcessing::kNoError; +} + +void EchoControlMobileImpl::AllocateRenderQueue() { + const size_t new_render_queue_element_max_size = std::max<size_t>( + static_cast<size_t>(1), + 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 + // data to put in the queue. + if (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_); + + render_signal_queue_.reset( + new SwapQueue<std::vector<int16_t>, RenderQueueItemVerifier<int16_t>>( + kMaxNumFramesToBuffer, template_queue_element, + RenderQueueItemVerifier<int16_t>(render_queue_element_max_size_))); + + render_queue_buffer_.resize(render_queue_element_max_size_); + capture_queue_buffer_.resize(render_queue_element_max_size_); + } else { + render_signal_queue_->Clear(); + } } void* EchoControlMobileImpl::CreateHandle() const { @@ -254,10 +360,14 @@ void* EchoControlMobileImpl::CreateHandle() 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)); } int EchoControlMobileImpl::InitializeHandle(void* handle) const { + rtc::CritScope cs_render(crit_render_); + rtc::CritScope cs_capture(crit_capture_); assert(handle != NULL); Handle* my_handle = static_cast<Handle*>(handle); if (WebRtcAecm_Init(my_handle, apm_->proc_sample_rate_hz()) != 0) { @@ -271,10 +381,12 @@ int EchoControlMobileImpl::InitializeHandle(void* handle) const { } } - return apm_->kNoError; + return AudioProcessing::kNoError; } int EchoControlMobileImpl::ConfigureHandle(void* handle) const { + rtc::CritScope cs_render(crit_render_); + rtc::CritScope cs_capture(crit_capture_); AecmConfig config; config.cngMode = comfort_noise_enabled_; config.echoMode = MapSetting(routing_mode_); @@ -282,13 +394,14 @@ int EchoControlMobileImpl::ConfigureHandle(void* handle) const { return WebRtcAecm_set_config(static_cast<Handle*>(handle), config); } -int EchoControlMobileImpl::num_handles_required() const { - return apm_->num_output_channels() * - apm_->num_reverse_channels(); +size_t EchoControlMobileImpl::num_handles_required() const { + // Not locked as it only relies on APM public API which is threadsafe. + return apm_->num_output_channels() * apm_->num_reverse_channels(); } int EchoControlMobileImpl::GetHandleError(void* handle) const { + // Not locked as it does not rely on anything in the state. assert(handle != NULL); - return MapError(WebRtcAecm_get_error_code(static_cast<Handle*>(handle))); + return AudioProcessing::kUnspecifiedError; } } // namespace webrtc |