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