Add unit tests for class VideoCaptureDeviceMFWin

Bug: 786854
Change-Id: I6f51ed00b664d305166d092edd68891eaa0b7232
Reviewed-on: https://chromium-review.googlesource.com/890442
Commit-Queue: Christian Fremerey <chfremer@chromium.org>
Reviewed-by: Christian Fremerey <chfremer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536281}
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 3da8b4b..44a16d8 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -304,6 +304,19 @@
   }
 
   if (is_win) {
+    sources += [ "video/win/video_capture_device_mf_win_unittest.cc" ]
+    libs = [
+      "mf.lib",
+      "mfplat.lib",
+      "mfreadwrite.lib",
+      "mfuuid.lib",
+    ]
+    ldflags = [
+      "/DELAYLOAD:mf.dll",
+      "/DELAYLOAD:mfplat.dll",
+      "/DELAYLOAD:mfreadwrite.dll",
+    ]
+
     # TODO(jschuh): https://crbug.com/167187 fix size_t to int truncations.
     configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   }
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 745d612..76b4cf4 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -354,14 +354,14 @@
   std::unique_ptr<VideoCaptureDevice> device;
   if (device_descriptor.capture_api == VideoCaptureApi::WIN_MEDIA_FOUNDATION) {
     DCHECK(PlatformSupportsMediaFoundation());
-    device.reset(new VideoCaptureDeviceMFWin(device_descriptor));
-    DVLOG(1) << " MediaFoundation Device: " << device_descriptor.display_name();
     ComPtr<IMFMediaSource> source;
     if (!CreateVideoCaptureDeviceMediaFoundation(
             device_descriptor.device_id.c_str(), source.GetAddressOf())) {
       return std::unique_ptr<VideoCaptureDevice>();
     }
-    if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
+    device.reset(new VideoCaptureDeviceMFWin(source));
+    DVLOG(1) << " MediaFoundation Device: " << device_descriptor.display_name();
+    if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init())
       device.reset();
   } else if (device_descriptor.capture_api ==
              VideoCaptureApi::WIN_DIRECT_SHOW) {
diff --git a/media/capture/video/win/video_capture_device_mf_win.cc b/media/capture/video/win/video_capture_device_mf_win.cc
index 50d1b7e..14cb3cd 100644
--- a/media/capture/video/win/video_capture_device_mf_win.cc
+++ b/media/capture/video/win/video_capture_device_mf_win.cc
@@ -243,108 +243,16 @@
   return *best_match;
 }
 
