Bug 905513 - Rework MediaSource/MediaElement integration and implement limited initial multiple-decoder support. r=doublec
authorMatthew Gregan <kinetik@flim.org>
Fri, 27 Sep 2013 17:22:37 +1200
changeset 148939 07179c6176b865a3c8a839d848e57f941bc761aa
parent 148938 ba062e4be8fd4c05051f20aa3e85ba8952a4a774
child 148940 935ab0a88dde1354384790b4c709c35bd1f7d0e9
push id25366
push userkwierso@gmail.com
push dateSat, 28 Sep 2013 02:13:38 +0000
treeherdermozilla-central@e1914e294152 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdoublec
bugs905513
milestone27.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 905513 - Rework MediaSource/MediaElement integration and implement limited initial multiple-decoder support. r=doublec
content/html/content/src/HTMLMediaElement.cpp
content/media/mediasource/MediaSource.cpp
content/media/mediasource/MediaSource.h
content/media/mediasource/MediaSourceDecoder.cpp
content/media/mediasource/MediaSourceDecoder.h
content/media/mediasource/MediaSourceInputAdapter.cpp
content/media/mediasource/MediaSourceInputAdapter.h
content/media/mediasource/SourceBuffer.cpp
content/media/mediasource/SourceBuffer.h
content/media/mediasource/SourceBufferList.cpp
content/media/mediasource/SourceBufferList.h
content/media/mediasource/SourceBufferResource.cpp
content/media/mediasource/SourceBufferResource.h
content/media/mediasource/SubBufferDecoder.h
content/media/mediasource/moz.build
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -63,16 +63,17 @@
 #include "nsMediaFragmentURIParser.h"
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
+#include "MediaSourceDecoder.h"
 
 #include "AudioChannelService.h"
 
 #include "nsCSSParser.h"
 #include "nsIMediaList.h"
 
 #include "ImageContainer.h"
 #include "nsIPowerManagerService.h"
@@ -596,17 +597,17 @@ void HTMLMediaElement::AbortExistingLoad
   if (mDecoder) {
     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
     ShutdownDecoder();
   }
   if (mSrcStream) {
     EndSrcMediaStreamPlayback();
   }
   if (mMediaSource) {
-    mMediaSource->DetachElement();
+    mMediaSource->Detach();
     mMediaSource = nullptr;
   }
   if (mAudioStream) {
     mAudioStream->Shutdown();
     mAudioStream = nullptr;
   }
 
   mLoadingSrc = nullptr;
@@ -1130,26 +1131,25 @@ nsresult HTMLMediaElement::LoadResource(
       nsCString specUTF8;
       mLoadingSrc->GetSpec(specUTF8);
       NS_ConvertUTF8toUTF16 spec(specUTF8);
       const PRUnichar* params[] = { spec.get() };
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       return rv;
     }
     mMediaSource = source.forget();
