1 /*
2 * Copyright (c) 2014 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 #include "common_audio/wav_header.h"
12
13 #include <string.h>
14
15 #include <limits>
16
17 #include "test/gtest.h"
18
19 namespace webrtc {
20
21 // Doesn't take ownership of the buffer.
22 class WavHeaderBufferReader : public WavHeaderReader {
23 public:
WavHeaderBufferReader(const uint8_t * buf,size_t size,bool check_read_size)24 WavHeaderBufferReader(const uint8_t* buf, size_t size, bool check_read_size)
25 : buf_(buf),
26 size_(size),
27 pos_(0),
28 buf_exhausted_(false),
29 check_read_size_(check_read_size) {}
30
~WavHeaderBufferReader()31 ~WavHeaderBufferReader() override {
32 // Verify the entire buffer has been read.
33 if (check_read_size_)
34 EXPECT_EQ(size_, pos_);
35 }
36
Read(void * buf,size_t num_bytes)37 size_t Read(void* buf, size_t num_bytes) override {
38 EXPECT_FALSE(buf_exhausted_);
39
40 const size_t bytes_remaining = size_ - pos_;
41 if (num_bytes > bytes_remaining) {
42 // The caller is signalled about an exhausted buffer when we return fewer
43 // bytes than requested. There should not be another read attempt after
44 // this point.
45 buf_exhausted_ = true;
46 num_bytes = bytes_remaining;
47 }
48 memcpy(buf, &buf_[pos_], num_bytes);
49 pos_ += num_bytes;
50 return num_bytes;
51 }
52
SeekForward(uint32_t num_bytes)53 bool SeekForward(uint32_t num_bytes) override {
54 // Verify we don't try to read outside of a properly sized header.
55 if (size_ >= kPcmWavHeaderSize)
56 EXPECT_GE(size_, pos_ + num_bytes);
57 EXPECT_FALSE(buf_exhausted_);
58
59 const size_t bytes_remaining = size_ - pos_;
60 if (num_bytes > bytes_remaining) {
61 // Error: cannot seek beyond EOF.
62 return false;
63 }
64 if (num_bytes == bytes_remaining) {
65 // There should not be another read attempt after this point.
66 buf_exhausted_ = true;
67 }
68 pos_ += num_bytes;
69 return true;
70 }
71
GetPosition()72 int64_t GetPosition() override { return pos_; }
73
74 private:
75 const uint8_t* buf_;
76 const size_t size_;
77 size_t pos_;
78 bool buf_exhausted_;
79 const bool check_read_size_;
80 };
81
82 // Try various choices of WAV header parameters, and make sure that the good
83 // ones are accepted and the bad ones rejected.
TEST(WavHeaderTest,CheckWavParameters)84 TEST(WavHeaderTest, CheckWavParameters) {
85 // Try some really stupid values for one parameter at a time.
86 EXPECT_TRUE(CheckWavParameters(1, 8000, WavFormat::kWavFormatPcm, 0));
87 EXPECT_FALSE(CheckWavParameters(0, 8000, WavFormat::kWavFormatPcm, 0));
88 EXPECT_FALSE(CheckWavParameters(0x10000, 8000, WavFormat::kWavFormatPcm, 0));
89 EXPECT_FALSE(CheckWavParameters(1, 0, WavFormat::kWavFormatPcm, 0));
90
91 // Too large values.
92 EXPECT_FALSE(
93 CheckWavParameters(1 << 20, 1 << 20, WavFormat::kWavFormatPcm, 0));
94 EXPECT_FALSE(CheckWavParameters(1, 8000, WavFormat::kWavFormatPcm,
95 std::numeric_limits<uint32_t>::max()));
96
97 // Not the same number of samples for each channel.
98 EXPECT_FALSE(CheckWavParameters(3, 8000, WavFormat::kWavFormatPcm, 5));
99 }
100
TEST(WavHeaderTest,ReadWavHeaderWithErrors)101 TEST(WavHeaderTest, ReadWavHeaderWithErrors) {
102 size_t num_channels = 0;
103 int sample_rate = 0;
104 WavFormat format = WavFormat::kWavFormatPcm;
105 size_t bytes_per_sample = 0;
106 size_t num_samples = 0;
107 int64_t data_start_pos = 0;
108
109 // Test a few ways the header can be invalid. We start with the valid header
110 // used in WriteAndReadWavHeader, and invalidate one field per test. The
111 // invalid field is indicated in the array name, and in the comments with
112 // *BAD*.
113 {
114 constexpr uint8_t kBadRiffID[] = {
115 // clang-format off
116 // clang formatting doesn't respect inline comments.
117 'R', 'i', 'f', 'f', // *BAD*
118 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
119 'W', 'A', 'V', 'E',
120 'f', 'm', 't', ' ',
121 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
122 1, 0, // format: PCM (1)
123 17, 0, // channels: 17
124 0x39, 0x30, 0, 0, // sample rate: 12345
125 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345
126 17, 0, // block align: NumChannels * BytesPerSample
127 8, 0, // bits per sample: 1 * 8
128 'd', 'a', 't', 'a',
129 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689
130 // clang-format on
131 };
132 WavHeaderBufferReader r(kBadRiffID, sizeof(kBadRiffID),
133 /*check_read_size=*/false);
134 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
135 &bytes_per_sample, &num_samples,
136 &data_start_pos));
137 }
138 {
139 constexpr uint8_t kBadBitsPerSample[] = {
140 // clang-format off
141 // clang formatting doesn't respect inline comments.
142 'R', 'I', 'F', 'F',
143 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
144 'W', 'A', 'V', 'E',
145 'f', 'm', 't', ' ',
146 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
147 1, 0, // format: PCM (1)
148 17, 0, // channels: 17
149 0x39, 0x30, 0, 0, // sample rate: 12345
150 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345
151 17, 0, // block align: NumChannels * BytesPerSample
152 1, 0, // bits per sample: *BAD*
153 'd', 'a', 't', 'a',
154 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689
155 // clang-format on
156 };
157 WavHeaderBufferReader r(kBadBitsPerSample, sizeof(kBadBitsPerSample),
158 /*check_read_size=*/true);
159 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
160 &bytes_per_sample, &num_samples,
161 &data_start_pos));
162 }
163 {
164 constexpr uint8_t kBadByteRate[] = {
165 // clang-format off
166 // clang formatting doesn't respect inline comments.
167 'R', 'I', 'F', 'F',
168 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
169 'W', 'A', 'V', 'E',
170 'f', 'm', 't', ' ',
171 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
172 1, 0, // format: PCM (1)
173 17, 0, // channels: 17
174 0x39, 0x30, 0, 0, // sample rate: 12345
175 0x00, 0x33, 0x03, 0, // byte rate: *BAD*
176 17, 0, // block align: NumChannels * BytesPerSample
177 8, 0, // bits per sample: 1 * 8
178 'd', 'a', 't', 'a',
179 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689
180 // clang-format on
181 };
182 WavHeaderBufferReader r(kBadByteRate, sizeof(kBadByteRate),
183 /*check_read_size=*/true);
184 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
185 &bytes_per_sample, &num_samples,
186 &data_start_pos));
187 }
188 {
189 constexpr uint8_t kBadFmtHeaderSize[] = {
190 // clang-format off
191 // clang formatting doesn't respect inline comments.
192 'R', 'I', 'F', 'F',
193 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
194 'W', 'A', 'V', 'E',
195 'f', 'm', 't', ' ',
196 17, 0, 0, 0, // size of fmt block *BAD*. Only 16 and 18 permitted.
197 1, 0, // format: PCM (1)
198 17, 0, // channels: 17
199 0x39, 0x30, 0, 0, // sample rate: 12345
200 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345
201 17, 0, // block align: NumChannels * BytesPerSample
202 8, 0, // bits per sample: 1 * 8
203 0, // extra (though invalid) header byte
204 'd', 'a', 't', 'a',
205 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689
206 // clang-format on
207 };
208 WavHeaderBufferReader r(kBadFmtHeaderSize, sizeof(kBadFmtHeaderSize),
209 /*check_read_size=*/false);
210 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
211 &bytes_per_sample, &num_samples,
212 &data_start_pos));
213 }
214 {
215 constexpr uint8_t kNonZeroExtensionField[] = {
216 // clang-format off
217 // clang formatting doesn't respect inline comments.
218 'R', 'I', 'F', 'F',
219 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
220 'W', 'A', 'V', 'E',
221 'f', 'm', 't', ' ',
222 18, 0, 0, 0, // size of fmt block - 8: 24 - 8
223 1, 0, // format: PCM (1)
224 17, 0, // channels: 17
225 0x39, 0x30, 0, 0, // sample rate: 12345
226 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345
227 17, 0, // block align: NumChannels * BytesPerSample
228 8, 0, // bits per sample: 1 * 8
229 1, 0, // non-zero extension field *BAD*
230 'd', 'a', 't', 'a',
231 0x99, 0xd0, 0x5b, 0x07, // size of payload: 123457689
232 // clang-format on
233 };
234 WavHeaderBufferReader r(kNonZeroExtensionField,
235 sizeof(kNonZeroExtensionField),
236 /*check_read_size=*/false);
237 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
238 &bytes_per_sample, &num_samples,
239 &data_start_pos));
240 }
241 {
242 constexpr uint8_t kMissingDataChunk[] = {
243 // clang-format off
244 // clang formatting doesn't respect inline comments.
245 'R', 'I', 'F', 'F',
246 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
247 'W', 'A', 'V', 'E',
248 'f', 'm', 't', ' ',
249 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
250 1, 0, // format: PCM (1)
251 17, 0, // channels: 17
252 0x39, 0x30, 0, 0, // sample rate: 12345
253 0xc9, 0x33, 0x03, 0, // byte rate: 1 * 17 * 12345
254 17, 0, // block align: NumChannels * BytesPerSample
255 8, 0, // bits per sample: 1 * 8
256 // clang-format on
257 };
258 WavHeaderBufferReader r(kMissingDataChunk, sizeof(kMissingDataChunk),
259 /*check_read_size=*/true);
260 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
261 &bytes_per_sample, &num_samples,
262 &data_start_pos));
263 }
264 {
265 constexpr uint8_t kMissingFmtAndDataChunks[] = {
266 // clang-format off
267 // clang formatting doesn't respect inline comments.
268 'R', 'I', 'F', 'F',
269 0xbd, 0xd0, 0x5b, 0x07, // size of whole file - 8: 123457689 + 44 - 8
270 'W', 'A', 'V', 'E',
271 // clang-format on
272 };
273 WavHeaderBufferReader r(kMissingFmtAndDataChunks,
274 sizeof(kMissingFmtAndDataChunks),
275 /*check_read_size=*/true);
276 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
277 &bytes_per_sample, &num_samples,
278 &data_start_pos));
279 }
280 }
281
282 // Try writing and reading a valid WAV header and make sure it looks OK.
TEST(WavHeaderTest,WriteAndReadWavHeader)283 TEST(WavHeaderTest, WriteAndReadWavHeader) {
284 constexpr int kSize = 4 + kPcmWavHeaderSize + 4;
285 uint8_t buf[kSize];
286 size_t header_size;
287 memset(buf, 0xa4, sizeof(buf));
288 WriteWavHeader(17, 12345, WavFormat::kWavFormatPcm, 123457689, buf + 4,
289 &header_size);
290 constexpr uint8_t kExpectedBuf[] = {
291 // clang-format off
292 // clang formatting doesn't respect inline comments.
293 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes before header
294 'R', 'I', 'F', 'F',
295 0x56, 0xa1, 0xb7, 0x0e, // size of whole file - 8: 123457689 + 44 - 8
296 'W', 'A', 'V', 'E',
297 'f', 'm', 't', ' ',
298 16, 0, 0, 0, // size of fmt block - 8: 24 - 8
299 1, 0, // format: PCM (1)
300 17, 0, // channels: 17
301 0x39, 0x30, 0, 0, // sample rate: 12345
302 0x92, 0x67, 0x06, 0, // byte rate: 2 * 17 * 12345
303 34, 0, // block align: NumChannels * BytesPerSample
304 16, 0, // bits per sample: 2 * 8
305 'd', 'a', 't', 'a',
306 0x32, 0xa1, 0xb7, 0x0e, // size of payload: 2 * 123457689
307 0xa4, 0xa4, 0xa4, 0xa4, // untouched bytes after header
308 // clang-format on
309 };
310 static_assert(sizeof(kExpectedBuf) == kSize, "buffer size");
311 EXPECT_EQ(0, memcmp(kExpectedBuf, buf, kSize));
312
313 size_t num_channels = 0;
314 int sample_rate = 0;
315 WavFormat format = WavFormat::kWavFormatPcm;
316 size_t bytes_per_sample = 0;
317 size_t num_samples = 0;
318 int64_t data_start_pos = 0;
319 WavHeaderBufferReader r(buf + 4, sizeof(buf) - 8,
320 /*check_read_size=*/true);
321 EXPECT_TRUE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
322 &bytes_per_sample, &num_samples, &data_start_pos));
323 EXPECT_EQ(17u, num_channels);
324 EXPECT_EQ(12345, sample_rate);
325 EXPECT_EQ(WavFormat::kWavFormatPcm, format);
326 EXPECT_EQ(2u, bytes_per_sample);
327 EXPECT_EQ(123457689u, num_samples);
328 }
329
330 // Try reading an atypical but valid WAV header and make sure it's parsed OK.
TEST(WavHeaderTest,ReadAtypicalWavHeader)331 TEST(WavHeaderTest, ReadAtypicalWavHeader) {
332 constexpr uint8_t kBuf[] = {
333 // clang-format off
334 // clang formatting doesn't respect inline comments.
335 'R', 'I', 'F', 'F',
336 0xbf, 0xd0, 0x5b, 0x07, // Size of whole file - 8 + extra 2 bytes of zero
337 // extension: 123457689 + 44 - 8 + 2 (atypical).
338 'W', 'A', 'V', 'E',
339 'f', 'm', 't', ' ',
340 18, 0, 0, 0, // Size of fmt block (with an atypical extension
341 // size field).
342 1, 0, // Format: PCM (1).
343 17, 0, // Channels: 17.
344 0x39, 0x30, 0, 0, // Sample rate: 12345.
345 0xc9, 0x33, 0x03, 0, // Byte rate: 1 * 17 * 12345.
346 17, 0, // Block align: NumChannels * BytesPerSample.
347 8, 0, // Bits per sample: 1 * 8.
348 0, 0, // Zero extension size field (atypical).
349 'd', 'a', 't', 'a',
350 0x99, 0xd0, 0x5b, 0x07, // Size of payload: 123457689.
351 // clang-format on
352 };
353
354 size_t num_channels = 0;
355 int sample_rate = 0;
356 WavFormat format = WavFormat::kWavFormatPcm;
357 size_t bytes_per_sample = 0;
358 size_t num_samples = 0;
359 int64_t data_start_pos = 0;
360 WavHeaderBufferReader r(kBuf, sizeof(kBuf), /*check_read_size=*/true);
361 EXPECT_TRUE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
362 &bytes_per_sample, &num_samples, &data_start_pos));
363 EXPECT_EQ(17u, num_channels);
364 EXPECT_EQ(12345, sample_rate);
365 EXPECT_EQ(WavFormat::kWavFormatPcm, format);
366 EXPECT_EQ(1u, bytes_per_sample);
367 EXPECT_EQ(123457689u, num_samples);
368 }
369
370 // Try reading a valid WAV header which contains an optional chunk and make sure
371 // it's parsed OK.
TEST(WavHeaderTest,ReadWavHeaderWithOptionalChunk)372 TEST(WavHeaderTest, ReadWavHeaderWithOptionalChunk) {
373 constexpr uint8_t kBuf[] = {
374 // clang-format off
375 // clang formatting doesn't respect inline comments.
376 'R', 'I', 'F', 'F',
377 0xcd, 0xd0, 0x5b, 0x07, // Size of whole file - 8 + an extra 16 bytes of
378 // "metadata" (8 bytes header, 16 bytes payload):
379 // 123457689 + 44 - 8 + 16.
380 'W', 'A', 'V', 'E',
381 'f', 'm', 't', ' ',
382 16, 0, 0, 0, // Size of fmt block.
383 1, 0, // Format: PCM (1).
384 17, 0, // Channels: 17.
385 0x39, 0x30, 0, 0, // Sample rate: 12345.
386 0xc9, 0x33, 0x03, 0, // Byte rate: 1 * 17 * 12345.
387 17, 0, // Block align: NumChannels * BytesPerSample.
388 8, 0, // Bits per sample: 1 * 8.
389 'L', 'I', 'S', 'T', // Metadata chunk ID.
390 16, 0, 0, 0, // Metadata chunk payload size.
391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Metadata (16 bytes).
392 'd', 'a', 't', 'a',
393 0x99, 0xd0, 0x5b, 0x07, // Size of payload: 123457689.
394 // clang-format on
395 };
396
397 size_t num_channels = 0;
398 int sample_rate = 0;
399 WavFormat format = WavFormat::kWavFormatPcm;
400 size_t bytes_per_sample = 0;
401 size_t num_samples = 0;
402 int64_t data_start_pos = 0;
403 WavHeaderBufferReader r(kBuf, sizeof(kBuf), /*check_read_size=*/true);
404 EXPECT_TRUE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
405 &bytes_per_sample, &num_samples, &data_start_pos));
406 EXPECT_EQ(17u, num_channels);
407 EXPECT_EQ(12345, sample_rate);
408 EXPECT_EQ(WavFormat::kWavFormatPcm, format);
409 EXPECT_EQ(1u, bytes_per_sample);
410 EXPECT_EQ(123457689u, num_samples);
411 }
412
413 // Try reading an invalid WAV header which has the the data chunk before the
414 // format one and make sure it's not parsed.
TEST(WavHeaderTest,ReadWavHeaderWithDataBeforeFormat)415 TEST(WavHeaderTest, ReadWavHeaderWithDataBeforeFormat) {
416 constexpr uint8_t kBuf[] = {
417 // clang-format off
418 // clang formatting doesn't respect inline comments.
419 'R', 'I', 'F', 'F',
420 52, 0, 0, 0, // Size of whole file - 8: 16 + 44 - 8.
421 'W', 'A', 'V', 'E',
422 'd', 'a', 't', 'a',
423 16, 0, 0, 0, // Data chunk payload size.
424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Data 16 bytes.
425 'f', 'm', 't', ' ',
426 16, 0, 0, 0, // Size of fmt block.
427 1, 0, // Format: Pcm (1).
428 1, 0, // Channels: 1.
429 60, 0, 0, 0, // Sample rate: 60.
430 60, 0, 0, 0, // Byte rate: 1 * 1 * 60.
431 1, 0, // Block align: NumChannels * BytesPerSample.
432 8, 0, // Bits per sample: 1 * 8.
433 // clang-format on
434 };
435
436 size_t num_channels = 0;
437 int sample_rate = 0;
438 WavFormat format = WavFormat::kWavFormatPcm;
439 size_t bytes_per_sample = 0;
440 size_t num_samples = 0;
441 int64_t data_start_pos = 0;
442 WavHeaderBufferReader r(kBuf, sizeof(kBuf), /*check_read_size=*/false);
443 EXPECT_FALSE(ReadWavHeader(&r, &num_channels, &sample_rate, &format,
444 &bytes_per_sample, &num_samples, &data_start_pos));
445 }
446
447 } // namespace webrtc
448