Bug 962385 - Make WMF PlatformDecoderModule conform to new async interface. r=padenot
authorChris Pearce <cpearce@mozilla.com>
Wed, 05 Feb 2014 14:29:31 +1300
changeset 166874 144021ba1c5c2dff2230f4e4992d705674a546cb
parent 166873 8b3c4d6edf9a78d7d954640614f6ec1c7ae0252f
child 166875 b94e05c2de77f34ee6955180aa9915e12c5a9dfb
push id4853
push usercbook@mozilla.com
push dateWed, 05 Feb 2014 13:54:35 +0000
treeherderfx-team@0965914f979c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs962385
milestone30.0a1
Bug 962385 - Make WMF PlatformDecoderModule conform to new async interface. r=padenot
content/media/fmp4/moz.build
content/media/fmp4/wmf/MFTDecoder.h
content/media/fmp4/wmf/WMFAudioDecoder.cpp
content/media/fmp4/wmf/WMFAudioDecoder.h
content/media/fmp4/wmf/WMFAudioOutputSource.cpp
content/media/fmp4/wmf/WMFAudioOutputSource.h
content/media/fmp4/wmf/WMFDecoderModule.cpp
content/media/fmp4/wmf/WMFDecoderModule.h
content/media/fmp4/wmf/WMFMediaDataDecoder.cpp
content/media/fmp4/wmf/WMFMediaDataDecoder.h
content/media/fmp4/wmf/WMFVideoDecoder.cpp
content/media/fmp4/wmf/WMFVideoDecoder.h
content/media/fmp4/wmf/WMFVideoOutputSource.cpp
content/media/fmp4/wmf/WMFVideoOutputSource.h
--- a/content/media/fmp4/moz.build
+++ b/content/media/fmp4/moz.build
@@ -49,22 +49,24 @@ UNIFIED_SOURCES += [
     'MP4Decoder.cpp',
     'MP4Reader.cpp',
     'PlatformDecoderModule.cpp',
 ]
 
 if CONFIG['MOZ_WMF']:
   EXPORTS += [
       'wmf/MFTDecoder.h',
-      'wmf/WMFAudioDecoder.h',
+      'wmf/WMFAudioOutputSource.h',
       'wmf/WMFDecoderModule.h',
-      'wmf/WMFVideoDecoder.h',
+      'wmf/WMFMediaDataDecoder.h',
+      'wmf/WMFVideoOutputSource.h',
   ]
   UNIFIED_SOURCES += [
       'wmf/MFTDecoder.cpp',
-      'wmf/WMFAudioDecoder.cpp',
+      'wmf/WMFAudioOutputSource.cpp',
       'wmf/WMFDecoderModule.cpp',
-      'wmf/WMFVideoDecoder.cpp',
+      'wmf/WMFMediaDataDecoder.cpp',
+      'wmf/WMFVideoOutputSource.cpp',
   ]
 
 FINAL_LIBRARY = 'gklayout'
 
 FAIL_ON_WARNINGS = True
