From cb1812199057c6962b4b2e72a69f077001b661c0 Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Wed, 26 Oct 2011 00:27:17 +0000 Subject: [PATCH] Add an unpacker tool for audioproc debug files. - It only unpacks audio data at the moment. - Also switch to Chrome's protoc.gypi for protobuf targets. This reduces the complexity of our targets. Review URL: http://webrtc-codereview.appspot.com/241009 git-svn-id: http://webrtc.googlecode.com/svn/trunk@817 4adac7df-926f-26a2-2b94-8c16560cd09d --- src/build/protoc.gypi | 97 ++++++++++++++ src/modules/audio_processing/apm_tests.gypi | 81 ++++-------- .../audio_processing/audio_processing.gypi | 76 +++-------- .../audio_processing/test/process_test.cc | 17 +-- src/modules/audio_processing/test/unpack.cc | 118 ++++++++++++++++++ 5 files changed, 261 insertions(+), 128 deletions(-) create mode 100644 src/build/protoc.gypi create mode 100644 src/modules/audio_processing/test/unpack.cc diff --git a/src/build/protoc.gypi b/src/build/protoc.gypi new file mode 100644 index 0000000000..70bf71e1c4 --- /dev/null +++ b/src/build/protoc.gypi @@ -0,0 +1,97 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# It was necessary to copy this file to WebRTC, because the path to +# build/common.gypi is different for the standalone and Chromium builds. Gyp +# doesn't permit conditional inclusion or variable expansion in include paths. +# http://code.google.com/p/gyp/wiki/InputFormatReference#Including_Other_Files + +# This file is meant to be included into a target to provide a rule +# to invoke protoc in a consistent manner. +# +# To use this, create a gyp target with the following form: +# { +# 'target_name': 'my_proto_lib', +# 'type': 'static_library', +# 'sources': [ +# 'foo.proto', +# 'bar.proto', +# ], +# 'variables': { +# # Optional, see below: 'proto_in_dir': '.' +# 'proto_out_dir': 'dir/for/my_proto_lib' +# }, +# 'includes': ['path/to/this/gypi/file'], +# } +# If necessary, you may add normal .cc files to the sources list or other gyp +# dependencies. The proto headers are guaranteed to be generated before any +# source files, even within this target, are compiled. +# +# The 'proto_in_dir' variable must be the relative path to the +# directory containing the .proto files. If left out, it defaults to '.'. +# +# The 'proto_out_dir' variable specifies the path suffix that output +# files are generated under. Targets that gyp-depend on my_proto_lib +# will be able to include the resulting proto headers with an include +# like: +# #include "dir/for/my_proto_lib/foo.pb.h" +# +# Implementation notes: +# A proto_out_dir of foo/bar produces +# <(SHARED_INTERMEDIATE_DIR)/protoc_out/foo/bar/{file1,file2}.pb.{cc,h} +# <(SHARED_INTERMEDIATE_DIR)/pyproto/foo/bar/{file1,file2}_pb2.py + +{ + 'variables': { + 'protoc': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)', + 'cc_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out/<(proto_out_dir)', + 'py_dir': '<(PRODUCT_DIR)/pyproto/<(proto_out_dir)', + 'proto_in_dir%': '.', + }, + 'rules': [ + { + 'rule_name': 'genproto', + 'extension': 'proto', + 'inputs': [ + '<(protoc)', + ], + 'outputs': [ + '<(py_dir)/<(RULE_INPUT_ROOT)_pb2.py', + '<(cc_dir)/<(RULE_INPUT_ROOT).pb.cc', + '<(cc_dir)/<(RULE_INPUT_ROOT).pb.h', + ], + 'action': [ + '<(protoc)', + '--proto_path=<(proto_in_dir)', + # Naively you'd use <(RULE_INPUT_PATH) here, but protoc requires + # --proto_path is a strict prefix of the path given as an argument. + '<(proto_in_dir)/<(RULE_INPUT_ROOT)<(RULE_INPUT_EXT)', + '--cpp_out=<(cc_dir)', + '--python_out=<(py_dir)', + ], + 'message': 'Generating C++ and Python code from <(RULE_INPUT_PATH)', + 'process_outputs_as_sources': 1, + }, + ], + 'dependencies': [ + '<(DEPTH)/third_party/protobuf/protobuf.gyp:protoc#host', + '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite', + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/protoc_out', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/protoc_out', + ] + }, + 'export_dependent_settings': [ + # The generated headers reference headers within protobuf_lite, + # so dependencies must be able to find those headers too. + '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite', + ], + # This target exports a hard dependency because it generates header + # files. + 'hard_dependency': 1, +} diff --git a/src/modules/audio_processing/apm_tests.gypi b/src/modules/audio_processing/apm_tests.gypi index d86ebd5c4a..2cf983e219 100644 --- a/src/modules/audio_processing/apm_tests.gypi +++ b/src/modules/audio_processing/apm_tests.gypi @@ -7,94 +7,59 @@ # be found in the AUTHORS file in the root of the source tree. { - 'variables': { - 'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out', - 'protoc_out_relpath': 'webrtc/audio_processing', - }, 'targets': [ { 'target_name': 'audioproc_unittest', 'type': 'executable', 'conditions': [ ['prefer_fixed_point==1', { - 'defines': ['WEBRTC_APM_UNIT_TEST_FIXED_PROFILE'], + 'defines': [ 'WEBRTC_APM_UNIT_TEST_FIXED_PROFILE' ], }, { - 'defines': ['WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE'], + 'defines': [ 'WEBRTC_APM_UNIT_TEST_FLOAT_PROFILE' ], }], ], 'dependencies': [ - 'audioproc_unittest_proto', 'audio_processing', + 'audioproc_unittest_proto', '<(webrtc_root)/common_audio/common_audio.gyp:spl', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/../test/test.gyp:test_support', '<(webrtc_root)/../testing/gtest.gyp:gtest', - '<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite', - ], - 'include_dirs': [ - '<(webrtc_root)/../testing/gtest/include', - '<(protoc_out_dir)', - ], - 'sources': [ - 'test/unit_test.cc', - '<(protoc_out_dir)/<(protoc_out_relpath)/unittest.pb.cc', - '<(protoc_out_dir)/<(protoc_out_relpath)/unittest.pb.h', ], + 'sources': [ 'test/unit_test.cc', ], }, { - # Protobuf compiler / generate rule for audioproc_unittest 'target_name': 'audioproc_unittest_proto', - 'type': 'none', + 'type': 'static_library', + 'sources': [ 'test/unittest.proto', ], 'variables': { - 'proto_relpath': - '<(webrtc_root)/modules/audio_processing/test', + 'proto_in_dir': 'test', + # Workaround to protect against gyp's pathname relativization when this + # file is included by modules.gyp. + 'proto_out_protected': 'webrtc/audio_processing', + 'proto_out_dir': '<(proto_out_protected)', }, - 'sources': [ - '<(proto_relpath)/unittest.proto', - ], - 'rules': [ - { - 'rule_name': 'genproto', - 'extension': 'proto', - 'inputs': [ - '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)', - ], - 'outputs': [ - '<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.cc', - '<(protoc_out_dir)/<(RULE_INPUT_ROOT).pb.h', - ], - 'action': [ - '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)', - '--proto_path=<(proto_relpath)', - '<(proto_relpath)/<(RULE_INPUT_NAME)', - '--cpp_out=<(protoc_out_dir)/<(protoc_out_relpath)', - ], - 'message': 'Generating C++ code from <(RULE_INPUT_PATH)', - }, - ], - 'dependencies': [ - '<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protoc#host', - ], - # This target exports a hard dependency because it generates header - # files. - 'hard_dependency': 1, + 'includes': [ '../../../build/protoc.gypi', ], }, { - 'target_name': 'audioproc_process_test', + 'target_name': 'audioproc', 'type': 'executable', 'dependencies': [ 'audio_processing', + 'audioproc_debug_proto', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/../testing/gtest.gyp:gtest', - '<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite', ], - 'include_dirs': [ - '<(webrtc_root)/../testing/gtest/include', - '<(protoc_out_dir)', - ], - 'sources': [ - 'test/process_test.cc', + 'sources': [ 'test/process_test.cc', ], + }, + { + 'target_name': 'unpack', + 'type': 'executable', + 'dependencies': [ + 'audioproc_debug_proto', + '<(webrtc_root)/../third_party/google-gflags/google-gflags.gyp:google-gflags', ], + 'sources': [ 'test/unpack.cc', ], }, ], } diff --git a/src/modules/audio_processing/audio_processing.gypi b/src/modules/audio_processing/audio_processing.gypi index a10745881f..4bc48fcc62 100644 --- a/src/modules/audio_processing/audio_processing.gypi +++ b/src/modules/audio_processing/audio_processing.gypi @@ -7,34 +7,21 @@ # be found in the AUTHORS file in the root of the source tree. { - 'variables': { - 'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out', - 'protoc_out_relpath': 'webrtc/audio_processing', - }, 'targets': [ { 'target_name': 'audio_processing', 'type': '<(library)', 'conditions': [ ['prefer_fixed_point==1', { - 'dependencies': ['ns_fix'], - 'defines': ['WEBRTC_NS_FIXED'], + 'dependencies': [ 'ns_fix' ], + 'defines': [ 'WEBRTC_NS_FIXED' ], }, { - 'dependencies': ['ns'], - 'defines': ['WEBRTC_NS_FLOAT'], - }], - ['build_with_chromium==1', { - 'dependencies': [ - '<(webrtc_root)/../protobuf/protobuf.gyp:protobuf_lite', - ], - }, { - 'dependencies': [ - '<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protobuf_lite', - ], + 'dependencies': [ 'ns' ], + 'defines': [ 'WEBRTC_NS_FLOAT' ], }], ], 'dependencies': [ - 'debug_proto', + 'audioproc_debug_proto', 'aec', 'aecm', 'agc', @@ -45,7 +32,6 @@ 'include_dirs': [ 'interface', '../interface', - '<(protoc_out_dir)', ], 'direct_dependent_settings': { 'include_dirs': [ @@ -77,54 +63,20 @@ 'processing_component.h', 'voice_detection_impl.cc', 'voice_detection_impl.h', - '<(protoc_out_dir)/<(protoc_out_relpath)/debug.pb.cc', - '<(protoc_out_dir)/<(protoc_out_relpath)/debug.pb.h', ], }, { - # Protobuf compiler / generate rule for audio_processing - 'target_name': 'debug_proto', - 'type': 'none', + 'target_name': 'audioproc_debug_proto', + 'type': 'static_library', + 'sources': [ 'debug.proto', ], 'variables': { - 'proto_relpath': '<(webrtc_root)/modules/audio_processing', + 'proto_in_dir': '.', + # Workaround to protect against gyp's pathname relativization when this + # file is included by modules.gyp. + 'proto_out_protected': 'webrtc/audio_processing', + 'proto_out_dir': '<(proto_out_protected)', }, - 'sources': [ - '<(proto_relpath)/debug.proto', - ], - 'rules': [ - { - 'rule_name': 'genproto', - 'extension': 'proto', - 'inputs': [ - '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)', - ], - 'outputs': [ - '<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.cc', - '<(protoc_out_dir)/<(protoc_out_relpath)/<(RULE_INPUT_ROOT).pb.h', - ], - 'action': [ - '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)', - '--proto_path=<(proto_relpath)', - '<(proto_relpath)/<(RULE_INPUT_NAME)', - '--cpp_out=<(protoc_out_dir)/<(protoc_out_relpath)', - ], - 'message': 'Generating C++ code from <(RULE_INPUT_PATH)', - }, - ], - 'conditions': [ - ['build_with_chromium==1', { - 'dependencies': [ - '<(webrtc_root)/../protobuf/protobuf.gyp:protoc#host', - ], - }, { - 'dependencies': [ - '<(webrtc_root)/../third_party/protobuf/protobuf.gyp:protoc#host', - ], - }], - ], - # This target exports a hard dependency because it generates header - # files. - 'hard_dependency': 1, + 'includes': [ '../../../build/protoc.gypi', ], }, ], } diff --git a/src/modules/audio_processing/test/process_test.cc b/src/modules/audio_processing/test/process_test.cc index 3e3c05968b..9e597563d0 100644 --- a/src/modules/audio_processing/test/process_test.cc +++ b/src/modules/audio_processing/test/process_test.cc @@ -44,7 +44,7 @@ bool ReadMessageFromFile(FILE* file, ::google::protobuf::MessageLite* msg) { // The "wire format" for the size is little-endian. // Assume process_test is running on a little-endian machine. - int32_t size; + int32_t size = 0; if (fread(&size, sizeof(int32_t), 1, file) != 1) { return false; } @@ -116,11 +116,12 @@ void usage() { printf(" --vad_out_file FILE\n"); printf("\n"); printf("Modifiers:\n"); - printf(" --noasm Disable SSE optimization.\n"); - printf(" --perf Measure performance.\n"); - printf(" --quiet Suppress text output.\n"); - printf(" --no_progress Suppress progress.\n"); - printf(" --version Print version information and exit.\n"); + printf(" --noasm Disable SSE optimization.\n"); + printf(" --perf Measure performance.\n"); + printf(" --quiet Suppress text output.\n"); + printf(" --no_progress Suppress progress.\n"); + printf(" --debug_file FILE Dump a debug recording.\n"); + printf(" --version Print version information and exit.\n"); } // void function for gtest. @@ -359,9 +360,9 @@ void void_main(int argc, char* argv[]) { printf("%s\n", version); return; - } else if (strcmp(argv[i], "--debug_recording") == 0) { + } else if (strcmp(argv[i], "--debug_file") == 0) { i++; - ASSERT_LT(i, argc) << "Specify filename after --debug_recording"; + ASSERT_LT(i, argc) << "Specify filename after --debug_file"; ASSERT_EQ(apm->kNoError, apm->StartDebugRecording(argv[i])); } else { FAIL() << "Unrecognized argument " << argv[i]; diff --git a/src/modules/audio_processing/test/unpack.cc b/src/modules/audio_processing/test/unpack.cc new file mode 100644 index 0000000000..2db0d35d20 --- /dev/null +++ b/src/modules/audio_processing/test/unpack.cc @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 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. + */ + +// Commandline tool to unpack audioproc debug files. +// +// The debug files are dumped as protobuf blobs. For analysis, it's necessary +// to unpack the file into its component parts: audio and other data. + +#include + +#include "google/gflags.h" +#include "webrtc/audio_processing/debug.pb.h" + +using webrtc::audioproc::Event; +using webrtc::audioproc::ReverseStream; +using webrtc::audioproc::Stream; + +// TODO(andrew): unpack more of the data. +DEFINE_string(input_file, "input.pcm", "The name of the input stream file."); +DEFINE_string(output_file, "output.pcm", "The name of the output stream file."); +DEFINE_string(reverse_file, "reverse.pcm", "The name of the reverse input " + "file."); + +// TODO(andrew): move this to a helper class to share with process_test.cc? +// Returns true on success, false on error or end-of-file. +bool ReadMessageFromFile(FILE* file, + ::google::protobuf::MessageLite* msg) { + // The "wire format" for the size is little-endian. + // Assume process_test is running on a little-endian machine. + int32_t size = 0; + if (fread(&size, sizeof(int32_t), 1, file) != 1) { + return false; + } + if (size <= 0) { + return false; + } + size_t usize = static_cast(size); + + char array[usize]; + if (fread(array, sizeof(char), usize, file) != usize) { + return false; + } + + msg->Clear(); + return msg->ParseFromArray(array, usize); +} + +int main(int argc, char* argv[]) { + std::string program_name = argv[0]; + std::string usage = "Commandline tool to unpack audioproc debug files.\n" + "Example usage:\n" + program_name + " debug_dump.pb\n"; + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (argc < 2) { + printf("%s", google::ProgramUsage()); + return 1; + } + + FILE* debug_file = fopen(argv[1], "rb"); + FILE* input_file = fopen(FLAGS_input_file.c_str(), "wb"); + FILE* output_file = fopen(FLAGS_output_file.c_str(), "wb"); + FILE* reverse_file = fopen(FLAGS_reverse_file.c_str(), "wb"); + Event event_msg; + while (ReadMessageFromFile(debug_file, &event_msg)) { + if (event_msg.type() == Event::REVERSE_STREAM) { + if (!event_msg.has_reverse_stream()) { + printf("Corrupted input file: ReverseStream missing.\n"); + return 1; + } + + const ReverseStream msg = event_msg.reverse_stream(); + if (!msg.has_data()) { + printf("Corrupted input file: ReverseStream::data missing.\n"); + return 1; + } + if (fwrite(msg.data().data(), msg.data().size(), 1, reverse_file) != 1) { + printf("Error when writing to %s\n", FLAGS_reverse_file.c_str()); + return 1; + } + } else if (event_msg.type() == Event::STREAM) { + if (!event_msg.has_stream()) { + printf("Corrupted input file: Stream missing.\n"); + return 1; + } + + const Stream msg = event_msg.stream(); + if (!msg.has_input_data()) { + printf("Corrupted input file: Stream::input_data missing.\n"); + return 1; + } + if (fwrite(msg.input_data().data(), msg.input_data().size(), 1, + input_file) != 1) { + printf("Error when writing to %s\n", FLAGS_input_file.c_str()); + return 1; + } + + if (!msg.has_output_data()) { + printf("Corrupted input file: Stream::output_data missing.\n"); + return 1; + } + if (fwrite(msg.output_data().data(), msg.output_data().size(), 1, + output_file) != 1) { + printf("Error when writing to %s\n", FLAGS_output_file.c_str()); + return 1; + } + } + } + + return 0; +}