diff --git a/webrtc/modules/audio_processing/BUILD.gn b/webrtc/modules/audio_processing/BUILD.gn index ea30a54d41..86df81da5c 100644 --- a/webrtc/modules/audio_processing/BUILD.gn +++ b/webrtc/modules/audio_processing/BUILD.gn @@ -234,6 +234,7 @@ rtc_static_library("audio_processing") { defines = [] deps = [ + ":aec_dump_interface", "..:module_api", "../..:webrtc_common", "../../audio/utility:audio_frame_operations", @@ -308,6 +309,17 @@ rtc_static_library("audio_processing") { ] } +rtc_source_set("aec_dump_interface") { + sources = [ + "include/aec_dump.cc", + "include/aec_dump.h", + ] + + deps = [ + "../../base:rtc_base_approved", + ] +} + rtc_source_set("audio_processing_c") { visibility = [ ":*" ] # Only targets in this file can depend on this. sources = [ diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc index 0786ead34f..a96f8ff8ab 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.cc +++ b/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -157,6 +157,23 @@ class HighPassFilterImpl : public HighPassFilter { RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(HighPassFilterImpl); }; +webrtc::InternalAPMStreamsConfig ToStreamsConfig( + const ProcessingConfig& api_format) { + webrtc::InternalAPMStreamsConfig result; + result.input_sample_rate = api_format.input_stream().sample_rate_hz(); + result.input_num_channels = api_format.input_stream().num_channels(); + result.output_num_channels = api_format.output_stream().num_channels(); + result.render_input_num_channels = + api_format.reverse_input_stream().num_channels(); + result.render_input_sample_rate = + api_format.reverse_input_stream().sample_rate_hz(); + result.output_sample_rate = api_format.output_stream().sample_rate_hz(); + result.render_output_sample_rate = + api_format.reverse_output_stream().sample_rate_hz(); + result.render_output_num_channels = + api_format.reverse_output_stream().num_channels(); + return result; +} } // namespace // Throughout webrtc, it's assumed that success is represented by zero. @@ -541,7 +558,9 @@ int AudioProcessingImpl::InitializeLocked() { } } #endif - + if (aec_dump_) { + aec_dump_->WriteInitMessage(ToStreamsConfig(formats_.api_format)); + } return kNoError; } @@ -867,6 +886,10 @@ int AudioProcessingImpl::ProcessStream(const float* const* src, } #endif + if (aec_dump_) { + RecordUnprocessedCaptureStream(src); + } + capture_.capture_audio->CopyFrom(src, formats_.api_format.input_stream()); RETURN_ON_ERR(ProcessCaptureStreamLocked()); capture_.capture_audio->CopyTo(formats_.api_format.output_stream(), dest); @@ -884,7 +907,9 @@ int AudioProcessingImpl::ProcessStream(const float* const* src, &crit_debug_, &debug_dump_.capture)); } #endif - + if (aec_dump_) { + RecordProcessedCaptureStream(dest); + } return kNoError; } @@ -1123,6 +1148,10 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { return kBadDataLengthError; } + if (aec_dump_) { + RecordUnprocessedCaptureStream(*frame); + } + #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP if (debug_dump_.debug_file->is_open()) { RETURN_ON_ERR(WriteConfigMessage(false)); @@ -1141,6 +1170,9 @@ int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { frame, submodule_states_.CaptureMultiBandProcessingActive() || submodule_states_.CaptureFullBandProcessingActive()); + if (aec_dump_) { + RecordProcessedCaptureStream(*frame); + } #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP if (debug_dump_.debug_file->is_open()) { audioproc::Stream* msg = debug_dump_.capture.event_msg->mutable_stream(); @@ -1426,7 +1458,14 @@ int AudioProcessingImpl::AnalyzeReverseStreamLocked( &crit_debug_, &debug_dump_.render)); } #endif - + if (aec_dump_) { + const size_t channel_size = + formats_.api_format.reverse_input_stream().num_frames(); + const size_t num_channels = + formats_.api_format.reverse_input_stream().num_channels(); + aec_dump_->WriteRenderStreamMessage( + FloatAudioFrame(src, num_channels, channel_size)); + } render_.render_audio->CopyFrom(src, formats_.api_format.reverse_input_stream()); return ProcessRenderStreamLocked(); @@ -1479,6 +1518,10 @@ int AudioProcessingImpl::ProcessReverseStream(AudioFrame* frame) { &crit_debug_, &debug_dump_.render)); } #endif + if (aec_dump_) { + aec_dump_->WriteRenderStreamMessage(*frame); + } + render_.render_audio->DeinterleaveFrom(frame); RETURN_ON_ERR(ProcessRenderStreamLocked()); render_.render_audio->InterleaveTo( @@ -1568,6 +1611,30 @@ int AudioProcessingImpl::delay_offset_ms() const { return capture_.delay_offset_ms; } +void AudioProcessingImpl::AttachAecDump(std::unique_ptr aec_dump) { + RTC_DCHECK(aec_dump); + rtc::CritScope cs_render(&crit_render_); + rtc::CritScope cs_capture(&crit_capture_); + + // The previously attached AecDump will be destroyed with the + // 'aec_dump' parameter, which is after locks are released. + aec_dump_.swap(aec_dump); + WriteAecDumpConfigMessage(true); + aec_dump_->WriteInitMessage(ToStreamsConfig(formats_.api_format)); +} + +void AudioProcessingImpl::DetachAecDump() { + // The d-tor of a task-queue based AecDump blocks until all pending + // tasks are done. This construction avoids blocking while holding + // the render and capture locks. + std::unique_ptr aec_dump = nullptr; + { + rtc::CritScope cs_render(&crit_render_); + rtc::CritScope cs_capture(&crit_capture_); + aec_dump = std::move(aec_dump_); + } +} + int AudioProcessingImpl::StartDebugRecording( const char filename[AudioProcessing::kMaxFilenameSize], int64_t max_log_size_bytes) { @@ -1639,11 +1706,12 @@ int AudioProcessingImpl::StartDebugRecordingForPlatformFile( } int AudioProcessingImpl::StopDebugRecording() { + DetachAecDump(); + +#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP // Run in a single-threaded manner. rtc::CritScope cs_render(&crit_render_); rtc::CritScope cs_capture(&crit_capture_); - -#ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP // We just return if recording hasn't started. debug_dump_.debug_file->CloseFile(); return kNoError; @@ -1904,6 +1972,121 @@ void AudioProcessingImpl::UpdateHistogramsOnCallEnd() { capture_.last_aec_system_delay_ms = 0; } +void AudioProcessingImpl::WriteAecDumpConfigMessage(bool forced) { + if (!aec_dump_) { + return; + } + std::string experiments_description = + public_submodules_->echo_cancellation->GetExperimentsDescription(); + // TODO(peah): Add semicolon-separated concatenations of experiment + // descriptions for other submodules. + if (capture_nonlocked_.level_controller_enabled) { + experiments_description += "LevelController;"; + } + if (constants_.agc_clipped_level_min != kClippedLevelMin) { + experiments_description += "AgcClippingLevelExperiment;"; + } + if (capture_nonlocked_.echo_canceller3_enabled) { + experiments_description += "EchoCanceller3;"; + } + + InternalAPMConfig apm_config; + + apm_config.aec_enabled = public_submodules_->echo_cancellation->is_enabled(); + apm_config.aec_delay_agnostic_enabled = + public_submodules_->echo_cancellation->is_delay_agnostic_enabled(); + apm_config.aec_drift_compensation_enabled = + public_submodules_->echo_cancellation->is_drift_compensation_enabled(); + apm_config.aec_extended_filter_enabled = + public_submodules_->echo_cancellation->is_extended_filter_enabled(); + apm_config.aec_suppression_level = static_cast( + public_submodules_->echo_cancellation->suppression_level()); + + apm_config.aecm_enabled = + public_submodules_->echo_control_mobile->is_enabled(); + apm_config.aecm_comfort_noise_enabled = + public_submodules_->echo_control_mobile->is_comfort_noise_enabled(); + apm_config.aecm_routing_mode = + static_cast(public_submodules_->echo_control_mobile->routing_mode()); + + apm_config.agc_enabled = public_submodules_->gain_control->is_enabled(); + apm_config.agc_mode = + static_cast(public_submodules_->gain_control->mode()); + apm_config.agc_limiter_enabled = + public_submodules_->gain_control->is_limiter_enabled(); + apm_config.noise_robust_agc_enabled = constants_.use_experimental_agc; + + apm_config.hpf_enabled = config_.high_pass_filter.enabled; + + apm_config.ns_enabled = public_submodules_->noise_suppression->is_enabled(); + apm_config.ns_level = + static_cast(public_submodules_->noise_suppression->level()); + + apm_config.transient_suppression_enabled = + capture_.transient_suppressor_enabled; + apm_config.intelligibility_enhancer_enabled = + capture_nonlocked_.intelligibility_enabled; + apm_config.experiments_description = experiments_description; + + if (!forced && apm_config == apm_config_for_aec_dump_) { + return; + } + aec_dump_->WriteConfig(apm_config); + apm_config_for_aec_dump_ = apm_config; +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const float* const* src) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + const size_t channel_size = formats_.api_format.input_stream().num_frames(); + const size_t num_channels = formats_.api_format.input_stream().num_channels(); + aec_dump_->AddCaptureStreamInput( + FloatAudioFrame(src, num_channels, channel_size)); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordUnprocessedCaptureStream( + const AudioFrame& capture_frame) { + RTC_DCHECK(aec_dump_); + WriteAecDumpConfigMessage(false); + + aec_dump_->AddCaptureStreamInput(capture_frame); + RecordAudioProcessingState(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const float* const* processed_capture_stream) { + RTC_DCHECK(aec_dump_); + + const size_t channel_size = formats_.api_format.output_stream().num_frames(); + const size_t num_channels = + formats_.api_format.output_stream().num_channels(); + aec_dump_->AddCaptureStreamOutput( + FloatAudioFrame(processed_capture_stream, num_channels, channel_size)); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordProcessedCaptureStream( + const AudioFrame& processed_capture_frame) { + RTC_DCHECK(aec_dump_); + + aec_dump_->AddCaptureStreamOutput(processed_capture_frame); + aec_dump_->WriteCaptureStreamMessage(); +} + +void AudioProcessingImpl::RecordAudioProcessingState() { + RTC_DCHECK(aec_dump_); + AecDump::AudioProcessingState audio_proc_state; + audio_proc_state.delay = capture_nonlocked_.stream_delay_ms; + audio_proc_state.drift = + public_submodules_->echo_cancellation->stream_drift_samples(); + audio_proc_state.level = gain_control()->stream_analog_level(); + audio_proc_state.keypress = capture_.key_pressed; + aec_dump_->AddAudioProcessingState(audio_proc_state); +} + #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP int AudioProcessingImpl::WriteMessageToDebugFile( FileWrapper* debug_file, diff --git a/webrtc/modules/audio_processing/audio_processing_impl.h b/webrtc/modules/audio_processing/audio_processing_impl.h index 0985257f83..b4128a98ca 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.h +++ b/webrtc/modules/audio_processing/audio_processing_impl.h @@ -23,6 +23,7 @@ #include "webrtc/base/swap_queue.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/modules/audio_processing/audio_buffer.h" +#include "webrtc/modules/audio_processing/include/aec_dump.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/audio_processing/render_queue_item_verifier.h" #include "webrtc/modules/audio_processing/rms_level.h" @@ -64,6 +65,8 @@ class AudioProcessingImpl : public AudioProcessing { void ApplyConfig(const AudioProcessing::Config& config) override; void SetExtraOptions(const webrtc::Config& config) override; void UpdateHistogramsOnCallEnd() override; + void AttachAecDump(std::unique_ptr aec_dump) override; + void DetachAecDump() override; int StartDebugRecording(const char filename[kMaxFilenameSize], int64_t max_log_size_bytes) override; int StartDebugRecording(FILE* handle, int64_t max_log_size_bytes) override; @@ -279,6 +282,34 @@ class AudioProcessingImpl : public AudioProcessing { EXCLUSIVE_LOCKS_REQUIRED(crit_render_); int ProcessRenderStreamLocked() EXCLUSIVE_LOCKS_REQUIRED(crit_render_); + // Collects configuration settings from public and private + // submodules to be saved as an audioproc::Config message on the + // AecDump if it is attached. If not |forced|, only writes the current + // config if it is different from the last saved one; if |forced|, + // writes the config regardless of the last saved. + void WriteAecDumpConfigMessage(bool forced) + EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + // Notifies attached AecDump of current configuration and capture data. + void RecordUnprocessedCaptureStream(const float* const* capture_stream) + EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + void RecordUnprocessedCaptureStream(const AudioFrame& capture_frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + // Notifies attached AecDump of current configuration and + // processed capture data and issues a capture stream recording + // request. + void RecordProcessedCaptureStream( + const float* const* processed_capture_stream) + EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + void RecordProcessedCaptureStream(const AudioFrame& processed_capture_frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + // Notifies attached AecDump about current state (delay, drift, etc). + void RecordAudioProcessingState() EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + // Debug dump methods that are internal and called without locks. // TODO(peah): Make thread safe. #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP @@ -303,6 +334,14 @@ class AudioProcessingImpl : public AudioProcessing { ApmDebugDumpState debug_dump_; #endif + // AecDump instance used for optionally logging APM config, input + // and output to file in the AEC-dump format defined in debug.proto. + std::unique_ptr aec_dump_; + + // Hold the last config written with AecDump for avoiding writing + // the same config twice. + InternalAPMConfig apm_config_for_aec_dump_ GUARDED_BY(crit_capture_); + // Critical sections. rtc::CriticalSection crit_render_ ACQUIRED_BEFORE(crit_capture_); rtc::CriticalSection crit_capture_; diff --git a/webrtc/modules/audio_processing/include/aec_dump.cc b/webrtc/modules/audio_processing/include/aec_dump.cc new file mode 100644 index 0000000000..f048badf73 --- /dev/null +++ b/webrtc/modules/audio_processing/include/aec_dump.cc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_processing/include/aec_dump.h" + +namespace webrtc { +InternalAPMConfig::InternalAPMConfig() = default; +InternalAPMConfig::InternalAPMConfig(const InternalAPMConfig&) = default; +InternalAPMConfig::InternalAPMConfig(InternalAPMConfig&&) = default; +InternalAPMConfig& InternalAPMConfig::operator=(const InternalAPMConfig&) = + default; + +bool InternalAPMConfig::operator==(const InternalAPMConfig& other) { + return aec_enabled == other.aec_enabled && + aec_delay_agnostic_enabled == other.aec_delay_agnostic_enabled && + aec_drift_compensation_enabled == + other.aec_drift_compensation_enabled && + aec_extended_filter_enabled == other.aec_extended_filter_enabled && + aec_suppression_level == other.aec_suppression_level && + aecm_enabled == other.aecm_enabled && + aecm_comfort_noise_enabled == other.aecm_comfort_noise_enabled && + aecm_routing_mode == other.aecm_routing_mode && + agc_enabled == other.agc_enabled && agc_mode == other.agc_mode && + agc_limiter_enabled == other.agc_limiter_enabled && + hpf_enabled == other.hpf_enabled && ns_enabled == other.ns_enabled && + ns_level == other.ns_level && + transient_suppression_enabled == other.transient_suppression_enabled && + intelligibility_enhancer_enabled == + other.intelligibility_enhancer_enabled && + noise_robust_agc_enabled == other.noise_robust_agc_enabled && + experiments_description == other.experiments_description; +} +} // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/aec_dump.h b/webrtc/modules/audio_processing/include/aec_dump.h new file mode 100644 index 0000000000..2640f07284 --- /dev/null +++ b/webrtc/modules/audio_processing/include/aec_dump.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ + +#include +#include +#include + +#include "webrtc/base/array_view.h" + +namespace webrtc { + +class AudioFrame; + +// Struct for passing current config from APM without having to +// include protobuf headers. +struct InternalAPMConfig { + InternalAPMConfig(); + InternalAPMConfig(const InternalAPMConfig&); + InternalAPMConfig(InternalAPMConfig&&); + + InternalAPMConfig& operator=(const InternalAPMConfig&); + InternalAPMConfig& operator=(InternalAPMConfig&&) = delete; + + bool operator==(const InternalAPMConfig& other); + + bool aec_enabled = false; + bool aec_delay_agnostic_enabled = false; + bool aec_drift_compensation_enabled = false; + bool aec_extended_filter_enabled = false; + int aec_suppression_level = 0; + bool aecm_enabled = false; + bool aecm_comfort_noise_enabled = false; + int aecm_routing_mode = 0; + bool agc_enabled = false; + int agc_mode = 0; + bool agc_limiter_enabled = false; + bool hpf_enabled = false; + bool ns_enabled = false; + int ns_level = 0; + bool transient_suppression_enabled = false; + bool intelligibility_enhancer_enabled = false; + bool noise_robust_agc_enabled = false; + std::string experiments_description = ""; +}; + +struct InternalAPMStreamsConfig { + int input_sample_rate = 0; + int output_sample_rate = 0; + int render_input_sample_rate = 0; + int render_output_sample_rate = 0; + + size_t input_num_channels = 0; + size_t output_num_channels = 0; + size_t render_input_num_channels = 0; + size_t render_output_num_channels = 0; +}; + +// Class to pass audio data in float** format. This is to avoid +// dependence on AudioBuffer, and avoid problems associated with +// rtc::ArrayView. +class FloatAudioFrame { + public: + // |num_channels| and |channel_size| describe the float** + // |audio_samples|. |audio_samples| is assumed to point to a + // two-dimensional |num_channels * channel_size| array of floats. + FloatAudioFrame(const float* const* audio_samples, + size_t num_channels, + size_t channel_size) + : audio_samples_(audio_samples), + num_channels_(num_channels), + channel_size_(channel_size) {} + + FloatAudioFrame() = delete; + + size_t num_channels() const { return num_channels_; } + + rtc::ArrayView channel(size_t idx) const { + RTC_DCHECK_LE(0, idx); + RTC_DCHECK_LE(idx, num_channels_); + return rtc::ArrayView(audio_samples_[idx], channel_size_); + } + + private: + const float* const* audio_samples_; + size_t num_channels_; + size_t channel_size_; +}; + +// An interface for recording configuration and input/output streams +// of the Audio Processing Module. The recordings are called +// 'aec-dumps' and are stored in a protobuf format defined in +// debug.proto. +// The Write* methods are always safe to call concurrently or +// otherwise for all implementing subclasses. The intended mode of +// operation is to create a protobuf object from the input, and send +// it away to be written to file asynchronously. +class AecDump { + public: + struct AudioProcessingState { + int delay; + int drift; + int level; + bool keypress; + }; + + virtual ~AecDump() = default; + + // Logs Event::Type INIT message. + virtual void WriteInitMessage( + const InternalAPMStreamsConfig& streams_config) = 0; + + // Logs Event::Type STREAM message. To log an input/output pair, + // call the AddCapture* and AddAudioProcessingState methods followed + // by a WriteCaptureStreamMessage call. + virtual void AddCaptureStreamInput(const FloatAudioFrame& src) = 0; + virtual void AddCaptureStreamOutput(const FloatAudioFrame& src) = 0; + virtual void AddCaptureStreamInput(const AudioFrame& frame) = 0; + virtual void AddCaptureStreamOutput(const AudioFrame& frame) = 0; + virtual void AddAudioProcessingState(const AudioProcessingState& state) = 0; + virtual void WriteCaptureStreamMessage() = 0; + + // Logs Event::Type REVERSE_STREAM message. + virtual void WriteRenderStreamMessage(const AudioFrame& frame) = 0; + virtual void WriteRenderStreamMessage(const FloatAudioFrame& src) = 0; + + // Logs Event::Type CONFIG message. + virtual void WriteConfig(const InternalAPMConfig& config) = 0; +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_INCLUDE_AEC_DUMP_H_ diff --git a/webrtc/modules/audio_processing/include/audio_processing.cc b/webrtc/modules/audio_processing/include/audio_processing.cc index 9a3b84dd0c..eec4db6179 100644 --- a/webrtc/modules/audio_processing/include/audio_processing.cc +++ b/webrtc/modules/audio_processing/include/audio_processing.cc @@ -10,6 +10,11 @@ #include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_processing/include/aec_dump.h" +// TODO(aleloi): remove AecDump header usage when internal projcets +// have updated. See https://bugs.webrtc.org/7404. + namespace webrtc { Beamforming::Beamforming() @@ -31,4 +36,20 @@ Beamforming::Beamforming(bool enabled, Beamforming::~Beamforming() {} +// TODO(aleloi): make pure virtual when internal projects have +// updated. See https://bugs.webrtc.org/7404 +void AudioProcessing::AttachAecDump(std::unique_ptr aec_dump) { + RTC_NOTREACHED(); +} + +// If no AecDump is attached, this has no effect. If an AecDump is +// attached, it's destructor is called. The d-tor may block until +// all pending logging tasks are completed. +// +// TODO(aleloi): make pure virtual when internal projects have +// updated. See https://bugs.webrtc.org/7404 +void AudioProcessing::DetachAecDump() { + RTC_NOTREACHED(); +} + } // namespace webrtc diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h index e470d0da5c..33fdff24b6 100644 --- a/webrtc/modules/audio_processing/include/audio_processing.h +++ b/webrtc/modules/audio_processing/include/audio_processing.h @@ -29,6 +29,7 @@ namespace webrtc { struct AecCore; +class AecDump; class AudioFrame; class NonlinearBeamformer; @@ -455,6 +456,26 @@ class AudioProcessing { virtual void set_delay_offset_ms(int offset) = 0; virtual int delay_offset_ms() const = 0; + // Attaches provided webrtc::AecDump for recording debugging + // information. Log file and maximum file size logic is supposed to + // be handled by implementing instance of AecDump. Calling this + // method when another AecDump is attached resets the active AecDump + // with a new one. This causes the d-tor of the earlier AecDump to + // be called. The d-tor call may block until all pending logging + // tasks are completed. + // + // TODO(aleloi): make pure virtual when internal projects have + // updated. See https://bugs.webrtc.org/7404 + virtual void AttachAecDump(std::unique_ptr aec_dump); + + // If no AecDump is attached, this has no effect. If an AecDump is + // attached, it's destructor is called. The d-tor may block until + // all pending logging tasks are completed. + // + // TODO(aleloi): make pure virtual when internal projects have + // updated. See https://bugs.webrtc.org/7404 + virtual void DetachAecDump(); + // Starts recording debugging information to a file specified by |filename|, // a NULL-terminated string. If there is an ongoing recording, the old file // will be closed, and recording will continue in the newly specified file. diff --git a/webrtc/modules/audio_processing/include/mock_audio_processing.h b/webrtc/modules/audio_processing/include/mock_audio_processing.h index 4bda0bd090..d43351e1de 100644 --- a/webrtc/modules/audio_processing/include/mock_audio_processing.h +++ b/webrtc/modules/audio_processing/include/mock_audio_processing.h @@ -13,6 +13,7 @@ #include +#include "webrtc/modules/audio_processing/include/aec_dump.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/test/gmock.h" @@ -174,6 +175,10 @@ class MockAudioProcessing : public AudioProcessing { MOCK_METHOD1(set_stream_key_pressed, void(bool key_pressed)); MOCK_METHOD1(set_delay_offset_ms, void(int offset)); MOCK_CONST_METHOD0(delay_offset_ms, int()); + + virtual void AttachAecDump(std::unique_ptr aec_dump) {} + MOCK_METHOD0(DetachAecDump, void()); + MOCK_METHOD2(StartDebugRecording, int(const char filename[kMaxFilenameSize], int64_t max_log_size_bytes)); MOCK_METHOD2(StartDebugRecording, int(FILE* handle,