--- a/content/media/fmp4/wmf/MFTDecoder.h
+++ b/content/media/fmp4/wmf/MFTDecoder.h
@@ -9,17 +9,17 @@
 
 #include "WMF.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIThread.h"
 
 namespace mozilla {
 
-class MFTDecoder {
+class MFTDecoder : public AtomicRefCounted<MFTDecoder> {
 public:
   MFTDecoder();
   ~MFTDecoder();
 
   // Creates the MFT. First thing to do as part of setup.
   //
   // Params:
   //  - aMFTClsID the clsid used by CoCreateInstance to instantiate the
rename from content/media/fmp4/wmf/WMFAudioDecoder.cpp
rename to content/media/fmp4/wmf/WMFAudioOutputSource.cpp
--- a/content/media/fmp4/wmf/WMFAudioDecoder.cpp
+++ b/content/media/fmp4/wmf/WMFAudioOutputSource.cpp
@@ -1,29 +1,28 @@
 /* -*- 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 "WMFAudioDecoder.h"
+#include "WMFAudioOutputSource.h"
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
 
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog();
 #define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define LOG(...)
 #endif
 
-
 namespace mozilla {
 
 static void
 AACAudioSpecificConfigToUserData(const uint8_t* aAudioSpecConfig,
                                  uint32_t aConfigLength,
                                  nsTArray<BYTE>& aOutUserData)
 {
   MOZ_ASSERT(aOutUserData.IsEmpty());
@@ -61,125 +60,97 @@ AACAudioSpecificConfigToUserData(const u
   WORD* w = (WORD*)heeInfo;
   w[0] = 0x1; // Payload type ADTS
   w[1] = 0xFE; // Profile level indication, none specified.
 
   aOutUserData.AppendElements(heeInfo, heeInfoLen);
   aOutUserData.AppendElements(aAudioSpecConfig, aConfigLength);
 }
 
-WMFAudioDecoder::WMFAudioDecoder(uint32_t aChannelCount,
-                                 uint32_t aSampleRate,
-                                 uint16_t aBitsPerSample,
-                                 const uint8_t* aAudioSpecConfig,
-                                 uint32_t aConfigLength)
-  : mAudioChannels(aChannelCount),
-    mAudioBytesPerSample(aBitsPerSample / 8),
-    mAudioRate(aSampleRate),
-    mLastStreamOffset(0),
-    mAudioFrameOffset(0),
-    mAudioFrameSum(0),
-    mMustRecaptureAudioPosition(true)
+WMFAudioOutputSource::WMFAudioOutputSource(const mp4_demuxer::AudioDecoderConfig& aConfig)
+  : mAudioChannels(ChannelLayoutToChannelCount(aConfig.channel_layout()))
+  , mAudioBytesPerSample(aConfig.bits_per_channel() / 8)
+  , mAudioRate(aConfig.samples_per_second())
+  , mAudioFrameOffset(0)
+  , mAudioFrameSum(0)
+  , mMustRecaptureAudioPosition(true)
 {
-  AACAudioSpecificConfigToUserData(aAudioSpecConfig,
-                                   aConfigLength,
+  MOZ_COUNT_CTOR(WMFAudioOutputSource);
+  AACAudioSpecificConfigToUserData(aConfig.extra_data(),
+                                   aConfig.extra_data_size(),
                                    mUserData);
 }
 
-nsresult
-WMFAudioDecoder::Init()
+WMFAudioOutputSource::~WMFAudioOutputSource()
 {
-  mDecoder = new MFTDecoder();
+  MOZ_COUNT_DTOR(WMFAudioOutputSource);
+}
 
-  HRESULT hr = mDecoder->Create(CLSID_CMSAACDecMFT);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+TemporaryRef<MFTDecoder>
+WMFAudioOutputSource::Init()
+{
+  RefPtr<MFTDecoder> decoder(new MFTDecoder());
+
+  HRESULT hr = decoder->Create(CLSID_CMSAACDecMFT);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   // Setup input/output media types
   RefPtr<IMFMediaType> type;
 
   hr = wmf::MFCreateMediaType(byRef(type));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, mAudioRate);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mAudioChannels);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0x1); // ADTS
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetBlob(MF_MT_USER_DATA,
                      mUserData.Elements(),
                      mUserData.Length());
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
-
-  hr = mDecoder->SetMediaTypes(type, MFAudioFormat_PCM);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
-  return NS_OK;
-}
+  hr = decoder->SetMediaTypes(type, MFAudioFormat_PCM);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
-nsresult
-WMFAudioDecoder::Shutdown()
-{
-  return NS_OK;
+  mDecoder = decoder;
+
+  return decoder.forget();
 }
 
-DecoderStatus
-WMFAudioDecoder::Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample)
+HRESULT
+WMFAudioOutputSource::Output(int64_t aStreamOffset,
+                        nsAutoPtr<MediaData>& aOutData)
 {
-  mLastStreamOffset = aSample->byte_offset;
-  const uint8_t* data = &aSample->data->front();
-  uint32_t length = aSample->data->size();
-  HRESULT hr = mDecoder->Input(data, length, aSample->composition_timestamp);
-  if (hr == MF_E_NOTACCEPTING) {
-    return DECODE_STATUS_NOT_ACCEPTING;
-  }
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
-
-  return DECODE_STATUS_OK;
-}
-
-DecoderStatus
-WMFAudioDecoder::Output(nsAutoPtr<MediaData>& aOutData)
-{
-  DecoderStatus status;
-  do {
-    status = OutputNonNegativeTimeSamples(aOutData);
-  } while (status == DECODE_STATUS_OK && !aOutData);
-  return status;
-}
-
-DecoderStatus
-WMFAudioDecoder::OutputNonNegativeTimeSamples(nsAutoPtr<MediaData>& aOutData)
-{
-
   aOutData = nullptr;
   RefPtr<IMFSample> sample;
   HRESULT hr = mDecoder->Output(&sample);
   if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
-    return DECODE_STATUS_NEED_MORE_INPUT;
+    return MF_E_TRANSFORM_NEED_MORE_INPUT;
   }
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   RefPtr<IMFMediaBuffer> buffer;
   hr = sample->ConvertToContiguousBuffer(byRef(buffer));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   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), DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   int32_t numSamples = currentLength / mAudioBytesPerSample;
   int32_t numFrames = numSamples / mAudioChannels;
 
   // Sometimes when starting decoding, the AAC decoder gives us samples
   // with a negative timestamp. AAC does usually have preroll (or encoder
   // delay) encoded into its bitstream, but the amount encoded to the stream
   // is variable, and it not signalled in-bitstream. There is sometimes
@@ -198,19 +169,19 @@ WMFAudioDecoder::OutputNonNegativeTimeSa
   // will be offset from this block's timestamp.
   UINT32 discontinuity = false;
   int32_t numFramesToStrip = 0;
   sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity);
   if (mMustRecaptureAudioPosition || discontinuity) {
     mAudioFrameSum = 0;
     LONGLONG timestampHns = 0;
     hr = sample->GetSampleTime(&timestampHns);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
     hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
     if (mAudioFrameOffset < 0) {
       // First sample has a negative timestamp. Strip off the samples until
       // we reach positive territory.
       numFramesToStrip = -mAudioFrameOffset;
       mAudioFrameOffset = 0;
     }
     mMustRecaptureAudioPosition = false;
   }
@@ -218,17 +189,17 @@ WMFAudioDecoder::OutputNonNegativeTimeSa
   int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);
   numFrames -= offset;
   numSamples -= offset * mAudioChannels;
   MOZ_ASSERT(numFrames >= 0);
   MOZ_ASSERT(numSamples >= 0);
   if (numFrames == 0) {
     // All data from this chunk stripped, loop back and try to output the next
     // frame, if possible.
-    return DECODE_STATUS_OK;
+    return S_OK;
   }
 
   nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[numSamples]);
 
   // Just assume PCM output for now...
   MOZ_ASSERT(mAudioBytesPerSample == 2);
   int16_t* pcm = ((int16_t*)data) + (offset * mAudioChannels);
   MOZ_ASSERT(pcm >= (int16_t*)data);
@@ -236,41 +207,32 @@ WMFAudioDecoder::OutputNonNegativeTimeSa
   MOZ_ASSERT(pcm+numSamples <= (int16_t*)(data + currentLength));
   for (int32_t i = 0; i < numSamples; ++i) {
     audioData[i] = AudioSampleToFloat(pcm[i]);
   }
 
   buffer->Unlock();
   int64_t timestamp;
   hr = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate, &timestamp);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   mAudioFrameSum += numFrames;
 
   int64_t duration;
   hr = FramesToUsecs(numFrames, mAudioRate, &duration);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  aOutData = new AudioData(mLastStreamOffset,
-                            timestamp,
-                            duration,
-                            numFrames,
-                            audioData.forget(),
-                            mAudioChannels);
+  aOutData = new AudioData(aStreamOffset,
+                           timestamp,
+                           duration,
+                           numFrames,
+                           audioData.forget(),
+                           mAudioChannels);
 
   #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
       timestamp, duration, currentLength);
   #endif
 
-  return DECODE_STATUS_OK;
-}
-
-DecoderStatus
-WMFAudioDecoder::Flush()
-{
-  NS_ENSURE_TRUE(mDecoder, DECODE_STATUS_ERROR);
-  HRESULT hr = mDecoder->Flush();
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
-  return DECODE_STATUS_OK;
+  return S_OK;
 }
 
 } // namespace mozilla
rename from content/media/fmp4/wmf/WMFAudioDecoder.h
rename to content/media/fmp4/wmf/WMFAudioOutputSource.h
--- a/content/media/fmp4/wmf/WMFAudioDecoder.h
+++ b/content/media/fmp4/wmf/WMFAudioOutputSource.h
@@ -1,74 +1,55 @@
 /* -*- 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(WMFAudioDecoder_h_)
-#define WMFAudioDecoder_h_
+#if !defined(WMFAudioOutputSource_h_)
+#define WMFAudioOutputSource_h_
 
 #include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
+#include "mozilla/RefPtr.h"
+#include "WMFMediaDataDecoder.h"
 
 namespace mozilla {
 
-class WMFAudioDecoder : public MediaDataDecoder {
+class WMFAudioOutputSource : public WMFOutputSource {
 public:
-  WMFAudioDecoder(uint32_t aChannelCount,
-                  uint32_t aSampleRate,
-                  uint16_t aBitsPerSample,
-                  const uint8_t* aUserData,
-                  uint32_t aUserDataLength);
-
-  virtual nsresult Init() MOZ_OVERRIDE;
+  WMFAudioOutputSource(const mp4_demuxer::AudioDecoderConfig& aConfig);
+  ~WMFAudioOutputSource();
 
-  virtual nsresult Shutdown() MOZ_OVERRIDE;
-
-  // Inserts data into the decoder's pipeline.
-  virtual DecoderStatus Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample);
+  virtual TemporaryRef<MFTDecoder> Init() MOZ_OVERRIDE;
 
-  // Blocks until a decoded sample is produced by the decoder.
-  virtual DecoderStatus Output(nsAutoPtr<MediaData>& aOutData);
-
-  virtual DecoderStatus Flush() MOZ_OVERRIDE;
-
+  // Note WMF's AAC decoder sometimes output negatively timestamped samples,
+  // presumably they're the preroll samples, and we strip them. We may return
+  // a null aOutput in this case.
+  virtual HRESULT Output(int64_t aStreamOffset,
+                         nsAutoPtr<MediaData>& aOutput) MOZ_OVERRIDE;
 private:
 
-
-  // A helper for Output() above. This has the same interface as Output()
-  // above, except that it returns DECODE_STATUS_OK and sets aOutData to
-  // nullptr when all the output samples have been stripped due to having
-  // negative timestamps. WMF's AAC decoder sometimes output negatively
-  // timestamped samples, presumably they're the preroll samples, and we
-  // strip them.
-  DecoderStatus OutputNonNegativeTimeSamples(nsAutoPtr<MediaData>& aOutData);
+  // IMFTransform wrapper that performs the decoding.
+  RefPtr<MFTDecoder> mDecoder;
 
-  nsAutoPtr<MFTDecoder> mDecoder;
-
-  uint32_t mAudioChannels;
-  uint32_t mAudioBytesPerSample;
-  uint32_t mAudioRate;
+  const uint32_t mAudioChannels;
+  const uint32_t mAudioBytesPerSample;
+  const uint32_t mAudioRate;
   nsTArray<BYTE> mUserData;
 
-  // The last offset into the media resource that was passed into Input().
-  // This is used to approximate the decoder's position in the media resource.
-  int64_t mLastStreamOffset;
-
   // The offset, in audio frames, at which playback started since the
   // last discontinuity.
   int64_t mAudioFrameOffset;
   // The number of audio frames that we've played since the last
   // discontinuity.
   int64_t mAudioFrameSum;
+
   // True if we need to re-initialize mAudioFrameOffset and mAudioFrameSum
   // from the next audio packet we decode. This happens after a seek, since
   // WMF doesn't mark a stream as having a discontinuity after a seek(0).
   bool mMustRecaptureAudioPosition;
 };
 
-
-
 } // namespace mozilla
 
-#endif
+#endif // WMFAudioOutputSource_h_
--- a/content/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/content/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -2,21 +2,22 @@
 /* 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 "WMFDecoderModule.h"
 #include "WMFDecoder.h"
-#include "WMFVideoDecoder.h"
-#include "WMFAudioDecoder.h"
+#include "WMFVideoOutputSource.h"
+#include "WMFAudioOutputSource.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
 #include "mp4_demuxer/audio_decoder_config.h"
+#include "WMFMediaDataDecoder.h"
 
 namespace mozilla {
 
 bool WMFDecoderModule::sIsWMFEnabled = false;
 bool WMFDecoderModule::sDXVAEnabled = false;
 
 WMFDecoderModule::WMFDecoderModule()
 {
@@ -63,45 +64,30 @@ WMFDecoderModule::Shutdown()
   NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
 
   return NS_OK;
 }
 
 MediaDataDecoder*
 WMFDecoderModule::CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                                     mozilla::layers::LayersBackend aLayersBackend,
-                                    mozilla::layers::ImageContainer* aImageContainer)
+                                    mozilla::layers::ImageContainer* aImageContainer,
+                                    MediaTaskQueue* aVideoTaskQueue,
+                                    MediaDataDecoderCallback* aCallback)
 {
-  return new WMFVideoDecoder(aLayersBackend,
-                             aImageContainer,
-                             sDXVAEnabled);
+  return new WMFMediaDataDecoder(new WMFVideoOutputSource(aLayersBackend,
+                                                          aImageContainer,
+                                                          sDXVAEnabled),
+                                 aVideoTaskQueue,
+                                 aCallback);
 }
 
 MediaDataDecoder*
-WMFDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig)
-{
-  return new WMFAudioDecoder(ChannelLayoutToChannelCount(aConfig.channel_layout()),
-                             aConfig.samples_per_second(),
-                             aConfig.bits_per_channel(),
-                             aConfig.extra_data(),
-                             aConfig.extra_data_size());
-}
-
-void
-WMFDecoderModule::OnDecodeThreadStart()
+WMFDecoderModule::CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
+                                   MediaTaskQueue* aAudioTaskQueue,
+                                   MediaDataDecoderCallback* aCallback)
 {
-  MOZ_ASSERT(!NS_IsMainThread(), "Must not be on main thread.");
-  // XXX WebAudio can call this on the main thread when using deprecated APIs.
-  // That should not happen. You cannot change the concurrency model once already set.
-  // The main thread will continue to be STA, which seems to work, but MSDN
-  // recommends that MTA be used.
-  // TODO: enforce that WebAudio stops doing that!
-  CoInitializeEx(0, COINIT_MULTITHREADED);
-}
-
-void
-WMFDecoderModule::OnDecodeThreadFinish()
-{
-  MOZ_ASSERT(!NS_IsMainThread(), "Must be on main thread.");
-  CoUninitialize();
+  return new WMFMediaDataDecoder(new WMFAudioOutputSource(aConfig),
+                                 aAudioTaskQueue,
+                                 aCallback);
 }
 
 } // namespace mozilla
--- a/content/media/fmp4/wmf/WMFDecoderModule.h
+++ b/content/media/fmp4/wmf/WMFDecoderModule.h
@@ -23,25 +23,25 @@ public:
   // Called when the decoders have shutdown. Main thread only.
   // Does this really need to be main thread only????
   virtual nsresult Shutdown() MOZ_OVERRIDE;
 
   // Decode thread.
   virtual MediaDataDecoder*
   CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                     mozilla::layers::LayersBackend aLayersBackend,
-                    mozilla::layers::ImageContainer* aImageContainer) MOZ_OVERRIDE;
+                    mozilla::layers::ImageContainer* aImageContainer,
+                    MediaTaskQueue* aVideoTaskQueue,
+                    MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
 
   // Decode thread.
   virtual MediaDataDecoder* CreateAACDecoder(
-    const mp4_demuxer::AudioDecoderConfig& aConfig) MOZ_OVERRIDE;
-
-  // Platform decoders can override these. Base implementation does nothing.
-  virtual void OnDecodeThreadStart() MOZ_OVERRIDE;
-  virtual void OnDecodeThreadFinish() MOZ_OVERRIDE;
+    const mp4_demuxer::AudioDecoderConfig& aConfig,
+    MediaTaskQueue* aAudioTaskQueue,
+    MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
 
   static void Init();
 private:
   static bool sIsWMFEnabled;
   static bool sDXVAEnabled;
 };
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -0,0 +1,138 @@
+/* -*- 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 "WMFMediaDataDecoder.h"
+#include "VideoUtils.h"
+#include "WMFUtils.h"
+#include "nsTArray.h"
+
+#include "prlog.h"
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* GetDemuxerLog();
+#define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+
+namespace mozilla {
+
+WMFMediaDataDecoder::WMFMediaDataDecoder(WMFOutputSource* aSource,
+                                         MediaTaskQueue* aTaskQueue,
+                                         MediaDataDecoderCallback* aCallback)
+  : mTaskQueue(aTaskQueue)
+  , mCallback(aCallback)
+  , mSource(aSource)
+{
+  MOZ_COUNT_CTOR(WMFMediaDataDecoder);
+}
+
+WMFMediaDataDecoder::~WMFMediaDataDecoder()
+{
+  MOZ_COUNT_DTOR(WMFMediaDataDecoder);
+}
+
+nsresult
+WMFMediaDataDecoder::Init()
+{
+  mDecoder = mSource->Init();
+  NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+nsresult
+WMFMediaDataDecoder::Shutdown()
+{
+  mDecoder = nullptr;
+  return NS_OK;
+}
+
+// Inserts data into the decoder's pipeline.
+nsresult
+WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
+{
+  mTaskQueue->Dispatch(
+    NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
+      this,
+      &WMFMediaDataDecoder::ProcessDecode,
+      nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+  return NS_OK;
+}
+
+void
+WMFMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
+{
+  const uint8_t* data = &aSample->data->front();
+  uint32_t length = aSample->data->size();
+  HRESULT hr = mDecoder->Input(data, length, aSample->composition_timestamp);
+  if (FAILED(hr)) {
+    NS_WARNING("WMFAudioDecoder failed to input data");
+    mCallback->Error();
+    return;
+  }
+
+  mLastStreamOffset = aSample->byte_offset;
+
+  ProcessOutput();
+}
+
+void
+WMFMediaDataDecoder::ProcessOutput()
+{
+  nsAutoPtr<MediaData> output;
+  HRESULT hr = S_OK;
+  while (SUCCEEDED(hr = mSource->Output(mLastStreamOffset, output)) &&
+         output) {
+    mCallback->Output(output.forget());
+  }
+  if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+    if (mTaskQueue->IsEmpty()) {
+      mCallback->InputExhausted();
+    }
+  } else if (FAILED(hr)) {
+    NS_WARNING("WMFMediaDataDecoder failed to output data");
+    mCallback->Error();
+  }
+}
+
+nsresult
+WMFMediaDataDecoder::Flush()
+{
+  // Flush the input task queue. This cancels all pending Decode() calls.
+  // Note this blocks until the task queue finishes its current job, if
+  // it's executing at all. Note the MP4Reader ignores all output while
+  // flushing.
+  mTaskQueue->Flush();
+
+  // Order the MFT to flush; drop all internal data.
+  NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
+  HRESULT hr = mDecoder->Flush();
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+void
+WMFMediaDataDecoder::ProcessDrain()
+{
+  // Order the decoder to drain...
+  if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
+    NS_WARNING("Failed to send DRAIN command to audio MFT");
+  }
+  // Then extract all available output.
+  ProcessOutput();
+}
+
+nsresult
+WMFMediaDataDecoder::Drain()
+{
+  mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain));
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -0,0 +1,88 @@
+/* -*- 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(WMFMediaDataDecoder_h_)
+#define WMFMediaDataDecoder_h_
+
+
+#include "WMF.h"
+#include "MP4Reader.h"
+#include "MFTDecoder.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+// Encapsulates the initialization of the MFTDecoder appropriate for decoding
+// a given stream, and the process of converting the IMFSample produced
+// by the MFT into a MediaData object.
+class WMFOutputSource {
+public:
+  virtual ~WMFOutputSource() {}
+
+  // Creates an initializs the MFTDecoder.
+  // Returns nullptr on failure.
+  virtual TemporaryRef<MFTDecoder> Init() = 0;
+
+  // Produces decoded output, if possible. Blocks until output can be produced,
+  // or until no more is able to be produced.
+  // Returns S_OK on success, or MF_E_TRANSFORM_NEED_MORE_INPUT if there's not
+  // enough data to produce more output. If this returns a failure code other
+  // than MF_E_TRANSFORM_NEED_MORE_INPUT, an error will be reported to the
+  // MP4Reader.
+  virtual HRESULT Output(int64_t aStreamOffset,
+                         nsAutoPtr<MediaData>& aOutput) = 0;
+};
+
+// Decodes audio and video using Windows Media Foundation. Samples are decoded
+// using the MFTDecoder created by the WMFOutputSource. This class implements
+// the higher-level logic that drives mapping the MFT to the async
+// MediaDataDecoder interface. The specifics of decoding the exact stream
+// type are handled by WMFOutputSource and the MFTDecoder it creates.
+class WMFMediaDataDecoder : public MediaDataDecoder {
+public:
+  WMFMediaDataDecoder(WMFOutputSource* aOutputSource,
+                      MediaTaskQueue* aAudioTaskQueue,
+                      MediaDataDecoderCallback* aCallback);
+  ~WMFMediaDataDecoder();
+
+  virtual nsresult Init() MOZ_OVERRIDE;
+
+  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
+
+  virtual nsresult Flush() MOZ_OVERRIDE;
+
+  virtual nsresult Drain() MOZ_OVERRIDE;
+
+  virtual nsresult Shutdown() MOZ_OVERRIDE;
+
+private:
+
+  // Called on the task queue. Inserts the sample into the decoder, and
+  // extracts output if available.
+  void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
+
+  // Called on the task queue. Extracts output if available, and delivers
+  // it to the reader. Called after ProcessDecode() and ProcessDrain().
+  void ProcessOutput();
+
+  // Called on the task queue. Orders the MFT to drain, and then extracts
+  // all available output.
+  void ProcessDrain();
+
+  RefPtr<MediaTaskQueue> mTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+
+  RefPtr<MFTDecoder> mDecoder;
+  nsAutoPtr<WMFOutputSource> mSource;
+
+  // The last offset into the media resource that was passed into Input().
+  // This is used to approximate the decoder's position in the media resource.
+  int64_t mLastStreamOffset;
+};
+
+} // namespace mozilla
+
+#endif // WMFMediaDataDecoder_h_
rename from content/media/fmp4/wmf/WMFVideoDecoder.cpp
rename to content/media/fmp4/wmf/WMFVideoOutputSource.cpp
--- a/content/media/fmp4/wmf/WMFVideoDecoder.cpp
+++ b/content/media/fmp4/wmf/WMFVideoOutputSource.cpp
@@ -1,15 +1,15 @@
 /* -*- 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 "WMFVideoDecoder.h"
+#include "WMFVideoOutputSource.h"
 #include "MediaDecoderReader.h"
 #include "WMFUtils.h"
 #include "ImageContainer.h"
 #include "VideoUtils.h"
 #include "DXVA2Manager.h"
 #include "nsThreadUtils.h"
 #include "Layers.h"
 #include "mozilla/layers/LayersTypes.h"
@@ -23,50 +23,49 @@ PRLogModuleInfo* GetDemuxerLog();
 #endif
 
 using mozilla::layers::Image;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
 
 namespace mozilla {
 
-WMFVideoDecoder::WMFVideoDecoder(mozilla::layers::LayersBackend aLayersBackend,
+WMFVideoOutputSource::WMFVideoOutputSource(mozilla::layers::LayersBackend aLayersBackend,
                                  mozilla::layers::ImageContainer* aImageContainer,
                                  bool aDXVAEnabled)
-  : mVideoStride(0),
-    mVideoWidth(0),
-    mVideoHeight(0),
-    mLastStreamOffset(0),
-    mImageContainer(aImageContainer),
-    mDXVAEnabled(aDXVAEnabled),
-    mLayersBackend(aLayersBackend),
-    mUseHwAccel(false)
+  : mVideoStride(0)
+  , mVideoWidth(0)
+  , mVideoHeight(0)
+  , mImageContainer(aImageContainer)
+  , mDXVAEnabled(aDXVAEnabled)
+  , mLayersBackend(aLayersBackend)
+  , mUseHwAccel(false)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
   MOZ_ASSERT(mImageContainer);
-  MOZ_COUNT_CTOR(WMFVideoDecoder);
+  MOZ_COUNT_CTOR(WMFVideoOutputSource);
 }
 
-WMFVideoDecoder::~WMFVideoDecoder()
+WMFVideoOutputSource::~WMFVideoOutputSource()
 {
-  MOZ_COUNT_DTOR(WMFVideoDecoder);
+  MOZ_COUNT_DTOR(WMFVideoOutputSource);
 }
 
 class CreateDXVAManagerEvent : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
     mDXVA2Manager = DXVA2Manager::Create();
     return NS_OK;
   }
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
 };
 
 bool
-WMFVideoDecoder::InitializeDXVA()
+WMFVideoOutputSource::InitializeDXVA()
 {
   // If we use DXVA but aren't running with a D3D layer manager then the
   // readback of decoded video frames from GPU to CPU memory grinds painting
   // to a halt, and makes playback performance *worse*.
   if (!mDXVAEnabled ||
       (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
        mLayersBackend != LayersBackend::LAYERS_D3D10)) {
     return false;
@@ -75,71 +74,72 @@ WMFVideoDecoder::InitializeDXVA()
   // The DXVA manager must be created on the main thread.
   nsRefPtr<CreateDXVAManagerEvent> event(new CreateDXVAManagerEvent());
   NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
   mDXVA2Manager = event->mDXVA2Manager;
 
   return mDXVA2Manager != nullptr;
 }
 
-nsresult
-WMFVideoDecoder::Init()
+TemporaryRef<MFTDecoder>
+WMFVideoOutputSource::Init()
 {
   bool useDxva = InitializeDXVA();
 
-  mDecoder = new MFTDecoder();
+  RefPtr<MFTDecoder> decoder(new MFTDecoder());
 
-  HRESULT hr = mDecoder->Create(CLSID_CMSH264DecoderMFT);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  HRESULT hr = decoder->Create(CLSID_CMSH264DecoderMFT);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   if (useDxva) {
-    RefPtr<IMFAttributes> attr(mDecoder->GetAttributes());
+    RefPtr<IMFAttributes> attr(decoder->GetAttributes());
 
     UINT32 aware = 0;
     if (attr) {
       attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
     }
     if (aware) {
       // TODO: Test if I need this anywhere... Maybe on Vista?
       //hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
       //NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
       MOZ_ASSERT(mDXVA2Manager);
       ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
-      hr = mDecoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
+      hr = decoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
       if (SUCCEEDED(hr)) {
         mUseHwAccel = true;
       }
     }
   }
 
   // Setup the input/output media types.
   RefPtr<IMFMediaType> type;
   hr = wmf::MFCreateMediaType(byRef(type));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   hr = type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_MixedInterlaceOrProgressive);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
   GUID outputType = mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12;
-  hr = mDecoder->SetMediaTypes(type, outputType);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+  hr = decoder->SetMediaTypes(type, outputType);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
+  mDecoder = decoder;
   LOG("Video Decoder initialized, Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
 
-  return NS_OK;
+  return decoder.forget();
 }
 
 HRESULT
-WMFVideoDecoder::ConfigureVideoFrameGeometry()
+WMFVideoOutputSource::ConfigureVideoFrameGeometry()
 {
   RefPtr<IMFMediaType> mediaType;
   HRESULT hr = mDecoder->GetOutputMediaType(mediaType);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   // Verify that the video subtype is what we expect it to be.
   // When using hardware acceleration/DXVA2 the video format should
   // be NV12, which is DXVA2's preferred format. For software decoding
@@ -189,40 +189,19 @@ WMFVideoDecoder::ConfigureVideoFrameGeom
       mVideoStride,
       mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height,
       displaySize.width, displaySize.height,
       aspectNum, aspectDenom);
 
   return S_OK;
 }
 
-nsresult
-WMFVideoDecoder::Shutdown()
-{
-  return NS_OK;
-}
-
-// Inserts data into the decoder's pipeline.
-DecoderStatus
-WMFVideoDecoder::Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample)
-{
-  mLastStreamOffset = aSample->byte_offset;
-  const uint8_t* data = &aSample->data->front();
-  uint32_t length = aSample->data->size();
-  HRESULT hr = mDecoder->Input(data, length, aSample->composition_timestamp);
-  if (hr == MF_E_NOTACCEPTING) {
-    return DECODE_STATUS_NOT_ACCEPTING;
-  }
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
-
-  return DECODE_STATUS_OK;
-}
-
 HRESULT
-WMFVideoDecoder::CreateBasicVideoFrame(IMFSample* aSample,
+WMFVideoOutputSource::CreateBasicVideoFrame(IMFSample* aSample,
+                                       int64_t aStreamOffset,
                                        VideoData** aOutVideoData)
 {
   NS_ENSURE_TRUE(aSample, E_POINTER);
   NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
 
   *aOutVideoData = nullptr;
 
   HRESULT hr;
@@ -287,17 +266,17 @@ WMFVideoDecoder::CreateBasicVideoFrame(I
   b.mPlanes[2].mWidth = halfWidth;
   b.mPlanes[2].mOffset = 0;
   b.mPlanes[2].mSkip = 0;
 
   Microseconds pts = GetSampleTime(aSample);
   Microseconds duration = GetSampleDuration(aSample);
   VideoData *v = VideoData::Create(mVideoInfo,
                                    mImageContainer,
-                                   mLastStreamOffset,
+                                   aStreamOffset,
                                    pts,
                                    duration,
                                    b,
                                    false,
                                    -1,
                                    mPictureRegion);
   if (twoDBuffer) {
     twoDBuffer->Unlock2D();
@@ -306,17 +285,18 @@ WMFVideoDecoder::CreateBasicVideoFrame(I
   }
 
   *aOutVideoData = v;
 
   return S_OK;
 }
 
 HRESULT
-WMFVideoDecoder::CreateD3DVideoFrame(IMFSample* aSample,
+WMFVideoOutputSource::CreateD3DVideoFrame(IMFSample* aSample,
+                                     int64_t aStreamOffset,
                                      VideoData** aOutVideoData)
 {
   NS_ENSURE_TRUE(aSample, E_POINTER);
   NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
   NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
   NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
 
   *aOutVideoData = nullptr;
@@ -329,80 +309,73 @@ WMFVideoDecoder::CreateD3DVideoFrame(IMF
                                   getter_AddRefs(image));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   NS_ENSURE_TRUE(image, E_FAIL);
 
   Microseconds pts = GetSampleTime(aSample);
   Microseconds duration = GetSampleDuration(aSample);
   VideoData *v = VideoData::CreateFromImage(mVideoInfo,
                                             mImageContainer,
-                                            mLastStreamOffset,
+                                            aStreamOffset,
                                             pts,
                                             duration,
                                             image.forget(),
                                             false,
                                             -1,
                                             mPictureRegion);
 
   NS_ENSURE_TRUE(v, E_FAIL);
   *aOutVideoData = v;
 
   return S_OK;
 }
 
 // Blocks until decoded sample is produced by the deoder.
-DecoderStatus
-WMFVideoDecoder::Output(nsAutoPtr<MediaData>& aOutData)
+HRESULT
+WMFVideoOutputSource::Output(int64_t aStreamOffset,
+                        nsAutoPtr<MediaData>& aOutData)
 {
   RefPtr<IMFSample> sample;
   HRESULT hr;
+  aOutData = nullptr;
 
   // Loop until we decode a sample, or an unexpected error that we can't
   // handle occurs.
   while (true) {
     hr = mDecoder->Output(&sample);
-    if (SUCCEEDED(hr)) {
-      NS_ENSURE_TRUE(sample, DECODE_STATUS_ERROR);
-      break;
-    }
     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
-      return DECODE_STATUS_NEED_MORE_INPUT;
+      return MF_E_TRANSFORM_NEED_MORE_INPUT;
     }
     if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
       // Video stream output type change. Probably a geometric apperature
       // change. Reconfigure the video geometry, so that we output the
       // correct size frames.
       MOZ_ASSERT(!sample);
       hr = ConfigureVideoFrameGeometry();
-      NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
+      NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
       // Loop back and try decoding again...
       continue;
     }
+    if (SUCCEEDED(hr)) {
+      break;
+    }
     // Else unexpected error, assert, and bail.
-    NS_WARNING("WMFVideoDecoder::Output() unexpected error");
-    return DECODE_STATUS_ERROR;
+    NS_WARNING("WMFVideoOutputSource::Output() unexpected error");
+    return E_FAIL;
   }
 
   VideoData* frame = nullptr;
   if (mUseHwAccel) {
-    hr = CreateD3DVideoFrame(sample, &frame);
+    hr = CreateD3DVideoFrame(sample, aStreamOffset, &frame);
   } else {
-    hr = CreateBasicVideoFrame(sample, &frame);
+    hr = CreateBasicVideoFrame(sample, aStreamOffset, &frame);
   }
   // Frame should be non null only when we succeeded.
   MOZ_ASSERT((frame != nullptr) == SUCCEEDED(hr));
-  NS_ENSURE_TRUE(SUCCEEDED(hr) && frame, DECODE_STATUS_ERROR);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  NS_ENSURE_TRUE(frame, E_FAIL);
 
   aOutData = frame;
 
-  return DECODE_STATUS_OK;
-}
-
-DecoderStatus
-WMFVideoDecoder::Flush()
-{
-  NS_ENSURE_TRUE(mDecoder, DECODE_STATUS_ERROR);
-  HRESULT hr = mDecoder->Flush();
-  NS_ENSURE_TRUE(SUCCEEDED(hr), DECODE_STATUS_ERROR);
-  return DECODE_STATUS_OK;
+  return S_OK;
 }
 
 } // namespace mozilla
rename from content/media/fmp4/wmf/WMFVideoDecoder.h
rename to content/media/fmp4/wmf/WMFVideoOutputSource.h
--- a/content/media/fmp4/wmf/WMFVideoDecoder.h
+++ b/content/media/fmp4/wmf/WMFVideoOutputSource.h
@@ -1,76 +1,67 @@
 /* -*- 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(WMFVideoDecoder_h_)
-#define WMFVideoDecoder_h_
+#if !defined(WMFVideoOutputSource_h_)
+#define WMFVideoOutputSource_h_
 
 #include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 #include "nsRect.h"
-
+#include "WMFMediaDataDecoder.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 
 class DXVA2Manager;
 
-class WMFVideoDecoder : public MediaDataDecoder {
+class WMFVideoOutputSource : public WMFOutputSource {
 public:
-  WMFVideoDecoder(mozilla::layers::LayersBackend aLayersBackend,
-                  mozilla::layers::ImageContainer* aImageContainer,
-                  bool aDXVAEnabled);
-  ~WMFVideoDecoder();
-
-  // Decode thread.
-  virtual nsresult Init() MOZ_OVERRIDE;
+  WMFVideoOutputSource(mozilla::layers::LayersBackend aLayersBackend,
+                       mozilla::layers::ImageContainer* aImageContainer,
+                       bool aDXVAEnabled);
+  ~WMFVideoOutputSource();
 
-  virtual nsresult Shutdown() MOZ_OVERRIDE;
-
-  // Inserts data into the decoder's pipeline.
-  virtual DecoderStatus Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample) MOZ_OVERRIDE;
+  virtual TemporaryRef<MFTDecoder> Init() MOZ_OVERRIDE;
 
-  // Blocks until a decoded sample is produced by the decoder.
-  virtual DecoderStatus Output(nsAutoPtr<MediaData>& aOutData) MOZ_OVERRIDE;
-
-  virtual DecoderStatus Flush() MOZ_OVERRIDE;
+  virtual HRESULT Output(int64_t aStreamOffset,
+                         nsAutoPtr<MediaData>& aOutput) MOZ_OVERRIDE;
 
 private:
 
   bool InitializeDXVA();
 
   HRESULT ConfigureVideoFrameGeometry();
 
   HRESULT CreateBasicVideoFrame(IMFSample* aSample,
+                                int64_t aStreamOffset,
                                 VideoData** aOutVideoData);
 
   HRESULT CreateD3DVideoFrame(IMFSample* aSample,
+                              int64_t aStreamOffset,
                               VideoData** aOutVideoData);
+
   // Video frame geometry.
   VideoInfo mVideoInfo;
   uint32_t mVideoStride;
   uint32_t mVideoWidth;
   uint32_t mVideoHeight;
   nsIntRect mPictureRegion;
 
-  // The last offset into the media resource that was passed into Input().
-  // This is used to approximate the decoder's position in the media resource.
-  int64_t mLastStreamOffset;
-
-  nsAutoPtr<MFTDecoder> mDecoder;
+  RefPtr<MFTDecoder> mDecoder;
   RefPtr<layers::ImageContainer> mImageContainer;
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
+  RefPtr<MediaTaskQueue> mTaskQueue;
+  MediaDataDecoderCallback* mCallback;
 
   const bool mDXVAEnabled;
   const layers::LayersBackend mLayersBackend;
   bool mUseHwAccel;
 };
 
-
-
 } // namespace mozilla
 
-#endif
+#endif // WMFVideoOutputSource_h_