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
This commit is contained in:
andrew@webrtc.org 2011-10-26 00:27:17 +00:00
parent fc9bcef8c7
commit cb18121990
5 changed files with 261 additions and 128 deletions

97
src/build/protoc.gypi Normal file
View File

@ -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,
}

View File

@ -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', ],
},
],
}

View File

@ -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', ],
},
],
}

View File

@ -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];

View File

@ -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 <stdio.h>
#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_t>(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;
}