Enabling a safe fall-back functionality for overruns in the runtime settings
Bug: b/177830919 Change-Id: I9369f6fc004ceb2b626d33b36262bc8aeabdb1a0 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/206988 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Alessio Bazzica <alessiob@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33371}
This commit is contained in:
parent
99bcf60a41
commit
652ada5029
@ -48,6 +48,13 @@ class EchoControl {
|
|||||||
// Provides an optional external estimate of the audio buffer delay.
|
// Provides an optional external estimate of the audio buffer delay.
|
||||||
virtual void SetAudioBufferDelay(int delay_ms) = 0;
|
virtual void SetAudioBufferDelay(int delay_ms) = 0;
|
||||||
|
|
||||||
|
// Specifies whether the capture output will be used. The purpose of this is
|
||||||
|
// to allow the echo controller to deactivate some of the processing when the
|
||||||
|
// resulting output is anyway not used, for instance when the endpoint is
|
||||||
|
// muted.
|
||||||
|
// TODO(b/177830919): Make pure virtual.
|
||||||
|
virtual void SetCaptureOutputUsage(bool capture_output_used) {}
|
||||||
|
|
||||||
// Returns wheter the signal is altered.
|
// Returns wheter the signal is altered.
|
||||||
virtual bool ActiveProcessing() const = 0;
|
virtual bool ActiveProcessing() const = 0;
|
||||||
|
|
||||||
|
|||||||
@ -833,6 +833,11 @@ void EchoCanceller3::SetAudioBufferDelay(int delay_ms) {
|
|||||||
block_processor_->SetAudioBufferDelay(delay_ms);
|
block_processor_->SetAudioBufferDelay(delay_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EchoCanceller3::SetCaptureOutputUsage(bool capture_output_used) {
|
||||||
|
// TODO(b/177830919): Add functionality for reducing the complexity when the
|
||||||
|
// echo canceller output is not used.
|
||||||
|
}
|
||||||
|
|
||||||
bool EchoCanceller3::ActiveProcessing() const {
|
bool EchoCanceller3::ActiveProcessing() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,6 +118,12 @@ class EchoCanceller3 : public EchoControl {
|
|||||||
// Provides an optional external estimate of the audio buffer delay.
|
// Provides an optional external estimate of the audio buffer delay.
|
||||||
void SetAudioBufferDelay(int delay_ms) override;
|
void SetAudioBufferDelay(int delay_ms) override;
|
||||||
|
|
||||||
|
// Specifies whether the capture output will be used. The purpose of this is
|
||||||
|
// to allow the echo controller to deactivate some of the processing when the
|
||||||
|
// resulting output is anyway not used, for instance when the endpoint is
|
||||||
|
// muted.
|
||||||
|
void SetCaptureOutputUsage(bool capture_output_used) override;
|
||||||
|
|
||||||
bool ActiveProcessing() const override;
|
bool ActiveProcessing() const override;
|
||||||
|
|
||||||
// Signals whether an external detector has detected echo leakage from the
|
// Signals whether an external detector has detected echo leakage from the
|
||||||
|
|||||||
@ -49,8 +49,6 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
constexpr int kRuntimeSettingQueueSize = 100;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) {
|
static bool LayoutHasKeyboard(AudioProcessing::ChannelLayout layout) {
|
||||||
@ -253,8 +251,8 @@ AudioProcessingImpl::AudioProcessingImpl(
|
|||||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
||||||
use_setup_specific_default_aec3_config_(
|
use_setup_specific_default_aec3_config_(
|
||||||
UseSetupSpecificDefaultAec3Congfig()),
|
UseSetupSpecificDefaultAec3Congfig()),
|
||||||
capture_runtime_settings_(kRuntimeSettingQueueSize),
|
capture_runtime_settings_(RuntimeSettingQueueSize()),
|
||||||
render_runtime_settings_(kRuntimeSettingQueueSize),
|
render_runtime_settings_(RuntimeSettingQueueSize()),
|
||||||
capture_runtime_settings_enqueuer_(&capture_runtime_settings_),
|
capture_runtime_settings_enqueuer_(&capture_runtime_settings_),
|
||||||
render_runtime_settings_enqueuer_(&render_runtime_settings_),
|
render_runtime_settings_enqueuer_(&render_runtime_settings_),
|
||||||
echo_control_factory_(std::move(echo_control_factory)),
|
echo_control_factory_(std::move(echo_control_factory)),
|
||||||
@ -674,6 +672,10 @@ void AudioProcessingImpl::HandleCaptureOutputUsedSetting(
|
|||||||
submodules_.agc_manager->HandleCaptureOutputUsedChange(
|
submodules_.agc_manager->HandleCaptureOutputUsedChange(
|
||||||
capture_.capture_output_used);
|
capture_.capture_output_used);
|
||||||
}
|
}
|
||||||
|
if (submodules_.echo_controller) {
|
||||||
|
submodules_.echo_controller->SetCaptureOutputUsage(
|
||||||
|
capture_.capture_output_used);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
|
void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
|
||||||
@ -720,19 +722,13 @@ AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() =
|
|||||||
|
|
||||||
bool AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
|
bool AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
|
||||||
RuntimeSetting setting) {
|
RuntimeSetting setting) {
|
||||||
int remaining_attempts = 10;
|
const bool successful_insert = runtime_settings_.Insert(&setting);
|
||||||
while (!runtime_settings_.Insert(&setting) && remaining_attempts-- > 0) {
|
|
||||||
RuntimeSetting setting_to_discard;
|
if (!successful_insert) {
|
||||||
if (runtime_settings_.Remove(&setting_to_discard)) {
|
|
||||||
RTC_LOG(LS_ERROR)
|
|
||||||
<< "The runtime settings queue is full. Oldest setting discarded.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remaining_attempts == 0) {
|
|
||||||
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.ApmRuntimeSettingCannotEnqueue", 1);
|
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.ApmRuntimeSettingCannotEnqueue", 1);
|
||||||
RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting.";
|
RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting.";
|
||||||
}
|
}
|
||||||
return remaining_attempts == 0;
|
return successful_insert;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioProcessingImpl::MaybeInitializeCapture(
|
int AudioProcessingImpl::MaybeInitializeCapture(
|
||||||
@ -806,6 +802,7 @@ int AudioProcessingImpl::ProcessStream(const float* const* src,
|
|||||||
|
|
||||||
void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
|
void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
|
||||||
RuntimeSetting setting;
|
RuntimeSetting setting;
|
||||||
|
int num_settings_processed = 0;
|
||||||
while (capture_runtime_settings_.Remove(&setting)) {
|
while (capture_runtime_settings_.Remove(&setting)) {
|
||||||
if (aec_dump_) {
|
if (aec_dump_) {
|
||||||
aec_dump_->WriteRuntimeSetting(setting);
|
aec_dump_->WriteRuntimeSetting(setting);
|
||||||
@ -864,6 +861,23 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
|
|||||||
HandleCaptureOutputUsedSetting(value);
|
HandleCaptureOutputUsedSetting(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
++num_settings_processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_settings_processed >= RuntimeSettingQueueSize()) {
|
||||||
|
// Handle overrun of the runtime settings queue, which likely will has
|
||||||
|
// caused settings to be discarded.
|
||||||
|
HandleOverrunInCaptureRuntimeSettingsQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioProcessingImpl::HandleOverrunInCaptureRuntimeSettingsQueue() {
|
||||||
|
// Fall back to a safe state for the case when a setting for capture output
|
||||||
|
// usage setting has been missed.
|
||||||
|
capture_.capture_output_used = true;
|
||||||
|
if (submodules_.echo_controller) {
|
||||||
|
submodules_.echo_controller->SetCaptureOutputUsage(
|
||||||
|
capture_.capture_output_used);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -342,6 +342,12 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||||||
void RecordAudioProcessingState()
|
void RecordAudioProcessingState()
|
||||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||||
|
|
||||||
|
// Ensures that overruns in the capture runtime settings queue is properly
|
||||||
|
// handled by the code, providing safe-fallbacks to mitigate the implications
|
||||||
|
// of any settings being missed.
|
||||||
|
void HandleOverrunInCaptureRuntimeSettingsQueue()
|
||||||
|
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_capture_);
|
||||||
|
|
||||||
// AecDump instance used for optionally logging APM config, input
|
// AecDump instance used for optionally logging APM config, input
|
||||||
// and output to file in the AEC-dump format defined in debug.proto.
|
// and output to file in the AEC-dump format defined in debug.proto.
|
||||||
std::unique_ptr<AecDump> aec_dump_;
|
std::unique_ptr<AecDump> aec_dump_;
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "api/scoped_refptr.h"
|
#include "api/scoped_refptr.h"
|
||||||
|
#include "modules/audio_processing/common.h"
|
||||||
#include "modules/audio_processing/include/audio_processing.h"
|
#include "modules/audio_processing/include/audio_processing.h"
|
||||||
#include "modules/audio_processing/optionally_built_submodule_creators.h"
|
#include "modules/audio_processing/optionally_built_submodule_creators.h"
|
||||||
#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
|
#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
|
||||||
@ -202,6 +203,88 @@ TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
|
|||||||
<< "Frame should be amplified.";
|
<< "Frame should be amplified.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) {
|
||||||
|
// Tests that the echo controller observes that the capture usage has been
|
||||||
|
// updated.
|
||||||
|
auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
|
||||||
|
const MockEchoControlFactory* echo_control_factory_ptr =
|
||||||
|
echo_control_factory.get();
|
||||||
|
|
||||||
|
std::unique_ptr<AudioProcessing> apm(
|
||||||
|
AudioProcessingBuilderForTesting()
|
||||||
|
.SetEchoControlFactory(std::move(echo_control_factory))
|
||||||
|
.Create());
|
||||||
|
|
||||||
|
constexpr int16_t kAudioLevel = 10000;
|
||||||
|
constexpr int kSampleRateHz = 48000;
|
||||||
|
constexpr int kNumChannels = 2;
|
||||||
|
std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
|
||||||
|
StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
|
||||||
|
frame.fill(kAudioLevel);
|
||||||
|
|
||||||
|
MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
|
||||||
|
|
||||||
|
// Ensure that SetCaptureOutputUsage is not called when no runtime settings
|
||||||
|
// are passed.
|
||||||
|
EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0);
|
||||||
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
||||||
|
|
||||||
|
// Ensure that SetCaptureOutputUsage is called with the right information when
|
||||||
|
// a runtime setting is passed.
|
||||||
|
EXPECT_CALL(*echo_control_mock,
|
||||||
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/false)));
|
||||||
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
||||||
|
|
||||||
|
EXPECT_CALL(*echo_control_mock,
|
||||||
|
SetCaptureOutputUsage(/*capture_output_used=*/true))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/true)));
|
||||||
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
||||||
|
|
||||||
|
// The number of positions to place items in the queue is equal to the queue
|
||||||
|
// size minus 1.
|
||||||
|
constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize();
|
||||||
|
|
||||||
|
// Ensure that SetCaptureOutputUsage is called with the right information when
|
||||||
|
// many runtime settings are passed.
|
||||||
|
for (int k = 0; k < kNumSlotsInQueue - 1; ++k) {
|
||||||
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/false)));
|
||||||
|
}
|
||||||
|
EXPECT_CALL(*echo_control_mock,
|
||||||
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
||||||
|
.Times(kNumSlotsInQueue - 1);
|
||||||
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
||||||
|
|
||||||
|
// Ensure that SetCaptureOutputUsage is properly called with the fallback
|
||||||
|
// value when the runtime settings queue becomes full.
|
||||||
|
for (int k = 0; k < kNumSlotsInQueue; ++k) {
|
||||||
|
EXPECT_TRUE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/false)));
|
||||||
|
}
|
||||||
|
EXPECT_FALSE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/false)));
|
||||||
|
EXPECT_FALSE(apm->PostRuntimeSetting(
|
||||||
|
AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
|
||||||
|
/*capture_output_used=*/false)));
|
||||||
|
EXPECT_CALL(*echo_control_mock,
|
||||||
|
SetCaptureOutputUsage(/*capture_output_used=*/false))
|
||||||
|
.Times(kNumSlotsInQueue);
|
||||||
|
EXPECT_CALL(*echo_control_mock,
|
||||||
|
SetCaptureOutputUsage(/*capture_output_used=*/true))
|
||||||
|
.Times(1);
|
||||||
|
apm->ProcessStream(frame.data(), config, config, frame.data());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(AudioProcessingImplTest,
|
TEST(AudioProcessingImplTest,
|
||||||
EchoControllerObservesPreAmplifierEchoPathGainChange) {
|
EchoControllerObservesPreAmplifierEchoPathGainChange) {
|
||||||
// Tests that the echo controller observes an echo path gain change when the
|
// Tests that the echo controller observes an echo path gain change when the
|
||||||
|
|||||||
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
constexpr int RuntimeSettingQueueSize() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
static inline size_t ChannelsFromLayout(AudioProcessing::ChannelLayout layout) {
|
static inline size_t ChannelsFromLayout(AudioProcessing::ChannelLayout layout) {
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case AudioProcessing::kMono:
|
case AudioProcessing::kMono:
|
||||||
|
|||||||
@ -431,8 +431,9 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
|||||||
return {Type::kCustomRenderProcessingRuntimeSetting, payload};
|
return {Type::kCustomRenderProcessingRuntimeSetting, payload};
|
||||||
}
|
}
|
||||||
|
|
||||||
static RuntimeSetting CreateCaptureOutputUsedSetting(bool payload) {
|
static RuntimeSetting CreateCaptureOutputUsedSetting(
|
||||||
return {Type::kCaptureOutputUsed, payload};
|
bool capture_output_used) {
|
||||||
|
return {Type::kCaptureOutputUsed, capture_output_used};
|
||||||
}
|
}
|
||||||
|
|
||||||
Type type() const { return type_; }
|
Type type() const { return type_; }
|
||||||
|
|||||||
@ -34,6 +34,10 @@ class MockEchoControl : public EchoControl {
|
|||||||
(override));
|
(override));
|
||||||
MOCK_METHOD(EchoControl::Metrics, GetMetrics, (), (const, override));
|
MOCK_METHOD(EchoControl::Metrics, GetMetrics, (), (const, override));
|
||||||
MOCK_METHOD(void, SetAudioBufferDelay, (int delay_ms), (override));
|
MOCK_METHOD(void, SetAudioBufferDelay, (int delay_ms), (override));
|
||||||
|
MOCK_METHOD(void,
|
||||||
|
SetCaptureOutputUsage,
|
||||||
|
(bool capture_output_used),
|
||||||
|
(override));
|
||||||
MOCK_METHOD(bool, ActiveProcessing, (), (const, override));
|
MOCK_METHOD(bool, ActiveProcessing, (), (const, override));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user