Bug 734546: Add DASH Decoders and Readers r=cpearce r=ted
authorSteve Workman <sworkman@mozilla.com>
Sat, 29 Sep 2012 16:29:04 -0700
changeset 108776 d882662a0bb6a753b34f9133c951c08def1a8851
parent 108775 6df63191884b8631984d92bef4b937ab084042cd
child 108777 a7c6371727090378860d6401de60c96a3aff1579
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewerscpearce, ted
bugs734546
milestone18.0a1
Bug 734546: Add DASH Decoders and Readers r=cpearce r=ted
configure.in
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/Makefile.in
content/media/MediaResource.cpp
content/media/dash/Makefile.in
content/media/dash/nsDASHDecoder.cpp
content/media/dash/nsDASHDecoder.h
content/media/dash/nsDASHReader.cpp
content/media/dash/nsDASHReader.h
content/media/dash/nsDASHRepDecoder.cpp
content/media/dash/nsDASHRepDecoder.h
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderReader.cpp
content/media/nsBuiltinDecoderReader.h
content/media/webm/Makefile.in
layout/build/Makefile.in
modules/libpref/src/init/all.js
netwerk/dash/Makefile.in
netwerk/dash/mpd/Makefile.in
netwerk/dash/mpd/nsDASHMPDParser.cpp
netwerk/dash/mpd/nsDASHWebMODManager.cpp
netwerk/dash/mpd/nsDASHWebMODParser.cpp
netwerk/mime/nsMimeTypes.h
uriloader/exthandler/nsExternalHelperAppService.cpp
--- a/configure.in
+++ b/configure.in
@@ -4176,16 +4176,17 @@ MOZ_CUBEB=
 MOZ_VORBIS=
 MOZ_TREMOR=
 MOZ_WAVE=1
 MOZ_SAMPLE_TYPE_FLOAT32=
 MOZ_SAMPLE_TYPE_S16=
 MOZ_MEDIA=
 MOZ_OPUS=1
 MOZ_WEBM=1
+MOZ_DASH=
 MOZ_WEBRTC=1
 MOZ_SRTP=
 MOZ_WEBRTC_SIGNALING=
 MOZ_MEDIA_PLUGINS=
 MOZ_MEDIA_NAVIGATOR=
 MOZ_OMX_PLUGIN=
 MOZ_VP8=
 MOZ_VP8_ERROR_CONCEALMENT=
@@ -5267,16 +5268,34 @@ MOZ_ARG_DISABLE_BOOL(webm,
     MOZ_WEBM=1)
 
 if test -n "$MOZ_WEBM"; then
     AC_DEFINE(MOZ_WEBM)
     MOZ_VP8=1
 fi;
 
 dnl ========================================================
+dnl = Enable DASH-WebM support
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(dash,
+[  --enable-dash          Enable support for DASH-WebM],
+    MOZ_DASH=1,
+    MOZ_DASH=)
+
+if test -n "$MOZ_DASH"; then
+    if test -n "$MOZ_WEBM"; then
+        AC_DEFINE(MOZ_DASH)
+    else
+        dnl Fail if WebM is not enabled as well as DASH.
+        AC_MSG_ERROR([WebM is currently disabled and must be enabled for DASH
+                     to work.])
+    fi
+fi;
+
+dnl ========================================================
 dnl = Enable media plugin support
 dnl ========================================================
 if test "$OS_TARGET" = Android -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk"; then
   dnl Enable support on android by default
   MOZ_MEDIA_PLUGINS=1
 fi
 
 MOZ_ARG_ENABLE_BOOL(media-plugins,
@@ -8518,16 +8537,17 @@ AC_SUBST(MOZ_MEDIA)
 AC_SUBST(MOZ_SYDNEYAUDIO)
 AC_SUBST(MOZ_SPEEX_RESAMPLER)
 AC_SUBST(MOZ_CUBEB)
 AC_SUBST(MOZ_WAVE)
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_OPUS)
 AC_SUBST(MOZ_WEBM)
+AC_SUBST(MOZ_DASH)
 AC_SUBST(MOZ_MEDIA_PLUGINS)
 AC_SUBST(MOZ_OMX_PLUGIN)
 AC_SUBST(MOZ_VP8_ERROR_CONCEALMENT)
 AC_SUBST(MOZ_VP8_ENCODER)
 AC_SUBST(MOZ_VP8)
 AC_SUBST(MOZ_OGG)
 AC_SUBST(MOZ_ALSA_LIBS)
 AC_SUBST(MOZ_ALSA_CFLAGS)
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -28,29 +28,36 @@
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
 class MediaResource;
 }
+#ifdef MOZ_DASH
+class nsDASHDecoder;
+#endif
 
 class nsHTMLMediaElement : public nsGenericHTMLElement,
                            public nsIObserver
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
   typedef mozilla::MediaStream MediaStream;
   typedef mozilla::MediaResource MediaResource;
 
   typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
 
