Bug 830171 - Use SourceReader in async mode to better allow shutdown on MediaResource close. r=padenot
authorChris Pearce <cpearce@mozilla.com>
Fri, 15 Feb 2013 21:35:48 +1300
changeset 122267 b5e103247c9f351e25a3980516a164989e397610
parent 122266 987a4c1c0a68ebd374f5874a64c8e2fd63ff4892
child 122268 beca555617478a690a8bc78e0a91bd7e10566764
push id24327
push usergszorc@mozilla.com
push dateTue, 19 Feb 2013 05:22:32 +0000
treeherdermozilla-central@e8f8a3f6f1f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs830171
milestone21.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 830171 - Use SourceReader in async mode to better allow shutdown on MediaResource close. r=padenot
content/media/wmf/Makefile.in
content/media/wmf/WMFByteStream.cpp
content/media/wmf/WMFByteStream.h
content/media/wmf/WMFReader.cpp
content/media/wmf/WMFReader.h
content/media/wmf/WMFSourceReaderCallback.cpp
content/media/wmf/WMFSourceReaderCallback.h
content/media/wmf/WMFUtils.cpp
content/media/wmf/WMFUtils.h
--- a/content/media/wmf/Makefile.in
+++ b/content/media/wmf/Makefile.in
@@ -20,16 +20,17 @@ EXPORTS		+= \
 		WMF.h \
 		$(NULL)
 
 CPPSRCS		= \
 		WMFByteStream.cpp \
 		WMFDecoder.cpp \
 		WMFReader.cpp \
 		WMFUtils.cpp \
+		WMFSourceReaderCallback.cpp \
 		$(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 OS_CXXFLAGS += -DNOMINMAX
 endif
 
 FORCE_STATIC_LIB = 1
 
--- a/content/media/wmf/WMFByteStream.cpp
+++ b/content/media/wmf/WMFByteStream.cpp
@@ -5,16 +5,17 @@
  * 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 "WMFSourceReaderCallback.h"
 #include "WMFUtils.h"
 #include "MediaResource.h"
 #include "nsISeekableStream.h"
 #include "mozilla/RefPtr.h"
 #include "nsIThreadPool.h"
 #include "nsXPCOMCIDInternal.h"
 #include <algorithm>
 
@@ -22,26 +23,16 @@ 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;
-}
-
 // Thread pool listener which ensures that MSCOM is initialized and
 // deinitialized on the thread pool thread. We can call back into WMF
 // on this thread, so we need MSCOM working.
 class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITHREADPOOLLISTENER
 };
@@ -98,25 +89,28 @@ public:
       NS_IF_RELEASE(sThreadPool);
       pool->Shutdown();
     }
     return NS_OK;
   }
   nsRefPtr<MediaResource> mResource;
 };
 
-WMFByteStream::WMFByteStream(MediaResource* aResource)
-  : mResourceMonitor("WMFByteStream.MediaResource"),
+WMFByteStream::WMFByteStream(MediaResource* aResource,
+                             WMFSourceReaderCallback* aSourceReaderCallback)
+  : mSourceReaderCallback(aSourceReaderCallback),
+    mResourceMonitor("WMFByteStream.MediaResource"),
     mResource(aResource),
     mReentrantMonitor("WMFByteStream.Data"),
     mOffset(0),
     mIsShutdown(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
   NS_ASSERTION(mResource, "Must have a valid media resource");
+  NS_ASSERTION(mSourceReaderCallback, "Must have a source reader callback.");
 
 #ifdef PR_LOGGING
   if (!gWMFByteStreamLog) {
     gWMFByteStreamLog = PR_NewLogModule("WMFByteStream");
   }
 #endif
 
   MOZ_COUNT_CTOR(WMFByteStream);
@@ -171,18 +165,21 @@ WMFByteStream::Init()
     LOG("WMFByteStream has Content-Type=%s", mResource->GetContentType());
   }
   return NS_OK;
 }
 
 nsresult
 WMFByteStream::Shutdown()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  mIsShutdown = true;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    mIsShutdown = true;
