mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-15 14:50:39 +01:00
Send runtime settings to the Audio Processing Module (APM)
This CL includes the following changes: - APM runtime setting (ID + float payload) and unit tests - Swap queue of APM runtime settings used in AudioProcessingImpl - runtime settings handler that forwards the settings to APM sub-modules when a message is retrieved from the queue - Unit test placeholder to check that the pre-gain update message is correctly delivered Bug: webrtc:9138 Change-Id: Id22704af15fde2b87a4431f5ce64ad1aeafc5280 Reviewed-on: https://webrtc-review.googlesource.com/69320 Reviewed-by: Per Åhgren <peah@webrtc.org> Reviewed-by: Alex Loiko <aleloi@webrtc.org> Commit-Queue: Alessio Bazzica <alessiob@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22873}
This commit is contained in:
parent
5b07c24056
commit
c054e78f4e
6 changed files with 131 additions and 0 deletions
|
@ -379,6 +379,8 @@ AudioProcessingImpl::AudioProcessingImpl(
|
||||||
NonlinearBeamformer* beamformer)
|
NonlinearBeamformer* beamformer)
|
||||||
: data_dumper_(
|
: data_dumper_(
|
||||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
||||||
|
runtime_settings_(new SwapQueue<RuntimeSetting>(100)),
|
||||||
|
runtime_settings_enqueuer_(runtime_settings_.get()),
|
||||||
high_pass_filter_impl_(new HighPassFilterImpl(this)),
|
high_pass_filter_impl_(new HighPassFilterImpl(this)),
|
||||||
echo_control_factory_(std::move(echo_control_factory)),
|
echo_control_factory_(std::move(echo_control_factory)),
|
||||||
submodule_states_(!!capture_post_processor, !!render_pre_processor),
|
submodule_states_(!!capture_post_processor, !!render_pre_processor),
|
||||||
|
@ -795,6 +797,32 @@ void AudioProcessingImpl::set_output_will_be_muted(bool muted) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
|
||||||
|
RTC_DCHECK(setting.type() != RuntimeSetting::Type::kNotSpecified);
|
||||||
|
runtime_settings_enqueuer_.Enqueue(setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioProcessingImpl::RuntimeSettingEnqueuer::RuntimeSettingEnqueuer(
|
||||||
|
SwapQueue<RuntimeSetting>* runtime_settings)
|
||||||
|
: runtime_settings_(runtime_settings) {
|
||||||
|
RTC_DCHECK(runtime_settings_);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() =
|
||||||
|
default;
|
||||||
|
|
||||||
|
void AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
|
||||||
|
RuntimeSetting setting) {
|
||||||
|
size_t remaining_attempts = 10;
|
||||||
|
while (!runtime_settings_->Insert(&setting) && remaining_attempts-- > 0) {
|
||||||
|
RuntimeSetting setting_to_discard;
|
||||||
|
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_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting.";
|
||||||
|
}
|
||||||
|
|
||||||
int AudioProcessingImpl::ProcessStream(const float* const* src,
|
int AudioProcessingImpl::ProcessStream(const float* const* src,
|
||||||
size_t samples_per_channel,
|
size_t samples_per_channel,
|
||||||
|
@ -877,6 +905,22 @@ int AudioProcessingImpl::ProcessStream(const float* const* src,
|
||||||
return kNoError;
|
return kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioProcessingImpl::HandleRuntimeSettings() {
|
||||||
|
RuntimeSetting setting;
|
||||||
|
while (runtime_settings_->Remove(&setting)) {
|
||||||
|
RTC_DCHECK(setting.type() != RuntimeSetting::Type::kNotSpecified);
|
||||||
|
switch (setting.type()) {
|
||||||
|
case RuntimeSetting::Type::kCapturePreGain:
|
||||||
|
// TODO(bugs.chromium.org/9138): Notify
|
||||||
|
// pre-gain when the sub-module is implemented.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) {
|
void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) {
|
||||||
EchoCancellationImpl::PackRenderAudioBuffer(audio, num_output_channels(),
|
EchoCancellationImpl::PackRenderAudioBuffer(audio, num_output_channels(),
|
||||||
num_reverse_channels(),
|
num_reverse_channels(),
|
||||||
|
@ -1131,6 +1175,8 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
||||||
|
HandleRuntimeSettings();
|
||||||
|
|
||||||
// Ensure that not both the AEC and AECM are active at the same time.
|
// Ensure that not both the AEC and AECM are active at the same time.
|
||||||
// TODO(peah): Simplify once the public API Enable functions for these
|
// TODO(peah): Simplify once the public API Enable functions for these
|
||||||
// are moved to APM.
|
// are moved to APM.
|
||||||
|
|
|
@ -66,6 +66,8 @@ class AudioProcessingImpl : public AudioProcessing {
|
||||||
std::unique_ptr<AudioGenerator> audio_generator) override;
|
std::unique_ptr<AudioGenerator> audio_generator) override;
|
||||||
void DetachPlayoutAudioGenerator() override;
|
void DetachPlayoutAudioGenerator() override;
|
||||||
|
|
||||||
|
void SetRuntimeSetting(RuntimeSetting setting) override;
|
||||||
|
|
||||||
// Capture-side exclusive methods possibly running APM in a
|
// Capture-side exclusive methods possibly running APM in a
|
||||||
// multi-threaded manner. Acquire the capture lock.
|
// multi-threaded manner. Acquire the capture lock.
|
||||||
int ProcessStream(AudioFrame* frame) override;
|
int ProcessStream(AudioFrame* frame) override;
|
||||||
|
@ -149,6 +151,21 @@ class AudioProcessingImpl : public AudioProcessing {
|
||||||
std::unique_ptr<ApmDataDumper> data_dumper_;
|
std::unique_ptr<ApmDataDumper> data_dumper_;
|
||||||
static int instance_count_;
|
static int instance_count_;
|
||||||
|
|
||||||
|
std::unique_ptr<SwapQueue<RuntimeSetting>> runtime_settings_;
|
||||||
|
|
||||||
|
// Class providing thread-safe message pipe functionality for
|
||||||
|
// |runtime_settings_|.
|
||||||
|
class RuntimeSettingEnqueuer {
|
||||||
|
public:
|
||||||
|
explicit RuntimeSettingEnqueuer(
|
||||||
|
SwapQueue<RuntimeSetting>* runtime_settings);
|
||||||
|
~RuntimeSettingEnqueuer();
|
||||||
|
void Enqueue(RuntimeSetting setting);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SwapQueue<RuntimeSetting>* runtime_settings_;
|
||||||
|
} runtime_settings_enqueuer_;
|
||||||
|
|
||||||
// Submodule interface implementations.
|
// Submodule interface implementations.
|
||||||
std::unique_ptr<HighPassFilter> high_pass_filter_impl_;
|
std::unique_ptr<HighPassFilter> high_pass_filter_impl_;
|
||||||
|
|
||||||
|
@ -239,6 +256,9 @@ class AudioProcessingImpl : public AudioProcessing {
|
||||||
void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
|
void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
|
||||||
void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
|
void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
|
||||||
|
|
||||||
|
// Handle all the runtime settings in the queue.
|
||||||
|
void HandleRuntimeSettings() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
|
||||||
|
|
||||||
void EmptyQueuedRenderAudio();
|
void EmptyQueuedRenderAudio();
|
||||||
void AllocateRenderQueue()
|
void AllocateRenderQueue()
|
||||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
|
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
|
||||||
|
|
|
@ -74,4 +74,10 @@ TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
|
||||||
EXPECT_NOERR(mock.ProcessReverseStream(&frame));
|
EXPECT_NOERR(mock.ProcessReverseStream(&frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
|
||||||
|
// TODO(bugs.chromium.org/9138): Implement this test as soon as the pre-gain
|
||||||
|
// sub-module is implemented and it is notified by HandleRuntimeSettings()
|
||||||
|
// when the gain changes.
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "rtc_base/numerics/safe_minmax.h"
|
#include "rtc_base/numerics/safe_minmax.h"
|
||||||
#include "rtc_base/protobuf_utils.h"
|
#include "rtc_base/protobuf_utils.h"
|
||||||
#include "rtc_base/refcountedobject.h"
|
#include "rtc_base/refcountedobject.h"
|
||||||
|
#include "rtc_base/swap_queue.h"
|
||||||
#include "rtc_base/task_queue.h"
|
#include "rtc_base/task_queue.h"
|
||||||
#include "rtc_base/thread.h"
|
#include "rtc_base/thread.h"
|
||||||
#include "system_wrappers/include/event_wrapper.h"
|
#include "system_wrappers/include/event_wrapper.h"
|
||||||
|
@ -2819,6 +2820,34 @@ INSTANTIATE_TEST_CASE_P(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
TEST(RuntimeSettingTest, TestDefaultCtor) {
|
||||||
|
auto s = AudioProcessing::RuntimeSetting();
|
||||||
|
EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RuntimeSettingTest, TestCapturePreGain) {
|
||||||
|
using Type = AudioProcessing::RuntimeSetting::Type;
|
||||||
|
{
|
||||||
|
auto s = AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.25f);
|
||||||
|
EXPECT_EQ(Type::kCapturePreGain, s.type());
|
||||||
|
float v;
|
||||||
|
s.GetFloat(&v);
|
||||||
|
EXPECT_EQ(1.25f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||||
|
EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RuntimeSettingTest, TestUsageWithSwapQueue) {
|
||||||
|
SwapQueue<AudioProcessing::RuntimeSetting> q(1);
|
||||||
|
auto s = AudioProcessing::RuntimeSetting();
|
||||||
|
ASSERT_TRUE(q.Insert(&s));
|
||||||
|
ASSERT_TRUE(q.Remove(&s));
|
||||||
|
EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ApmConfiguration, EnablePostProcessing) {
|
TEST(ApmConfiguration, EnablePostProcessing) {
|
||||||
// Verify that apm uses a capture post processing module if one is provided.
|
// Verify that apm uses a capture post processing module if one is provided.
|
||||||
webrtc::Config webrtc_config;
|
webrtc::Config webrtc_config;
|
||||||
|
|
|
@ -302,6 +302,32 @@ class AudioProcessing : public rtc::RefCountInterface {
|
||||||
kStereoAndKeyboard
|
kStereoAndKeyboard
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Specifies the properties of a setting to be passed to AudioProcessing at
|
||||||
|
// runtime.
|
||||||
|
class RuntimeSetting {
|
||||||
|
public:
|
||||||
|
enum class Type { kNotSpecified, kCapturePreGain };
|
||||||
|
|
||||||
|
RuntimeSetting() : type_(Type::kNotSpecified), value_(0.f) {}
|
||||||
|
~RuntimeSetting() = default;
|
||||||
|
|
||||||
|
static RuntimeSetting CreateCapturePreGain(float gain) {
|
||||||
|
RTC_DCHECK_GE(gain, 1.f) << "Attenuation is not allowed.";
|
||||||
|
return {Type::kCapturePreGain, gain};
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type() const { return type_; }
|
||||||
|
void GetFloat(float* value) const {
|
||||||
|
RTC_DCHECK(value);
|
||||||
|
*value = value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RuntimeSetting(Type id, float value) : type_(id), value_(value) {}
|
||||||
|
Type type_;
|
||||||
|
float value_;
|
||||||
|
};
|
||||||
|
|
||||||
~AudioProcessing() override {}
|
~AudioProcessing() override {}
|
||||||
|
|
||||||
// Initializes internal states, while retaining all user settings. This
|
// Initializes internal states, while retaining all user settings. This
|
||||||
|
@ -359,6 +385,9 @@ class AudioProcessing : public rtc::RefCountInterface {
|
||||||
// Default false.
|
// Default false.
|
||||||
virtual void set_output_will_be_muted(bool muted) = 0;
|
virtual void set_output_will_be_muted(bool muted) = 0;
|
||||||
|
|
||||||
|
// Enqueue a runtime setting.
|
||||||
|
virtual void SetRuntimeSetting(RuntimeSetting setting) = 0;
|
||||||
|
|
||||||
// Processes a 10 ms |frame| of the primary audio stream. On the client-side,
|
// Processes a 10 ms |frame| of the primary audio stream. On the client-side,
|
||||||
// this is the near-end (or captured) audio.
|
// this is the near-end (or captured) audio.
|
||||||
//
|
//
|
||||||
|
|
|
@ -167,6 +167,7 @@ class MockAudioProcessing : public testing::NiceMock<AudioProcessing> {
|
||||||
MOCK_CONST_METHOD0(num_output_channels, size_t());
|
MOCK_CONST_METHOD0(num_output_channels, size_t());
|
||||||
MOCK_CONST_METHOD0(num_reverse_channels, size_t());
|
MOCK_CONST_METHOD0(num_reverse_channels, size_t());
|
||||||
MOCK_METHOD1(set_output_will_be_muted, void(bool muted));
|
MOCK_METHOD1(set_output_will_be_muted, void(bool muted));
|
||||||
|
MOCK_METHOD1(SetRuntimeSetting, void(RuntimeSetting setting));
|
||||||
MOCK_METHOD1(ProcessStream, int(AudioFrame* frame));
|
MOCK_METHOD1(ProcessStream, int(AudioFrame* frame));
|
||||||
MOCK_METHOD7(ProcessStream, int(const float* const* src,
|
MOCK_METHOD7(ProcessStream, int(const float* const* src,
|
||||||
size_t samples_per_channel,
|
size_t samples_per_channel,
|
||||||
|
|
Loading…
Reference in a new issue