APM: add AudioProcessingImpl::capture_::applied_input_volume(_changed)

The `recommended_stream_analog_level()` getter is used to retrieve
both the applied and the recommended input volume. This behavior is
error-prone since the caller must know what is returned based on
the point in the code (namely, before/after the AGC has changed
the last applied input volume into a recommended level).

This CL is a first step to make clarity on which input volume is
handled in different parts of APM. Next in the pipeline: make
`recommended_stream_analog_level()` a trivial getter that always
returns the recommended level.

Main changes:
- When `recommended_stream_analog_level()` is called but
  `set_stream_analog_level()` is not called, APM logs an error
  and returns a fall-back volume (which should not be applied
  since, when `set_stream_analog_level()` is not called, no
  external input volume is expected to be present
- When APM is used without calling the `*_stream_analog_level()`
  methods (e.g., when the caller does not provide any input volume),
  the recorded AEC dumps won't store `Stream::applied_input_level`

Other changes:
- Removed `AudioProcessingImpl::capture_::prev_analog_mic_level`
- Removed redundant code in `GainController2` around detecting
  input volume changes (already done by APM)
- Adapted the `audioproc_f` and `unpack_aecdump` tools
- Data dumps clean-up: the applied and the recommended input
  volumes are now recorded in an AGC implementation agnostic way

Bug: webrtc:7494, b/241923537
Change-Id: I3cb4a731fd9f3dc19bf6ac679b7ed8c969ea283b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/271544
Reviewed-by: Per Åhgren <peah@webrtc.org>
Reviewed-by: Hanna Silen <silen@webrtc.org>
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38054}
This commit is contained in:
Alessio Bazzica 2022-09-07 17:14:26 +02:00 committed by WebRTC LUCI CQ
parent 0c0c602653
commit fcf1af3049
14 changed files with 141 additions and 96 deletions

View file

@ -114,7 +114,10 @@ rtc_source_set("aec_dump_interface") {
":api",
":audio_frame_view",
]
absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ]
absl_deps = [
"//third_party/abseil-cpp/absl/base:core_headers",
"//third_party/abseil-cpp/absl/types:optional",
]
}
rtc_library("gain_controller2") {

View file

@ -53,7 +53,9 @@ void CaptureStreamInfo::AddAudioProcessingState(
auto* stream = event_->mutable_stream();
stream->set_delay(state.delay);
stream->set_drift(state.drift);
stream->set_applied_input_volume(state.applied_input_volume);
if (state.applied_input_volume.has_value()) {
stream->set_applied_input_volume(*state.applied_input_volume);
}
stream->set_keypress(state.keypress);
}
} // namespace webrtc

View file