+  }
+  mSourceReaderCallback->Cancel();
   return NS_OK;
 }
 
 // IUnknown Methods
 STDMETHODIMP
 WMFByteStream::QueryInterface(REFIID aIId, void **aInterface)
 {
   LOG("WMFByteStream::QueryInterface %s", GetGUIDName(aIId).get());
--- a/content/media/wmf/WMFByteStream.h
+++ b/content/media/wmf/WMFByteStream.h
@@ -16,33 +16,34 @@
 #include "mozilla/RefPtr.h"
 
 class nsIThreadPool;
 
 namespace mozilla {
 
 class MediaResource;
 class ReadRequest;
+class WMFSourceReaderCallback;
 
 // 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(), and more rarely synchronously
 // using Read().
 //
 // Note: This implementation attempts to be bug-compatible with Windows Media
 //       Foundation's implementation of IMFByteStream. The behaviour of WMF's
 //       IMFByteStream was determined by creating it and testing the edge cases.
 //       For details see the test code at:
 //       https://github.com/cpearce/IMFByteStreamBehaviour/
 class WMFByteStream MOZ_FINAL : public IMFByteStream
                               , public IMFAttributes
 {
 public:
-  WMFByteStream(MediaResource* aResource);
+  WMFByteStream(MediaResource* aResource, WMFSourceReaderCallback* aCallback);
   ~WMFByteStream();
 
   nsresult Init();
   nsresult Shutdown();
 
   // IUnknown Methods.
   STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
   STDMETHODIMP_(ULONG) AddRef();
@@ -118,16 +119,22 @@ private:
 
   // Returns true if the current position of the stream is at end of stream.
   bool IsEOS();
 
   // Reference to the thread pool in which we perform the reads asynchronously.
   // Note this is pool is shared amongst all active WMFByteStreams.
   nsCOMPtr<nsIThreadPool> mThreadPool;
 
+  // Reference to the source reader's callback. We use this reference to
+  // notify threads waiting on a ReadSample() callback to stop waiting
+  // if the stream is closed, which happens when the media element is
+  // shutdown.
+  RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
+
   // Monitor that ensures that multiple concurrent async reads are processed
   // in serial on a resource. This prevents concurrent async reads and seeks
   // from interleaving, to ensure that reads occur at the offset they're
   // supposed to!
   ReentrantMonitor mResourceMonitor;
 
   // Resource we're wrapping. Note this object's methods are threadsafe,
   // but because multiple reads can be processed concurrently in the thread
--- a/content/media/wmf/WMFReader.cpp
+++ b/content/media/wmf/WMFReader.cpp
@@ -3,16 +3,17 @@
 /* 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"
+#include "WMFSourceReaderCallback.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"
 
@@ -82,19 +83,20 @@ WMFReader::Init(MediaDecoderReader* aClo
   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;
   }
 
+  mSourceReaderCallback = new WMFSourceReaderCallback();
+
   // Must be created on main thread.
-  mByteStream = new WMFByteStream(mDecoder->GetResource());
-
+  mByteStream = new WMFByteStream(mDecoder->GetResource(), mSourceReaderCallback);
   return mByteStream->Init();
 }
 
 bool
 WMFReader::HasAudio()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   return mHasAudio;
@@ -436,17 +438,24 @@ 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, byRef(mSourceReader));
+  RefPtr<IMFAttributes> attr;
+  hr = wmf::MFCreateAttributes(byRef(attr), 1);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, mSourceReaderCallback);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+
+  hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, attr, byRef(mSourceReader));
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   hr = ConfigureVideoDecoder();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
   hr = ConfigureAudioDecoder();
   NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
 
@@ -478,39 +487,51 @@ GetSampleDuration(IMFSample* aSample)
   return HNsToUsecs(duration);
 }
 
 bool
 WMFReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
-  DWORD flags;
-  LONGLONG timestampHns;
   HRESULT hr;
-
-  RefPtr<IMFSample> sample;
   hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
                                  0, // control flags
-                                 nullptr, // read stream index
-                                 &flags,
-                                 &timestampHns,
-                                 byRef(sample));
+                                 0, // read stream index
+                                 nullptr,
+                                 nullptr,
+                                 nullptr);
 
+  if (FAILED(hr)) {
+    LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x", hr);
+    // End the stream.
+    mAudioQueue.Finish();
+    return false;
+  }
+
+  DWORD flags = 0;
+  LONGLONG timestampHns = 0;
+  RefPtr<IMFSample> sample;
+  hr = mSourceReaderCallback->Wait(&flags, &timestampHns, byRef(sample));
   if (FAILED(hr) ||
       (flags & MF_SOURCE_READERF_ERROR) ||
       (flags & MF_SOURCE_READERF_ENDOFSTREAM) ||
       (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
     LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x flags=0x%x",
         hr, flags);
-    // End of stream.
+    // End the stream.
     mAudioQueue.Finish();
     return false;
   }
 
+  if (!sample) {
+    // Not enough data? Try again...
+    return true;
+  }
+
   RefPtr<IMFMediaBuffer> buffer;
   hr = sample->ConvertToContiguousBuffer(byRef(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);
@@ -546,43 +567,52 @@ WMFReader::DecodeVideoFrame(bool &aKeyfr
 {
   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;
 
-  RefPtr<IMFSample> sample;
   hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                                  0, // control flags
-                                 nullptr, // read stream index
-                                 &flags,
-                                 &timestampHns,
-                                 byRef(sample));
+                                 0, // read stream index
+                                 nullptr,
+                                 nullptr,
+                                 nullptr);
+  if (FAILED(hr)) {
+    LOG("WMFReader::DecodeVideoData() ReadSample failed with hr=0x%x", hr);
+    // End the stream.
+    mVideoQueue.Finish();
+    return false;
+  }
+
+  DWORD flags = 0;
+  LONGLONG timestampHns = 0;
+  RefPtr<IMFSample> sample;
+  hr = mSourceReaderCallback->Wait(&flags, &timestampHns, byRef(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.
+      // End the stream.
       mVideoQueue.Finish();
       return false;
     }
     LOG("WMFReader; Null sample after video decode. Maybe insufficient data...");
     return true;
   }
 
   if ((flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
--- a/content/media/wmf/WMFReader.h
+++ b/content/media/wmf/WMFReader.h
@@ -7,16 +7,17 @@
 #define WMFReader_h_
 
 #include "WMF.h"
 #include "MediaDecoderReader.h"
 
 namespace mozilla {
 
 class WMFByteStream;
+class WMFSourceReaderCallback;
 
 // Decoder backend for reading H.264/AAC in MP4/M4A and MP3 audio files,
 // using Windows Media Foundation.
 class WMFReader : public MediaDecoderReader
 {
 public:
   WMFReader(AbstractMediaDecoder* aDecoder);
 
@@ -48,16 +49,17 @@ public:
 private:
 
   HRESULT ConfigureAudioDecoder();
   HRESULT ConfigureVideoDecoder();
   HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
 
   RefPtr<IMFSourceReader> mSourceReader;
   RefPtr<WMFByteStream> mByteStream;
+  RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
 
   // Region inside the video frame that makes up the picture. Pixels outside
   // of this region should not be rendered.
   nsIntRect mPictureRegion;
 
   uint32_t mAudioChannels;
   uint32_t mAudioBytesPerSample;
   uint32_t mAudioRate;
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFSourceReaderCallback.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "WMFSourceReaderCallback.h"
+#include "WMFUtils.h"
+
+namespace mozilla {
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gMediaDecoderLog;
+#define LOG(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+#define LOG(...)
+#endif
+
+// IUnknown Methods
+STDMETHODIMP
+WMFSourceReaderCallback::QueryInterface(REFIID aIId, void **aInterface)
+{
+  LOG("WMFSourceReaderCallback::QueryInterface %s", GetGUIDName(aIId).get());
+
+  if (aIId == IID_IMFSourceReaderCallback) {
+    return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
+  }
+  if (aIId == IID_IUnknown) {
+    return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
+  }
+
+  *aInterface = NULL;
+  return E_NOINTERFACE;
+}
+
+NS_IMPL_THREADSAFE_ADDREF(WMFSourceReaderCallback)
+NS_IMPL_THREADSAFE_RELEASE(WMFSourceReaderCallback)
+
+WMFSourceReaderCallback::WMFSourceReaderCallback()
+  : mResultStatus(S_OK)
+  , mStreamFlags(0)
+  , mTimestamp(0)
+  , mSample(nullptr)
+  , mReadFinished(false)
+  , mMonitor("WMFSourceReaderCallback")
+{
+}
+
+HRESULT
+WMFSourceReaderCallback::NotifyReadComplete(HRESULT aReadStatus,
+                                            DWORD aStreamIndex,
+                                            DWORD aStreamFlags,
+                                            LONGLONG aTimestamp,
+                                            IMFSample *aSample)
+{
+  // Note: aSample can be NULL on success if more data is required!
+  ReentrantMonitorAutoEnter mon(mMonitor);
+
+  if (mSample) {
+    // The WMFReader should have called Wait() to retrieve the last
+    // sample returned by the last ReadSample() call, but if we're
+    // aborting the read before Wait() is called the sample ref
+    // can be non-null.
+    mSample->Release();
+    mSample = nullptr;
+  }
+
+  if (SUCCEEDED(aReadStatus)) {
+    if (aSample) {
+      mTimestamp = aTimestamp;
+      mSample = aSample;
+      mSample->AddRef();
+    }
+  }
+
+  mResultStatus = aReadStatus;
+  mStreamFlags = aStreamFlags;
+
+  // Set the sentinal value and notify the monitor, so that threads waiting
+  // in Wait() are awoken.
+  mReadFinished = true;
+  mon.NotifyAll();
+
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFSourceReaderCallback::OnReadSample(HRESULT aReadStatus,
+                                      DWORD aStreamIndex,
+                                      DWORD aStreamFlags,
+                                      LONGLONG aTimestamp,
+                                      IMFSample *aSample)
+{
+  LOG("WMFSourceReaderCallback::OnReadSample() hr=0x%x flags=0x%x time=%lld sample=%p",
+      aReadStatus, aStreamFlags, aTimestamp, aSample);
+  return NotifyReadComplete(aReadStatus,
+                            aStreamIndex,
+                            aStreamFlags,
+                            aTimestamp,
+                            aSample);
+}
+
+HRESULT
+WMFSourceReaderCallback::Cancel()
+{
+  LOG("WMFSourceReaderCallback::Cancel()");
+  return NotifyReadComplete(E_ABORT,
+                            0,
+                            0,
+                            0,
+                            nullptr);
+}
+
+STDMETHODIMP
+WMFSourceReaderCallback::OnEvent(DWORD, IMFMediaEvent *)
+{
+  return S_OK;
+}
+
+STDMETHODIMP
+WMFSourceReaderCallback::OnFlush(DWORD)
+{
+  return S_OK;
+}
+
+HRESULT
+WMFSourceReaderCallback::Wait(DWORD* aStreamFlags,
+                              LONGLONG* aTimeStamp,
+                              IMFSample** aSample)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  LOG("WMFSourceReaderCallback::Wait() starting wait");
+  while (!mReadFinished) {
+    mon.Wait();
+  }
+  mReadFinished = false;
+  LOG("WMFSourceReaderCallback::Wait() done waiting");
+
+  *aStreamFlags = mStreamFlags;
+  *aTimeStamp = mTimestamp;
+  *aSample = mSample;
+  HRESULT hr = mResultStatus;
+
+  mSample = nullptr;
+  mTimestamp = 0;
+  mStreamFlags = 0;
+  mResultStatus = S_OK;
+
+  return hr;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/wmf/WMFSourceReaderCallback.h
@@ -0,0 +1,83 @@
+/* -*- 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(WMFSourceReaderCallback_h_)
+#define WMFSourceReaderCallback_h_
+
+#include "WMF.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+// A listener which we pass into the IMFSourceReader upon creation which is
+// notified when an asynchronous call to IMFSourceReader::ReadSample()
+// completes. This allows us to abort ReadSample() operations when the
+// WMFByteStream's underlying MediaResource is closed. This ensures that
+// the decode threads don't get stuck in a synchronous ReadSample() call
+// when the MediaResource is unexpectedly shutdown.
+class WMFSourceReaderCallback : public IMFSourceReaderCallback
+{
+public:
+  WMFSourceReaderCallback();
+
+  // IUnknown Methods.
+  STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
+  STDMETHODIMP_(ULONG) AddRef();
+  STDMETHODIMP_(ULONG) Release();
+
+  // IMFSourceReaderCallback methods
+  STDMETHODIMP OnReadSample(HRESULT hrStatus,
+                            DWORD dwStreamIndex,
+                            DWORD dwStreamFlags,
+                            LONGLONG llTimestamp,
+                            IMFSample *pSample);
+  STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *);
+  STDMETHODIMP OnFlush(DWORD);
+
+  // Causes the calling thread to block waiting for the
+  // IMFSourceReader::ReadSample() result callback to occur, or for the
+  // WMFByteStream to be closed.
+  HRESULT Wait(DWORD* aStreamFlags,
+               LONGLONG* aTimeStamp,
+               IMFSample** aSample);
+
+  // Cancels Wait() calls.
+  HRESULT Cancel();
+
+private:
+
+  // Sets state to record the result of a read, and awake threads
+  // waiting in Wait().
+  HRESULT NotifyReadComplete(HRESULT aReadStatus,
+                             DWORD aStreamIndex,
+                             DWORD aStreamFlags,
+                             LONGLONG aTimestamp,
+                             IMFSample *aSample);
+
+  // Synchronizes all member data in this class, and Wait() blocks on
+  // and NotifyReadComplete() notifies this monitor.
+  ReentrantMonitor mMonitor;
+
+  // Read result data.
+  HRESULT mResultStatus;
+  DWORD mStreamFlags;
+  LONGLONG mTimestamp;
+  IMFSample* mSample;
+
+  // Sentinal. Set to true when a read result is returned. Wait() won't exit
+  // until this is set to true.
+  bool mReadFinished;
+
+  // IUnknown ref counting.
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+
+};
+
+} // namespace mozilla
+
+#endif // WMFSourceReaderCallback_h_
\ No newline at end of file
--- a/content/media/wmf/WMFUtils.cpp
+++ b/content/media/wmf/WMFUtils.cpp
@@ -194,16 +194,26 @@ nsCString GetGUIDName(const GUID& guid)
 bool
 SourceReaderHasStream(IMFSourceReader* aReader, const DWORD aIndex)
 {
   RefPtr<IMFMediaType> nativeType;
   HRESULT hr = aReader->GetNativeMediaType(aIndex, 0, byRef(nativeType));
   return FAILED(hr) ? false : true;
 }
 
+HRESULT
+DoGetInterface(IUnknown* aUnknown, void** aInterface)
+{
+  if (!aInterface)
+    return E_POINTER;
+  *aInterface = aUnknown;
+  aUnknown->AddRef();
+  return S_OK;
+}
+
 namespace wmf {
 
 // Some SDK versions don't define the AAC decoder CLSID.
 #ifndef CLSID_CMSAACDecMFT
 // {32D186A7-218F-4C75-8876-DD77273A8999}
 DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
 #endif
 
--- a/content/media/wmf/WMFUtils.h
+++ b/content/media/wmf/WMFUtils.h
@@ -6,17 +6,18 @@
 
 #include "WMF.h"
 #include "nsString.h"
 
 // Various utilities shared by WMF backend files.
 
 namespace mozilla {
 
-nsCString GetGUIDName(const GUID& guid);
+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);
 
@@ -40,20 +41,27 @@ public:
   }
 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) {
+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) {
+inline int64_t
+HNsToUsecs(int64_t hNanoSecs) {
   return hNanoSecs / 10;
 }
 
+// Assigns aUnknown to *aInterface, and AddRef's it.
+// Helper for MSCOM QueryInterface implementations.
+HRESULT
+DoGetInterface(IUnknown* aUnknown, void** aInterface);
+
 } // namespace mozilla