1/*
2 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11// Commandline tool to unpack audioproc debug files.
12//
13// The debug files are dumped as protobuf blobs. For analysis, it's necessary
14// to unpack the file into its component parts: audio and other data.
15
16#include <stdio.h>
17
18#include "google/gflags.h"
19#include "scoped_ptr.h"
20#include "typedefs.h"
21#include "webrtc/audio_processing/debug.pb.h"
22
23using webrtc::scoped_array;
24
25using webrtc::audioproc::Event;
26using webrtc::audioproc::ReverseStream;
27using webrtc::audioproc::Stream;
28using webrtc::audioproc::Init;
29
30// TODO(andrew): unpack more of the data.
31DEFINE_string(input_file, "input.pcm", "The name of the input stream file.");
32DEFINE_string(output_file, "ref_out.pcm",
33              "The name of the reference output stream file.");
34DEFINE_string(reverse_file, "reverse.pcm",
35              "The name of the reverse input stream file.");
36DEFINE_string(delay_file, "delay.int32", "The name of the delay file.");
37DEFINE_string(drift_file, "drift.int32", "The name of the drift file.");
38DEFINE_string(level_file, "level.int32", "The name of the level file.");
39DEFINE_string(settings_file, "settings.txt", "The name of the settings file.");
40DEFINE_bool(full, false,
41            "Unpack the full set of files (normally not needed).");
42
43// TODO(andrew): move this to a helper class to share with process_test.cc?
44// Returns true on success, false on error or end-of-file.
45bool ReadMessageFromFile(FILE* file,
46                        ::google::protobuf::MessageLite* msg) {
47  // The "wire format" for the size is little-endian.
48  // Assume process_test is running on a little-endian machine.
49  int32_t size = 0;
50  if (fread(&size, sizeof(int32_t), 1, file) != 1) {
51    return false;
52  }
53  if (size <= 0) {
54    return false;
55  }
56  const size_t usize = static_cast<size_t>(size);
57
58  scoped_array<char> array(new char[usize]);
59  if (fread(array.get(), sizeof(char), usize, file) != usize) {
60    return false;
61  }
62
63  msg->Clear();
64  return msg->ParseFromArray(array.get(), usize);
65}
66
67int main(int argc, char* argv[]) {
68  std::string program_name = argv[0];
69  std::string usage = "Commandline tool to unpack audioproc debug files.\n"
70    "Example usage:\n" + program_name + " debug_dump.pb\n";
71  google::SetUsageMessage(usage);
72  google::ParseCommandLineFlags(&argc, &argv, true);
73
74  if (argc < 2) {
75    printf("%s", google::ProgramUsage());
76    return 1;
77  }
78
79  FILE* debug_file = fopen(argv[1], "rb");
80  if (debug_file == NULL) {
81    printf("Unable to open %s\n", argv[1]);
82    return 1;
83  }
84  FILE* input_file = fopen(FLAGS_input_file.c_str(), "wb");
85  if (input_file == NULL) {
86    printf("Unable to open %s\n", FLAGS_input_file.c_str());
87    return 1;
88  }
89  FILE* output_file = fopen(FLAGS_output_file.c_str(), "wb");
90  if (output_file == NULL) {
91    printf("Unable to open %s\n", FLAGS_output_file.c_str());
92    return 1;
93  }
94  FILE* reverse_file = fopen(FLAGS_reverse_file.c_str(), "wb");
95  if (reverse_file == NULL) {
96    printf("Unable to open %s\n", FLAGS_reverse_file.c_str());
97    return 1;
98  }
99  FILE* settings_file = fopen(FLAGS_settings_file.c_str(), "wb");
100  if (settings_file == NULL) {
101    printf("Unable to open %s\n", FLAGS_settings_file.c_str());
102    return 1;
103  }
104
105  FILE* delay_file = NULL;
106  FILE* drift_file = NULL;
107  FILE* level_file = NULL;
108  if (FLAGS_full) {
109    delay_file = fopen(FLAGS_delay_file.c_str(), "wb");
110    if (delay_file == NULL) {
111      printf("Unable to open %s\n", FLAGS_delay_file.c_str());
112      return 1;
113    }
114    drift_file = fopen(FLAGS_drift_file.c_str(), "wb");
115    if (drift_file == NULL) {
116      printf("Unable to open %s\n", FLAGS_drift_file.c_str());
117      return 1;
118    }
119    level_file = fopen(FLAGS_level_file.c_str(), "wb");
120    if (level_file == NULL) {
121      printf("Unable to open %s\n", FLAGS_level_file.c_str());
122      return 1;
123    }
124  }
125
126  Event event_msg;
127  int frame_count = 0;
128  while (ReadMessageFromFile(debug_file, &event_msg)) {
129    if (event_msg.type() == Event::REVERSE_STREAM) {
130      if (!event_msg.has_reverse_stream()) {
131        printf("Corrupted input file: ReverseStream missing.\n");
132        return 1;
133      }
134
135      const ReverseStream msg = event_msg.reverse_stream();
136      if (msg.has_data()) {
137        if (fwrite(msg.data().data(), msg.data().size(), 1, reverse_file) !=
138            1) {
139          printf("Error when writing to %s\n", FLAGS_reverse_file.c_str());
140          return 1;
141        }
142      }
143    } else if (event_msg.type() == Event::STREAM) {
144      frame_count++;
145      if (!event_msg.has_stream()) {
146        printf("Corrupted input file: Stream missing.\n");
147        return 1;
148      }
149
150      const Stream msg = event_msg.stream();
151      if (msg.has_input_data()) {
152        if (fwrite(msg.input_data().data(), msg.input_data().size(), 1,
153                   input_file) != 1) {
154          printf("Error when writing to %s\n", FLAGS_input_file.c_str());
155          return 1;
156        }
157      }
158
159      if (msg.has_output_data()) {
160        if (fwrite(msg.output_data().data(), msg.output_data().size(), 1,
161                   output_file) != 1) {
162          printf("Error when writing to %s\n", FLAGS_output_file.c_str());
163          return 1;
164        }
165      }
166
167      if (FLAGS_full) {
168        if (msg.has_delay()) {
169          int32_t delay = msg.delay();
170          if (fwrite(&delay, sizeof(int32_t), 1, delay_file) != 1) {
171            printf("Error when writing to %s\n", FLAGS_delay_file.c_str());
172            return 1;
173          }
174        }
175
176        if (msg.has_drift()) {
177          int32_t drift = msg.drift();
178          if (fwrite(&drift, sizeof(int32_t), 1, drift_file) != 1) {
179            printf("Error when writing to %s\n", FLAGS_drift_file.c_str());
180            return 1;
181          }
182        }
183
184        if (msg.has_level()) {
185          int32_t level = msg.level();
186          if (fwrite(&level, sizeof(int32_t), 1, level_file) != 1) {
187            printf("Error when writing to %s\n", FLAGS_level_file.c_str());
188            return 1;
189          }
190        }
191      }
192    } else if (event_msg.type() == Event::INIT) {
193      if (!event_msg.has_init()) {
194        printf("Corrupted input file: Init missing.\n");
195        return 1;
196      }
197
198      const Init msg = event_msg.init();
199      // These should print out zeros if they're missing.
200      fprintf(settings_file, "Init at frame: %d\n", frame_count);
201      fprintf(settings_file, "  Sample rate: %d\n", msg.sample_rate());
202      fprintf(settings_file, "  Device sample rate: %d\n",
203              msg.device_sample_rate());
204      fprintf(settings_file, "  Input channels: %d\n",
205              msg.num_input_channels());
206      fprintf(settings_file, "  Output channels: %d\n",
207              msg.num_output_channels());
208      fprintf(settings_file, "  Reverse channels: %d\n",
209              msg.num_reverse_channels());
210
211      fprintf(settings_file, "\n");
212    }
213  }
214
215  return 0;
216}
217