Bug 1168435 - Part 3 - Split MP3TrackDemuxer from MP3Demuxer. r?jya draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Sat, 10 Jun 2017 20:44:25 +0200
changeset 592704 9b03c76fc119904e52121a08f3c98fa49aeba518
parent 592150 24dfaa612058e54186bc6e4026f53ae2ea86d51e
child 632917 ab22a00722251c31248402ca16c9e8fa862fa848
push id63489
push usermozilla@buttercookie.de
push dateMon, 12 Jun 2017 18:35:49 +0000
reviewersjya
bugs1168435
milestone55.0a1
Bug 1168435 - Part 3 - Split MP3TrackDemuxer from MP3Demuxer. r?jya MozReview-Commit-ID: LyJUuRAyOaG
dom/media/mp3/MP3Demuxer.cpp
dom/media/mp3/MP3Demuxer.h
dom/media/mp3/MP3TrackDemuxer.cpp
dom/media/mp3/MP3TrackDemuxer.h
dom/media/mp3/moz.build
--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3Demuxer.cpp
@@ -1,42 +1,28 @@
 /* -*- 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 <inttypes.h>
-#include <algorithm>
-#include <limits>
-
-#include "mozilla/Assertions.h"
-#include "mozilla/SizePrintfMacros.h"
-#include "nsAutoPtr.h"
 #include "VideoUtils.h"
-#include "TimeUnits.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);
   }
@@ -82,697 +68,25 @@ bool
 MP3Demuxer::IsSeekable() const
 {
   return true;
 }
 
 void
 MP3Demuxer::NotifyDataArrived()
 {
-  // TODO: bug 1169485.
-  NS_WARNING("Unimplemented function NotifyDataArrived");
-  MP3LOGV("NotifyDataArrived()");
+  if (!mTrackDemuxer) {
+    return;
+  }
+  mTrackDemuxer->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);
+  if (!mTrackDemuxer) {
+    return;
   }
-
-  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;
+  mTrackDemuxer->NotifyDataRemoved();
 }
 
 } // namespace mp3
 } // namespace mozilla
--- a/dom/media/mp3/MP3Demuxer.h
+++ b/dom/media/mp3/MP3Demuxer.h
@@ -1,24 +1,20 @@
 /* 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 "MediaDataDemuxer.h"
-#include "MediaResource.h"
-#include "MP3FrameParser.h"
+#include "MP3TrackDemuxer.h"
 
 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;
@@ -31,133 +27,12 @@ public:
 private:
   // Synchronous initialization.
   bool InitInternal();
 
   RefPtr<MediaResource> mSource;
   RefPtr<MP3TrackDemuxer> mTrackDemuxer;
 };
 
-// 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
copy from dom/media/mp3/MP3Demuxer.cpp
copy to dom/media/mp3/MP3TrackDemuxer.cpp
--- a/dom/media/mp3/MP3Demuxer.cpp
+++ b/dom/media/mp3/MP3TrackDemuxer.cpp
@@ -1,15 +1,15 @@
 /* -*- 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 "MP3TrackDemuxer.h"
 
 #include <inttypes.h>
 #include <algorithm>
 #include <limits>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "nsAutoPtr.h"
@@ -25,89 +25,16 @@ extern mozilla::LazyLogModule gMediaDemu
 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)
@@ -370,16 +297,32 @@ MP3TrackDemuxer::GetBuffered()
     MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]",
             start.ToMicroseconds(), end.ToMicroseconds());
     buffered += TimeInterval(start, end);
   }
 
   return buffered;
 }
 
+void
+MP3TrackDemuxer::NotifyDataArrived()
+{
+  // TODO: bug 1169485.
+  NS_WARNING("Unimplemented function NotifyDataArrived");
+  MP3LOGV("NotifyDataArrived()");
+}
+
+void
+MP3TrackDemuxer::NotifyDataRemoved()
+{
+  // TODO: bug 1169485.
+  NS_WARNING("Unimplemented function NotifyDataRemoved");
+  MP3LOGV("NotifyDataRemoved()");
+}
+
 int64_t
 MP3TrackDemuxer::StreamLength() const
 {
   return mSource.GetLength();
 }
 
 TimeUnit
 MP3TrackDemuxer::Duration() const
copy from dom/media/mp3/MP3Demuxer.h
copy to dom/media/mp3/MP3TrackDemuxer.h
--- a/dom/media/mp3/MP3Demuxer.h
+++ b/dom/media/mp3/MP3TrackDemuxer.h
@@ -1,46 +1,22 @@
 /* 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_TRACK_DEMUXER_H_
+#define MP3_TRACK_DEMUXER_H_
 
 #include "MediaDataDemuxer.h"
 #include "MediaResource.h"
 #include "MP3FrameParser.h"
 
 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;
-};
-
 // 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);
 
@@ -72,16 +48,19 @@ public:
   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;
 
+  void NotifyDataArrived();
+  void NotifyDataRemoved();
+
 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.
--- a/dom/media/mp3/moz.build
+++ b/dom/media/mp3/moz.build
@@ -3,17 +3,19 @@
 # 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',
+    'MP3TrackDemuxer.h',
 ]
 
 UNIFIED_SOURCES += [
     'MP3Decoder.cpp',
     'MP3Demuxer.cpp',
     'MP3FrameParser.cpp',
+    'MP3TrackDemuxer.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'