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 108667 d882662a0bb6a753b34f9133c951c08def1a8851
parent 108666 6df63191884b8631984d92bef4b937ab084042cd
child 108668 a7c6371727090378860d6401de60c96a3aff1579
push id23577
push usermlamouri@mozilla.com
push dateSun, 30 Sep 2012 12:19:29 +0000
treeherdermozilla-central@0df33af01cee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, ted
bugs734546
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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" }
 };