wav_header_unittest.cc (17565B)
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 <cstdint> 14 #include <cstring> 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: 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 31 ~WavHeaderBufferReader() override { 32 // Verify the entire buffer has been read. 33 if (check_read_size_) 34 EXPECT_EQ(size_, pos_); 35 } 36 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 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 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. 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 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. 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. 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. 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. 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