-HRESULT ExecuteHresultCallbackWithRetries(
-    base::RepeatingCallback<HRESULT()> callback) {
-  // Retry callback execution on MF_E_INVALIDREQUEST.
-  // MF_E_INVALIDREQUEST is not documented in MediaFoundation documentation.
-  // It could mean that MediaFoundation or the underlying device can be in a
-  // state that reject these calls. Since MediaFoundation gives no intel about
-  // that state beginning and ending (i.e. via some kind of event), we retry the
-  // call until it succeed.
-  HRESULT hr;
-  int retry_count = 0;
-  do {
-    hr = callback.Run();
-    if (FAILED(hr))
-      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
-
-    // Give up after ~10 seconds
-  } while (hr == MF_E_INVALIDREQUEST && retry_count++ < 200);
-
-  return hr;
-}
-
-HRESULT GetDeviceStreamCount(IMFCaptureSource* source, DWORD* count) {
-  // Sometimes, GetDeviceStreamCount returns an
-  // undocumented MF_E_INVALIDREQUEST. Retrying solves the issue.
-  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
-      [](IMFCaptureSource* source, DWORD* count) {
-        return source->GetDeviceStreamCount(count);
-      },
-      source, count));
-}
-
-HRESULT GetDeviceStreamCategory(
-    IMFCaptureSource* source,
-    DWORD stream_index,
-    MF_CAPTURE_ENGINE_STREAM_CATEGORY* stream_category) {
-  // We believe that GetDeviceStreamCategory could be affected by the same
-  // behaviour of GetDeviceStreamCount and GetAvailableDeviceMediaType
-  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
-      [](IMFCaptureSource* source, DWORD stream_index,
-         MF_CAPTURE_ENGINE_STREAM_CATEGORY* stream_category) {
-        return source->GetDeviceStreamCategory(stream_index, stream_category);
-      },
-      source, stream_index, stream_category));
-}
-
-HRESULT GetAvailableDeviceMediaType(IMFCaptureSource* source,
-                                    DWORD stream_index,
-                                    DWORD media_type_index,
-                                    IMFMediaType** type) {
-  // Rarely, for some unknown reason, GetAvailableDeviceMediaType returns an
-  // undocumented MF_E_INVALIDREQUEST. Retrying solves the issue.
-  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
-      [](IMFCaptureSource* source, DWORD stream_index, DWORD media_type_index,
-         IMFMediaType** type) {
-        return source->GetAvailableDeviceMediaType(stream_index,
-                                                   media_type_index, type);
-      },
-      source, stream_index, media_type_index, type));
-}
-
-HRESULT FillCapabilities(IMFCaptureSource* source,
-                         bool photo,
-                         CapabilityList* capabilities) {
-  DWORD stream_count = 0;
-  HRESULT hr = GetDeviceStreamCount(source, &stream_count);
+HRESULT CreateCaptureEngine(IMFCaptureEngine** engine) {
+  ComPtr<IMFCaptureEngineClassFactory> capture_engine_class_factory;
+  HRESULT hr = CoCreateInstance(
+      CLSID_MFCaptureEngineClassFactory, NULL, CLSCTX_INPROC_SERVER,
+      IID_PPV_ARGS(capture_engine_class_factory.GetAddressOf()));
   if (FAILED(hr))
     return hr;
 
-  for (DWORD stream_index = 0; stream_index < stream_count; stream_index++) {
-    MF_CAPTURE_ENGINE_STREAM_CATEGORY stream_category;
-    hr = GetDeviceStreamCategory(source, stream_index, &stream_category);
-    if (FAILED(hr))
-      return hr;
-
-    if ((photo && stream_category !=
-                      MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_INDEPENDENT) ||
-        (!photo &&
-         stream_category != MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW &&
-         stream_category != MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_CAPTURE)) {
-      continue;
-    }
-
-    DWORD media_type_index = 0;
-    ComPtr<IMFMediaType> type;
-    while (SUCCEEDED(hr = GetAvailableDeviceMediaType(source, stream_index,
-                                                      media_type_index,
-                                                      type.GetAddressOf()))) {
-      VideoCaptureFormat format;
-      if (GetFormatFromMediaType(type.Get(), photo, &format))
-        capabilities->emplace_back(media_type_index, format, stream_index);
-      type.Reset();
-      ++media_type_index;
-    }
-    if (hr == MF_E_NO_MORE_TYPES) {
-      hr = S_OK;
-    }
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  return hr;
+  return capture_engine_class_factory->CreateInstance(CLSID_MFCaptureEngine,
+                                                      IID_PPV_ARGS(engine));
 }
 
 class MFVideoCallback final
@@ -456,10 +364,126 @@
   return false;
 }
 
+HRESULT VideoCaptureDeviceMFWin::ExecuteHresultCallbackWithRetries(
+    base::RepeatingCallback<HRESULT()> callback) {
+  // Retry callback execution on MF_E_INVALIDREQUEST.
+  // MF_E_INVALIDREQUEST is not documented in MediaFoundation documentation.
+  // It could mean that MediaFoundation or the underlying device can be in a
+  // state that reject these calls. Since MediaFoundation gives no intel about
+  // that state beginning and ending (i.e. via some kind of event), we retry the
+  // call until it succeed.
+  HRESULT hr;
+  int retry_count = 0;
+  do {
+    hr = callback.Run();
+    if (FAILED(hr))
+      base::PlatformThread::Sleep(
+          base::TimeDelta::FromMilliseconds(retry_delay_in_ms_));
+
+    // Give up after some amount of time
+  } while (hr == MF_E_INVALIDREQUEST && retry_count++ < max_retry_count_);
+
+  return hr;
+}
+
+HRESULT VideoCaptureDeviceMFWin::GetDeviceStreamCount(IMFCaptureSource* source,
+                                                      DWORD* count) {
+  // Sometimes, GetDeviceStreamCount returns an
+  // undocumented MF_E_INVALIDREQUEST. Retrying solves the issue.
+  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
+      [](IMFCaptureSource* source, DWORD* count) {
+        return source->GetDeviceStreamCount(count);
+      },
+      source, count));
+}
+
+HRESULT VideoCaptureDeviceMFWin::GetDeviceStreamCategory(
+    IMFCaptureSource* source,
+    DWORD stream_index,
+    MF_CAPTURE_ENGINE_STREAM_CATEGORY* stream_category) {
+  // We believe that GetDeviceStreamCategory could be affected by the same
+  // behaviour of GetDeviceStreamCount and GetAvailableDeviceMediaType
+  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
+      [](IMFCaptureSource* source, DWORD stream_index,
+         MF_CAPTURE_ENGINE_STREAM_CATEGORY* stream_category) {
+        return source->GetDeviceStreamCategory(stream_index, stream_category);
+      },
+      source, stream_index, stream_category));
+}
+
+HRESULT VideoCaptureDeviceMFWin::GetAvailableDeviceMediaType(
+    IMFCaptureSource* source,
+    DWORD stream_index,
+    DWORD media_type_index,
+    IMFMediaType** type) {
+  // Rarely, for some unknown reason, GetAvailableDeviceMediaType returns an
+  // undocumented MF_E_INVALIDREQUEST. Retrying solves the issue.
+  return ExecuteHresultCallbackWithRetries(base::BindRepeating(
+      [](IMFCaptureSource* source, DWORD stream_index, DWORD media_type_index,
+         IMFMediaType** type) {
+        return source->GetAvailableDeviceMediaType(stream_index,
+                                                   media_type_index, type);
+      },
+      source, stream_index, media_type_index, type));
+}
+
+HRESULT VideoCaptureDeviceMFWin::FillCapabilities(
+    IMFCaptureSource* source,
+    bool photo,
+    CapabilityList* capabilities) {
+  DWORD stream_count = 0;
+  HRESULT hr = GetDeviceStreamCount(source, &stream_count);
+  if (FAILED(hr))
+    return hr;
+
+  for (DWORD stream_index = 0; stream_index < stream_count; stream_index++) {
+    MF_CAPTURE_ENGINE_STREAM_CATEGORY stream_category;
+    hr = GetDeviceStreamCategory(source, stream_index, &stream_category);
+    if (FAILED(hr))
+      return hr;
+
+    if ((photo && stream_category !=
+                      MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_INDEPENDENT) ||
+        (!photo &&
+         stream_category != MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW &&
+         stream_category != MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_CAPTURE)) {
+      continue;
+    }
+
+    DWORD media_type_index = 0;
+    ComPtr<IMFMediaType> type;
+    while (SUCCEEDED(hr = GetAvailableDeviceMediaType(source, stream_index,
+                                                      media_type_index,
+                                                      type.GetAddressOf()))) {
+      VideoCaptureFormat format;
+      if (GetFormatFromMediaType(type.Get(), photo, &format))
+        capabilities->emplace_back(media_type_index, format, stream_index);
+      type.Reset();
+      ++media_type_index;
+    }
+    if (hr == MF_E_NO_MORE_TYPES) {
+      hr = S_OK;
+    }
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return hr;
+}
+
+VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(ComPtr<IMFMediaSource> source)
+    : VideoCaptureDeviceMFWin(source, nullptr) {}
+
 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
-    const VideoCaptureDeviceDescriptor& device_descriptor)
-    : descriptor_(device_descriptor),
-      create_mf_photo_callback_(base::BindRepeating(&CreateMFPhotoCallback)),
+    ComPtr<IMFMediaSource> source,
+    ComPtr<IMFCaptureEngine> engine)
+    : create_mf_photo_callback_(base::BindRepeating(&CreateMFPhotoCallback)),
+      is_initialized_(false),
+      max_retry_count_(200),
+      retry_delay_in_ms_(50),
+      source_(source),
+      engine_(engine),
       is_started_(false) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
@@ -468,43 +492,31 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-bool VideoCaptureDeviceMFWin::Init(const ComPtr<IMFMediaSource>& source) {
+bool VideoCaptureDeviceMFWin::Init() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!engine_);
+  DCHECK(!is_initialized_);
 
   HRESULT hr = S_OK;
+  if (!engine_)
+    hr = CreateCaptureEngine(engine_.GetAddressOf());
+
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return false;
+  }
+
   ComPtr<IMFAttributes> attributes;
-  ComPtr<IMFCaptureEngineClassFactory> capture_engine_class_factory;
   MFCreateAttributes(attributes.GetAddressOf(), 1);
   DCHECK(attributes);
 
-  hr = CoCreateInstance(
-      CLSID_MFCaptureEngineClassFactory, NULL, CLSCTX_INPROC_SERVER,
-      IID_PPV_ARGS(capture_engine_class_factory.GetAddressOf()));
-  if (FAILED(hr)) {
-    LogError(FROM_HERE, hr);
-    return false;
-  }
-  hr = capture_engine_class_factory->CreateInstance(
-      CLSID_MFCaptureEngine, IID_PPV_ARGS(engine_.GetAddressOf()));
-  if (FAILED(hr)) {
-    LogError(FROM_HERE, hr);
-    return false;
-  }
-
   video_callback_ = new MFVideoCallback(this);
