WaveDemuxer.cpp (21738B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WaveDemuxer.h" 8 9 #include <inttypes.h> 10 11 #include <algorithm> 12 13 #include "BufferReader.h" 14 #include "TimeUnits.h" 15 #include "VideoUtils.h" 16 #include "mozilla/Assertions.h" 17 #include "mozilla/EndianUtils.h" 18 #include "mozilla/Logging.h" 19 #include "mozilla/Utf8.h" 20 21 using mozilla::media::TimeIntervals; 22 using mozilla::media::TimeUnit; 23 24 namespace mozilla { 25 26 #define LOG(msg, ...) \ 27 MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__) 28 29 WAVDemuxer::WAVDemuxer(MediaResource* aSource) : mSource(aSource) { 30 DDLINKCHILD("source", aSource); 31 } 32 33 bool WAVDemuxer::InitInternal() { 34 if (!mTrackDemuxer) { 35 mTrackDemuxer = new WAVTrackDemuxer(mSource.GetResource()); 36 DDLINKCHILD("track demuxer", mTrackDemuxer.get()); 37 } 38 return mTrackDemuxer->Init(); 39 } 40 41 RefPtr<WAVDemuxer::InitPromise> WAVDemuxer::Init() { 42 if (!InitInternal()) { 43 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR, 44 __func__); 45 } 46 return InitPromise::CreateAndResolve(NS_OK, __func__); 47 } 48 49 uint32_t WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const { 50 return aType == TrackInfo::kAudioTrack ? 1u : 0u; 51 } 52 53 already_AddRefed<MediaTrackDemuxer> WAVDemuxer::GetTrackDemuxer( 54 TrackInfo::TrackType aType, uint32_t aTrackNumber) { 55 if (!mTrackDemuxer) { 56 return nullptr; 57 } 58 return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget(); 59 } 60 61 bool WAVDemuxer::IsSeekable() const { return true; } 62 63 // WAVTrackDemuxer 64 65 WAVTrackDemuxer::WAVTrackDemuxer(MediaResource* aSource) 66 : mSource(aSource), 67 mOffset(0), 68 mFirstChunkOffset(0), 69 mNumParsedChunks(0), 70 mChunkIndex(0), 71 mDataLength(0), 72 mTotalChunkLen(0), 73 mSamplesPerChunk(0), 74 mSamplesPerSecond(0), 75 mChannels(0), 76 mSampleFormat(0) { 77 DDLINKCHILD("source", aSource); 78 Reset(); 79 } 80 81 bool WAVTrackDemuxer::Init() { 82 Reset(); 83 FastSeek(TimeUnit()); 84 85 if (!mInfo) { 86 mInfo = MakeUnique<AudioInfo>(); 87 mInfo->mCodecSpecificConfig = 88 AudioCodecSpecificVariant{WaveCodecSpecificData{}}; 89 } 90 91 if (!RIFFParserInit()) { 92 return false; 93 } 94 95 bool hasValidFmt = false; 96 97 while (true) { 98 if (!HeaderParserInit()) { 99 return false; 100 } 101 102 uint32_t chunkName = mHeaderParser.GiveHeader().ChunkName(); 103 uint32_t chunkSize = mHeaderParser.GiveHeader().ChunkSize(); 104 105 if (chunkName == FRMT_CODE) { 106 hasValidFmt = FmtChunkParserInit(); 107 } else if (chunkName == LIST_CODE) { 108 mHeaderParser.Reset(); 109 uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + chunkSize; 110 if (endOfListChunk > UINT32_MAX) { 111 return false; 112 } 113 if (!ListChunkParserInit(chunkSize)) { 114 mOffset = endOfListChunk; 115 } 116 } else if (chunkName == DATA_CODE) { 117 mDataLength = chunkSize; 118 if (mFirstChunkOffset != mOffset) { 119 mFirstChunkOffset = mOffset; 120 } 121 break; 122 } else { 123 mOffset += chunkSize; // Skip other irrelevant chunks. 124 } 125 if (mOffset & 1) { 126 // Wave files are 2-byte aligned so we need to round up 127 mOffset += 1; 128 } 129 mHeaderParser.Reset(); 130 } 131 132 if (!hasValidFmt) { 133 return false; 134 } 135 136 int64_t streamLength = StreamLength(); 137 // If the chunk length and the resource length are not equal, use the 138 // resource length as the "real" data chunk length, if it's longer than the 139 // chunk size. 140 if (streamLength != -1) { 141 uint64_t streamLengthPositive = static_cast<uint64_t>(streamLength); 142 if (streamLengthPositive > mFirstChunkOffset && 143 mDataLength > streamLengthPositive - mFirstChunkOffset) { 144 mDataLength = streamLengthPositive - mFirstChunkOffset; 145 } 146 } 147 148 mSamplesPerSecond = mFmtChunk.SampleRate(); 149 mChannels = mFmtChunk.Channels(); 150 if (!mSamplesPerSecond || !mChannels || !mFmtChunk.ValidBitsPerSamples()) { 151 return false; 152 } 153 mSamplesPerChunk = 154 DATA_CHUNK_SIZE * 8 / mChannels / mFmtChunk.ValidBitsPerSamples(); 155 mSampleFormat = mFmtChunk.ValidBitsPerSamples(); 156 157 mInfo->mRate = mSamplesPerSecond; 158 mInfo->mChannels = mChannels; 159 mInfo->mBitDepth = mFmtChunk.ValidBitsPerSamples(); 160 mInfo->mProfile = AssertedCast<uint8_t>(mFmtChunk.WaveFormat() & 0x00FF); 161 mInfo->mExtendedProfile = 162 AssertedCast<uint8_t>(mFmtChunk.WaveFormat() & 0xFF00 >> 8); 163 mInfo->mMimeType = "audio/wave; codecs="; 164 // 1: linear integer pcm 165 // 3: float 166 // 6: alaw 167 // 7: ulaw 168 mInfo->mMimeType.AppendInt(mInfo->mProfile); 169 mInfo->mDuration = Duration(); 170 mInfo->mChannelMap = mFmtChunk.ChannelMap(); 171 172 if (AudioConfig::ChannelLayout::Channels(mInfo->mChannelMap) != 173 mInfo->mChannels) { 174 AudioConfig::ChannelLayout::ChannelMap defaultForChannelCount = 175 AudioConfig::ChannelLayout(mInfo->mChannels).Map(); 176 LOG(("Channel count of %" PRIu32 177 " and channel layout disagree, overriding channel map from %s to %s", 178 mInfo->mChannels, 179 AudioConfig::ChannelLayout::ChannelMapToString(mInfo->mChannelMap) 180 .get(), 181 AudioConfig::ChannelLayout::ChannelMapToString(defaultForChannelCount) 182 .get())); 183 mInfo->mChannelMap = defaultForChannelCount; 184 } 185 LOG(("WavDemuxer initialized: %s", mInfo->ToString().get())); 186 187 return mInfo->mDuration.IsPositive(); 188 } 189 190 bool WAVTrackDemuxer::RIFFParserInit() { 191 RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader()); 192 if (!riffHeader) { 193 return false; 194 } 195 BufferReader RIFFReader(riffHeader->Data(), 12); 196 (void)mRIFFParser.Parse(RIFFReader); 197 return mRIFFParser.RiffHeader().IsValid(11); 198 } 199 200 bool WAVTrackDemuxer::HeaderParserInit() { 201 RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader()); 202 if (!header) { 203 return false; 204 } 205 BufferReader headerReader(header->Data(), 8); 206 (void)mHeaderParser.Parse(headerReader); 207 return true; 208 } 209 210 bool WAVTrackDemuxer::FmtChunkParserInit() { 211 RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk()); 212 if (!fmtChunk || fmtChunk->Size() < 16) { 213 return false; 214 } 215 nsTArray<uint8_t> fmtChunkData(fmtChunk->Data(), fmtChunk->Size()); 216 mFmtChunk.Init(std::move(fmtChunkData)); 217 return true; 218 } 219 220 bool WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize) { 221 uint32_t bytesRead = 0; 222 223 RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag()); 224 if (!infoTag) { 225 return false; 226 } 227 228 BufferReader infoTagReader(infoTag->Data(), 4); 229 auto res = infoTagReader.ReadU32(); 230 if (res.isErr() || (res.isOk() && res.unwrap() != INFO_CODE)) { 231 return false; 232 } 233 234 bytesRead += 4; 235 236 while (bytesRead < aChunkSize) { 237 if (!HeaderParserInit()) { 238 return false; 239 } 240 241 bytesRead += 8; 242 243 uint32_t id = mHeaderParser.GiveHeader().ChunkName(); 244 uint32_t length = mHeaderParser.GiveHeader().ChunkSize(); 245 246 // SubChunk Length Cannot Exceed List Chunk length. 247 if (length > aChunkSize - bytesRead) { 248 length = aChunkSize - bytesRead; 249 } 250 251 MediaByteRange mRange = {mOffset, mOffset + length}; 252 RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange); 253 if (!mChunkData) { 254 return false; 255 } 256 257 const char* rawData = reinterpret_cast<const char*>(mChunkData->Data()); 258 259 nsCString val(rawData, length); 260 if (length > 0 && val[length - 1] == '\0') { 261 val.SetLength(length - 1); 262 } 263 264 if (length % 2) { 265 mOffset += 1; 266 length += length % 2; 267 } 268 269 bytesRead += length; 270 271 if (!IsUtf8(val)) { 272 mHeaderParser.Reset(); 273 continue; 274 } 275 276 switch (id) { 277 case 0x49415254: // IART 278 mInfo->mTags.AppendElement(MetadataTag("artist"_ns, val)); 279 break; 280 case 0x49434d54: // ICMT 281 mInfo->mTags.AppendElement(MetadataTag("comments"_ns, val)); 282 break; 283 case 0x49474e52: // IGNR 284 mInfo->mTags.AppendElement(MetadataTag("genre"_ns, val)); 285 break; 286 case 0x494e414d: // INAM 287 mInfo->mTags.AppendElement(MetadataTag("name"_ns, val)); 288 break; 289 default: 290 LOG(("Metadata key %08x not handled", id)); 291 } 292 293 mHeaderParser.Reset(); 294 } 295 return true; 296 } 297 298 media::TimeUnit WAVTrackDemuxer::SeekPosition() const { 299 TimeUnit pos = Duration(mChunkIndex); 300 if (Duration() > TimeUnit()) { 301 pos = std::min(Duration(), pos); 302 } 303 return pos; 304 } 305 306 RefPtr<MediaRawData> WAVTrackDemuxer::DemuxSample() { 307 return GetNextChunk(FindNextChunk()); 308 } 309 310 UniquePtr<TrackInfo> WAVTrackDemuxer::GetInfo() const { return mInfo->Clone(); } 311 312 RefPtr<WAVTrackDemuxer::SeekPromise> WAVTrackDemuxer::Seek( 313 const TimeUnit& aTime) { 314 FastSeek(aTime); 315 const TimeUnit seekTime = ScanUntil(aTime); 316 return SeekPromise::CreateAndResolve(seekTime, __func__); 317 } 318 319 TimeUnit WAVTrackDemuxer::FastSeek(const TimeUnit& aTime) { 320 if (aTime.ToMicroseconds()) { 321 mChunkIndex = ChunkIndexFromTime(aTime); 322 } else { 323 mChunkIndex = 0; 324 } 325 326 mOffset = OffsetFromChunkIndex(mChunkIndex); 327 328 if (mOffset > mFirstChunkOffset && StreamLength() > 0) { 329 mOffset = std::min(static_cast<uint64_t>(StreamLength() - 1), mOffset); 330 } 331 332 return Duration(mChunkIndex); 333 } 334 335 TimeUnit WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime) { 336 if (!aTime.ToMicroseconds()) { 337 return FastSeek(aTime); 338 } 339 340 if (Duration(mChunkIndex) > aTime) { 341 FastSeek(aTime); 342 } 343 344 return SeekPosition(); 345 } 346 347 RefPtr<WAVTrackDemuxer::SamplesPromise> WAVTrackDemuxer::GetSamples( 348 int32_t aNumSamples) { 349 MOZ_ASSERT(aNumSamples); 350 351 RefPtr<SamplesHolder> datachunks = new SamplesHolder(); 352 353 while (aNumSamples--) { 354 RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk()); 355 if (!datachunk) { 356 break; 357 } 358 if (!datachunk->HasValidTime()) { 359 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 360 __func__); 361 } 362 datachunks->AppendSample(std::move(datachunk)); 363 } 364 365 if (datachunks->GetSamples().IsEmpty()) { 366 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, 367 __func__); 368 } 369 370 return SamplesPromise::CreateAndResolve(datachunks, __func__); 371 } 372 373 void WAVTrackDemuxer::Reset() { 374 FastSeek(TimeUnit()); 375 mParser.Reset(); 376 mHeaderParser.Reset(); 377 mRIFFParser.Reset(); 378 } 379 380 RefPtr<WAVTrackDemuxer::SkipAccessPointPromise> 381 WAVTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) { 382 return SkipAccessPointPromise::CreateAndReject( 383 SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__); 384 } 385 386 int64_t WAVTrackDemuxer::GetResourceOffset() const { 387 return AssertedCast<int64_t>(mOffset); 388 } 389 390 TimeIntervals WAVTrackDemuxer::GetBuffered() { 391 TimeUnit duration = Duration(); 392 393 if (duration <= TimeUnit()) { 394 return TimeIntervals(); 395 } 396 397 AutoPinned<MediaResource> stream(mSource.GetResource()); 398 return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds()); 399 } 400 401 int64_t WAVTrackDemuxer::StreamLength() const { return mSource.GetLength(); } 402 403 TimeUnit WAVTrackDemuxer::Duration() const { 404 if (!mDataLength || !mChannels || !mSampleFormat) { 405 return TimeUnit(); 406 } 407 408 int64_t numSamples = 409 static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat; 410 411 int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond; 412 413 if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) { 414 numUSeconds++; 415 } 416 417 return TimeUnit::FromMicroseconds(numUSeconds); 418 } 419 420 TimeUnit WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const { 421 if (!mSamplesPerSecond || !mSamplesPerChunk) { 422 return TimeUnit(); 423 } 424 const int64_t frames = mSamplesPerChunk * aNumDataChunks; 425 return TimeUnit(frames, mSamplesPerSecond); 426 } 427 428 TimeUnit WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const { 429 if (!mSamplesPerSecond || !mChannels || !mSampleFormat) { 430 return TimeUnit(); 431 } 432 433 uint64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat; 434 435 return TimeUnit(numSamples, mSamplesPerSecond); 436 } 437 438 MediaByteRange WAVTrackDemuxer::FindNextChunk() { 439 if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) { 440 return {mOffset, mOffset + DATA_CHUNK_SIZE}; 441 } 442 return {mOffset, mFirstChunkOffset + mDataLength}; 443 } 444 445 MediaByteRange WAVTrackDemuxer::FindChunkHeader() { 446 return {mOffset, mOffset + CHUNK_HEAD_SIZE}; 447 } 448 449 MediaByteRange WAVTrackDemuxer::FindRIFFHeader() { 450 return {mOffset, mOffset + RIFF_CHUNK_SIZE}; 451 } 452 453 MediaByteRange WAVTrackDemuxer::FindFmtChunk() { 454 return {mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize()}; 455 } 456 457 MediaByteRange WAVTrackDemuxer::FindListChunk() { 458 return {mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize()}; 459 } 460 461 MediaByteRange WAVTrackDemuxer::FindInfoTag() { return {mOffset, mOffset + 4}; } 462 463 bool WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange) { 464 UpdateState(aRange); 465 return true; 466 } 467 468 already_AddRefed<MediaRawData> WAVTrackDemuxer::GetNextChunk( 469 const MediaByteRange& aRange) { 470 if (!aRange.Length()) { 471 return nullptr; 472 } 473 474 RefPtr<MediaRawData> datachunk = new MediaRawData(); 475 datachunk->mOffset = aRange.mStart; 476 477 UniquePtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter()); 478 if (!chunkWriter->SetSize(static_cast<uint32_t>(aRange.Length()))) { 479 return nullptr; 480 } 481 482 const uint32_t read = Read(chunkWriter->Data(), datachunk->mOffset, 483 AssertedCast<int64_t>(datachunk->Size())); 484 485 if (read != aRange.Length()) { 486 return nullptr; 487 } 488 489 UpdateState(aRange); 490 ++mNumParsedChunks; 491 ++mChunkIndex; 492 493 datachunk->mTime = Duration(mChunkIndex - 1); 494 495 if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) { 496 datachunk->mDuration = Duration(1); 497 } else { 498 uint32_t mBytesRemaining = mDataLength - mChunkIndex * DATA_CHUNK_SIZE; 499 datachunk->mDuration = DurationFromBytes(mBytesRemaining); 500 } 501 datachunk->mTimecode = datachunk->mTime; 502 datachunk->mKeyframe = true; 503 504 MOZ_ASSERT(!datachunk->mTime.IsNegative()); 505 MOZ_ASSERT(!datachunk->mDuration.IsNegative()); 506 507 return datachunk.forget(); 508 } 509 510 already_AddRefed<MediaRawData> WAVTrackDemuxer::GetFileHeader( 511 const MediaByteRange& aRange) { 512 if (!aRange.Length()) { 513 return nullptr; 514 } 515 516 RefPtr<MediaRawData> fileHeader = new MediaRawData(); 517 fileHeader->mOffset = aRange.mStart; 518 519 UniquePtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter()); 520 if (!headerWriter->SetSize(static_cast<uint32_t>(aRange.Length()))) { 521 return nullptr; 522 } 523 524 const uint32_t read = Read(headerWriter->Data(), fileHeader->mOffset, 525 AssertedCast<int64_t>(fileHeader->Size())); 526 527 if (read != aRange.Length()) { 528 return nullptr; 529 } 530 531 UpdateState(aRange); 532 533 return fileHeader.forget(); 534 } 535 536 uint64_t WAVTrackDemuxer::OffsetFromChunkIndex(uint32_t aChunkIndex) const { 537 return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE; 538 } 539 540 uint64_t WAVTrackDemuxer::ChunkIndexFromTime( 541 const media::TimeUnit& aTime) const { 542 if (!mSamplesPerChunk || !mSamplesPerSecond) { 543 return 0; 544 } 545 double chunkDurationS = 546 mSamplesPerChunk / static_cast<double>(mSamplesPerSecond); 547 int64_t chunkIndex = std::floor(aTime.ToSeconds() / chunkDurationS); 548 return chunkIndex; 549 } 550 551 void WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange) { 552 // Full chunk parsed, move offset to its end. 553 mOffset = static_cast<uint32_t>(aRange.mEnd); 554 mTotalChunkLen += static_cast<uint64_t>(aRange.Length()); 555 } 556 557 int64_t WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, 558 int64_t aSize) { 559 const int64_t streamLen = StreamLength(); 560 if (mInfo && streamLen > 0) { 561 int64_t max = streamLen > aOffset ? streamLen - aOffset : 0; 562 aSize = std::min(aSize, max); 563 } 564 uint32_t read = 0; 565 const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer), 566 static_cast<uint32_t>(aSize), &read); 567 NS_ENSURE_SUCCESS(rv, 0); 568 return read; 569 } 570 571 // RIFFParser 572 573 Result<uint32_t, nsresult> RIFFParser::Parse(BufferReader& aReader) { 574 for (auto res = aReader.ReadU8(); 575 res.isOk() && !mRiffHeader.ParseNext(res.unwrap()); 576 res = aReader.ReadU8()) { 577 } 578 579 if (mRiffHeader.IsValid()) { 580 return RIFF_CHUNK_SIZE; 581 } 582 583 return 0; 584 } 585 586 void RIFFParser::Reset() { mRiffHeader.Reset(); } 587 588 const RIFFParser::RIFFHeader& RIFFParser::RiffHeader() const { 589 return mRiffHeader; 590 } 591 592 // RIFFParser::RIFFHeader 593 594 RIFFParser::RIFFHeader::RIFFHeader() { Reset(); } 595 596 void RIFFParser::RIFFHeader::Reset() { 597 memset(mRaw, 0, sizeof(mRaw)); 598 mPos = 0; 599 } 600 601 bool RIFFParser::RIFFHeader::ParseNext(uint8_t c) { 602 if (!Update(c)) { 603 Reset(); 604 if (!Update(c)) { 605 Reset(); 606 } 607 } 608 return IsValid(); 609 } 610 611 bool RIFFParser::RIFFHeader::IsValid(int aPos) const { 612 if (aPos > -1 && aPos < 4) { 613 return RIFF[aPos] == mRaw[aPos]; 614 } 615 if (aPos > 7 && aPos < 12) { 616 return WAVE[aPos - 8] == mRaw[aPos]; 617 } 618 return true; 619 } 620 621 bool RIFFParser::RIFFHeader::IsValid() const { return mPos >= RIFF_CHUNK_SIZE; } 622 623 bool RIFFParser::RIFFHeader::Update(uint8_t c) { 624 if (mPos < RIFF_CHUNK_SIZE) { 625 mRaw[mPos] = c; 626 } 627 return IsValid(mPos++); 628 } 629 630 // HeaderParser 631 632 Result<uint32_t, nsresult> HeaderParser::Parse(BufferReader& aReader) { 633 for (auto res = aReader.ReadU8(); 634 res.isOk() && !mHeader.ParseNext(res.unwrap()); res = aReader.ReadU8()) { 635 } 636 637 if (mHeader.IsValid()) { 638 return CHUNK_HEAD_SIZE; 639 } 640 641 return 0; 642 } 643 644 void HeaderParser::Reset() { mHeader.Reset(); } 645 646 const HeaderParser::ChunkHeader& HeaderParser::GiveHeader() const { 647 return mHeader; 648 } 649 650 // HeaderParser::ChunkHeader 651 652 HeaderParser::ChunkHeader::ChunkHeader() { Reset(); } 653 654 void HeaderParser::ChunkHeader::Reset() { 655 memset(mRaw, 0, sizeof(mRaw)); 656 mPos = 0; 657 } 658 659 bool HeaderParser::ChunkHeader::ParseNext(uint8_t c) { 660 Update(c); 661 return IsValid(); 662 } 663 664 bool HeaderParser::ChunkHeader::IsValid() const { 665 return mPos >= CHUNK_HEAD_SIZE; 666 } 667 668 uint32_t HeaderParser::ChunkHeader::ChunkName() const { 669 return static_cast<uint32_t>( 670 ((mRaw[0] << 24) | (mRaw[1] << 16) | (mRaw[2] << 8) | (mRaw[3]))); 671 } 672 673 uint32_t HeaderParser::ChunkHeader::ChunkSize() const { 674 return static_cast<uint32_t>( 675 ((mRaw[7] << 24) | (mRaw[6] << 16) | (mRaw[5] << 8) | (mRaw[4]))); 676 } 677 678 void HeaderParser::ChunkHeader::Update(uint8_t c) { 679 if (mPos < CHUNK_HEAD_SIZE) { 680 mRaw[mPos++] = c; 681 } 682 } 683 684 // FormatChunk 685 686 void FormatChunk::Init(nsTArray<uint8_t>&& aData) { mRaw = std::move(aData); } 687 688 uint16_t FormatChunk::Channels() const { return (mRaw[3] << 8) | (mRaw[2]); } 689 690 uint32_t FormatChunk::SampleRate() const { 691 return static_cast<uint32_t>((mRaw[7] << 24) | (mRaw[6] << 16) | 692 (mRaw[5] << 8) | (mRaw[4])); 693 } 694 695 uint16_t FormatChunk::AverageBytesPerSec() const { 696 return static_cast<uint16_t>((mRaw[11] << 24) | (mRaw[10] << 16) | 697 (mRaw[9] << 8) | (mRaw[8])); 698 } 699 700 uint16_t FormatChunk::BlockAlign() const { 701 return static_cast<uint16_t>(mRaw[13] << 8) | (mRaw[12]); 702 } 703 704 uint16_t FormatChunk::ValidBitsPerSamples() const { 705 return (mRaw[15] << 8) | (mRaw[14]); 706 } 707 708 // Constants to deal with WAVEFORMATEXTENSIBLE struct 709 constexpr size_t MIN_SIZE_WAVEFORMATEXTENSIBLE = 22; 710 constexpr size_t OFFSET_CHANNEL_MAP = 20; 711 constexpr size_t OFFSET_FORMAT = 24; 712 constexpr size_t SIZE_WAVEFORMATEX = 18; 713 714 template <typename T> 715 Result<T, nsresult> GetFromExtradata(const nsTArray<uint8_t>& aRawData, 716 size_t aOffset) { 717 // Check there is extradata 718 MOZ_ASSERT(((aRawData[1] << 8) | aRawData[0]) == 0xFFFE, 719 "GetFromExtradata called without a tag of 0xFFFE"); 720 if (aRawData.Length() <= SIZE_WAVEFORMATEX) { 721 return Err(NS_ERROR_UNEXPECTED); 722 } 723 uint16_t extradataSize = 724 static_cast<uint16_t>(aRawData[17] << 8) | (aRawData[16]); 725 // The length of this chunk is at least 18, check if it's long enough to 726 // hold the WAVE_FORMAT_EXTENSIBLE struct, that is 22 more than the 727 // WAVEFORMATEX. 728 if (extradataSize < MIN_SIZE_WAVEFORMATEXTENSIBLE || 729 aRawData.Length() < SIZE_WAVEFORMATEX + MIN_SIZE_WAVEFORMATEXTENSIBLE) { 730 return Err(NS_ERROR_UNEXPECTED); 731 } 732 BufferReader reader(aRawData.Elements() + aOffset, sizeof(T)); 733 T value = reader.ReadType<T>(); 734 T swapped = mozilla::NativeEndian::swapFromLittleEndian(value); 735 return swapped; 736 } 737 738 uint16_t FormatChunk::WaveFormat() const { 739 uint16_t format = (mRaw[1] << 8) | mRaw[0]; 740 if (format != 0xFFFE) { 741 return format; 742 } 743 auto formatResult = GetFromExtradata<uint16_t>(mRaw, OFFSET_FORMAT); 744 if (formatResult.isErr()) { 745 LOG(("Error getting the Wave format, returning PCM")); 746 return 1; 747 } 748 return formatResult.unwrap(); 749 } 750 751 AudioConfig::ChannelLayout::ChannelMap FormatChunk::ChannelMap() const { 752 uint16_t format = (mRaw[1] << 8) | mRaw[0]; 753 if (format != 0xFFFE) { 754 return AudioConfig::ChannelLayout(Channels()).Map(); 755 } 756 auto mapResult = GetFromExtradata<uint32_t>(mRaw, OFFSET_CHANNEL_MAP); 757 if (mapResult.isErr()) { 758 LOG(("Error getting channel map, falling back to default order")); 759 return AudioConfig::ChannelLayout(Channels()).Map(); 760 } 761 // ChannelLayout::ChannelMap is by design bit-per-bit compatible with 762 // WAVEFORMATEXTENSIBLE's dwChannelMask attribute. 763 return mapResult.unwrap(); 764 } 765 766 // DataParser 767 768 DataParser::DataParser() = default; 769 770 void DataParser::Reset() { mChunk.Reset(); } 771 772 const DataParser::DataChunk& DataParser::CurrentChunk() const { return mChunk; } 773 774 // DataParser::DataChunk 775 776 void DataParser::DataChunk::Reset() { mPos = 0; } 777 778 } // namespace mozilla