Bug 1168435 - Part 2 - Refactor new MP3 frame parser out of the demuxer. r=jya
authorJan Henning <jh+bugzilla@buttercookie.de>
Sat, 10 Jun 2017 20:43:14 +0200
changeset 364569 d234d0f6c68bf458c520cdadaa7317894712d318
parent 364568 134c66e88b66b2575ac59f9652f95c4ce89f159d
child 364570 505412c61184054b8c215a73a982d2267ee833f8
push id32049
push usercbook@mozilla.com
push dateMon, 19 Jun 2017 11:36:23 +0000
treeherdermozilla-central@26d62a1ac0e3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1168435
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1168435 - Part 2 - Refactor new MP3 frame parser out of the demuxer. r=jya MozReview-Commit-ID: 7ZwjtMpuhRR
dom/media/MP3Decoder.cpp
dom/media/MP3Decoder.h
dom/media/MP3Demuxer.cpp
dom/media/MP3Demuxer.h
dom/media/moz.build
dom/media/mp3/MP3Decoder.cpp
dom/media/mp3/MP3Decoder.h
dom/media/mp3/MP3Demuxer.cpp
dom/media/mp3/MP3Demuxer.h
dom/media/mp3/MP3FrameParser.cpp
dom/media/mp3/MP3FrameParser.h
dom/media/mp3/moz.build
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -34,16 +34,17 @@ DIRS += [
     'flac',
     'gmp',
     'gmp-plugin',
     'gmp-plugin-openh264',
     'imagecapture',
     'ipc',
     'mediasink',
     'mediasource',
+    'mp3',
     'ogg',
     'platforms',
     'systemservices',
     'wave',
     'webaudio',
     'webm',
     'webrtc',
     'webspeech',
@@ -131,18 +132,16 @@ EXPORTS += [
     'MediaStatistics.h',
     'MediaStreamGraph.h',
     'MediaStreamListener.h',
     'MediaStreamVideoSink.h',
     'MediaTimer.h',
     'MediaTrack.h',
     'MediaTrackList.h',
     'MemoryBlockCache.h',
-    'MP3Decoder.h',
-    'MP3Demuxer.h',
     'nsIDocumentActivity.h',
     'PrincipalChangeObserver.h',
     'QueueObject.h',
     'SeekJob.h',
     'SeekTarget.h',
     'SelfRef.h',
     'SharedBuffer.h',
     'StreamTracks.h',
@@ -238,18 +237,16 @@ UNIFIED_SOURCES += [
     'MediaStreamGraph.cpp',
     'MediaStreamListener.cpp',
     'MediaStreamTrack.cpp',
     'MediaStreamVideoSink.cpp',
     'MediaTimer.cpp',
     'MediaTrack.cpp',
     'MediaTrackList.cpp',
     'MemoryBlockCache.cpp',
-    'MP3Decoder.cpp',
-    'MP3Demuxer.cpp',
     'QueueObject.cpp',
     'SeekJob.cpp',
     'StreamTracks.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
rename from dom/media/MP3Decoder.cpp
rename to dom/media/mp3/MP3Decoder.cpp
--- a/dom/media/MP3Decoder.cpp
+++ b/dom/media/mp3/MP3Decoder.cpp
@@ -1,16 +1,17 @@
 
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MP3Decoder.h"
+
 #include "MediaContainerType.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "MP3Demuxer.h"
 #include "PDMFactory.h"
 
 namespace mozilla {
 
rename from dom/media/MP3Decoder.h
rename to dom/media/mp3/MP3Decoder.h
rename from dom/media/MP3Demuxer.cpp
rename to dom/media/mp3/MP3Demuxer.cpp
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -1,27 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MP3Demuxer.h"
 
+#include <algorithm>
 #include <inttypes.h>
-#include <algorithm>
 #include <limits>
 
 #include "mozilla/Assertions.h"
-#include "mozilla/EndianUtils.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "nsAutoPtr.h"
+#include "TimeUnits.h"
 #include "VideoUtils.h"
-#include "TimeUnits.h"
-#include "prenv.h"
 
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define MP3LOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
 #define MP3LOGV(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
 
 using mozilla::media::TimeUnit;
@@ -771,719 +769,10 @@ MP3TrackDemuxer::AverageFrameLength() co
   const auto& vbr = mParser.VBRInfo();
   if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
     return static_cast<double>(vbr.NumBytes().value())
            / (vbr.NumAudioFrames().value() + 1);
   }
   return 0.0;
 }
 
-// FrameParser
-
-namespace frame_header {
-// FrameHeader mRaw byte offsets.
-static const int SYNC1 = 0;
-static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
-static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
-static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
-} // namespace frame_header
-
-FrameParser::FrameParser()
-{
-}
-
-void
-FrameParser::Reset()
-{
-  mID3Parser.Reset();
-  mFrame.Reset();
-}
-
-void
-FrameParser::ResetFrameData()
-{
-  mFrame.Reset();
-  mFirstFrame.Reset();
-  mPrevFrame.Reset();
-}
-
-void
-FrameParser::EndFrameSession()
-{
-  if (!mID3Parser.Header().IsValid()) {
-    // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
-    mID3Parser.Reset();
-  }
-  mPrevFrame = mFrame;
-  mFrame.Reset();
-}
-
-const FrameParser::Frame&
-FrameParser::CurrentFrame() const
-{
-  return mFrame;
-}
-
-const FrameParser::Frame&
-FrameParser::PrevFrame() const
-{
-  return mPrevFrame;
-}
-
-const FrameParser::Frame&
-FrameParser::FirstFrame() const
-{
-  return mFirstFrame;
-}
-
-const ID3Parser::ID3Header&
-FrameParser::ID3Header() const
-{
-  return mID3Parser.Header();
-}
-
-const FrameParser::VBRHeader&
-FrameParser::VBRInfo() const
-{
-  return mVBRHeader;
-}
-
-bool
-FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip)
-{
-  MOZ_ASSERT(aReader && aBytesToSkip);
-  *aBytesToSkip = 0;
-
-  if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) {
-    // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
-    // ID3v1 tags may only be at file end.
-    // TODO: should we try to read ID3 tags at end of file/mid-stream, too?
-    const size_t prevReaderOffset = aReader->Offset();
-    const uint32_t tagSize = mID3Parser.Parse(aReader);
-    if (tagSize) {
-      // ID3 tag found, skip past it.
-      const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
-
-      if (skipSize > aReader->Remaining()) {
-        // Skipping across the ID3v2 tag would take us past the end of the
-        // buffer, therefore we return immediately and let the calling function
-        // handle skipping the rest of the tag.
-        MP3LOGV("ID3v2 tag detected, size=%d,"
-                " needing to skip %" PRIuSIZE " bytes past the current buffer",
-                tagSize, skipSize - aReader->Remaining());
-        *aBytesToSkip = skipSize - aReader->Remaining();
-        return false;
-      }
-      MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
-      aReader->Read(skipSize);
-    } else {
-      // No ID3v2 tag found, rewinding reader in order to search for a MPEG
-      // frame header.
-      aReader->Seek(prevReaderOffset);
-    }
-  }
-
-  while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { }
-
-  if (mFrame.Length()) {
-    // MP3 frame found.
-    if (!mFirstFrame.Length()) {
-      mFirstFrame = mFrame;
-    }
-    // Indicate success.
-    return true;
-  }
-  return false;
-}
-
-// FrameParser::Header
-
-FrameParser::FrameHeader::FrameHeader()
-{
-  Reset();
-}
-
-uint8_t
-FrameParser::FrameHeader::Sync1() const
-{
-  return mRaw[frame_header::SYNC1];
-}
-
-uint8_t
-FrameParser::FrameHeader::Sync2() const
-{
-  return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawVersion() const
-{
-  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawLayer() const
-{
-  return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawProtection() const
-{
-  return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawBitrate() const
-{
-  return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4;
-}
-
-uint8_t
-FrameParser::FrameHeader::RawSampleRate() const
-{
-  return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2;
-}
-
-uint8_t
-FrameParser::FrameHeader::Padding() const
-{
-  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1;
-}
-
-uint8_t
-FrameParser::FrameHeader::Private() const
-{
-  return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE];
-}
-
-uint8_t
-FrameParser::FrameHeader::RawChannelMode() const
-{
-  return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6;
-}
-
-int32_t
-FrameParser::FrameHeader::Layer() const
-{
-  static const uint8_t LAYERS[4] = { 0, 3, 2, 1 };
-
-  return LAYERS[RawLayer()];
-}
-
-int32_t
-FrameParser::FrameHeader::SampleRate() const
-{
-  // Sample rates - use [version][srate]
-  static const uint16_t SAMPLE_RATE[4][4] = {
-    { 11025, 12000,  8000, 0 }, // MPEG 2.5
-    {     0,     0,     0, 0 }, // Reserved
-    { 22050, 24000, 16000, 0 }, // MPEG 2
-    { 44100, 48000, 32000, 0 }  // MPEG 1
-  };
-
-  return SAMPLE_RATE[RawVersion()][RawSampleRate()];
-}
-
-int32_t
-FrameParser::FrameHeader::Channels() const
-{
-  // 3 is single channel (mono), any other value is some variant of dual
-  // channel.
-  return RawChannelMode() == 3 ? 1 : 2;
-}
-
-int32_t
-FrameParser::FrameHeader::SamplesPerFrame() const
-{
-  // Samples per frame - use [version][layer]
-  static const uint16_t FRAME_SAMPLE[4][4] = {
-    // Layer     3     2     1       Version
-    {      0,  576, 1152,  384 }, // 2.5
-    {      0,    0,    0,    0 }, // Reserved
-    {      0,  576, 1152,  384 }, // 2
-    {      0, 1152, 1152,  384 }  // 1
-  };
-
-  return FRAME_SAMPLE[RawVersion()][RawLayer()];
-}
-
-int32_t
-FrameParser::FrameHeader::Bitrate() const
-{
-  // Bitrates - use [version][layer][bitrate]
-  static const uint16_t BITRATE[4][4][16] = {
-    { // Version 2.5
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
-      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
-      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
-      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
-    },
-    { // Reserved
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Invalid
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }  // Invalid
-    },
-    { // Version 2
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
-      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 3
-      { 0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0 }, // Layer 2
-      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }  // Layer 1
-    },
-    { // Version 1
-      { 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 }, // Reserved
-      { 0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
-      { 0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
-      { 0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
-    }
-  };
-
-  return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()];
-}
-
-int32_t
-FrameParser::FrameHeader::SlotSize() const
-{
-  // Slot size (MPEG unit of measurement) - use [layer]
-  static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
-
-  return SLOT_SIZE[RawLayer()];
-}
-
-bool
-FrameParser::FrameHeader::ParseNext(uint8_t c)
-{
-  if (!Update(c)) {
-    Reset();
-    if (!Update(c)) {
-      Reset();
-    }
-  }
-  return IsValid();
-}
-
-bool
-FrameParser::FrameHeader::IsValid(int aPos) const
-{
-  if (aPos >= SIZE) {
-    return true;
-  }
-  if (aPos == frame_header::SYNC1) {
-    return Sync1() == 0xFF;
-  }
-  if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) {
-    return Sync2() == 7 &&
-           RawVersion() != 1 &&
-           Layer() == 3;
-  }
-  if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) {
-    return RawBitrate() != 0xF && RawBitrate() != 0 &&
-           RawSampleRate() != 3;
-  }
-  return true;
-}
-
-bool
-FrameParser::FrameHeader::IsValid() const
-{
-  return mPos >= SIZE;
-}
-
-void
-FrameParser::FrameHeader::Reset()
-{
-  mPos = 0;
-}
-
-bool
-FrameParser::FrameHeader::Update(uint8_t c)
-{
-  if (mPos < SIZE) {
-    mRaw[mPos] = c;
-  }
-  return IsValid(mPos++);
-}
-
-// FrameParser::VBRHeader
-
-namespace vbr_header {
-static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"};
-static const uint32_t TOC_SIZE = 100;
-} // namespace vbr_header
-
-FrameParser::VBRHeader::VBRHeader()
-  : mType(NONE)
-{
-}
-
-FrameParser::VBRHeader::VBRHeaderType
-FrameParser::VBRHeader::Type() const
-{
-  return mType;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::NumAudioFrames() const
-{
-  return mNumAudioFrames;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::NumBytes() const
-{
-  return mNumBytes;
-}
-
-const Maybe<uint32_t>&
-FrameParser::VBRHeader::Scale() const
-{
-  return mScale;
-}
-
-bool
-FrameParser::VBRHeader::IsTOCPresent() const
-{
-  return mTOC.size() == vbr_header::TOC_SIZE;
-}
-
-bool
-FrameParser::VBRHeader::IsValid() const
-{
-  return mType != NONE;
-}
-
-bool
-FrameParser::VBRHeader::IsComplete() const
-{
-  return IsValid()
-         && mNumAudioFrames.valueOr(0) > 0
-         && mNumBytes.valueOr(0) > 0
-         // We don't care about the scale for any computations here.
-         // && mScale < 101
-         && true;
-}
-
-int64_t
-FrameParser::VBRHeader::Offset(float aDurationFac) const
-{
-  if (!IsTOCPresent()) {
-    return -1;
-  }
-
-  // Constrain the duration percentage to [0, 99].
-  const float durationPer =
-    100.0f * std::min(0.99f, std::max(0.0f, aDurationFac));
-  const size_t fullPer = durationPer;
-  const float rest = durationPer - fullPer;
-
-  MOZ_ASSERT(fullPer < mTOC.size());
-  int64_t offset = mTOC.at(fullPer);
-
-  if (rest > 0.0 && fullPer + 1 < mTOC.size()) {
-    offset += rest * (mTOC.at(fullPer + 1) - offset);
-  }
-
-  return offset;
-}
-
-bool
-FrameParser::VBRHeader::ParseXing(ByteReader* aReader)
-{
-  static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
-  static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
-
-  enum Flags
-  {
-    NUM_FRAMES = 0x01,
-    NUM_BYTES = 0x02,
-    TOC = 0x04,
-    VBR_SCALE = 0x08
-  };
-
-  MOZ_ASSERT(aReader);
-  const size_t prevReaderOffset = aReader->Offset();
-
-  // We have to search for the Xing header as its position can change.
-  while (aReader->CanRead32() &&
-         aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) {
-    aReader->Read(1);
-  }
-
-  if (aReader->CanRead32()) {
-    // Skip across the VBR header ID tag.
-    aReader->ReadU32();
-    mType = XING;
-  }
-  uint32_t flags = 0;
-  if (aReader->CanRead32()) {
-    flags = aReader->ReadU32();
-  }
-  if (flags & NUM_FRAMES && aReader->CanRead32()) {
-    mNumAudioFrames = Some(aReader->ReadU32());
-  }
-  if (flags & NUM_BYTES && aReader->CanRead32()) {
-    mNumBytes = Some(aReader->ReadU32());
-  }
-  if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
-    if (!mNumBytes) {
-      // We don't have the stream size to calculate offsets, skip the TOC.
-      aReader->Read(vbr_header::TOC_SIZE);
-    } else {
-      mTOC.clear();
-      mTOC.reserve(vbr_header::TOC_SIZE);
-      for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
-        mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value());
-      }
-    }
-  }
-  if (flags & VBR_SCALE && aReader->CanRead32()) {
-    mScale = Some(aReader->ReadU32());
-  }
-
-  aReader->Seek(prevReaderOffset);
-  return mType == XING;
-}
-
-bool
-FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader)
-{
-  static const uint32_t TAG = BigEndian::readUint32("VBRI");
-  static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
-  static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14;
-  static const uint32_t MIN_FRAME_SIZE = OFFSET + 26;
-
-  MOZ_ASSERT(aReader);
-  // ParseVBRI assumes that the ByteReader offset points to the beginning of a
-  // frame, therefore as a simple check, we look for the presence of a frame
-  // sync at that position.
-  MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0);
-  const size_t prevReaderOffset = aReader->Offset();
-
-  // VBRI have a fixed relative position, so let's check for it there.
-  if (aReader->Remaining() > MIN_FRAME_SIZE) {
-    aReader->Seek(prevReaderOffset + OFFSET);
-    if (aReader->ReadU32() == TAG) {
-      aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
-      mNumAudioFrames = Some(aReader->ReadU32());
-      mType = VBRI;
-      aReader->Seek(prevReaderOffset);
-      return true;
-    }
-  }
-  aReader->Seek(prevReaderOffset);
-  return false;
-}
-
-bool
-FrameParser::VBRHeader::Parse(ByteReader* aReader)
-{
-  const bool rv = ParseVBRI(aReader) || ParseXing(aReader);
-  if (rv) {
-    MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s"
-           " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%" PRIuSIZE,
-           vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0),
-           NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size());
-  }
-  return rv;
-}
-
-// FrameParser::Frame
-
-void
-FrameParser::Frame::Reset()
-{
-  mHeader.Reset();
-}
-
-int32_t
-FrameParser::Frame::Length() const
-{
-  if (!mHeader.IsValid() || !mHeader.SampleRate()) {
-    return 0;
-  }
-
-  const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f;
-  const int32_t frameLen = bitsPerSample * mHeader.Bitrate()
-                           / mHeader.SampleRate()
-                           + mHeader.Padding() * mHeader.SlotSize();
-  return frameLen;
-}
-
-bool
-FrameParser::Frame::ParseNext(uint8_t c)
-{
-  return mHeader.ParseNext(c);
-}
-
-const FrameParser::FrameHeader&
-FrameParser::Frame::Header() const
-{
-  return mHeader;
-}
-
-bool
-FrameParser::ParseVBRHeader(ByteReader* aReader)
-{
-  return mVBRHeader.Parse(aReader);
-}
-
-// ID3Parser
-
-// Constants
-namespace id3_header {
-static const int ID_LEN = 3;
-static const int VERSION_LEN = 2;
-static const int FLAGS_LEN = 1;
-static const int SIZE_LEN = 4;
-
-static const int ID_END = ID_LEN;
-static const int VERSION_END = ID_END + VERSION_LEN;
-static const int FLAGS_END = VERSION_END + FLAGS_LEN;
-static const int SIZE_END = FLAGS_END + SIZE_LEN;
-
-static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
-
-static const uint8_t MIN_MAJOR_VER = 2;
-static const uint8_t MAX_MAJOR_VER = 4;
-} // namespace id3_header
-
-uint32_t
-ID3Parser::Parse(ByteReader* aReader)
-{
-  MOZ_ASSERT(aReader);
-
-  while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { }
-
-  return mHeader.TotalTagSize();
-}
-
-void
-ID3Parser::Reset()
-{
-  mHeader.Reset();
-}
-
-const ID3Parser::ID3Header&
-ID3Parser::Header() const
-{
-  return mHeader;
-}
-
-// ID3Parser::Header
-
-ID3Parser::ID3Header::ID3Header()
-{
-  Reset();
-}
-
-void
-ID3Parser::ID3Header::Reset()
-{
-  mSize = 0;
-  mPos = 0;
-}
-
-uint8_t
-ID3Parser::ID3Header::MajorVersion() const
-{
-  return mRaw[id3_header::ID_END];
-}
-
-uint8_t
-ID3Parser::ID3Header::MinorVersion() const
-{
-  return mRaw[id3_header::ID_END + 1];
-}
-
-uint8_t
-ID3Parser::ID3Header::Flags() const
-{
-  return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN];
-}
-
-uint32_t
-ID3Parser::ID3Header::Size() const
-{
-  if (!IsValid()) {
-    return 0;
-  }
-  return mSize;
-}
-
-uint8_t
-ID3Parser::ID3Header::FooterSize() const
-{
-  if (Flags() & (1 << 4)) {
-    return SIZE;
-  }
-  return 0;
-}
-
-uint32_t
-ID3Parser::ID3Header::TotalTagSize() const
-{
-  if (IsValid()) {
-    // Header found, return total tag size.
-    return ID3Header::SIZE + Size() + FooterSize();
-  }
-  return 0;
-}
-
-bool
-ID3Parser::ID3Header::ParseNext(uint8_t c)
-{
-  if (!Update(c)) {
-    Reset();
-    if (!Update(c)) {
-      Reset();
-    }
-  }
-  return IsValid();
-}
-
-bool
-ID3Parser::ID3Header::IsValid(int aPos) const
-{
-  if (aPos >= SIZE) {
-    return true;
-  }
-  const uint8_t c = mRaw[aPos];
-  switch (aPos) {
-    case 0: case 1: case 2:
-      // Expecting "ID3".
-      return id3_header::ID[aPos] == c;
-    case 3:
-      return MajorVersion() >= id3_header::MIN_MAJOR_VER
-             && MajorVersion() <= id3_header::MAX_MAJOR_VER;
-    case 4:
-      return MinorVersion() < 0xFF;
-    case 5:
-      // Validate flags for supported versions, see bug 949036.
-      return ((0xFF >> MajorVersion()) & c) == 0;
-    case 6: case 7: case 8: case 9:
-      return c < 0x80;
-  }
-  return true;
-}
-
-bool
-ID3Parser::ID3Header::IsValid() const
-{
-  return mPos >= SIZE;
-}
-
-bool
-ID3Parser::ID3Header::Update(uint8_t c)
-{
-  if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN
-      && mPos < id3_header::SIZE_END) {
-    mSize <<= 7;
-    mSize |= c;
-  }
-  if (mPos < SIZE) {
-    mRaw[mPos] = c;
-  }
-  return IsValid(mPos++);
-}
-
 } // namespace mp3
 } // namespace mozilla
