tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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