-  if (FAILED(hr)) {
-    LogError(FROM_HERE, hr);
-    return false;
-  }
-
   hr = engine_->Initialize(video_callback_.get(), attributes.Get(), nullptr,
-                           source.Get());
+                           source_.Get());
   if (FAILED(hr)) {
     LogError(FROM_HERE, hr);
     return false;
   }
-
+  is_initialized_ = true;
   return true;
 }
 
@@ -760,11 +772,6 @@
     return;
   }
 
-  if (FAILED(hr)) {
-    LogError(FROM_HERE, hr);
-    return;
-  }
-
   ComPtr<IMFMediaType> current_media_type;
   hr = source->GetCurrentDeviceMediaType(
       selected_photo_capability_ ? selected_photo_capability_->stream_index
diff --git a/media/capture/video/win/video_capture_device_mf_win.h b/media/capture/video/win/video_capture_device_mf_win.h
index a5d7857..dcdafcf 100644
--- a/media/capture/video/win/video_capture_device_mf_win.h
+++ b/media/capture/video/win/video_capture_device_mf_win.h
@@ -39,11 +39,15 @@
   static bool FormatFromGuid(const GUID& guid, VideoPixelFormat* format);
 
   explicit VideoCaptureDeviceMFWin(
-      const VideoCaptureDeviceDescriptor& device_descriptor);
+      Microsoft::WRL::ComPtr<IMFMediaSource> source);
+  explicit VideoCaptureDeviceMFWin(
+      Microsoft::WRL::ComPtr<IMFMediaSource> source,
+      Microsoft::WRL::ComPtr<IMFCaptureEngine> engine);
+
   ~VideoCaptureDeviceMFWin() override;
 
   // Opens the device driver for this device.
-  bool Init(const Microsoft::WRL::ComPtr<IMFMediaSource>& source);
+  bool Init();
 
   // VideoCaptureDevice implementation.
   void AllocateAndStart(
@@ -76,13 +80,38 @@
     create_mf_photo_callback_ = cb;
   }
 
+  void set_max_retry_count_for_testing(int max_retry_count) {
+    max_retry_count_ = max_retry_count;
+  }
+
+  void set_retry_delay_in_ms_for_testing(int retry_delay_in_ms) {
+    retry_delay_in_ms_ = retry_delay_in_ms;
+  }
+
  private:
+  HRESULT ExecuteHresultCallbackWithRetries(
+      base::RepeatingCallback<HRESULT()> callback);
+  HRESULT GetDeviceStreamCount(IMFCaptureSource* source, DWORD* count);
+  HRESULT GetDeviceStreamCategory(
+      IMFCaptureSource* source,
+      DWORD stream_index,
+      MF_CAPTURE_ENGINE_STREAM_CATEGORY* stream_category);
+  HRESULT GetAvailableDeviceMediaType(IMFCaptureSource* source,
+                                      DWORD stream_index,
+                                      DWORD media_type_index,
+                                      IMFMediaType** type);
+
+  HRESULT FillCapabilities(IMFCaptureSource* source,
+                           bool photo,
+                           CapabilityList* capabilities);
   void OnError(const base::Location& from_here, HRESULT hr);
   void OnError(const base::Location& from_here, const char* message);
 
-  VideoCaptureDeviceDescriptor descriptor_;
   CreateMFPhotoCallbackCB create_mf_photo_callback_;
   scoped_refptr<MFVideoCallback> video_callback_;
+  bool is_initialized_;
+  int max_retry_count_;
+  int retry_delay_in_ms_;
 
   // Guards the below variables from concurrent access between methods running
   // on |sequence_checker_| and calls to OnIncomingCapturedData() and OnEvent()
@@ -90,6 +119,7 @@
   base::Lock lock_;
 
   std::unique_ptr<VideoCaptureDevice::Client> client_;
+  const Microsoft::WRL::ComPtr<IMFMediaSource> source_;
   Microsoft::WRL::ComPtr<IMFCaptureEngine> engine_;
   std::unique_ptr<CapabilityWin> selected_video_capability_;
   CapabilityList photo_capabilities_;
