tor-browser

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

MP3FrameParser.cpp (25447B)


      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 "MP3FrameParser.h"
      8 
      9 #include <inttypes.h>
     10 
     11 #include <algorithm>
     12 
     13 #include "MediaDataDemuxer.h"
     14 #include "TimeUnits.h"
     15 #include "VideoUtils.h"
     16 #include "mozilla/Assertions.h"
     17 #include "mozilla/EndianUtils.h"
     18 #include "mozilla/ScopeExit.h"
     19 #include "mozilla/Try.h"
     20 
     21 #define MP3LOG(msg, ...) \
     22  MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
     23 #define MP3LOGV(msg, ...)                      \
     24  MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, \
     25          ("MP3Demuxer " msg, ##__VA_ARGS__))
     26 
     27 namespace mozilla {
     28 
     29 // FrameParser
     30 
     31 namespace frame_header {
     32 // FrameHeader mRaw byte offsets.
     33 static const int SYNC1 = 0;
     34 static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
     35 static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
     36 static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
     37 }  // namespace frame_header
     38 
     39 FrameParser::FrameParser() = default;
     40 
     41 void FrameParser::Reset() {
     42  mID3Parser.Reset();
     43  mFrame.Reset();
     44 }
     45 
     46 void FrameParser::ResetFrameData() {
     47  mFrame.Reset();
     48  mFirstFrame.Reset();
     49  mPrevFrame.Reset();
     50 }
     51 
     52 void FrameParser::EndFrameSession() {
     53  if (!mID3Parser.Header().IsValid()) {
     54    // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
     55    mID3Parser.Reset();
     56  }
     57  mPrevFrame = mFrame;
     58  mFrame.Reset();
     59 }
     60 
     61 const FrameParser::Frame& FrameParser::CurrentFrame() const { return mFrame; }
     62 
     63 const FrameParser::Frame& FrameParser::PrevFrame() const { return mPrevFrame; }
     64 
     65 const FrameParser::Frame& FrameParser::FirstFrame() const {
     66  return mFirstFrame;
     67 }
     68 
     69 const ID3Parser::ID3Header& FrameParser::ID3Header() const {
     70  return mID3Parser.Header();
     71 }
     72 
     73 uint32_t FrameParser::TotalID3HeaderSize() const {
     74  uint32_t ID3v1Size = 0;
     75  if (mID3v1MetadataFound) {
     76    ID3v1Size = 128;
     77  }
     78  return ID3v1Size + mID3Parser.TotalHeadersSize();
     79 }
     80 
     81 const FrameParser::VBRHeader& FrameParser::VBRInfo() const {
     82  return mVBRHeader;
     83 }
     84 
     85 Result<bool, nsresult> FrameParser::Parse(BufferReader* aReader,
     86                                          uint32_t* aBytesToSkip) {
     87  MOZ_ASSERT(aReader && aBytesToSkip);
     88  *aBytesToSkip = 0;
     89 
     90  if (ID3Parser::IsBufferStartingWithID3v1Tag(aReader)) {
     91    // This is usually at the end of the file, and is always 128 bytes, that
     92    // can simply be skipped.
     93    aReader->Read(128);
     94    *aBytesToSkip = 128;
     95    mID3v1MetadataFound = true;
     96    MP3LOGV("ID3v1 tag detected, skipping 128 bytes past the current buffer");
     97    return false;
     98  }
     99 
    100  if (ID3Parser::IsBufferStartingWithID3Tag(aReader) && !mFirstFrame.Length()) {
    101    // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
    102    // ID3v1 tags may only be at file end.
    103    const size_t prevReaderOffset = aReader->Offset();
    104    const uint32_t tagSize = mID3Parser.Parse(aReader);
    105    if (!!tagSize) {
    106      // ID3 tag found, skip past it.
    107      const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
    108 
    109      if (skipSize > aReader->Remaining()) {
    110        // Skipping across the ID3v2 tag would take us past the end of the
    111        // buffer, therefore we return immediately and let the calling function
    112        // handle skipping the rest of the tag.
    113        MP3LOGV(
    114            "ID3v2 tag detected, size=%d,"
    115            " needing to skip %zu bytes past the current buffer",
    116            tagSize, skipSize - aReader->Remaining());
    117        *aBytesToSkip = skipSize - aReader->Remaining();
    118        return false;
    119      }
    120      MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
    121      aReader->Read(skipSize);
    122    } else {
    123      // No ID3v2 tag found, rewinding reader in order to search for a MPEG
    124      // frame header.
    125      aReader->Seek(prevReaderOffset);
    126    }
    127  }
    128 
    129  for (auto res = aReader->ReadU8();
    130       res.isOk() && !mFrame.ParseNext(res.unwrap()); res = aReader->ReadU8()) {
    131  }
    132 
    133  if (mFrame.Length()) {
    134    // MP3 frame found.
    135    if (!mFirstFrame.Length()) {
    136      mFirstFrame = mFrame;
    137    }
    138    // Indicate success.
    139    return true;
    140  }
    141  return false;
    142 }
    143 
    144 // FrameParser::Header
    145 
    146 FrameParser::FrameHeader::FrameHeader() { Reset(); }
    147 
    148 uint8_t FrameParser::FrameHeader::Sync1() const {
    149  return mRaw[frame_header::SYNC1];
    150 }
    151 
    152 uint8_t FrameParser::FrameHeader::Sync2() const {
    153  return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5;
    154 }
    155 
    156 uint8_t FrameParser::FrameHeader::RawVersion() const {
    157  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3;
    158 }
    159 
    160 uint8_t FrameParser::FrameHeader::RawLayer() const {
    161  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1;
    162 }
    163 
    164 uint8_t FrameParser::FrameHeader::RawProtection() const {
    165  return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6;
    166 }
    167 
    168 uint8_t FrameParser::FrameHeader::RawBitrate() const {
    169  return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4;
    170 }
    171 
    172 uint8_t FrameParser::FrameHeader::RawSampleRate() const {
    173  return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2;
    174 }
    175 
    176 uint8_t FrameParser::FrameHeader::Padding() const {
    177  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1;
    178 }
    179 
    180 uint8_t FrameParser::FrameHeader::Private() const {
    181  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE];
    182 }
    183 
    184 uint8_t FrameParser::FrameHeader::RawChannelMode() const {
    185  return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6;
    186 }
    187 
    188 uint32_t FrameParser::FrameHeader::Layer() const {
    189  static const uint8_t LAYERS[4] = {0, 3, 2, 1};
    190 
    191  return LAYERS[RawLayer()];
    192 }
    193 
    194 uint32_t FrameParser::FrameHeader::SampleRate() const {
    195  // Sample rates - use [version][srate]
    196  static const uint16_t SAMPLE_RATE[4][4] = {
    197      // clang-format off
    198    { 11025, 12000,  8000, 0 }, // MPEG 2.5
    199    {     0,     0,     0, 0 }, // Reserved
    200    { 22050, 24000, 16000, 0 }, // MPEG 2
    201    { 44100, 48000, 32000, 0 }  // MPEG 1
    202      // clang-format on
    203  };
    204 
    205  return SAMPLE_RATE[RawVersion()][RawSampleRate()];
    206 }
    207 
    208 uint32_t FrameParser::FrameHeader::Channels() const {
    209  // 3 is single channel (mono), any other value is some variant of dual
    210  // channel.
    211  return RawChannelMode() == 3 ? 1 : 2;
    212 }
    213 
    214 uint32_t FrameParser::FrameHeader::SamplesPerFrame() const {
    215  // Samples per frame - use [version][layer]
    216  static const uint16_t FRAME_SAMPLE[4][4] = {
    217      // clang-format off
    218    // Layer     3     2     1       Version
    219    {      0,  576, 1152,  384 }, // 2.5
    220    {      0,    0,    0,    0 }, // Reserved
    221    {      0,  576, 1152,  384 }, // 2
    222    {      0, 1152, 1152,  384 }  // 1
    223      // clang-format on
    224  };
    225 
    226  return FRAME_SAMPLE[RawVersion()][RawLayer()];
    227 }
    228 
    229 uint32_t FrameParser::FrameHeader::Bitrate() const {
    230  // Bitrates - use [version][layer][bitrate]
    231  static const uint16_t BITRATE[4][4][16] = {
    232      // clang-format off
    233    { // Version 2.5
    234      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
    235      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
    236      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
    237      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
    238    },
    239    { // Reserved
    240      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
    241      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
    242      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
    243      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }  // Invalid
    244    },
    245    { // Version 2
    246      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
    247      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
    248      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
    249      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
    250    },
    251    { // Version 1
    252      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
    253      { 0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
    254      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
    255      { 0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
    256    }
    257      // clang-format on
    258  };
    259 
    260  return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()];
    261 }
    262 
    263 uint32_t FrameParser::FrameHeader::SlotSize() const {
    264  // Slot size (MPEG unit of measurement) - use [layer]
    265  static const uint8_t SLOT_SIZE[4] = {0, 1, 1, 4};  // Rsvd, 3, 2, 1
    266 
    267  return SLOT_SIZE[RawLayer()];
    268 }
    269 
    270 bool FrameParser::FrameHeader::ParseNext(uint8_t c) {
    271  if (!Update(c)) {
    272    Reset();
    273    if (!Update(c)) {
    274      Reset();
    275    }
    276  }
    277  return IsValid();
    278 }
    279 
    280 bool FrameParser::ID3v1MetadataFound() const { return mID3v1MetadataFound; }
    281 
    282 bool FrameParser::FrameHeader::IsValid(int aPos) const {
    283  if (aPos >= SIZE) {
    284    return true;
    285  }
    286  if (aPos == frame_header::SYNC1) {
    287    return Sync1() == 0xFF;
    288  }
    289  if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) {
    290    return Sync2() == 7 && RawVersion() != 1 && Layer() == 3;
    291  }
    292  if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) {
    293    return RawBitrate() != 0xF && RawBitrate() != 0 && RawSampleRate() != 3;
    294  }
    295  return true;
    296 }
    297 
    298 bool FrameParser::FrameHeader::IsValid() const { return mPos >= SIZE; }
    299 
    300 void FrameParser::FrameHeader::Reset() { mPos = 0; }
    301 
    302 bool FrameParser::FrameHeader::Update(uint8_t c) {
    303  if (mPos < SIZE) {
    304    mRaw[mPos] = c;
    305  }
    306  return IsValid(mPos++);
    307 }
    308 
    309 // FrameParser::VBRHeader
    310 
    311 namespace vbr_header {
    312 static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"};
    313 static const uint32_t TOC_SIZE = 100;
    314 }  // namespace vbr_header
    315 
    316 FrameParser::VBRHeader::VBRHeader() : mType(NONE) {}
    317 
    318 FrameParser::VBRHeader::VBRHeaderType FrameParser::VBRHeader::Type() const {
    319  return mType;
    320 }
    321 
    322 const Maybe<uint32_t>& FrameParser::VBRHeader::NumAudioFrames() const {
    323  return mNumAudioFrames;
    324 }
    325 
    326 const Maybe<uint32_t>& FrameParser::VBRHeader::NumBytes() const {
    327  return mNumBytes;
    328 }
    329 
    330 const Maybe<uint32_t>& FrameParser::VBRHeader::Scale() const { return mScale; }
    331 
    332 bool FrameParser::VBRHeader::IsTOCPresent() const {
    333  // This doesn't use VBRI TOC
    334  return !mTOC.empty() && mType != VBRI;
    335 }
    336 
    337 bool FrameParser::VBRHeader::IsValid() const { return mType != NONE; }
    338 
    339 bool FrameParser::VBRHeader::IsComplete() const {
    340  return IsValid() && mNumAudioFrames.valueOr(0) > 0 && mNumBytes.valueOr(0) > 0
    341      // We don't care about the scale for any computations here.
    342      // && mScale < 101
    343      ;
    344 }
    345 
    346 int64_t FrameParser::VBRHeader::Offset(media::TimeUnit aTime,
    347                                       media::TimeUnit aDuration) const {
    348  if (!IsTOCPresent()) {
    349    return -1;
    350  }
    351 
    352  int64_t offset = -1;
    353  if (mType == XING) {
    354    // Constrain the duration percentage to [0, 99].
    355    double percent = 100. * aTime.ToSeconds() / aDuration.ToSeconds();
    356    const double durationPer = std::clamp(percent, 0., 99.);
    357    double integer;
    358    const double fractional = modf(durationPer, &integer);
    359    size_t integerPer = AssertedCast<size_t>(integer);
    360 
    361    MOZ_ASSERT(integerPer < mTOC.size());
    362    offset = mTOC.at(integerPer);
    363    if (fractional > 0.0 && integerPer + 1 < mTOC.size()) {
    364      offset += AssertedCast<int64_t>(fractional) *
    365                (mTOC.at(integerPer + 1) - offset);
    366    }
    367  }
    368  // TODO: VBRI TOC seeking
    369  MP3LOG("VBRHeader::Offset (%s): %f is at byte %" PRId64 "",
    370         mType == XING ? "XING" : "VBRI", aTime.ToSeconds(), offset);
    371 
    372  return offset;
    373 }
    374 
    375 Result<bool, nsresult> FrameParser::VBRHeader::ParseXing(BufferReader* aReader,
    376                                                         size_t aFrameSize) {
    377  static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
    378  static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
    379  static const uint32_t LAME_TAG = BigEndian::readUint32("LAME");
    380  static const uint32_t LAVC_TAG = BigEndian::readUint32("Lavc");
    381 
    382  enum Flags {
    383    NUM_FRAMES = 0x01,
    384    NUM_BYTES = 0x02,
    385    TOC = 0x04,
    386    VBR_SCALE = 0x08
    387  };
    388 
    389  MOZ_ASSERT(aReader);
    390 
    391  // Seek backward to the original position before leaving this scope.
    392  const size_t prevReaderOffset = aReader->Offset();
    393  auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); });
    394 
    395  // We have to search for the Xing header as its position can change.
    396  for (auto res = aReader->PeekU32();
    397       res.isOk() && res.unwrap() != XING_TAG && res.unwrap() != INFO_TAG;) {
    398    aReader->Read(1);
    399    res = aReader->PeekU32();
    400  }
    401 
    402  // Skip across the VBR header ID tag.
    403  MOZ_TRY(aReader->ReadU32());
    404  mType = XING;
    405 
    406  uint32_t flags = MOZ_TRY(aReader->ReadU32());
    407 
    408  if (flags & NUM_FRAMES) {
    409    uint32_t frames = MOZ_TRY(aReader->ReadU32());
    410    mNumAudioFrames = Some(frames);
    411  }
    412  if (flags & NUM_BYTES) {
    413    uint32_t bytes = MOZ_TRY(aReader->ReadU32());
    414    mNumBytes = Some(bytes);
    415  }
    416  if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
    417    if (!mNumBytes) {
    418      // We don't have the stream size to calculate offsets, skip the TOC.
    419      aReader->Read(vbr_header::TOC_SIZE);
    420    } else {
    421      mTOC.clear();
    422      mTOC.reserve(vbr_header::TOC_SIZE);
    423      for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
    424        uint8_t data = MOZ_TRY(aReader->ReadU8());
    425        mTOC.push_back(
    426            AssertedCast<uint32_t>(1.0f / 256.0f * data * mNumBytes.value()));
    427      }
    428    }
    429  }
    430  if (flags & VBR_SCALE) {
    431    mScale = Some(MOZ_TRY(aReader->ReadU32()));
    432  }
    433 
    434  uint32_t lameOrLavcTag = MOZ_TRY(aReader->ReadU32());
    435 
    436  if (lameOrLavcTag == LAME_TAG || lameOrLavcTag == LAVC_TAG) {
    437    // Skip 17 bytes after the LAME tag:
    438    // - http://gabriel.mp3-tech.org/mp3infotag.html
    439    // - 5 bytes for the encoder short version string
    440    // - 1 byte for the info tag revision + VBR method
    441    // - 1 byte for the lowpass filter value
    442    // - 8 bytes for the ReplayGain information
    443    // - 1 byte for the encoding flags + ATH Type
    444    // - 1 byte for the specified bitrate if ABR, else the minimal bitrate
    445    if (!aReader->Read(17)) {
    446      return mozilla::Err(NS_ERROR_FAILURE);
    447    }
    448 
    449    // The encoder delay is three bytes, for two 12-bits integers are the
    450    // encoder delay and the padding.
    451    const uint8_t* delayPadding = aReader->Read(3);
    452    if (!delayPadding) {
    453      return mozilla::Err(NS_ERROR_FAILURE);
    454    }
    455    mEncoderDelay =
    456        uint32_t(delayPadding[0]) << 4 | (delayPadding[1] & 0xf0) >> 4;
    457    mEncoderPadding = uint32_t(delayPadding[1] & 0x0f) << 8 | delayPadding[2];
    458 
    459    constexpr uint16_t DEFAULT_DECODER_DELAY = 529;
    460    mEncoderDelay += DEFAULT_DECODER_DELAY + aFrameSize;  // ignore first frame.
    461    mEncoderPadding -= std::min(mEncoderPadding, DEFAULT_DECODER_DELAY);
    462 
    463    MP3LOG("VBRHeader::ParseXing: LAME encoder delay section: delay: %" PRIu16
    464           " frames, padding: %" PRIu16 " frames",
    465           mEncoderDelay, mEncoderPadding);
    466  }
    467 
    468  return mType == XING;
    469 }
    470 
    471 template <typename T>
    472 int readAndConvertToInt(BufferReader* aReader) {
    473  int value = AssertedCast<int>(aReader->ReadType<T>());
    474  return value;
    475 }
    476 
    477 Result<bool, nsresult> FrameParser::VBRHeader::ParseVBRI(
    478    BufferReader* aReader) {
    479  static const uint32_t TAG = BigEndian::readUint32("VBRI");
    480  static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
    481  static const uint32_t MIN_FRAME_SIZE = OFFSET + 26;
    482 
    483  MOZ_ASSERT(aReader);
    484  // ParseVBRI assumes that the ByteReader offset points to the beginning of a
    485  // frame, therefore as a simple check, we look for the presence of a frame
    486  // sync at that position.
    487  auto sync = aReader->PeekU16();
    488  if (sync.isOk()) {  // To avoid compiler complains 'set but unused'.
    489    MOZ_ASSERT((sync.unwrap() & 0xFFE0) == 0xFFE0);
    490  }
    491 
    492  // Seek backward to the original position before leaving this scope.
    493  const size_t prevReaderOffset = aReader->Offset();
    494  auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); });
    495 
    496  // VBRI have a fixed relative position, so let's check for it there.
    497  if (aReader->Remaining() > MIN_FRAME_SIZE) {
    498    aReader->Seek(prevReaderOffset + OFFSET);
    499    uint32_t tag = MOZ_TRY(aReader->ReadU32());
    500    if (tag == TAG) {
    501      uint16_t vbriEncoderVersion, vbriEncoderDelay, vbriQuality;
    502      uint32_t vbriBytes, vbriFrames;
    503      uint16_t vbriSeekOffsetsTableSize, vbriSeekOffsetsScaleFactor,
    504          vbriSeekOffsetsBytesPerEntry, vbriSeekOffsetsFramesPerEntry;
    505      vbriEncoderVersion = MOZ_TRY(aReader->ReadU16());
    506      vbriEncoderDelay = MOZ_TRY(aReader->ReadU16());
    507      vbriQuality = MOZ_TRY(aReader->ReadU16());
    508      vbriBytes = MOZ_TRY(aReader->ReadU32());
    509      vbriFrames = MOZ_TRY(aReader->ReadU32());
    510      vbriSeekOffsetsTableSize = MOZ_TRY(aReader->ReadU16());
    511      vbriSeekOffsetsScaleFactor = MOZ_TRY(aReader->ReadU32());
    512      vbriSeekOffsetsBytesPerEntry = MOZ_TRY(aReader->ReadU16());
    513      vbriSeekOffsetsFramesPerEntry = MOZ_TRY(aReader->ReadU16());
    514 
    515      mTOC.reserve(vbriSeekOffsetsTableSize + 1);
    516 
    517      int (*readFunc)(BufferReader*) = nullptr;
    518      switch (vbriSeekOffsetsBytesPerEntry) {
    519        case 1:
    520          readFunc = &readAndConvertToInt<uint8_t>;
    521          break;
    522        case 2:
    523          readFunc = &readAndConvertToInt<int16_t>;
    524          break;
    525        case 4:
    526          readFunc = &readAndConvertToInt<int32_t>;
    527          break;
    528        case 8:
    529          readFunc = &readAndConvertToInt<int64_t>;
    530          break;
    531        default:
    532          MP3LOG("Unhandled vbriSeekOffsetsBytesPerEntry size of %hd",
    533                 vbriSeekOffsetsBytesPerEntry);
    534          break;
    535      }
    536      for (uint32_t i = 0; readFunc && i < vbriSeekOffsetsTableSize; i++) {
    537        int entry = readFunc(aReader);
    538        mTOC.push_back(entry * vbriSeekOffsetsScaleFactor);
    539      }
    540      MP3LOG(
    541          "Header::Parse found valid  header: EncoderVersion=%hu "
    542          "EncoderDelay=%hu "
    543          "Quality=%hu "
    544          "Bytes=%u "
    545          "Frames=%u "
    546          "SeekOffsetsTableSize=%u "
    547          "SeekOffsetsScaleFactor=%hu "
    548          "SeekOffsetsBytesPerEntry=%hu "
    549          "SeekOffsetsFramesPerEntry=%hu",
    550          vbriEncoderVersion, vbriEncoderDelay, vbriQuality, vbriBytes,
    551          vbriFrames, vbriSeekOffsetsTableSize, vbriSeekOffsetsScaleFactor,
    552          vbriSeekOffsetsBytesPerEntry, vbriSeekOffsetsFramesPerEntry);
    553      // Adjust the number of frames so it's counted the same way as in the XING
    554      // header
    555      if (vbriFrames < 1) {
    556        return false;
    557      }
    558      mNumAudioFrames = Some(vbriFrames - 1);
    559      mNumBytes = Some(vbriBytes);
    560      mEncoderDelay = vbriEncoderDelay;
    561      mVBRISeekOffsetsFramesPerEntry = vbriSeekOffsetsFramesPerEntry;
    562      MP3LOG("TOC:");
    563      for (auto entry : mTOC) {
    564        MP3LOG("%" PRId64, entry);
    565      }
    566 
    567      mType = VBRI;
    568      return true;
    569    }
    570  }
    571  return false;
    572 }
    573 
    574 bool FrameParser::VBRHeader::Parse(BufferReader* aReader, size_t aFrameSize) {
    575  auto res = std::make_pair(ParseVBRI(aReader), ParseXing(aReader, aFrameSize));
    576  const bool rv = (res.first.isOk() && res.first.unwrap()) ||
    577                  (res.second.isOk() && res.second.unwrap());
    578  if (rv) {
    579    MP3LOG(
    580        "VBRHeader::Parse found valid VBR/CBR header: type=%s"
    581        " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%zu Delay=%u",
    582        vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0),
    583        NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size(), mEncoderDelay);
    584  }
    585  return rv;
    586 }
    587 
    588 // FrameParser::Frame
    589 
    590 void FrameParser::Frame::Reset() { mHeader.Reset(); }
    591 
    592 uint32_t FrameParser::Frame::Length() const {
    593  if (!mHeader.IsValid() || !mHeader.SampleRate()) {
    594    return 0;
    595  }
    596 
    597  const uint32_t bitsPerSample = mHeader.SamplesPerFrame() / 8;
    598  const uint32_t frameLen =
    599      bitsPerSample * mHeader.Bitrate() / mHeader.SampleRate() +
    600      mHeader.Padding() * mHeader.SlotSize();
    601  return frameLen;
    602 }
    603 
    604 bool FrameParser::Frame::ParseNext(uint8_t c) { return mHeader.ParseNext(c); }
    605 
    606 const FrameParser::FrameHeader& FrameParser::Frame::Header() const {
    607  return mHeader;
    608 }
    609 
    610 bool FrameParser::ParseVBRHeader(BufferReader* aReader) {
    611  return mVBRHeader.Parse(aReader, CurrentFrame().Header().SamplesPerFrame());
    612 }
    613 
    614 // ID3Parser
    615 
    616 // Constants
    617 namespace id3_header {
    618 static const int ID_LEN = 3;
    619 static const int VERSION_LEN = 2;
    620 static const int FLAGS_LEN = 1;
    621 static const int SIZE_LEN = 4;
    622 
    623 static const int ID_END = ID_LEN;
    624 static const int VERSION_END = ID_END + VERSION_LEN;
    625 static const int FLAGS_END = VERSION_END + FLAGS_LEN;
    626 static const int SIZE_END = FLAGS_END + SIZE_LEN;
    627 
    628 static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
    629 static const uint8_t IDv1[ID_LEN] = {'T', 'A', 'G'};
    630 
    631 static const uint8_t MIN_MAJOR_VER = 2;
    632 static const uint8_t MAX_MAJOR_VER = 4;
    633 }  // namespace id3_header
    634 
    635 bool ID3Parser::IsBufferStartingWithID3v1Tag(BufferReader* aReader) {
    636  mozilla::Result<uint32_t, nsresult> res = aReader->PeekU24();
    637  if (res.isErr()) {
    638    return false;
    639  }
    640  // If buffer starts with ID3v1 tag, `rv` would be reverse and its content
    641  // should be '3' 'D' 'I' from the lowest bit.
    642  uint32_t rv = res.unwrap();
    643  for (int idx = id3_header::ID_LEN - 1; idx >= 0; idx--) {
    644    if ((rv & 0xff) != id3_header::IDv1[idx]) {
    645      return false;
    646    }
    647    rv = rv >> 8;
    648  }
    649  return true;
    650 }
    651 
    652 /* static */
    653 bool ID3Parser::IsBufferStartingWithID3Tag(BufferReader* aReader) {
    654  mozilla::Result<uint32_t, nsresult> res = aReader->PeekU24();
    655  if (res.isErr()) {
    656    return false;
    657  }
    658  // If buffer starts with ID3v2 tag, `rv` would be reverse and its content
    659  // should be '3' 'D' 'I' from the lowest bit.
    660  uint32_t rv = res.unwrap();
    661  for (int idx = id3_header::ID_LEN - 1; idx >= 0; idx--) {
    662    if ((rv & 0xff) != id3_header::ID[idx]) {
    663      return false;
    664    }
    665    rv = rv >> 8;
    666  }
    667  return true;
    668 }
    669 
    670 uint32_t ID3Parser::Parse(BufferReader* aReader) {
    671  MOZ_ASSERT(aReader);
    672  MOZ_ASSERT(ID3Parser::IsBufferStartingWithID3Tag(aReader));
    673 
    674  if (!mHeader.HasSizeBeenSet()) {
    675    return ParseInternal(aReader);
    676  }
    677 
    678  // Encounter another possible ID3 header, if that is valid then we would use
    679  // it and save the size of previous one in order to report the size of all ID3
    680  // headers together in `TotalHeadersSize()`.
    681  ID3Header prevHeader = mHeader;
    682  mHeader.Reset();
    683  uint32_t size = ParseInternal(aReader);
    684  if (!size) {
    685    // next ID3 is invalid, so revert the header.
    686    mHeader = prevHeader;
    687    return size;
    688  }
    689 
    690  mFormerID3Size += prevHeader.TotalTagSize();
    691  return size;
    692 }
    693 
    694 uint32_t ID3Parser::ParseInternal(BufferReader* aReader) {
    695  for (auto res = aReader->ReadU8();
    696       res.isOk() && !mHeader.ParseNext(res.unwrap());
    697       res = aReader->ReadU8()) {
    698  }
    699  return mHeader.TotalTagSize();
    700 }
    701 
    702 void ID3Parser::Reset() {
    703  mHeader.Reset();
    704  mFormerID3Size = 0;
    705 }
    706 
    707 uint32_t ID3Parser::TotalHeadersSize() const {
    708  return mHeader.TotalTagSize() + mFormerID3Size;
    709 }
    710 
    711 const ID3Parser::ID3Header& ID3Parser::Header() const { return mHeader; }
    712 
    713 // ID3Parser::Header
    714 
    715 ID3Parser::ID3Header::ID3Header() { Reset(); }
    716 
    717 void ID3Parser::ID3Header::Reset() {
    718  mSize.reset();
    719  mPos = 0;
    720 }
    721 
    722 uint8_t ID3Parser::ID3Header::MajorVersion() const {
    723  return mRaw[id3_header::ID_END];
    724 }
    725 
    726 uint8_t ID3Parser::ID3Header::MinorVersion() const {
    727  return mRaw[id3_header::ID_END + 1];
    728 }
    729 
    730 uint8_t ID3Parser::ID3Header::Flags() const {
    731  return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN];
    732 }
    733 
    734 uint32_t ID3Parser::ID3Header::Size() const {
    735  if (!IsValid() || !mSize) {
    736    return 0;
    737  }
    738  return *mSize;
    739 }
    740 
    741 bool ID3Parser::ID3Header::HasSizeBeenSet() const { return !!mSize; }
    742 
    743 uint8_t ID3Parser::ID3Header::FooterSize() const {
    744  if (Flags() & (1 << 4)) {
    745    return SIZE;
    746  }
    747  return 0;
    748 }
    749 
    750 uint32_t ID3Parser::ID3Header::TotalTagSize() const {
    751  if (IsValid()) {
    752    // Header found, return total tag size.
    753    return ID3Header::SIZE + Size() + FooterSize();
    754  }
    755  return 0;
    756 }
    757 
    758 bool ID3Parser::ID3Header::ParseNext(uint8_t c) {
    759  if (!Update(c)) {
    760    Reset();
    761    if (!Update(c)) {
    762      Reset();
    763    }
    764  }
    765  return IsValid();
    766 }
    767 
    768 bool ID3Parser::ID3Header::IsValid(int aPos) const {
    769  if (aPos >= SIZE) {
    770    return true;
    771  }
    772  const uint8_t c = mRaw[aPos];
    773  switch (aPos) {
    774    case 0:
    775    case 1:
    776    case 2:
    777      // Expecting "ID3".
    778      return id3_header::ID[aPos] == c;
    779    case 3:
    780      return MajorVersion() >= id3_header::MIN_MAJOR_VER &&
    781             MajorVersion() <= id3_header::MAX_MAJOR_VER;
    782    case 4:
    783      return MinorVersion() < 0xFF;
    784    case 5:
    785      // Validate flags for supported versions, see bug 949036.
    786      return ((0xFF >> MajorVersion()) & c) == 0;
    787    case 6:
    788    case 7:
    789    case 8:
    790    case 9:
    791      return c < 0x80;
    792  }
    793  return true;
    794 }
    795 
    796 bool ID3Parser::ID3Header::IsValid() const { return mPos >= SIZE; }
    797 
    798 bool ID3Parser::ID3Header::Update(uint8_t c) {
    799  if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN &&
    800      mPos < id3_header::SIZE_END) {
    801    uint32_t tmp = mSize.valueOr(0) << 7;
    802    mSize = Some(tmp | c);
    803  }
    804  if (mPos < SIZE) {
    805    mRaw[mPos] = c;
    806  }
    807  return IsValid(mPos++);
    808 }
    809 
    810 }  // namespace mozilla