+#ifdef MOZ_DASH
+  friend class nsDASHDecoder;
+#endif
+
   enum CanPlayStatus {
     CANPLAY_NO,
     CANPLAY_MAYBE,
     CANPLAY_YES
   };
 
   mozilla::CORSMode GetCORSMode() {
     return mCORSMode;
@@ -314,16 +321,22 @@ public:
   static char const *const gH264Codecs[7];
 #endif
 
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsEnabled();
   static bool IsMediaPluginsType(const nsACString& aType);
 #endif
 
+#ifdef MOZ_DASH
+  static bool IsDASHEnabled();
+  static bool IsDASHMPDType(const nsACString& aType);
+  static const char gDASHMPDTypes[1][21];
+#endif
+
   /**
    * Get the mime type for this element.
    */
   void GetMimeType(nsCString& aMimeType);
 
   /**
    * Called when a child source element is added to this media element. This
    * may queue a task to run the select resource algorithm if appropriate.
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -86,16 +86,19 @@
 #endif
 #ifdef MOZ_MEDIA_PLUGINS
 #include "nsMediaPluginHost.h"
 #include "nsMediaPluginDecoder.h"
 #endif
 #ifdef MOZ_WIDGET_GONK
 #include "nsMediaOmxDecoder.h"
 #endif
+#ifdef MOZ_DASH
+#include "nsDASHDecoder.h"
+#endif
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gMediaElementLog;
 static PRLogModuleInfo* gMediaElementEventsLog;
 #define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
 #define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
 #else
 #define LOG(type, msg)
@@ -2216,16 +2219,47 @@ nsHTMLMediaElement::IsMediaPluginsType(c
     if (aType.EqualsASCII(supportedTypes[i])) {
       return true;
     }
   }
   return false;
 }
 #endif
 
+#ifdef MOZ_DASH
+/* static */
+const char nsHTMLMediaElement::gDASHMPDTypes[1][21] = {
+  "application/dash+xml"
+};
+
+/* static */
+bool
+nsHTMLMediaElement::IsDASHEnabled()
+{
+  return Preferences::GetBool("media.dash.enabled");
+}
+
+/* static */
+bool
+nsHTMLMediaElement::IsDASHMPDType(const nsACString& aType)
+{
+  if (!IsDASHEnabled()) {
+    return false;
+  }
+
+  for (uint32_t i = 0; i < ArrayLength(gDASHMPDTypes); ++i) {
+    if (aType.EqualsASCII(gDASHMPDTypes[i])) {
+      return true;
+    }
+  }
+
+  return false;
+}
+#endif
+
 /* static */
 nsHTMLMediaElement::CanPlayStatus 
 nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
                                        char const *const ** aCodecList)
 {
 #ifdef MOZ_RAW
   if (IsRawType(nsDependentCString(aMIMEType))) {
     *aCodecList = gRawCodecs;
@@ -2245,16 +2279,23 @@ nsHTMLMediaElement::CanHandleMediaType(c
   }
 #endif
 #ifdef MOZ_WEBM
   if (IsWebMType(nsDependentCString(aMIMEType))) {
     *aCodecList = gWebMCodecs;
     return CANPLAY_YES;
   }
 #endif
+#ifdef MOZ_DASH
+  if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
+    // DASH manifest uses WebM codecs only.
+    *aCodecList = gWebMCodecs;
+    return CANPLAY_YES;
+  }
+#endif
 
 #ifdef MOZ_GSTREAMER
   if (IsH264Type(nsDependentCString(aMIMEType))) {
     *aCodecList = gH264Codecs;
     return CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_WIDGET_GONK
@@ -2434,16 +2475,25 @@ nsHTMLMediaElement::CreateDecoder(const 
     nsRefPtr<nsWebMDecoder> decoder = new nsWebMDecoder();
 #endif
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
 
+#ifdef MOZ_DASH
+  if (IsDASHMPDType(aType)) {
+    nsRefPtr<nsDASHDecoder> decoder = new nsDASHDecoder();
+    if (decoder->Init(this)) {
+      return decoder.forget();
+    }
+  }
+#endif
+
 #ifdef MOZ_GSTREAMER 
   if (IsH264Type(aType)) {
     nsRefPtr<nsGStreamerDecoder> decoder = new nsGStreamerDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -79,16 +79,20 @@ endif
 ifdef MOZ_WEBM
 PARALLEL_DIRS += webm
 endif
 
 ifdef MOZ_GSTREAMER
 PARALLEL_DIRS += gstreamer
 endif
 
+ifdef MOZ_DASH
+PARALLEL_DIRS += dash
+endif
+
 ifdef MOZ_MEDIA_PLUGINS
 PARALLEL_DIRS += plugins
 endif
 
 PARALLEL_DIRS += webrtc
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 PARALLEL_DIRS += omx
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -976,17 +976,16 @@ ChannelMediaResource::CacheClientSeek(in
   //       block, so we need to adjust the first chunk of a seek.
   //       E.g. For "DASH-WebM On Demand" this means the first chunk after
   //       seeking will most likely be larger than the subsegment (cluster).
   //
   //   3 - Call |OpenByteRange| requesting |mByteRange| bytes.
 
   if (mByteRangeDownloads) {
     // Query decoder for chunk containing desired offset.
-    // XXX Implement |nsDASHRepDecoder|::|GetByteRange| in future patch.
     nsresult rv;
     {
       ReentrantMonitorAutoEnter mon(mSeekOffsetMonitor);
       // Ensure that media cache can only request an equal or smaller offset;
       // it may be trying to include the start of a cache block.
       NS_ENSURE_TRUE(aOffset <= mSeekOffset, NS_ERROR_ILLEGAL_VALUE);
       rv = mDecoder->GetByteRangeForSeek(mSeekOffset, mByteRange);
       mSeekOffset = -1;
new file mode 100644
--- /dev/null
+++ b/content/media/dash/Makefile.in
@@ -0,0 +1,44 @@
+# -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
+# vim: set ts=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/.
+#
+# Contributor(s):
+#     Steve Workman <sworkman@mozilla.com>
+
+DEPTH      := @DEPTH@
+topsrcdir  := @top_srcdir@
+srcdir     := @srcdir@
+VPATH      := @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         := content
+LIBRARY_NAME   := gkcondash_s
+LIBXUL_LIBRARY := 1
+
+EXPORTS := \
+  nsDASHDecoder.h \
+  nsDASHRepDecoder.h \
+  nsDASHReader.h \
+  $(NULL)
+
+CPPSRCS := \
+  nsDASHDecoder.cpp \
+  nsDASHRepDecoder.cpp \
+  nsDASHReader.cpp \
+  $(NULL)
+
+FORCE_STATIC_LIB := 1
+
+include $(topsrcdir)/config/rules.mk
+
+LOCAL_INCLUDES := \
+  -I$(topsrcdir)/netwerk/dash/mpd \
+  -I$(srcdir)/../webm \
+  -I$(srcdir)/../../base/src \
+  -I$(srcdir)/../../html/content/src \
+  $(MOZ_LIBVPX_INCLUDES) \
+  $(NULL)
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHDecoder.cpp
@@ -0,0 +1,725 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP.
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * Interaction with nsBuiltinDecoderStateMachine, nsHTMLMediaElement,
+ * ChannelMediaResource and sub-decoders (nsWebMDecoder).
+ *
+ *
+ *        nsBuiltinDecoderStateMachine      nsHTMLMediaElement
+ *               1 /           \ 1           / 1
+ *                /             \           /
+ *             1 /               \ 1       / 1
+ * nsDASHReader ------ nsDASHDecoder ------------ ChannelMediaResource
+ *          |1          1      1     |1      \1             (for MPD Manifest)
+ *          |                        |        ------------
+ *          |*                       |*                   \*
+ *     nsWebMReader ------- nsDASHRepDecoder ------- ChannelMediaResource
+ *                 1       1                      1       1 (for media streams)
+ *
+ * One decoder and state-machine, as with current, non-DASH decoders.
+ *
+ * DASH adds multiple readers, decoders and resources, in order to manage
+ * download and decode of the MPD manifest and individual media streams.
+ *
+ * Rep/|Representation| is for an individual media stream, e.g. audio
+ * nsDASHRepDecoder is the decoder for a rep/|Representation|.
+ *
+ * FLOW
+ *
+ * 1 - Download and parse the MPD (DASH XML-based manifest).
+ *
+ * Media element creates new |nsDASHDecoder| object:
+ *   member var initialization to default values, including a/v sub-decoders.
+ *   nsBuiltinDecoder and nsMediaDecoder constructors are called.
+ *   nsBuiltinDecoder::Init() is called.
+ *
+ * Media element creates new |ChannelMediaResource|:
+ *   used to download MPD manifest.
+ *
+ * Media element calls |nsDASHDecoder|->Load() to download the MPD file:
+ *   creates an |nsDASHReader| object to forward calls to multiple
+ *     nsWebMReaders (corresponding to MPD |Representation|s i.e. streams).
+ *     Note: 1 |nsDASHReader| per DASH/WebM MPD.
+ *
+ *   also calls |ChannelMediaResource|::Open().
+ *     uses nsHttpChannel to download MPD; notifies nsDASHDecoder.
+ *
+ * Meanwhile, back in |nsDASHDecoder|->Load():
+ *   nsBuiltinDecoderStateMachine is created.
+ *     has ref to |nsDASHReader| object.
+ *     state machine is scheduled.
+ *
+ * Media element finishes decoder setup:
+ *   element added to media URI table etc.
+ *
+ * -- At this point, objects are waiting on HTTP returning MPD data.
+ *
+ * MPD Download (Async |ChannelMediaResource| channel callbacks):
+ *   calls nsDASHDecoder::|NotifyDownloadEnded|().
+ *     nsDASHDecoder parses MPD XML to DOM to MPD classes.
+ *       gets |Segment| URLs from MPD for audio and video streams.
+ *       creates |nsIChannel|s, |ChannelMediaResource|s.
+ *         stores resources as member vars (to forward function calls later).
+ *       creates |nsWebMReader|s and |nsDASHRepDecoder|s.
+ *         DASHreader creates |nsWebMReader|s.
+ *         |Representation| decoders are connected to the |ChannelMediaResource|s.
+ *
+ *     |nsDASHDecoder|->|LoadRepresentations|() starts download and decode.
+ *
+ *
+ * 2 - Media Stream, Byte Range downloads.
+ *
+ * -- At this point the Segment media stream downloads are managed by
+ *    individual |ChannelMediaResource|s and |nsWebMReader|s.
+ *    A single |nsDASHDecoder| and |nsBuiltinDecoderStateMachine| manage them
+ *    and communicate to |nsHTMLMediaElement|.
+ *
+ * Each |nsDASHRepDecoder| gets init range and index range from its MPD
+ * |Representation|. |nsDASHRepDecoder| uses ChannelMediaResource to start the
+ * byte range downloads, calling |OpenByteRange| with a |MediaByteRange|
+ * object.
+ * Once the init and index segments have been downloaded and |ReadMetadata| has
+ * completed, each |nsWebMReader| notifies it's peer |nsDASHRepDecoder|.
+ * Note: the decoder must wait until index data is parsed because it needs to
+ *       get the offsets of the subsegments (WebM clusters) from the media file
+ *       itself.
+ * Since byte ranges for subsegments are obtained, |nsDASHRepdecoder| continues
+ * downloading the files in byte range chunks.
+ *
+ * XXX Note that this implementation of nsDASHRepDecoder is focused on DASH
+ *     WebM On Demand profile: on the todo list is an action item to make this
+ *     more abstract.
+ *
+ * Note on |Seek|: Currently, |nsMediaCache| requires that seeking start at the
+ *                 beginning of the block in which the desired offset would be
+ *                 found. As such, when |ChannelMediaResource| does a seek
+ *                 using DASH WebM subsegments (clusters), it requests a start
+ *                 offset that corresponds to the beginning of the block, not
+ *                 the start offset of the cluster. For DASH Webm, which has
+ *                 media encoded in single files, this is fine. Future work on
+ *                 other profiles will require this to be re-examined.
+ */
+
+#include <limits>
+#include <prdtoa.h>
+#include "nsIURI.h"
+#include "nsIFileURL.h"
+#include "nsNetUtil.h"
+#include "VideoUtils.h"
+#include "nsThreadUtils.h"
+#include "nsContentUtils.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsICachingChannel.h"
+#include "nsBuiltinDecoderStateMachine.h"
+#include "nsWebMDecoder.h"
+#include "nsWebMReader.h"
+#include "nsDASHReader.h"
+#include "nsDASHMPDParser.h"
+#include "nsDASHRepDecoder.h"
+#include "nsDASHDecoder.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gBuiltinDecoderLog;
+#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                             ("%p [nsDASHDecoder] " msg, this, __VA_ARGS__))
+#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                         ("%p [nsDASHDecoder] " msg, this))
+#else
+#define LOG(msg, ...)
+#define LOG1(msg)
+#endif
+
+nsDASHDecoder::nsDASHDecoder() :
+  nsBuiltinDecoder(),
+  mNotifiedLoadAborted(false),
+  mBuffer(nullptr),
+  mBufferLength(0),
+  mMPDReaderThread(nullptr),
+  mPrincipal(nullptr),
+  mDASHReader(nullptr),
+  mAudioRepDecoder(nullptr),
+  mVideoRepDecoder(nullptr)
+{
+  MOZ_COUNT_CTOR(nsDASHDecoder);
+}
+
+nsDASHDecoder::~nsDASHDecoder()
+{
+  MOZ_COUNT_DTOR(nsDASHDecoder);
+}
+
+nsDecoderStateMachine*
+nsDASHDecoder::CreateStateMachine()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  return new nsBuiltinDecoderStateMachine(this, mDASHReader);
+}
+
+void
+nsDASHDecoder::ReleaseStateMachine()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  // Since state machine owns mDASHReader, remove reference to it.
+  mDASHReader = nullptr;
+
+  nsBuiltinDecoder::ReleaseStateMachine();
+  for (uint i = 0; i < mAudioRepDecoders.Length(); i++) {
+    mAudioRepDecoders[i]->ReleaseStateMachine();
+  }
+  for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
+    mVideoRepDecoders[i]->ReleaseStateMachine();
+  }
+}
+
+nsresult
+nsDASHDecoder::Load(MediaResource* aResource,
+                    nsIStreamListener** aStreamListener,
+                    nsMediaDecoder* aCloneDonor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  mDASHReader = new nsDASHReader(this);
+
+  nsresult rv = OpenResource(aResource, aStreamListener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mDecoderStateMachine = CreateStateMachine();
+  if (!mDecoderStateMachine) {
+    LOG1("Failed to create state machine!");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+void
+nsDASHDecoder::NotifyDownloadEnded(nsresult aStatus)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  // Should be no download ended notification if MPD Manager exists.
+  if (mMPDManager) {
+    LOG("Network Error! Repeated MPD download notification but MPD Manager "
+        "[%p] already exists!", mMPDManager.get());
+    NetworkError();
+    return;
+  }
+
+  if (NS_SUCCEEDED(aStatus)) {
+    LOG1("MPD downloaded.");
+
+    // mPrincipal must be set on main thread before dispatch to parser thread.
+    mPrincipal = GetCurrentPrincipal();
+
+    // Create reader thread for |ChannelMediaResource|::|Read|.
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &nsDASHDecoder::ReadMPDBuffer);
+    NS_ENSURE_TRUE(event, );
+
+    nsresult rv = NS_NewNamedThread("DASH MPD Reader",
+                                    getter_AddRefs(mMPDReaderThread),
+                                    event,
+                                    MEDIA_THREAD_STACK_SIZE);
+    if (NS_FAILED(rv) || !mMPDReaderThread) {
+      LOG("Error creating MPD reader thread: rv[%x] thread [%p].",
+          rv, mMPDReaderThread.get());
+      DecodeError();
+      return;
+    }
+  } else if (aStatus == NS_BINDING_ABORTED) {
+    LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
+    if (mElement) {
+      mElement->LoadAborted();
+    }
+    return;
+  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
+    LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
+    NetworkError();
+  }
+}
+
+void
+nsDASHDecoder::ReadMPDBuffer()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
+
+  LOG1("Started reading from the MPD buffer.");
+
+  int64_t length = mResource->GetLength();
+  if (length <= 0 || length > DASH_MAX_MPD_SIZE) {
+    LOG("MPD is larger than [%d]MB.", DASH_MAX_MPD_SIZE/(1024*1024));
+    DecodeError();
+    return;
+  }
+
+  mBuffer = new char[length];
+
+  uint32_t count = 0;
+  nsresult rv = mResource->Read(mBuffer, length, &count);
+  // By this point, all bytes should be available for reading.
+  if (NS_FAILED(rv) || count != length) {
+    LOG("Error reading MPD buffer: rv [%x] count [%d] length [%d].",
+        rv, count, length);
+    DecodeError();
+    return;
+  }
+  // Store buffer length for processing on main thread.
+  mBufferLength = static_cast<uint32_t>(length);
+
+  LOG1("Finished reading MPD buffer; back to main thread for parsing.");
+
+  // Dispatch event to Main thread to parse MPD buffer.
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &nsDASHDecoder::OnReadMPDBufferCompleted);
+  rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    LOG("Error dispatching parse event to main thread: rv[%x]", rv);
+    DecodeError();
+    return;
+  }
+}
+
+void
+nsDASHDecoder::OnReadMPDBufferCompleted()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (mShuttingDown) {
+    return;
+  }
+
+  // Shutdown the thread.
+  if (!mMPDReaderThread) {
+    LOG1("Error: MPD reader thread does not exist!");
+    DecodeError();
+    return;
+  }
+  nsresult rv = mMPDReaderThread->Shutdown();
+  if (NS_FAILED(rv)) {
+    LOG("MPD reader thread did not shutdown correctly! rv [%x]", rv);
+    DecodeError();
+    return;
+  }
+  mMPDReaderThread = nullptr;
+
+  // Close the MPD resource.
+  rv = mResource ? mResource->Close() : NS_ERROR_NULL_POINTER;
+  if (NS_FAILED(rv)) {
+    LOG("Media Resource did not close correctly! rv [%x]", rv);
+    NetworkError();
+    return;
+  }
+
+  // Start parsing the MPD data and loading the media.
+  rv = ParseMPDBuffer();
+  if (NS_FAILED(rv)) {
+    LOG("Error parsing MPD buffer! rv [%x]", rv);
+    DecodeError();
+    return;
+  }
+  rv = CreateRepDecoders();
+  if (NS_FAILED(rv)) {
+    LOG("Error creating decoders for Representations! rv [%x]", rv);
+    DecodeError();
+    return;
+  }
+
+  rv = LoadRepresentations();
+  if (NS_FAILED(rv)) {
+    LOG("Error loading Representations! rv [%x]", rv);
+    NetworkError();
+    return;
+  }
+
+  // Notify reader that it can start reading metadata. Sub-readers will still
+  // block until sub-resources have downloaded data into the media cache.
+  mDASHReader->ReadyToReadMetadata();
+}
+
+nsresult
+nsDASHDecoder::ParseMPDBuffer()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(mBuffer, NS_ERROR_NULL_POINTER);
+
+  LOG1("Started parsing the MPD buffer.");
+
+  // Parse MPD buffer and get root DOM element.
+  nsAutoPtr<nsDASHMPDParser> parser;
+  parser = new nsDASHMPDParser(mBuffer.forget(), mBufferLength, mPrincipal,
+                               mResource->URI());
+  mozilla::net::DASHMPDProfile profile;
+  parser->Parse(getter_Transfers(mMPDManager), &profile);
+  mBuffer = nullptr;
+  NS_ENSURE_TRUE(mMPDManager, NS_ERROR_NULL_POINTER);
+
+  LOG("Finished parsing the MPD buffer. Profile is [%d].", profile);
+
+  return NS_OK;
+}
+
+nsresult
+nsDASHDecoder::CreateRepDecoders()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(mMPDManager, NS_ERROR_NULL_POINTER);
+
+  // Global settings for the presentation.
+  int64_t startTime = mMPDManager->GetStartTime();
+  mDuration = mMPDManager->GetDuration();
+  NS_ENSURE_TRUE(startTime >= 0 && mDuration > 0, NS_ERROR_ILLEGAL_VALUE);
+
+  // For each audio/video stream, create a |ChannelMediaResource| object.
+
+  for (int i = 0; i < mMPDManager->GetNumAdaptationSets(); i++) {
+    IMPDManager::AdaptationSetType asType = mMPDManager->GetAdaptationSetType(i);
+    for (int j = 0; j < mMPDManager->GetNumRepresentations(i); j++) {
+      // Get URL string.
+      nsAutoString segmentUrl;
+      nsresult rv = mMPDManager->GetFirstSegmentUrl(i, j, segmentUrl);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Get segment |nsIURI|; use MPD's base URI in case of relative paths.
+      nsCOMPtr<nsIURI> url;
+      rv = NS_NewURI(getter_AddRefs(url), segmentUrl, nullptr, mResource->URI());
+      NS_ENSURE_SUCCESS(rv, rv);
+#ifdef PR_LOGGING
+      nsAutoCString newUrl;
+      rv = url->GetSpec(newUrl);
+      NS_ENSURE_SUCCESS(rv, rv);
+      LOG("Using URL=\"%s\" for AdaptationSet [%d] Representation [%d]",
+          newUrl.get(), i, j);
+#endif
+
+      // 'file://' URLs are not supported.
+      nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url);
+      NS_ENSURE_FALSE(fileURL, NS_ERROR_ILLEGAL_VALUE);
+
+      // Create |nsDASHRepDecoder| objects for each representation.
+      if (asType == IMPDManager::DASH_VIDEO_STREAM) {
+        Representation const * rep = mMPDManager->GetRepresentation(i, j);
+        NS_ENSURE_TRUE(rep, NS_ERROR_NULL_POINTER);
+        rv = CreateVideoRepDecoder(url, rep);
+        NS_ENSURE_SUCCESS(rv, rv);
+      } else if (asType == IMPDManager::DASH_AUDIO_STREAM) {
+        Representation const * rep = mMPDManager->GetRepresentation(i, j);
+        NS_ENSURE_TRUE(rep, NS_ERROR_NULL_POINTER);
+        rv = CreateAudioRepDecoder(url, rep);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+  }
+
+  NS_ENSURE_TRUE(mVideoRepDecoder, NS_ERROR_NOT_INITIALIZED);
+  NS_ENSURE_TRUE(mAudioRepDecoder, NS_ERROR_NOT_INITIALIZED);
+
+  return NS_OK;
+}
+
+nsresult
+nsDASHDecoder::CreateAudioRepDecoder(nsIURI* aUrl,
+                                     mozilla::net::Representation const * aRep)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_ARG(aUrl);
+  NS_ENSURE_ARG(aRep);
+  NS_ENSURE_TRUE(mElement, NS_ERROR_NOT_INITIALIZED);
+
+  // Create subdecoder and init with media element.
+  nsDASHRepDecoder* audioDecoder = new nsDASHRepDecoder(this);
+  NS_ENSURE_TRUE(audioDecoder->Init(mElement), NS_ERROR_NOT_INITIALIZED);
+
+  if (!mAudioRepDecoder) {
+    mAudioRepDecoder = audioDecoder;
+  }
+  mAudioRepDecoders.AppendElement(audioDecoder);
+
+  // Create sub-reader; attach to DASH reader and sub-decoder.
+  nsWebMReader* audioReader = new nsWebMReader(audioDecoder);
+  if (mDASHReader) {
+    mDASHReader->AddAudioReader(audioReader);
+  }
+  audioDecoder->SetReader(audioReader);
+
+  // Create media resource with URL and connect to sub-decoder.
+  MediaResource* audioResource
+    = CreateAudioSubResource(aUrl, static_cast<nsMediaDecoder*>(audioDecoder));
+  NS_ENSURE_TRUE(audioResource, NS_ERROR_NOT_INITIALIZED);
+
+  audioDecoder->SetResource(audioResource);
+  audioDecoder->SetMPDRepresentation(aRep);
+
+  return NS_OK;
+}
+
+nsresult
+nsDASHDecoder::CreateVideoRepDecoder(nsIURI* aUrl,
+                                     mozilla::net::Representation const * aRep)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_ARG(aUrl);
+  NS_ENSURE_ARG(aRep);
+  NS_ENSURE_TRUE(mElement, NS_ERROR_NOT_INITIALIZED);
+
+  // Create subdecoder and init with media element.
+  nsDASHRepDecoder* videoDecoder = new nsDASHRepDecoder(this);
+  NS_ENSURE_TRUE(videoDecoder->Init(mElement), NS_ERROR_NOT_INITIALIZED);
+
+  if (!mVideoRepDecoder) {
+    mVideoRepDecoder = videoDecoder;
+  }
+  mVideoRepDecoders.AppendElement(videoDecoder);
+
+  // Create sub-reader; attach to DASH reader and sub-decoder.
+  nsWebMReader* videoReader = new nsWebMReader(videoDecoder);
+  if (mDASHReader) {
+    mDASHReader->AddVideoReader(videoReader);
+  }
+  videoDecoder->SetReader(videoReader);
+
+  // Create media resource with URL and connect to sub-decoder.
+  MediaResource* videoResource
+    = CreateVideoSubResource(aUrl, static_cast<nsMediaDecoder*>(videoDecoder));
+  NS_ENSURE_TRUE(videoResource, NS_ERROR_NOT_INITIALIZED);
+
+  videoDecoder->SetResource(videoResource);
+  videoDecoder->SetMPDRepresentation(aRep);
+
+  return NS_OK;
+}
+
+mozilla::MediaResource*
+nsDASHDecoder::CreateAudioSubResource(nsIURI* aUrl,
+                                      nsMediaDecoder* aAudioDecoder)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(aUrl, nullptr);
+  NS_ENSURE_TRUE(aAudioDecoder, nullptr);
+
+  // Create channel for representation.
+  nsCOMPtr<nsIChannel> channel;
+  nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  // Create resource for representation.
+  MediaResource* audioResource
+    = MediaResource::Create(aAudioDecoder, channel);
+  NS_ENSURE_TRUE(audioResource, nullptr);
+
+  return audioResource;
+}
+
+mozilla::MediaResource*
+nsDASHDecoder::CreateVideoSubResource(nsIURI* aUrl,
+                                      nsMediaDecoder* aVideoDecoder)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(aUrl, nullptr);
+  NS_ENSURE_TRUE(aVideoDecoder, nullptr);
+
+  // Create channel for representation.
+  nsCOMPtr<nsIChannel> channel;
+  nsresult rv = CreateSubChannel(aUrl, getter_AddRefs(channel));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  // Create resource for representation.
+  MediaResource* videoResource
+    = MediaResource::Create(aVideoDecoder, channel);
+  NS_ENSURE_TRUE(videoResource, nullptr);
+
+  return videoResource;
+}
+
+nsresult
+nsDASHDecoder::CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_ARG(aUrl);
+
+  nsCOMPtr<nsILoadGroup> loadGroup = mElement->GetDocumentLoadGroup();
+  NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
+
+  // Check for a Content Security Policy to pass down to the channel
+  // created to load the media content.
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  nsresult rv = mElement->NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv,rv);
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
+  }
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel),
+                     aUrl,
+                     nullptr,
+                     loadGroup,
+                     nullptr,
+                     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
+                     channelPolicy);
+  NS_ENSURE_SUCCESS(rv,rv);
+  NS_ENSURE_TRUE(channel, NS_ERROR_NULL_POINTER);
+
+  NS_ADDREF(*aChannel = channel);
+  return NS_OK;
+}
+
+nsresult
+nsDASHDecoder::LoadRepresentations()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  nsresult rv;
+  {
+    // Hold the lock while we do this to set proper lock ordering
+    // expectations for dynamic deadlock detectors: decoder lock(s)
+    // should be grabbed before the cache lock.
+    ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+
+    // Load the decoders for each |Representation|'s media streams.
+    if (mAudioRepDecoder) {
+      rv = mAudioRepDecoder->Load();
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    if (mVideoRepDecoder) {
+      rv = mVideoRepDecoder->Load();
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    if (NS_FAILED(rv)) {
+      LOG("Failed to open stream! rv [%x].", rv);
+      return rv;
+    }
+  }
+
+  if (mAudioRepDecoder) {
+    mAudioRepDecoder->SetStateMachine(mDecoderStateMachine);
+  }
+  if (mVideoRepDecoder) {
+    mVideoRepDecoder->SetStateMachine(mDecoderStateMachine);
+  }
+
+  // Now that subreaders are init'd, it's ok to init state machine.
+  return InitializeStateMachine(nullptr);
+}
+
+void
+nsDASHDecoder::NotifyDownloadEnded(nsDASHRepDecoder* aRepDecoder,
+                                   nsresult aStatus,
+                                   MediaByteRange &aRange)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  // MPD Manager must exist, indicating MPD has been downloaded and parsed.
+  if (!mMPDManager) {
+    LOG1("Network Error! MPD Manager must exist, indicating MPD has been "
+        "downloaded and parsed");
+    NetworkError();
+    return;
+  }
+
+  // Decoder for the media |Representation| must not be null.
+  if (!aRepDecoder) {
+    LOG1("Decoder for Representation is reported as null.");
+    DecodeError();
+    return;
+  }
+
+  if (NS_SUCCEEDED(aStatus)) {
+    // Return error if |aRepDecoder| does not match current audio/video decoder.
+    if (aRepDecoder != mAudioRepDecoder && aRepDecoder != mVideoRepDecoder) {
+      LOG("Error! Decoder [%p] does not match current sub-decoders!",
+          aRepDecoder);
+      DecodeError();
+      return;
+    }
+    LOG("Byte range downloaded: decoder [%p] range requested [%d - %d]",
+        aRepDecoder, aRange.mStart, aRange.mEnd);
+
+    // XXX Do Stream Switching here before loading next bytes, e.g.
+    // decoder = PossiblySwitchDecoder(aRepDecoder);
+    // decoder->LoadNextByteRange();
+    aRepDecoder->LoadNextByteRange();
+    return;
+  } else if (aStatus == NS_BINDING_ABORTED) {
+    LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
+    if (mElement) {
+      mElement->LoadAborted();
+    }
+    return;
+  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
+    LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
+    NetworkError();
+  }
+}
+
+void
+nsDASHDecoder::LoadAborted()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (!mNotifiedLoadAborted && mElement) {
+    mElement->LoadAborted();
+    mNotifiedLoadAborted = true;
+    LOG1("Load Aborted! Notifying media element.");
+  }
+}
+
+void
+nsDASHDecoder::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  // Notify reader of shutdown first.
+  if (mDASHReader) {
+    mDASHReader->NotifyDecoderShuttingDown();
+  }
+
+  // Call parent class shutdown.
+  nsBuiltinDecoder::Shutdown();
+  NS_ENSURE_TRUE(mShuttingDown, );
+
+  // Shutdown reader thread if not already done.
+  if (mMPDReaderThread) {
+    nsresult rv = mMPDReaderThread->Shutdown();
+    NS_ENSURE_SUCCESS(rv, );
+    mMPDReaderThread = nullptr;
+  }
+
+  // Forward to sub-decoders.
+  for (uint i = 0; i < mAudioRepDecoders.Length(); i++) {
+    if (mAudioRepDecoders[i]) {
+      mAudioRepDecoders[i]->Shutdown();
+    }
+  }
+  for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
+    if (mVideoRepDecoders[i]) {
+      mVideoRepDecoders[i]->Shutdown();
+    }
+  }
+}
+
+void
+nsDASHDecoder::DecodeError()
+{
+  if (NS_IsMainThread()) {
+    nsBuiltinDecoder::DecodeError();
+  } else {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &nsBuiltinDecoder::DecodeError);
+    nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      LOG("Error dispatching DecodeError event to main thread: rv[%x]", rv);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHDecoder.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * see nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
+
+#if !defined(nsDASHDecoder_h_)
+#define nsDASHDecoder_h_
+
+#include "nsTArray.h"
+#include "nsIURI.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+#include "nsBuiltinDecoder.h"
+#include "nsDASHReader.h"
+
+class nsDASHRepDecoder;
+
+namespace mozilla {
+namespace net {
+class IMPDManager;
+class nsDASHMPDParser;
+class Representation;
+}// net
+}// mozilla
+
+class nsDASHDecoder : public nsBuiltinDecoder
+{
+public:
+  typedef class mozilla::net::IMPDManager IMPDManager;
+  typedef class mozilla::net::nsDASHMPDParser nsDASHMPDParser;
+  typedef class mozilla::net::Representation Representation;
+
+  // XXX Arbitrary max file size for MPD. 50MB seems generously large.
+  static const uint32_t DASH_MAX_MPD_SIZE = 50*1024*1024;
+
+  nsDASHDecoder();
+  ~nsDASHDecoder();
+
+  // Clone not supported; just return nullptr.
+  nsMediaDecoder* Clone() { return nullptr; }
+
+  // Creates a single state machine for all stream decoders.
+  // Called from Load on the main thread only.
+  nsDecoderStateMachine* CreateStateMachine();
+
+  // Loads the MPD from the network and subsequently loads the media streams.
+  // Called from the main thread only.
+  nsresult Load(MediaResource* aResource,
+                nsIStreamListener** aListener,
+                nsMediaDecoder* aCloneDonor);
+
+  // Notifies download of MPD file has ended.
+  // Called on the main thread only.
+  void NotifyDownloadEnded(nsresult aStatus);
+
+  // Notifies that a byte range download has ended. As per the DASH spec, this
+  // allows for stream switching at the boundaries of the byte ranges.
+  // Called on the main thread only.
+  void NotifyDownloadEnded(nsDASHRepDecoder* aRepDecoder,
+                           nsresult aStatus,
+                           MediaByteRange &aRange);
+
+  // Drop reference to state machine and tell sub-decoders to do the same.
+  // Only called during shutdown dance, on main thread only.
+  void ReleaseStateMachine();
+
+  // Overridden to forward |Shutdown| to sub-decoders.
+  // Called on the main thread only.
+  void Shutdown();
+
+  // Called by sub-decoders when load has been aborted. Will notify media
+  // element only once. Called on the main thread only.
+  void LoadAborted();
+
+  // Notifies the element that decoding has failed. On main thread, call is
+  // forwarded to |nsBuiltinDecoder|::|Error| immediately. On other threads,
+  // a call is dispatched for execution on the main thread.
+  void DecodeError();
+
+private:
+  // Reads the MPD data from resource to a byte stream.
+  // Called on the MPD reader thread.
+  void ReadMPDBuffer();
+
+  // Called when MPD data is completely read.
+  // On the main thread.
+  void OnReadMPDBufferCompleted();
+
+  // Parses the copied MPD byte stream.
+  // On the main thread: DOM APIs complain when off the main thread.
+  nsresult ParseMPDBuffer();
+
+  // Creates the sub-decoders for a |Representation|, i.e. media streams.
+  // On the main thread.
+  nsresult CreateRepDecoders();
+
+  // Creates audio/video decoders for individual |Representation|s.
+  // On the main thread.
+  nsresult CreateAudioRepDecoder(nsIURI* aUrl, Representation const * aRep);
+  nsresult CreateVideoRepDecoder(nsIURI* aUrl, Representation const * aRep);
+
+  // Creates audio/video resources for individual |Representation|s.
+  // On the main thread.
+  MediaResource* CreateAudioSubResource(nsIURI* aUrl,
+                                        nsMediaDecoder* aAudioDecoder);
+  MediaResource* CreateVideoSubResource(nsIURI* aUrl,
+                                        nsMediaDecoder* aVideoDecoder);
+
+  // Creates an http channel for a |Representation|.
+  // On the main thread.
+  nsresult CreateSubChannel(nsIURI* aUrl, nsIChannel** aChannel);
+
+  // Loads the media |Representations|, i.e. the media streams.
+  // On the main thread.
+  nsresult LoadRepresentations();
+
+  // True when media element has already been notified of an aborted load.
+  bool mNotifiedLoadAborted;
+
+  // Ptr for the MPD data.
+  nsAutoArrayPtr<char>         mBuffer;
+  // Length of the MPD data.
+  uint32_t                     mBufferLength;
+  // Ptr to the MPD Reader thread.
+  nsCOMPtr<nsIThread>          mMPDReaderThread;
+  // Document Principal.
+  nsCOMPtr<nsIPrincipal>       mPrincipal;
+
+  // MPD Manager provides access to the MPD information.
+  nsAutoPtr<IMPDManager>       mMPDManager;
+
+  // Main reader object; manages all sub-readers for |Representation|s. Owned by
+  // state machine; destroyed in state machine's destructor.
+  nsDASHReader* mDASHReader;
+
+  // Sub-decoder for current audio |Representation|.
+  nsRefPtr<nsDASHRepDecoder> mAudioRepDecoder;
+  // Array of pointers for the |Representation|s in the audio |AdaptationSet|.
+  nsTArray<nsRefPtr<nsDASHRepDecoder> > mAudioRepDecoders;
+
+  // Sub-decoder for current video |Representation|.
+  nsRefPtr<nsDASHRepDecoder> mVideoRepDecoder;
+  // Array of pointers for the |Representation|s in the video |AdaptationSet|.
+  nsTArray<nsRefPtr<nsDASHRepDecoder> > mVideoRepDecoders;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHReader.cpp
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * see nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
+
+#include "nsTimeRanges.h"
+#include "VideoFrameContainer.h"
+#include "nsBuiltinDecoder.h"
+#include "nsDASHReader.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gBuiltinDecoderLog;
+#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                             ("%p [nsDASHReader] " msg, this, __VA_ARGS__))
+#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                         ("%p [nsDASHReader] " msg, this))
+#else
+#define LOG(msg, ...)
+#define LOG1(msg)
+#endif
+
+nsresult
+nsDASHReader::Init(nsBuiltinDecoderReader* aCloneDonor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  NS_ASSERTION(mAudioReaders.Length() != 0 && mVideoReaders.Length() != 0,
+               "Audio and video readers should exist already.");
+
+  nsresult rv;
+  for (uint i = 0; i < mAudioReaders.Length(); i++) {
+    rv = mAudioReaders[i]->Init(nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  for (uint i = 0; i < mVideoReaders.Length(); i++) {
+    rv = mVideoReaders[i]->Init(nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+void
+nsDASHReader::AddAudioReader(nsBuiltinDecoderReader* aAudioReader)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(aAudioReader, );
+
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+  mAudioReaders.AppendElement(aAudioReader);
+  // XXX For now, just pick the first reader to be default.
+  if (!mAudioReader)
+    mAudioReader = aAudioReader;
+}
+
+void
+nsDASHReader::AddVideoReader(nsBuiltinDecoderReader* aVideoReader)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(aVideoReader, );
+
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+  mVideoReaders.AppendElement(aVideoReader);
+  // XXX For now, just pick the first reader to be default.
+  if (!mVideoReader)
+    mVideoReader = aVideoReader;
+}
+
+int64_t
+nsDASHReader::VideoQueueMemoryInUse()
+{
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  return (mVideoReader ? mVideoReader->VideoQueueMemoryInUse() : 0);
+}
+
+int64_t
+nsDASHReader::AudioQueueMemoryInUse()
+{
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  return (mAudioReader ? mAudioReader->AudioQueueMemoryInUse() : 0);
+}
+
+bool
+nsDASHReader::DecodeVideoFrame(bool &aKeyframeSkip,
+                               int64_t aTimeThreshold)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  if (mVideoReader) {
+   return mVideoReader->DecodeVideoFrame(aKeyframeSkip, aTimeThreshold);
+  } else {
+   return false;
+  }
+}
+
+bool
+nsDASHReader::DecodeAudioData()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  return (mAudioReader ? mAudioReader->DecodeAudioData() : false);
+}
+
+nsresult
+nsDASHReader::ReadMetadata(nsVideoInfo* aInfo,
+                           nsHTMLMediaElement::MetadataTags** aTags)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  // Wait for MPD to be parsed and child readers created.
+  LOG1("Waiting for metadata download.");
+  nsresult rv = WaitForMetadata();
+  // If we get an abort, return silently; the decoder is shutting down.
+  if (NS_ERROR_ABORT == rv) {
+    return NS_OK;
+  }
+  // Verify no other errors before continuing.
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Get metadata from child readers.
+  nsVideoInfo audioInfo, videoInfo;
+
+  if (mVideoReader) {
+    rv = mVideoReader->ReadMetadata(&videoInfo, aTags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mInfo.mHasVideo      = videoInfo.mHasVideo;
+    mInfo.mDisplay       = videoInfo.mDisplay;
+  }
+  if (mAudioReader) {
+    rv = mAudioReader->ReadMetadata(&audioInfo, aTags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mInfo.mHasAudio      = audioInfo.mHasAudio;
+    mInfo.mAudioRate     = audioInfo.mAudioRate;
+    mInfo.mAudioChannels = audioInfo.mAudioChannels;
+    mInfo.mStereoMode    = audioInfo.mStereoMode;
+  }
+
+  *aInfo = mInfo;
+
+  return NS_OK;
+}
+
+nsresult
+nsDASHReader::Seek(int64_t aTime,
+                   int64_t aStartTime,
+                   int64_t aEndTime,
+                   int64_t aCurrentTime)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  nsresult rv;
+
+  if (mAudioReader) {
+    rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  if (mVideoReader) {
+    rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+nsresult
+nsDASHReader::GetBuffered(nsTimeRanges* aBuffered,
+                          int64_t aStartTime)
+{
+  NS_ENSURE_ARG(aBuffered);
+
+  MediaResource* resource = nullptr;
+  nsBuiltinDecoder* decoder = nullptr;
+
+  // Need to find intersect of |nsTimeRanges| for audio and video.
+  nsTimeRanges audioBuffered, videoBuffered;
+  uint32_t audioRangeCount, videoRangeCount;
+
+  nsresult rv = NS_OK;
+
+  // First, get buffered ranges for sub-readers.
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  if (mAudioReader) {
+    decoder = mAudioReader->GetDecoder();
+    NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
+    resource = decoder->GetResource();
+    NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
+    resource->Pin();
+    rv = mAudioReader->GetBuffered(&audioBuffered, aStartTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+    resource->Unpin();
+    rv = audioBuffered.GetLength(&audioRangeCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  if (mVideoReader) {
+    decoder = mVideoReader->GetDecoder();
+    NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
+    resource = decoder->GetResource();
+    NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
+    resource->Pin();
+    rv = mVideoReader->GetBuffered(&videoBuffered, aStartTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+    resource->Unpin();
+    rv = videoBuffered.GetLength(&videoRangeCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Now determine buffered data for available sub-readers.
+  if (mAudioReader && mVideoReader) {
+    // Calculate intersecting ranges.
+    for (uint32_t i = 0; i < audioRangeCount; i++) {
+      // |A|udio, |V|ideo, |I|ntersect.
+      double startA, startV, startI;
+      double endA, endV, endI;
+      rv = audioBuffered.Start(i, &startA);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = audioBuffered.End(i, &endA);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      for (uint32_t j = 0; j < videoRangeCount; j++) {
+        rv = videoBuffered.Start(i, &startV);
+        NS_ENSURE_SUCCESS(rv, rv);
+        rv = videoBuffered.End(i, &endV);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // If video block is before audio block, compare next video block.
+        if (startA > endV) {
+          continue;
+        // If video block is after audio block, all of them are; compare next
+        // audio block.
+        } else if (endA < startV) {
+          break;
+        }
+        // Calculate intersections of current audio and video blocks.
+        startI = (startA > startV) ? startA : startV;
+        endI = (endA > endV) ? endV : endA;
+        aBuffered->Add(startI, endI);
+      }
+    }
+  } else if (mAudioReader) {
+    *aBuffered = audioBuffered;
+  } else if (mVideoReader) {
+    *aBuffered = videoBuffered;
+  } else {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  return NS_OK;
+}
+
+VideoData*
+nsDASHReader::FindStartTime(int64_t& aOutStartTime)
+{
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine or decode thread.");
+
+  // Extract the start times of the bitstreams in order to calculate
+  // the duration.
+  int64_t videoStartTime = INT64_MAX;
+  int64_t audioStartTime = INT64_MAX;
+  VideoData* videoData = nullptr;
+
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  if (HasVideo()) {
+    // Forward to video reader.
+    videoData
+       = mVideoReader->DecodeToFirstData(&nsBuiltinDecoderReader::DecodeVideoFrame,
+                                         VideoQueue());
+    if (videoData) {
+      videoStartTime = videoData->mTime;
+    }
+  }
+  if (HasAudio()) {
+    // Forward to audio reader.
+    AudioData* audioData
+        = mAudioReader->DecodeToFirstData(&nsBuiltinDecoderReader::DecodeAudioData,
+                                          AudioQueue());
+    if (audioData) {
+      audioStartTime = audioData->mTime;
+    }
+  }
+
+  int64_t startTime = NS_MIN(videoStartTime, audioStartTime);
+  if (startTime != INT64_MAX) {
+    aOutStartTime = startTime;
+  }
+
+  return videoData;
+}
+
+MediaQueue<AudioData>&
+nsDASHReader::AudioQueue()
+{
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  NS_ASSERTION(mAudioReader, "mAudioReader is NULL!");
+  return mAudioReader->AudioQueue();
+}
+
+MediaQueue<VideoData>&
+nsDASHReader::VideoQueue()
+{
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  NS_ASSERTION(mVideoReader, "mVideoReader is NULL!");
+  return mVideoReader->VideoQueue();
+}
+
+bool
+nsDASHReader::IsSeekableInBufferedRanges()
+{
+  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
+                                         mDecoder->GetReentrantMonitor());
+  // At least one subreader must exist, and all subreaders must return true.
+  return (mVideoReader || mAudioReader) &&
+          !((mVideoReader && !mVideoReader->IsSeekableInBufferedRanges()) ||
+            (mAudioReader && !mAudioReader->IsSeekableInBufferedRanges()));
+}
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHReader.h
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * see nsDASHDecoder.cpp for comments on DASH object interaction
+ */
+
+#if !defined(nsDASHReader_h_)
+#define nsDASHReader_h_
+
+#include "nsBuiltinDecoderReader.h"
+
+class nsDASHReader : public nsBuiltinDecoderReader
+{
+public:
+  typedef mozilla::MediaResource MediaResource;
+
+  nsDASHReader(nsBuiltinDecoder* aDecoder) :
+    nsBuiltinDecoderReader(aDecoder),
+    mReadMetadataMonitor("media.dashreader.readmetadata"),
+    mReadyToReadMetadata(false),
+    mDecoderIsShuttingDown(false),
+    mAudioReader(this),
+    mVideoReader(this),
+    mAudioReaders(this),
+    mVideoReaders(this)
+  {
+    MOZ_COUNT_CTOR(nsDASHReader);
+  }
+  ~nsDASHReader()
+  {
+    MOZ_COUNT_DTOR(nsDASHReader);
+  }
+
+  // Adds a pointer to a audio/video reader for a media |Representation|.
+  // Called on the main thread only.
+  void AddAudioReader(nsBuiltinDecoderReader* aAudioReader);
+  void AddVideoReader(nsBuiltinDecoderReader* aVideoReader);
+
+  // Waits for metadata bytes to be downloaded, then reads and parses them.
+  // Called on the decode thread only.
+  nsresult ReadMetadata(nsVideoInfo* aInfo,
+                        nsHTMLMediaElement::MetadataTags** aTags);
+
+  // Waits for |ReadyToReadMetadata| or |NotifyDecoderShuttingDown|
+  // notification, whichever comes first. Ensures no attempt to read metadata
+  // during |nsDASHDecoder|::|Shutdown|. Called on decode thread only.
+  nsresult WaitForMetadata() {
+    NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+    ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
+    while (true) {
+      // Abort if the decoder has started shutting down.
+      if (mDecoderIsShuttingDown) {
+        return NS_ERROR_ABORT;
+      } else if (mReadyToReadMetadata) {
+        break;
+      }
+      mon.Wait();
+    }
+    return NS_OK;
+  }
+
+  // Called on the main thread by |nsDASHDecoder| to notify that metadata bytes
+  // have been downloaded.
+  void ReadyToReadMetadata() {
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+    ReentrantMonitorAutoEnter mon(mReadMetadataMonitor);
+    mReadyToReadMetadata = true;
+    mon.NotifyAll();
+  }
+
+  // Called on the main thread by |nsDASHDecoder| when it starts Shutdown. Will
+  // wake metadata monitor if waiting for a silent return from |ReadMetadata|.
+  void NotifyDecoderShuttingDown() {
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+    ReentrantMonitorAutoEnter metadataMon(mReadMetadataMonitor);
+    mDecoderIsShuttingDown = true;
+    // Notify |ReadMetadata| of the shutdown if it's waiting.
+    metadataMon.NotifyAll();
+  }
+
+  // Audio/video status are dependent on the presence of audio/video readers.
+  // Call on decode thread only.
+  bool HasAudio() {
+    NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+    return mAudioReader ? mAudioReader->HasAudio() : false;
+  }
+  bool HasVideo() {
+    NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+    return mVideoReader ? mVideoReader->HasVideo() : false;
+  }
+
+  // Returns references to the audio/video queues of sub-readers. Called on
+  // decode, state machine and audio threads.
+  MediaQueue<AudioData>& AudioQueue();
+  MediaQueue<VideoData>& VideoQueue();
+
+  // Called from nsBuiltinDecoderStateMachine on the main thread.
+  nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
+
+  // Used by |MediaMemoryReporter|.
+  int64_t VideoQueueMemoryInUse();
+  int64_t AudioQueueMemoryInUse();
+
+  // Called on the decode thread.
+  bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold);
+  bool DecodeAudioData();
+
+  // Converts seek time to byte offset. Called on the decode thread only.
+  nsresult Seek(int64_t aTime,
+                int64_t aStartTime,
+                int64_t aEndTime,
+                int64_t aCurrentTime);
+
+  // Called by state machine on multiple threads.
+  nsresult GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime);
+
+  // Called on the state machine or decode threads.
+  VideoData* FindStartTime(int64_t& aOutStartTime);
+
+  // Call by state machine on multiple threads.
+  bool IsSeekableInBufferedRanges();
+
+private:
+  // Similar to |ReentrantMonitorAutoEnter|, this class enters the supplied
+  // monitor in its constructor, but only if the conditional value |aEnter| is
+  // true. Used here to allow read access on the sub-readers' owning thread,
+  // i.e. the decode thread, while locking write accesses from all threads,
+  // and read accesses from non-decode threads.
+  class ReentrantMonitorConditionallyEnter
+  {
+  public:
+    ReentrantMonitorConditionallyEnter(bool aEnter,
+                                       ReentrantMonitor &aReentrantMonitor) :
+      mReentrantMonitor(nullptr)
+    {
+      MOZ_COUNT_CTOR(nsDASHReader::ReentrantMonitorConditionallyEnter);
+      if (aEnter) {
+        mReentrantMonitor = &aReentrantMonitor;
+        NS_ASSERTION(mReentrantMonitor, "null monitor");
+        mReentrantMonitor->Enter();
+      }
+    }
+    ~ReentrantMonitorConditionallyEnter(void)
+    {
+      if (mReentrantMonitor) {
+        mReentrantMonitor->Exit();
+      }
+      MOZ_COUNT_DTOR(nsDASHReader::ReentrantMonitorConditionallyEnter);
+    }
+  private:
+    // Restrict to constructor and destructor defined above.
+    ReentrantMonitorConditionallyEnter();
+    ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
+    ReentrantMonitorConditionallyEnter& operator =(const ReentrantMonitorConditionallyEnter&);
+    static void* operator new(size_t) CPP_THROW_NEW;
+    static void operator delete(void*);
+
+    // Ptr to the |ReentrantMonitor| object. Null if |aEnter| in constructor
+    // was false.
+    ReentrantMonitor* mReentrantMonitor;
+  };
+
+  // Monitor and booleans used to wait for metadata bytes to be downloaded, and
+  // skip reading metadata if |nsDASHDecoder|'s shutdown is in progress.
+  ReentrantMonitor mReadMetadataMonitor;
+  bool mReadyToReadMetadata;
+  bool mDecoderIsShuttingDown;
+
+  // Wrapper class protecting accesses to sub-readers. Asserts that the
+  // decoder monitor has been entered for write access on all threads and read
+  // access on all threads that are not the decode thread. Read access on the
+  // decode thread does not need to be protected.
+  class MonitoredSubReader
+  {
+  public:
+    // Main constructor takes a pointer to the owning |nsDASHReader| to verify
+    // correct entry into the decoder's |ReentrantMonitor|.
+    MonitoredSubReader(nsDASHReader* aReader) :
+      mReader(aReader),
+      mSubReader(nullptr)
+    {
+      MOZ_COUNT_CTOR(nsDASHReader::MonitoredSubReader);
+      NS_ASSERTION(mReader, "Reader is null!");
+    }
+    // Note: |mSubReader|'s refcount will be decremented in this destructor.
+    ~MonitoredSubReader()
+    {
+      MOZ_COUNT_DTOR(nsDASHReader::MonitoredSubReader);
+    }
+
+    // Override '=' to always assert thread is "in monitor" for writes/changes
+    // to |mSubReader|.
+    MonitoredSubReader& operator=(nsBuiltinDecoderReader* rhs)
+    {
+      NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
+      mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
+      mSubReader = rhs;
+      return *this;
+    }
+
+    // Override '*' to assert threads other than the decode thread are "in
+    // monitor" for ptr reads.
+    operator nsBuiltinDecoderReader*() const
+    {
+      NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
+      if (!mReader->GetDecoder()->OnDecodeThread()) {
+        mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
+      }
+      return mSubReader;
+    }
+
+    // Override '->' to assert threads other than the decode thread are "in
+    // monitor" for |mSubReader| function calls.
+    nsBuiltinDecoderReader* operator->() const
+    {
+      return *this;
+    }
+  private:
+    // Pointer to |nsDASHReader| object which owns this |MonitoredSubReader|.
+    nsDASHReader* mReader;
+    // Ref ptr to the sub reader.
+    nsRefPtr<nsBuiltinDecoderReader> mSubReader;
+  };
+
+  // Wrapped ref ptrs to current sub-readers of individual media
+  // |Representation|s. Decoder monitor must be entered for write access on all
+  // threads and read access on all threads that are not the decode thread.
+  // Read access on the decode thread does not need to be protected.
+  // Note: |MonitoredSubReader| class will assert correct monitor use.
+  MonitoredSubReader mAudioReader;
+  MonitoredSubReader mVideoReader;
+
+  // Wrapper class protecting accesses to sub-reader list. Asserts that the
+  // decoder monitor has been entered for write access on all threads and read
+  // access on all threads that are not the decode thread. Read access on the
+  // decode thread does not need to be protected.
+  // Note: Elems accessed via operator[] are not protected with monitor
+  // assertion checks once obtained.
+  class MonitoredSubReaderList
+  {
+  public:
+    // Main constructor takes a pointer to the owning |nsDASHReader| to verify
+    // correct entry into the decoder's |ReentrantMonitor|.
+    MonitoredSubReaderList(nsDASHReader* aReader) :
+      mReader(aReader)
+    {
+      MOZ_COUNT_CTOR(nsDASHReader::MonitoredSubReaderList);
+      NS_ASSERTION(mReader, "Reader is null!");
+    }
+    // Note: Elements in |mSubReaderList| will have their refcounts decremented
+    // in this destructor.
+    ~MonitoredSubReaderList()
+    {
+      MOZ_COUNT_DTOR(nsDASHReader::MonitoredSubReaderList);
+    }
+
+    // Returns Length of |mSubReaderList| array. Will assert threads other than
+    // the decode thread are "in monitor".
+    uint32_t Length() const
+    {
+      NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
+      if (!mReader->GetDecoder()->OnDecodeThread()) {
+        mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
+      }
+      return mSubReaderList.Length();
+    }
+
+    // Override '[]' to assert threads other than the decode thread are "in
+    // monitor" for accessing individual elems. Note: elems returned do not
+    // have monitor assertions builtin like |MonitoredSubReader| objects.
+    nsRefPtr<nsBuiltinDecoderReader>& operator[](uint32_t i)
+    {
+      NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
+      if (!mReader->GetDecoder()->OnDecodeThread()) {
+        mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
+      }
+      return mSubReaderList[i];
+    }
+
+    // Appends a reader to the end of |mSubReaderList|. Will always assert that
+    // the thread is "in monitor".
+    void
+    AppendElement(nsBuiltinDecoderReader* aReader)
+    {
+      NS_ASSERTION(mReader->GetDecoder(), "Decoder is null!");
+      mReader->GetDecoder()->GetReentrantMonitor().AssertCurrentThreadIn();
+      mSubReaderList.AppendElement(aReader);
+    }
+  private:
+    // Pointer to |nsDASHReader| object which owns this |MonitoredSubReader|.
+    nsDASHReader* mReader;
+    // Ref ptrs to the sub readers.
+    nsTArray<nsRefPtr<nsBuiltinDecoderReader> > mSubReaderList;
+  };
+
+  // Ref ptrs to all sub-readers of individual media |Representation|s.
+  // Decoder monitor must be entered for write access on all threads and read
+  // access on all threads that are not the decode thread. Read acces on the
+  // decode thread does not need to be protected.
+  MonitoredSubReaderList mAudioReaders;
+  MonitoredSubReaderList mVideoReaders;
+};
+
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHRepDecoder.cpp
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * see nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
+
+#include "prlog.h"
+#include "VideoUtils.h"
+#include "SegmentBase.h"
+#include "nsBuiltinDecoderStateMachine.h"
+#include "nsDASHReader.h"
+#include "MediaResource.h"
+#include "nsDASHRepDecoder.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gBuiltinDecoderLog;
+#define LOG(msg, ...) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                             ("%p [nsDASHRepDecoder] " msg, this, __VA_ARGS__))
+#define LOG1(msg) PR_LOG(gBuiltinDecoderLog, PR_LOG_DEBUG, \
+                         ("%p [nsDASHRepDecoder] " msg, this))
+#else
+#define LOG(msg, ...)
+#define LOG1(msg)
+#endif
+
+nsDecoderStateMachine*
+nsDASHRepDecoder::CreateStateMachine()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  // Do not create; just return current state machine.
+  return mDecoderStateMachine;
+}
+
+nsresult
+nsDASHRepDecoder::SetStateMachine(nsDecoderStateMachine* aSM)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  mDecoderStateMachine = aSM;
+  return NS_OK;
+}
+
+void
+nsDASHRepDecoder::SetResource(MediaResource* aResource)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  mResource = aResource;
+}
+
+void
+nsDASHRepDecoder::SetMPDRepresentation(Representation const * aRep)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  mMPDRepresentation = aRep;
+}
+
+void
+nsDASHRepDecoder::SetReader(nsWebMReader* aReader)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  mReader = aReader;
+}
+
+nsresult
+nsDASHRepDecoder::Load(MediaResource* aResource,
+                       nsIStreamListener** aListener,
+                       nsMediaDecoder* aCloneDonor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  NS_ENSURE_TRUE(mMPDRepresentation, NS_ERROR_NOT_INITIALIZED);
+
+  // Get init range and index range from MPD.
+  SegmentBase const * segmentBase = mMPDRepresentation->GetSegmentBase();
+  NS_ENSURE_TRUE(segmentBase, NS_ERROR_NULL_POINTER);
+
+  // Get and set init range.
+  segmentBase->GetInitRange(&mInitByteRange.mStart, &mInitByteRange.mEnd);
+  NS_ENSURE_TRUE(!mInitByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
+  mReader->SetInitByteRange(mInitByteRange);
+
+  // Get and set index range.
+  segmentBase->GetIndexRange(&mIndexByteRange.mStart, &mIndexByteRange.mEnd);
+  NS_ENSURE_TRUE(!mIndexByteRange.IsNull(), NS_ERROR_NOT_INITIALIZED);
+  mReader->SetIndexByteRange(mIndexByteRange);
+
+  // Determine byte range to Open.
+  // For small deltas between init and index ranges, we need to bundle the byte
+  // range requests together in order to deal with |nsMediaCache|'s control of
+  // seeking (see |nsMediaCache|::|Update|). |nsMediaCache| will not initiate a
+  // |ChannelMediaResource|::|CacheClientSeek| for the INDEX byte range if the
+  // delta between it and the INIT byte ranges is less than
+  // |SEEK_VS_READ_THRESHOLD|. To get around this, request all metadata bytes
+  // now so |nsMediaCache| can assume the bytes are en route.
+  int64_t delta = NS_MAX(mIndexByteRange.mStart, mInitByteRange.mStart)
+                - NS_MIN(mIndexByteRange.mEnd, mInitByteRange.mEnd);
+  MediaByteRange byteRange;
+  if (delta <= SEEK_VS_READ_THRESHOLD) {
+    byteRange.mStart = NS_MIN(mIndexByteRange.mStart, mInitByteRange.mStart);
+    byteRange.mEnd = NS_MAX(mIndexByteRange.mEnd, mInitByteRange.mEnd);
+    // Loading everything in one chunk .
+    mMetadataChunkCount = 1;
+  } else {
+    byteRange = mInitByteRange;
+    // Loading in two chunks: init and index.
+    mMetadataChunkCount = 2;
+  }
+  mCurrentByteRange = byteRange;
+  return mResource->OpenByteRange(nullptr, byteRange);
+}
+
+void
+nsDASHRepDecoder::NotifyDownloadEnded(nsresult aStatus)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (!mMainDecoder) {
+    LOG("Error! Main Decoder is reported as null: mMainDecoder [%p]",
+        mMainDecoder.get());
+    DecodeError();
+    return;
+  }
+
+  if (NS_SUCCEEDED(aStatus)) {
+    // Decrement counter as metadata chunks are downloaded.
+    // Note: Reader gets next chunk download via |ChannelMediaResource|:|Seek|.
+    if (mMetadataChunkCount > 0) {
+      LOG("Metadata chunk [%d] downloaded: range requested [%d - %d]",
+          mMetadataChunkCount,
+          mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
+      mMetadataChunkCount--;
+    } else {
+      // Notify main decoder that a DATA byte range is downloaded.
+      LOG("Byte range downloaded: status [%x] range requested [%d - %d]",
+          aStatus, mCurrentByteRange.mStart, mCurrentByteRange.mEnd);
+      mMainDecoder->NotifyDownloadEnded(this, aStatus,
+                                        mCurrentByteRange);
+    }
+  } else if (aStatus == NS_BINDING_ABORTED) {
+    LOG("MPD download has been cancelled by the user: aStatus [%x].", aStatus);
+    if (mMainDecoder) {
+      mMainDecoder->LoadAborted();
+    }
+    return;
+  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
+    LOG("Network error trying to download MPD: aStatus [%x].", aStatus);
+    NetworkError();
+  }
+}
+
+void
+nsDASHRepDecoder::OnReadMetadataCompleted()
+{
+  NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
+
+  LOG1("Metadata has been read.");
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &nsDASHRepDecoder::LoadNextByteRange);
+  nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    LOG("Error dispatching parse event to main thread: rv[%x]", rv);
+    DecodeError();
+    return;
+  }
+}
+
+void
+nsDASHRepDecoder::LoadNextByteRange()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (!mResource) {
+    LOG1("Error: resource is reported as null!");
+    DecodeError();
+    return;
+  }
+
+  // Populate the array of subsegment byte ranges if it's empty.
+  nsresult rv;
+  if (mByteRanges.IsEmpty()) {
+    if (!mReader) {
+      LOG1("Error: mReader should not be null!");
+      DecodeError();
+      return;
+    }
+    rv = mReader->GetIndexByteRanges(mByteRanges);
+    // If empty, just fail.
+    if (NS_FAILED(rv) || mByteRanges.IsEmpty()) {
+      LOG1("Error getting list of subsegment byte ranges.");
+      DecodeError();
+      return;
+    }
+  }
+
+  // Get byte range for subsegment.
+  if (mSubsegmentIdx < mByteRanges.Length()) {
+    mCurrentByteRange = mByteRanges[mSubsegmentIdx];
+  } else {
+    mCurrentByteRange.Clear();
+    LOG("End of subsegments: index [%d] out of range.", mSubsegmentIdx);
+    return;
+  }
+
+  // Open byte range corresponding to subsegment.
+  rv = mResource->OpenByteRange(nullptr, mCurrentByteRange);
+  if (NS_FAILED(rv)) {
+    LOG("Error opening byte range [%d - %d]: rv [%x].",
+        mCurrentByteRange.mStart, mCurrentByteRange.mEnd, rv);
+    NetworkError();
+    return;
+  }
+  // Increment subsegment index for next load.
+  mSubsegmentIdx++;
+}
+
+nsresult
+nsDASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
+                                      MediaByteRange& aByteRange)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  // Check data ranges, if available.
+  for (int i = 0; i < mByteRanges.Length(); i++) {
+    NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
+    if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
+      mCurrentByteRange = aByteRange = mByteRanges[i];
+      mSubsegmentIdx = i;
+      return NS_OK;
+    }
+  }
+  // Check metadata ranges; init range.
+  if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
+    mCurrentByteRange = aByteRange = mInitByteRange;
+    mSubsegmentIdx = 0;
+    return NS_OK;
+  }
+  // ... index range.
+  if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
+    mCurrentByteRange = aByteRange = mIndexByteRange;
+    mSubsegmentIdx = 0;
+    return NS_OK;
+  }
+
+  aByteRange.Clear();
+  if (mByteRanges.IsEmpty()) {
+    // Assume mByteRanges will be populated after metadata is read.
+    LOG("Can't get range for offset [%d].", aOffset);
+    return NS_ERROR_NOT_AVAILABLE;
+  } else {
+    // Cannot seek to an unknown offset.
+    // XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
+    LOG("Error! Offset [%d] is in an unknown range!", aOffset);
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+}
+
+void
+nsDASHRepDecoder::NetworkError()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->NetworkError(); }
+}
+
+void
+nsDASHRepDecoder::SetDuration(double aDuration)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->SetDuration(aDuration); }
+}
+
+void
+nsDASHRepDecoder::SetInfinite(bool aInfinite)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->SetInfinite(aInfinite); }
+}
+
+void
+nsDASHRepDecoder::SetSeekable(bool aSeekable)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->SetSeekable(aSeekable); }
+}
+
+void
+nsDASHRepDecoder::Progress(bool aTimer)
+{
+  if (mMainDecoder) { mMainDecoder->Progress(aTimer); }
+}
+
+void
+nsDASHRepDecoder::NotifyDataArrived(const char* aBuffer,
+                                    uint32_t aLength,
+                                    int64_t aOffset)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  LOG("Data bytes [%d - %d] arrived via buffer [%p].",
+      aOffset, aOffset+aLength, aBuffer);
+  // Notify reader directly, since call to |nsBuiltinDecoderStateMachine|::
+  // |NotifyDataArrived| will go to |nsDASHReader|::|NotifyDataArrived|, which
+  // has no way to forward the notification to the correct sub-reader.
+  if (mReader) {
+    mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
+  }
+  // Forward to main decoder which will notify state machine.
+  if (mMainDecoder) {
+    mMainDecoder->NotifyDataArrived(aBuffer, aLength, aOffset);
+  }
+}
+
+void
+nsDASHRepDecoder::NotifyBytesDownloaded()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->NotifyBytesDownloaded(); }
+}
+
+void
+nsDASHRepDecoder::NotifySuspendedStatusChanged()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  if (mMainDecoder) { mMainDecoder->NotifySuspendedStatusChanged(); }
+}
+
+bool
+nsDASHRepDecoder::OnStateMachineThread() const
+{
+  return (mMainDecoder ? mMainDecoder->OnStateMachineThread() : false);
+}
+
+bool
+nsDASHRepDecoder::OnDecodeThread() const
+{
+  return (mMainDecoder ? mMainDecoder->OnDecodeThread() : false);
+}
+
+ReentrantMonitor&
+nsDASHRepDecoder::GetReentrantMonitor()
+{
+  return mMainDecoder->GetReentrantMonitor();
+}
+
+nsDecoderStateMachine::State
+nsDASHRepDecoder::GetDecodeState()
+{
+  // XXX SHUTDOWN might not be an appropriate error.
+  return (mMainDecoder ? mMainDecoder->GetDecodeState()
+                       : nsDecoderStateMachine::DECODER_STATE_SHUTDOWN);
+}
+
+mozilla::layers::ImageContainer*
+nsDASHRepDecoder::GetImageContainer()
+{
+  NS_ASSERTION(mMainDecoder && mMainDecoder->OnDecodeThread(),
+               "Should be on decode thread.");
+  return (mMainDecoder ? mMainDecoder->GetImageContainer() : nullptr);
+}
+
+void
+nsDASHRepDecoder::DecodeError()
+{
+  if (NS_IsMainThread()) {
+    nsBuiltinDecoder::DecodeError();
+  } else {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &nsBuiltinDecoder::DecodeError);
+    nsresult rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      LOG("Error dispatching DecodeError event to main thread: rv[%x]", rv);
+    }
+  }
+}
+
+void
+nsDASHRepDecoder::ReleaseStateMachine()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  // Since state machine owns mReader, remove reference to it.
+  mReader = nullptr;
+
+  nsBuiltinDecoder::ReleaseStateMachine();
+}
new file mode 100644
--- /dev/null
+++ b/content/media/dash/nsDASHRepDecoder.h
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+
+/* This Source Code Form Is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* DASH - Dynamic Adaptive Streaming over HTTP
+ *
+ * DASH is an adaptive bitrate streaming technology where a multimedia file is
+ * partitioned into one or more segments and delivered to a client using HTTP.
+ *
+ * see nsDASHDecoder.cpp for info on DASH interaction with the media engine.*/
+
+#if !defined(nsDASHRepDecoder_h_)
+#define nsDASHRepDecoder_h_
+
+#include "Representation.h"
+#include "ImageLayers.h"
+#include "nsDASHDecoder.h"
+#include "nsWebMDecoder.h"
+#include "nsWebMReader.h"
+#include "nsBuiltinDecoder.h"
+
+class nsDASHDecoder;
+
+class nsDASHRepDecoder : public nsBuiltinDecoder
+{
+public:
+  typedef mozilla::net::Representation Representation;
+  typedef mozilla::net::SegmentBase SegmentBase;
+  typedef mozilla::layers::ImageContainer ImageContainer;
+
+  // Constructor takes a ptr to the main decoder.
+  nsDASHRepDecoder(nsDASHDecoder* aMainDecoder) :
+    mMainDecoder(aMainDecoder),
+    mMPDRepresentation(nullptr),
+    mMetadataChunkCount(0),
+    mCurrentByteRange(),
+    mSubsegmentIdx(0),
+    mReader(nullptr)
+  {
+    MOZ_COUNT_CTOR(nsDASHRepDecoder);
+  }
+
+  ~nsDASHRepDecoder()
+  {
+    MOZ_COUNT_DTOR(nsDASHRepDecoder);
+  }
+
+  // Clone not supported; just return nullptr.
+  virtual nsMediaDecoder* Clone() { return nullptr; }
+
+  // Called by the main decoder at creation time; points to the main state
+  // machine managed by the main decoder. Called on the main thread only.
+  nsresult SetStateMachine(nsDecoderStateMachine* aSM);
+
+private:
+  // Overridden to return the ptr set by SetStateMachine. Called on the main
+  // thread only.
+  nsDecoderStateMachine* CreateStateMachine();
+
+public:
+  // Called by nsDASHDecoder at creation time; points to the media resource
+  // for this decoder's |Representation|. Called on the main thread only.
+  void SetResource(MediaResource* aResource);
+
+  // Sets the |Representation| object for this decoder. Called on the main
+  // thread.
+  void SetMPDRepresentation(Representation const * aRep);
+
+  // Called from nsDASHDecoder on main thread; Starts media stream download.
+  nsresult Load(MediaResource* aResource = nullptr,
+                nsIStreamListener** aListener = nullptr,
+                nsMediaDecoder* aCloneDonor = nullptr);
+
+  // Loads the next byte range (or first one on first call). Called on the main
+  // thread only.
+  void LoadNextByteRange();
+
+  // Calls from nsDASHRepDecoder. Called on the main thread only.
+  void SetReader(nsWebMReader* aReader);
+
+  // Called if the media file encounters a network error. Call on the main
+  // thread only.
+  void NetworkError();
+
+  // Set the duration of the media resource in units of seconds.
+  // This is called via a channel listener if it can pick up the duration
+  // from a content header. Must be called from the main thread only.
+  virtual void SetDuration(double aDuration);
+
+  // Set media stream as infinite. Called on the main thread only.
+  void SetInfinite(bool aInfinite);
+
+  // Sets media stream as seekable. Called on main thread only.
+  void SetSeekable(bool aSeekable);
+
+  // Fire progress events if needed according to the time and byte
+  // constraints outlined in the specification. aTimer is true
+  // if the method is called as a result of the progress timer rather
+  // than the result of downloaded data.
+  void Progress(bool aTimer);
+
+  // Called as data arrives on the stream and is read into the cache.  Called
+  // on the main thread only.
+  void NotifyDataArrived(const char* aBuffer,
+                         uint32_t aLength,
+                         int64_t aOffset);
+
+  // Called by MediaResource when some data has been received.
+  // Call on the main thread only.
+  void NotifyBytesDownloaded();
+
+  // Notify that a byte range request has been completed by the media resource.
+  // Called on the main thread only.
+  void NotifyDownloadEnded(nsresult aStatus);
+
+  // Called by MediaResource when the "cache suspended" status changes.
+  // If MediaResource::IsSuspendedByCache returns true, then the decoder
+  // should stop buffering or otherwise waiting for download progress and
+  // start consuming data, if possible, because the cache is full.
+  void NotifySuspendedStatusChanged();
+
+  // Gets a byte range containing the byte offset. Call on main thread only.
+  nsresult GetByteRangeForSeek(int64_t const aOffset,
+                               MediaByteRange& aByteRange);
+
+  // Returns true if the current thread is the state machine thread.
+  bool OnStateMachineThread() const;
+
+  // Returns true if the current thread is the decode thread.
+  bool OnDecodeThread() const;
+
+  // Returns main decoder's monitor for synchronised access.
+  ReentrantMonitor& GetReentrantMonitor();
+
+  // Return the current decode state, according to the main decoder. The
+  // decoder monitor must be obtained before calling this.
+  nsDecoderStateMachine::State GetDecodeState();
+
+  // Called on the decode thread from nsWebMReader.
+  ImageContainer* GetImageContainer();
+
+  // Called when Metadata has been read; notifies that index data is read.
+  // Called on the decode thread only.
+  void OnReadMetadataCompleted();
+
+  // Overridden to cleanup ref to |nsDASHDecoder|. Called on main thread only.
+  void Shutdown() {
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+    // Call parent class shutdown.
+    nsBuiltinDecoder::Shutdown();
+    NS_ENSURE_TRUE(mShuttingDown, );
+    // Cleanup ref to main decoder.
+    mMainDecoder = nullptr;
+  }
+
+  // Drop reference to state machine and mReader (owned by state machine).
+  // Only called during shutdown dance.
+  void ReleaseStateMachine();
+
+  // Notifies the element that decoding has failed.
+  void DecodeError();
+
+private:
+  // The main decoder.
+  nsRefPtr<nsDASHDecoder> mMainDecoder;
+  // This decoder's MPD |Representation| object.
+  Representation const * mMPDRepresentation;
+
+  // Countdown var for loading metadata byte ranges.
+  uint16_t        mMetadataChunkCount;
+
+  // All the byte ranges for this |Representation|.
+  nsTArray<MediaByteRange> mByteRanges;
+
+  // Byte range for the init and index bytes.
+  MediaByteRange  mInitByteRange;
+  MediaByteRange  mIndexByteRange;
+
+  // The current byte range being requested.
+  MediaByteRange  mCurrentByteRange;
+  // Index of the current byte range.
+  uint64_t        mSubsegmentIdx;
+
+  // Ptr to the reader object for this |Representation|. Owned by state
+  // machine.
+  nsBuiltinDecoderReader*   mReader;
+};
+
+#endif //nsDASHRepDecoder_h_
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -706,17 +706,17 @@ public:
   // Drop reference to state machine.  Only called during shutdown dance.
   virtual void ReleaseStateMachine() { mDecoderStateMachine = nullptr; }
 
    // Called when a "MozAudioAvailable" event listener is added to the media
    // element. Called on the main thread.
    virtual void NotifyAudioAvailableListener();
 
   // Notifies the element that decoding has failed.