diff --git a/media/capture/video/win/video_capture_device_mf_win_unittest.cc b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
new file mode 100644
index 0000000..cada827
--- /dev/null
+++ b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
@@ -0,0 +1,1130 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <mfapi.h>
+#include <mferror.h>
+#include <stddef.h>
+#include <wincodec.h>
+
+#include "media/capture/video/win/video_capture_device_factory_win.h"
+#include "media/capture/video/win/video_capture_device_mf_win.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::AtLeast;
+using ::testing::Mock;
+using Microsoft::WRL::ComPtr;
+
+namespace media {
+
+namespace {
+class MockClient : public VideoCaptureDevice::Client {
+ public:
+  void OnIncomingCapturedData(const uint8_t* data,
+                              int length,
+                              const VideoCaptureFormat& frame_format,
+                              int clockwise_rotation,
+                              base::TimeTicks reference_time,
+                              base::TimeDelta timestamp,
+                              int frame_feedback_id = 0) override {}
+
+  MOCK_METHOD4(
+      ReserveOutputBuffer,
+      Buffer(const gfx::Size&, VideoPixelFormat, VideoPixelStorage, int));
+
+  void OnIncomingCapturedBuffer(Buffer buffer,
+                                const VideoCaptureFormat& format,
+                                base::TimeTicks reference_,
+                                base::TimeDelta timestamp) override {}
+
+  void OnIncomingCapturedBufferExt(
+      Buffer buffer,
+      const VideoCaptureFormat& format,
+      base::TimeTicks reference_time,
+      base::TimeDelta timestamp,
+      gfx::Rect visible_rect,
+      const VideoFrameMetadata& additional_metadata) override {}
+
+  MOCK_METHOD4(
+      ResurrectLastOutputBuffer,
+      Buffer(const gfx::Size&, VideoPixelFormat, VideoPixelStorage, int));
+
+  MOCK_METHOD2(OnError, void(const base::Location&, const std::string&));
+
+  double GetBufferPoolUtilization() const override { return 0.0; }
+
+  MOCK_METHOD0(OnStarted, void());
+};
+
+class MockImageCaptureClient
+    : public base::RefCountedThreadSafe<MockImageCaptureClient> {
+ public:
+  // GMock doesn't support move-only arguments, so we use this forward method.
+  void DoOnGetPhotoState(mojom::PhotoStatePtr received_state) {
+    state = std::move(received_state);
+  }
+
+  MOCK_METHOD1(OnCorrectSetPhotoOptions, void(bool));
+
+  // GMock doesn't support move-only arguments, so we use this forward method.
+  void DoOnPhotoTaken(mojom::BlobPtr blob) {
+    EXPECT_TRUE(blob);
+    OnCorrectPhotoTaken();
+  }
+  MOCK_METHOD0(OnCorrectPhotoTaken, void(void));
+
+  mojom::PhotoStatePtr state;
+
+ private:
+  friend class base::RefCountedThreadSafe<MockImageCaptureClient>;
+  virtual ~MockImageCaptureClient() = default;
+};
+
+class MockMFMediaSource : public base::RefCountedThreadSafe<MockMFMediaSource>,
+                          public IMFMediaSource {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockMFMediaSource>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockMFMediaSource>::Release();
+    return 1U;
+  }
+  STDMETHOD(GetEvent)(DWORD dwFlags, IMFMediaEvent** ppEvent) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(BeginGetEvent)
+  (IMFAsyncCallback* pCallback, IUnknown* punkState) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(EndGetEvent)
+  (IMFAsyncResult* pResult, IMFMediaEvent** ppEvent) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(QueueEvent)
+  (MediaEventType met,
+   REFGUID guidExtendedType,
+   HRESULT hrStatus,
+   const PROPVARIANT* pvValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetCharacteristics)(DWORD* pdwCharacteristics) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(CreatePresentationDescriptor)
+  (IMFPresentationDescriptor** ppPresentationDescriptor) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(Start)
+  (IMFPresentationDescriptor* pPresentationDescriptor,
+   const GUID* pguidTimeFormat,
+   const PROPVARIANT* pvarStartPosition) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(Stop)(void) override { return E_NOTIMPL; }
+  STDMETHOD(Pause)(void) override { return E_NOTIMPL; }
+  STDMETHOD(Shutdown)(void) override { return E_NOTIMPL; }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockMFMediaSource>;
+  virtual ~MockMFMediaSource() = default;
+};
+
+class MockMFCaptureSource
+    : public base::RefCountedThreadSafe<MockMFCaptureSource>,
+      public IMFCaptureSource {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockMFCaptureSource>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockMFCaptureSource>::Release();
+    return 1U;
+  }
+  STDMETHOD(GetCaptureDeviceSource)
+  (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType,
+   IMFMediaSource** ppMediaSource) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetCaptureDeviceActivate)
+  (MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType,
+   IMFActivate** ppActivate) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetService)
+  (REFIID rguidService, REFIID riid, IUnknown** ppUnknown) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(AddEffect)(DWORD dwSourceStreamIndex, IUnknown* pUnknown) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(RemoveEffect)
+  (DWORD dwSourceStreamIndex, IUnknown* pUnknown) override { return E_NOTIMPL; }
+  STDMETHOD(RemoveAllEffects)(DWORD dwSourceStreamIndex) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetAvailableDeviceMediaType)
+  (DWORD stream_index,
+   DWORD media_type_index,
+   IMFMediaType** media_type) override {
+    return DoGetAvailableDeviceMediaType(stream_index, media_type_index,
+                                         media_type);
+  }
+
+  MOCK_METHOD3(DoGetAvailableDeviceMediaType,
+               HRESULT(DWORD, DWORD, IMFMediaType**));
+
+  STDMETHOD(SetCurrentDeviceMediaType)
+  (DWORD dwSourceStreamIndex, IMFMediaType* pMediaType) override {
+    return S_OK;
+  }
+
+  STDMETHOD(GetCurrentDeviceMediaType)
+  (DWORD stream_index, IMFMediaType** media_type) {
+    return DoGetCurrentDeviceMediaType(stream_index, media_type);
+  }
+  MOCK_METHOD2(DoGetCurrentDeviceMediaType, HRESULT(DWORD, IMFMediaType**));
+
+  STDMETHOD(GetDeviceStreamCount)(DWORD* count) {
+    return DoGetDeviceStreamCount(count);
+  }
+  MOCK_METHOD1(DoGetDeviceStreamCount, HRESULT(DWORD*));
+
+  STDMETHOD(GetDeviceStreamCategory)
+  (DWORD stream_index, MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
+    return DoGetDeviceStreamCategory(stream_index, category);
+  }
+  MOCK_METHOD2(DoGetDeviceStreamCategory,
+               HRESULT(DWORD, MF_CAPTURE_ENGINE_STREAM_CATEGORY*));
+
+  STDMETHOD(GetMirrorState)(DWORD dwStreamIndex, BOOL* pfMirrorState) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetMirrorState)(DWORD dwStreamIndex, BOOL fMirrorState) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetStreamIndexFromFriendlyName)
+  (UINT32 uifriendlyName, DWORD* pdwActualStreamIndex) override {
+    return E_NOTIMPL;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockMFCaptureSource>;
+  virtual ~MockMFCaptureSource() = default;
+};
+
+class MockCapturePreviewSink
+    : public base::RefCountedThreadSafe<MockCapturePreviewSink>,
+      public IMFCapturePreviewSink {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
+    if (riid == IID_IUnknown || riid == IID_IMFCapturePreviewSink) {
+      AddRef();
+      *object = this;
+      return S_OK;
+    }
+    return E_NOINTERFACE;
+  }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockCapturePreviewSink>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockCapturePreviewSink>::Release();
+    return 1U;
+  }
+  STDMETHOD(GetOutputMediaType)
+  (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetService)
+  (DWORD dwSinkStreamIndex,
+   REFGUID rguidService,
+   REFIID riid,
+   IUnknown** ppUnknown) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(AddStream)
+  (DWORD dwSourceStreamIndex,
+   IMFMediaType* pMediaType,
+   IMFAttributes* pAttributes,
+   DWORD* pdwSinkStreamIndex) override {
+    return S_OK;
+  }
+  STDMETHOD(Prepare)(void) override { return E_NOTIMPL; }
+  STDMETHOD(RemoveAllStreams)(void) override { return S_OK; }
+  STDMETHOD(SetRenderHandle)(HANDLE handle) override { return E_NOTIMPL; }
+  STDMETHOD(SetRenderSurface)(IUnknown* pSurface) override { return E_NOTIMPL; }
+  STDMETHOD(UpdateVideo)
+  (const MFVideoNormalizedRect* pSrc,
+   const RECT* pDst,
+   const COLORREF* pBorderClr) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetSampleCallback)
+  (DWORD dwStreamSinkIndex,
+   IMFCaptureEngineOnSampleCallback* pCallback) override {
+    sample_callback = pCallback;
+    return S_OK;
+  }
+  STDMETHOD(GetMirrorState)(BOOL* pfMirrorState) override { return E_NOTIMPL; }
+  STDMETHOD(SetMirrorState)(BOOL fMirrorState) override { return E_NOTIMPL; }
+  STDMETHOD(GetRotation)
+  (DWORD dwStreamIndex, DWORD* pdwRotationValue) override { return E_NOTIMPL; }
+  STDMETHOD(SetRotation)(DWORD dwStreamIndex, DWORD dwRotationValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetCustomSink)(IMFMediaSink* pMediaSink) override {
+    return E_NOTIMPL;
+  }
+
+  scoped_refptr<IMFCaptureEngineOnSampleCallback> sample_callback;
+
+ private:
+  friend class base::RefCountedThreadSafe<MockCapturePreviewSink>;
+  virtual ~MockCapturePreviewSink() = default;
+};
+
+class MockCapturePhotoSink
+    : public base::RefCountedThreadSafe<MockCapturePhotoSink>,
+      public IMFCapturePhotoSink {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
+    if (riid == IID_IUnknown || riid == IID_IMFCapturePhotoSink) {
+      AddRef();
+      *object = this;
+      return S_OK;
+    }
+    return E_NOINTERFACE;
+  }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockCapturePhotoSink>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockCapturePhotoSink>::Release();
+    return 1U;
+  }
+  STDMETHOD(GetOutputMediaType)
+  (DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetService)
+  (DWORD dwSinkStreamIndex,
+   REFGUID rguidService,
+   REFIID riid,
+   IUnknown** ppUnknown) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(AddStream)
+  (DWORD dwSourceStreamIndex,
+   IMFMediaType* pMediaType,
+   IMFAttributes* pAttributes,
+   DWORD* pdwSinkStreamIndex) override {
+    return S_OK;
+  }
+  STDMETHOD(Prepare)(void) override { return E_NOTIMPL; }
+  STDMETHOD(RemoveAllStreams)(void) override { return S_OK; }
+
+  STDMETHOD(SetOutputFileName)(LPCWSTR fileName) override { return E_NOTIMPL; }
+  STDMETHOD(SetSampleCallback)
+  (IMFCaptureEngineOnSampleCallback* pCallback) override {
+    sample_callback = pCallback;
+    return S_OK;
+  }
+  STDMETHOD(SetOutputByteStream)(IMFByteStream* pByteStream) override {
+    return E_NOTIMPL;
+  }
+
+  scoped_refptr<IMFCaptureEngineOnSampleCallback> sample_callback;
+
+ private:
+  friend class base::RefCountedThreadSafe<MockCapturePhotoSink>;
+  virtual ~MockCapturePhotoSink() = default;
+};
+
+class MockMFCaptureEngine
+    : public base::RefCountedThreadSafe<MockMFCaptureEngine>,
+      public IMFCaptureEngine {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) { return S_OK; }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockMFCaptureEngine>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockMFCaptureEngine>::Release();
+    return 1U;
+  }
+  STDMETHOD(Initialize)
+  (IMFCaptureEngineOnEventCallback* pEventCallback,
+   IMFAttributes* pAttributes,
+   IUnknown* pAudioSource,
+   IUnknown* pVideoSource) override {
+    EXPECT_TRUE(pEventCallback);
+    EXPECT_TRUE(pAttributes);
+    EXPECT_TRUE(pVideoSource);
+    event_callback = pEventCallback;
+    OnCorrectInitialize();
+    return S_OK;
+  }
+
+  MOCK_METHOD0(OnCorrectInitialize, void(void));
+
+  STDMETHOD(StartPreview)(void) override {
+    OnStartPreview();
+    return S_OK;
+  }
+
+  MOCK_METHOD0(OnStartPreview, void(void));
+
+  STDMETHOD(StopPreview)(void) override {
+    OnStopPreview();
+    return S_OK;
+  }
+
+  MOCK_METHOD0(OnStopPreview, void(void));
+
+  STDMETHOD(StartRecord)(void) override { return E_NOTIMPL; }
+  STDMETHOD(StopRecord)(BOOL bFinalize, BOOL bFlushUnprocessedSamples) {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(TakePhoto)(void) override {
+    OnTakePhoto();
+    return S_OK;
+  }
+  MOCK_METHOD0(OnTakePhoto, void(void));
+
+  STDMETHOD(GetSink)(MF_CAPTURE_ENGINE_SINK_TYPE type, IMFCaptureSink** sink) {
+    return DoGetSink(type, sink);
+  }
+  MOCK_METHOD2(DoGetSink,
+               HRESULT(MF_CAPTURE_ENGINE_SINK_TYPE, IMFCaptureSink**));
+
+  STDMETHOD(GetSource)(IMFCaptureSource** source) {
+    *source = DoGetSource();
+    return source ? S_OK : E_FAIL;
+  }
+  MOCK_METHOD0(DoGetSource, IMFCaptureSource*());
+
+  scoped_refptr<IMFCaptureEngineOnEventCallback> event_callback;
+
+ private:
+  friend class base::RefCountedThreadSafe<MockMFCaptureEngine>;
+  virtual ~MockMFCaptureEngine() = default;
+};
+
+class StubMFMediaType : public base::RefCountedThreadSafe<StubMFMediaType>,
+                        public IMFMediaType {
+ public:
+  StubMFMediaType(GUID major_type,
+                  GUID sub_type,
+                  int frame_width,
+                  int frame_height,
+                  int frame_rate)
+      : major_type_(major_type),
+        sub_type_(sub_type),
+        frame_width_(frame_width),
+        frame_height_(frame_height),
+        frame_rate_(frame_rate) {}
+
+  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<StubMFMediaType>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<StubMFMediaType>::Release();
+    return 1U;
+  }
+  STDMETHOD(GetItem)(REFGUID key, PROPVARIANT* value) override {
+    if (key == MF_MT_FRAME_SIZE) {
+      value->vt = VT_UI8;
+      value->uhVal.QuadPart = Pack2UINT32AsUINT64(frame_width_, frame_height_);
+      return S_OK;
+    }
+    if (key == MF_MT_FRAME_RATE) {
+      value->vt = VT_UI8;
+      value->uhVal.QuadPart = Pack2UINT32AsUINT64(frame_rate_, 1);
+      return S_OK;
+    }
+    if (key == MF_MT_PIXEL_ASPECT_RATIO) {
+      value->vt = VT_UI8;
+      value->uhVal.QuadPart = Pack2UINT32AsUINT64(1, 1);
+      return S_OK;
+    }
+    if (key == MF_MT_INTERLACE_MODE) {
+      value->vt = VT_UI4;
+      value->uintVal = MFVideoInterlace_Progressive;
+      return S_OK;
+    }
+    return E_FAIL;
+  }
+  STDMETHOD(GetItemType)(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(CompareItem)
+  (REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(Compare)
+  (IMFAttributes* pTheirs,
+   MF_ATTRIBUTES_MATCH_TYPE MatchType,
+   BOOL* pbResult) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetUINT32)(REFGUID key, UINT32* value) override {
+    if (key == MF_MT_INTERLACE_MODE) {
+      *value = MFVideoInterlace_Progressive;
+      return S_OK;
+    }
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetUINT64)(REFGUID key, UINT64* value) override {
+    if (key == MF_MT_FRAME_SIZE) {
+      *value = (long long)frame_width_ << 32 | frame_height_;
+      return S_OK;
+    }
+    if (key == MF_MT_FRAME_RATE) {
+      *value = (long long)frame_rate_ << 32 | 1;
+      return S_OK;
+    }
+    if (key == MF_MT_PIXEL_ASPECT_RATIO) {
+      *value = (long long)1 << 32 | 1;
+      return S_OK;
+    }
+    return E_FAIL;
+  }
+  STDMETHOD(GetDouble)(REFGUID guidKey, double* pfValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetGUID)(REFGUID key, GUID* value) override {
+    if (key == MF_MT_MAJOR_TYPE) {
+      *value = major_type_;
+      return S_OK;
+    }
+    if (key == MF_MT_SUBTYPE) {
+      *value = sub_type_;
+      return S_OK;
+    }
+    return E_FAIL;
+  }
+  STDMETHOD(GetStringLength)(REFGUID guidKey, UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetString)
+  (REFGUID guidKey,
+   LPWSTR pwszValue,
+   UINT32 cchBufSize,
+   UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetAllocatedString)
+  (REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetBlobSize)(REFGUID guidKey, UINT32* pcbBlobSize) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetBlob)
+  (REFGUID guidKey,
+   UINT8* pBuf,
+   UINT32 cbBufSize,
+   UINT32* pcbBlobSize) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetAllocatedBlob)
+  (REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetUnknown)(REFGUID guidKey, REFIID riid, LPVOID* ppv) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetItem)(REFGUID guidKey, REFPROPVARIANT Value) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(DeleteItem)(REFGUID guidKey) override { return E_NOTIMPL; }
+  STDMETHOD(DeleteAllItems)(void) override { return E_NOTIMPL; }
+  STDMETHOD(SetUINT32)(REFGUID guidKey, UINT32 unValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetUINT64)(REFGUID guidKey, UINT64 unValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetDouble)(REFGUID guidKey, double fValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetGUID)(REFGUID guidKey, REFGUID guidValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetString)(REFGUID guidKey, LPCWSTR wszValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetBlob)
+  (REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(SetUnknown)(REFGUID guidKey, IUnknown* pUnknown) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(LockStore)(void) override { return E_NOTIMPL; }
+  STDMETHOD(UnlockStore)(void) override { return E_NOTIMPL; }
+  STDMETHOD(GetCount)(UINT32* pcItems) override { return E_NOTIMPL; }
+  STDMETHOD(GetItemByIndex)
+  (UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(CopyAllItems)(IMFAttributes* pDest) override { return E_NOTIMPL; }
+  STDMETHOD(GetMajorType)(GUID* pguidMajorType) override { return E_NOTIMPL; }
+  STDMETHOD(IsCompressedFormat)(BOOL* pfCompressed) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(IsEqual)(IMFMediaType* pIMediaType, DWORD* pdwFlags) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetRepresentation)
+  (GUID guidRepresentation, LPVOID* ppvRepresentation) override {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(FreeRepresentation)
+  (GUID guidRepresentation, LPVOID pvRepresentation) override {
+    return E_NOTIMPL;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<StubMFMediaType>;
+  virtual ~StubMFMediaType() = default;
+
+  const GUID major_type_;
+  const GUID sub_type_;
+  const int frame_width_;
+  const int frame_height_;
+  const int frame_rate_;
+};
+
+class MockMFMediaEvent : public base::RefCountedThreadSafe<MockMFMediaEvent>,
+                         public IMFMediaEvent {
+ public:
+  STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MockMFMediaEvent>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MockMFMediaEvent>::Release();
+    return 1U;
+  }
+
+  STDMETHOD(GetItem)(REFGUID guidKey, PROPVARIANT* pValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetItemType)(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(CompareItem)
+  (REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(Compare)
+  (IMFAttributes* pTheirs,
+   MF_ATTRIBUTES_MATCH_TYPE MatchType,
+   BOOL* pbResult) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetUINT32)(REFGUID guidKey, UINT32* punValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetUINT64)(REFGUID guidKey, UINT64* punValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetDouble)(REFGUID guidKey, double* pfValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetGUID)(REFGUID guidKey, GUID* pguidValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetStringLength)(REFGUID guidKey, UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetString)
+  (REFGUID guidKey,
+   LPWSTR pwszValue,
+   UINT32 cchBufSize,
+   UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetAllocatedString)
+  (REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetBlobSize)(REFGUID guidKey, UINT32* pcbBlobSize) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetBlob)
+  (REFGUID guidKey,
+   UINT8* pBuf,
+   UINT32 cbBufSize,
+   UINT32* pcbBlobSize) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetAllocatedBlob)
+  (REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetUnknown)(REFGUID guidKey, REFIID riid, LPVOID* ppv) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetItem)(REFGUID guidKey, REFPROPVARIANT Value) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(DeleteItem)(REFGUID guidKey) override { return E_NOTIMPL; }
+
+  STDMETHOD(DeleteAllItems)(void) override { return E_NOTIMPL; }
+
+  STDMETHOD(SetUINT32)(REFGUID guidKey, UINT32 unValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetUINT64)(REFGUID guidKey, UINT64 unValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetDouble)(REFGUID guidKey, double fValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetGUID)(REFGUID guidKey, REFGUID guidValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetString)(REFGUID guidKey, LPCWSTR wszValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetBlob)
+  (REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(SetUnknown)(REFGUID guidKey, IUnknown* pUnknown) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(LockStore)(void) override { return E_NOTIMPL; }
+
+  STDMETHOD(UnlockStore)(void) override { return E_NOTIMPL; }
+
+  STDMETHOD(GetCount)(UINT32* pcItems) override { return E_NOTIMPL; }
+
+  STDMETHOD(GetItemByIndex)
+  (UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(CopyAllItems)(IMFAttributes* pDest) override { return E_NOTIMPL; }
+
+  STDMETHOD(GetType)(MediaEventType* pmet) override { return E_NOTIMPL; }
+
+  STDMETHOD(GetExtendedType)(GUID* pguidExtendedType) override {
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD(GetStatus)(HRESULT* status) override {
+    *status = DoGetStatus();
+    return S_OK;
+  }
+  MOCK_METHOD0(DoGetStatus, HRESULT());
+
+  STDMETHOD(GetValue)(PROPVARIANT* pvValue) override { return E_NOTIMPL; }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockMFMediaEvent>;
+  virtual ~MockMFMediaEvent() = default;
+};
+
+}  // namespace
+
+const int kArbitraryValidVideoWidth = 1920;
+const int kArbitraryValidVideoHeight = 1080;
+
+const int kArbitraryValidPhotoWidth = 3264;
+const int kArbitraryValidPhotoHeight = 2448;
+
+class VideoCaptureDeviceMFWinTest : public ::testing::Test {
+ protected:
+  VideoCaptureDeviceMFWinTest()
+      : media_source_(new MockMFMediaSource()),
+        engine_(new MockMFCaptureEngine()),
+        client_(new MockClient()),
+        image_capture_client_(new MockImageCaptureClient()),
+        device_(new VideoCaptureDeviceMFWin(media_source_, engine_)),
+        capture_source_(new MockMFCaptureSource()),
+        media_foundation_supported_(
+            VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()) {}
+
+  void SetUp() override {
+    if (!media_foundation_supported_)
+      return;
+    device_->set_max_retry_count_for_testing(3);
+    device_->set_retry_delay_in_ms_for_testing(1);
+
+    EXPECT_CALL(*(engine_.Get()), OnCorrectInitialize());
+    EXPECT_TRUE(device_->Init());
+    EXPECT_CALL(*(engine_.Get()), DoGetSource())
+        .WillRepeatedly(Invoke([this]() {
+          this->capture_source_->AddRef();
+          return this->capture_source_.get();
+        }));
+  }
+
+  bool ShouldSkipTest() {
+    if (media_foundation_supported_)
+      return false;
+    DVLOG(1) << "Media foundation is not supported by the current platform. "
+                "Skipping test.";
+    return true;
+  }
+
+  void PrepareMFDeviceWithOneVideoStream() {
+    EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
+        .WillRepeatedly(Invoke([](DWORD* stream_count) {
+          *stream_count = 1;
+          return S_OK;
+        }));
+    EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(0, _))
+        .WillRepeatedly(Invoke([](DWORD stream_index,
+                                  MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
+          *category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
+          return S_OK;
+        }));
+
+    EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(0, _, _))
+        .WillRepeatedly(Invoke([](DWORD stream_index, DWORD media_type_index,
+                                  IMFMediaType** media_type) {
+          if (media_type_index != 0)
+            return MF_E_NO_MORE_TYPES;
+
+          *media_type = new StubMFMediaType(
+              MFMediaType_Video, MFVideoFormat_MJPG, kArbitraryValidVideoWidth,
+              kArbitraryValidVideoHeight, 30);
+          (*media_type)->AddRef();
+
+          return S_OK;
+        }));
+
+    EXPECT_CALL(*(engine_.Get()),
+                DoGetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _))
+        .WillRepeatedly(Invoke(
+            [](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
+              *sink = new MockCapturePreviewSink();
+              (*sink)->AddRef();
+              return S_OK;
+            }));
+
+    EXPECT_CALL(*capture_source_, DoGetCurrentDeviceMediaType(_, _))
+        .WillRepeatedly(
+            Invoke([](DWORD stream_index, IMFMediaType** media_type) {
+              *media_type = new StubMFMediaType(
+                  MFMediaType_Video, MFVideoFormat_MJPG,
+                  kArbitraryValidVideoWidth, kArbitraryValidVideoHeight, 30);
+              (*media_type)->AddRef();
+              return S_OK;
+            }));
+  }
+
+  void PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream() {
+    EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
+        .WillRepeatedly(Invoke([](DWORD* stream_count) {
+          *stream_count = 2;
+          return S_OK;
+        }));
+    EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(_, _))
+        .WillRepeatedly(Invoke([](DWORD stream_index,
+                                  MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
+          if (stream_index == 0) {
+            *category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
+            return S_OK;
+          } else if (stream_index == 1) {
+            *category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_INDEPENDENT;
+            return S_OK;
+          }
+          return E_FAIL;
+        }));
+
+    auto get_device_media_type = [](DWORD stream_index,
+                                    IMFMediaType** media_type) {
+      if (stream_index == 0) {
+        *media_type = new StubMFMediaType(MFMediaType_Video, MFVideoFormat_MJPG,
+                                          kArbitraryValidVideoWidth,
+                                          kArbitraryValidVideoHeight, 30);
+        (*media_type)->AddRef();
+        return S_OK;
+      } else if (stream_index == 1) {
+        *media_type = new StubMFMediaType(
+            MFMediaType_Image, GUID_ContainerFormatJpeg,
+            kArbitraryValidPhotoWidth, kArbitraryValidPhotoHeight, 0);
+        (*media_type)->AddRef();
+        return S_OK;
+      }
+      return E_FAIL;
+    };
+
+    EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(_, _, _))
+        .WillRepeatedly(Invoke(
+            [get_device_media_type](DWORD stream_index, DWORD media_type_index,
+                                    IMFMediaType** media_type) {
+              if (media_type_index != 0)
+                return MF_E_NO_MORE_TYPES;
+              return get_device_media_type(stream_index, media_type);
+            }));
+
+    EXPECT_CALL(*(engine_.Get()), DoGetSink(_, _))
+        .WillRepeatedly(Invoke(
+            [](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
+              if (sink_type == MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW) {
+                *sink = new MockCapturePreviewSink();
+                (*sink)->AddRef();
+                return S_OK;
+              } else if (sink_type == MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO) {
+                *sink = new MockCapturePhotoSink();
+                (*sink)->AddRef();
+                return S_OK;
+              }
+              return E_FAIL;
+            }));
+
+    EXPECT_CALL(*capture_source_, DoGetCurrentDeviceMediaType(_, _))
+        .WillRepeatedly(Invoke(get_device_media_type));
+  }
+
+  Microsoft::WRL::ComPtr<MockMFMediaSource> media_source_;
+  Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine_;
+  std::unique_ptr<MockClient> client_;
+  scoped_refptr<MockImageCaptureClient> image_capture_client_;
+  std::unique_ptr<VideoCaptureDeviceMFWin> device_;
+  VideoCaptureFormat last_format_;
+
+  scoped_refptr<MockMFCaptureSource> capture_source_;
+
+ private:
+  const bool media_foundation_supported_;
+};
+
+// Expects StartPreview() to be called on AllocateAndStart()
+TEST_F(VideoCaptureDeviceMFWinTest, StartPreviewOnAllocateAndStart) {
+  if (ShouldSkipTest())
+    return;
+
+  PrepareMFDeviceWithOneVideoStream();
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+  EXPECT_CALL(*(engine_.Get()), OnStopPreview());
+
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+
+  device_->StopAndDeAllocate();
+}
+
+// Expects OnError() to be called on an errored IMFMediaEvent
+TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnErrorMediaEvent) {
+  if (ShouldSkipTest())
+    return;
+
+  PrepareMFDeviceWithOneVideoStream();
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+  EXPECT_CALL(*client_, OnError(_, _));
+  scoped_refptr<MockMFMediaEvent> media_event_error = new MockMFMediaEvent();
+  EXPECT_CALL(*media_event_error, DoGetStatus()).WillRepeatedly(Return(E_FAIL));
+
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+  engine_->event_callback->OnEvent(media_event_error.get());
+}
+
+// Allocates device with flaky methods failing with MF_E_INVALIDREQUEST and
+// expects the device to retry and start correctly
+TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFlakyInvalidRequest) {
+  if (ShouldSkipTest())
+    return;
+
+  EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
+      .Times(AtLeast(2))
+      .WillOnce(Return(MF_E_INVALIDREQUEST))
+      .WillRepeatedly(Invoke([](DWORD* stream_count) {
+        *stream_count = 1;
+        return S_OK;
+      }));
+  EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(0, _))
+      .Times(AtLeast(2))
+      .WillOnce(Return(MF_E_INVALIDREQUEST))
+      .WillRepeatedly(Invoke(
+          [](DWORD stream_index, MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
+            *category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
+            return S_OK;
+          }));
+
+  EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(0, _, _))
+      .Times(AtLeast(2))
+      .WillOnce(Return(MF_E_INVALIDREQUEST))
+      .WillRepeatedly(Invoke([](DWORD stream_index, DWORD media_type_index,
+                                IMFMediaType** media_type) {
+        if (media_type_index != 0)
+          return MF_E_NO_MORE_TYPES;
+
+        *media_type = new StubMFMediaType(MFMediaType_Video, MFVideoFormat_MJPG,
+                                          kArbitraryValidVideoWidth,
+                                          kArbitraryValidVideoHeight, 30);
+        (*media_type)->AddRef();
+
+        return S_OK;
+      }));
+
+  EXPECT_CALL(*(engine_.Get()),
+              DoGetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _))
+      .WillRepeatedly(Invoke(
+          [](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
+            *sink = new MockCapturePreviewSink();
+            (*sink)->AddRef();
+            return S_OK;
+          }));
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+}
+
+// Allocates device with methods always failing with MF_E_INVALIDREQUEST and
+// expects the device to give up and call OnError()
+TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFailingInvalidRequest) {
+  if (ShouldSkipTest())
+    return;
+
+  EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
+      .WillRepeatedly(Return(MF_E_INVALIDREQUEST));
+
+  EXPECT_CALL(*client_, OnError(_, _));
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+}
+
+// Given an |IMFCaptureSource| offering a video stream without photo stream to
+// |VideoCaptureDevice|, when asking the photo state from |VideoCaptureDevice|
+// then expect the returned state to match the video resolution
+TEST_F(VideoCaptureDeviceMFWinTest, GetPhotoStateViaVideoStream) {
+  if (ShouldSkipTest())
+    return;
+
+  PrepareMFDeviceWithOneVideoStream();
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+
+  VideoCaptureDevice::GetPhotoStateCallback get_photo_state_callback =
+      base::BindOnce(&MockImageCaptureClient::DoOnGetPhotoState,
+                     image_capture_client_);
+  device_->GetPhotoState(std::move(get_photo_state_callback));
+
+  mojom::PhotoState* state = image_capture_client_->state.get();
+  ASSERT_EQ(state->width->min, kArbitraryValidVideoWidth);
+  ASSERT_EQ(state->width->current, kArbitraryValidVideoWidth);
+  ASSERT_EQ(state->width->max, kArbitraryValidVideoWidth);
+
+  ASSERT_EQ(state->height->min, kArbitraryValidVideoHeight);
+  ASSERT_EQ(state->height->current, kArbitraryValidVideoHeight);
+  ASSERT_EQ(state->height->max, kArbitraryValidVideoHeight);
+}
+
+// Given an |IMFCaptureSource| offering a video stream and a photo stream to
+// |VideoCaptureDevice|, when asking the photo state from |VideoCaptureDevice|
+// then expect the returned state to match the available photo resolution
+TEST_F(VideoCaptureDeviceMFWinTest, GetPhotoStateViaPhotoStream) {
+  if (ShouldSkipTest())
+    return;
+
+  PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream();
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+  VideoCaptureDevice::GetPhotoStateCallback get_photo_state_callback =
+      base::BindOnce(&MockImageCaptureClient::DoOnGetPhotoState,
+                     image_capture_client_);
+  device_->GetPhotoState(std::move(get_photo_state_callback));
+
+  mojom::PhotoState* state = image_capture_client_->state.get();
+  ASSERT_EQ(state->width->min, kArbitraryValidPhotoWidth);
+  ASSERT_EQ(state->width->current, kArbitraryValidPhotoWidth);
+  ASSERT_EQ(state->width->max, kArbitraryValidPhotoWidth);
+
+  ASSERT_EQ(state->height->min, kArbitraryValidPhotoHeight);
+  ASSERT_EQ(state->height->current, kArbitraryValidPhotoHeight);
+  ASSERT_EQ(state->height->max, kArbitraryValidPhotoHeight);
+}
+
+// Given an |IMFCaptureSource| offering a video stream and a photo stream to
+// |VideoCaptureDevice|, when taking photo from |VideoCaptureDevice| then
+// expect IMFCaptureEngine::TakePhoto() to be called
+TEST_F(VideoCaptureDeviceMFWinTest, TakePhotoViaPhotoStream) {
+  if (ShouldSkipTest())
+    return;
+
+  PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream();
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+
+  EXPECT_CALL(*(engine_.Get()), OnTakePhoto());
+
+  device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
+  VideoCaptureDevice::TakePhotoCallback take_photo_callback = base::BindOnce(
+      &MockImageCaptureClient::DoOnPhotoTaken, image_capture_client_);
+  device_->TakePhoto(std::move(take_photo_callback));
+}
+
+}  // namespace media
\ No newline at end of file