From 59bef08fd6ef39e327d58faa071223969a853481 Mon Sep 17 00:00:00 2001
From: fedor <fedor@mail.ru>
Date: Wed, 14 Mar 2018 18:20:43 +0300
Subject: [PATCH] radia aac

---
 dom/media/ADTSDecoder.cpp   |  53 +++
 dom/media/ADTSDecoder.h     |  30 ++
 dom/media/ADTSDemuxer.cpp   | 859 ++++++++++++++++++++++++++++++++++++++++++++
 dom/media/ADTSDemuxer.h     | 142 ++++++++
 dom/media/DecoderTraits.cpp |  18 +
 dom/media/MediaResource.cpp |  63 ++++
 dom/media/MediaResource.h   |  94 +++++
 dom/media/VideoUtils.cpp    |   7 +
 dom/media/VideoUtils.h      |   4 +-
 dom/media/moz.build         |   4 +
 10 files changed, 1273 insertions(+), 1 deletion(-)
 create mode 100644 dom/media/ADTSDecoder.cpp
 create mode 100644 dom/media/ADTSDecoder.h
 create mode 100644 dom/media/ADTSDemuxer.cpp
 create mode 100644 dom/media/ADTSDemuxer.h

diff --git a/dom/media/ADTSDecoder.cpp b/dom/media/ADTSDecoder.cpp
new file mode 100644
index 0000000..2951973
--- /dev/null
+++ b/dom/media/ADTSDecoder.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "ADTSDecoder.h"
+#include "ADTSDemuxer.h"
+#include "MediaDecoderStateMachine.h"
+#include "MediaFormatReader.h"
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+MediaDecoder*
+ADTSDecoder::Clone()
+{
+  if (!IsEnabled())
+    return nullptr;
+
+  return new ADTSDecoder();
+}
+
+MediaDecoderStateMachine*
+ADTSDecoder::CreateStateMachine()
+{
+  RefPtr<MediaDecoderReader> reader =
+      new MediaFormatReader(this, new ADTSDemuxer(GetResource()));
+  return new MediaDecoderStateMachine(this, reader);
+}
+
+/* static */ bool
+ADTSDecoder::IsEnabled()
+{
+  PlatformDecoderModule::Init();
+
+  nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
+  return (platform && platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm")));
+}
+
+/* static */ bool
+ADTSDecoder::CanHandleMediaType(const nsACString& aType,
+                                const nsAString& aCodecs)
+{
+  if (aType.EqualsASCII("audio/aac") || aType.EqualsASCII("audio/aacp") ||
+      aType.EqualsASCII("audio/x-aac")) {
+    return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("aac"));
+  }
+
+  return false;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ADTSDecoder.h b/dom/media/ADTSDecoder.h
new file mode 100644
index 0000000..8be8c29
--- /dev/null
+++ b/dom/media/ADTSDecoder.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 ADTS_DECODER_H_
+#define ADTS_DECODER_H_
+
+#include "MediaDecoder.h"
+
+namespace mozilla {
+
+class ADTSDecoder : public MediaDecoder {
+public:
+  // MediaDecoder interface.
+  explicit ADTSDecoder() : MediaDecoder() {}
+  MediaDecoder* Clone() override;
+  MediaDecoderStateMachine* CreateStateMachine() override;
+
+  // Returns true if the ADTS backend is pref'ed on, and we're running on a
+  // platform that is likely to have decoders for the format.
+  static bool IsEnabled();
+  static bool CanHandleMediaType(const nsACString& aType,
+                                 const nsAString& aCodecs);
+};
+
+} // namespace mozilla
+
+#endif // !ADTS_DECODER_H_
diff --git a/dom/media/ADTSDemuxer.cpp b/dom/media/ADTSDemuxer.cpp
new file mode 100644
index 0000000..9dea02b
--- /dev/null
+++ b/dom/media/ADTSDemuxer.cpp
@@ -0,0 +1,859 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "ADTSDemuxer.h"
+
+#include <inttypes.h>
+
+#include "nsAutoPtr.h"
+#include "VideoUtils.h"
+#include "TimeUnits.h"
+#include "prenv.h"
+
+#ifdef PR_LOGGING
+//PRLogModuleInfo* gMediaDemuxerLog; fix fixfix
+//#define ADTSLOG(msg, ...) \
+//  PR_LOG(gMediaDemuxerLog, PR_LOG_DEBUG, ("ADTSDemuxer " msg, ##__VA_ARGS__))
+//#else
+#define ADTSLOG(msg, ...)  do {} while (false)
+#endif
+
+namespace mozilla {
+namespace adts {
+
+// adts::FrameHeader - Holds the ADTS frame header and its parsing
+// state.
+//
+// ADTS Frame Structure
+//
+// 11111111 1111BCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP(QQQQQQQQ QQQQQQQQ)
+//
+// Header consists of 7 or 9 bytes(without or with CRC).
+// Letter   Length(bits)  Description
+// { sync } 12            syncword 0xFFF, all bits must be 1
+// B        1             MPEG Version: 0 for MPEG-4, 1 for MPEG-2
+// C        2             Layer: always 0
+// D        1             protection absent, Warning, set to 1 if there is no
+//                        CRC and 0 if there is CRC
+// E        2             profile, the MPEG-4 Audio Object Type minus 1
+// F        4             MPEG-4 Sampling Frequency Index (15 is forbidden)
+// H        3             MPEG-4 Channel Configuration (in the case of 0, the
+//                        channel configuration is sent via an in-band PCE)
+// M        13            frame length, this value must include 7 or 9 bytes of
+//                        header length: FrameLength =
+//                          (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)
+// O        11            Buffer fullness
+// P        2             Number of AAC frames(RDBs) in ADTS frame minus 1, for
+//                        maximum compatibility always use 1 AAC frame per ADTS
+//                        frame
+// Q        16            CRC if protection absent is 0
+class FrameHeader {
+public:
+  uint32_t mFrameLength;
+  uint32_t mSampleRate;
+  uint32_t mSamples;
+  uint32_t mChannels;
+  uint8_t  mObjectType;
+  uint8_t  mSamplingIndex;
+  uint8_t  mChannelConfig;
+  uint8_t  mNumAACFrames;
+  bool     mHaveCrc;
+
+  // Returns whether aPtr matches a valid ADTS header sync marker
+  static bool MatchesSync(const uint8_t* aPtr) {
+    return aPtr[0] == 0xFF && (aPtr[1] & 0xF6) == 0xF0;
+  }
+
+  FrameHeader() { Reset(); }
+
+  // Header size
+  size_t HeaderSize() const { return (mHaveCrc) ? 9 : 7; }
+
+  bool IsValid() const { return mFrameLength > 0; }
+
+  // Resets the state to allow for a new parsing session.
+  void Reset() { PodZero(this); }
+
+  // Returns whether the byte creates a valid sequence up to this point.
+  bool Parse(const uint8_t* aPtr) {
+    const uint8_t* p = aPtr;
+
+    if (!MatchesSync(p)) {
+      return false;
+    }
+
+    // AAC has 1024 samples per frame per channel.
+    mSamples = 1024;
+
+    mHaveCrc = !(p[1] & 0x01);
+    mObjectType = ((p[2] & 0xC0) >> 6) + 1;
+    mSamplingIndex = (p[2] & 0x3C) >> 2;
+    mChannelConfig = (p[2] & 0x01) << 2 | (p[3] & 0xC0) >> 6;
+    mFrameLength = (p[3] & 0x03) << 11 | (p[4] & 0xFF) << 3 | (p[5] & 0xE0) >> 5;
+    mNumAACFrames = (p[6] & 0x03) + 1;
+
+    static const int32_t SAMPLE_RATES[16] = {
+      96000, 88200, 64000, 48000,
+      44100, 32000, 24000, 22050,
+      16000, 12000, 11025,  8000,
+      7350
+    };
+    mSampleRate = SAMPLE_RATES[mSamplingIndex];
+
+    MOZ_ASSERT(mChannelConfig < 8);
+    mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig;
+
+    return true;
+  }
+};
+
+
+// adts::Frame - Frame meta container used to parse and hold a frame
+// header and side info.
+class Frame {
+public:
+  Frame() : mOffset(0), mHeader() {}
+
+  int64_t Offset() const { return mOffset; }
+  size_t Length() const {
+    // TODO: If fields are zero'd when invalid, this check wouldn't be necessary.
+    if (!mHeader.IsValid()) {
+      return 0;
+    }
+
+    return mHeader.mFrameLength;
+  }
+
+  // Returns the offset to the start of frame's raw data.
+  int64_t PayloadOffset() const {
+    return mOffset + mHeader.HeaderSize();
+  }
+
+  // Returns the length of the frame's raw data (excluding the header) in bytes.
+  size_t PayloadLength() const {
+    // TODO: If fields are zero'd when invalid, this check wouldn't be necessary.
+    if (!mHeader.IsValid()) {
+      return 0;
+    }
+
+    return mHeader.mFrameLength - mHeader.HeaderSize();
+  }
+
+  // Returns the parsed frame header.
+  const FrameHeader& Header() const {
+    return mHeader;
+  }
+
+  bool IsValid() const {
+    return mHeader.IsValid();
+  }
+
+  // Resets the frame header and data.
+  void Reset() {
+    mHeader.Reset();
+    mOffset = 0;
+  }
+
+  // Returns whether the valid
+  bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
+    MOZ_ASSERT(aStart && aEnd);
+
+    bool found = false;
+    uint8_t* ptr = aStart;
+    // Require at least 7 bytes of data at the end of the buffer for the minimum
+    // ADTS frame header.
+    while (ptr < aEnd - 7 && !found) {
+      found = mHeader.Parse(ptr);
+      ptr++;
+    }
+
+    mOffset = aOffset + (ptr - aStart) - 1;
+
+    return found;
+  }
+
+private:
+  // The offset to the start of the header.
+  int64_t mOffset;
+
+  // The currently parsed frame header.
+  FrameHeader mHeader;
+};
+
+
+class FrameParser {
+public:
+
+  // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
+  const Frame& CurrentFrame() const { return mFrame; }
+
+
+  // Returns the first parsed frame. Reset via Reset.
+  const Frame& FirstFrame() const { return mFirstFrame; }
+
+  // Resets the parser. Don't use between frames as first frame data is reset.
+  void Reset() {
+    EndFrameSession();
+    mFirstFrame.Reset();
+  }
+
+  // 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() {
+    mFrame.Reset();
+  }
+
+  // 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(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
+    const bool found = mFrame.Parse(aOffset, aStart, aEnd);
+
+    if (mFrame.Length() && !mFirstFrame.Length()) {
+      mFirstFrame = mFrame;
+    }
+
+    return found;
+  }
+
+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;
+};
+
+
+// Return the AAC Profile Level Indication based upon sample rate and channels
+// Information based upon table 1.10 from ISO/IEC 14496-3:2005(E)
+static int8_t
+ProfileLevelIndication(const Frame& frame)
+{
+  const FrameHeader& header = frame.Header();
+  MOZ_ASSERT(header.IsValid());
+
+  if (!header.IsValid()) {
+    return 0;
+  }
+
+  const int channels = header.mChannels;
+  const int sampleRate = header.mSampleRate;
+
+  if (channels <= 2) {
+    if (sampleRate <= 24000) {
+      // AAC Profile  L1
+      return 0x28;
+    }
+    else if (sampleRate <= 48000) {
+      // AAC Profile  L2
+      return 0x29;
+    }
+  }
+  else if (channels <= 5) {
+    if (sampleRate <= 48000) {
+      // AAC Profile  L4
+      return 0x2A;
+    }
+    else if (sampleRate <= 96000) {
+      // AAC Profile  L5
+      return 0x2B;
+    }
+  }
+
+  // TODO: Should this be 0xFE for 'no audio profile specified'?
+  return 0;
+}
+
+
+// Initialize the AAC AudioSpecificConfig.
+// Only handles two-byte version for AAC-LC.
+static void
+InitAudioSpecificConfig(const Frame& frame,
+                        MediaByteBuffer* aBuffer)
+{
+  const FrameHeader& header = frame.Header();
+  MOZ_ASSERT(header.IsValid());
+
+  int audioObjectType = header.mObjectType;
+  int samplingFrequencyIndex = header.mSamplingIndex;
+  int channelConfig = header.mChannelConfig;
+
+  uint8_t asc[2];
+  asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1;
+  asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3;
+
+  aBuffer->AppendElements(asc, 2);
+}
+
+} // namespace adts
+
+// ADTSDemuxer
+
+ADTSDemuxer::ADTSDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+{}
+
+bool
+ADTSDemuxer::InitInternal()
+{
+  if (!mTrackDemuxer) {
+    mTrackDemuxer = new ADTSTrackDemuxer(mSource);
+  }
+  return mTrackDemuxer->Init();
+}
+
+nsRefPtr<ADTSDemuxer::InitPromise> 
+ADTSDemuxer::Init()
+{
+  if (!InitInternal()) {
+    ADTSLOG("Init() failure: waiting for data");
+
+    return InitPromise::CreateAndReject(
+      DemuxerFailureReason::WAITING_FOR_DATA, __func__);
+  }
+
+  ADTSLOG("Init() successful");
+  return InitPromise::CreateAndResolve(NS_OK, __func__);
+}
+
+bool
+ADTSDemuxer::HasTrackType(TrackInfo::TrackType aType) const
+{
+  return aType == TrackInfo::kAudioTrack;
+}
+
+uint32_t
+ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
+{
+  return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
+}
+
+already_AddRefed<MediaTrackDemuxer>
+ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
+{
+  if (!mTrackDemuxer) {
+    return nullptr;
+  }
+
+  return nsRefPtr<ADTSTrackDemuxer>(mTrackDemuxer).forget();
+}
+
+bool
+ADTSDemuxer::IsSeekable() const
+{
+  int64_t length = mSource->GetLength();
+  if (length > -1)
+    return true;
+  return false;
+}
+
+
+// ADTSTrackDemuxer
+ADTSTrackDemuxer::ADTSTrackDemuxer(MediaResource* aSource)
+  : mSource(aSource)
+  , mParser(new adts::FrameParser())
+  , mOffset(0)
+  , mNumParsedFrames(0)
+  , mFrameIndex(0)
+  , mTotalFrameLen(0)
+  , mSamplesPerFrame(0)
+  , mSamplesPerSecond(0)
+  , mChannels(0)
+{
+  Reset();
+}
+
+ADTSTrackDemuxer::~ADTSTrackDemuxer()
+{
+  delete mParser;
+  mParser = nullptr;
+}
+
+bool
+ADTSTrackDemuxer::Init()
+{
+
+  FastSeek(media::TimeUnit());
+  // Read the first frame to fetch sample rate and other meta data.
+  nsRefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame(true)));
+
+  ADTSLOG("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(media::TimeUnit());
+
+  if (!mInfo) {
+    mInfo = MakeUnique<AudioInfo>();
+  }
+
+  mInfo->mRate = mSamplesPerSecond;
+  mInfo->mChannels = mChannels;
+  mInfo->mBitDepth = 16;
+  mInfo->mDuration = Duration().ToMicroseconds();
+
+  // AAC Specific information
+  mInfo->mMimeType = "audio/mp4a-latm";
+
+  // Configure AAC codec-specific values.
+
+  // According to
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx,
+  // wAudioProfileLevelIndication, which is passed mInfo->mProfile, is
+  // a value from Table 1.12 -- audioProfileLevelIndication values, ISO/IEC 14496-3.
+  mInfo->mProfile = ProfileLevelIndication(mParser->FirstFrame());
+  // For AAC, mExtendedProfile contains the audioObjectType from Table
+  // 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC
+  mInfo->mExtendedProfile = mParser->FirstFrame().Header().mObjectType;
+  InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig);
+
+  ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 "}",
+          mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, mInfo->mDuration);
+
+  return mSamplesPerSecond && mChannels;
+}
+
+UniquePtr<TrackInfo>
+ADTSTrackDemuxer::GetInfo() const
+{
+  return mInfo->Clone();
+}
+
+already_AddRefed<MediaDataDemuxer>
+ADTSDemuxer::Clone() const {
+  nsRefPtr<ADTSDemuxer> demuxer = new ADTSDemuxer(mSource);
+  if (!demuxer->InitInternal()) {
+    NS_WARNING("Couldn't recreate ADTSTrackDemuxer");
+    return nullptr;
+  }
+  return demuxer.forget();
+}
+
+nsRefPtr<ADTSTrackDemuxer::SeekPromise>
+ADTSTrackDemuxer::Seek(media::TimeUnit aTime)
+{
+  // Efficiently seek to the position.
+  FastSeek(aTime);
+  // Correct seek position by scanning the next frames.
+  const media::TimeUnit seekTime = ScanUntil(aTime);
+
+  return SeekPromise::CreateAndResolve(seekTime, __func__);
+}
+
+int64_t
+ADTSTrackDemuxer::GetEvictionOffset(media::TimeUnit aTime)
+{
+  // Unused.
+  return 0;
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::FastSeek(const media::TimeUnit& aTime)
+{
+  ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
+         " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
+         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
+         mFrameIndex, mOffset);
+
+  const int64_t firstFrameOffset = mParser->FirstFrame().Offset();
+  if (!aTime.ToMicroseconds()) {
+    // Quick seek to the beginning of the stream.
+    mOffset = firstFrameOffset;
+  } else if (AverageFrameLength() > 0) {
+    mOffset = firstFrameOffset + FrameIndexFromTime(aTime) *
+      AverageFrameLength();
+  }
+
+  if (mOffset > firstFrameOffset && StreamLength() > 0) {
+    mOffset = std::min(StreamLength() - 1, mOffset);
+  }
+
+  mFrameIndex = FrameIndexFromOffset(mOffset);
+  mParser->EndFrameSession();
+
+  ADTSLOG("FastSeek End avgFrameLen=%f mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64
+          " SL=%llu",
+          AverageFrameLength(), mNumParsedFrames, mFrameIndex,
+          firstFrameOffset, mOffset, StreamLength());
+
+  return Duration(mFrameIndex);
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::ScanUntil(const media::TimeUnit& aTime)
+{
+  ADTSLOG("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) {
+    FastSeek(aTime);
+  }
+
+  while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) {
+    ADTSLOG("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
+             " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
+             AverageFrameLength(), mNumParsedFrames, mFrameIndex,
+             mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
+  }
+
+  ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
+          AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+
+  return Duration(mFrameIndex);
+}
+
+nsRefPtr<ADTSTrackDemuxer::SamplesPromise>
+ADTSTrackDemuxer::GetSamples(int32_t aNumSamples)
+{
+  ADTSLOG("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);
+
+  MOZ_ASSERT(aNumSamples);
+
+  nsRefPtr<SamplesHolder> frames = new SamplesHolder();
+
+  while (aNumSamples--) {
+    nsRefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
+    if (!frame)
+      break;
+
+    frames->mSamples.AppendElement(frame);
+  }
+
+  ADTSLOG("GetSamples() End mSamples.Size()=%d 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(
+      DemuxerFailureReason::END_OF_STREAM, __func__);
+  }
+
+  return SamplesPromise::CreateAndResolve(frames, __func__);
+}
+
+void
+ADTSTrackDemuxer::Reset()
+{
+  ADTSLOG("Reset()");
+  MOZ_ASSERT(mParser);
+  if (mParser) {
+    mParser->Reset();
+  }
+  FastSeek(media::TimeUnit());
+}
+
+nsRefPtr<ADTSTrackDemuxer::SkipAccessPointPromise>
+ADTSTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
+{
+  // Will not be called for audio-only resources.
+  return SkipAccessPointPromise::CreateAndReject(
+    SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__);
+}
+
+int64_t
+ADTSTrackDemuxer::GetResourceOffset() const
+{
+  return mOffset;
+}
+
+media::TimeIntervals
+ADTSTrackDemuxer::GetBuffered()
+{
+  media::TimeUnit duration = Duration();
+
+  if (duration <= media::TimeUnit()) {
+    return media::TimeIntervals();
+  }
+
+  AutoPinned<MediaResource> stream(mSource.GetResource());
+  return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
+}
+
+int64_t
+ADTSTrackDemuxer::StreamLength() const
+{
+  return mSource.GetLength();
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::Duration() const
+{
+  if (!mNumParsedFrames) {
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+
+  const int64_t streamLen = StreamLength();
+  if (streamLen < 0) {
+    // Unknown length, we can't estimate duration.
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+  const int64_t firstFrameOffset = mParser->FirstFrame().Offset();
+  int64_t numFrames = (streamLen - firstFrameOffset) / AverageFrameLength();
+  return Duration(numFrames);
+}
+
+media::TimeUnit
+ADTSTrackDemuxer::Duration(int64_t aNumFrames) const
+{
+  if (!mSamplesPerSecond) {
+    return media::TimeUnit::FromMicroseconds(-1);
+  }
+
+  return FramesToTimeUnit(aNumFrames * mSamplesPerFrame, mSamplesPerSecond);
+}
+
+const adts::Frame&
+ADTSTrackDemuxer::FindNextFrame(bool findFirstFrame /*= false*/)
+{
+  static const int BUFFER_SIZE = 4096;
+  static const int MAX_SKIPPED_BYTES = 10 * BUFFER_SIZE;
+
+  ADTSLOG("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 = mOffset;
+
+  // Prepare the parser for the next frame parsing session.
+  mParser->EndFrameSession();
+
+  // Check whether we've found a valid ADTS frame.
+  while (!foundFrame) {
+    if ((read = Read(buffer, frameHeaderOffset, BUFFER_SIZE)) == 0) {
+      ADTSLOG("FindNext() EOS without a frame");
+      break;
+    }
+
+    if (frameHeaderOffset - mOffset > MAX_SKIPPED_BYTES) {
+      ADTSLOG("FindNext() exceeded MAX_SKIPPED_BYTES without a frame");
+      break;
+    }
+
+    const adts::Frame& currentFrame = mParser->CurrentFrame();
+    foundFrame = mParser->Parse(frameHeaderOffset, buffer, buffer + read);
+    if (findFirstFrame && foundFrame) {
+      // Check for sync marker after the found frame, since it's
+      // possible to find sync marker in AAC data. If sync marker
+      // exists after the current frame then we've found a frame
+      // header.
+      int64_t nextFrameHeaderOffset = currentFrame.Offset() + currentFrame.Length();
+      int32_t read = Read(buffer, nextFrameHeaderOffset, 2);
+      if (read != 2 || !adts::FrameHeader::MatchesSync(buffer)) {
+        frameHeaderOffset = currentFrame.Offset() + 1;
+        mParser->Reset();
+        foundFrame = false;
+        continue;
+      }
+    }
+
+    if (foundFrame) {
+      break;
+    }
+
+    // Minimum header size is 7 bytes.
+    int64_t advance = read - 7;
+
+    // Check for offset overflow.
+    if (frameHeaderOffset + advance <= frameHeaderOffset) {
+      break;
+    }
+
+    frameHeaderOffset += advance;
+  }
+
+  if (!foundFrame || !mParser->CurrentFrame().Length()) {
+    ADTSLOG("FindNext() Exit foundFrame=%d mParser->CurrentFrame().Length()=%d ",
+           foundFrame, mParser->CurrentFrame().Length());
+    mParser->Reset();
+    return mParser->CurrentFrame();
+  }
+
+  ADTSLOG("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
+          " mFrameIndex=%" PRId64 " frameHeaderOffset=%d"
+          " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
+          " mChannels=%d",
+          mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
+          mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
+
+  return mParser->CurrentFrame();
+}
+
+bool
+ADTSTrackDemuxer::SkipNextFrame(const adts::Frame& aFrame)
+{
+  if (!mNumParsedFrames || !aFrame.Length()) {
+    nsRefPtr<MediaRawData> frame(GetNextFrame(aFrame));
+    return frame;
+  }
+
+  UpdateState(aFrame);
+
+  ADTSLOG("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>
+ADTSTrackDemuxer::GetNextFrame(const adts::Frame& aFrame)
+{
+  ADTSLOG("GetNext() Begin({mOffset=%" PRId64 " HeaderSize()=%d Length()=%d})",
+         aFrame.Offset(), aFrame.Header().HeaderSize(), aFrame.PayloadLength());
+  if (!aFrame.IsValid())
+    return nullptr;
+
+  const int64_t offset = aFrame.PayloadOffset();
+  const uint32_t length = aFrame.PayloadLength();
+
+  nsRefPtr<MediaRawData> frame = new MediaRawData();
+  frame->mOffset = offset;
+
+  nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
+  if (!frameWriter->SetSize(length)) {
+    ADTSLOG("GetNext() Exit failed to allocated media buffer");
+    return nullptr;
+  }
+
+  const uint32_t read = Read(frameWriter->Data(), offset, length);
+  if (read != length) {
+    ADTSLOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size());
+    return nullptr;
+  }
+
+  UpdateState(aFrame);
+
+  frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
+  frame->mDuration = Duration(1).ToMicroseconds();
+  frame->mTimecode = frame->mTime;
+  frame->mKeyframe = true;
+
+  MOZ_ASSERT(frame->mTime >= 0);
+  MOZ_ASSERT(frame->mDuration > 0);
+
+  ADTSLOG("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
+ADTSTrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const
+{
+  int64_t frameIndex = 0;
+
+  if (AverageFrameLength() > 0) {
+    frameIndex = (aOffset - mParser->FirstFrame().Offset()) / AverageFrameLength();
+  }
+
+  ADTSLOG("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex);
+  return std::max<int64_t>(0, frameIndex);
+}
+
+int64_t
+ADTSTrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const
+{
+  int64_t frameIndex = 0;
+  if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
+    frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1;
+  }
+
+  ADTSLOG("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex);
+  return std::max<int64_t>(0, frameIndex);
+}
+
+void
+ADTSTrackDemuxer::UpdateState(const adts::Frame& aFrame)
+{
+  int32_t frameLength = aFrame.Length();
+  // Prevent overflow.
+  if (mTotalFrameLen + frameLength < 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 = aFrame.Offset() + frameLength;
+  mTotalFrameLen += frameLength;
+
+  if (!mSamplesPerFrame) {
+    const adts::FrameHeader& header = aFrame.Header();
+    mSamplesPerFrame = header.mSamples;
+    mSamplesPerSecond = header.mSampleRate;
+    mChannels = header.mChannels;
+  }
+
+  ++mNumParsedFrames;
+  ++mFrameIndex;
+  MOZ_ASSERT(mFrameIndex > 0);
+}
+
+int32_t
+ADTSTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
+{
+  ADTSLOG("ADTSTrackDemuxer::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;
+  ADTSLOG("ADTSTrackDemuxer::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
+ADTSTrackDemuxer::AverageFrameLength() const
+{
+  if (mNumParsedFrames) {
+    return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
+  }
+
+  return 0.0;
+}
+
+} // namespace mozilla
diff --git a/dom/media/ADTSDemuxer.h b/dom/media/ADTSDemuxer.h
new file mode 100644
index 0000000..e9ccf21
--- /dev/null
+++ b/dom/media/ADTSDemuxer.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 ADTS_DEMUXER_H_
+#define ADTS_DEMUXER_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "MediaDataDemuxer.h"
+#include "MediaResource.h"
+#include "mp4_demuxer/ByteReader.h"
+
+namespace mozilla {
+
+namespace adts {
+class Frame;
+class FrameParser;
+}
+
+class ADTSTrackDemuxer;
+
+class ADTSDemuxer : public MediaDataDemuxer {
+public:
+  // MediaDataDemuxer interface.
+  explicit ADTSDemuxer(MediaResource* aSource);
+  nsRefPtr<InitPromise>  Init() override;
+  bool HasTrackType(TrackInfo::TrackType aType) const override;
+  already_AddRefed<MediaDataDemuxer> Clone() 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;
+
+private:
+  bool InitInternal();
+
+  nsRefPtr<MediaResource> mSource;
+  nsRefPtr<ADTSTrackDemuxer> mTrackDemuxer;
+};
+
+class ADTSTrackDemuxer : public MediaTrackDemuxer {
+public:
+  explicit ADTSTrackDemuxer(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;
+
+  // MediaTrackDemuxer interface.
+  UniquePtr<TrackInfo> GetInfo() const override;
+  nsRefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
+  nsRefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
+  void Reset() override;
+  nsRefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
+    media::TimeUnit aTimeThreshold) override;
+  int64_t GetResourceOffset() const override;
+  media::TimeIntervals GetBuffered() override;
+
+  int64_t GetEvictionOffset(media::TimeUnit aTime) override;
+
+private:
+  // Destructor.
+  ~ADTSTrackDemuxer();
+
+  // 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 next valid frame and returns its byte range.
+  const adts::Frame& FindNextFrame(bool findFirstFrame = false);
+
+  // Skips the next frame given the provided byte range.
+  bool SkipNextFrame(const adts::Frame& aFrame);
+
+  // Returns the next ADTS frame, if available.
+  already_AddRefed<MediaRawData> GetNextFrame(const adts::Frame& aFrame);
+
+  // Updates post-read meta data.
+  void UpdateState(const adts::Frame& aFrame);
+
+  // Returns the frame index for the given offset.
+  int64_t FrameIndexFromOffset(int64_t aOffset) const;
+
+  // Returns the 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) ADTS resource.
+  MediaResourceIndex mSource;
+
+  // ADTS frame parser used to detect frames and extract side info.
+  adts::FrameParser* mParser;
+
+  // Current byte offset in the source stream.
+  int64_t mOffset;
+
+  // 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.
+  uint32_t mSamplesPerFrame;
+
+  // Samples per second metric derived from frame headers or 0 if none available.
+  uint32_t mSamplesPerSecond;
+
+  // Channel count derived from frame headers or 0 if none available.
+  uint32_t mChannels;
+
+  // Audio track config info.
+  UniquePtr<AudioInfo> mInfo;
+};
+
+} // mozilla
+
+#endif // !ADTS_DEMUXER_H_
diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp
index 65f02e2..3644a91 100644
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -28,6 +28,10 @@
 #include "RawDecoder.h"
 #include "RawReader.h"
 #endif
+
+#include "ADTSDecoder.h"
+#include "ADTSDemuxer.h"
+
 #ifdef MOZ_ANDROID_OMX
 #include "AndroidMediaPluginHost.h"
 #include "AndroidMediaDecoder.h"
@@ -362,6 +366,13 @@ IsMP3SupportedType(const nsACString& aType,
   return MP3Decoder::CanHandleMediaType(aType, aCodecs);
 }
 
+static bool
+IsAACSupportedType(const nsACString& aType,
+                   const nsAString& aCodecs = EmptyString())
+{
+  return ADTSDecoder::CanHandleMediaType(aType, aCodecs);
+}
+
 #ifdef MOZ_APPLEMEDIA
 static const char * const gAppleMP3Types[] = {
   "audio/mp3",
@@ -592,6 +603,10 @@ if (IsMP3SupportedType(aType)) {
     decoder = new MP3Decoder();
     return decoder.forget();
   }
+if (IsAACSupportedType(aType)) {
+    decoder = new ADTSDecoder();
+    return decoder.forget();
+  }
 #ifdef MOZ_RAW
   if (IsRawType(aType)) {
     decoder = new RawDecoder();
@@ -713,6 +728,9 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
 if (IsMP3SupportedType(aType)) {
     decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource()));
   } else
+if (IsAACSupportedType(aType)) {
+    decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource()));
+  } else
 #ifdef MOZ_RAW
   if (IsRawType(aType)) {
     decoderReader = new RawReader(aDecoder);
diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp
index 2290be0..71908a7 100644
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -1673,5 +1673,68 @@ void BaseMediaResource::DispatchBytesConsumed(int64_t aNumBytes, int64_t aOffset
   NS_DispatchToMainThread(event);
 }
 
+nsresult
+MediaResourceIndex::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
+  // We purposefuly don't check that we may attempt to read past
+  // mResource->GetLength() as the resource's length may change over time.
+
+  nsresult rv = ReadAt(mOffset, aBuffer, aCount, aBytes);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  mOffset += *aBytes;
+  return NS_OK;
+}
+
+nsresult
+MediaResourceIndex::ReadAt(int64_t aOffset, char* aBuffer,
+                           uint32_t aCount, uint32_t* aBytes) const
+{
+  *aBytes = 0;
+  while (aCount > 0) {
+    uint32_t bytesRead = 0;
+    nsresult rv = mResource->ReadAt(aOffset, aBuffer, aCount, &bytesRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!bytesRead) {
+      break;
+    }
+    *aBytes += bytesRead;
+    aOffset += bytesRead;
+    aBuffer += bytesRead;
+    aCount -= bytesRead;
+  }
+  return NS_OK;
+}
+
+nsresult
+MediaResourceIndex::Seek(int32_t aWhence, int64_t aOffset)
+{
+  switch (aWhence) {
+    case SEEK_SET:
+      break;
+    case SEEK_CUR:
+      aOffset += mOffset;
+      break;
+    case SEEK_END:
+    {
+      int64_t length = mResource->GetLength();
+      if (length == -1 || length - aOffset < 0) {
+        return NS_ERROR_FAILURE;
+      }
+      aOffset = mResource->GetLength() - aOffset;
+    }
+      break;
+    default:
+      return NS_ERROR_FAILURE;
+  }
+
+  mOffset = aOffset;
+
+  return NS_OK;
+}
+
 } // namespace mozilla
 
diff --git a/dom/media/MediaResource.h b/dom/media/MediaResource.h
index 9ad5890..d79bbd3 100644
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -14,6 +14,7 @@
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "MediaCache.h"
+#include "MediaData.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
@@ -758,6 +759,99 @@ private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+/*
+ * MediaResourceIndex provides a way to access MediaResource objects.
+ * Read, Seek and Tell must only be called on non-main threads.
+ * In the case of the Ogg Decoder they are called on the Decode thread for
+ * example. You must ensure that no threads are calling these methods once
+ * the MediaResource has been Closed.
+ */
+
+class MediaResourceIndex
+{
+public:
+  explicit MediaResourceIndex(MediaResource* aResource)
+    : mResource(aResource)
+    , mOffset(0)
+  {}
+
+  // Read up to aCount bytes from the stream. The buffer must have
+  // enough room for at least aCount bytes. Stores the number of
+  // actual bytes read in aBytes (0 on end of file).
+  // May read less than aCount bytes if the number of
+  // available bytes is less than aCount. Always check *aBytes after
+  // read, and call again if necessary.
+  nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
+  // Seek to the given bytes offset in the stream. aWhence can be
+  // one of:
+  //   NS_SEEK_SET
+  //   NS_SEEK_CUR
+  //   NS_SEEK_END
+  //
+  // In the Http strategy case the cancel will cause the http
+  // channel's listener to close the pipe, forcing an i/o error on any
+  // blocked read. This will allow the decode thread to complete the
+  // event.
+  //
+  // In the case of a seek in progress, the byte range request creates
+  // a new listener. This is done on the main thread via seek
+  // synchronously dispatching an event. This avoids the issue of us
+  // closing the listener but an outstanding byte range request
+  // creating a new one. They run on the same thread so no explicit
+  // synchronisation is required. The byte range request checks for
+  // the cancel flag and does not create a new channel or listener if
+  // we are cancelling.
+  //
+  // The default strategy does not do any seeking - the only issue is
+  // a blocked read which it handles by causing the listener to close
+  // the pipe, as per the http case.
+  //
+  // The file strategy doesn't block for any great length of time so
+  // is fine for a no-op cancel.
+  nsresult Seek(int32_t aWhence, int64_t aOffset);
+  // Report the current offset in bytes from the start of the stream.
+  int64_t Tell() const { return mOffset; }
+
+  // Return the underlying MediaResource.
+  MediaResource* GetResource() const { return mResource; }
+
+  // Read up to aCount bytes from the stream. The read starts at
+  // aOffset in the stream, seeking to that location initially if
+  // it is not the current stream offset.
+  // Unlike MediaResource::ReadAt, ReadAt only returns fewer bytes than
+  // requested if end of stream or an error is encountered. There is no need to
+  // call it again to get more data.
+  // *aBytes will contain the number of bytes copied, even if an error occurred.
+  // ReadAt doesn't have an impact on the offset returned by Tell().
+  nsresult ReadAt(int64_t aOffset, char* aBuffer,
+                  uint32_t aCount, uint32_t* aBytes) const;
+
+  // Convenience methods, directly calling the MediaResource method of the same
+  // name.
+  // Those functions do not update the MediaResource offset as returned
+  // by Tell().
+
+  // This method returns nullptr if anything fails.
+  // Otherwise, it returns an owned buffer.
+  // MediaReadAt may return fewer bytes than requested if end of stream is
+  // encountered. There is no need to call it again to get more data.
+//  already_AddRefed<MediaByteBuffer> MediaReadAt(int64_t aOffset, uint32_t aCount) const
+//  {
+//    return mResource->MediaReadAt(aOffset, aCount);
+//  }
+  // Get the length of the stream in bytes. Returns -1 if not known.
+  // This can change over time; after a seek operation, a misbehaving
+  // server may give us a resource of a different length to what it had
+  // reported previously --- or it may just lie in its Content-Length
+  // header and give us more or less data than it reported. We will adjust
+  // the result of GetLength to reflect the data that's actually arriving.
+  int64_t GetLength() const { return mResource->GetLength(); }
+
+private:
+  nsRefPtr<MediaResource> mResource;
+  int64_t mOffset;
+};
+
 } // namespace mozilla
 
 #endif
diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp
index f8a5c8f..b2d9b5f 100644
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -37,6 +37,13 @@ CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
   return SaferMultDiv(aFrames, USECS_PER_S, aRate);
 }
 
+media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate) {
+  int64_t major = aFrames / aRate;
+  int64_t remainder = aFrames % aRate;
+  return media::TimeUnit::FromMicroseconds(major) * USECS_PER_S +
+    (media::TimeUnit::FromMicroseconds(remainder) * USECS_PER_S) / aRate;
+}
+
 // Converts from microseconds to number of audio frames, given the specified
 // audio rate.
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
diff --git a/dom/media/VideoUtils.h b/dom/media/VideoUtils.h
index 7084e8a..8bdd093 100644
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -131,7 +131,9 @@ media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStr
 // if the operation succeeded, or false if there was an integer overflow
 // while calulating the conversion.
 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
-
+// Converts from number of audio frames (aFrames) TimeUnit, given
+// the specified audio rate (aRate).
+media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
 // Converts from microseconds (aUsecs) to number of audio frames, given the
 // specified audio rate (aRate). Stores the result in aOutFrames. Returns
 // true if the operation succeeded, or false if there was an integer
diff --git a/dom/media/moz.build b/dom/media/moz.build
index 04a99db..44aec0b 100644
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -75,6 +75,8 @@ XPIDL_MODULE = 'dom_media'
 EXPORTS += [
     'AbstractMediaDecoder.h',
     'AbstractThread.h',
+    'ADTSDecoder.h',
+    'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
@@ -164,6 +166,8 @@ EXPORTS.mozilla.dom += [
 
 UNIFIED_SOURCES += [
     'AbstractThread.cpp',
+    'ADTSDecoder.cpp',
+    'ADTSDemuxer.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
     'AudioSegment.cpp',
-- 
2.8.2.windows.1