-  void DecodeError();
+  virtual void DecodeError();
 
   // Schedules the state machine to run one cycle on the shared state
   // machine thread. Main thread only.
   nsresult ScheduleStateMachineThread();
 
   /******
    * The following members should be accessed with the decoder lock held.
    ******/
--- a/content/media/nsBuiltinDecoderReader.cpp
+++ b/content/media/nsBuiltinDecoderReader.cpp
@@ -371,34 +371,16 @@ VideoData* nsBuiltinDecoderReader::FindS
   int64_t startTime = NS_MIN(videoStartTime, audioStartTime);
   if (startTime != INT64_MAX) {
     aOutStartTime = startTime;
   }
 
   return videoData;
 }
 
-/*template<class Data>
-Data* nsBuiltinDecoderReader::DecodeToFirstData(DecodeFn aDecodeFn,
-                                                MediaQueue<Data>& aQueue)
-{
-  bool eof = false;
-  while (!eof && aQueue.GetSize() == 0) {
-    {
-      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
-      if (mDecoder->GetDecodeState() == nsDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
-        return nullptr;
-      }
-    }
-    eof = !(this->*aDecodeFn)();
-  }
-  Data* d = nullptr;
-  return (d = aQueue.PeekFront()) ? d : nullptr;
-}*/
-
 nsresult nsBuiltinDecoderReader::DecodeToTarget(int64_t aTarget)
 {
   // Decode forward to the target frame. Start with video, if we have it.
   if (HasVideo()) {
     bool eof = false;
     int64_t startTime = -1;
     nsAutoPtr<VideoData> video;
     while (HasVideo() && !eof) {
--- a/content/media/nsBuiltinDecoderReader.h
+++ b/content/media/nsBuiltinDecoderReader.h
@@ -525,17 +525,17 @@ public:
 
   // Sets range for initialization bytes; used by DASH.
   virtual void SetInitByteRange(MediaByteRange &aByteRange) { }
 
   // Sets range for index frame bytes; used by DASH.
   virtual void SetIndexByteRange(MediaByteRange &aByteRange) { }
 
   // Returns list of ranges for index frame start/end offsets. Used by DASH.
-  nsresult GetIndexByteRanges(nsTArray<MediaByteRange>& aByteRanges) {
+  virtual nsresult GetIndexByteRanges(nsTArray<MediaByteRange>& aByteRanges) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
 protected:
   // Pumps the decode until we reach frames required to play at time aTarget
   // (usecs).
   nsresult DecodeToTarget(int64_t aTarget);
 
--- a/content/media/webm/Makefile.in
+++ b/content/media/webm/Makefile.in
@@ -27,8 +27,14 @@ CPPSRCS		= \
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES = \
 		$(MOZ_LIBVPX_CFLAGS) \
 		$(NULL)
+
+ifdef MOZ_DASH
+LOCAL_INCLUDES += \
+  -I$(srcdir)/../dash \
+  $(NULL)
+endif
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -186,16 +186,22 @@ SHARED_LIBRARY_LIBS 	+= \
 endif
 
 ifdef MOZ_MEDIA_PLUGINS
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/plugins/$(LIB_PREFIX)gkconmediaplugins_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
+ifdef MOZ_DASH
+SHARED_LIBRARY_LIBS += \
+  $(DEPTH)/content/media/dash/$(LIB_PREFIX)gkcondash_s.$(LIB_SUFFIX) \
+  $(NULL)
+endif
+
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 INCLUDES	+= \
 		-I$(srcdir)/../../base/src \
 		-I$(srcdir)/../../html/content/src \
 		-I$(ANDROID_SOURCE)/dalvik/libnativehelper/include/nativehelper \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/binder/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/utils/ \
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -160,16 +160,19 @@ pref("media.ogg.enabled", true);
 pref("media.opus.enabled", true);
 #endif
 #ifdef MOZ_WAVE
 pref("media.wave.enabled", true);
 #endif
 #ifdef MOZ_WEBM
 pref("media.webm.enabled", true);
 #endif
+#ifdef MOZ_DASH
+pref("media.dash.enabled", true);
+#endif
 #ifdef MOZ_GSTREAMER
 pref("media.h264.enabled", true);
 #endif
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", false);
 #else
 #ifdef ANDROID
 pref("media.navigator.enabled", true);
--- a/netwerk/dash/Makefile.in
+++ b/netwerk/dash/Makefile.in
@@ -3,22 +3,22 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #
 # Contributor(s):
 #     Steve Workman <sworkman@mozilla.com
 
-DEPTH     = ../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
+DEPTH     := @DEPTH@
+topsrcdir := @top_srcdir@
+srcdir    := @srcdir@
+VPATH     := @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-PARALLEL_DIRS    = \
+PARALLEL_DIRS := \
   mpd \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DIMPL_NS_NET
--- a/netwerk/dash/mpd/Makefile.in
+++ b/netwerk/dash/mpd/Makefile.in
@@ -1,55 +1,44 @@
 # -*- Mode: makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- #
 # vim: set ts=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/.
 #
 # Contributor(s):
-#     Steve Workman <sworkman@mozilla.com
+#     Steve Workman <sworkman@mozilla.com>
 
-DEPTH     = ../../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
+DEPTH     := @DEPTH@
+topsrcdir := @top_srcdir@
+srcdir    := @srcdir@
+VPATH     := @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE         = necko
-LIBRARY_NAME   = nkdashmpd_s
-LIBXUL_LIBRARY = 1
-XPIDL_MODULE   = necko_dashmpd
-GRE_MODULE     = 1
-FORCE_STATIC_LIB = 1
+MODULE         := necko
+LIBRARY_NAME   := nkdashmpd_s
+LIBXUL_LIBRARY := 1
+FORCE_STATIC_LIB := 1
 
-CPPSRCS = \
+CPPSRCS := \
   nsDASHMPDParser.cpp \
   IMPDManager.cpp \
   nsDASHWebMODManager.cpp \
   nsDASHWebMODParser.cpp \
   MPD.cpp \
   Period.cpp \
   AdaptationSet.cpp \
   Representation.cpp \
   SegmentBase.cpp \
   $(NULL)
 
-EXPORTS = \
-  IMPDManager.h \
-  nsDASHMPDParser.h \
-  $(NULL)
 
-LOCAL_INCLUDES = \
-  -I$(srcdir)/../manager/ \
+LOCAL_INCLUDES := \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/html/content/public \
   -I$(topsrcdir)/content/html/content/src \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
-include $(topsrcdir)/ipc/chromium/chromium-config.mk
-include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DIMPL_NS_NET
-
-
--- a/netwerk/dash/mpd/nsDASHMPDParser.cpp
+++ b/netwerk/dash/mpd/nsDASHMPDParser.cpp
@@ -33,19 +33,22 @@
 #include "nsIDOMNode.h"
 #include "nsString.h"
 #include "IMPDManager.h"
 #include "nsDASHMPDParser.h"
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo* gDASHMPDParserLog = nullptr;
 #define LOG(msg, ...) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
-                             ("%p [nsDASHMPDParser] " msg, this, ##__VA_ARGS__))
+                             ("%p [nsDASHMPDParser] " msg, this, __VA_ARGS__))
+#define LOG1(msg) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
+                         ("%p [nsDASHMPDParser] " msg, this))
 #else
 #define LOG(msg, ...)
+#define LOG1(msg)
 #endif
 
 namespace mozilla {
 namespace net {
 
 nsDASHMPDParser::nsDASHMPDParser(char*         aMPDData,
                                  uint32_t      aDataLength,
                                  nsIPrincipal* aPrincipal,
@@ -78,17 +81,17 @@ nsDASHMPDParser::Parse(IMPDManager**    
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
 
 #ifdef PR_LOGGING
   {
     nsAutoCString spec;
     nsresult rv = mURI->GetSpec(spec);
     if (NS_FAILED(rv)) {
-      LOG("Preparing to parse MPD: cannot get spec from URI");
+      LOG1("Preparing to parse MPD: cannot get spec from URI");
     } else {
       LOG("Preparing to parse MPD: mURI:\"%s\"", spec.get());
     }
   }
 #endif
 
   // Get mDoc element from mData buffer using DOMParser.
   nsCOMPtr<nsIDOMParser> DOMParser;
@@ -98,17 +101,17 @@ nsDASHMPDParser::Parse(IMPDManager**    
 
   nsCOMPtr<nsIDOMDocument> doc;
   rv = DOMParser->ParseFromBuffer(reinterpret_cast<uint8_t const *>(mData.get()),
                                   mDataLength,
                                   "application/xml",
                                   getter_AddRefs(doc));
   NS_ENSURE_SUCCESS(rv, rv);
   if(!doc) {
-    LOG("ERROR! Document not parsed as XML!");
+    LOG1("ERROR! Document not parsed as XML!");
     return NS_ERROR_NO_INTERFACE;
   }
   // Use root node to create MPD manager.
   nsCOMPtr<nsIDOMElement> root;
   rv = doc->GetDocumentElement(getter_AddRefs(root));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
 #ifdef PR_LOGGING
--- a/netwerk/dash/mpd/nsDASHWebMODManager.cpp
+++ b/netwerk/dash/mpd/nsDASHWebMODManager.cpp
@@ -41,19 +41,22 @@
 
 namespace mozilla {
 namespace net {
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo* gnsDASHWebMODManagerLog = nullptr;
 #define LOG(msg, ...) \
         PR_LOG(gnsDASHWebMODManagerLog, PR_LOG_DEBUG, \
-               ("%p [nsDASHWebMODManager] " msg, this, ##__VA_ARGS__))
+               ("%p [nsDASHWebMODManager] " msg, this, __VA_ARGS__))
+#define LOG1(msg) PR_LOG(gDASHMPDParserLog, PR_LOG_DEBUG, \
+                         ("%p [nsDASHWebMODManager] " msg, this))
 #else
 #define LOG(msg, ...)
+#define LOG1(msg)
 #endif
 
 nsDASHWebMODManager::nsDASHWebMODManager(MPD* aMpd)
 {
   MOZ_COUNT_CTOR(nsDASHWebMODManager);
   NS_ENSURE_TRUE(aMpd,);
   mMpd = aMpd;
 #if defined(PR_LOGGING)
--- a/netwerk/dash/mpd/nsDASHWebMODParser.cpp
+++ b/netwerk/dash/mpd/nsDASHWebMODParser.cpp
@@ -38,44 +38,48 @@
 #include "nsIDOMElement.h"
 #include "prlog.h"
 #include "nsDASHWebMODParser.h"
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo* gnsDASHWebMODParserLog = nullptr;
 #define LOG(msg, ...) \
         PR_LOG(gnsDASHWebMODParserLog, PR_LOG_DEBUG, \
-               ("%p [nsDASHWebMODParser] " msg, this, ##__VA_ARGS__))
+               ("%p [nsDASHWebMODParser] " msg, this, __VA_ARGS__))
+#define LOG1(msg) \
+        PR_LOG(gnsDASHWebMODParserLog, PR_LOG_DEBUG, \
+               ("%p [nsDASHWebMODParser] " msg, this))
 #else
 #define LOG(msg, ...)
+#define LOG1(msg)
 #endif
 
 namespace mozilla {
 namespace net {
 
 nsDASHWebMODParser::nsDASHWebMODParser(nsIDOMElement* aRoot) :
   mRoot(aRoot)
 {
   MOZ_COUNT_CTOR(nsDASHWebMODParser);
 #if defined(PR_LOGGING)
   if(!gnsDASHWebMODParserLog)
     gnsDASHWebMODParserLog = PR_NewLogModule("nsDASHWebMODParser");
 #endif
-  LOG("Created nsDASHWebMODParser");
+  LOG1("Created nsDASHWebMODParser");
 }
 
 nsDASHWebMODParser::~nsDASHWebMODParser()
 {
   MOZ_COUNT_DTOR(nsDASHWebMODParser);
 }
 
 MPD*
 nsDASHWebMODParser::Parse()
 {
-  LOG("Parsing DOM into MPD objects");
+  LOG1("Parsing DOM into MPD objects");
   nsAutoPtr<MPD> mpd(new MPD());
 
   nsresult rv = VerifyMPDAttributes();
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = SetMPDBaseUrls(mpd);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
@@ -214,17 +218,17 @@ nsDASHWebMODParser::SetPeriods(MPD* aMpd
       }
 
       bool bIgnoreThisPeriod;
       rv = SetAdaptationSets(child, period, bIgnoreThisPeriod);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // |Period| should be ignored if its child elems are invalid
       if (bIgnoreThisPeriod) {
-        LOG("Ignoring period");
+        LOG1("Ignoring period");
       } else {
         aMpd->AddPeriod(period.forget());
         LOG("Period #%d: added to MPD", i++);
       }
     }
     rv = child->GetNextElementSibling(getter_AddRefs(nextChild));
     NS_ENSURE_SUCCESS(rv, rv);
     child = nextChild;
@@ -245,43 +249,43 @@ nsDASHWebMODParser::ValidateAdaptationSe
 
   // Now check for video and audio specific attributes.
   nsAutoString mimeType;
   if (bAttributesValid) {
     rv = GetAttribute(aChild, NS_LITERAL_STRING("mimeType"), mimeType);
     NS_ENSURE_SUCCESS(rv, rv);
     bAttributesValid = !mimeType.IsEmpty();
     if (!bAttributesValid)
-      LOG("mimeType not present!");
+      LOG1("mimeType not present!");
   }
   // Validate attributes for video.
   if (bAttributesValid && mimeType.EqualsLiteral(VIDEO_WEBM)) {
     // @|segmentAlignment| is optional
     if (bAttributesValid) {
       rv = GetAttribute(aChild, NS_LITERAL_STRING("segmentAlignment"), value);
       NS_ENSURE_SUCCESS(rv, rv);
       bAttributesValid = (value.IsEmpty() || value.EqualsLiteral("true"));
       if (!bAttributesValid)
-        LOG("segmentAlignment not present or invalid!");
+        LOG1("segmentAlignment not present or invalid!");
     }
     if (bAttributesValid) {
       rv = GetAttribute(aChild, NS_LITERAL_STRING("subsegmentAlignment"),
                         value);
       NS_ENSURE_SUCCESS(rv, rv);
       bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
       if (!bAttributesValid)
-        LOG("subsegmentAlignment not present or invalid!");
+        LOG1("subsegmentAlignment not present or invalid!");
     }
     if (bAttributesValid) {
       rv = GetAttribute(aChild, NS_LITERAL_STRING("bitstreamSwitching"),
                         value);
       NS_ENSURE_SUCCESS(rv, rv);
       bAttributesValid = (!value.IsEmpty() && value.EqualsLiteral("true"));
       if (!bAttributesValid)
-        LOG("bitstreamSwitching not present or invalid!");
+        LOG1("bitstreamSwitching not present or invalid!");
     }
   } else if (bAttributesValid && mimeType.EqualsLiteral(AUDIO_WEBM)) {
   // Validate attributes for audio.
   } else if (bAttributesValid) {
     // attributes are not valid since mimeType is wrong
     bAttributesValid = false;
     LOG("mimeType is invalid: %s", NS_ConvertUTF16toUTF8(mimeType).get());
   }
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -131,16 +131,19 @@
 #define TEXT_CACHE_MANIFEST                 "text/cache-manifest"
 
 #define VIDEO_MPEG                          "video/mpeg"
 #define VIDEO_MP4                           "video/mp4"
 #define VIDEO_RAW                           "video/x-raw-yuv"
 #define VIDEO_OGG                           "video/ogg"
 #define VIDEO_WEBM                          "video/webm"
 #define APPLICATION_OGG                     "application/ogg"
+#ifdef MOZ_DASH
+#define APPLICATION_DASH                    "application/dash+xml"
+#endif
 
 /* x-uuencode-apple-single. QuickMail made me do this. */
 #define UUENCODE_APPLE_SINGLE               "x-uuencode-apple-single"
 
 /* The standard MIME message-content-encoding values:
  */
 #define ENCODING_7BIT                       "7bit"
 #define ENCODING_8BIT                       "8bit"
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -397,16 +397,19 @@ static nsDefaultMimeTypeEntry defaultMim
 #ifdef MOZ_OPUS
   { AUDIO_OGG, "opus" },
 #endif
 #endif
 #ifdef MOZ_WEBM
   { VIDEO_WEBM, "webm" },
   { AUDIO_WEBM, "webm" },
 #endif
+#ifdef MOZ_DASH
+  { APPLICATION_DASH, "mpd" },
+#endif
 #ifdef MOZ_GSTREAMER
   { VIDEO_MP4, "mp4" },
 #endif
 #ifdef MOZ_RAW
   { VIDEO_RAW, "yuv" }
 #endif
 };
 
@@ -471,16 +474,19 @@ static nsExtraMimeTypeEntry extraMimeEnt
   { TEXT_CSS, "css", "Style Sheet" },
   { VIDEO_OGG, "ogv", "Ogg Video" },
   { VIDEO_OGG, "ogg", "Ogg Video" },
   { APPLICATION_OGG, "ogg", "Ogg Video"},
   { AUDIO_OGG, "oga", "Ogg Audio" },
   { AUDIO_OGG, "opus", "Opus Audio" },
   { VIDEO_WEBM, "webm", "Web Media Video" },
   { AUDIO_WEBM, "webm", "Web Media Audio" },
+#ifdef MOZ_DASH
+  { APPLICATION_DASH, "mpd", "DASH Media Presentation Description" },
+#endif
 #ifdef MOZ_MEDIA_PLUGINS
   { AUDIO_MP3, "mp3", "MPEG Audio" },
 #endif
   { VIDEO_MP4, "mp4", "MPEG-4 Video" },
   { VIDEO_RAW, "yuv", "Raw YUV Video" },
   { AUDIO_WAV, "wav", "Waveform Audio" }
 };