@ -143,6 +143,8 @@ void PackRenderAudioBufferForEchoDetector(const AudioBuffer& audio,
audio.channels_const()[0] + audio.num_frames());
}
constexpr int kUnspecifiedDataDumpInputVolume = -100;
} // namespace
// Throughout webrtc, it's assumed that success is represented by zero.
@ -1073,7 +1075,6 @@ int AudioProcessingImpl::ProcessStream(const int16_t* const src,
if (aec_dump_) {
RecordProcessedCaptureStream(dest, output_config);
}
return kNoError;
}
@ -1088,6 +1089,10 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
RTC_DCHECK_LE(
!!submodules_.echo_controller + !!submodules_.echo_control_mobile, 1);
data_dumper_->DumpRaw(
"applied_input_volume",
capture_.applied_input_volume.value_or(kUnspecifiedDataDumpInputVolume));
AudioBuffer* capture_buffer = capture_.capture_audio.get(); // For brevity.
AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get();
@ -1123,16 +1128,16 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
levels.peak, 1, RmsLevel::kMinLevelDb, 64);
}
// Detect an analog gain change.
int analog_mic_level = recommended_stream_analog_level_locked();
const bool analog_mic_level_changed =
capture_.prev_analog_mic_level != analog_mic_level &&
capture_.prev_analog_mic_level != -1;
capture_.prev_analog_mic_level = analog_mic_level;
analog_gain_stats_reporter_.UpdateStatistics(analog_mic_level);
if (capture_.applied_input_volume.has_value()) {
// Log the applied input volume only when available.
input_volume_stats_reporter_.UpdateStatistics(
*capture_.applied_input_volume);
}
if (submodules_.echo_controller) {
capture_.echo_path_gain_change = analog_mic_level_changed;
// Determine if the echo path gain has changed by checking all the gains
// applied before AEC.
capture_.echo_path_gain_change = capture_.applied_input_volume_changed;
// Detect and flag any change in the capture level adjustment pre-gain.
if (submodules_.capture_levels_adjuster) {
@ -1141,7 +1146,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
capture_.echo_path_gain_change =
capture_.echo_path_gain_change ||
(capture_.prev_pre_adjustment_gain != pre_adjustment_gain &&
capture_.prev_pre_adjustment_gain >= 0.f);
capture_.prev_pre_adjustment_gain >= 0.0f);
capture_.prev_pre_adjustment_gain = pre_adjustment_gain;
}
@ -1312,9 +1317,9 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
}
if (submodules_.gain_controller2) {
submodules_.gain_controller2->NotifyAnalogLevel(
recommended_stream_analog_level_locked());
submodules_.gain_controller2->Process(voice_probability, capture_buffer);
submodules_.gain_controller2->Process(
voice_probability, capture_.applied_input_volume_changed,
capture_buffer);
}
if (submodules_.capture_post_processor) {
@ -1333,12 +1338,6 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
levels.peak, 1, RmsLevel::kMinLevelDb, 64);
}
if (submodules_.agc_manager) {
int level = recommended_stream_analog_level_locked();
data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1,
&level);
}
// Compute echo-detector stats.
if (submodules_.echo_detector) {
auto ed_metrics = submodules_.echo_detector->GetMetrics();
@ -1388,6 +1387,9 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
capture_.capture_output_used_last_frame = capture_.capture_output_used;
capture_.was_stream_delay_set = false;
// TODO(bugs.webrtc.org/7494): Dump recommended input volume.
return kNoError;
}
@ -1605,14 +1607,13 @@ void AudioProcessingImpl::set_stream_analog_level(int level) {
}
void AudioProcessingImpl::set_stream_analog_level_locked(int level) {
// Cache the level for later reporting back as the recommended input volume to
// use.
capture_.cached_stream_analog_level_ = level;
capture_.applied_input_volume_changed =
capture_.applied_input_volume.has_value() &&
*capture_.applied_input_volume != level;
capture_.applied_input_volume = level;
if (submodules_.agc_manager) {
submodules_.agc_manager->set_stream_analog_level(level);
data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level",
1, &level);
return;
}
@ -1629,6 +1630,10 @@ int AudioProcessingImpl::recommended_stream_analog_level() const {
}
int AudioProcessingImpl::recommended_stream_analog_level_locked() const {
if (!capture_.applied_input_volume.has_value()) {
RTC_LOG(LS_ERROR) << "set_stream_analog_level has not been called";
}
if (submodules_.agc_manager) {
return submodules_.agc_manager->recommended_analog_level();
}
@ -1637,7 +1642,9 @@ int AudioProcessingImpl::recommended_stream_analog_level_locked() const {
return submodules_.gain_control->stream_analog_level();
}
return capture_.cached_stream_analog_level_;
// Input volume to recommend when `set_stream_analog_level()` is not called.
constexpr int kFallBackInputVolume = 255;
return capture_.applied_input_volume.value_or(kFallBackInputVolume);
}
bool AudioProcessingImpl::CreateAndAttachAecDump(absl::string_view file_name,
@ -2137,10 +2144,7 @@ void AudioProcessingImpl::RecordAudioProcessingState() {
AecDump::AudioProcessingState audio_proc_state;
audio_proc_state.delay = capture_nonlocked_.stream_delay_ms;
audio_proc_state.drift = 0;
// TODO(bugs.webrtc.org/7494): Refactor to clarify that `stream_analog_level`
// is in fact assigned to the applied volume and not to the recommended one.
audio_proc_state.applied_input_volume =
recommended_stream_analog_level_locked();
audio_proc_state.applied_input_volume = capture_.applied_input_volume;
audio_proc_state.keypress = capture_.key_pressed;
aec_dump_->AddAudioProcessingState(audio_proc_state);
}
@ -2153,10 +2157,10 @@ AudioProcessingImpl::ApmCaptureState::ApmCaptureState()
capture_processing_format(kSampleRate16kHz),
split_rate(kSampleRate16kHz),
echo_path_gain_change(false),
prev_analog_mic_level(-1),
prev_pre_adjustment_gain(-1.f),
prev_pre_adjustment_gain(-1.0f),
playout_volume(-1),
prev_playout_volume(-1) {}
prev_playout_volume(-1),
applied_input_volume_changed(false) {}
AudioProcessingImpl::ApmCaptureState::~ApmCaptureState() = default;

View file

@ -20,6 +20,7 @@
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/function_view.h"
#include "modules/audio_processing/aec3/echo_canceller3.h"
@ -466,12 +467,14 @@ class AudioProcessingImpl : public AudioProcessing {
StreamConfig capture_processing_format;
int split_rate;
bool echo_path_gain_change;
int prev_analog_mic_level;
float prev_pre_adjustment_gain;
int playout_volume;
int prev_playout_volume;
AudioProcessingStats stats;
int cached_stream_analog_level_ = 0;
// Input volume applied on the audio input device when the audio is
// acquired. Unspecified when unknown.
absl::optional<int> applied_input_volume;
bool applied_input_volume_changed;
} capture_ RTC_GUARDED_BY(mutex_capture_);
struct ApmCaptureNonLockedState {
@ -532,7 +535,7 @@ class AudioProcessingImpl : public AudioProcessing {
RmsLevel capture_output_rms_ RTC_GUARDED_BY(mutex_capture_);
int capture_rms_interval_counter_ RTC_GUARDED_BY(mutex_capture_) = 0;
AnalogGainStatsReporter analog_gain_stats_reporter_
AnalogGainStatsReporter input_volume_stats_reporter_
RTC_GUARDED_BY(mutex_capture_);
// Lock protection not needed.

View file

@ -955,8 +955,8 @@ TEST_F(ApmTest, PreAmplifier) {
EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.5f);
}
// This test a simple test that ensures that the emulated analog mic gain
// functionality runs without crashing.
// Ensures that the emulated analog mic gain functionality runs without
// crashing.
TEST_F(ApmTest, AnalogMicGainEmulation) {
// Fill the audio frame with a sawtooth pattern.
rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(&frame_);

View file

@ -28,7 +28,6 @@ namespace {
using Agc2Config = AudioProcessing::Config::GainController2;
constexpr int kUnspecifiedAnalogLevel = -1;
constexpr int kLogLimiterStatsPeriodMs = 30'000;
constexpr int kFrameLengthMs = 10;
constexpr int kLogLimiterStatsPeriodNumFrames =
@ -81,8 +80,7 @@ GainController2::GainController2(const Agc2Config& config,
num_channels,
&data_dumper_)),
limiter_(sample_rate_hz, &data_dumper_, /*histogram_name_prefix=*/"Agc2"),
calls_since_last_limiter_log_(0),
analog_level_(kUnspecifiedAnalogLevel) {
calls_since_last_limiter_log_(0) {
RTC_DCHECK(Validate(config));
data_dumper_.InitiateNewSetOfRecordings();
const bool use_vad = config.adaptive_digital.enabled;
@ -112,7 +110,6 @@ void GainController2::Initialize(int sample_rate_hz, int num_channels) {
}
data_dumper_.InitiateNewSetOfRecordings();
calls_since_last_limiter_log_ = 0;
analog_level_ = kUnspecifiedAnalogLevel;
}
void GainController2::SetFixedGainDb(float gain_db) {
@ -126,8 +123,14 @@ void GainController2::SetFixedGainDb(float gain_db) {
}
void GainController2::Process(absl::optional<float> speech_probability,
bool input_volume_changed,
AudioBuffer* audio) {
data_dumper_.DumpRaw("agc2_notified_analog_level", analog_level_);
data_dumper_.DumpRaw("agc2_applied_input_volume_changed",
input_volume_changed);
if (input_volume_changed && !!adaptive_digital_controller_) {
adaptive_digital_controller_->HandleInputGainChange();
}
AudioFrameView<float> float_frame(audio->channels(), audio->num_channels(),
audio->num_frames());
if (vad_) {
@ -159,13 +162,6 @@ void GainController2::Process(absl::optional<float> speech_probability,
}
}
void GainController2::NotifyAnalogLevel(int level) {
if (analog_level_ != level && adaptive_digital_controller_) {
adaptive_digital_controller_->HandleInputGainChange();
}
analog_level_ = level;
}
bool GainController2::Validate(
const AudioProcessing::Config::GainController2& config) {
const auto& fixed = config.fixed_digital;

View file

@ -50,11 +50,12 @@ class GainController2 {
// Applies fixed and adaptive digital gains to `audio` and runs a limiter.
// If the internal VAD is used, `speech_probability` is ignored. Otherwise
// `speech_probability` is used for digital adaptive gain if it's available
// (limited to values [0.0, 1.0]).
void Process(absl::optional<float> speech_probability, AudioBuffer* audio);
// Handles analog level changes.
void NotifyAnalogLevel(int level);
// (limited to values [0.0, 1.0]). Handles input volume changes; if the caller
// cannot determine whether an input volume change occurred, set
// `input_volume_changed` to false.
void Process(absl::optional<float> speech_probability,
bool input_volume_changed,
AudioBuffer* audio);
static bool Validate(const AudioProcessing::Config::GainController2& config);
@ -69,7 +70,6 @@ class GainController2 {
std::unique_ptr<AdaptiveDigitalGainController> adaptive_digital_controller_;
Limiter limiter_;
int calls_since_last_limiter_log_;
int analog_level_;
};
} // namespace webrtc

View file

@ -47,7 +47,8 @@ float RunAgc2WithConstantInput(GainController2& agc2,
// Give time to the level estimator to converge.
for (int i = 0; i < num_frames + 1; ++i) {
SetAudioBufferSamples(input_level, ab);
agc2.Process(/*speech_probability=*/absl::nullopt, &ab);
agc2.Process(/*speech_probability=*/absl::nullopt,
/*input_volume_changed=*/false, &ab);
}
// Return the last sample from the last processed frame.
@ -283,12 +284,14 @@ TEST(GainController2, CheckFinalGainWithAdaptiveDigitalController) {
x *= gain;
}
test::CopyVectorToAudioBuffer(stream_config, frame, &audio_buffer);
agc2.Process(/*speech_probability=*/absl::nullopt, &audio_buffer);
agc2.Process(/*speech_probability=*/absl::nullopt,
/*input_volume_changed=*/false, &audio_buffer);
}
// Estimate the applied gain by processing a probing frame.
SetAudioBufferSamples(/*value=*/1.0f, audio_buffer);
agc2.Process(/*speech_probability=*/absl::nullopt, &audio_buffer);
agc2.Process(/*speech_probability=*/absl::nullopt,
/*input_volume_changed=*/false, &audio_buffer);
const float applied_gain_db =
20.0f * std::log10(audio_buffer.channels_const()[0][0]);
@ -343,10 +346,13 @@ TEST(GainController2,
x *= gain;
}
test::CopyVectorToAudioBuffer(stream_config, frame, &audio_buffer);
agc2.Process(kSpeechProbabilities[j], &audio_buffer);
agc2.Process(kSpeechProbabilities[j], /*input_volume_changed=*/false,
&audio_buffer);
test::CopyVectorToAudioBuffer(stream_config, frame,
&audio_buffer_reference);
agc2_reference.Process(absl::nullopt, &audio_buffer_reference);
agc2_reference.Process(/*speech_probability=*/absl::nullopt,
/*input_volume_changed=*/false,
&audio_buffer_reference);
// Check the output buffers.
for (int i = 0; i < kStereo; ++i) {
@ -407,10 +413,13 @@ TEST(GainController2,
x *= gain;
}
test::CopyVectorToAudioBuffer(stream_config, frame, &audio_buffer);
agc2.Process(kSpeechProbabilities[j], &audio_buffer);
agc2.Process(kSpeechProbabilities[j], /*input_volume_changed=*/false,
&audio_buffer);
test::CopyVectorToAudioBuffer(stream_config, frame,
&audio_buffer_reference);
agc2_reference.Process(absl::nullopt, &audio_buffer_reference);
agc2_reference.Process(/*speech_probability=*/absl::nullopt,
/*input_volume_changed=*/false,
&audio_buffer_reference);
// Check the output buffers.
for (int i = 0; i < kStereo; ++i) {
for (int j = 0; j < static_cast<int>(audio_buffer.num_frames()); ++j) {
@ -472,11 +481,13 @@ TEST(GainController2,
}
test::CopyVectorToAudioBuffer(stream_config, frame,
&audio_buffer_reference);
agc2_reference.Process(absl::nullopt, &audio_buffer_reference);
agc2_reference.Process(absl::nullopt, /*input_volume_changed=*/false,
&audio_buffer_reference);
test::CopyVectorToAudioBuffer(stream_config, frame, &audio_buffer);
agc2.Process(vad.Analyze(AudioFrameView<const float>(
audio_buffer.channels(), audio_buffer.num_channels(),
audio_buffer.num_frames())),
float speech_probability = vad.Analyze(AudioFrameView<const float>(
audio_buffer.channels(), audio_buffer.num_channels(),
audio_buffer.num_frames()));
agc2.Process(speech_probability, /*input_volume_changed=*/false,
&audio_buffer);
// Check the output buffer.
for (int i = 0; i < kStereo; ++i) {

View file

@ -16,6 +16,7 @@
#include <string>
#include "absl/base/attributes.h"
#include "absl/types/optional.h"
#include "modules/audio_processing/include/audio_frame_view.h"
#include "modules/audio_processing/include/audio_processing.h"
@ -67,7 +68,7 @@ class AecDump {
struct AudioProcessingState {
int delay;
int drift;
int applied_input_volume;
absl::optional<int> applied_input_volume;
bool keypress;
};

View file

@ -589,9 +589,10 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
// HAL. Must be within the range [0, 255].
virtual void set_stream_analog_level(int level) = 0;
// When an analog mode is set, this should be called after ProcessStream()
// to obtain the recommended new analog level for the audio HAL. It is the
// user's responsibility to apply this level.
// When an analog mode is set, this should be called after
// `set_stream_analog_level()` and `ProcessStream()` to obtain the recommended
// new analog level for the audio HAL. It is the user's responsibility to
// apply this level.
virtual int recommended_stream_analog_level() const = 0;
// This must be called if and only if echo processing is enabled.

View file

@ -174,9 +174,11 @@ void AecDumpBasedSimulator::PrepareProcessStreamCall(
}
}
// The stream analog level is always logged in the AEC dumps.
RTC_CHECK(msg.has_applied_input_volume());
aec_dump_mic_level_ = msg.applied_input_volume();
// Set the applied input level if available.
aec_dump_applied_input_level_ =
msg.has_applied_input_volume()
? absl::optional<int>(msg.applied_input_volume())
: absl::nullopt;
}
void AecDumpBasedSimulator::VerifyProcessStreamBitExactness(

View file

@ -118,7 +118,7 @@ AudioProcessingSimulator::AudioProcessingSimulator(
std::unique_ptr<AudioProcessingBuilder> ap_builder)
: settings_(settings),
ap_(std::move(audio_processing)),
analog_mic_level_(settings.initial_mic_level),
applied_input_volume_(settings.initial_mic_level),
fake_recording_device_(
settings.initial_mic_level,
settings_.simulate_mic_gain ? *settings.simulated_mic_kind : 0),
@ -208,29 +208,50 @@ AudioProcessingSimulator::~AudioProcessingSimulator() {
}
void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
// Optionally use the fake recording device to simulate analog gain.
// Optionally simulate the input volume.
if (settings_.simulate_mic_gain) {
RTC_DCHECK(!settings_.use_analog_mic_gain_emulation);
if (settings_.aec_dump_input_filename) {
// When the analog gain is simulated and an AEC dump is used as input, set
// the undo level to `aec_dump_mic_level_` to virtually restore the
// unmodified microphone signal level.
fake_recording_device_.SetUndoMicLevel(aec_dump_mic_level_);
// Set the input volume to simulate.
fake_recording_device_.SetMicLevel(applied_input_volume_);
if (settings_.aec_dump_input_filename &&
aec_dump_applied_input_level_.has_value()) {
// For AEC dumps, use the applied input level, if recorded, to "virtually
// restore" the capture signal level before the input volume was applied.
fake_recording_device_.SetUndoMicLevel(*aec_dump_applied_input_level_);
}
// Apply the input volume.
if (fixed_interface) {
fake_recording_device_.SimulateAnalogGain(fwd_frame_.data);
} else {
fake_recording_device_.SimulateAnalogGain(in_buf_.get());
}
}
// Notify the current mic level to AGC.
// Let APM know which input volume was applied.
// Keep track of whether `set_stream_analog_level()` is called.
bool applied_input_volume_set = false;
if (settings_.simulate_mic_gain) {
// When the input volume is simulated, use the volume applied for
// simulation.
ap_->set_stream_analog_level(fake_recording_device_.MicLevel());
applied_input_volume_set = true;
} else if (!settings_.use_analog_mic_gain_emulation) {
// Notify the current mic level to AGC.
ap_->set_stream_analog_level(settings_.aec_dump_input_filename
? aec_dump_mic_level_
: analog_mic_level_);
// Ignore the recommended input volume stored in `applied_input_volume_` and
// instead notify APM with the recorded input volume (if available).
if (settings_.aec_dump_input_filename &&
aec_dump_applied_input_level_.has_value()) {
// The actually applied input volume is available in the AEC dump.
ap_->set_stream_analog_level(*aec_dump_applied_input_level_);
applied_input_volume_set = true;
} else if (!settings_.aec_dump_input_filename) {
// Wav files do not include any information about the actually applied
// input volume. Hence, use the recommended input volume stored in
// `applied_input_volume_`.
ap_->set_stream_analog_level(applied_input_volume_);
applied_input_volume_set = true;
}
}
// Post any scheduled runtime settings.
@ -266,13 +287,12 @@ void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
out_config_, out_buf_->channels()));
}
// Store the mic level suggested by AGC.
// Note that when the analog gain is simulated and an AEC dump is used as
// input, `analog_mic_level_` will not be used with set_stream_analog_level().
analog_mic_level_ = ap_->recommended_stream_analog_level();
if (settings_.simulate_mic_gain) {
fake_recording_device_.SetMicLevel(analog_mic_level_);
// Retrieve the recommended input volume only if `set_stream_analog_level()`
// has been called to stick to the APM API contract.
if (applied_input_volume_set) {
applied_input_volume_ = ap_->recommended_stream_analog_level();
}
if (buffer_memory_writer_) {
RTC_CHECK(!buffer_file_writer_);
buffer_memory_writer_->Write(*out_buf_);

View file

@ -219,7 +219,7 @@ class AudioProcessingSimulator {
Int16Frame rev_frame_;
Int16Frame fwd_frame_;
bool bitexact_output_ = true;
int aec_dump_mic_level_ = 0;
absl::optional<int> aec_dump_applied_input_level_ = 0;
protected:
size_t output_reset_counter_ = 0;
@ -235,7 +235,7 @@ class AudioProcessingSimulator {
std::unique_ptr<WavWriter> linear_aec_output_file_writer_;
ApiCallStatistics api_call_statistics_;
std::ofstream residual_echo_likelihood_graph_writer_;
int analog_mic_level_;
int applied_input_volume_;
FakeRecordingDevice fake_recording_device_;
TaskQueueForTest worker_queue_;

View file

@ -121,7 +121,9 @@ void DebugDumpReplayer::OnStreamEvent(const audioproc::Stream& msg) {
// APM should have been created.
RTC_CHECK(apm_.get());
apm_->set_stream_analog_level(msg.applied_input_volume());
if (msg.has_applied_input_volume()) {
apm_->set_stream_analog_level(msg.applied_input_volume());
}
RTC_CHECK_EQ(AudioProcessing::kNoError,
apm_->set_stream_delay_ms(msg.delay()));