rename from dom/media/MP3Demuxer.h
rename to dom/media/mp3/MP3Demuxer.h
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/mp3/MP3Demuxer.h
@@ -1,21 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MP3_DEMUXER_H_
 #define MP3_DEMUXER_H_
 
-#include "mozilla/Attributes.h"
-#include "mozilla/Maybe.h"
 #include "MediaDataDemuxer.h"
 #include "MediaResource.h"
-#include "mp4_demuxer/ByteReader.h"
-#include <vector>
+#include "MP3FrameParser.h"
 
 namespace mozilla {
 namespace mp3 {
 
 class MP3TrackDemuxer;
 
 class MP3Demuxer : public MediaDataDemuxer
 {
@@ -34,341 +31,16 @@ public:
 private:
   // Synchronous initialization.
   bool InitInternal();
 
   RefPtr<MediaResource> mSource;
   RefPtr<MP3TrackDemuxer> mTrackDemuxer;
 };
 
-// ID3 header parser state machine used by FrameParser.
-// The header contains the following format (one byte per term):
-// 'I' 'D' '3' MajorVersion MinorVersion Flags Size1 Size2 Size3 Size4
-// For more details see http://id3.org/id3v2.3.0.
-class ID3Parser
-{
-public:
-  // Holds the ID3 header and its parsing state.
-  class ID3Header
-  {
-  public:
-    // The header size is static, see class comment.
-    static const int SIZE = 10;
-
-    // Constructor.
-    ID3Header();
-
-    // Resets the state to allow for a new parsing session.
-    void Reset();
-
-    // The ID3 tags are versioned like this: ID3vMajorVersion.MinorVersion.
-    uint8_t MajorVersion() const;
-    uint8_t MinorVersion() const;
-
-    // The ID3 flags field.
-    uint8_t Flags() const;
-
-    // The derived size based on the provided size fields.
-    uint32_t Size() const;
-
-    // Returns the size of an ID3v2.4 footer if present and zero otherwise.
-    uint8_t FooterSize() const;
-
-    // The total size of the ID3 tag including header/footer, or zero if
-    // none has been found.
-    uint32_t TotalTagSize() const;
-
-    // Returns whether the parsed data is a valid ID3 header up to the given
-    // byte position.
-    bool IsValid(int aPos) const;
-
-    // Returns whether the parsed data is a complete and valid ID3 header.
-    bool IsValid() const;
-
-    // Parses the next provided byte.
-    // Returns whether the byte creates a valid sequence up to this point.
-    bool ParseNext(uint8_t c);
-
-  private:
-    // Updates the parser state machine with the provided next byte.
-    // Returns whether the provided byte is a valid next byte in the sequence.
-    bool Update(uint8_t c);
-
-    // The currently parsed byte sequence.
-    uint8_t mRaw[SIZE];
-
-    // The derived size as provided by the size fields.
-    // The header size fields holds a 4 byte sequence with each MSB set to 0,
-    // this bits need to be ignored when deriving the actual size.
-    uint32_t mSize;
-
-    // The current byte position in the parsed sequence. Reset via Reset and
-    // incremented via Update.
-    int mPos;
-  };
-
-  // Returns the parsed ID3 header. Note: check for validity.
-  const ID3Header& Header() const;
-
-  // Parses contents of given ByteReader for a valid ID3v2 header.
-  // Returns the total ID3v2 tag size if successful and zero otherwise.
-  uint32_t Parse(mp4_demuxer::ByteReader* aReader);
-
-  // Resets the state to allow for a new parsing session.
-  void Reset();
-
-private:
-  // The currently parsed ID3 header. Reset via Reset, updated via Parse.
-  ID3Header mHeader;
-};
-
-// MPEG audio frame parser.
-// The MPEG frame header has the following format (one bit per character):
-// 11111111 111VVLLC BBBBSSPR MMEETOHH
-// {   sync   } - 11 sync bits
-//   VV         - MPEG audio version ID (0->2.5, 1->reserved, 2->2, 3->1)
-//   LL         - Layer description (0->reserved, 1->III, 2->II, 3->I)
-//   C          - CRC protection bit (0->protected, 1->not protected)
-//   BBBB       - Bitrate index (see table in implementation)
-//   SS         - Sampling rate index (see table in implementation)
-//   P          - Padding bit (0->not padded, 1->padded by 1 slot size)
-//   R          - Private bit (ignored)
-//   MM         - Channel mode (0->stereo, 1->joint stereo, 2->dual channel,
-//                3->single channel)
-//   EE         - Mode extension for joint stereo (ignored)
-//   T          - Copyright (0->disabled, 1->enabled)
-//   O          - Original (0->copy, 1->original)
-//   HH         - Emphasis (0->none, 1->50/15 ms, 2->reserved, 3->CCIT J.17)
-class FrameParser
-{
-public:
-  // Holds the frame header and its parsing state.
-  class FrameHeader
-  {
-  public:
-    // The header size is static, see class comments.
-    static const int SIZE = 4;
-
-    // Constructor.
-    FrameHeader();
-
-    // Raw field access, see class comments for details.
-    uint8_t Sync1() const;
-    uint8_t Sync2() const;
-    uint8_t RawVersion() const;
-    uint8_t RawLayer() const;
-    uint8_t RawProtection() const;
-    uint8_t RawBitrate() const;
-    uint8_t RawSampleRate() const;
-    uint8_t Padding() const;
-    uint8_t Private() const;
-    uint8_t RawChannelMode() const;
-
-    // Sampling rate frequency in Hz.
-    int32_t SampleRate() const;
-
-    // Number of audio channels.
-    int32_t Channels() const;
-
-    // Samples per frames, static depending on MPEG version and layer.
-    int32_t SamplesPerFrame() const;
-
-    // Slot size used for padding, static depending on MPEG layer.
-    int32_t SlotSize() const;
-
-    // Bitrate in kbps, can vary between frames.
-    int32_t Bitrate() const;
-
-    // MPEG layer (0->invalid, 1->I, 2->II, 3->III).
-    int32_t Layer() const;
-
-    // Returns whether the parsed data is a valid frame header up to the given
-    // byte position.
-    bool IsValid(const int aPos) const;
-
-    // Returns whether the parsed data is a complete and valid frame header.
-    bool IsValid() const;
-
-    // Resets the state to allow for a new parsing session.
-    void Reset();
-
-    // Parses the next provided byte.
-    // Returns whether the byte creates a valid sequence up to this point.
-    bool ParseNext(const uint8_t c);
-
-  private:
-    // Updates the parser state machine with the provided next byte.
-    // Returns whether the provided byte is a valid next byte in the sequence.
-    bool Update(const uint8_t c);
-
-    // The currently parsed byte sequence.
-    uint8_t mRaw[SIZE];
-
-    // The current byte position in the parsed sequence. Reset via Reset and
-    // incremented via Update.
-    int mPos;
-  };
-
-  // VBR frames may contain Xing or VBRI headers for additional info, we use
-  // this class to parse them and access this info.
-  class VBRHeader
-  {
-  public:
-    // Synchronize with vbr_header TYPE_STR on change.
-    enum VBRHeaderType
-    {
-      NONE = 0,
-      XING,
-      VBRI
-    };
-
-    // Constructor.
-    VBRHeader();
-
-    // Returns the parsed VBR header type, or NONE if no valid header found.
-    VBRHeaderType Type() const;
-
-    // Returns the total number of audio frames (excluding the VBR header frame)
-    // expected in the stream/file.
-    const Maybe<uint32_t>& NumAudioFrames() const;
-
-    // Returns the expected size of the stream.
-    const Maybe<uint32_t>& NumBytes() const;
-
-    // Returns the VBR scale factor (0: best quality, 100: lowest quality).
-    const Maybe<uint32_t>& Scale() const;
-
-    // Returns true iff Xing/Info TOC (table of contents) is present.
-    bool IsTOCPresent() const;
-
-    // Returns whether the header is valid (type XING or VBRI).
-    bool IsValid() const;
-
-    // Returns whether the header is valid and contains reasonable non-zero
-    // field values.
-    bool IsComplete() const;
-
-    // Returns the byte offset for the given duration percentage as a factor
-    // (0: begin, 1.0: end).
-    int64_t Offset(float aDurationFac) const;
-
-    // Parses contents of given ByteReader for a valid VBR header.
-    // The offset of the passed ByteReader needs to point to an MPEG frame
-    // begin, as a VBRI-style header is searched at a fixed offset relative to
-    // frame begin. Returns whether a valid VBR header was found in the range.
-    bool Parse(mp4_demuxer::ByteReader* aReader);
-
-  private:
-    // Parses contents of given ByteReader for a valid Xing header.
-    // The initial ByteReader offset will be preserved.
-    // Returns whether a valid Xing header was found in the range.
-    bool ParseXing(mp4_demuxer::ByteReader* aReader);
-
-    // Parses contents of given ByteReader for a valid VBRI header.
-    // The initial ByteReader offset will be preserved. It also needs to point
-    // to the beginning of a valid MPEG frame, as VBRI headers are searched
-    // at a fixed offset relative to frame begin.
-    // Returns whether a valid VBRI header was found in the range.
-    bool ParseVBRI(mp4_demuxer::ByteReader* aReader);
-
-    // The total number of frames expected as parsed from a VBR header.
-    Maybe<uint32_t> mNumAudioFrames;
-
-    // The total number of bytes expected in the stream.
-    Maybe<uint32_t> mNumBytes;
-
-    // The VBR scale factor.
-    Maybe<uint32_t> mScale;
-
-    // The TOC table mapping duration percentage to byte offset.
-    std::vector<int64_t> mTOC;
-
-    // The detected VBR header type.
-    VBRHeaderType mType;
-  };
-
-  // Frame meta container used to parse and hold a frame header and side info.
-  class Frame
-  {
-  public:
-    // Returns the length of the frame excluding the header in bytes.
-    int32_t Length() const;
-
-    // Returns the parsed frame header.
-    const FrameHeader& Header() const;
-
-    // Resets the frame header and data.
-    void Reset();
-
-    // Parses the next provided byte.
-    // Returns whether the byte creates a valid sequence up to this point.
-    bool ParseNext(uint8_t c);
-
-  private:
-    // The currently parsed frame header.
-    FrameHeader mHeader;
-  };
-
-  // Constructor.
-  FrameParser();
-
-  // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
-  const Frame& CurrentFrame() const;
-
-  // Returns the previously parsed frame. Reset via Reset.
-  const Frame& PrevFrame() const;
-
-  // Returns the first parsed frame. Reset via Reset.
-  const Frame& FirstFrame() const;
-
-  // Returns the parsed ID3 header. Note: check for validity.
-  const ID3Parser::ID3Header& ID3Header() const;
-
-  // Returns the parsed VBR header info. Note: check for validity by type.
-  const VBRHeader& VBRInfo() const;
-
-  // Resets the parser.
-  void Reset();
-
-  // Resets all frame data, but not the ID3Header.
-  // Don't use between frames as first frame data is reset.
-  void ResetFrameData();
-
-  // Clear the last parsed frame to allow for next frame parsing, i.e.:
-  // - sets PrevFrame to CurrentFrame
-  // - resets the CurrentFrame
-  // - resets ID3Header if no valid header was parsed yet
-  void EndFrameSession();
-
-  // Parses contents of given ByteReader for a valid frame header and returns
-  // true if one was found. After returning, the variable passed to
-  // 'aBytesToSkip' holds the amount of bytes to be skipped (if any) in order to
-  // jump across a large ID3v2 tag spanning multiple buffers.
-  bool Parse(mp4_demuxer::ByteReader* aReader, uint32_t* aBytesToSkip);
-
-  // Parses contents of given ByteReader for a valid VBR header.
-  // The offset of the passed ByteReader needs to point to an MPEG frame begin,
-  // as a VBRI-style header is searched at a fixed offset relative to frame
-  // begin. Returns whether a valid VBR header was found.
-  bool ParseVBRHeader(mp4_demuxer::ByteReader* aReader);
-
-private:
-  // ID3 header parser.
-  ID3Parser mID3Parser;
-
-  // VBR header parser.
-  VBRHeader mVBRHeader;
-
-  // We keep the first parsed frame around for static info access, the
-  // previously parsed frame for debugging and the currently parsed frame.
-  Frame mFirstFrame;
-  Frame mFrame;
-  Frame mPrevFrame;
-};
-
 // The MP3 demuxer used to extract MPEG frames and side information out of
 // MPEG streams.
 class MP3TrackDemuxer : public MediaTrackDemuxer
 {
 public:
   // Constructor, expecting a valid media resource.
   explicit MP3TrackDemuxer(MediaResource* aSource);
 
copy from dom/media/MP3Demuxer.cpp
copy to dom/media/mp3/MP3FrameParser.cpp
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3FrameParser.cpp
@@ -1,786 +1,35 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "MP3Demuxer.h"
+#include "MP3FrameParser.h"
 
+#include <algorithm>
 #include <inttypes.h>
-#include <algorithm>
-#include <limits>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/SizePrintfMacros.h"
-#include "nsAutoPtr.h"
 #include "VideoUtils.h"
-#include "TimeUnits.h"
-#include "prenv.h"
 
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 #define MP3LOG(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
 #define MP3LOGV(msg, ...) \
   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
 
-using mozilla::media::TimeUnit;
-using mozilla::media::TimeInterval;
-using mozilla::media::TimeIntervals;
 using mp4_demuxer::ByteReader;
 
 namespace mozilla {
 namespace mp3 {
 
-// MP3Demuxer
-
-MP3Demuxer::MP3Demuxer(MediaResource* aSource) : mSource(aSource) { }
-
-bool
-MP3Demuxer::InitInternal()
-{
-  if (!mTrackDemuxer) {
-    mTrackDemuxer = new MP3TrackDemuxer(mSource);
-  }
-  return mTrackDemuxer->Init();
-}
-
-RefPtr<MP3Demuxer::InitPromise>
-MP3Demuxer::Init()
-{
-  if (!InitInternal()) {
-    MP3LOG("MP3Demuxer::Init() failure: waiting for data");
-
-    return InitPromise::CreateAndReject(
-      NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
-  }
-
-  MP3LOG("MP3Demuxer::Init() successful");
-  return InitPromise::CreateAndResolve(NS_OK, __func__);
-}
-
-bool
-MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack;
-}
-
-uint32_t
-MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
-{
-  return aType == TrackInfo::kAudioTrack ? 1u : 0u;
-}
-
-already_AddRefed<MediaTrackDemuxer>
-MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
-{
-  if (!mTrackDemuxer) {
-    return nullptr;
-  }
-  return RefPtr<MP3TrackDemuxer>(mTrackDemuxer).forget();
-}
-
-bool
-MP3Demuxer::IsSeekable() const
-{
-  return true;
-}
-
-void
-MP3Demuxer::NotifyDataArrived()
-{
-  // TODO: bug 1169485.
-  NS_WARNING("Unimplemented function NotifyDataArrived");
-  MP3LOGV("NotifyDataArrived()");
-}
-
-void
-MP3Demuxer::NotifyDataRemoved()
-{
-  // TODO: bug 1169485.
-  NS_WARNING("Unimplemented function NotifyDataRemoved");
-  MP3LOGV("NotifyDataRemoved()");
-}
-
-
-// MP3TrackDemuxer
-
-MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource)
-  : mSource(aSource)
-  , mFrameLock(false)
-  , mOffset(0)
-  , mFirstFrameOffset(0)
-  , mNumParsedFrames(0)
-  , mFrameIndex(0)
-  , mTotalFrameLen(0)
-  , mSamplesPerFrame(0)
-  , mSamplesPerSecond(0)
-  , mChannels(0)
-{
-  Reset();
-}
-
-bool
-MP3TrackDemuxer::Init()
-{
-  Reset();
-  FastSeek(TimeUnit());
-  // Read the first frame to fetch sample rate and other meta data.
-  RefPtr<MediaRawData> frame(GetNextFrame(FindFirstFrame()));
-
-  MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
-         StreamLength(), !!frame);
-
-  if (!frame) {
-    return false;
-  }
-
-  // Rewind back to the stream begin to avoid dropping the first frame.
-  FastSeek(TimeUnit());
-
-  if (!mInfo) {
-    mInfo = MakeUnique<AudioInfo>();
-  }
-
-  mInfo->mRate = mSamplesPerSecond;
-  mInfo->mChannels = mChannels;
-  mInfo->mBitDepth = 16;
-  mInfo->mMimeType = "audio/mpeg";
-  mInfo->mDuration = Duration();
-
-  MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}",
-         mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
-         mInfo->mDuration.ToMicroseconds());
-
-  return mSamplesPerSecond && mChannels;
-}
-
-media::TimeUnit
-MP3TrackDemuxer::SeekPosition() const
-{
-  TimeUnit pos = Duration(mFrameIndex);
-  if (Duration() > TimeUnit()) {
-    pos = std::min(Duration(), pos);
-  }
-  return pos;
-}
-
-const FrameParser::Frame&
-MP3TrackDemuxer::LastFrame() const
-{
-  return mParser.PrevFrame();
-}
-
-RefPtr<MediaRawData>
-MP3TrackDemuxer::DemuxSample()
-{
-  return GetNextFrame(FindNextFrame());
-}
-
-const ID3Parser::ID3Header&
-MP3TrackDemuxer::ID3Header() const
-{
-  return mParser.ID3Header();
-}
-
-const FrameParser::VBRHeader&
-MP3TrackDemuxer::VBRInfo() const
-{
-  return mParser.VBRInfo();
-}
-
-UniquePtr<TrackInfo>
-MP3TrackDemuxer::GetInfo() const
-{
-  return mInfo->Clone();
-}
-
-RefPtr<MP3TrackDemuxer::SeekPromise>
-MP3TrackDemuxer::Seek(const TimeUnit& aTime)
-{
-  // Efficiently seek to the position.
-  FastSeek(aTime);
-  // Correct seek position by scanning the next frames.
-  const TimeUnit seekTime = ScanUntil(aTime);
-
-  return SeekPromise::CreateAndResolve(seekTime, __func__);
-}
-
-TimeUnit
-MP3TrackDemuxer::FastSeek(const TimeUnit& aTime)
-{
-  MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
-         " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
-         mFrameIndex, mOffset);
-
-  const auto& vbr = mParser.VBRInfo();
-  if (!aTime.ToMicroseconds()) {
-    // Quick seek to the beginning of the stream.
-    mFrameIndex = 0;
-  } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
-    // Use TOC for more precise seeking.
-    const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
-                                                  Duration().ToMicroseconds();
-    mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac));
-  } else if (AverageFrameLength() > 0) {
-    mFrameIndex = FrameIndexFromTime(aTime);
-  }
-
-  mOffset = OffsetFromFrameIndex(mFrameIndex);
-
-  if (mOffset > mFirstFrameOffset && StreamLength() > 0) {
-    mOffset = std::min(StreamLength() - 1, mOffset);
-  }
-
-  mParser.EndFrameSession();
-
-  MP3LOG("FastSeek End TOC=%d avgFrameLen=%f mNumParsedFrames=%" PRIu64
-         " mFrameIndex=%" PRId64 " mFirstFrameOffset=%" PRId64 " mOffset=%" PRIu64
-         " SL=%" PRId64 " NumBytes=%u",
-         vbr.IsTOCPresent(), AverageFrameLength(), mNumParsedFrames, mFrameIndex,
-         mFirstFrameOffset, mOffset, StreamLength(), vbr.NumBytes().valueOr(0));
-
-  return Duration(mFrameIndex);
-}
-
-TimeUnit
-MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime)
-{
-  MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
-         " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
-         mFrameIndex, mOffset);
-
-  if (!aTime.ToMicroseconds()) {
-    return FastSeek(aTime);
-  }
-
-  if (Duration(mFrameIndex) > aTime) {
-    // We've seeked past the target time, rewind back a little to correct it.
-    const int64_t rewind = aTime.ToMicroseconds() / 100;
-    FastSeek(aTime - TimeUnit::FromMicroseconds(rewind));
-  }
-
-  if (Duration(mFrameIndex + 1) > aTime) {
-    return SeekPosition();
-  }
-
-  MediaByteRange nextRange = FindNextFrame();
-  while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
-    nextRange = FindNextFrame();
-    MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
-            " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
-            AverageFrameLength(), mNumParsedFrames,
-            mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
-  }
-
-  MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
-         " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
-
-  return SeekPosition();
-}
-
-RefPtr<MP3TrackDemuxer::SamplesPromise>
-MP3TrackDemuxer::GetSamples(int32_t aNumSamples)
-{
-  MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
-          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
-          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
-          aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
-          mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
-  if (!aNumSamples) {
-    return SamplesPromise::CreateAndReject(
-        NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
-  }
-
-  RefPtr<SamplesHolder> frames = new SamplesHolder();
-
-  while (aNumSamples--) {
-    RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
-    if (!frame) {
-      break;
-    }
-
-    frames->mSamples.AppendElement(frame);
-  }
-
-  MP3LOGV("GetSamples() End mSamples.Size()=%" PRIuSIZE " aNumSamples=%d mOffset=%" PRIu64
-          " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
-          " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
-          "mChannels=%d",
-          frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames,
-          mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
-          mChannels);
-
-  if (frames->mSamples.IsEmpty()) {
-    return SamplesPromise::CreateAndReject(
-        NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
-  }
-  return SamplesPromise::CreateAndResolve(frames, __func__);
-}
-
-void
-MP3TrackDemuxer::Reset()
-{
-  MP3LOG("Reset()");
-
-  FastSeek(TimeUnit());
-  mParser.Reset();
-}
-
-RefPtr<MP3TrackDemuxer::SkipAccessPointPromise>
-MP3TrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
-{
-  // Will not be called for audio-only resources.
-  return SkipAccessPointPromise::CreateAndReject(
-    SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
-}
-
-int64_t
-MP3TrackDemuxer::GetResourceOffset() const
-{
-  return mOffset;
-}
-
-TimeIntervals
-MP3TrackDemuxer::GetBuffered()
-{
-  AutoPinned<MediaResource> stream(mSource.GetResource());
-  TimeIntervals buffered;
-
-  if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) {
-    // Special case completely cached files. This also handles local files.
-    buffered += TimeInterval(TimeUnit(), Duration());
-    MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]",
-            TimeUnit().ToMicroseconds(), Duration().ToMicroseconds());
-    return buffered;
-  }
-
-  MediaByteRangeSet ranges;
-  nsresult rv = stream->GetCachedRanges(ranges);
-  NS_ENSURE_SUCCESS(rv, buffered);
-
-  for (const auto& range: ranges) {
-    if (range.IsEmpty()) {
-      continue;
-    }
-    TimeUnit start = Duration(FrameIndexFromOffset(range.mStart));
-    TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd));
-    MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]",
-            start.ToMicroseconds(), end.ToMicroseconds());
-    buffered += TimeInterval(start, end);
-  }
-
-  return buffered;
-}
-
-int64_t
-MP3TrackDemuxer::StreamLength() const
-{
-  return mSource.GetLength();
-}
-
-TimeUnit
-MP3TrackDemuxer::Duration() const
-{
-  if (!mNumParsedFrames) {
-    return TimeUnit::FromMicroseconds(-1);
-  }
-
-  int64_t numFrames = 0;
-  const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
-  if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
-    // VBR headers don't include the VBR header frame.
-    numFrames = numAudioFrames.value() + 1;
-  } else {
-    const int64_t streamLen = StreamLength();
-    if (streamLen < 0) {
-      // Unknown length, we can't estimate duration.
-      return TimeUnit::FromMicroseconds(-1);
-    }
-    if (AverageFrameLength() > 0) {
-      numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
-    }
-  }
-  return Duration(numFrames);
-}
-
-TimeUnit
-MP3TrackDemuxer::Duration(int64_t aNumFrames) const
-{
-  if (!mSamplesPerSecond) {
-    return TimeUnit::FromMicroseconds(-1);
-  }
-
-  const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
-  return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
-}
-
-MediaByteRange
-MP3TrackDemuxer::FindFirstFrame()
-{
-  // We attempt to find multiple successive frames to avoid locking onto a false
-  // positive if we're fed a stream that has been cut mid-frame.
-  // For compatibility reasons we have to use the same frame count as Chrome, since
-  // some web sites actually use a file that short to test our playback capabilities.
-  static const int MIN_SUCCESSIVE_FRAMES = 3;
-  mFrameLock = false;
-
-  MediaByteRange candidateFrame = FindNextFrame();
-  int numSuccFrames = candidateFrame.Length() > 0;
-  MediaByteRange currentFrame = candidateFrame;
-  MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64
-          " Length()=%" PRIu64,
-          candidateFrame.mStart, candidateFrame.Length());
-
-  while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
-    mParser.EndFrameSession();
-    mOffset = currentFrame.mEnd;
-    const MediaByteRange prevFrame = currentFrame;
-
-    // FindNextFrame() here will only return frames consistent with our candidate frame.
-    currentFrame = FindNextFrame();
-    numSuccFrames += currentFrame.Length() > 0;
-    // Multiple successive false positives, which wouldn't be caught by the consistency
-    // checks alone, can be detected by wrong alignment (non-zero gap between frames).
-    const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;
-
-    if (!currentFrame.Length() || frameSeparation != 0) {
-      MP3LOGV("FindFirst() not enough successive frames detected, "
-              "rejecting candidate frame: successiveFrames=%d, last "
-              "Length()=%" PRIu64 ", last frameSeparation=%" PRId64,
-              numSuccFrames, currentFrame.Length(), frameSeparation);
-
-      mParser.ResetFrameData();
-      mOffset = candidateFrame.mStart + 1;
-      candidateFrame = FindNextFrame();
-      numSuccFrames = candidateFrame.Length() > 0;
-      currentFrame = candidateFrame;
-      MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64
-              " Length()=%" PRIu64,
-              candidateFrame.mStart, candidateFrame.Length());
-    }
-  }
-
-  if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
-    MP3LOG("FindFirst() accepting candidate frame: "
-           "successiveFrames=%d", numSuccFrames);
-    mFrameLock = true;
-  } else {
-    MP3LOG("FindFirst() no suitable first frame found");
-  }
-  return candidateFrame;
-}
-
-static bool
-VerifyFrameConsistency(const FrameParser::Frame& aFrame1,
-                       const FrameParser::Frame& aFrame2)
-{
-  const auto& h1 = aFrame1.Header();
-  const auto& h2 = aFrame2.Header();
-
-  return h1.IsValid()
-         && h2.IsValid()
-         && h1.Layer() == h2.Layer()
-         && h1.SlotSize() == h2.SlotSize()
-         && h1.SamplesPerFrame() == h2.SamplesPerFrame()
-         && h1.Channels() == h2.Channels()
-         && h1.SampleRate() == h2.SampleRate()
-         && h1.RawVersion() == h2.RawVersion()
-         && h1.RawProtection() == h2.RawProtection();
-}
-
-MediaByteRange
-MP3TrackDemuxer::FindNextFrame()
-{
-  static const int BUFFER_SIZE = 64;
-  static const uint32_t MAX_SKIPPABLE_BYTES = 1024 * BUFFER_SIZE;
-
-  MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
-          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
-          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
-          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
-          mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
-  uint8_t buffer[BUFFER_SIZE];
-  int32_t read = 0;
-
-  bool foundFrame = false;
-  int64_t frameHeaderOffset = 0;
-  int64_t startOffset = mOffset;
-  const bool searchingForID3 = !mParser.ID3Header().Size();
-
-  // Check whether we've found a valid MPEG frame.
-  while (!foundFrame) {
-    // How many bytes we can go without finding a valid MPEG frame
-    // (effectively rounded up to the next full buffer size multiple, as we
-    // only check this before reading the next set of data into the buffer).
-
-    // This default value of 0 will be used during testing whether we're being
-    // fed a valid stream, which shouldn't have any gaps between frames.
-    uint32_t maxSkippableBytes = 0;
-
-    if (!mParser.FirstFrame().Length()) {
-      // We're looking for the first valid frame. A well-formed file should
-      // have its first frame header right at the start (skipping an ID3 tag
-      // if necessary), but in order to support files that might have been
-      // improperly cut, we search the first few kB for a frame header.
-      maxSkippableBytes = MAX_SKIPPABLE_BYTES;
-      // Since we're counting the skipped bytes from the offset we started
-      // this parsing session with, we need to discount the ID3 tag size only
-      // if we were looking for one during the current frame parsing session.
-      if (searchingForID3) {
-        maxSkippableBytes += mParser.ID3Header().TotalTagSize();
-      }
-    } else if (mFrameLock) {
-      // We've found a valid MPEG stream, so don't impose any limits
-      // to allow skipping corrupted data until we hit EOS.
-      maxSkippableBytes = std::numeric_limits<uint32_t>::max();
-    }
-
-    if ((mOffset - startOffset > maxSkippableBytes)
-        || (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) {
-      MP3LOG("FindNext() EOS or exceeded maxSkippeableBytes without a frame");
-      // This is not a valid MPEG audio stream or we've reached EOS, give up.
-      break;
-    }
-
-    ByteReader reader(buffer, read);
-    uint32_t bytesToSkip = 0;
-    foundFrame = mParser.Parse(&reader, &bytesToSkip);
-    frameHeaderOffset =
-      mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE;
-
-    // If we've found neither an MPEG frame header nor an ID3v2 tag,
-    // the reader shouldn't have any bytes remaining.
-    MOZ_ASSERT(foundFrame || bytesToSkip || !reader.Remaining());
-
-    if (foundFrame && mParser.FirstFrame().Length()
-        && !VerifyFrameConsistency(mParser.FirstFrame(),
-                                   mParser.CurrentFrame())) {
-      // We've likely hit a false-positive, ignore it and proceed with the
-      // search for the next valid frame.
-      foundFrame = false;
-      mOffset = frameHeaderOffset + 1;
-      mParser.EndFrameSession();
-    } else {
-      // Advance mOffset by the amount of bytes read and if necessary,
-      // skip an ID3v2 tag which stretches beyond the current buffer.
-      NS_ENSURE_TRUE(mOffset + read + bytesToSkip > mOffset,
-                     MediaByteRange(0, 0));
-      mOffset += read + bytesToSkip;
-    }
-  }
-
-  if (!foundFrame || !mParser.CurrentFrame().Length()) {
-    MP3LOG("FindNext() Exit foundFrame=%d mParser.CurrentFrame().Length()=%d ",
-           foundFrame, mParser.CurrentFrame().Length());
-    return { 0, 0 };
-  }
-
-  MP3LOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
-          " mFrameIndex=%" PRId64 " frameHeaderOffset=%" PRId64
-          " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
-          " mChannels=%d",
-          mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
-          mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
-  return { frameHeaderOffset, frameHeaderOffset + mParser.CurrentFrame().Length() };
-}
-
-bool
-MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange)
-{
-  if (!mNumParsedFrames || !aRange.Length()) {
-    // We can't skip the first frame, since it could contain VBR headers.
-    RefPtr<MediaRawData> frame(GetNextFrame(aRange));
-    return frame;
-  }
-
-  UpdateState(aRange);
-
-  MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
-          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
-          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
-          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
-          mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
-  return true;
-}
-
-already_AddRefed<MediaRawData>
-MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange)
-{
-  MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})",
-         aRange.mStart, aRange.Length());
-  if (!aRange.Length()) {
-    return nullptr;
-  }
-
-  RefPtr<MediaRawData> frame = new MediaRawData();
-  frame->mOffset = aRange.mStart;
-
-  nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
-  if (!frameWriter->SetSize(aRange.Length())) {
-    MP3LOG("GetNext() Exit failed to allocated media buffer");
-    return nullptr;
-  }
-
-  const uint32_t read =
-    Read(frameWriter->Data(), frame->mOffset, frame->Size());
-
-  if (read != aRange.Length()) {
-    MP3LOG("GetNext() Exit read=%u frame->Size()=%" PRIuSIZE, read, frame->Size());
-    return nullptr;
-  }
-
-  UpdateState(aRange);
-
-  frame->mTime = Duration(mFrameIndex - 1);
-  frame->mDuration = Duration(1);
-  frame->mTimecode = frame->mTime;
-  frame->mKeyframe = true;
-
-  MOZ_ASSERT(!frame->mTime.IsNegative());
-  MOZ_ASSERT(frame->mDuration.IsPositive());
-
-  if (mNumParsedFrames == 1) {
-    // First frame parsed, let's read VBR info if available.
-    ByteReader reader(frame->Data(), frame->Size());
-    mParser.ParseVBRHeader(&reader);
-    mFirstFrameOffset = frame->mOffset;
-  }
-
-  MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
-          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
-          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
-          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
-          mSamplesPerFrame, mSamplesPerSecond, mChannels);
-
-  return frame.forget();
-}
-
-int64_t
-MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const
-{
-  int64_t offset = 0;
-  const auto& vbr = mParser.VBRInfo();
-
-  if (vbr.IsComplete()) {
-    offset = mFirstFrameOffset
-             + aFrameIndex * vbr.NumBytes().value()
-               / vbr.NumAudioFrames().value();
-  } else if (AverageFrameLength() > 0) {
-    offset = mFirstFrameOffset + aFrameIndex * AverageFrameLength();
-  }
-
-  MP3LOGV("OffsetFromFrameIndex(%" PRId64 ") -> %" PRId64, aFrameIndex, offset);
-  return std::max<int64_t>(mFirstFrameOffset, offset);
-}
-
-int64_t
-MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const
-{
-  int64_t frameIndex = 0;
-  const auto& vbr = mParser.VBRInfo();
-
-  if (vbr.IsComplete()) {
-    frameIndex = static_cast<float>(aOffset - mFirstFrameOffset)
-                 / vbr.NumBytes().value()
-                 * vbr.NumAudioFrames().value();
-    frameIndex = std::min<int64_t>(vbr.NumAudioFrames().value(), frameIndex);
-  } else if (AverageFrameLength() > 0) {
-    frameIndex = (aOffset - mFirstFrameOffset) / AverageFrameLength();
-  }
-
-  MP3LOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex);
-  return std::max<int64_t>(0, frameIndex);
-}
-
-int64_t
-MP3TrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const
-{
-  int64_t frameIndex = 0;
-  if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
-    frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1;
-  }
-
-  MP3LOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(),
-          frameIndex);
-  return std::max<int64_t>(0, frameIndex);
-}
-
-void
-MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange)
-{
-  // Prevent overflow.
-  if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) {
-    // These variables have a linear dependency and are only used to derive the
-    // average frame length.
-    mTotalFrameLen /= 2;
-    mNumParsedFrames /= 2;
-  }
-
-  // Full frame parsed, move offset to its end.
-  mOffset = aRange.mEnd;
-
-  mTotalFrameLen += aRange.Length();
-
-  if (!mSamplesPerFrame) {
-    mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame();
-    mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate();
-    mChannels = mParser.CurrentFrame().Header().Channels();
-  }
-
-  ++mNumParsedFrames;
-  ++mFrameIndex;
-  MOZ_ASSERT(mFrameIndex > 0);
-
-  // Prepare the parser for the next frame parsing session.
-  mParser.EndFrameSession();
-}
-
-int32_t
-MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
-{
-  MP3LOGV("MP3TrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize);
-
-  const int64_t streamLen = StreamLength();
-  if (mInfo && streamLen > 0) {
-    // Prevent blocking reads after successful initialization.
-    aSize = std::min<int64_t>(aSize, streamLen - aOffset);
-  }
-
-  uint32_t read = 0;
-  MP3LOGV("MP3TrackDemuxer::Read        -> ReadAt(%d)", aSize);
-  const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
-                                     static_cast<uint32_t>(aSize), &read);
-  NS_ENSURE_SUCCESS(rv, 0);
-  return static_cast<int32_t>(read);
-}
-
-double
-MP3TrackDemuxer::AverageFrameLength() const
-{
-  if (mNumParsedFrames) {
-    return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
-  }
-  const auto& vbr = mParser.VBRInfo();
-  if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
-    return static_cast<double>(vbr.NumBytes().value())
-           / (vbr.NumAudioFrames().value() + 1);
-  }
-  return 0.0;
-}
-
 // FrameParser
 
 namespace frame_header {
 // FrameHeader mRaw byte offsets.
 static const int SYNC1 = 0;
 static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
 static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
 static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
copy from dom/media/MP3Demuxer.h
copy to dom/media/mp3/MP3FrameParser.h
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/mp3/MP3FrameParser.h
@@ -1,49 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef MP3_DEMUXER_H_
-#define MP3_DEMUXER_H_
+#ifndef MP3_FRAME_PARSER_H_
+#define MP3_FRAME_PARSER_H_
 
-#include "mozilla/Attributes.h"
+#include <vector>
+
 #include "mozilla/Maybe.h"
-#include "MediaDataDemuxer.h"
-#include "MediaResource.h"
 #include "mp4_demuxer/ByteReader.h"
-#include <vector>
 
 namespace mozilla {
 namespace mp3 {
 
-class MP3TrackDemuxer;
-
-class MP3Demuxer : public MediaDataDemuxer
-{
-public:
-  // MediaDataDemuxer interface.
-  explicit MP3Demuxer(MediaResource* aSource);
-  RefPtr<InitPromise> Init() override;
-  bool HasTrackType(TrackInfo::TrackType aType) const override;
-  uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
-  already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
-      TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
-  bool IsSeekable() const override;
-  void NotifyDataArrived() override;
-  void NotifyDataRemoved() override;
-
-private:
-  // Synchronous initialization.
-  bool InitInternal();
-
-  RefPtr<MediaResource> mSource;
-  RefPtr<MP3TrackDemuxer> mTrackDemuxer;
-};
-
 // ID3 header parser state machine used by FrameParser.
 // The header contains the following format (one byte per term):
 // 'I' 'D' '3' MajorVersion MinorVersion Flags Size1 Size2 Size3 Size4
 // For more details see http://id3.org/id3v2.3.0.
 class ID3Parser
 {
 public:
   // Holds the ID3 header and its parsing state.
@@ -359,133 +333,12 @@ private:
 
   // We keep the first parsed frame around for static info access, the
   // previously parsed frame for debugging and the currently parsed frame.
   Frame mFirstFrame;
   Frame mFrame;
   Frame mPrevFrame;
 };
 
-// The MP3 demuxer used to extract MPEG frames and side information out of
-// MPEG streams.
-class MP3TrackDemuxer : public MediaTrackDemuxer
-{
-public:
-  // Constructor, expecting a valid media resource.
-  explicit MP3TrackDemuxer(MediaResource* aSource);
-
-  // Initializes the track demuxer by reading the first frame for meta data.
-  // Returns initialization success state.
-  bool Init();
-
-  // Returns the total stream length if known, -1 otherwise.
-  int64_t StreamLength() const;
-
-  // Returns the estimated stream duration, or a 0-duration if unknown.
-  media::TimeUnit Duration() const;
-
-  // Returns the estimated duration up to the given frame number,
-  // or a 0-duration if unknown.
-  media::TimeUnit Duration(int64_t aNumFrames) const;
-
-  // Returns the estimated current seek position time.
-  media::TimeUnit SeekPosition() const;
-
-  const FrameParser::Frame& LastFrame() const;
-  RefPtr<MediaRawData> DemuxSample();
-
-  const ID3Parser::ID3Header& ID3Header() const;
-  const FrameParser::VBRHeader& VBRInfo() const;
-
-  // MediaTrackDemuxer interface.
-  UniquePtr<TrackInfo> GetInfo() const override;
-  RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override;
-  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
-  void Reset() override;
-  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
-    const media::TimeUnit& aTimeThreshold) override;
-  int64_t GetResourceOffset() const override;
-  media::TimeIntervals GetBuffered() override;
-
-private:
-  // Destructor.
-  ~MP3TrackDemuxer() {}
-
-  // Fast approximate seeking to given time.
-  media::TimeUnit FastSeek(const media::TimeUnit& aTime);
-
-  // Seeks by scanning the stream up to the given time for more accurate results.
-  media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
-
-  // Finds the first valid frame and returns its byte range if found
-  // or a null-byte range otherwise.
-  MediaByteRange FindFirstFrame();
-
-  // Finds the next valid frame and returns its byte range if found
-  // or a null-byte range otherwise.
-  MediaByteRange FindNextFrame();
-
-  // Skips the next frame given the provided byte range.
-  bool SkipNextFrame(const MediaByteRange& aRange);
-
-  // Returns the next MPEG frame, if available.
-  already_AddRefed<MediaRawData> GetNextFrame(const MediaByteRange& aRange);
-
-  // Updates post-read meta data.
-  void UpdateState(const MediaByteRange& aRange);
-
-  // Returns the estimated offset for the given frame index.
-  int64_t OffsetFromFrameIndex(int64_t aFrameIndex) const;
-
-  // Returns the estimated frame index for the given offset.
-  int64_t FrameIndexFromOffset(int64_t aOffset) const;
-
-  // Returns the estimated frame index for the given time.
-  int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const;
-
-  // Reads aSize bytes into aBuffer from the source starting at aOffset.
-  // Returns the actual size read.
-  int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);
-
-  // Returns the average frame length derived from the previously parsed frames.
-  double AverageFrameLength() const;
-
-  // The (hopefully) MPEG resource.
-  MediaResourceIndex mSource;
-
-  // MPEG frame parser used to detect frames and extract side info.
-  FrameParser mParser;
-
-  // Whether we've locked onto a valid sequence of frames or not.
-  bool mFrameLock;
-
-  // Current byte offset in the source stream.
-  int64_t mOffset;
-
-  // Byte offset of the begin of the first frame, or 0 if none parsed yet.
-  int64_t mFirstFrameOffset;
-
-  // Total parsed frames.
-  uint64_t mNumParsedFrames;
-
-  // Current frame index.
-  int64_t mFrameIndex;
-
-  // Sum of parsed frames' lengths in bytes.
-  uint64_t mTotalFrameLen;
-
-  // Samples per frame metric derived from frame headers or 0 if none available.
-  int32_t mSamplesPerFrame;
-
-  // Samples per second metric derived from frame headers or 0 if none available.
-  int32_t mSamplesPerSecond;
-
-  // Channel count derived from frame headers or 0 if none available.
-  int32_t mChannels;
-
-  // Audio track config info.
-  UniquePtr<AudioInfo> mInfo;
-};
-
 } // namespace mp3
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/media/mp3/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+    'MP3Decoder.h',
+    'MP3Demuxer.h',
+    'MP3FrameParser.h',
+]
+
+UNIFIED_SOURCES += [
+    'MP3Decoder.cpp',
+    'MP3Demuxer.cpp',
+    'MP3FrameParser.cpp',
+]
+
+FINAL_LIBRARY = 'xul'