-    if (!mMediaSource->AttachElement(this)) {
-      // XXX(kinetik): Handle failure: run "If the media data cannot be
-      // fetched at all, due to network errors, causing the user agent to
-      // give up trying to fetch the resource" section of resource fetch
-      // algorithm.
+    nsRefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(this);
+    if (!mMediaSource->Attach(decoder)) {
+      // TODO: Handle failure: run "If the media data cannot be fetched at
+      // all, due to network errors, causing the user agent to give up
+      // trying to fetch the resource" section of resource fetch algorithm.
       return NS_ERROR_FAILURE;
     }
-    // XXX(kinetik): Bug 881512. Wire this up properly; return from here (as
-    // MediaStreams setup does) rather than relying on mediasource->channel
-    // conversion.
+    nsRefPtr<MediaResource> resource = new MediaSourceResource();
+    return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
 
   // check for a Content Security Policy to pass down to the channel
   // created to load the media content
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
@@ -1984,17 +1984,17 @@ HTMLMediaElement::~HTMLMediaElement()
   UnregisterFreezableElement();
   if (mDecoder) {
     ShutdownDecoder();
   }
   if (mSrcStream) {
     EndSrcMediaStreamPlayback();
   }
   if (mMediaSource) {
-    mMediaSource->DetachElement();
+    mMediaSource->Detach();
     mMediaSource = nullptr;
   }
 
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
     "Destroyed media element should no longer be in element table");
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
--- a/content/media/mediasource/MediaSource.cpp
+++ b/content/media/mediasource/MediaSource.cpp
@@ -1,40 +1,51 @@
 /* -*- 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 "MediaSource.h"
 
-#include "mozilla/dom/HTMLMediaElement.h"
-#include "MediaSourceInputAdapter.h"
+#include "AsyncEventRunner.h"
+#include "DecoderTraits.h"
 #include "SourceBuffer.h"
 #include "SourceBufferList.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/mozalloc.h"
 #include "nsContentTypeParser.h"
-#include "nsIInputStream.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsIRunnable.h"
+#include "nsPIDOMWindow.h"
+#include "nsStringGlue.h"
+#include "nsThreadUtils.h"
+#include "prlog.h"
+
+struct JSContext;
+class JSObject;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaSourceLog;
 #define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
 #else
 #define LOG(type, msg)
 #endif
 
-namespace mozilla {
-namespace dom {
+// Arbitrary limit.
+static const unsigned int MAX_SOURCE_BUFFERS = 16;
 
-already_AddRefed<nsIInputStream>
-MediaSource::CreateInternalStream()
-{
-  nsRefPtr<MediaSourceInputAdapter> adapter = new MediaSourceInputAdapter(this);
-  mAdapters.AppendElement(adapter);
-  return adapter.forget();
-}
+namespace mozilla {
+
+namespace dom {
 
 /* static */ already_AddRefed<MediaSource>
 MediaSource::Constructor(const GlobalObject& aGlobal,
                          ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -90,29 +101,35 @@ MediaSource::SetDuration(double aDuratio
 }
 
 already_AddRefed<SourceBuffer>
 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
 {
   if (!IsTypeSupportedInternal(aType, aRv)) {
     return nullptr;
   }
-  // TODO: Temporary limit until multiple decoders are supported.  Bug 881512.
-  if (mSourceBuffers->Length() >= 1) {
+  if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
   }
   if (mReadyState != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
-  mContentType = aType;
-  nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this);
+  nsContentTypeParser parser(aType);
+  nsAutoString mimeType;
+  nsresult rv = parser.GetType(mimeType);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+  nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
   mSourceBuffers->Append(sourceBuffer);
-  sourceBuffer->Attach();
+  LOG(PR_LOG_DEBUG, ("%p AddSourceBuffer(Type=%s) -> %p", this,
+                     NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get()));
   return sourceBuffer.forget();
 }
 
 void
 MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
 {
   SourceBuffer* sourceBuffer = &aSourceBuffer;
   if (!mSourceBuffers->Contains(sourceBuffer)) {
@@ -132,17 +149,16 @@ MediaSource::RemoveSourceBuffer(SourceBu
   //     remove sourceBuffer video, audio, text Tracks from MediaElement tracks
   //     remove sourceBuffer video, audio, text Tracks and fire "removetrack" at affected lists
   //     fire "removetrack" at modified MediaElement track lists
   // If removed enabled/selected, fire "change" at affected MediaElement list.
   if (mActiveSourceBuffers->Contains(sourceBuffer)) {
     mActiveSourceBuffers->Remove(sourceBuffer);
   }
   mSourceBuffers->Remove(sourceBuffer);
-  sourceBuffer->Detach();
   // TODO: Free all resources associated with sourceBuffer
 }
 
 void
 MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
 {
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
@@ -155,78 +171,63 @@ MediaSource::EndOfStream(const Optional<
 /* static */ bool
 MediaSource::IsTypeSupported(const GlobalObject& aGlobal,
                              const nsAString& aType)
 {
   ErrorResult unused;
   return IsTypeSupportedInternal(aType, unused);
 }
 
-void
-MediaSource::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
+bool
+MediaSource::Attach(MediaSourceDecoder* aDecoder)
 {
-  MonitorAutoLock mon(mMonitor);
-  LOG(PR_LOG_DEBUG, ("%p Append(ArrayBuffer=%u) mData=%u", this, aLength, mData.Length()));
-  mData.AppendElements(aData, aLength);
-  NotifyListeners();
-}
-
-bool
-MediaSource::AttachElement(HTMLMediaElement* aElement)
-{
-  LOG(PR_LOG_DEBUG, ("%p Attaching element %p", this, aElement));
-  MOZ_ASSERT(aElement);
-  mElement = aElement;
+  LOG(PR_LOG_DEBUG, ("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner()));
+  MOZ_ASSERT(aDecoder);
   if (mReadyState != MediaSourceReadyState::Closed) {
     return false;
   }
+  mDecoder = aDecoder;
+  mDecoder->AttachMediaSource(this);
   SetReadyState(MediaSourceReadyState::Open);
   return true;
 }
 
 void
-MediaSource::DetachElement()
+MediaSource::Detach()
 {
-  LOG(PR_LOG_DEBUG, ("%p Detaching element %p", this, mElement.get()));
-  MOZ_ASSERT(mElement);
-  mElement = nullptr;
+  LOG(PR_LOG_DEBUG, ("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner()));
+  MOZ_ASSERT(mDecoder);
+  mDecoder->DetachMediaSource();
+  mDecoder = nullptr;
   mDuration = UnspecifiedNaN();
   mActiveSourceBuffers->Clear();
-  mSourceBuffers->DetachAndClear();
+  mSourceBuffers->Clear();
   SetReadyState(MediaSourceReadyState::Closed);
-
-  for (uint32_t i = 0; i < mAdapters.Length(); ++i) {
-    mAdapters[i]->Close();
-  }
-  mAdapters.Clear();
 }
 
 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
   : nsDOMEventTargetHelper(aWindow)
   , mDuration(UnspecifiedNaN())
-  , mMonitor("mozilla::dom::MediaSource::mMonitor")
+  , mDecoder(nullptr)
   , mReadyState(MediaSourceReadyState::Closed)
 {
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
 
 #ifdef PR_LOGGING
   if (!gMediaSourceLog) {
     gMediaSourceLog = PR_NewLogModule("MediaSource");
   }
 #endif
 }
 
 void
 MediaSource::SetReadyState(MediaSourceReadyState aState)
 {
   MOZ_ASSERT(aState != mReadyState);
-  MonitorAutoLock mon(mMonitor);
-
-  NotifyListeners();
 
   if ((mReadyState == MediaSourceReadyState::Closed ||
        mReadyState == MediaSourceReadyState::Ended) &&
       aState == MediaSourceReadyState::Open) {
     mReadyState = aState;
     QueueAsyncSimpleEvent("sourceopen");
     return;
   }
@@ -269,24 +270,16 @@ void
 MediaSource::QueueAsyncSimpleEvent(const char* aName)
 {
   LOG(PR_LOG_DEBUG, ("%p Queuing event %s to MediaSource", this, aName));
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 void
-MediaSource::NotifyListeners()
-{
-  for (uint32_t i = 0; i < mAdapters.Length(); ++i) {
-    mAdapters[i]->NotifyListener();
-  }
-}
-
-void
 MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
 {
   if (mDuration == aNewDuration) {
     return;
   }
   double oldDuration = mDuration;
   mDuration = aNewDuration;
   if (aNewDuration < oldDuration) {
@@ -298,16 +291,17 @@ MediaSource::DurationChange(double aNewD
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
   // TODO: Update media element's duration and run element's duration change algorithm.
 }
 
 void
 MediaSource::EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
 {
   SetReadyState(MediaSourceReadyState::Ended);
+  mSourceBuffers->Ended();
   if (!aError.WasPassed()) {
     // TODO:
     // Run duration change algorithm.
     // DurationChange(highestDurationOfSourceBuffers, aRv);
     // if (aRv.Failed()) {
     //   return;
     // }
     // Notify media element that all data is now available.
@@ -380,20 +374,21 @@ MediaSource::GetParentObject() const
 }
 
 JSObject*
 MediaSource::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return MediaSourceBinding::Wrap(aCx, aScope, this);
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_4(MediaSource, nsDOMEventTargetHelper,
-                                     mSourceBuffers, mActiveSourceBuffers, mAdapters, mElement)
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaSource, nsDOMEventTargetHelper,
+                                     mSourceBuffers, mActiveSourceBuffers)
 
 NS_IMPL_ADDREF_INHERITED(MediaSource, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaSource, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 } // namespace dom
+
 } // namespace mozilla
--- a/content/media/mediasource/MediaSource.h
+++ b/content/media/mediasource/MediaSource.h
@@ -2,41 +2,46 @@
 /* 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 mozilla_dom_MediaSource_h_
 #define mozilla_dom_MediaSource_h_
 
-#include "AsyncEventRunner.h"
+#include "MediaSourceDecoder.h"
+#include "js/RootingAPI.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/Monitor.h"
 #include "mozilla/dom/MediaSourceBinding.h"
-#include "mozilla/dom/TypedArray.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
+#include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMEventTargetHelper.h"
-#include "nsWrapperCache.h"
+#include "nsID.h"
+#include "nsISupports.h"
 #include "nscore.h"
 
-class nsIInputStream;
+struct JSContext;
+class JSObject;
+class nsPIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
-class HTMLMediaElement;
-class MediaSourceInputAdapter;
+class GlobalObject;
+class SourceBuffer;
 class SourceBufferList;
-class SourceBuffer;
 class TimeRanges;
+template <typename T> class Optional;
 
 #define MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID \
   { 0x3839d699, 0x22c5, 0x439f, \
   { 0x94, 0xca, 0x0e, 0x0b, 0x26, 0xf9, 0xca, 0xbf } }
 
 class MediaSource MOZ_FINAL : public nsDOMEventTargetHelper
 {
 public:
@@ -63,81 +68,51 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaSource, nsDOMEventTargetHelper)
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
   nsPIDOMWindow* GetParentObject() const;
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
-  const nsString& GetType()
-  {
-    return mContentType;
-  }
-
-  already_AddRefed<nsIInputStream> CreateInternalStream();
-
-
-  void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
-
-  // Semi-private, for MediaSourceInputAdapter only.
-  nsTArray<uint8_t> const& GetData()
-  {
-    return mData;
-  }
-
-  Monitor& GetMonitor()
-  {
-    return mMonitor;
-  }
-
-  bool AppendDone() const
-  {
-    return mReadyState == MediaSourceReadyState::Closed || mReadyState == MediaSourceReadyState::Ended;
-  }
-
-  // Attach this MediaSource to MediaElement aElement.  Returns false if already attached.
-  bool AttachElement(HTMLMediaElement* aElement);
-  void DetachElement();
+  // Attach this MediaSource to Decoder aDecoder.  Returns false if already attached.
+  bool Attach(MediaSourceDecoder* aDecoder);
+  void Detach();
 
   // Set mReadyState to aState and fire the required events at the MediaSource.
   void SetReadyState(MediaSourceReadyState aState);
 
   void GetBuffered(TimeRanges* aRanges);
 
+ // Used by SourceBuffer to call CreateSubDecoder.
+  MediaSourceDecoder* GetDecoder()
+  {
+    return mDecoder;
+  }
+
 private:
   explicit MediaSource(nsPIDOMWindow* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
-  void NotifyListeners();
-
   void DurationChange(double aNewDuration, ErrorResult& aRv);
   void EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv);
 
   static bool IsTypeSupportedInternal(const nsAString& aType, ErrorResult& aRv);
 
   double mDuration;
 
-  nsTArray<nsRefPtr<MediaSourceInputAdapter> > mAdapters;
-
-  // Protected by monitor.
-  nsTArray<uint8_t> mData;
-
-  // Protects access to mData.
-  Monitor mMonitor;
-
   nsRefPtr<SourceBufferList> mSourceBuffers;
   nsRefPtr<SourceBufferList> mActiveSourceBuffers;
 
-  nsRefPtr<HTMLMediaElement> mElement;
+  nsRefPtr<MediaSourceDecoder> mDecoder;
 
-  nsString mContentType;
   MediaSourceReadyState mReadyState;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)
 
 } // namespace dom
+
 } // namespace mozilla
 #endif /* mozilla_dom_MediaSource_h_ */
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -0,0 +1,215 @@
+/* -*- 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 "MediaSourceDecoder.h"
+
+#include "AbstractMediaDecoder.h"
+#include "MediaDecoderReader.h"
+#include "MediaDecoderStateMachine.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/mozalloc.h"
+#include "nsISupports.h"
+#include "prlog.h"
+#include "SubBufferDecoder.h"
+#include "SourceBufferResource.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaSourceLog;
+#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
+#else
+#define LOG(type, msg)
+#endif
+
+namespace mozilla {
+
+namespace dom {
+
+class TimeRanges;
+
+} // namespace dom
+
+class MediaSourceReader : public MediaDecoderReader
+{
+public:
+  MediaSourceReader(AbstractMediaDecoder* aDecoder)
+    : MediaDecoderReader(aDecoder)
+  {
+  }
+
+  nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  bool DecodeAudioData() MOZ_OVERRIDE
+  {
+    if (GetAudioReader()) {
+      return GetAudioReader()->DecodeAudioData();
+    }
+    return false;
+  }
+
+  bool DecodeVideoFrame(bool& aKeyFrameSkip, int64_t aTimeThreshold) MOZ_OVERRIDE
+  {
+    if (GetVideoReader()) {
+      return GetVideoReader()->DecodeVideoFrame(aKeyFrameSkip, aTimeThreshold);
+    }
+    return false;
+  }
+
+  bool HasVideo() MOZ_OVERRIDE
+  {
+    return mInfo.mHasVideo;
+  }
+
+  bool HasAudio() MOZ_OVERRIDE
+  {
+    return mInfo.mHasAudio;
+  }
+
+  nsresult ReadMetadata(VideoInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
+
+  nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
+                int64_t aCurrentTime) MOZ_OVERRIDE
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE
+  {
+    // XXX: Merge result with audio reader.
+    return GetVideoReader()->GetBuffered(aBuffered, aStartTime);
+  }
+
+  MediaQueue<AudioData>& AudioQueue() MOZ_OVERRIDE
+  {
+    // TODO: Share AudioQueue with SubReaders.
+    if (GetAudioReader()) {
+      return GetAudioReader()->AudioQueue();
+    }
+    return MediaDecoderReader::AudioQueue();
+  }
+
+  MediaQueue<VideoData>& VideoQueue() MOZ_OVERRIDE
+  {
+    // TODO: Share VideoQueue with SubReaders.
+    if (GetVideoReader()) {
+      return GetVideoReader()->VideoQueue();
+    }
+    return MediaDecoderReader::VideoQueue();
+  }
+
+private:
+  MediaDecoderReader* GetVideoReader()
+  {
+    MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
+    return decoder->GetVideoReader();
+  }
+
+  MediaDecoderReader* GetAudioReader()
+  {
+    MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
+    return decoder->GetAudioReader();
+  }
+};
+
+MediaSourceDecoder::MediaSourceDecoder(HTMLMediaElement* aElement)
+  : mMediaSource(nullptr)
+  , mVideoReader(nullptr),
+    mAudioReader(nullptr)
+{
+  Init(aElement);
+}
+
+MediaDecoder*
+MediaSourceDecoder::Clone()
+{
+  // TODO: Sort out cloning.
+  return nullptr;
+}
+
+MediaDecoderStateMachine*
+MediaSourceDecoder::CreateStateMachine()
+{
+  return new MediaDecoderStateMachine(this, new MediaSourceReader(this));
+}
+
+nsresult
+MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
+{
+  return NS_OK;
+}
+
+void
+MediaSourceDecoder::AttachMediaSource(MediaSource* aMediaSource)
+{
+  MOZ_ASSERT(!mMediaSource && !mDecoderStateMachine);
+  mMediaSource = aMediaSource;
+  mDecoderStateMachine = CreateStateMachine();
+}
+
+void
+MediaSourceDecoder::DetachMediaSource()
+{
+  mMediaSource = nullptr;
+}
+
+SubBufferDecoder*
+MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
+{
+  MediaResource* resource = new SourceBufferResource(nullptr, aType);
+  nsRefPtr<SubBufferDecoder> decoder = new SubBufferDecoder(resource, this);
+  nsAutoPtr<MediaDecoderReader> reader(DecoderTraits::CreateReader(aType, decoder));
+  reader->Init(nullptr);
+
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mDecoders.AppendElement(decoder);
+  mReaders.AppendElement(reader);
+  LOG(PR_LOG_DEBUG, ("Registered subdecoder %p subreader %p", decoder.get(), reader.get()));
+  mon.NotifyAll();
+
+  decoder->SetReader(reader.forget());
+  return decoder;
+}
+
+nsresult
+MediaSourceReader::ReadMetadata(VideoInfo* aInfo, MetadataTags** aTags)
+{
+  mDecoder->SetMediaSeekable(true);
+  mDecoder->SetTransportSeekable(false);
+
+  MediaSourceDecoder* decoder = static_cast<MediaSourceDecoder*>(mDecoder);
+  const nsTArray<MediaDecoderReader*>& readers = decoder->GetReaders();
+  bool gotVideo = false;
+  bool gotAudio = false;
+  for (uint32_t i = 0; i < readers.Length(); ++i) {
+    MediaDecoderReader* reader = readers[i];
+    VideoInfo vi;
+    nsresult rv = reader->ReadMetadata(&vi, aTags);
+    LOG(PR_LOG_DEBUG, ("ReadMetadata on SB reader %p", reader));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    if (vi.mHasVideo && !gotVideo) {
+      mInfo = vi;
+      decoder->SetVideoReader(reader);
+      gotVideo = true;
+    }
+    if (vi.mHasAudio && !gotAudio) {
+      mInfo.mAudioRate = vi.mAudioRate;
+      mInfo.mAudioChannels = vi.mAudioChannels;
+      mInfo.mHasAudio = true;
+      decoder->SetAudioReader(reader);
+      gotAudio = true;
+    }
+  }
+  *aInfo = mInfo;
+
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/MediaSourceDecoder.h
@@ -0,0 +1,246 @@
+/* -*- 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 MOZILLA_MEDIASOURCEDECODER_H_
+#define MOZILLA_MEDIASOURCEDECODER_H_
+
+#include "MediaCache.h"
+#include "MediaDecoder.h"
+#include "MediaResource.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsCOMPtr.h"
+#include "nsError.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+class nsIPrincipal;
+class nsIStreamListener;
+
+namespace mozilla {
+
+class MediaDecoderReader;
+class MediaDecoderStateMachine;
+class SubBufferDecoder;
+
+namespace dom {
+
+class HTMLMediaElement;
+class MediaSource;
+
+} // namespace dom
+
+class MediaSourceDecoder : public MediaDecoder
+{
+public:
+  MediaSourceDecoder(HTMLMediaElement* aElement);
+
+  MediaDecoder* Clone() MOZ_OVERRIDE;
+  MediaDecoderStateMachine* CreateStateMachine() MOZ_OVERRIDE;
+  nsresult Load(nsIStreamListener**, MediaDecoder*) MOZ_OVERRIDE;
+
+  void AttachMediaSource(MediaSource* aMediaSource);
+  void DetachMediaSource();
+
+  SubBufferDecoder* CreateSubDecoder(const nsACString& aType);
+
+  const nsTArray<MediaDecoderReader*>& GetReaders()
+  {
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+    while (mReaders.Length() == 0) {
+      mon.Wait();
+    }
+    return mReaders;
+  }
+
+  void SetVideoReader(MediaDecoderReader* aReader)
+  {
+    MOZ_ASSERT(aReader && !mVideoReader);
+    mVideoReader = aReader;
+  }
+
+  void SetAudioReader(MediaDecoderReader* aReader)
+  {
+    MOZ_ASSERT(aReader && !mAudioReader);
+    mAudioReader = aReader;
+  }
+
+  MediaDecoderReader* GetVideoReader()
+  {
+    return mVideoReader;
+  }
+
+  MediaDecoderReader* GetAudioReader()
+  {
+    return mAudioReader;
+  }
+
+private:
+  MediaSource* mMediaSource;
+
+  nsTArray<nsRefPtr<SubBufferDecoder> > mDecoders;
+  nsTArray<MediaDecoderReader*> mReaders; // Readers owned by Decoders.
+
+  MediaDecoderReader* mVideoReader;
+  MediaDecoderReader* mAudioReader;
+};
+
+class MediaSourceResource MOZ_FINAL : public MediaResource
+{
+public:
+  MediaSourceResource()
+  {
+  }
+
+  nsresult Close()
+  {
+    return NS_OK;
+  }
+
+  void Suspend(bool aCloseImmediately)
+  {
+  }
+
+  void Resume()
+  {
+  }
+
+  already_AddRefed<nsIPrincipal> GetCurrentPrincipal()
+  {
+    return nullptr;
+  }
+
+  bool CanClone()
+  {
+    return false;
+  }
+
+  already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
+  {
+    return nullptr;
+  }
+
+  void SetReadMode(MediaCacheStream::ReadMode aMode)
+  {
+  }
+
+  void SetPlaybackRate(uint32_t aBytesPerSecond)
+  {
+  }
+
+  nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult Seek(int32_t aWhence, int64_t aOffset)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+  void StartSeekingForMetadata()
+  {
+  }
+
+  void EndSeekingForMetadata()
+  {
+  }
+
+  int64_t Tell()
+  {
+    return -1;
+  }
+
+  void Pin()
+  {
+  }
+
+  void Unpin()
+  {
+  }
+
+  double GetDownloadRate(bool* aIsReliable)
+  {
+    return 0;
+  }
+
+  int64_t GetLength()
+  {
+    return -1;
+  }
+
+  int64_t GetNextCachedData(int64_t aOffset)
+  {
+    return aOffset;
+  }
+
+  int64_t GetCachedDataEnd(int64_t aOffset)
+  {
+    return GetLength();
+  }
+
+  bool IsDataCachedToEndOfResource(int64_t aOffset)
+  {
+    return true;
+  }
+
+  bool IsSuspendedByCache()
+  {
+    return false;
+  }
+
+  bool IsSuspended()
+  {
+    return false;
+  }
+
+  nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+
+  nsresult Open(nsIStreamListener** aStreamListener)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+#ifdef MOZ_DASH
+  nsresult OpenByteRange(nsIStreamListener** aStreamListener,
+                         const MediaByteRange& aByteRange)
+  {
+    return NS_ERROR_FAILURE;
+  }
+#endif
+
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+  {
+    aRanges.AppendElement(MediaByteRange(0, GetLength()));
+    return NS_OK;
+  }
+
+  bool IsTransportSeekable() MOZ_OVERRIDE
+  {
+    return true;
+  }
+
+  const nsCString& GetContentType() const MOZ_OVERRIDE
+  {
+    return mType;
+  }
+
+private:
+  const nsAutoCString mType;
+};
+
+} // namespace mozilla
+
+#endif /* MOZILLA_MEDIASOURCEDECODER_H_ */
deleted file mode 100644
--- a/content/media/mediasource/MediaSourceInputAdapter.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/* -*- 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 "MediaSourceInputAdapter.h"
-
-#include "nsStreamUtils.h"
-#include "nsCycleCollectionParticipant.h"
-
-#ifdef PR_LOGGING
-extern PRLogModuleInfo* gMediaSourceLog;
-#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
-#else
-#define LOG(type, msg)
-#endif
-
-namespace mozilla {
-namespace dom {
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::Close()
-{
-  MonitorAutoLock mon(mMediaSource->GetMonitor());
-  LOG(PR_LOG_DEBUG, ("%p IA::Close", this));
-  //MOZ_ASSERT(!mClosed);
-  mClosed = true;
-  NotifyListener();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::Available(uint64_t* aAvailable)
-{
-  MonitorAutoLock mon(mMediaSource->GetMonitor());
-  if (mClosed) {
-    LOG(PR_LOG_DEBUG, ("%p IA::Available (closed)", this));
-    return NS_BASE_STREAM_CLOSED;
-  }
-  *aAvailable = Available();
-  LOG(PR_LOG_DEBUG, ("%p IA::Available available=%llu", this, *aAvailable));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::Read(char* aBuf, uint32_t aCount, uint32_t* aWriteCount)
-{
-  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aWriteCount);
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
-                                      uint32_t aCount, uint32_t* aWriteCount)
-{
-  MonitorAutoLock mon(mMediaSource->GetMonitor());
-
-  uint32_t available = Available();
-  LOG(PR_LOG_DEBUG, ("%p IA::ReadSegments aCount=%u available=%u appendDone=%d rv=%x",
-                     this, aCount, available, mMediaSource->AppendDone(),
-                     mMediaSource->AppendDone() ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK));
-  if (available == 0) {
-    *aWriteCount = 0;
-    return mMediaSource->AppendDone() ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
-  }
-
-  uint32_t count = std::min(aCount, available);
-  nsresult rv = aWriter(this, aClosure,
-                        reinterpret_cast<const char*>(&mMediaSource->GetData()[mOffset]),
-                        0, count, aWriteCount);
-  if (NS_SUCCEEDED(rv)) {
-    MOZ_ASSERT(*aWriteCount <= count);
-    mOffset += *aWriteCount;
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::IsNonBlocking(bool* aNonBlocking)
-{
-  LOG(PR_LOG_DEBUG, ("%p IA::IsNonBlocking", this));
-  *aNonBlocking = true;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::CloseWithStatus(nsresult aStatus)
-{
-  return Close();
-}
-
-NS_IMETHODIMP
-MediaSourceInputAdapter::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
-                                   uint32_t aRequestedCount, nsIEventTarget* aTarget)
-{
-  LOG(PR_LOG_DEBUG, ("%p IA::AsyncWait aCallback=%p aFlags=%u aRequestedCount=%u aTarget=%p",
-                     this, aCallback, aFlags, aRequestedCount, aTarget));
-
-  if (aFlags != 0) {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
-  if (mCallback || mCallbackTarget) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  mCallback = aCallback;
-  mCallbackTarget = aTarget;
-  mNotifyThreshold = aRequestedCount;
-  if (!aRequestedCount) {
-    mNotifyThreshold = 1024;
-  }
-
-  NotifyListener();
-
-  return NS_OK;
-}
-
-void
-MediaSourceInputAdapter::NotifyListener()
-{
-  if (!mCallback) {
-    return;
-  }
-  // Don't notify unless more data is available than the threshold, except
-  // in the case that there's no more data coming.
-  if (Available() < mNotifyThreshold && !mClosed && !mMediaSource->AppendDone()) {
-    return;
-  }
-  nsCOMPtr<nsIInputStreamCallback> callback;
-  if (mCallbackTarget) {
-    callback = NS_NewInputStreamReadyEvent(mCallback, mCallbackTarget);
-  } else {
-    callback = mCallback;
-  }
-  MOZ_ASSERT(callback);
-  mCallback = nullptr;
-  mCallbackTarget = nullptr;
-  mNotifyThreshold = 0;
-  LOG(PR_LOG_DEBUG, ("%p IA::NotifyListener", this));
-  callback->OnInputStreamReady(this);
-
-}
-
-uint64_t
-MediaSourceInputAdapter::Available()
-{
-  return mMediaSource->GetData().Length() - mOffset;
-}
-
-MediaSourceInputAdapter::~MediaSourceInputAdapter()
-{
-  LOG(PR_LOG_DEBUG, ("%p Destroy input adapter", this));
-}
-
-MediaSourceInputAdapter::MediaSourceInputAdapter(MediaSource* aMediaSource)
-  : mMediaSource(aMediaSource)
-  , mOffset(0)
-  , mClosed(false)
-{
-  LOG(PR_LOG_DEBUG, ("%p Create input adapter for %p", this, aMediaSource));
-}
-
-NS_IMPL_CYCLE_COLLECTION_1(MediaSourceInputAdapter, mMediaSource)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaSourceInputAdapter)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaSourceInputAdapter)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaSourceInputAdapter)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
-  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
-NS_INTERFACE_MAP_END
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/content/media/mediasource/MediaSourceInputAdapter.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- 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 MOZILLA_MEDIASOURCEINPUTADAPTER_H_
-#define MOZILLA_MEDIASOURCEINPUTADAPTER_H_
-
-#include "nsIAsyncInputStream.h"
-#include "nsCycleCollectionParticipant.h"
-#include "MediaSource.h"
-
-namespace mozilla {
-namespace dom {
-
-class MediaSourceInputAdapter MOZ_FINAL : public nsIAsyncInputStream
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(MediaSourceInputAdapter)
-  NS_DECL_NSIINPUTSTREAM
-  NS_DECL_NSIASYNCINPUTSTREAM
-
-  MediaSourceInputAdapter(MediaSource* aMediaSource);
-  ~MediaSourceInputAdapter();
-
-  void NotifyListener();
-
-private:
-  uint64_t Available();
-
-  nsRefPtr<MediaSource> mMediaSource;
-  nsCOMPtr<nsIInputStreamCallback> mCallback;
-  nsCOMPtr<nsIEventTarget> mCallbackTarget;
-  int64_t mOffset;
-  uint32_t mNotifyThreshold;
-  bool mClosed;
-};
-
-} // namespace dom
-} // namespace mozilla
-#endif /* MOZILLA_MEDIASOURCEINPUTADAPTER_H_ */
--- a/content/media/mediasource/SourceBuffer.cpp
+++ b/content/media/mediasource/SourceBuffer.cpp
@@ -1,85 +1,164 @@
 /* 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 "SourceBuffer.h"
 
+#include "AsyncEventRunner.h"
+#include "DecoderTraits.h"
+#include "MediaDecoder.h"
+#include "MediaSourceDecoder.h"
+#include "SourceBufferResource.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/dom/MediaSourceBinding.h"
+#include "mozilla/dom/TimeRanges.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
+#include "prlog.h"
+#include "SubBufferDecoder.h"
+
+struct JSContext;
+class JSObject;
+
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaSourceLog;
 #define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
 #else
 #define LOG(type, msg)
 #endif
 
 namespace mozilla {
+
+class MediaResource;
+class ReentrantMonitor;
+
+namespace layers {
+
+class ImageContainer;
+
+} // namespace layers
+
+ReentrantMonitor&
+SubBufferDecoder::GetReentrantMonitor()
+{
+  return mParentDecoder->GetReentrantMonitor();
+}
+
+bool
+SubBufferDecoder::OnStateMachineThread() const
+{
+  return mParentDecoder->OnStateMachineThread();
+}
+
+bool
+SubBufferDecoder::OnDecodeThread() const
+{
+  return mParentDecoder->OnDecodeThread();
+}
+
+void
+SubBufferDecoder::SetMediaDuration(int64_t aDuration)
+{
+  mParentDecoder->SetMediaDuration(aDuration);
+}
+
+void
+SubBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
+{
+  mParentDecoder->UpdateEstimatedMediaDuration(aDuration);
+}
+
+void
+SubBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
+{
+  mParentDecoder->SetMediaSeekable(aMediaSeekable);
+}
+
+void
+SubBufferDecoder::SetTransportSeekable(bool aTransportSeekable)
+{
+  mParentDecoder->SetTransportSeekable(aTransportSeekable);
+}
+
+layers::ImageContainer*
+SubBufferDecoder::GetImageContainer()
+{
+  return mParentDecoder->GetImageContainer();
+}
+
 namespace dom {
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
-  if (!mAttached || mUpdating) {
+  if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
-  // TODO:: Test append state.
-  // TODO:: If aMode is "sequence", set sequence start time.
+  // TODO: Test append state.
+  // TODO: If aMode is "sequence", set sequence start time.
   mAppendMode = aMode;
 }
 
 void
 SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
 {
-  if (!mAttached || mUpdating) {
+  if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
   // TODO: Test append state.
   // TODO: If aMode is "sequence", set sequence start time.
   mTimestampOffset = aTimestampOffset;
 }
 
 already_AddRefed<TimeRanges>
 SourceBuffer::GetBuffered(ErrorResult& aRv)
 {
-  if (!mAttached) {
+  if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   nsRefPtr<TimeRanges> ranges = new TimeRanges();
-  // TODO: Populate ranges.
+  mDecoder->GetBuffered(ranges);
   return ranges.forget();
 }
 
 void
 SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
 {
-  if (!mAttached || mUpdating) {
+  if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   mAppendWindowStart = aAppendWindowStart;
 }
 
 void
 SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
 {
-  if (!mAttached || mUpdating) {
+  if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (IsNaN(aAppendWindowEnd) ||
       aAppendWindowEnd <= mAppendWindowStart) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
@@ -96,77 +175,85 @@ void
 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
 {
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
-  if (!mAttached) {
+  if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mUpdating) {
     // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
     AbortUpdating();
   }
   // TODO: Run reset parser algorithm.
-  // XXX: Need to run these two resets through setters?
   mAppendWindowStart = 0;
   mAppendWindowEnd = PositiveInfinity();
 }
 
 void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
+  if (!IsAttached() || mUpdating ||
+      mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
   if (aStart < 0 || aStart > mMediaSource->Duration() ||
       aEnd <= aStart) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
-  if (!mAttached || mUpdating ||
-      mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return;
-  }
   StartUpdating();
   /// TODO: Run coded frame removal algorithm asynchronously (would call StopUpdating()).
   StopUpdating();
 }
 
 void
-SourceBuffer::Attach()
+SourceBuffer::Detach()
 {
-  MOZ_ASSERT(!mAttached);
-  mAttached = true;
+  Ended();
+  mDecoder = nullptr;
+  mMediaSource = nullptr;
 }
 
 void
-SourceBuffer::Detach()
+SourceBuffer::Ended()
 {
-  MOZ_ASSERT(mAttached);
-  mAttached = false;
+  static_cast<SourceBufferResource*>(mDecoder->GetResource())->Ended();
 }
 
-SourceBuffer::SourceBuffer(MediaSource* aMediaSource)
+SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : nsDOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
   , mAppendWindowStart(0)
   , mAppendWindowEnd(PositiveInfinity())
   , mTimestampOffset(0)
   , mAppendMode(SourceBufferAppendMode::Segments)
   , mUpdating(false)
-  , mAttached(false)
 {
   MOZ_ASSERT(aMediaSource);
+  MediaSourceDecoder* parentDecoder = aMediaSource->GetDecoder();
+  mDecoder = parentDecoder->CreateSubDecoder(aType);
+  MOZ_ASSERT(mDecoder);
+}
+
+SourceBuffer::~SourceBuffer()
+{
+  if (mDecoder) {
+    static_cast<SourceBufferResource*>(mDecoder->GetResource())->Ended();
+  }
 }
 
 MediaSource*
 SourceBuffer::GetParentObject() const
 {
   return mMediaSource;
 }
 
@@ -215,33 +302,39 @@ SourceBuffer::AbortUpdating()
   mUpdating = false;
   QueueAsyncSimpleEvent("abort");
   QueueAsyncSimpleEvent("updateend");
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
-  if (!mAttached || mUpdating) {
+  if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
   // TODO: Run coded frame eviction algorithm.
   // TODO: Test buffer full flag.
-  mMediaSource->AppendData(aData, aLength, aRv); // XXX: Appending to input buffer.
+  LOG(PR_LOG_DEBUG, ("%p Append(ArrayBuffer=%u)", this, aLength));
   StartUpdating();
+  // XXX: For future reference: NDA call must run on the main thread.
+  mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
+                              aLength,
+                              static_cast<SourceBufferResource*>(mDecoder->GetResource())->GetLength());
   // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
+  static_cast<SourceBufferResource*>(mDecoder->GetResource())->AppendData(aData, aLength);
   StopUpdating();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(SourceBuffer, nsDOMEventTargetHelper, mMediaSource)
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 } // namespace dom
+
 } // namespace mozilla
--- a/content/media/mediasource/SourceBuffer.h
+++ b/content/media/mediasource/SourceBuffer.h
@@ -2,35 +2,47 @@
 /* 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 mozilla_dom_SourceBuffer_h_
 #define mozilla_dom_SourceBuffer_h_
 
-#include "AsyncEventRunner.h"
+#include "MediaDecoderReader.h"
 #include "MediaSource.h"
+#include "js/RootingAPI.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/SourceBufferBinding.h"
-#include "mozilla/dom/TimeRanges.h"
 #include "mozilla/dom/TypedArray.h"
-#include "mozilla/ErrorResult.h"
+#include "mozilla/mozalloc.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
-#include "nscore.h"
+#include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMEventTargetHelper.h"
-#include "nsWrapperCache.h"
+#include "nsISupports.h"
+#include "nsStringGlue.h"
+#include "nscore.h"
+
+class JSObject;
+struct JSContext;
 
 namespace mozilla {
 
+class ErrorResult;
+class SourceBufferResource;
+class SubBufferDecoder;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
+class TimeRanges;
+
 class SourceBuffer MOZ_FINAL : public nsDOMEventTargetHelper
 {
 public:
   /** WebIDL Methods. */
   SourceBufferAppendMode Mode() const
   {
     return mAppendMode;
   }
@@ -71,48 +83,55 @@ public:
   void Abort(ErrorResult& aRv);
 
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
   /** End WebIDL Methods. */
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SourceBuffer, nsDOMEventTargetHelper)
 
-  explicit SourceBuffer(MediaSource* aMediaSource);
+  explicit SourceBuffer(MediaSource* aMediaSource, const nsACString& aType);
+  ~SourceBuffer();
 
   MediaSource* GetParentObject() const;
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
-  // Notify the SourceBuffer that it has been attached to or detached from
-  // the MediaSource's sourceBuffer list.
-  void Attach();
+  // Notify the SourceBuffer that it has been detached from the
+  // MediaSource's sourceBuffer list.
   void Detach();
+  bool IsAttached() const
+  {
+    return mMediaSource != nullptr;
+  }
+
+  void Ended();
 
 private:
   friend class AsyncEventRunner<SourceBuffer>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
 
   // Shared implementation of AppendBuffer overloads.
   void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
 
   nsRefPtr<MediaSource> mMediaSource;
 
+  nsRefPtr<SubBufferDecoder> mDecoder;
+
   double mAppendWindowStart;
   double mAppendWindowEnd;
 
   double mTimestampOffset;
 
   SourceBufferAppendMode mAppendMode;
   bool mUpdating;
-
-  bool mAttached;
 };
 
 } // namespace dom
+
 } // namespace mozilla
 #endif /* mozilla_dom_SourceBuffer_h_ */
--- a/content/media/mediasource/SourceBufferList.cpp
+++ b/content/media/mediasource/SourceBufferList.cpp
@@ -1,26 +1,39 @@
 /* -*- 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 "SourceBufferList.h"
 
+#include "AsyncEventRunner.h"
+#include "mozilla/ErrorResult.h"
 #include "mozilla/dom/SourceBufferListBinding.h"
+#include "mozilla/mozalloc.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsIRunnable.h"
+#include "nsStringGlue.h"
+#include "nsThreadUtils.h"
+#include "prlog.h"
+
+struct JSContext;
+class JSObject;
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gMediaSourceLog;
 #define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
 #else
 #define LOG(type, msg)
 #endif
 
 namespace mozilla {
+
 namespace dom {
 
 SourceBuffer*
 SourceBufferList::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
   aFound = aIndex < mSourceBuffers.Length();
   return aFound ? mSourceBuffers[aIndex] : nullptr;
 }
@@ -37,47 +50,42 @@ SourceBufferList::Append(SourceBuffer* a
   mSourceBuffers.AppendElement(aSourceBuffer);
   QueueAsyncSimpleEvent("addsourcebuffer");
 }
 
 void
 SourceBufferList::Remove(SourceBuffer* aSourceBuffer)
 {
   MOZ_ALWAYS_TRUE(mSourceBuffers.RemoveElement(aSourceBuffer));
+  aSourceBuffer->Detach();
   QueueAsyncSimpleEvent("removesourcebuffer");
 }
 
 bool
 SourceBufferList::Contains(SourceBuffer* aSourceBuffer)
 {
   return mSourceBuffers.Contains(aSourceBuffer);
 }
 
 void
 SourceBufferList::Clear()
 {
+  for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
+    mSourceBuffers[i]->Detach();
+  }
   mSourceBuffers.Clear();
   QueueAsyncSimpleEvent("removesourcebuffer");
 }
 
 bool
 SourceBufferList::IsEmpty()
 {
   return mSourceBuffers.IsEmpty();
 }
 
-void
-SourceBufferList::DetachAndClear()
-{
-  for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
-    mSourceBuffers[i]->Detach();
-  }
-  Clear();
-}
-
 bool
 SourceBufferList::AnyUpdating()
 {
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
     if (mSourceBuffers[i]->Updating()) {
       return true;
     }
   }
@@ -91,16 +99,24 @@ SourceBufferList::Remove(double aStart, 
     mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
     if (aRv.Failed()) {
       return;
     }
   }
 }
 
 void
+SourceBufferList::Ended()
+{
+  for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
+    mSourceBuffers[i]->Ended();
+  }
+}
+
+void
 SourceBufferList::DispatchSimpleEvent(const char* aName)
 {
   LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to SourceBufferList", this, aName));
   DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
 }
 
 void
 SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
@@ -134,9 +150,10 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_2(Sou
 
 NS_IMPL_ADDREF_INHERITED(SourceBufferList, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBufferList, nsDOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBufferList)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 } // namespace dom
+
 } // namespace mozilla
--- a/content/media/mediasource/SourceBufferList.h
+++ b/content/media/mediasource/SourceBufferList.h
@@ -2,25 +2,29 @@
 /* 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 mozilla_dom_SourceBufferList_h_
 #define mozilla_dom_SourceBufferList_h_
 
-#include "AsyncEventRunner.h"
-#include "MediaSource.h"
 #include "SourceBuffer.h"
+#include "js/RootingAPI.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
-#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionNoteChild.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMEventTargetHelper.h"
-#include "nsWrapperCache.h"
-#include "nscore.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
+
+struct JSContext;
+class JSObject;
 
 namespace mozilla {
 
 class ErrorResult;
 template <typename T> class AsyncEventRunner;
 
 namespace dom {
 
@@ -54,30 +58,31 @@ public:
   bool Contains(SourceBuffer* aSourceBuffer);
 
   // Remove all SourceBuffers and fire a single "removesourcebuffer" at the list.
   void Clear();
 
   // True if list has zero entries.
   bool IsEmpty();
 
-  // Detach and remove all SourceBuffers and fire a single "removesourcebuffer" at the list.
-  void DetachAndClear();
-
   // Returns true if updating is true on any SourceBuffers in the list.
   bool AnyUpdating();
 
   // Calls Remove(aStart, aEnd) on each SourceBuffer in the list.  Aborts on
   // first error, with result returned in aRv.
   void Remove(double aStart, double aEnd, ErrorResult& aRv);
 
+  // Mark all SourceBuffers input buffers as ended.
+  void Ended();
+
 private:
   friend class AsyncEventRunner<SourceBufferList>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   nsRefPtr<MediaSource> mMediaSource;
   nsTArray<nsRefPtr<SourceBuffer> > mSourceBuffers;
 };
 
 } // namespace dom
+
 } // namespace mozilla
 #endif /* mozilla_dom_SourceBufferList_h_ */
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/SourceBufferResource.cpp
@@ -0,0 +1,162 @@
+/* -*- 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 "SourceBufferResource.h"
+
+#include <string.h>
+#include <algorithm>
+
+#include "nsISeekableStream.h"
+#include "nsTraceRefcnt.h"
+#include "prenv.h"
+#include "prlog.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaSourceLog;
+#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
+#else
+#define LOG(type, msg)
+#endif
+
+namespace mozilla {
+
+namespace dom {
+
+class SourceBuffer;
+
+}  // namespace dom
+
+nsresult
+SourceBufferResource::Close()
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  LOG(PR_LOG_DEBUG, ("%p SBR::Close", this));
+  //MOZ_ASSERT(!mClosed);
+  mClosed = true;
+  mon.NotifyAll();
+  return NS_OK;
+}
+
+nsresult
+SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  bool blockingRead = !!aBytes;
+
+  while (blockingRead && !mEnded && mOffset + aCount > GetLength()) {
+    LOG(PR_LOG_DEBUG, ("%p SBR::Read waiting for data", this));
+    mon.Wait();
+  }
+
+  uint32_t available = GetLength() - mOffset;
+  uint32_t count = std::min(aCount, available);
+  if (!PR_GetEnv("MOZ_QUIET")) {
+    LOG(PR_LOG_DEBUG, ("%p SBR::Read aCount=%u length=%u offset=%u "
+                       "available=%u count=%u, blocking=%d bufComplete=%d",
+                       this, aCount, GetLength(), mOffset, available, count,
+                       blockingRead, mEnded));
+  }
+  if (available == 0) {
+    LOG(PR_LOG_DEBUG, ("%p SBR::Read EOF", this));
+    *aBytes = 0;
+    return NS_OK;
+  }
+
+  memcpy(aBuffer, &mInputBuffer[mOffset], count);
+  *aBytes = count;
+  mOffset += count;
+  return NS_OK;
+}
+
+nsresult
+SourceBufferResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return Read(aBuffer, aCount, aBytes);
+}
+
+nsresult
+SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  if (mClosed) {
+    return NS_ERROR_FAILURE;
+  }
+
+  int64_t newOffset = mOffset;
+  switch (aWhence) {
+  case nsISeekableStream::NS_SEEK_END:
+    newOffset = GetLength() - aOffset;
+    break;
+  case nsISeekableStream::NS_SEEK_CUR:
+    newOffset += aOffset;
+    break;
+  case nsISeekableStream::NS_SEEK_SET:
+    newOffset = aOffset;
+    break;
+  }
+
+  if (newOffset < 0 || newOffset > GetLength()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mOffset = newOffset;
+  mon.NotifyAll();
+
+  return NS_OK;
+}
+
+nsresult
+SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  return Read(aBuffer, aCount, nullptr);
+}
+
+void
+SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  mInputBuffer.AppendElements(aData, aLength);
+  mon.NotifyAll();
+}
+
+void
+SourceBufferResource::Ended()
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  mEnded = true;
+  mon.NotifyAll();
+}
+
+SourceBufferResource::~SourceBufferResource()
+{
+  MOZ_COUNT_DTOR(SourceBufferResource);
+  LOG(PR_LOG_DEBUG, ("%p SBR::~SBR", this));
+}
+
+SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
+                                           const nsACString& aType)
+  : mPrincipal(aPrincipal)
+  , mType(aType)
+  , mMonitor("mozilla::SourceBufferResource::mMonitor")
+  , mOffset(0)
+  , mClosed(false)
+  , mEnded(false)
+{
+  MOZ_COUNT_CTOR(SourceBufferResource);
+  LOG(PR_LOG_DEBUG, ("%p SBR::SBR()", this));
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/SourceBufferResource.h
@@ -0,0 +1,145 @@
+/* -*- 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 MOZILLA_SOURCEBUFFERRESOURCE_H_
+#define MOZILLA_SOURCEBUFFERRESOURCE_H_
+
+#include "MediaCache.h"
+#include "MediaResource.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsCOMPtr.h"
+#include "nsError.h"
+#include "nsIPrincipal.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+#include "nscore.h"
+
+class nsIStreamListener;
+
+namespace mozilla {
+
+class MediaDecoder;
+
+namespace dom {
+
+class SourceBuffer;
+
+}  // namespace dom
+
+class SourceBufferResource MOZ_FINAL : public MediaResource
+{
+public:
+  SourceBufferResource(nsIPrincipal* aPrincipal,
+                       const nsACString& aType);
+  ~SourceBufferResource();
+
+  nsresult Close();
+  void Suspend(bool aCloseImmediately) {}
+  void Resume() {}
+
+  already_AddRefed<nsIPrincipal> GetCurrentPrincipal()
+  {
+    return nsCOMPtr<nsIPrincipal>(mPrincipal).forget();
+  }
+
+  bool CanClone()
+  {
+    return false;
+  }
+
+  already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
+  {
+    return nullptr;
+  }
+
+  void SetReadMode(MediaCacheStream::ReadMode aMode)
+  {
+  }
+
+  void SetPlaybackRate(uint32_t aBytesPerSecond)
+  {
+  }
+
+  nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes);
+  nsresult ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes);
+  nsresult Seek(int32_t aWhence, int64_t aOffset);
+
+  void StartSeekingForMetadata()
+  {
+  }
+
+  void EndSeekingForMetadata()
+  {
+  }
+
+  int64_t Tell()
+  {
+    return mOffset;
+  }
+
+  void Pin()
+  {
+  }
+
+  void Unpin()
+  {
+  }
+
+  double GetDownloadRate(bool* aIsReliable) { return 0; }
+  int64_t GetLength() { return mInputBuffer.Length(); }
+  int64_t GetNextCachedData(int64_t aOffset) { return aOffset; }
+  int64_t GetCachedDataEnd(int64_t aOffset) { return GetLength(); }
+  bool IsDataCachedToEndOfResource(int64_t aOffset) { return true; }
+  bool IsSuspendedByCache() { return false; }
+  bool IsSuspended() { return false; }
+  nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount);
+
+  nsresult Open(nsIStreamListener** aStreamListener)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+#ifdef MOZ_DASH
+  nsresult OpenByteRange(nsIStreamListener** aStreamListener,
+                         const MediaByteRange& aByteRange)
+  {
+    return NS_ERROR_FAILURE;
+  }
+#endif
+
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges)
+  {
+    aRanges.AppendElement(MediaByteRange(0, GetLength()));
+    return NS_OK;
+  }
+
+  bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
+
+  const nsCString& GetContentType() const MOZ_OVERRIDE
+  {
+    return mType;
+  }
+
+  // Used by SourceBuffer.
+  void AppendData(const uint8_t* aData, uint32_t aLength);
+  void Ended();
+
+private:
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  const nsAutoCString mType;
+
+  // Provides synchronization between SourceBuffers and InputAdapters.
+  ReentrantMonitor mMonitor;
+  nsTArray<uint8_t> mInputBuffer;
+
+  int64_t mOffset;
+  bool mClosed;
+  bool mEnded;
+};
+
+} // namespace mozilla
+#endif /* MOZILLA_SOURCEBUFFERRESOURCE_H_ */
new file mode 100644
--- /dev/null
+++ b/content/media/mediasource/SubBufferDecoder.h
@@ -0,0 +1,62 @@
+/* -*- 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 MOZILLA_SUBBUFFERDECODER_H_
+#define MOZILLA_SUBBUFFERDECODER_H_
+
+#include "BufferDecoder.h"
+
+namespace mozilla {
+
+class MediaSourceDecoder;
+
+class SubBufferDecoder : public BufferDecoder
+{
+public:
+  // This class holds a weak pointer to MediaResource.  It's the responsibility
+  // of the caller to manage the memory of the MediaResource object.
+  SubBufferDecoder(MediaResource* aResource, MediaSourceDecoder* aParentDecoder)
+    : BufferDecoder(aResource), mParentDecoder(aParentDecoder), mReader(nullptr)
+  {
+  }
+
+  void SetReader(MediaDecoderReader* aReader)
+  {
+    MOZ_ASSERT(!mReader);
+    mReader = aReader;
+  }
+
+  ReentrantMonitor& GetReentrantMonitor() MOZ_OVERRIDE;
+  bool OnStateMachineThread() const MOZ_OVERRIDE;
+  bool OnDecodeThread() const MOZ_OVERRIDE;
+  void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
+  void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
+  void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;
+  void SetTransportSeekable(bool aTransportSeekable) MOZ_OVERRIDE;
+  layers::ImageContainer* GetImageContainer() MOZ_OVERRIDE;
+
+  void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
+  {
+    mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
+
+    // XXX: aOffset makes no sense here, need view of "data timeline".
+    mParentDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
+  }
+
+  nsresult GetBuffered(TimeRanges* aBuffered)
+  {
+    // XXX: Need mStartTime (from StateMachine) instead of passing 0.
+    return mReader->GetBuffered(aBuffered, 0);
+  }
+
+private:
+  MediaSourceDecoder* mParentDecoder;
+  nsAutoPtr<MediaDecoderReader> mReader;
+};
+
+} // namespace mozilla
+
+#endif /* MOZILLA_SUBBUFFERDECODER_H_ */
--- a/content/media/mediasource/moz.build
+++ b/content/media/mediasource/moz.build
@@ -6,29 +6,31 @@
 PARALLEL_DIRS += [
     'test'
 ]
 
 MODULE = 'content'
 
 EXPORTS += [
     'AsyncEventRunner.h',
+    'MediaSourceDecoder.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'MediaSource.h',
     'SourceBuffer.h',
     'SourceBufferList.h',
 ]
 
 CPP_SOURCES += [
     'MediaSource.cpp',
-    'MediaSourceInputAdapter.cpp',
+    'MediaSourceDecoder.cpp',
     'SourceBuffer.cpp',
     'SourceBufferList.cpp',
+    'SourceBufferResource.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'gkconmediasource_s'