Bug 799315 - Use Windows Media Foundation to play back MP4, MP3 and M4A on Windows 7 and later (preff'd off). r=padenot
authorChris Pearce <cpearce@mozilla.com>
Tue, 18 Dec 2012 21:49:58 +1300
changeset 125491 a75e9af08e9c348fc8b163467bac82d3b6748580
parent 125490 7f71d8e13583c20781ef06ca7aa32aaf499920bc
child 125492 b0fc7f12a86ce90ce2ebe8f647d6341763723699
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs799315
milestone20.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 799315 - Use Windows Media Foundation to play back MP4, MP3 and M4A on Windows 7 and later (preff'd off). r=padenot
content/base/src/nsContentUtils.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/media/DecoderTraits.cpp
content/media/DecoderTraits.h
content/media/Makefile.in
content/media/MediaDecoder.cpp
content/media/MediaDecoder.h
content/media/MediaDecoderReader.h
content/media/MediaDecoderStateMachine.cpp
content/media/test/Makefile.in
content/media/test/can_play_type_mpeg.js
content/media/test/test_can_play_type_mpeg.html
content/media/wmf/Makefile.in
content/media/wmf/WMF.h
content/media/wmf/WMFByteStream.cpp
content/media/wmf/WMFByteStream.h
content/media/wmf/WMFDecoder.cpp
content/media/wmf/WMFDecoder.h
content/media/wmf/WMFReader.cpp
content/media/wmf/WMFReader.h
content/media/wmf/WMFUtils.cpp
content/media/wmf/WMFUtils.h
layout/build/Makefile.in
layout/build/nsLayoutStatics.cpp
netwerk/mime/nsMimeTypes.h
uriloader/exthandler/nsExternalHelperAppService.cpp
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6540,16 +6540,27 @@ nsContentUtils::FindInternalContentViewe
       DecoderTraits::IsMediaPluginsType(nsDependentCString(aType))) {
     docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
     if (docFactory && aLoaderType) {
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 #endif // MOZ_MEDIA_PLUGINS
+
+#ifdef MOZ_WMF
+  if (DecoderTraits::IsWMFSupportedType(nsDependentCString(aType))) {
+    docFactory = do_GetService("@mozilla.org/content/document-loader-factory;1");
+    if (docFactory && aLoaderType) {
+      *aLoaderType = TYPE_CONTENT;
+    }
+    return docFactory.forget();
+  }
+#endif
+
 #endif // MOZ_MEDIA
 
   return NULL;
 }
 
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -97,16 +97,19 @@
 #include "MediaPluginDecoder.h"
 #endif
 #ifdef MOZ_WIDGET_GONK
 #include "MediaOmxDecoder.h"
 #endif
 #ifdef MOZ_DASH
 #include "DASHDecoder.h"
 #endif
+#ifdef MOZ_WMF
+#include "WMFDecoder.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)
@@ -2158,17 +2161,16 @@ nsHTMLMediaElement::CreateDecoder(const 
   // bundled decoders, unless the "media.prefer-gstreamer" pref is set.
   if (DecoderTraits::IsGStreamerSupportedType(aType)) {
     nsRefPtr<GStreamerDecoder> decoder = new GStreamerDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
-
 #ifdef MOZ_RAW
   if (DecoderTraits::IsRawType(aType)) {
     nsRefPtr<RawDecoder> decoder = new RawDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
@@ -2207,25 +2209,32 @@ nsHTMLMediaElement::CreateDecoder(const 
 #ifdef MOZ_WEBM
   if (DecoderTraits::IsWebMType(aType)) {
     nsRefPtr<WebMDecoder> decoder = new WebMDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
-
 #ifdef MOZ_DASH
   if (DecoderTraits::IsDASHMPDType(aType)) {
     nsRefPtr<DASHDecoder> decoder = new DASHDecoder();
     if (decoder->Init(this)) {
       return decoder.forget();
     }
   }
 #endif
+#ifdef MOZ_WMF
+  if (DecoderTraits::IsWMFSupportedType(aType)) {
+    nsRefPtr<WMFDecoder> decoder = new WMFDecoder();
+    if (decoder->Init(this)) {
+      return decoder.forget();
+    }
+  }
+#endif
 
   return nullptr;
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -8,16 +8,19 @@
 #include "MediaDecoder.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 #ifdef MOZ_GSTREAMER
 #include "mozilla/Preferences.h"
 #endif
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
 
 namespace mozilla
 {
 
 template <class String>
 static bool
 CodecListContains(char const *const * aCodecs, const String& aCodec)
 {
@@ -237,16 +240,23 @@ DecoderTraits::IsDASHMPDType(const nsACS
   if (!MediaDecoder::IsDASHEnabled()) {
     return false;
   }
 
   return CodecListContains(gDASHMPDTypes, aType);
 }
 #endif
 
+#ifdef MOZ_WMF
+bool DecoderTraits::IsWMFSupportedType(const nsACString& aType)
+{
+  return WMFDecoder::GetSupportedCodecs(aType, nullptr);
+}
+#endif
+
 /* static */
 bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
 {
 #ifdef MOZ_WAVE
   if (IsWaveType(nsDependentCString(aMIMEType))) {
     // We should not return true for Wave types, since there are some
     // Wave codecs actually in use in the wild that we don't support, and
     // we should allow those to be handled by plugins or helper apps.
@@ -292,29 +302,33 @@ DecoderTraits::CanHandleMediaType(const 
 #endif
 #ifdef MOZ_DASH
   if (IsDASHMPDType(nsDependentCString(aMIMEType))) {
     // DASH manifest uses WebM codecs only.
     codecList = gWebMCodecs;
     result = CANPLAY_YES;
   }
 #endif
-
 #ifdef MOZ_GSTREAMER
   if (IsH264Type(nsDependentCString(aMIMEType))) {
     codecList = gH264Codecs;
     result = CANPLAY_MAYBE;
   }
 #endif
 #ifdef MOZ_WIDGET_GONK
   if (IsOmxSupportedType(nsDependentCString(aMIMEType))) {
     codecList = gH264Codecs;
     result = CANPLAY_MAYBE;
   }
 #endif
+#ifdef MOZ_WMF
+  if (WMFDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList)) {
+    result = CANPLAY_MAYBE;
+  }
+#endif
 #ifdef MOZ_MEDIA_PLUGINS
   if (MediaDecoder::IsMediaPluginsEnabled() &&
       GetMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList))
     result = CANPLAY_MAYBE;
 #endif
   if (result == CANPLAY_NO || !aHaveRequestedCodecs) {
     return result;
   }
--- a/content/media/DecoderTraits.h
+++ b/content/media/DecoderTraits.h
@@ -64,14 +64,17 @@ public:
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsType(const nsACString& aType);
 #endif
 
 #ifdef MOZ_DASH
   static bool IsDASHMPDType(const nsACString& aType);
 #endif
 
+#ifdef MOZ_WMF
+  static bool IsWMFSupportedType(const nsACString& aType);
+#endif
 };
 
 }
 
 #endif
 
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -93,16 +93,20 @@ endif
 ifdef MOZ_DASH
 PARALLEL_DIRS += dash
 endif
 
 ifdef MOZ_MEDIA_PLUGINS
 PARALLEL_DIRS += plugins
 endif
 
+ifdef MOZ_WMF
+PARALLEL_DIRS += wmf
+endif
+
 PARALLEL_DIRS += webrtc
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 PARALLEL_DIRS += omx
 endif
 
 TEST_DIRS += test
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -18,16 +18,20 @@
 #include "nsContentUtils.h"
 #include "ImageContainer.h"
 #include "MediaResource.h"
 #include "nsError.h"
 #include "mozilla/Preferences.h"
 #include <cstdlib> // for std::abs(int/long)
 #include <cmath> // for std::abs(float/double)
 
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
+
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 // Number of milliseconds between progress events as defined by spec
 static const uint32_t PROGRESS_MS = 350;
 
@@ -1636,16 +1640,24 @@ MediaDecoder::IsMediaPluginsEnabled()
 #ifdef MOZ_DASH
 bool
 MediaDecoder::IsDASHEnabled()
 {
   return Preferences::GetBool("media.dash.enabled");
 }
 #endif
 
+#ifdef MOZ_WMF
+bool
+MediaDecoder::IsWMFEnabled()
+{
+  return WMFDecoder::IsEnabled();
+}
+#endif
+
 MediaMemoryReporter* MediaMemoryReporter::sUniqueInstance;
 
 NS_MEMORY_REPORTER_IMPLEMENT(MediaDecodedVideoMemory,
   "explicit/media/decoded-video",
   KIND_HEAP,
   UNITS_BYTES,
   MediaMemoryReporter::GetDecodedVideoMemory,
   "Memory used by decoded video frames.")
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -762,16 +762,20 @@ public:
 #ifdef MOZ_MEDIA_PLUGINS
   static bool IsMediaPluginsEnabled();
 #endif
 
 #ifdef MOZ_DASH
   static bool IsDASHEnabled();
 #endif
 
+#ifdef MOZ_WMF
+  static bool IsWMFEnabled();
+#endif
+
   // Schedules the state machine to run one cycle on the shared state
   // machine thread. Main thread only.
   nsresult ScheduleStateMachineThread();
 
   struct Statistics {
     // Estimate of the current playback rate (bytes/second).
     double mPlaybackRate;
     // Estimate of the current download rate (bytes/second). This
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -409,16 +409,27 @@ public:
 
   // Moves the decode head to aTime microseconds. aStartTime and aEndTime
   // denote the start and end times of the media in usecs, and aCurrentTime
   // is the current playback position in microseconds.
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime) = 0;
+  
+  // Called when the decode thread is started, before calling any other
+  // decode, read metadata, or seek functions. Do any thread local setup
+  // in this function.
+  virtual void OnDecodeThreadStart() {}
+  
+  // Called when the decode thread is about to finish, after all calls to
+  // any other decode, read metadata, or seek functions. Any backend specific
+  // thread local tear down must be done in this function. Note that another
+  // decode thread could start up and run in future.
+  virtual void OnDecodeThreadFinish() {}
 
 protected:
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<AudioData> mAudioQueue;
 
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -462,39 +462,44 @@ int64_t MediaDecoderStateMachine::GetDec
     audioDecoded += mAudioEndTime - GetMediaTime();
   }
   return audioDecoded;
 }
 
 void MediaDecoderStateMachine::DecodeThreadRun()
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  mReader->OnDecodeThreadStart();
+  
+  {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-  if (mState == DECODER_STATE_DECODING_METADATA) {
-    if (NS_FAILED(DecodeMetadata())) {
+    if (mState == DECODER_STATE_DECODING_METADATA &&
+        NS_FAILED(DecodeMetadata())) {
       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
                    "Should be in shutdown state if metadata loading fails.");
       LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
     }
-  }
 
-  while (mState != DECODER_STATE_SHUTDOWN &&
-         mState != DECODER_STATE_COMPLETED &&
-         !mStopDecodeThread)
-  {
-    if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
-      DecodeLoop();
-    } else if (mState == DECODER_STATE_SEEKING) {
-      DecodeSeek();
+    while (mState != DECODER_STATE_SHUTDOWN &&
+           mState != DECODER_STATE_COMPLETED &&
+           !mStopDecodeThread)
+    {
+      if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
+        DecodeLoop();
+      } else if (mState == DECODER_STATE_SEEKING) {
+        DecodeSeek();
+      }
     }
+
+    mDecodeThreadIdle = true;
+    LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
   }
-
-  mDecodeThreadIdle = true;
-  LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
+  
+  mReader->OnDecodeThreadFinish();
 }
 
 void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
                                                DecodedStreamData* aStream,
                                                AudioSegment* aOutput)
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -33,16 +33,17 @@ include $(DEPTH)/config/autoconf.mk
 # make the test first check canPlayType for the type, and if it's not
 # supported, just do ok(true, "Type not supported") and stop the test.
 
 MOCHITEST_FILES = \
 		allowed.sjs \
 		can_play_type_ogg.js \
 		can_play_type_wave.js \
 		can_play_type_webm.js \
+		can_play_type_mpeg.js \
 		cancellable_request.sjs \
 		dynamic_redirect.sjs \
 		dynamic_resource.sjs \
 		file_access_controls.html \
 		fragment_play.js \
 		fragment_noplay.js \
 		manifest.js \
 		reactivate_helper.html \
@@ -67,16 +68,17 @@ MOCHITEST_FILES = \
 		test_audio2.html \
 		test_autoplay.html \
 		test_bug465498.html \
 		test_bug493187.html \
 		test_bug495145.html \
 		test_bug495300.html \
 		test_bug686942.html \
 		test_can_play_type.html \
+		test_can_play_type_mpeg.html \
 		test_closing_connections.html \
 		test_constants.html \
 		test_controls.html \
 		test_currentTime.html \
 		test_decode_error.html \
 		test_defaultMuted.html \
 		test_delay_load.html \
 		test_error_on_404.html \
new file mode 100644
--- /dev/null
+++ b/content/media/test/can_play_type_mpeg.js
@@ -0,0 +1,27 @@
+function check_mp4(v, enabled) {
+  function check(type, expected) {
+    var ex = enabled ? expected : "";
+    is(v.canPlayType(type), ex, type + "='" + ex + "'");
+  }
+
+  check("video/mp4", "maybe");
+  check("audio/mp4", "maybe");
+  check("audio/mpeg", "maybe");
+
+  check("video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.42001E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.4D401E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001E, mp4a.40.2\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"", "probably");
+
+  check("video/mp4; codecs=\"avc1.42E01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.42001E\"", "probably");
+  check("video/mp4; codecs=\"avc1.58A01E\"", "probably");
+  check("video/mp4; codecs=\"avc1.4D401E\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001E\"", "probably");
+  check("video/mp4; codecs=\"avc1.64001F\"", "probably");
+
+  check("audio/mp4; codecs=\"mp4a.40.2\"", "probably");
+  check("audio/mp4; codecs=mp4a.40.2", "probably");
+}
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_can_play_type_mpeg.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=799315
+-->
+<head>
+  <title>Test for MP4 and MP3 support</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<video id="v"></video>
+
+<pre id="test">
+<script src="can_play_type_mpeg.js"></script>
+<script>
+
+function IsWindowsVistaOrLater() {
+  var re = /Windows NT (\d.\d)/;
+  var winver = navigator.userAgent.match(re);
+  return winver && winver.length == 2 && parseFloat(winver[1]) >= 6.0;
+}
+
+function getMediaPref(name) {
+  // Can't use SpecialPowers.getBoolPref because it throws when pref isn't
+  // present, and for example on non-Windows systems the WMF prefs won't be
+  // present.
+  var pref = false;
+  var prefService = SpecialPowers.wrap(SpecialPowers.Components)
+                                 .classes["@mozilla.org/preferences-service;1"]
+                                 .getService(SpecialPowers.Ci.nsIPrefService);
+  var branch = prefService.getBranch("media.");
+  try {
+    pref = branch.getBoolPref(name);
+  } catch(ex) { }
+  return pref;
+}
+
+var haveMp4 = (getMediaPref("windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
+               getMediaPref("omx.enabled") ||
+               getMediaPref("gstreamer.enabled");
+// TODO:  Add "getMediaPref("plugins.enabled")" once MP4 works on Gingerbread.
+             
+check_mp4(document.getElementById('v'), haveMp4);
+
+mediaTestCleanup();
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/Makefile.in
@@ -0,0 +1,30 @@
+# 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/.
+
+DEPTH		= @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+FAIL_ON_WARNINGS := 1
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= content
+LIBRARY_NAME	= gkconwmf_s
+LIBXUL_LIBRARY 	= 1
+
+EXPORTS		+= \
+		WMFDecoder.h \
+		$(NULL)
+
+CPPSRCS		= \
+		WMFByteStream.cpp \
+		WMFDecoder.cpp \
+		WMFReader.cpp \
+		WMFUtils.cpp \
+		$(NULL)
+
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMF.h
@@ -0,0 +1,112 @@
+/* -*- 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/. */
+
+#ifndef WMF_H_
+#define WMF_H_
+
+#if WINVER < _WIN32_WINNT_WIN7
+#error \
+You must include WMF.h before including mozilla headers, \
+otherwise mozconfig.h will be included \
+and that sets WINVER to WinXP, \
+which makes Windows Media Foundation unavailable.
+#endif
+
+#pragma push_macro("WINVER")
+#undef WINVER
+#define WINVER _WIN32_WINNT_WIN7
+
+#include <windows.h>
+#include <mfapi.h>
+#include <mfidl.h>
+#include <mfreadwrite.h>
+#include <mfobjects.h>
+#include <stdio.h>
+#include <mferror.h>
+#include <comdef.h>
+#include <propvarutil.h>
+#include <wmcodecdsp.h>
+
+#pragma comment(lib,"uuid.lib")
+#pragma comment(lib,"mfuuid.lib")
+
+_COM_SMARTPTR_TYPEDEF(IMFSourceReader, IID_IMFSourceReader);
+_COM_SMARTPTR_TYPEDEF(IMFMediaType, IID_IMFMediaType);
+_COM_SMARTPTR_TYPEDEF(IMFSample, IID_IMFSample);
+_COM_SMARTPTR_TYPEDEF(IMFMediaBuffer, IID_IMFMediaBuffer);
+_COM_SMARTPTR_TYPEDEF(IMFAsyncResult, IID_IMFAsyncResult);
+_COM_SMARTPTR_TYPEDEF(IMF2DBuffer, IID_IMF2DBuffer);
+
+namespace mozilla {
+namespace wmf {
+
+// Loads/Unloads all the DLLs in which the WMF functions are located.
+// The DLLs must be loaded before any of the WMF functions below will work.
+// All the function definitions below are wrappers which locate the
+// corresponding WMF function in the appropriate DLL (hence why LoadDLL()
+// must be called first...).
+HRESULT LoadDLLs();
+HRESULT UnloadDLLs();
+
+// All functions below are wrappers around the corresponding WMF function,
+// and automatically locate and call the corresponding function in the WMF DLLs.
+
+HRESULT MFStartup();
+
+HRESULT MFShutdown();
+
+HRESULT MFPutWorkItem(DWORD aWorkQueueId,
+                      IMFAsyncCallback *aCallback,
+                      IUnknown *aState);
+
+HRESULT MFAllocateWorkQueue(DWORD *aOutWorkQueueId);
+
+HRESULT MFUnlockWorkQueue(DWORD aWorkQueueId);
+
+HRESULT MFCreateAsyncResult(IUnknown *aUunkObject,
+                            IMFAsyncCallback *aCallback,
+                            IUnknown *aUnkState,
+                            IMFAsyncResult **aOutAsyncResult);
+
+HRESULT MFInvokeCallback(IMFAsyncResult *aAsyncResult);
+
+HRESULT MFCreateMediaType(IMFMediaType **aOutMFType);
+
+HRESULT MFCreateSourceReaderFromByteStream(IMFByteStream *aByteStream,
+                                           IMFAttributes *aAttributes,
+                                           IMFSourceReader **aOutSourceReader);
+
+HRESULT PropVariantToUInt32(REFPROPVARIANT aPropvar, ULONG *aOutUL);
+
+HRESULT PropVariantToInt64(REFPROPVARIANT aPropVar, LONGLONG *aOutLL);
+
+HRESULT MFTGetInfo(CLSID aClsidMFT,
+                   LPWSTR *aOutName,
+                   MFT_REGISTER_TYPE_INFO **aOutInputTypes,
+                   UINT32 *aOutNumInputTypes,
+                   MFT_REGISTER_TYPE_INFO **aOutOutputTypes,
+                   UINT32 *aOutNumOutputTypes,
+                   IMFAttributes **aOutAttributes);
+
+HRESULT MFGetStrideForBitmapInfoHeader(DWORD aFormat,
+                                       DWORD aWidth,
+                                       LONG *aOutStride);
+
+// Note: We shouldn't use this in production code; it's really only
+// here so we can compare behaviour of the SourceReader using WMF's
+// byte stream and ours when debugging.
+HRESULT MFCreateSourceReaderFromURL(LPCWSTR aURL,
+                                    IMFAttributes *aAttributes,
+                                    IMFSourceReader **aSourceReader);
+
+} // end namespace wmf
+} // end namespace mozilla
+
+
+
+#pragma pop_macro("WINVER")
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFByteStream.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+
+#include "Unknwn.h"
+#include <ole2.h>
+
+#include "WMFByteStream.h"
+#include "WMFUtils.h"
+#include "MediaResource.h"
+#include "nsISeekableStream.h"
+
+namespace mozilla {
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* gWMFByteStreamLog = nullptr;
+#define LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+HRESULT
+DoGetInterface(IUnknown* aUnknown, void** aInterface)
+{
+  if (!aInterface)
+    return E_POINTER;
+  *aInterface = aUnknown;
+  aUnknown->AddRef();
+  return S_OK;
+}
+
+WMFByteStream::WMFByteStream(MediaResource* aResource)
+  : mWorkQueueId(MFASYNC_CALLBACK_QUEUE_UNDEFINED),
+    mReentrantMonitor("WMFByteStream"),
+    mOffset(0),
+    mResource(aResource)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  NS_ASSERTION(mResource, "Must have a valid media resource");
+
+#ifdef PR_LOGGING
+  if (!gWMFByteStreamLog) {
+    gWMFByteStreamLog = PR_NewLogModule("WMFByteStream");
+  }
+#endif
+
+  MOZ_COUNT_CTOR(WMFByteStream);
+}
+
+WMFByteStream::~WMFByteStream()
+{
+  MOZ_COUNT_DTOR(WMFByteStream);
+}
+
+nsresult
+WMFByteStream::Init()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  // Work queue is not yet initialized, try to create.
+  HRESULT hr = wmf::MFAllocateWorkQueue(&mWorkQueueId);
+  if (FAILED(hr)) {
+    NS_WARNING("WMFByteStream Failed to allocate work queue.");
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+nsresult
+WMFByteStream::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  if (mWorkQueueId != MFASYNC_CALLBACK_QUEUE_UNDEFINED) {
+    HRESULT hr = wmf::MFUnlockWorkQueue(mWorkQueueId);
+    if (FAILED(hr)) {
+      NS_WARNING("WMFByteStream Failed to unlock work queue.");
+      LOG("WMFByteStream unlock work queue hr=%x %d\n", hr, hr);
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
+
+// IUnknown Methods
+STDMETHODIMP
+WMFByteStream::QueryInterface(REFIID aIId, void **aInterface)
+{
+  LOG("WMFByteStream::QueryInterface %s", GetGUIDName(aIId).get());
+
+  if (aIId == IID_IMFByteStream) {
+    return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
+  }
+  if (aIId == IID_IMFAsyncCallback) {
+    return DoGetInterface(static_cast<IMFAsyncCallback*>(this), aInterface);
+  }
+  if (aIId == IID_IUnknown) {
+    return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
+  }
+
+  *aInterface = NULL;
+  return E_NOINTERFACE;
+}
+
+NS_IMPL_THREADSAFE_ADDREF(WMFByteStream);
+NS_IMPL_THREADSAFE_RELEASE(WMFByteStream);
+
+NS_IMPL_THREADSAFE_ADDREF(WMFByteStream::AsyncReadRequestState);
+NS_IMPL_THREADSAFE_RELEASE(WMFByteStream::AsyncReadRequestState);
+
+// IUnknown Methods
+STDMETHODIMP
+WMFByteStream::AsyncReadRequestState::QueryInterface(REFIID aIId, void **aInterface)
+{
+  LOG("WMFByteStream::AsyncReadRequestState::QueryInterface %s", GetGUIDName(aIId).get());
+
+  if (aIId == IID_IUnknown) {
+    return DoGetInterface(static_cast<IUnknown*>(this), aInterface);
+  }
+
+  *aInterface = NULL;
+  return E_NOINTERFACE;
+}
+
+// IMFByteStream Methods
+STDMETHODIMP
+WMFByteStream::BeginRead(BYTE *aBuffer,
+                         ULONG aLength,
+                         IMFAsyncCallback *aCallback,
+                         IUnknown *aCallerState)
+{
+  NS_ENSURE_TRUE(aBuffer, E_POINTER);
+  NS_ENSURE_TRUE(aCallback, E_POINTER);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  LOG("WMFByteStream::BeginRead() mOffset=%lld tell=%lld length=%lu",
+      mOffset, mResource->Tell(), aLength);
+
+  // Create an object to store our state.
+  IUnknownPtr requestState = new AsyncReadRequestState(mOffset, aBuffer, aLength);
+
+  // Create an IMFAsyncResult, this is passed back to the caller as a token to
+  // retrieve the number of bytes read.
+  IMFAsyncResultPtr callersResult;
+  HRESULT hr = wmf::MFCreateAsyncResult(requestState,
+                                        aCallback,
+                                        aCallerState,
+                                        &callersResult);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Queue a work item on our Windows Media Foundation work queue to call
+  // this object's Invoke() function which performs the read, and in turn
+  // invokes the caller's callback to notify the caller that the read
+  // operation is complete. Note: This creates another IMFAsyncResult to
+  // wrap callersResult, and it's that wrapping IMFAsyncResult which is
+  // passed to Invoke().
+  hr = wmf::MFPutWorkItem(mWorkQueueId, this, callersResult);
+
+  return hr;
+}
+
+// IMFAsyncCallback::Invoke implementation. This is called back on the work
+// queue thread, and performs the actual read.
+STDMETHODIMP
+WMFByteStream::Invoke(IMFAsyncResult* aResult)
+{
+  // Note: We assume that the WMF Work Queue that calls this function does
+  // so synchronously, i.e. this function call returns before any other
+  // work queue item runs. This is important, as if we run multiple instances
+  // of this function at once we'll interleave seeks and reads on the
+  // media resoure.
+
+  // Extract the caller's IMFAsyncResult object from the wrapping aResult object.
+  IMFAsyncResultPtr callerResult;
+  IUnknownPtr unknown;
+  HRESULT hr = aResult->GetState(&unknown);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  callerResult = unknown;
+  NS_ENSURE_TRUE(callerResult, E_FAIL);
+
+  // Get the object that holds our state information for the asynchronous call.
+  hr = callerResult->GetObject(&unknown);
+  NS_ENSURE_TRUE(SUCCEEDED(hr) && unknown, hr);
+  AsyncReadRequestState* requestState =
+    static_cast<AsyncReadRequestState*>(unknown.GetInterfacePtr());
+
+  // Ensure the read head is at the correct offset in the resource. It may not
+  // be if the SourceReader seeked.
+  if (mResource->Tell() != requestState->mOffset) {
+    nsresult rv = mResource->Seek(nsISeekableStream::NS_SEEK_SET,
+                                  requestState->mOffset);
+    if (NS_FAILED(rv)) {
+      // Let SourceReader know the read failed.
+      callerResult->SetStatus(E_FAIL);
+      wmf::MFInvokeCallback(callerResult);
+      LOG("WMFByteStream::Invoke() seek to read offset failed, aborting read");
+      return S_OK;
+    }
+  }
+  NS_ASSERTION(mResource->Tell() == requestState->mOffset, "State mismatch!");
+
+  // Read in a loop to ensure we fill the buffer, when possible.
+  ULONG totalBytesRead = 0;
+  nsresult rv = NS_OK;
+  while (totalBytesRead < requestState->mBufferLength) {
+    BYTE* buffer = requestState->mBuffer + totalBytesRead;
+    ULONG bytesRead = 0;
+    ULONG length = requestState->mBufferLength - totalBytesRead;
+    rv = mResource->Read(reinterpret_cast<char*>(buffer),
+                         length,
+                         reinterpret_cast<uint32_t*>(&bytesRead));
+    totalBytesRead += bytesRead;
+    if (NS_FAILED(rv) || bytesRead == 0) {
+      break;
+    }
+  }
+
+  // Record the number of bytes read, so the caller can retrieve
+  // it later.
+  requestState->mBytesRead = NS_SUCCEEDED(rv) ? totalBytesRead : 0;
+  callerResult->SetStatus(S_OK);
+
+  LOG("WMFByteStream::Invoke() read %d at %lld finished rv=%x",
+       requestState->mBytesRead, requestState->mOffset, rv);
+
+  // Let caller know read is complete.
+  wmf::MFInvokeCallback(callerResult);
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::BeginWrite(const BYTE *, ULONG ,
+                          IMFAsyncCallback *,
+                          IUnknown *)
+{
+  LOG("WMFByteStream::BeginWrite()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Close()
+{
+  LOG("WMFByteStream::Close()");
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead)
+{
+  NS_ENSURE_TRUE(aResult, E_POINTER);
+  NS_ENSURE_TRUE(aBytesRead, E_POINTER);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  // Extract our state object.
+  IUnknownPtr unknown;
+  HRESULT hr = aResult->GetObject(&unknown);
+  if (FAILED(hr) || !unknown) {
+    return E_INVALIDARG;
+  }
+  AsyncReadRequestState* requestState =
+    static_cast<AsyncReadRequestState*>(unknown.GetInterfacePtr());
+
+  // Important: Only advance the read cursor if the caller hasn't seeked
+  // since it called BeginRead(). If it has seeked, we still must report
+  // the number of bytes read (in *aBytesRead), but we don't advance the
+  // read cursor; reads performed after the seek will do that. The SourceReader
+  // caller seems to keep track of which async read requested which range
+  // to be read, and does call SetCurrentPosition() before it calls EndRead().
+  if (mOffset == requestState->mOffset) {
+    mOffset += requestState->mBytesRead;
+  }
+
+  // Report result.
+  *aBytesRead = requestState->mBytesRead;
+
+  LOG("WMFByteStream::EndRead() offset=%lld *aBytesRead=%u mOffset=%lld hr=0x%x eof=%d",
+      requestState->mOffset, *aBytesRead, mOffset, hr, (mOffset == mResource->GetLength()));
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::EndWrite(IMFAsyncResult *, ULONG *)
+{
+  LOG("WMFByteStream::EndWrite()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Flush()
+{
+  LOG("WMFByteStream::Flush()");
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetCapabilities(DWORD *aCapabilities)
+{
+  LOG("WMFByteStream::GetCapabilities()");
+  NS_ENSURE_TRUE(aCapabilities, E_POINTER);
+  *aCapabilities = MFBYTESTREAM_IS_READABLE |
+                   MFBYTESTREAM_IS_SEEKABLE;
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetCurrentPosition(QWORD *aPosition)
+{
+  NS_ENSURE_TRUE(aPosition, E_POINTER);
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  *aPosition = mOffset;
+  LOG("WMFByteStream::GetCurrentPosition() %lld", mOffset);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::GetLength(QWORD *aLength)
+{
+  NS_ENSURE_TRUE(aLength, E_POINTER);
+  int64_t length = mResource->GetLength();
+  *aLength = length;
+  LOG("WMFByteStream::GetLength() %lld", length);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::IsEndOfStream(BOOL *aEndOfStream)
+{
+  NS_ENSURE_TRUE(aEndOfStream, E_POINTER);
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  *aEndOfStream = (mOffset == mResource->GetLength());
+  LOG("WMFByteStream::IsEndOfStream() %d", *aEndOfStream);
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::Read(BYTE*, ULONG, ULONG*)
+{
+  LOG("WMFByteStream::Read()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
+                    LONGLONG aSeekOffset,
+                    DWORD aSeekFlags,
+                    QWORD *aCurrentPosition)
+{
+  LOG("WMFByteStream::Seek(%d, %lld)", aSeekOrigin, aSeekOffset);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  if (aSeekOrigin == msoBegin) {
+    mOffset = aSeekOffset;
+  } else {
+    mOffset += aSeekOffset;
+  }
+
+  if (aCurrentPosition) {
+    *aCurrentPosition = mOffset;
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::SetCurrentPosition(QWORD aPosition)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  LOG("WMFByteStream::SetCurrentPosition(%lld)",
+      aPosition);
+
+  // Note: WMF calls SetCurrentPosition() sometimes after calling BeginRead()
+  // but before the read has finished, and thus before it's called EndRead().
+  // See comment in EndRead() for more details.
+
+  int64_t length = mResource->GetLength();
+  if (length >= 0 && aPosition > uint64_t(length)) {
+    // Despite the fact that the MSDN IMFByteStream::SetCurrentPosition()
+    // documentation says that we should return E_INVALIDARG when the seek
+    // position is after the length, if we do that IMFSourceReader::ReadSample()
+    // fails in some situations. So we just clamp the seek target to
+    // the EOS, and things seem to just work...
+    LOG("WMFByteStream::SetCurrentPosition(%lld) clamping position to eos (%lld)", aPosition, length);
+    aPosition = length;
+  }
+  mOffset = aPosition;
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFByteStream::SetLength(QWORD)
+{
+  LOG("WMFByteStream::SetLength()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::Write(const BYTE *, ULONG, ULONG *)
+{
+  LOG("WMFByteStream::Write()");
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP
+WMFByteStream::GetParameters(DWORD*, DWORD*)
+{
+  LOG("WMFByteStream::GetParameters()");
+  return E_NOTIMPL;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFByteStream.h
@@ -0,0 +1,125 @@
+/* -*- 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/. */
+#if !defined(WMFByteStream_h_)
+#define WMFByteStream_h_
+
+#include "WMF.h"
+
+#include "nsISupportsImpl.h"
+#include "nsCOMPtr.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+class MediaResource;
+
+// Wraps a MediaResource around an IMFByteStream interface, so that it can
+// be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
+// on which blocking I/O is performed. The SourceReader requests reads
+// asynchronously using {Begin,End}Read(). The synchronous I/O methods aren't
+// used by the SourceReader, so they're not implemented on this class.
+class WMFByteStream : public IMFByteStream,
+                      public IMFAsyncCallback
+{
+public:
+  WMFByteStream(MediaResource* aResource);
+  ~WMFByteStream();
+
+  nsresult Init();
+  nsresult Shutdown();
+
+  // IUnknown Methods.
+  STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
+  STDMETHODIMP_(ULONG) AddRef();
+  STDMETHODIMP_(ULONG) Release();
+
+  // IMFByteStream Methods.
+  STDMETHODIMP BeginRead(BYTE *aBuffer,
+                         ULONG aLength,
+                         IMFAsyncCallback *aCallback,
+                         IUnknown *aCallerState);
+  STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
+                          IMFAsyncCallback *,
+                          IUnknown *);
+  STDMETHODIMP Close();
+  STDMETHODIMP EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead);
+  STDMETHODIMP EndWrite(IMFAsyncResult *, ULONG *);
+  STDMETHODIMP Flush();
+  STDMETHODIMP GetCapabilities(DWORD *aCapabilities);
+  STDMETHODIMP GetCurrentPosition(QWORD *aPosition);
+  STDMETHODIMP GetLength(QWORD *pqwLength);
+  STDMETHODIMP IsEndOfStream(BOOL *aIsEndOfStream);
+  STDMETHODIMP Read(BYTE *, ULONG, ULONG *);
+  STDMETHODIMP Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
+                    LONGLONG aSeekOffset,
+                    DWORD aSeekFlags,
+                    QWORD *aCurrentPosition);
+  STDMETHODIMP SetCurrentPosition(QWORD aPosition);
+  STDMETHODIMP SetLength(QWORD);
+  STDMETHODIMP Write(const BYTE *, ULONG, ULONG *);
+
+  // IMFAsyncCallback methods.
+  // We perform an async read operation in this callback implementation.
+  STDMETHODIMP GetParameters(DWORD*, DWORD*);
+  STDMETHODIMP Invoke(IMFAsyncResult* aResult);
+
+private:
+
+  // Id of the work queue upon which we perfrom reads. This
+  // objects's Invoke() function is called on the work queue's thread,
+  // and it's there that we perform blocking IO. This has value
+  // MFASYNC_CALLBACK_QUEUE_UNDEFINED if the work queue hasn't been
+  // created yet.
+  DWORD mWorkQueueId;
+
+  // Stores data regarding an async read opreation.
+  class AsyncReadRequestState : public IUnknown {
+  public:
+    AsyncReadRequestState(int64_t aOffset, BYTE* aBuffer, ULONG aLength)
+      : mOffset(aOffset),
+        mBuffer(aBuffer),
+        mBufferLength(aLength),
+        mBytesRead(0)
+    {}
+
+    // IUnknown Methods
+    STDMETHODIMP QueryInterface(REFIID aRIID, LPVOID *aOutObject);
+    STDMETHODIMP_(ULONG) AddRef();
+    STDMETHODIMP_(ULONG) Release();
+
+    int64_t mOffset;
+    BYTE* mBuffer;
+    ULONG mBufferLength;
+    ULONG mBytesRead;
+
+    // IUnknown ref counting.
+    nsAutoRefCnt mRefCnt;
+    NS_DECL_OWNINGTHREAD;
+  };
+
+  // Resource we're wrapping. Note this object's methods are threadsafe,
+  // and we only call read/seek on the work queue's thread.
+  MediaResource* mResource;
+
+  // Protects mOffset, which is accessed by the SourceReaders thread(s), and
+  // on the work queue thread.
+  ReentrantMonitor mReentrantMonitor;
+
+  // Current offset of the logical read cursor. We maintain this separately
+  // from the media resource's offset since a partially complete read (in Invoke())
+  // would leave the resource's offset at a value unexpected by the caller,
+  // since the read hadn't yet completed.
+  int64_t mOffset;
+
+  // IUnknown ref counting.
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFDecoder.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "WMFDecoder.h"
+#include "WMFReader.h"
+#include "WMFUtils.h"
+#include "MediaDecoderStateMachine.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+
+MediaDecoderStateMachine* WMFDecoder::CreateStateMachine()
+{
+  return new MediaDecoderStateMachine(this, new WMFReader(this));
+}
+
+bool
+WMFDecoder::GetSupportedCodecs(const nsACString& aType,
+                               char const *const ** aCodecList)
+{
+  if (!MediaDecoder::IsWMFEnabled() ||
+      NS_FAILED(LoadDLLs()))
+    return false;
+
+  // MP3 is specified to have no codecs in its "type" param:
+  // http://wiki.whatwg.org/wiki/Video_type_parameters#MPEG
+  // So specify an empty codecs list, so that if script specifies 
+  // a "type" param with codecs, it will be reported as not supported
+  // as per the spec.
+  static char const *const mp3AudioCodecs[] = {
+    nullptr
+  };
+  if (aType.EqualsASCII("audio/mpeg")) {
+    if (aCodecList) {
+      *aCodecList = mp3AudioCodecs;
+    }
+    // Assume that if LoadDLLs() didn't fail, we can decode MP3.
+    return true;
+  }
+
+  // AAC in M4A.
+  static char const *const aacAudioCodecs[] = {
+    "mp4a.40.2",    // AAC-LC
+    nullptr
+  };
+  if (aType.EqualsASCII("audio/mp4")) {
+    if (aCodecList) {
+      *aCodecList = aacAudioCodecs;
+    }
+    return true;
+  }
+
+  // H.264 + AAC in MP4.
+  static char const *const H264Codecs[] = {
+    "avc1.42E01E",  // H.264 Constrained Baseline Profile Level 3.0
+    "avc1.42001E",  // H.264 Baseline Profile Level 3.0
+    "avc1.58A01E",  // H.264 Extended Profile Level 3.0
+    "avc1.4D401E",  // H.264 Main Profile Level 3.0
+    "avc1.64001E",  // H.264 High Profile Level 3.0
+    "avc1.64001F",  // H.264 High Profile Level 3.1
+    "mp4a.40.2",    // AAC-LC
+    nullptr
+  };
+  if (aType.EqualsASCII("video/mp4")) {
+    if (aCodecList) {
+      *aCodecList = H264Codecs;
+    }
+    return true;
+  }
+
+  return false;
+}
+
+nsresult
+WMFDecoder::LoadDLLs()
+{
+  return SUCCEEDED(wmf::LoadDLLs()) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void
+WMFDecoder::UnloadDLLs()
+{
+  wmf::UnloadDLLs();
+}
+
+bool IsWindows7OrLater()
+{
+  OSVERSIONINFO versionInfo;
+  BOOL isWin7OrLater = FALSE;
+  versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+  if (!GetVersionEx(&versionInfo)) {
+    return false;
+  }
+  // Note: Win Vista = 6.0
+  //       Win 7     = 6.1
+  //       Win 8     = 6.2
+  return versionInfo.dwMajorVersion > 6 ||
+        (versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1);
+}
+
+/* static */
+bool
+WMFDecoder::IsEnabled()
+{
+  // We only use WMF on Windows 7 and up, until we can properly test Vista
+  // and how it responds with and without the Platform Update installed.
+  return IsWindows7OrLater() &&
+         Preferences::GetBool("media.windows-media-foundation.enabled");
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFDecoder.h
@@ -0,0 +1,49 @@
+/* -*- 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/. */
+#if !defined(WMFDecoder_h_)
+#define WMFDecoder_h_
+
+#include "MediaDecoder.h"
+
+namespace mozilla {
+
+// Decoder that uses Windows Media Foundation to playback H.264/AAC in MP4
+// and M4A files, and MP3 files. Playback is strictly limited to only those
+// codecs.
+class WMFDecoder : public MediaDecoder
+{
+public:
+
+  virtual MediaDecoder* Clone() {
+    if (!IsWMFEnabled()) {
+      return nullptr;
+    }
+    return new WMFDecoder();
+  }
+
+  virtual MediaDecoderStateMachine* CreateStateMachine();
+
+  // Returns true if aType is a MIME type that we render with the
+  // Windows Media Foundation backend. If aCodecList is non null,
+  // it is filled with a (static const) null-terminated list of strings
+  // denoting the codecs we'll playback. Note that playback is strictly
+  // limited to the codecs in this list.
+  static bool GetSupportedCodecs(const nsACString& aType,
+                                 char const *const ** aCodecList);
+
+  // Loads the DLLs required by Windows Media Foundation. If this returns
+  // failure, you can assume that WMF is not available on the user's system.
+  static nsresult LoadDLLs();
+  static void UnloadDLLs();
+
+  // Returns true if the WMF backend is preffed on, and we're running on a
+  // version of Windows which is likely to support WMF.
+  static bool IsEnabled();
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFReader.cpp
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMFReader.h"
+#include "WMFDecoder.h"
+#include "WMFUtils.h"
+#include "WMFByteStream.h"
+
+#ifndef MOZ_SAMPLE_TYPE_FLOAT32
+#error We expect 32bit float audio samples on desktop for the Windows Media Foundation media backend.
+#endif
+
+#include "MediaDecoder.h"
+#include "VideoUtils.h"
+
+namespace mozilla {
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaDecoderLog;
+#define LOG(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+// Uncomment to enable verbose per-sample logging.
+//#define LOG_SAMPLE_DECODE 1
+
+WMFReader::WMFReader(MediaDecoder* aDecoder)
+  : MediaDecoderReader(aDecoder),
+    mSourceReader(nullptr),
+    mAudioChannels(0),
+    mAudioBytesPerSample(0),
+    mAudioRate(0),
+    mVideoWidth(0),
+    mVideoHeight(0),
+    mVideoStride(0),
+    mHasAudio(false),
+    mHasVideo(false),
+    mCanSeek(false)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+  MOZ_COUNT_CTOR(WMFReader);
+}
+
+WMFReader::~WMFReader()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  // Note: We must shutdown the byte stream before calling MFShutdown, else we
+  // get assertion failures when unlocking the byte stream's work queue.
+  if (mByteStream) {
+    nsresult rv = mByteStream->Shutdown();
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown WMFByteStream");
+  }
+  HRESULT hr = wmf::MFShutdown();
+  NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
+  MOZ_COUNT_DTOR(WMFReader);
+}
+
+void
+WMFReader::OnDecodeThreadStart()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), );
+}
+
+void
+WMFReader::OnDecodeThreadFinish()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  CoUninitialize();
+}
+
+nsresult
+WMFReader::Init(MediaDecoderReader* aCloneDonor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+
+  nsresult rv = WMFDecoder::LoadDLLs();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (FAILED(wmf::MFStartup())) {
+    NS_WARNING("Failed to initialize Windows Media Foundation");
+    return NS_ERROR_FAILURE;
+  }
+
+  // Must be created on main thread.
+  mByteStream = new WMFByteStream(mDecoder->GetResource());
+
+  return mByteStream->Init();
+}
+
+bool
+WMFReader::HasAudio()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  return mHasAudio;
+}
+
+bool
+WMFReader::HasVideo()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  return mHasVideo;
+}
+
+static HRESULT
+ConfigureSourceReaderStream(IMFSourceReader *aReader,
+                            const DWORD aStreamIndex,
+                            const GUID& aOutputSubType,
+                            const GUID* aAllowedInSubTypes,
+                            const uint32_t aNumAllowedInSubTypes)
+{
+  NS_ENSURE_TRUE(aReader, E_POINTER);
+  NS_ENSURE_TRUE(aAllowedInSubTypes, E_POINTER);
+
+  IMFMediaTypePtr nativeType;
+  IMFMediaTypePtr type;
+  HRESULT hr;
+
+  // Find the native format of the stream.
+  hr = aReader->GetNativeMediaType(aStreamIndex, 0, &nativeType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Get the native output subtype of the stream. This denotes the uncompressed
+  // type.
+  GUID subType;
+  hr = nativeType->GetGUID(MF_MT_SUBTYPE, &subType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Ensure the input type of the media is in the allowed formats list.
+  bool isSubTypeAllowed = false;
+  for (uint32_t i = 0; i < aNumAllowedInSubTypes; i++) {
+    if (aAllowedInSubTypes[i] == subType) {
+      isSubTypeAllowed = true;
+      break;
+    }
+  }
+  if (!isSubTypeAllowed) {
+    nsCString name = GetGUIDName(subType);
+    LOG("ConfigureSourceReaderStream subType=%s is not allowed to be decoded", name.get());
+    return E_FAIL;
+  }
+
+  // Find the major type.
+  GUID majorType;
+  hr = nativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Define the output type.
+  hr = wmf::MFCreateMediaType(&type);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = type->SetGUID(MF_MT_MAJOR_TYPE, majorType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = type->SetGUID(MF_MT_SUBTYPE, aOutputSubType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // Set the uncompressed format. This can fail if the decoder can't produce
+  // that type.
+  return aReader->SetCurrentMediaType(aStreamIndex, NULL, type);
+}
+
+// Returns the duration of the resource, in microseconds.
+HRESULT
+GetSourceReaderDuration(IMFSourceReader *aReader,
+                        int64_t& aOutDuration)
+{
+  AutoPropVar var;
+  HRESULT hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
+                                                 MF_PD_DURATION,
+                                                 &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  // WMF stores duration in hundred nanosecond units.
+  int64_t duration_hns = 0;
+  hr = wmf::PropVariantToInt64(var, &duration_hns);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  aOutDuration = HNsToUsecs(duration_hns);
+
+  return S_OK;
+}
+
+HRESULT
+GetSourceReaderCanSeek(IMFSourceReader* aReader, bool& aOutCanSeek)
+{
+  NS_ENSURE_TRUE(aReader, E_FAIL);
+
+  HRESULT hr;
+  AutoPropVar var;
+  hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
+                                         MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS,
+                                         &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  ULONG flags = 0;
+  hr = wmf::PropVariantToUInt32(var, &flags);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
+
+  return S_OK;
+}
+
+static HRESULT
+GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
+{
+  // Try to get the default stride from the media type.
+  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
+  if (SUCCEEDED(hr)) {
+    return S_OK;
+  }
+
+  // Stride attribute not set, calculate it.
+  GUID subtype = GUID_NULL;
+  uint32_t width = 0;
+  uint32_t height = 0;
+
+  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  return hr;
+}
+
+void
+WMFReader::ConfigureVideoDecoder()
+{
+  NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
+
+  // Determine if we have video.
+  if (!mSourceReader ||
+      !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM)) {
+    return;
+  }
+
+  static const GUID MP4VideoTypes[] = {
+    MFVideoFormat_H264
+  };
+  HRESULT hr = ConfigureSourceReaderStream(mSourceReader,
+                                           MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                           MFVideoFormat_YV12,
+                                           MP4VideoTypes,
+                                           NS_ARRAY_LENGTH(MP4VideoTypes));
+  if (FAILED(hr)) {
+    LOG("Failed to configured video output for MFVideoFormat_YV12");
+    return;
+  }
+
+  IMFMediaTypePtr mediaType;
+  hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                          &mediaType);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to get configured video media type");
+    return;
+  }
+
+  if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &mVideoWidth, &mVideoHeight))) {
+    NS_WARNING("WMF video decoder failed to get frame dimensions!");
+    return;
+  }
+
+  LOG("Video frame %u x %u", mVideoWidth, mVideoHeight);
+  uint32_t aspectNum = 0, aspectDenom = 0;
+  if (FAILED(MFGetAttributeRatio(mediaType,
+                                 MF_MT_PIXEL_ASPECT_RATIO,
+                                 &aspectNum,
+                                 &aspectDenom))) {
+    NS_WARNING("WMF video decoder failed to get pixel aspect ratio!");
+    return;
+  }
+  LOG("Video aspect ratio %u x %u", aspectNum, aspectDenom);
+
+  GetDefaultStride(mediaType, &mVideoStride);
+
+  // Calculate and validate the frame size.
+  nsIntSize frameSize = nsIntSize(mVideoWidth, mVideoHeight);
+  nsIntRect pictureRegion = nsIntRect(0, 0, mVideoWidth, mVideoHeight);
+  nsIntSize displaySize = frameSize;
+  ScaleDisplayByAspectRatio(displaySize, float(aspectNum)/float(aspectDenom));
+  if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
+    // Video track's frame sizes will overflow. Ignore the video track.
+    return;
+  }
+  mInfo.mDisplay = displaySize;
+
+  LOG("Successfully configured video stream");
+
+  mHasVideo = mInfo.mHasVideo = true;
+}
+
+void
+WMFReader::ConfigureAudioDecoder()
+{
+  NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
+
+  if (!mSourceReader ||
+      !SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_AUDIO_STREAM)) {
+    // No audio stream.
+    return;
+  }
+
+  static const GUID MP4AudioTypes[] = {
+    MFAudioFormat_AAC,
+    MFAudioFormat_MP3
+  };
+  if (FAILED(ConfigureSourceReaderStream(mSourceReader,
+                                         MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                         MFAudioFormat_Float,
+                                         MP4AudioTypes,
+                                         NS_ARRAY_LENGTH(MP4AudioTypes)))) {
+    NS_WARNING("Failed to configure WMF Audio decoder for PCM output");
+    return;
+  }
+
+  IMFMediaTypePtr mediaType;
+  HRESULT hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                                  &mediaType);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to get configured audio media type");
+    return;
+  }
+
+  mAudioRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
+  mAudioChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0);
+  mAudioBytesPerSample = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16) / 8;
+
+  mInfo.mAudioChannels = mAudioChannels;
+  mInfo.mAudioRate = mAudioRate;
+  mHasAudio = mInfo.mHasAudio = true;
+
+  LOG("Successfully configured audio stream. rate=%u channels=%u bitsPerSample=%u",
+      mAudioRate, mAudioChannels, mAudioBytesPerSample);
+}
+
+nsresult
+WMFReader::ReadMetadata(VideoInfo* aInfo,
+                        MetadataTags** aTags)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  LOG("WMFReader::ReadMetadata()");
+  HRESULT hr;
+
+  hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, NULL, &mSourceReader);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  ConfigureVideoDecoder();
+  ConfigureAudioDecoder();
+
+  // Abort if both video and audio failed to initialize.
+  NS_ENSURE_TRUE(mInfo.mHasAudio || mInfo.mHasVideo, NS_ERROR_FAILURE);
+
+  int64_t duration = 0;
+  if (SUCCEEDED(GetSourceReaderDuration(mSourceReader, duration))) {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    mDecoder->SetMediaDuration(duration);
+  }
+
+  hr = GetSourceReaderCanSeek(mSourceReader, mCanSeek);
+  NS_ASSERTION(SUCCEEDED(hr), "Can't determine if resource is seekable");
+
+  *aInfo = mInfo;
+  *aTags = nullptr;
+  // aTags can be retrieved using techniques like used here:
+  // http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx
+
+  return NS_OK;
+}
+
+static int64_t
+GetSampleDuration(IMFSample* aSample)
+{
+  int64_t duration = 0;
+  aSample->GetSampleDuration(&duration);
+  return HNsToUsecs(duration);
+}
+
+bool
+WMFReader::DecodeAudioData()
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  DWORD flags;
+  LONGLONG timestampHns;
+  HRESULT hr;
+
+  IMFSamplePtr sample;
+  hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
+                                 0, // control flags
+                                 nullptr, // read stream index
+                                 &flags,
+                                 &timestampHns,
+                                 &sample);
+
+  if (FAILED(hr) ||
+      (flags & MF_SOURCE_READERF_ERROR) ||
+      (flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+    // End of stream.
+    mAudioQueue.Finish();
+    return false;
+  }
+
+  IMFMediaBufferPtr buffer;
+  hr = sample->ConvertToContiguousBuffer(&buffer);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
+  BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
+  DWORD maxLength = 0, currentLength = 0;
+  hr = buffer->Lock(&data, &maxLength, &currentLength);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+
+  uint32_t numFrames = currentLength / mAudioBytesPerSample / mAudioChannels;
+  NS_ASSERTION(sizeof(AudioDataValue) == mAudioBytesPerSample, "Size calculation is wrong");
+  nsAutoArrayPtr<AudioDataValue> pcmSamples(new AudioDataValue[numFrames * mAudioChannels]);
+  memcpy(pcmSamples.get(), data, currentLength);
+  buffer->Unlock();
+
+  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t timestamp = HNsToUsecs(timestampHns);
+  int64_t duration = GetSampleDuration(sample);
+
+  mAudioQueue.Push(new AudioData(offset,
+                                 timestamp,
+                                 duration,
+                                 numFrames,
+                                 pcmSamples.forget(),
+                                 mAudioChannels));
+
+  #ifdef LOG_SAMPLE_DECODE
+  LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
+      timestamp, duration, currentLength);
+  #endif
+
+  return true;
+}
+
+bool
+WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
+                            int64_t aTimeThreshold)
+{
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+
+  // Record number of frames decoded and parsed. Automatically update the
+  // stats counters using the AutoNotifyDecoded stack-based class.
+  uint32_t parsed = 0, decoded = 0;
+  AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
+
+  DWORD flags;
+  LONGLONG timestampHns;
+  HRESULT hr;
+
+  IMFSamplePtr sample;
+  hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
+                                 0, // control flags
+                                 nullptr, // read stream index
+                                 &flags,
+                                 &timestampHns,
+                                 &sample);
+  if (flags & MF_SOURCE_READERF_ERROR) {
+    NS_WARNING("WMFReader: Catastrophic failure reading video sample");
+    // Future ReadSample() calls will fail, so give up and report end of stream.
+    mVideoQueue.Finish();
+    return false;
+  }
+
+  if (FAILED(hr)) {
+    // Unknown failure, ask caller to try again?
+    return true;
+  }
+
+  if (!sample) {
+    if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+      LOG("WMFReader; Null sample after video decode, at end of stream");
+      // End of stream.
+      mVideoQueue.Finish();
+      return false;
+    }
+    LOG("WMFReader; Null sample after video decode. Maybe insufficient data...");
+    return true;
+  }
+
+  int64_t timestamp = HNsToUsecs(timestampHns);
+  if (timestamp < aTimeThreshold) {
+    return true;
+  }
+  int64_t offset = mDecoder->GetResource()->Tell();
+  int64_t duration = GetSampleDuration(sample);
+
+  IMFMediaBufferPtr buffer;
+
+  // Must convert to contiguous buffer to use IMD2DBuffer interface.
+  hr = sample->ConvertToContiguousBuffer(&buffer);
+  if (FAILED(hr)) {
+    NS_WARNING("ConvertToContiguousBuffer() failed!");
+    return true;
+  }
+
+  // Try and use the IMF2DBuffer interface if available, otherwise fallback
+  // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
+  // but only some systems (Windows 8?) support it.
+  BYTE* data = nullptr;
+  LONG stride = 0;
+  IMF2DBufferPtr twoDBuffer = buffer;
+  if (twoDBuffer) {
+    hr = twoDBuffer->Lock2D(&data, &stride);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+  } else {
+    hr = buffer->Lock(&data, NULL, NULL);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), false);
+    stride = mVideoStride;
+  }
+
+  // YV12, planar format: [YYYY....][VVVV....][UUUU....]
+  // i.e., Y, then V, then U.
+  VideoData::YCbCrBuffer b;
+
+  // Y (Y') plane
+  b.mPlanes[0].mData = data;
+  b.mPlanes[0].mStride = stride;
+  b.mPlanes[0].mHeight = mVideoHeight;
+  b.mPlanes[0].mWidth = stride;
+  b.mPlanes[0].mOffset = 0;
+  b.mPlanes[0].mSkip = 0;
+
+  // The V and U planes are stored 16-row-aligned, so we need to add padding
+  // to the row heights to ensure the Y'CbCr planes are referenced properly.
+  uint32_t padding = 0;
+  if (mVideoHeight % 16 != 0) {
+    padding = 16 - (mVideoHeight % 16);
+  }
+  uint32_t y_size = stride * (mVideoHeight + padding);
+  uint32_t v_size = stride * (mVideoHeight + padding) / 4;
+  uint32_t halfStride = (stride + 1) / 2;
+  uint32_t halfHeight = (mVideoHeight + 1) / 2;
+
+  // U plane (Cb)
+  b.mPlanes[1].mData = data + y_size + v_size;
+  b.mPlanes[1].mStride = halfStride;
+  b.mPlanes[1].mHeight = halfHeight;
+  b.mPlanes[1].mWidth = halfStride;
+  b.mPlanes[1].mOffset = 0;
+  b.mPlanes[1].mSkip = 0;
+
+  // V plane (Cr)
+  b.mPlanes[2].mData = data + y_size;
+  b.mPlanes[2].mStride = halfStride;
+  b.mPlanes[2].mHeight = halfHeight;
+  b.mPlanes[2].mWidth = halfStride;
+  b.mPlanes[2].mOffset = 0;
+  b.mPlanes[2].mSkip = 0;
+
+  VideoData *v = VideoData::Create(mInfo,
+                                   mDecoder->GetImageContainer(),
+                                   offset,
+                                   timestamp,
+                                   timestamp + duration,
+                                   b,
+                                   false,
+                                   -1,
+                                   nsIntRect(0, 0, mVideoWidth, mVideoHeight));
+  if (twoDBuffer) {
+    twoDBuffer->Unlock2D();
+  } else {
+    buffer->Unlock();
+  }
+
+  if (!v) {
+    NS_WARNING("Failed to create VideoData");
+    return false;
+  }
+  parsed++;
+  decoded++;
+  mVideoQueue.Push(v);
+
+  #ifdef LOG_SAMPLE_DECODE
+  LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d width=%u height=%u flags=%u",
+      timestamp, duration, stride, mVideoWidth, mVideoHeight, flags);
+  #endif
+
+  if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
+    // End of stream.
+    mVideoQueue.Finish();
+    LOG("End of video stream");
+    return false;
+  }
+
+  return true;
+}
+
+nsresult
+WMFReader::Seek(int64_t aTargetUs,
+                int64_t aStartTime,
+                int64_t aEndTime,
+                int64_t aCurrentTime)
+{
+  LOG("WMFReader::Seek() %lld", aTargetUs);
+
+  NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
+  if (!mCanSeek) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = ResetDecode();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoPropVar var;
+  HRESULT hr = InitPropVariantFromInt64(UsecsToHNs(aTargetUs), &var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  hr = mSourceReader->SetCurrentPosition(GUID_NULL, var);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  return DecodeToTarget(aTargetUs);
+}
+
+nsresult
+WMFReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime)
+{
+  MediaResource* stream = mDecoder->GetResource();
+  int64_t durationUs = 0;
+  {
+    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+    durationUs = mDecoder->GetMediaDuration();
+  }
+  GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFReader.h
@@ -0,0 +1,71 @@
+/* -*- 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/. */
+#if !defined(WMFReader_h_)
+#define WMFReader_h_
+
+#include "WMF.h"
+#include "MediaDecoderReader.h"
+
+namespace mozilla {
+
+class WMFByteStream;
+
+// Decoder backend for reading H.264/AAC in MP4/M4A and MP3 audio files,
+// using Windows Media Foundation.
+class WMFReader : public MediaDecoderReader
+{
+public:
+  WMFReader(MediaDecoder* aDecoder);
+
+  virtual ~WMFReader();
+
+  nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
+
+  bool DecodeAudioData() MOZ_OVERRIDE;
+  bool DecodeVideoFrame(bool &aKeyframeSkip,
+                        int64_t aTimeThreshold) MOZ_OVERRIDE;
+
+  bool HasAudio() MOZ_OVERRIDE;
+  bool HasVideo() MOZ_OVERRIDE;
+
+  nsresult ReadMetadata(VideoInfo* aInfo,
+                        MetadataTags** aTags) MOZ_OVERRIDE;
+
+  nsresult Seek(int64_t aTime,
+                int64_t aStartTime,
+                int64_t aEndTime,
+                int64_t aCurrentTime) MOZ_OVERRIDE;
+
+  nsresult GetBuffered(nsTimeRanges* aBuffered,
+                       int64_t aStartTime) MOZ_OVERRIDE;
+
+  void OnDecodeThreadStart() MOZ_OVERRIDE;
+  void OnDecodeThreadFinish() MOZ_OVERRIDE;
+
+private:
+
+  void ConfigureAudioDecoder();
+  void ConfigureVideoDecoder();
+
+  IMFSourceReaderPtr mSourceReader;
+  RefPtr<WMFByteStream> mByteStream;
+
+  uint32_t mAudioChannels;
+  uint32_t mAudioBytesPerSample;
+  uint32_t mAudioRate;
+
+  uint32_t mVideoWidth;
+  uint32_t mVideoHeight;
+  uint32_t mVideoStride;
+
+  bool mHasAudio;
+  bool mHasVideo;
+  bool mCanSeek;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFUtils.cpp
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMFUtils.h"
+#include "mozilla/StandardInteger.h"
+#include "prlog.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+struct GuidToName {
+  GUID guid;
+  const char* name;
+};
+
+#define GUID_TO_NAME_ENTRY(g) { g, #g }
+#define INTERFACE_TO_NAME_ENTRY(i) {IID_##i, #i }
+
+GuidToName GuidToNameTable[] = {
+  GUID_TO_NAME_ENTRY(MF_MT_MAJOR_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_MAJOR_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_SUBTYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_ALL_SAMPLES_INDEPENDENT),
+  GUID_TO_NAME_ENTRY(MF_MT_FIXED_SIZE_SAMPLES),
+  GUID_TO_NAME_ENTRY(MF_MT_COMPRESSED),
+  GUID_TO_NAME_ENTRY(MF_MT_SAMPLE_SIZE),
+  GUID_TO_NAME_ENTRY(MF_MT_WRAPPED_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_NUM_CHANNELS),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_AVG_BYTES_PER_SECOND),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_BLOCK_ALIGNMENT),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_BITS_PER_SAMPLE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_SAMPLES_PER_BLOCK),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_CHANNEL_MASK),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_FOLDDOWN_MATRIX),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_PEAKREF),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_PEAKTARGET),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_AVGREF),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_WMADRC_AVGTARGET),
+  GUID_TO_NAME_ENTRY(MF_MT_AUDIO_PREFER_WAVEFORMATEX),
+  GUID_TO_NAME_ENTRY(MF_MT_AAC_PAYLOAD_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_SIZE),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE_RANGE_MAX),
+  GUID_TO_NAME_ENTRY(MF_MT_FRAME_RATE_RANGE_MIN),
+  GUID_TO_NAME_ENTRY(MF_MT_PIXEL_ASPECT_RATIO),
+  GUID_TO_NAME_ENTRY(MF_MT_DRM_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_PAD_CONTROL_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_SOURCE_CONTENT_HINT),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_CHROMA_SITING),
+  GUID_TO_NAME_ENTRY(MF_MT_INTERLACE_MODE),
+  GUID_TO_NAME_ENTRY(MF_MT_TRANSFER_FUNCTION),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_PRIMARIES),
+  GUID_TO_NAME_ENTRY(MF_MT_CUSTOM_VIDEO_PRIMARIES),
+  GUID_TO_NAME_ENTRY(MF_MT_YUV_MATRIX),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_LIGHTING),
+  GUID_TO_NAME_ENTRY(MF_MT_VIDEO_NOMINAL_RANGE),
+  GUID_TO_NAME_ENTRY(MF_MT_GEOMETRIC_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_MINIMUM_DISPLAY_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_PAN_SCAN_APERTURE),
+  GUID_TO_NAME_ENTRY(MF_MT_PAN_SCAN_ENABLED),
+  GUID_TO_NAME_ENTRY(MF_MT_AVG_BITRATE),
+  GUID_TO_NAME_ENTRY(MF_MT_AVG_BIT_ERROR_RATE),
+  GUID_TO_NAME_ENTRY(MF_MT_MAX_KEYFRAME_SPACING),
+  GUID_TO_NAME_ENTRY(MF_MT_DEFAULT_STRIDE),
+  GUID_TO_NAME_ENTRY(MF_MT_PALETTE),
+  GUID_TO_NAME_ENTRY(MF_MT_USER_DATA),
+  GUID_TO_NAME_ENTRY(MF_MT_AM_FORMAT_TYPE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG_START_TIME_CODE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_PROFILE),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_LEVEL),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG2_FLAGS),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG_SEQUENCE_HEADER),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_SRC_PACK_0),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_CTRL_PACK_0),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_SRC_PACK_1),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_AAUX_CTRL_PACK_1),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_VAUX_SRC_PACK),
+  GUID_TO_NAME_ENTRY(MF_MT_DV_VAUX_CTRL_PACK),
+  GUID_TO_NAME_ENTRY(MF_MT_ARBITRARY_HEADER),
+  GUID_TO_NAME_ENTRY(MF_MT_ARBITRARY_FORMAT),
+  GUID_TO_NAME_ENTRY(MF_MT_IMAGE_LOSS_TOLERANT),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG4_SAMPLE_DESCRIPTION),
+  GUID_TO_NAME_ENTRY(MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY),
+  GUID_TO_NAME_ENTRY(MF_MT_ORIGINAL_4CC),
+  GUID_TO_NAME_ENTRY(MF_MT_ORIGINAL_WAVE_FORMAT_TAG),
+
+  GUID_TO_NAME_ENTRY(MFMediaType_Audio),
+  GUID_TO_NAME_ENTRY(MFMediaType_Video),
+  GUID_TO_NAME_ENTRY(MFMediaType_Protected),
+  GUID_TO_NAME_ENTRY(MFMediaType_SAMI),
+  GUID_TO_NAME_ENTRY(MFMediaType_Script),
+  GUID_TO_NAME_ENTRY(MFMediaType_Image),
+  GUID_TO_NAME_ENTRY(MFMediaType_HTML),
+  GUID_TO_NAME_ENTRY(MFMediaType_Binary),
+  GUID_TO_NAME_ENTRY(MFMediaType_FileTransfer),
+
+  GUID_TO_NAME_ENTRY(MFVideoFormat_AI44),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_ARGB32),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_AYUV),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DV25),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DV50),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVH1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVSD),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_DVSL),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_H264),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_I420),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_IYUV),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_M4S2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MJPG),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP43),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP4S),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MP4V),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MPG1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MSS1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_MSS2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_NV11),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_NV12),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P010),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P016),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_P216),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB24),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB32),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB555),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB565),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_RGB8),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_UYVY),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_v210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_v410),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WMV3),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_WVC1),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y210),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y216),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y410),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y416),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y41P),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_Y41T),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YUY2),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YV12),
+  GUID_TO_NAME_ENTRY(MFVideoFormat_YVYU),
+
+  GUID_TO_NAME_ENTRY(MFAudioFormat_PCM),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_Float),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_DTS),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_Dolby_AC3_SPDIF),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_DRM),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudioV8),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudioV9),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMAudio_Lossless),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_WMASPDIF),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MSP1),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MP3),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_MPEG),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_AAC),
+  GUID_TO_NAME_ENTRY(MFAudioFormat_ADTS),
+
+  // Interfaces which may be implemented by WMFByteStream.
+  INTERFACE_TO_NAME_ENTRY(IUnknown),
+  INTERFACE_TO_NAME_ENTRY(IMFByteStream),
+  INTERFACE_TO_NAME_ENTRY(IMFMediaSource),
+  INTERFACE_TO_NAME_ENTRY(IMFAttributes),
+  INTERFACE_TO_NAME_ENTRY(IMFByteStreamBuffering),
+};
+
+nsCString GetGUIDName(const GUID& guid)
+{
+  unsigned numTypes = NS_ARRAY_LENGTH(GuidToNameTable);
+  for (unsigned i = 0; i < numTypes; i++) {
+    if (guid == GuidToNameTable[i].guid) {
+      return nsDependentCString(GuidToNameTable[i].name);
+    }
+  }
+
+  WCHAR* name = nullptr;
+  HRESULT hr = StringFromCLSID(guid , &name);
+  if (FAILED(hr)) {
+    return nsDependentCString("GuidUnknown");
+  }
+  nsCString name_u8(NS_ConvertUTF16toUTF8(nsDependentString((PRUnichar*)(name))));
+  CoTaskMemFree(name);
+  return name_u8;
+}
+
+bool
+SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex)
+{
+  IMFMediaTypePtr nativeType;
+  HRESULT hr = aReader->GetNativeMediaType(aIndex, 0, &nativeType);
+  return FAILED(hr) ? false : true;
+}
+
+namespace wmf {
+
+static bool sDLLsLoaded = false;
+static bool sFailedToLoadDlls = false;
+
+static HMODULE sMfPlatMod = NULL;
+
+struct WMFModule {
+  const char* name;
+  HMODULE handle;
+};
+
+static WMFModule sDLLs[] = {
+  { "C:\\Windows\\system32\\mfplat.dll", NULL },
+  { "C:\\Windows\\system32\\mfreadwrite.dll", NULL },
+  { "C:\\Windows\\system32\\propsys.dll", NULL },
+  { "C:\\Windows\\system32\\mf.dll", NULL },
+};
+
+HRESULT
+LoadDLLs()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  if (sDLLsLoaded) {
+    return S_OK;
+  }
+  if (sFailedToLoadDlls) {
+    return E_FAIL;
+  }
+
+  // Try to load all the required DLLs.
+  uint32_t dllLength = NS_ARRAY_LENGTH(sDLLs);
+  for (uint32_t i = 0; i < dllLength; i++) {
+    sDLLs[i].handle = LoadLibraryA(sDLLs[i].name);
+    if (!sDLLs[i].handle) {
+      sFailedToLoadDlls = true;
+      UnloadDLLs();
+      wmf::MFShutdown();
+      return E_FAIL;
+    }
+  }
+
+  sDLLsLoaded = true;
+
+  return S_OK;
+}
+
+HRESULT
+UnloadDLLs()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  uint32_t length = NS_ARRAY_LENGTH(sDLLs);
+  for (uint32_t i = 0; i < length; i++) {
+    if (sDLLs[i].handle) {
+      FreeLibrary(sDLLs[i].handle);
+      sDLLs[i].handle = NULL;
+    }
+    sDLLsLoaded = false;
+  }
+  return S_OK;
+}
+
+#define ENSURE_FUNCTION_PTR(FunctionName, DLL) \
+  static FunctionName##Ptr_t FunctionName##Ptr = nullptr; \
+  if (!FunctionName##Ptr) { \
+    FunctionName##Ptr = (FunctionName##Ptr_t)GetProcAddress(GetModuleHandle( #DLL ), #FunctionName ); \
+    if (!FunctionName##Ptr) { \
+      NS_WARNING("Failed to get GetProcAddress of " #FunctionName " from " #DLL ); \
+      return E_FAIL; \
+    } \
+  }
+
+#define DECL_FUNCTION_PTR(FunctionName, ...) \
+  typedef HRESULT (STDMETHODCALLTYPE * FunctionName##Ptr_t)(__VA_ARGS__)
+
+HRESULT
+MFStartup()
+{
+  DECL_FUNCTION_PTR(MFStartup, ULONG, DWORD);
+  ENSURE_FUNCTION_PTR(MFStartup, Mfplat.dll)
+  return MFStartupPtr(MF_VERSION, MFSTARTUP_FULL);
+}
+
+HRESULT
+MFShutdown()
+{
+  DECL_FUNCTION_PTR(MFShutdown);
+  ENSURE_FUNCTION_PTR(MFShutdown, Mfplat.dll)
+  return (MFShutdownPtr)();
+}
+
+HRESULT
+MFPutWorkItem(DWORD aQueueId,
+              IMFAsyncCallback *aCallback,
+              IUnknown *aState)
+{
+  DECL_FUNCTION_PTR(MFPutWorkItem, DWORD, IMFAsyncCallback*, IUnknown*);
+  ENSURE_FUNCTION_PTR(MFPutWorkItem, Mfplat.dll)
+  return (MFPutWorkItemPtr)(aQueueId, aCallback, aState);
+}
+
+HRESULT
+MFAllocateWorkQueue(DWORD *aOutWorkQueueId)
+{
+  DECL_FUNCTION_PTR(MFAllocateWorkQueue, DWORD*);
+  ENSURE_FUNCTION_PTR(MFAllocateWorkQueue, Mfplat.dll)
+  return (MFAllocateWorkQueuePtr)(aOutWorkQueueId);
+}
+
+HRESULT
+MFUnlockWorkQueue(DWORD aWorkQueueId)
+{
+  DECL_FUNCTION_PTR(MFUnlockWorkQueue, DWORD);
+  ENSURE_FUNCTION_PTR(MFUnlockWorkQueue, Mfplat.dll);
+  return (MFUnlockWorkQueuePtr)(aWorkQueueId);
+}
+
+HRESULT MFCreateAsyncResult(IUnknown *aUnkObject,
+                            IMFAsyncCallback *aCallback,
+                            IUnknown *aUnkState,
+                            IMFAsyncResult **aOutAsyncResult)
+{
+  DECL_FUNCTION_PTR(MFCreateAsyncResult, IUnknown*, IMFAsyncCallback*, IUnknown*, IMFAsyncResult**);
+  ENSURE_FUNCTION_PTR(MFCreateAsyncResult, Mfplat.dll)
+  return (MFCreateAsyncResultPtr)(aUnkObject, aCallback, aUnkState, aOutAsyncResult);
+}
+
+HRESULT
+MFInvokeCallback(IMFAsyncResult *aAsyncResult)
+{
+  DECL_FUNCTION_PTR(MFInvokeCallback, IMFAsyncResult*);
+  ENSURE_FUNCTION_PTR(MFInvokeCallback, Mfplat.dll);
+  return (MFInvokeCallbackPtr)(aAsyncResult);
+}
+
+HRESULT
+MFCreateMediaType(IMFMediaType **aOutMFType)
+{
+  DECL_FUNCTION_PTR(MFCreateMediaType, IMFMediaType**);
+  ENSURE_FUNCTION_PTR(MFCreateMediaType, Mfplat.dll)
+  return (MFCreateMediaTypePtr)(aOutMFType);
+}
+
+HRESULT
+MFCreateSourceReaderFromByteStream(IMFByteStream *aByteStream,
+                                   IMFAttributes *aAttributes,
+                                   IMFSourceReader **aOutSourceReader)
+{
+  DECL_FUNCTION_PTR(MFCreateSourceReaderFromByteStream, IMFByteStream*, IMFAttributes*, IMFSourceReader**);
+  ENSURE_FUNCTION_PTR(MFCreateSourceReaderFromByteStream, Mfreadwrite.dll)
+  return (MFCreateSourceReaderFromByteStreamPtr)(aByteStream,
+                                                 aAttributes,
+                                                 aOutSourceReader);
+}
+
+HRESULT
+PropVariantToUInt32(REFPROPVARIANT aPropvar, ULONG *aOutUL)
+{
+  DECL_FUNCTION_PTR(PropVariantToUInt32, REFPROPVARIANT, ULONG *);
+  ENSURE_FUNCTION_PTR(PropVariantToUInt32, Propsys.dll)
+  return (PropVariantToUInt32Ptr)(aPropvar, aOutUL);
+}
+
+HRESULT PropVariantToInt64(REFPROPVARIANT aPropVar, LONGLONG *aOutLL)
+{
+  DECL_FUNCTION_PTR(PropVariantToInt64, REFPROPVARIANT, LONGLONG *);
+  ENSURE_FUNCTION_PTR(PropVariantToInt64, Propsys.dll)
+  return (PropVariantToInt64Ptr)(aPropVar, aOutLL);
+}
+
+HRESULT MFTGetInfo(CLSID aClsidMFT,
+                   LPWSTR *aOutName,
+                   MFT_REGISTER_TYPE_INFO **aOutInputTypes,
+                   UINT32 *aOutNumInputTypes,
+                   MFT_REGISTER_TYPE_INFO **aOutOutputTypes,
+                   UINT32 *aOutNumOutputTypes,
+                   IMFAttributes **aOutAttributes)
+{
+  DECL_FUNCTION_PTR(MFTGetInfo, CLSID, LPWSTR*, MFT_REGISTER_TYPE_INFO**, UINT32*, MFT_REGISTER_TYPE_INFO**, UINT32*, IMFAttributes**);
+  ENSURE_FUNCTION_PTR(MFTGetInfo, Mfplat.dll)
+  return (MFTGetInfoPtr)(aClsidMFT,
+                         aOutName,
+                         aOutInputTypes,
+                         aOutNumInputTypes,
+                         aOutOutputTypes,
+                         aOutNumOutputTypes,
+                         aOutAttributes);
+}
+
+HRESULT MFGetStrideForBitmapInfoHeader(DWORD aFormat,
+                                       DWORD aWidth,
+                                       LONG *aOutStride)
+{
+  DECL_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, DWORD, DWORD, LONG*);
+  ENSURE_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, Mfplat.dll)
+  return (MFGetStrideForBitmapInfoHeaderPtr)(aFormat, aWidth, aOutStride);
+}
+
+HRESULT MFCreateSourceReaderFromURL(LPCWSTR aURL,
+                                    IMFAttributes *aAttributes,
+                                    IMFSourceReader **aSourceReader)
+{
+  DECL_FUNCTION_PTR(MFCreateSourceReaderFromURL, LPCWSTR, IMFAttributes*, IMFSourceReader**);
+  ENSURE_FUNCTION_PTR(MFCreateSourceReaderFromURL, Mfreadwrite.dll)
+  return (MFCreateSourceReaderFromURLPtr)(aURL, aAttributes, aSourceReader);
+}
+
+} // end namespace wmf
+} // end namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFUtils.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WMF.h"
+#include "nsString.h"
+
+// Various utilities shared by WMF backend files.
+
+namespace mozilla {
+
+nsCString GetGUIDName(const GUID& guid);
+
+// Returns true if the reader has a stream with the specified index.
+// Index can be a specific index, or one of:
+//   MF_SOURCE_READER_FIRST_VIDEO_STREAM
+//   MF_SOURCE_READER_FIRST_AUDIO_STREAM
+bool
+SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex);
+
+// Auto manages the lifecycle of a PROPVARIANT.
+class AutoPropVar {
+public:
+  AutoPropVar() {
+    PropVariantInit(&mVar);
+  }
+  ~AutoPropVar() {
+    PropVariantClear(&mVar);
+  }
+  operator PROPVARIANT&() {
+    return mVar;
+  }
+  PROPVARIANT* operator->() {
+    return &mVar;
+  }
+  PROPVARIANT* operator&() {
+    return &mVar;
+  }
+private:
+  PROPVARIANT mVar;
+};
+
+// Converts from microseconds to hundreds of nanoseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t UsecsToHNs(int64_t aUsecs) {
+  return aUsecs * 10;
+}
+
+// Converts from hundreds of nanoseconds to microseconds.
+// We use microseconds for our timestamps, whereas WMF uses
+// hundreds of nanoseconds.
+inline int64_t HNsToUsecs(int64_t hNanoSecs) {
+  return hNanoSecs / 10;
+}
+
+} // namespace mozilla
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -172,16 +172,22 @@ SHARED_LIBRARY_LIBS     += \
 endif
 
 ifdef MOZ_WEBM
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/webm/$(LIB_PREFIX)gkconwebm_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
+ifdef MOZ_WMF
+SHARED_LIBRARY_LIBS 	+= \
+	$(DEPTH)/content/media/wmf/$(LIB_PREFIX)gkconwmf_s.$(LIB_SUFFIX) \
+	$(NULL)
+endif
+
 ifdef MOZ_WAVE
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/wave/$(LIB_PREFIX)gkconwave_s.$(LIB_SUFFIX) \
 	$(NULL)
 endif
 
 ifdef MOZ_GSTREAMER
 SHARED_LIBRARY_LIBS 	+= \
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -74,16 +74,20 @@
 
 #include "nsHTMLEditor.h"
 #include "nsTextServicesDocument.h"
 
 #ifdef MOZ_MEDIA_PLUGINS
 #include "MediaPluginHost.h"
 #endif
 
