diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index 6f1b855150..dbbed49c1c 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -473,6 +473,7 @@ if (rtc_include_tests) { ] deps = [ + "../api:function_view", "../common_audio", "../modules/audio_processing", "../modules/audio_processing:audioproc_debug_proto", diff --git a/rtc_tools/unpack_aecdump/unpack.cc b/rtc_tools/unpack_aecdump/unpack.cc index 78d8a118e1..88593fb44f 100644 --- a/rtc_tools/unpack_aecdump/unpack.cc +++ b/rtc_tools/unpack_aecdump/unpack.cc @@ -19,7 +19,9 @@ #include #include #include +#include +#include "api/function_view.h" #include "common_audio/wav_file.h" #include "modules/audio_processing/test/protobuf_utils.h" #include "modules/audio_processing/test/test_utils.h" @@ -103,6 +105,108 @@ bool WritingCallOrderFile() { return FLAG_full; } +bool WritingRuntimeSettingFiles() { + return FLAG_full; +} + +// Exports RuntimeSetting AEC dump events to Audacity-readable files. +// This class is not RAII compliant. +class RuntimeSettingWriter { + public: + RuntimeSettingWriter( + std::string name, + rtc::FunctionView is_exporter_for, + rtc::FunctionView get_timeline_label) + : setting_name_(std::move(name)), + is_exporter_for_(is_exporter_for), + get_timeline_label_(get_timeline_label) {} + ~RuntimeSettingWriter() { Flush(); } + + bool IsExporterFor(const Event& event) const { + return is_exporter_for_(event); + } + + // Writes to file the payload of |event| using |frame_count| to calculate + // timestamp. + void WriteEvent(const Event& event, int frame_count) { + RTC_DCHECK(is_exporter_for_(event)); + if (file_ == nullptr) { + rtc::StringBuilder file_name; + file_name << setting_name_ << frame_offset_ << ".txt"; + file_ = OpenFile(file_name.str(), "wb"); + } + + // Time in the current WAV file, in seconds. + double time = (frame_count - frame_offset_) / 100.0; + std::string label = get_timeline_label_(event); + // In Audacity, all annotations are encoded as intervals. + fprintf(file_, "%.6f\t%.6f\t%s \n", time, time, label.c_str()); + } + + // Handles an AEC dump initialization event, occurring at frame + // |frame_offset|. + void HandleInitEvent(int frame_offset) { + Flush(); + frame_offset_ = frame_offset; + } + + private: + void Flush() { + if (file_ != nullptr) { + fclose(file_); + file_ = nullptr; + } + } + + FILE* file_ = nullptr; + int frame_offset_ = 0; + const std::string setting_name_; + const rtc::FunctionView is_exporter_for_; + const rtc::FunctionView get_timeline_label_; +}; + +// Returns RuntimeSetting exporters for runtime setting types defined in +// debug.proto. +std::vector RuntimeSettingWriters() { + return { + RuntimeSettingWriter( + "CapturePreGain", + [](const Event& event) -> bool { + return event.runtime_setting().has_capture_pre_gain(); + }, + [](const Event& event) -> std::string { + return std::to_string(event.runtime_setting().capture_pre_gain()); + }), + RuntimeSettingWriter( + "CustomRenderProcessingRuntimeSetting", + [](const Event& event) -> bool { + return event.runtime_setting() + .has_custom_render_processing_setting(); + }, + [](const Event& event) -> std::string { + return std::to_string( + event.runtime_setting().custom_render_processing_setting()); + }), + RuntimeSettingWriter( + "CaptureFixedPostGain", + [](const Event& event) -> bool { + return event.runtime_setting().has_capture_fixed_post_gain(); + }, + [](const Event& event) -> std::string { + return std::to_string( + event.runtime_setting().capture_fixed_post_gain()); + }), + RuntimeSettingWriter( + "PlayoutVolumeChange", + [](const Event& event) -> bool { + return event.runtime_setting().has_playout_volume_change(); + }, + [](const Event& event) -> std::string { + return std::to_string( + event.runtime_setting().playout_volume_change()); + })}; +} + } // namespace int do_main(int argc, char* argv[]) { @@ -146,6 +250,9 @@ int do_main(int argc, char* argv[]) { : nullptr; FILE* settings_file = OpenFile(FLAG_settings_file, "wb"); + std::vector runtime_setting_writers = + RuntimeSettingWriters(); + while (ReadMessageFromFile(debug_file, &event_msg)) { if (event_msg.type() == Event::REVERSE_STREAM) { if (!event_msg.has_reverse_stream()) { @@ -386,6 +493,20 @@ int do_main(int argc, char* argv[]) { callorder_name << FLAG_callorder_file << frame_count << ".char"; callorder_char_file = OpenFile(callorder_name.str(), "wb"); } + + if (WritingRuntimeSettingFiles()) { + for (RuntimeSettingWriter& writer : runtime_setting_writers) { + writer.HandleInitEvent(frame_count); + } + } + } + } else if (event_msg.type() == Event::RUNTIME_SETTING) { + if (WritingRuntimeSettingFiles()) { + for (RuntimeSettingWriter& writer : runtime_setting_writers) { + if (writer.IsExporterFor(event_msg)) { + writer.WriteEvent(event_msg, frame_count); + } + } } } }