1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <mfidl.h>
6 
7 #include <ks.h>
8 #include <mfapi.h>
9 #include <mferror.h>
10 #include <stddef.h>
11 
12 #include "base/bind.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "media/capture/video/win/video_capture_device_factory_win.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 using ::testing::Mock;
19 
20 namespace media {
21 
22 namespace {
23 
24 // MediaFoundation devices
25 const wchar_t* kMFDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
26 const wchar_t* kMFDeviceName0 = L"Device 0";
27 
28 const wchar_t* kMFDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00";
29 const wchar_t* kMFDeviceName1 = L"Device 1";
30 
31 const wchar_t* kMFDeviceId2 = L"\\\\?\\usb#vid_0002&pid_0002&mi_00";
32 const wchar_t* kMFDeviceName2 = L"Device 2";
33 
34 const wchar_t* kMFDeviceId5 = L"\\\\?\\usb#vid_0005&pid_0005&mi_00";
35 const wchar_t* kMFDeviceName5 = L"Dazzle";
36 
37 const wchar_t* kMFDeviceId6 = L"\\\\?\\usb#vid_eb1a&pid_2860&mi_00";
38 const wchar_t* kMFDeviceName6 = L"Empia Device";
39 
GetMFSupportedFormats(const VideoCaptureDeviceDescriptor & device,VideoCaptureFormats * formats)40 void GetMFSupportedFormats(const VideoCaptureDeviceDescriptor& device,
41                            VideoCaptureFormats* formats) {
42   if (device.device_id == base::SysWideToUTF8(kMFDeviceId6)) {
43     VideoCaptureFormat arbitrary_format;
44     formats->emplace_back(arbitrary_format);
45   }
46 }
47 
48 // DirectShow devices
49 const wchar_t* kDirectShowDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
50 const wchar_t* kDirectShowDeviceName0 = L"Device 0";
51 
52 const wchar_t* kDirectShowDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00#1";
53 const wchar_t* kDirectShowDeviceName1 = L"Device 1";
54 
55 const wchar_t* kDirectShowDeviceId3 = L"Virtual Camera 3";
56 const wchar_t* kDirectShowDeviceName3 = L"Virtual Camera";
57 
58 const wchar_t* kDirectShowDeviceId4 = L"Virtual Camera 4";
59 const wchar_t* kDirectShowDeviceName4 = L"Virtual Camera";
60 
61 const wchar_t* kDirectShowDeviceId5 = L"\\\\?\\usb#vid_0005&pid_0005&mi_00#5";
62 const wchar_t* kDirectShowDeviceName5 = L"Dazzle";
63 
64 const wchar_t* kDirectShowDeviceId6 = L"\\\\?\\usb#vid_eb1a&pid_2860&mi_00";
65 const wchar_t* kDirectShowDeviceName6 = L"Empia Device";
66 
GetDirectShowSupportedFormats(const VideoCaptureDeviceDescriptor & device,VideoCaptureFormats * formats)67 void GetDirectShowSupportedFormats(const VideoCaptureDeviceDescriptor& device,
68                                    VideoCaptureFormats* formats) {
69   if (device.device_id == base::SysWideToUTF8(kDirectShowDeviceId5)) {
70     VideoCaptureFormat arbitrary_format;
71     formats->emplace_back(arbitrary_format);
72   }
73 }
74 
75 using iterator = VideoCaptureDeviceDescriptors::const_iterator;
FindDescriptorInRange(iterator begin,iterator end,const std::string & device_id)76 iterator FindDescriptorInRange(iterator begin,
77                                iterator end,
78                                const std::string& device_id) {
79   return std::find_if(
80       begin, end, [device_id](const VideoCaptureDeviceDescriptor& descriptor) {
81         return device_id == descriptor.device_id;
82       });
83 }
84 
85 class MockMFActivate : public base::RefCountedThreadSafe<MockMFActivate>,
86                        public IMFActivate {
87  public:
MockMFActivate(const std::wstring & symbolic_link,const std::wstring & name,bool kscategory_video_camera,bool kscategory_sensor_camera)88   MockMFActivate(const std::wstring& symbolic_link,
89                  const std::wstring& name,
90                  bool kscategory_video_camera,
91                  bool kscategory_sensor_camera)
92       : symbolic_link_(symbolic_link),
93         name_(name),
94         kscategory_video_camera_(kscategory_video_camera),
95         kscategory_sensor_camera_(kscategory_sensor_camera) {}
96 
MatchesQuery(IMFAttributes * query,HRESULT * status)97   bool MatchesQuery(IMFAttributes* query, HRESULT* status) {
98     UINT32 count;
99     *status = query->GetCount(&count);
100     if (FAILED(*status))
101       return false;
102     GUID value;
103     *status = query->GetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &value);
104     if (FAILED(*status))
105       return false;
106     if (value != MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)
107       return false;
108     *status = query->GetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
109                              &value);
110     if (SUCCEEDED(*status)) {
111       if ((value == KSCATEGORY_SENSOR_CAMERA && kscategory_sensor_camera_) ||
112           (value == KSCATEGORY_VIDEO_CAMERA && kscategory_video_camera_))
113         return true;
114     } else if (*status == MF_E_ATTRIBUTENOTFOUND) {
115       // When no category attribute is specified, it should behave the same as
116       // if KSCATEGORY_VIDEO_CAMERA is specified.
117       *status = S_OK;
118       if (kscategory_video_camera_)
119         return true;
120     }
121     return false;
122   }
123 
QueryInterface(REFIID riid,void ** ppvObject)124   IFACEMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override {
125     return E_NOTIMPL;
126   }
AddRef()127   IFACEMETHODIMP_(ULONG) AddRef() override {
128     base::RefCountedThreadSafe<MockMFActivate>::AddRef();
129     return 1U;
130   }
131 
Release()132   IFACEMETHODIMP_(ULONG) Release() override {
133     base::RefCountedThreadSafe<MockMFActivate>::Release();
134     return 1U;
135   }
GetItem(REFGUID key,PROPVARIANT * value)136   IFACEMETHODIMP GetItem(REFGUID key, PROPVARIANT* value) override {
137     return E_FAIL;
138   }
GetItemType(REFGUID guidKey,MF_ATTRIBUTE_TYPE * pType)139   IFACEMETHODIMP GetItemType(REFGUID guidKey,
140                              MF_ATTRIBUTE_TYPE* pType) override {
141     return E_NOTIMPL;
142   }
CompareItem(REFGUID guidKey,REFPROPVARIANT Value,BOOL * pbResult)143   IFACEMETHODIMP CompareItem(REFGUID guidKey,
144                              REFPROPVARIANT Value,
145                              BOOL* pbResult) override {
146     return E_NOTIMPL;
147   }
Compare(IMFAttributes * pTheirs,MF_ATTRIBUTES_MATCH_TYPE MatchType,BOOL * pbResult)148   IFACEMETHODIMP Compare(IMFAttributes* pTheirs,
149                          MF_ATTRIBUTES_MATCH_TYPE MatchType,
150                          BOOL* pbResult) override {
151     return E_NOTIMPL;
152   }
GetUINT32(REFGUID key,UINT32 * value)153   IFACEMETHODIMP GetUINT32(REFGUID key, UINT32* value) override {
154     if (key == MF_MT_INTERLACE_MODE) {
155       *value = MFVideoInterlace_Progressive;
156       return S_OK;
157     }
158     return E_NOTIMPL;
159   }
GetUINT64(REFGUID key,UINT64 * value)160   IFACEMETHODIMP GetUINT64(REFGUID key, UINT64* value) override {
161     return E_FAIL;
162   }
GetDouble(REFGUID guidKey,double * pfValue)163   IFACEMETHODIMP GetDouble(REFGUID guidKey, double* pfValue) override {
164     return E_NOTIMPL;
165   }
GetGUID(REFGUID key,GUID * value)166   IFACEMETHODIMP GetGUID(REFGUID key, GUID* value) override { return E_FAIL; }
GetStringLength(REFGUID guidKey,UINT32 * pcchLength)167   IFACEMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength) override {
168     return E_NOTIMPL;
169   }
GetString(REFGUID guidKey,LPWSTR pwszValue,UINT32 cchBufSize,UINT32 * pcchLength)170   IFACEMETHODIMP GetString(REFGUID guidKey,
171                            LPWSTR pwszValue,
172                            UINT32 cchBufSize,
173                            UINT32* pcchLength) override {
174     return E_NOTIMPL;
175   }
GetAllocatedString(REFGUID guidKey,LPWSTR * ppwszValue,UINT32 * pcchLength)176   IFACEMETHODIMP GetAllocatedString(REFGUID guidKey,
177                                     LPWSTR* ppwszValue,
178                                     UINT32* pcchLength) override {
179     std::wstring value;
180     if (guidKey == MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK) {
181       value = symbolic_link_;
182     } else if (guidKey == MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME) {
183       value = name_;
184     } else {
185       return E_NOTIMPL;
186     }
187     *ppwszValue = static_cast<wchar_t*>(
188         CoTaskMemAlloc((value.size() + 1) * sizeof(wchar_t)));
189     wcscpy(*ppwszValue, value.c_str());
190     *pcchLength = value.length();
191     return S_OK;
192   }
GetBlobSize(REFGUID guidKey,UINT32 * pcbBlobSize)193   IFACEMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) override {
194     return E_NOTIMPL;
195   }
GetBlob(REFGUID guidKey,UINT8 * pBuf,UINT32 cbBufSize,UINT32 * pcbBlobSize)196   IFACEMETHODIMP GetBlob(REFGUID guidKey,
197                          UINT8* pBuf,
198                          UINT32 cbBufSize,
199                          UINT32* pcbBlobSize) override {
200     return E_NOTIMPL;
201   }
GetAllocatedBlob(REFGUID guidKey,UINT8 ** ppBuf,UINT32 * pcbSize)202   IFACEMETHODIMP GetAllocatedBlob(REFGUID guidKey,
203                                   UINT8** ppBuf,
204                                   UINT32* pcbSize) override {
205     return E_NOTIMPL;
206   }
GetUnknown(REFGUID guidKey,REFIID riid,LPVOID * ppv)207   IFACEMETHODIMP GetUnknown(REFGUID guidKey,
208                             REFIID riid,
209                             LPVOID* ppv) override {
210     return E_NOTIMPL;
211   }
SetItem(REFGUID guidKey,REFPROPVARIANT Value)212   IFACEMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) override {
213     return E_NOTIMPL;
214   }
DeleteItem(REFGUID guidKey)215   IFACEMETHODIMP DeleteItem(REFGUID guidKey) override { return E_NOTIMPL; }
DeleteAllItems(void)216   IFACEMETHODIMP DeleteAllItems(void) override { return E_NOTIMPL; }
SetUINT32(REFGUID guidKey,UINT32 unValue)217   IFACEMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) override {
218     return E_NOTIMPL;
219   }
SetUINT64(REFGUID guidKey,UINT64 unValue)220   IFACEMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue) override {
221     return E_NOTIMPL;
222   }
SetDouble(REFGUID guidKey,double fValue)223   IFACEMETHODIMP SetDouble(REFGUID guidKey, double fValue) override {
224     return E_NOTIMPL;
225   }
SetGUID(REFGUID guidKey,REFGUID guidValue)226   IFACEMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) override {
227     return E_NOTIMPL;
228   }
SetString(REFGUID guidKey,LPCWSTR wszValue)229   IFACEMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) override {
230     return E_NOTIMPL;
231   }
SetBlob(REFGUID guidKey,const UINT8 * pBuf,UINT32 cbBufSize)232   IFACEMETHODIMP SetBlob(REFGUID guidKey,
233                          const UINT8* pBuf,
234                          UINT32 cbBufSize) override {
235     return E_NOTIMPL;
236   }
SetUnknown(REFGUID guidKey,IUnknown * pUnknown)237   IFACEMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown) override {
238     return E_NOTIMPL;
239   }
LockStore(void)240   IFACEMETHODIMP LockStore(void) override { return E_NOTIMPL; }
UnlockStore(void)241   IFACEMETHODIMP UnlockStore(void) override { return E_NOTIMPL; }
GetCount(UINT32 * pcItems)242   IFACEMETHODIMP GetCount(UINT32* pcItems) override { return E_NOTIMPL; }
GetItemByIndex(UINT32 unIndex,GUID * pguidKey,PROPVARIANT * pValue)243   IFACEMETHODIMP GetItemByIndex(UINT32 unIndex,
244                                 GUID* pguidKey,
245                                 PROPVARIANT* pValue) override {
246     return E_NOTIMPL;
247   }
CopyAllItems(IMFAttributes * pDest)248   IFACEMETHODIMP CopyAllItems(IMFAttributes* pDest) override {
249     return E_NOTIMPL;
250   }
ActivateObject(REFIID riid,void ** ppv)251   IFACEMETHODIMP ActivateObject(REFIID riid, void** ppv) override {
252     return E_NOTIMPL;
253   }
DetachObject(void)254   IFACEMETHODIMP DetachObject(void) override { return E_NOTIMPL; }
ShutdownObject(void)255   IFACEMETHODIMP ShutdownObject(void) override { return E_NOTIMPL; }
256 
257  private:
258   friend class base::RefCountedThreadSafe<MockMFActivate>;
259   virtual ~MockMFActivate() = default;
260 
261   const std::wstring symbolic_link_;
262   const std::wstring name_;
263   const bool kscategory_video_camera_;
264   const bool kscategory_sensor_camera_;
265 };
266 
267 class StubPropertyBag : public base::RefCountedThreadSafe<StubPropertyBag>,
268                         public IPropertyBag {
269  public:
StubPropertyBag(const wchar_t * device_path,const wchar_t * description)270   StubPropertyBag(const wchar_t* device_path, const wchar_t* description)
271       : device_path_(device_path), description_(description) {}
272 
QueryInterface(REFIID riid,void ** ppvObject)273   IFACEMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override {
274     return E_NOTIMPL;
275   }
AddRef(void)276   IFACEMETHODIMP_(ULONG) AddRef(void) override {
277     base::RefCountedThreadSafe<StubPropertyBag>::AddRef();
278     return 1U;
279   }
Release(void)280   IFACEMETHODIMP_(ULONG) Release(void) override {
281     base::RefCountedThreadSafe<StubPropertyBag>::Release();
282     return 1U;
283   }
Read(LPCOLESTR pszPropName,VARIANT * pVar,IErrorLog * pErrorLog)284   IFACEMETHODIMP Read(LPCOLESTR pszPropName,
285                       VARIANT* pVar,
286                       IErrorLog* pErrorLog) override {
287     if (pszPropName == std::wstring(L"Description")) {
288       pVar->vt = VT_BSTR;
289       pVar->bstrVal = SysAllocString(description_);
290       return S_OK;
291     }
292     if (pszPropName == std::wstring(L"DevicePath")) {
293       pVar->vt = VT_BSTR;
294       pVar->bstrVal = SysAllocString(device_path_);
295       return S_OK;
296     }
297     return E_NOTIMPL;
298   }
Write(LPCOLESTR pszPropName,VARIANT * pVar)299   IFACEMETHODIMP Write(LPCOLESTR pszPropName, VARIANT* pVar) override {
300     return E_NOTIMPL;
301   }
302 
303  private:
304   friend class base::RefCountedThreadSafe<StubPropertyBag>;
305   virtual ~StubPropertyBag() = default;
306 
307   const wchar_t* device_path_;
308   const wchar_t* description_;
309 };
310 
311 class StubMoniker : public base::RefCountedThreadSafe<StubMoniker>,
312                     public IMoniker {
313  public:
StubMoniker(const wchar_t * device_path,const wchar_t * description)314   StubMoniker(const wchar_t* device_path, const wchar_t* description)
315       : device_path_(device_path), description_(description) {}
316 
QueryInterface(REFIID riid,void ** ppvObject)317   IFACEMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override {
318     return E_NOTIMPL;
319   }
AddRef(void)320   IFACEMETHODIMP_(ULONG) AddRef(void) override {
321     base::RefCountedThreadSafe<StubMoniker>::AddRef();
322     return 1U;
323   }
Release(void)324   IFACEMETHODIMP_(ULONG) Release(void) override {
325     base::RefCountedThreadSafe<StubMoniker>::Release();
326     return 1U;
327   }
GetClassID(CLSID * pClassID)328   IFACEMETHODIMP GetClassID(CLSID* pClassID) override { return E_NOTIMPL; }
IsDirty(void)329   IFACEMETHODIMP IsDirty(void) override { return E_NOTIMPL; }
Load(IStream * pStm)330   IFACEMETHODIMP Load(IStream* pStm) override { return E_NOTIMPL; }
Save(IStream * pStm,BOOL fClearDirty)331   IFACEMETHODIMP Save(IStream* pStm, BOOL fClearDirty) override {
332     return E_NOTIMPL;
333   }
GetSizeMax(ULARGE_INTEGER * pcbSize)334   IFACEMETHODIMP GetSizeMax(ULARGE_INTEGER* pcbSize) override {
335     return E_NOTIMPL;
336   }
BindToObject(IBindCtx * pbc,IMoniker * pmkToLeft,REFIID riidResult,void ** ppvResult)337   IFACEMETHODIMP BindToObject(IBindCtx* pbc,
338                               IMoniker* pmkToLeft,
339                               REFIID riidResult,
340                               void** ppvResult) override {
341     return E_NOTIMPL;
342   }
BindToStorage(IBindCtx * pbc,IMoniker * pmkToLeft,REFIID riid,void ** ppvObj)343   IFACEMETHODIMP BindToStorage(IBindCtx* pbc,
344                                IMoniker* pmkToLeft,
345                                REFIID riid,
346                                void** ppvObj) override {
347     StubPropertyBag* propertyBag =
348         new StubPropertyBag(device_path_, description_);
349     propertyBag->AddRef();
350     *ppvObj = propertyBag;
351     return S_OK;
352   }
Reduce(IBindCtx * pbc,DWORD dwReduceHowFar,IMoniker ** ppmkToLeft,IMoniker ** ppmkReduced)353   IFACEMETHODIMP Reduce(IBindCtx* pbc,
354                         DWORD dwReduceHowFar,
355                         IMoniker** ppmkToLeft,
356                         IMoniker** ppmkReduced) override {
357     return E_NOTIMPL;
358   }
ComposeWith(IMoniker * pmkRight,BOOL fOnlyIfNotGeneric,IMoniker ** ppmkComposite)359   IFACEMETHODIMP ComposeWith(IMoniker* pmkRight,
360                              BOOL fOnlyIfNotGeneric,
361                              IMoniker** ppmkComposite) override {
362     return E_NOTIMPL;
363   }
Enum(BOOL fForward,IEnumMoniker ** ppenumMoniker)364   IFACEMETHODIMP Enum(BOOL fForward, IEnumMoniker** ppenumMoniker) override {
365     return E_NOTIMPL;
366   }
IsEqual(IMoniker * pmkOtherMoniker)367   IFACEMETHODIMP IsEqual(IMoniker* pmkOtherMoniker) override {
368     return E_NOTIMPL;
369   }
Hash(DWORD * pdwHash)370   IFACEMETHODIMP Hash(DWORD* pdwHash) override { return E_NOTIMPL; }
IsRunning(IBindCtx * pbc,IMoniker * pmkToLeft,IMoniker * pmkNewlyRunning)371   IFACEMETHODIMP IsRunning(IBindCtx* pbc,
372                            IMoniker* pmkToLeft,
373                            IMoniker* pmkNewlyRunning) override {
374     return E_NOTIMPL;
375   }
GetTimeOfLastChange(IBindCtx * pbc,IMoniker * pmkToLeft,FILETIME * pFileTime)376   IFACEMETHODIMP GetTimeOfLastChange(IBindCtx* pbc,
377                                      IMoniker* pmkToLeft,
378                                      FILETIME* pFileTime) override {
379     return E_NOTIMPL;
380   }
Inverse(IMoniker ** ppmk)381   IFACEMETHODIMP Inverse(IMoniker** ppmk) override { return E_NOTIMPL; }
CommonPrefixWith(IMoniker * pmkOther,IMoniker ** ppmkPrefix)382   IFACEMETHODIMP CommonPrefixWith(IMoniker* pmkOther,
383                                   IMoniker** ppmkPrefix) override {
384     return E_NOTIMPL;
385   }
RelativePathTo(IMoniker * pmkOther,IMoniker ** ppmkRelPath)386   IFACEMETHODIMP RelativePathTo(IMoniker* pmkOther,
387                                 IMoniker** ppmkRelPath) override {
388     return E_NOTIMPL;
389   }
GetDisplayName(IBindCtx * pbc,IMoniker * pmkToLeft,LPOLESTR * ppszDisplayName)390   IFACEMETHODIMP GetDisplayName(IBindCtx* pbc,
391                                 IMoniker* pmkToLeft,
392                                 LPOLESTR* ppszDisplayName) override {
393     return E_NOTIMPL;
394   }
ParseDisplayName(IBindCtx * pbc,IMoniker * pmkToLeft,LPOLESTR pszDisplayName,ULONG * pchEaten,IMoniker ** ppmkOut)395   IFACEMETHODIMP ParseDisplayName(IBindCtx* pbc,
396                                   IMoniker* pmkToLeft,
397                                   LPOLESTR pszDisplayName,
398                                   ULONG* pchEaten,
399                                   IMoniker** ppmkOut) override {
400     return E_NOTIMPL;
401   }
IsSystemMoniker(DWORD * pdwMksys)402   IFACEMETHODIMP IsSystemMoniker(DWORD* pdwMksys) override { return E_NOTIMPL; }
403 
404  private:
405   friend class base::RefCountedThreadSafe<StubMoniker>;
406   virtual ~StubMoniker() = default;
407 
408   const wchar_t* device_path_;
409   const wchar_t* description_;
410 };
411 
412 class StubEnumMoniker : public base::RefCountedThreadSafe<StubEnumMoniker>,
413                         public IEnumMoniker {
414  public:
AddMoniker(StubMoniker * moniker)415   void AddMoniker(StubMoniker* moniker) { monikers_.push_back(moniker); }
416 
QueryInterface(REFIID riid,void ** ppvObject)417   IFACEMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override {
418     return E_NOTIMPL;
419   }
AddRef()420   IFACEMETHODIMP_(ULONG) AddRef() override {
421     base::RefCountedThreadSafe<StubEnumMoniker>::AddRef();
422     return 1U;
423   }
Release()424   IFACEMETHODIMP_(ULONG) Release() override {
425     base::RefCountedThreadSafe<StubEnumMoniker>::Release();
426     return 1U;
427   }
Next(ULONG celt,IMoniker ** rgelt,ULONG * pceltFetched)428   IFACEMETHODIMP Next(ULONG celt,
429                       IMoniker** rgelt,
430                       ULONG* pceltFetched) override {
431     cursor_position_ = cursor_position_ + celt;
432     if (cursor_position_ >= monikers_.size())
433       return E_FAIL;
434     IMoniker* moniker = monikers_.at(cursor_position_);
435     *rgelt = moniker;
436     moniker->AddRef();
437     return S_OK;
438   }
Skip(ULONG celt)439   IFACEMETHODIMP Skip(ULONG celt) override { return E_NOTIMPL; }
Reset(void)440   IFACEMETHODIMP Reset(void) override {
441     cursor_position_ = unsigned(-1);
442     return S_OK;
443   }
Clone(IEnumMoniker ** ppenum)444   IFACEMETHODIMP Clone(IEnumMoniker** ppenum) override { return E_NOTIMPL; }
445 
446  private:
447   friend class base::RefCountedThreadSafe<StubEnumMoniker>;
448   virtual ~StubEnumMoniker() = default;
449 
450   std::vector<IMoniker*> monikers_;
451   ULONG cursor_position_ = unsigned(-1);
452 };
453 
MockMFEnumDeviceSources(IMFAttributes * attributes,IMFActivate *** devices,UINT32 * count)454 HRESULT __stdcall MockMFEnumDeviceSources(IMFAttributes* attributes,
455                                           IMFActivate*** devices,
456                                           UINT32* count) {
457   MockMFActivate* mock_devices[] = {
458       new MockMFActivate(kMFDeviceId0, kMFDeviceName0, true, false),
459       new MockMFActivate(kMFDeviceId1, kMFDeviceName1, true, true),
460       new MockMFActivate(kMFDeviceId2, kMFDeviceName2, false, true),
461       new MockMFActivate(kMFDeviceId5, kMFDeviceName5, true, false),
462       new MockMFActivate(kMFDeviceId6, kMFDeviceName6, true, false)};
463   // Iterate once to get the match count and check for errors.
464   *count = 0U;
465   HRESULT hr;
466   for (MockMFActivate* device : mock_devices) {
467     if (device->MatchesQuery(attributes, &hr))
468       (*count)++;
469     if (FAILED(hr))
470       return hr;
471   }
472   // Second iteration packs the returned devices and increments their
473   // reference count.
474   *devices = static_cast<IMFActivate**>(
475       CoTaskMemAlloc(sizeof(IMFActivate*) * (*count)));
476   int offset = 0;
477   for (MockMFActivate* device : mock_devices) {
478     if (!device->MatchesQuery(attributes, &hr))
479       continue;
480     *(*devices + offset++) = device;
481     device->AddRef();
482   }
483   return S_OK;
484 }
485 
EnumerateStubDirectShowDevices(IEnumMoniker ** enum_moniker)486 HRESULT EnumerateStubDirectShowDevices(IEnumMoniker** enum_moniker) {
487   StubMoniker* monikers[] = {
488       new StubMoniker(kDirectShowDeviceId0, kDirectShowDeviceName0),
489       new StubMoniker(kDirectShowDeviceId1, kDirectShowDeviceName1),
490       new StubMoniker(kDirectShowDeviceId3, kDirectShowDeviceName3),
491       new StubMoniker(kDirectShowDeviceId4, kDirectShowDeviceName4),
492       new StubMoniker(kDirectShowDeviceId5, kDirectShowDeviceName5),
493       new StubMoniker(kDirectShowDeviceId6, kDirectShowDeviceName6)};
494 
495   StubEnumMoniker* stub_enum_moniker = new StubEnumMoniker();
496   for (StubMoniker* moniker : monikers)
497     stub_enum_moniker->AddMoniker(moniker);
498 
499   stub_enum_moniker->AddRef();
500   *enum_moniker = stub_enum_moniker;
501   return S_OK;
502 }
503 
504 }  // namespace
505 
506 class VideoCaptureDeviceFactoryWinTest : public ::testing::Test {
507  protected:
VideoCaptureDeviceFactoryWinTest()508   VideoCaptureDeviceFactoryWinTest()
509       : media_foundation_supported_(
510             VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()) {}
511 
SetUp()512   void SetUp() override {
513     factory_.set_mf_get_supported_formats_func_for_testing(
514         base::BindRepeating(&GetMFSupportedFormats));
515     factory_.set_direct_show_get_supported_formats_func_for_testing(
516         base::BindRepeating(&GetDirectShowSupportedFormats));
517   }
518 
ShouldSkipMFTest()519   bool ShouldSkipMFTest() {
520     if (media_foundation_supported_)
521       return false;
522     DVLOG(1) << "Media foundation is not supported by the current platform. "
523                 "Skipping test.";
524     return true;
525   }
526 
527   VideoCaptureDeviceFactoryWin factory_;
528   const bool media_foundation_supported_;
529 };
530 
531 class VideoCaptureDeviceFactoryMFWinTest
532     : public VideoCaptureDeviceFactoryWinTest {
SetUp()533   void SetUp() override {
534     VideoCaptureDeviceFactoryWinTest::SetUp();
535     factory_.set_use_media_foundation_for_testing(true);
536   }
537 };
538 
TEST_F(VideoCaptureDeviceFactoryMFWinTest,GetDeviceDescriptors)539 TEST_F(VideoCaptureDeviceFactoryMFWinTest, GetDeviceDescriptors) {
540   if (ShouldSkipMFTest())
541     return;
542   factory_.set_mf_enum_device_sources_func_for_testing(
543       &MockMFEnumDeviceSources);
544   factory_.set_direct_show_enum_devices_func_for_testing(
545       base::BindRepeating(&EnumerateStubDirectShowDevices));
546   VideoCaptureDeviceDescriptors descriptors;
547   factory_.GetDeviceDescriptors(&descriptors);
548   EXPECT_EQ(descriptors.size(), 7U);
549   for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
550     // Verify that there are no duplicates.
551     EXPECT_EQ(FindDescriptorInRange(descriptors.begin(), it, it->device_id),
552               it);
553   }
554   iterator it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
555                                       base::SysWideToUTF8(kMFDeviceId0));
556   EXPECT_NE(it, descriptors.end());
557   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
558   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName0));
559 
560   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
561                              base::SysWideToUTF8(kMFDeviceId1));
562   EXPECT_NE(it, descriptors.end());
563   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
564   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName1));
565 
566   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
567                              base::SysWideToUTF8(kMFDeviceId2));
568   EXPECT_NE(it, descriptors.end());
569   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR);
570   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName2));
571 
572   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
573                              base::SysWideToUTF8(kDirectShowDeviceId3));
574   EXPECT_NE(it, descriptors.end());
575   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
576   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName3));
577 
578   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
579                              base::SysWideToUTF8(kDirectShowDeviceId4));
580   EXPECT_NE(it, descriptors.end());
581   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
582   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName4));
583 
584   // Devices that are listed in MediaFoundation but only report supported
585   // formats in DirectShow are expected to get enumerated with
586   // VideoCaptureApi::WIN_DIRECT_SHOW
587   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
588                              base::SysWideToUTF8(kDirectShowDeviceId5));
589   EXPECT_NE(it, descriptors.end());
590   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
591   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName5));
592 
593   // Devices that are listed in both MediaFoundation and DirectShow but are
594   // blacklisted for use with MediaFoundation are expected to get enumerated
595   // with VideoCaptureApi::WIN_DIRECT_SHOW.
596   it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
597                              base::SysWideToUTF8(kDirectShowDeviceId6));
598   EXPECT_NE(it, descriptors.end());
599   EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
600   EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName6));
601 }
602 
603 }  // namespace media
604