+#ifdef MOZ_WMF
+#include "WMFDecoder.h"
+#endif
+
 #ifdef MOZ_SYDNEYAUDIO
 #include "AudioStream.h"
 #endif
 
 #include "nsError.h"
 
 #include "nsCycleCollector.h"
 #include "nsJSEnvironment.h"
@@ -329,25 +333,29 @@ nsLayoutStatics::Shutdown()
   nsDOMClassInfo::ShutDown();
   nsListControlFrame::Shutdown();
   nsXBLWindowKeyHandler::ShutDown();
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_MEDIA_PLUGINS
-  MediaPluginHost::Shutdown();  
+  MediaPluginHost::Shutdown();
 #endif
 
 #ifdef MOZ_SYDNEYAUDIO
   AudioStream::ShutdownLibrary();
 #endif
 
+#ifdef MOZ_WMF
+  WMFDecoder::UnloadDLLs();
+#endif
+
   nsCORSListenerProxy::Shutdown();
-  
+
   nsIPresShell::ReleaseStatics();
 
   nsTreeSanitizer::ReleaseStatics();
 
   nsHtml5Module::ReleaseStatics();
 
   nsRegion::ShutdownStatic();
 
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -71,16 +71,17 @@
 #define APPLICATION_MATHML_XML              "application/mathml+xml"
 #define APPLICATION_RDF_XML                 "application/rdf+xml"
 
 #define AUDIO_BASIC                         "audio/basic"
 #define AUDIO_OGG                           "audio/ogg"
 #define AUDIO_WAV                           "audio/x-wav"
 #define AUDIO_WEBM                          "audio/webm"
 #define AUDIO_MP3                           "audio/mpeg"
+#define AUDIO_MP4                           "audio/mp4"
 
 #define BINARY_OCTET_STREAM                 "binary/octet-stream"
 
 #define IMAGE_GIF                           "image/gif"
 #define IMAGE_JPG                           "image/jpeg"
 #define IMAGE_PJPG                          "image/pjpeg"
 #define IMAGE_PNG                           "image/png"
 #define IMAGE_PPM                           "image/x-portable-pixmap"
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -400,18 +400,20 @@ static nsDefaultMimeTypeEntry defaultMim
 #endif
 #ifdef MOZ_WEBM
   { VIDEO_WEBM, "webm" },
   { AUDIO_WEBM, "webm" },
 #endif
 #ifdef MOZ_DASH
   { APPLICATION_DASH, "mpd" },
 #endif
-#ifdef MOZ_GSTREAMER
+#if defined(MOZ_GSTREAMER) || defined(MOZ_WMF)
   { VIDEO_MP4, "mp4" },
+  { AUDIO_MP4, "m4a" },
+  { AUDIO_MP3, "mp3" },
 #endif
 #ifdef MOZ_RAW
   { VIDEO_RAW, "yuv" }
 #endif
 };
 
 /**
  * This is a small private struct used to help us initialize some
@@ -477,20 +479,19 @@ static nsExtraMimeTypeEntry extraMimeEnt
   { 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
-#if defined(MOZ_MEDIA_PLUGINS) || defined(MOZ_WIDGET_GONK)
   { AUDIO_MP3, "mp3", "MPEG Audio" },
-#endif
   { VIDEO_MP4, "mp4", "MPEG-4 Video" },
+  { AUDIO_MP4, "m4a", "MPEG-4 Audio" },
   { VIDEO_RAW, "yuv", "Raw YUV Video" },
   { AUDIO_WAV, "wav", "Waveform Audio" },
   { VIDEO_3GPP, "3gpp,3gp", "3GPP Video" }
 };
 
 #undef MAC_TYPE
 
 /**