1 // Copyright 2014 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 "media/capture/video/win/video_capture_device_factory_win.h"
6 
7 #include <mfapi.h>
8 #include <mferror.h>
9 #include <objbase.h>
10 #include <stddef.h>
11 #include <windows.devices.enumeration.h>
12 #include <windows.foundation.collections.h>
13 #include <wrl.h>
14 #include <wrl/client.h>
15 
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/feature_list.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/no_destructor.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "base/win/core_winrt_util.h"
26 #include "base/win/scoped_co_mem.h"
27 #include "base/win/scoped_variant.h"
28 #include "base/win/windows_version.h"
29 #include "media/base/media_switches.h"
30 #include "media/base/win/mf_initializer.h"
31 #include "media/capture/video/win/metrics.h"
32 #include "media/capture/video/win/video_capture_device_mf_win.h"
33 #include "media/capture/video/win/video_capture_device_win.h"
34 
35 using Descriptor = media::VideoCaptureDeviceDescriptor;
36 using Descriptors = media::VideoCaptureDeviceDescriptors;
37 using base::win::GetActivationFactory;
38 using base::win::ScopedCoMem;
39 using base::win::ScopedHString;
40 using base::win::ScopedVariant;
41 using Microsoft::WRL::ComPtr;
42 
43 namespace media {
44 
45 namespace {
46 
47 // In Windows device identifiers, the USB VID and PID are preceded by the string
48 // "vid_" or "pid_".  The identifiers are each 4 bytes long.
49 const char kVidPrefix[] = "vid_";  // Also contains '\0'.
50 const char kPidPrefix[] = "pid_";  // Also contains '\0'.
51 const size_t kVidPidSize = 4;
52 
53 // Avoid enumerating and/or using certain devices due to they provoking crashes
54 // or any other reason (http://crbug.com/378494). This enum is defined for the
55 // purposes of UMA collection. Existing entries cannot be removed.
56 enum BlacklistedCameraNames {
57   BLACKLISTED_CAMERA_GOOGLE_CAMERA_ADAPTER = 0,
58   BLACKLISTED_CAMERA_IP_CAMERA = 1,
59   BLACKLISTED_CAMERA_CYBERLINK_WEBCAM_SPLITTER = 2,
60   BLACKLISTED_CAMERA_EPOCCAM = 3,
61   // This one must be last, and equal to the previous enumerated value.
62   BLACKLISTED_CAMERA_MAX = BLACKLISTED_CAMERA_EPOCCAM,
63 };
64 
65 #define UWP_ENUM_ERROR_HANDLER(hr, err_log)                         \
66   DLOG(WARNING) << err_log << logging::SystemErrorCodeToString(hr); \
67   origin_task_runner_->PostTask(FROM_HERE,                          \
68                                 base::BindOnce(device_info_callback, nullptr))
69 
70 // Blacklisted devices are identified by a characteristic prefix of the name.
71 // This prefix is used case-insensitively. This list must be kept in sync with
72 // |BlacklistedCameraNames|.
73 const char* const kBlacklistedCameraNames[] = {
74     // Name of a fake DirectShow filter on computers with GTalk installed.
75     "Google Camera Adapter",
76     // The following software WebCams cause crashes.
77     "IP Camera [JPEG/MJPEG]", "CyberLink Webcam Splitter", "EpocCam",
78 };
79 static_assert(base::size(kBlacklistedCameraNames) == BLACKLISTED_CAMERA_MAX + 1,
80               "kBlacklistedCameraNames should be same size as "
81               "BlacklistedCameraNames enum");
82 
83 // Use this list only for USB webcams.
84 const char* const kModelIdsBlacklistedForMediaFoundation[] = {
85     // Devices using Empia 2860 or 2820 chips, see https://crbug.com/849636.
86     "eb1a:2860", "eb1a:2820", "1ce6:2820",
87     // Elgato HD60 Pro
88     "12ab:0380",
89     // Sensoray 2253
90     "1943:2253",
91     // Dell E5440
92     "0c45:64d0", "0c45:64d2",
93     // Dell E7440
94     "1bcf:2985",
95     // Lenovo Thinkpad Model 20CG0006FMZ front and rear cameras, see
96     // also https://crbug.com/924528.
97     "04ca:7047", "04ca:7048",
98     // HP Elitebook 840 G1
99     "04f2:b3ed", "04f2:b3ca", "05c8:035d", "05c8:0369",
100     // HP HD Camera. See https://crbug.com/1011888.
101     "04ca:7095",
102     // RBG/IR camera for Windows Hello Face Auth. See https://crbug.com/984864.
103     "13d3:5257",
104     // Acer Aspire f5-573g. See https://crbug.com/1034644.
105     "0bda:57f2"};
106 
107 // Use this list only for non-USB webcams.
108 const char* const kDisplayNamesBlacklistedForMediaFoundation[] = {
109     // VMware Virtual Webcams cause hangs when there is no physical Webcam.
110     // See https://crbug.com/1044974.
111     "VMware Virtual Webcam"};
112 
113 const std::vector<
114     std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>>&
GetMFAttributes()115 GetMFAttributes() {
116   static const base::NoDestructor<std::vector<
117       std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>>>
118       mf_attributes({{{VideoCaptureApi::WIN_MEDIA_FOUNDATION,
119                        {
120                            {MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
121                             MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID},
122                        }},
123                       {VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR,
124                        {{MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
125                          MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID},
126                         {MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
127                          KSCATEGORY_SENSOR_CAMERA}}}}});
128   return *mf_attributes;
129 }
130 
IsDeviceBlacklistedForQueryingDetailedFrameRates(const std::string & display_name)131 bool IsDeviceBlacklistedForQueryingDetailedFrameRates(
132     const std::string& display_name) {
133   return display_name.find("WebcamMax") != std::string::npos;
134 }
135 
IsDeviceBlacklistedForMediaFoundationByModelId(const std::string & model_id)136 bool IsDeviceBlacklistedForMediaFoundationByModelId(
137     const std::string& model_id) {
138   return base::Contains(kModelIdsBlacklistedForMediaFoundation, model_id);
139 }
140 
IsDeviceBlacklistedForMediaFoundationByDisplayName(const std::string & display_name)141 bool IsDeviceBlacklistedForMediaFoundationByDisplayName(
142     const std::string& display_name) {
143   return base::Contains(kDisplayNamesBlacklistedForMediaFoundation,
144                         display_name);
145 }
146 
LoadMediaFoundationDlls()147 bool LoadMediaFoundationDlls() {
148   static const wchar_t* const kMfDLLs[] = {
149       L"%WINDIR%\\system32\\mf.dll", L"%WINDIR%\\system32\\mfplat.dll",
150       L"%WINDIR%\\system32\\mfreadwrite.dll",
151       L"%WINDIR%\\system32\\MFCaptureEngine.dll"};
152 
153   for (const wchar_t* kMfDLL : kMfDLLs) {
154     wchar_t path[MAX_PATH] = {0};
155     ExpandEnvironmentStringsW(kMfDLL, path, base::size(path));
156     if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
157       return false;
158   }
159   return true;
160 }
161 
PrepareVideoCaptureAttributesMediaFoundation(const std::vector<std::pair<GUID,GUID>> & attributes_data,int count,IMFAttributes ** attributes)162 bool PrepareVideoCaptureAttributesMediaFoundation(
163     const std::vector<std::pair<GUID, GUID>>& attributes_data,
164     int count,
165     IMFAttributes** attributes) {
166   // Once https://bugs.chromium.org/p/chromium/issues/detail?id=791615 is fixed,
167   // we must make sure that this method succeeds in capture_unittests context
168   // when MediaFoundation is enabled.
169   if (!VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation())
170     return false;
171 
172   if (FAILED(MFCreateAttributes(attributes, count)))
173     return false;
174 
175   for (const auto& value : attributes_data) {
176     if (!SUCCEEDED((*attributes)->SetGUID(value.first, value.second)))
177       return false;
178   }
179   return true;
180 }
181 
CreateVideoCaptureDeviceMediaFoundation(const Descriptor & descriptor,IMFMediaSource ** source)182 bool CreateVideoCaptureDeviceMediaFoundation(const Descriptor& descriptor,
183                                              IMFMediaSource** source) {
184   ComPtr<IMFAttributes> attributes;
185   DCHECK_EQ(GetMFAttributes()[0].first, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
186   const auto& attributes_data =
187       descriptor.capture_api == VideoCaptureApi::WIN_MEDIA_FOUNDATION
188           ? GetMFAttributes()[0].second
189           : GetMFAttributes()[1].second;
190   // We allocate attributes_data.size() + 1 (+1 is for sym_link below) elements
191   // in attributes store.
192   if (!PrepareVideoCaptureAttributesMediaFoundation(
193           attributes_data, attributes_data.size() + 1, &attributes)) {
194     return false;
195   }
196 
197   attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
198                         base::SysUTF8ToWide(descriptor.device_id).c_str());
199   return SUCCEEDED(MFCreateDeviceSource(attributes.Get(), source));
200 }
201 
IsDeviceBlackListed(const std::string & name)202 bool IsDeviceBlackListed(const std::string& name) {
203   DCHECK_EQ(BLACKLISTED_CAMERA_MAX + 1,
204             static_cast<int>(base::size(kBlacklistedCameraNames)));
205   for (size_t i = 0; i < base::size(kBlacklistedCameraNames); ++i) {
206     if (base::StartsWith(name, kBlacklistedCameraNames[i],
207                          base::CompareCase::INSENSITIVE_ASCII)) {
208       DVLOG(1) << "Enumerated blacklisted device: " << name;
209       UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.BlacklistedDevice", i,
210                                 BLACKLISTED_CAMERA_MAX + 1);
211       return true;
212     }
213   }
214   return false;
215 }
216 
GetDeviceModelId(const std::string & device_id)217 std::string GetDeviceModelId(const std::string& device_id) {
218   const size_t vid_prefix_size = sizeof(kVidPrefix) - 1;
219   const size_t pid_prefix_size = sizeof(kPidPrefix) - 1;
220   const size_t vid_location = device_id.find(kVidPrefix);
221   if (vid_location == std::string::npos ||
222       vid_location + vid_prefix_size + kVidPidSize > device_id.size()) {
223     return std::string();
224   }
225   const size_t pid_location = device_id.find(kPidPrefix);
226   if (pid_location == std::string::npos ||
227       pid_location + pid_prefix_size + kVidPidSize > device_id.size()) {
228     return std::string();
229   }
230   const std::string id_vendor =
231       device_id.substr(vid_location + vid_prefix_size, kVidPidSize);
232   const std::string id_product =
233       device_id.substr(pid_location + pid_prefix_size, kVidPidSize);
234   return id_vendor + ":" + id_product;
235 }
236 
EnumerateDirectShowDevices(IEnumMoniker ** enum_moniker)237 HRESULT EnumerateDirectShowDevices(IEnumMoniker** enum_moniker) {
238   ComPtr<ICreateDevEnum> dev_enum;
239   HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
240                                   IID_PPV_ARGS(&dev_enum));
241   if (FAILED(hr))
242     return hr;
243 
244   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
245                                        enum_moniker, 0);
246   return hr;
247 }
248 
DescriptorsContainDeviceId(const Descriptors & descriptors,const std::string & device_id)249 bool DescriptorsContainDeviceId(const Descriptors& descriptors,
250                                 const std::string& device_id) {
251   return std::find_if(
252              descriptors.begin(), descriptors.end(),
253              [device_id](const VideoCaptureDeviceDescriptor& descriptor) {
254                return device_id == descriptor.device_id;
255              }) != descriptors.end();
256 }
257 
258 // Returns a non DirectShow descriptor using the provided name and model
FindNonDirectShowDescriptorByNameAndModel(const Descriptors & descriptors,const std::string & name_and_model)259 Descriptors::const_iterator FindNonDirectShowDescriptorByNameAndModel(
260     const Descriptors& descriptors,
261     const std::string& name_and_model) {
262   return std::find_if(
263       descriptors.begin(), descriptors.end(),
264       [name_and_model](const VideoCaptureDeviceDescriptor& descriptor) {
265         return descriptor.capture_api != VideoCaptureApi::WIN_DIRECT_SHOW &&
266                name_and_model == descriptor.GetNameAndModel();
267       });
268 }
269 
GetDeviceSupportedFormatsDirectShow(const Descriptor & descriptor,VideoCaptureFormats * formats)270 void GetDeviceSupportedFormatsDirectShow(const Descriptor& descriptor,
271                                          VideoCaptureFormats* formats) {
272   DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for "
273            << descriptor.display_name();
274   bool query_detailed_frame_rates =
275       !IsDeviceBlacklistedForQueryingDetailedFrameRates(
276           descriptor.display_name());
277   CapabilityList capability_list;
278   VideoCaptureDeviceWin::GetDeviceCapabilityList(
279       descriptor.device_id, query_detailed_frame_rates, &capability_list);
280   for (const auto& entry : capability_list) {
281     formats->emplace_back(entry.supported_format);
282     DVLOG(1) << descriptor.display_name() << " "
283              << VideoCaptureFormat::ToString(entry.supported_format);
284   }
285 }
286 
GetDeviceSupportedFormatsMediaFoundation(const Descriptor & descriptor,VideoCaptureFormats * formats)287 void GetDeviceSupportedFormatsMediaFoundation(const Descriptor& descriptor,
288                                               VideoCaptureFormats* formats) {
289   DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for "
290            << descriptor.display_name();
291   ComPtr<IMFMediaSource> source;
292   if (!CreateVideoCaptureDeviceMediaFoundation(descriptor, &source)) {
293     return;
294   }
295 
296   ComPtr<IMFSourceReader> reader;
297   HRESULT hr = MFCreateSourceReaderFromMediaSource(source.Get(), NULL, &reader);
298   if (FAILED(hr)) {
299     DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: "
300                 << logging::SystemErrorCodeToString(hr);
301     return;
302   }
303 
304   DWORD stream_index = 0;
305   ComPtr<IMFMediaType> type;
306   while (SUCCEEDED(hr = reader->GetNativeMediaType(
307                        static_cast<DWORD>(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
308                        stream_index, &type))) {
309     UINT32 width, height;
310     hr = MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE, &width, &height);
311     if (FAILED(hr)) {
312       DLOG(ERROR) << "MFGetAttributeSize failed: "
313                   << logging::SystemErrorCodeToString(hr);
314       return;
315     }
316     VideoCaptureFormat capture_format;
317     capture_format.frame_size.SetSize(width, height);
318 
319     UINT32 numerator, denominator;
320     hr = MFGetAttributeRatio(type.Get(), MF_MT_FRAME_RATE, &numerator,
321                              &denominator);
322     if (FAILED(hr)) {
323       DLOG(ERROR) << "MFGetAttributeSize failed: "
324                   << logging::SystemErrorCodeToString(hr);
325       return;
326     }
327     capture_format.frame_rate =
328         denominator ? static_cast<float>(numerator) / denominator : 0.0f;
329 
330     GUID type_guid;
331     hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
332     if (FAILED(hr)) {
333       DLOG(ERROR) << "GetGUID failed: " << logging::SystemErrorCodeToString(hr);
334       return;
335     }
336     VideoCaptureDeviceMFWin::GetPixelFormatFromMFSourceMediaSubtype(
337         type_guid, &capture_format.pixel_format);
338     type.Reset();
339     ++stream_index;
340     if (capture_format.pixel_format == PIXEL_FORMAT_UNKNOWN)
341       continue;
342     formats->push_back(capture_format);
343 
344     DVLOG(1) << descriptor.display_name() << " "
345              << VideoCaptureFormat::ToString(capture_format);
346   }
347 }
348 
IsEnclosureLocationSupported()349 bool IsEnclosureLocationSupported() {
350   // DeviceInformation class is only available in Win10 onwards (v10.0.10240.0).
351   if (base::win::GetVersion() < base::win::Version::WIN10) {
352     DVLOG(1) << "DeviceInformation not supported before Windows 10";
353     return false;
354   }
355 
356   if (!(base::win::ResolveCoreWinRTDelayload() &&
357         ScopedHString::ResolveCoreWinRTStringDelayload())) {
358     DLOG(ERROR) << "Failed loading functions from combase.dll";
359     return false;
360   }
361 
362   return true;
363 }
364 
365 }  // namespace
366 
367 // Returns true if the current platform supports the Media Foundation API
368 // and that the DLLs are available.  On Vista this API is an optional download
369 // but the API is advertised as a part of Windows 7 and onwards.  However,
370 // we've seen that the required DLLs are not available in some Win7
371 // distributions such as Windows 7 N and Windows 7 KN.
372 // static
PlatformSupportsMediaFoundation()373 bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() {
374   static bool g_dlls_available = LoadMediaFoundationDlls();
375   return g_dlls_available;
376 }
377 
VideoCaptureDeviceFactoryWin()378 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin()
379     : use_media_foundation_(
380           base::FeatureList::IsEnabled(media::kMediaFoundationVideoCapture)),
381       com_thread_("Windows Video Capture COM Thread") {
382   mf_enum_device_sources_func_ =
383       PlatformSupportsMediaFoundation() ? MFEnumDeviceSources : nullptr;
384   direct_show_enum_devices_func_ =
385       base::BindRepeating(&EnumerateDirectShowDevices);
386 
387   mf_get_supported_formats_func_ =
388       base::BindRepeating(&GetDeviceSupportedFormatsMediaFoundation);
389   direct_show_get_supported_formats_func_ =
390       base::BindRepeating(&GetDeviceSupportedFormatsDirectShow);
391 
392   if (use_media_foundation_ && !PlatformSupportsMediaFoundation()) {
393     use_media_foundation_ = false;
394     LogVideoCaptureWinBackendUsed(
395         VideoCaptureWinBackendUsed::kUsingDirectShowAsFallback);
396   } else if (use_media_foundation_) {
397     session_ = InitializeMediaFoundation();
398     LogVideoCaptureWinBackendUsed(
399         VideoCaptureWinBackendUsed::kUsingMediaFoundationAsDefault);
400   } else {
401     LogVideoCaptureWinBackendUsed(
402         VideoCaptureWinBackendUsed::kUsingDirectShowAsDefault);
403   }
404 }
405 
~VideoCaptureDeviceFactoryWin()406 VideoCaptureDeviceFactoryWin::~VideoCaptureDeviceFactoryWin() {
407   if (com_thread_.IsRunning()) {
408     com_thread_.Stop();
409   }
410 }
411 
CreateDevice(const Descriptor & device_descriptor)412 std::unique_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::CreateDevice(
413     const Descriptor& device_descriptor) {
414   DCHECK(thread_checker_.CalledOnValidThread());
415   switch (device_descriptor.capture_api) {
416     case VideoCaptureApi::WIN_MEDIA_FOUNDATION:
417       FALLTHROUGH;
418     case VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR: {
419       DCHECK(PlatformSupportsMediaFoundation());
420       ComPtr<IMFMediaSource> source;
421       if (!CreateVideoCaptureDeviceMediaFoundation(device_descriptor,
422                                                    &source)) {
423         break;
424       }
425       std::unique_ptr<VideoCaptureDevice> device(
426           new VideoCaptureDeviceMFWin(device_descriptor, source));
427       DVLOG(1) << " MediaFoundation Device: "
428                << device_descriptor.display_name();
429       if (static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init())
430         return device;
431       break;
432     }
433     case VideoCaptureApi::WIN_DIRECT_SHOW: {
434       DVLOG(1) << " DirectShow Device: " << device_descriptor.display_name();
435       std::unique_ptr<VideoCaptureDevice> device(
436           new VideoCaptureDeviceWin(device_descriptor));
437       if (static_cast<VideoCaptureDeviceWin*>(device.get())->Init())
438         return device;
439       break;
440     }
441     default:
442       NOTREACHED();
443       break;
444   }
445   return nullptr;
446 }
447 
GetDeviceDescriptors(VideoCaptureDeviceDescriptors * device_descriptors)448 void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors(
449     VideoCaptureDeviceDescriptors* device_descriptors) {
450   DCHECK(thread_checker_.CalledOnValidThread());
451 
452   if (use_media_foundation_ && session_) {
453     DCHECK(PlatformSupportsMediaFoundation());
454     GetDeviceDescriptorsMediaFoundation(device_descriptors);
455     AugmentDescriptorListWithDirectShowOnlyDevices(device_descriptors);
456   } else {
457     GetDeviceDescriptorsDirectShow(device_descriptors);
458   }
459 }
460 
GetCameraLocationsAsync(std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,DeviceDescriptorsCallback result_callback)461 void VideoCaptureDeviceFactoryWin::GetCameraLocationsAsync(
462     std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
463     DeviceDescriptorsCallback result_callback) {
464   DCHECK(thread_checker_.CalledOnValidThread());
465 
466   if (IsEnclosureLocationSupported()) {
467     origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
468     com_thread_.init_com_with_mta(true);
469     com_thread_.Start();
470     com_thread_.task_runner()->PostTask(
471         FROM_HERE,
472         base::BindOnce(&VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP,
473                        base::Unretained(this), std::move(device_descriptors),
474                        std::move(result_callback)));
475   } else {
476     DeviceInfoReady(std::move(device_descriptors), std::move(result_callback));
477   }
478 }
479 
EnumerateDevicesUWP(std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,DeviceDescriptorsCallback result_callback)480 void VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP(
481     std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
482     DeviceDescriptorsCallback result_callback) {
483   DCHECK_GE(base::win::OSInfo::GetInstance()->version_number().build, 10240);
484 
485   VideoCaptureDeviceFactoryWin* factory = this;
486   scoped_refptr<base::SingleThreadTaskRunner> com_thread_runner =
487       com_thread_.task_runner();
488 
489   // The |device_info_callback| created by base::BindRepeating() is copyable,
490   // which is necessary for the below lambda function of |callback| for the
491   // asynchronous operation. The reason is to permanently capture anything in a
492   // lambda, it must be copyable, merely movable is insufficient.
493   auto device_info_callback = base::BindRepeating(
494       &VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP,
495       base::Unretained(factory), base::Passed(&device_descriptors),
496       base::Passed(&result_callback));
497   auto callback =
498       Microsoft::WRL::Callback<
499           ABI::Windows::Foundation::IAsyncOperationCompletedHandler<
500               DeviceInformationCollection*>>(
501           [com_thread_runner, device_info_callback](
502               IAsyncOperation<DeviceInformationCollection*>* operation,
503               AsyncStatus status) -> HRESULT {
504             com_thread_runner->PostTask(
505                 FROM_HERE, base::BindOnce(device_info_callback,
506                                           base::Unretained(operation)));
507             return S_OK;
508           });
509 
510   ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformationStatics>
511       dev_info_statics;
512   HRESULT hr = GetActivationFactory<
513       ABI::Windows::Devices::Enumeration::IDeviceInformationStatics,
514       RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
515       &dev_info_statics);
516   if (FAILED(hr)) {
517     UWP_ENUM_ERROR_HANDLER(hr, "DeviceInformation factory failed: ");
518     return;
519   }
520 
521   IAsyncOperation<DeviceInformationCollection*>* async_op;
522   hr = dev_info_statics->FindAllAsyncDeviceClass(
523       ABI::Windows::Devices::Enumeration::DeviceClass_VideoCapture, &async_op);
524   if (FAILED(hr)) {
525     UWP_ENUM_ERROR_HANDLER(hr, "Find all devices asynchronously failed: ");
526     return;
527   }
528 
529   hr = async_op->put_Completed(callback.Get());
530   if (FAILED(hr)) {
531     UWP_ENUM_ERROR_HANDLER(hr, "Register async operation callback failed: ");
532     return;
533   }
534 
535   // Keep a reference to incomplete |asyn_op| for releasing later.
536   async_ops_.insert(async_op);
537 }
538 
FoundAllDevicesUWP(std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,DeviceDescriptorsCallback result_callback,IAsyncOperation<DeviceInformationCollection * > * operation)539 void VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP(
540     std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
541     DeviceDescriptorsCallback result_callback,
542     IAsyncOperation<DeviceInformationCollection*>* operation) {
543   if (!operation) {
544     origin_task_runner_->PostTask(
545         FROM_HERE,
546         base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
547                        weak_ptr_factory_.GetWeakPtr(),
548                        std::move(device_descriptors),
549                        std::move(result_callback)));
550     return;
551   }
552 
553   ComPtr<ABI::Windows::Foundation::Collections::IVectorView<
554       ABI::Windows::Devices::Enumeration::DeviceInformation*>>
555       devices;
556   operation->GetResults(&devices);
557 
558   unsigned int count = 0;
559   if (devices) {
560     devices->get_Size(&count);
561   }
562 
563   for (unsigned int j = 0; j < count; ++j) {
564     ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformation> device_info;
565     HRESULT hr = devices->GetAt(j, &device_info);
566     if (SUCCEEDED(hr)) {
567       HSTRING id;
568       device_info->get_Id(&id);
569 
570       std::string device_id = ScopedHString(id).GetAsUTF8();
571       transform(device_id.begin(), device_id.end(), device_id.begin(),
572                 ::tolower);
573       const std::string model_id = GetDeviceModelId(device_id);
574 
575       ComPtr<ABI::Windows::Devices::Enumeration::IEnclosureLocation>
576           enclosure_location;
577       hr = device_info->get_EnclosureLocation(&enclosure_location);
578       if (FAILED(hr)) {
579         break;
580       }
581 
582       VideoFacingMode facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
583       if (enclosure_location) {
584         ABI::Windows::Devices::Enumeration::Panel panel;
585         enclosure_location->get_Panel(&panel);
586         switch (panel) {
587           case ABI::Windows::Devices::Enumeration::Panel_Unknown:
588             facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
589             break;
590           case ABI::Windows::Devices::Enumeration::Panel_Front:
591             facing = VideoFacingMode::MEDIA_VIDEO_FACING_USER;
592             break;
593           case ABI::Windows::Devices::Enumeration::Panel_Back:
594             facing = VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT;
595             break;
596           default:
597             facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE;
598         }
599       }
600 
601       for (Descriptor& descriptor : *device_descriptors) {
602         if (!descriptor.model_id.compare(model_id)) {
603           descriptor.facing = facing;
604           break;
605         }
606       }
607     }
608   }
609 
610   origin_task_runner_->PostTask(
611       FROM_HERE,
612       base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
613                      base::Unretained(this), std::move(device_descriptors),
614                      std::move(result_callback)));
615 
616   auto it = async_ops_.find(operation);
617   DCHECK(it != async_ops_.end());
618   (*it)->Release();
619   async_ops_.erase(it);
620 }
621 
DeviceInfoReady(std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,DeviceDescriptorsCallback result_callback)622 void VideoCaptureDeviceFactoryWin::DeviceInfoReady(
623     std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
624     DeviceDescriptorsCallback result_callback) {
625   if (com_thread_.IsRunning()) {
626     com_thread_.Stop();
627   }
628 
629   std::move(result_callback).Run(std::move(device_descriptors));
630 }
631 
GetDeviceDescriptorsMediaFoundation(Descriptors * device_descriptors)632 void VideoCaptureDeviceFactoryWin::GetDeviceDescriptorsMediaFoundation(
633     Descriptors* device_descriptors) {
634   DVLOG(1) << " GetDeviceDescriptorsMediaFoundation";
635   // Recent non-RGB (depth, IR) cameras could be marked as sensor cameras in
636   // driver inf file and MFEnumDeviceSources enumerates them only if attribute
637   // KSCATEGORY_SENSOR_CAMERA is supplied. We enumerate twice. As it is possible
638   // that SENSOR_CAMERA is also in VIDEO_CAMERA category, we prevent duplicate
639   // entries. https://crbug.com/807293
640   for (const auto& api_attributes : GetMFAttributes()) {
641     ScopedCoMem<IMFActivate*> devices;
642     UINT32 count;
643     if (!EnumerateVideoDevicesMediaFoundation(api_attributes.second, &devices,
644                                               &count)) {
645       return;
646     }
647     const bool list_was_empty = !device_descriptors->size();
648     for (UINT32 i = 0; i < count; ++i) {
649       ScopedCoMem<wchar_t> name;
650       UINT32 name_size;
651       HRESULT hr = devices[i]->GetAllocatedString(
652           MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size);
653       if (SUCCEEDED(hr)) {
654         ScopedCoMem<wchar_t> id;
655         UINT32 id_size;
656         hr = devices[i]->GetAllocatedString(
657             MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
658             &id_size);
659         if (SUCCEEDED(hr)) {
660           const std::string device_id =
661               base::SysWideToUTF8(std::wstring(id, id_size));
662           const std::string model_id = GetDeviceModelId(device_id);
663           const std::string display_name =
664               base::SysWideToUTF8(std::wstring(name, name_size));
665           if (IsDeviceBlacklistedForMediaFoundationByModelId(model_id) ||
666               IsDeviceBlacklistedForMediaFoundationByDisplayName(
667                   display_name)) {
668             continue;
669           }
670           if (list_was_empty ||
671               !DescriptorsContainDeviceId(*device_descriptors, device_id)) {
672             device_descriptors->emplace_back(display_name, device_id, model_id,
673                                              api_attributes.first);
674           }
675         }
676       }
677       DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: "
678                                  << logging::SystemErrorCodeToString(hr);
679       devices[i]->Release();
680     }
681   }
682 }
683 
684 // Adds descriptors that are only reported by the DirectShow API.
685 // Replaces MediaFoundation descriptors with corresponding DirectShow
686 // ones if the MediaFoundation one has no supported formats,
687 // but the DirectShow one does.
688 void VideoCaptureDeviceFactoryWin::
AugmentDescriptorListWithDirectShowOnlyDevices(VideoCaptureDeviceDescriptors * device_descriptors)689     AugmentDescriptorListWithDirectShowOnlyDevices(
690         VideoCaptureDeviceDescriptors* device_descriptors) {
691   // DirectShow virtual cameras are not supported by MediaFoundation.
692   // To overcome this, based on device name and model, we append
693   // missing DirectShow device descriptor to full descriptors list.
694   Descriptors direct_show_descriptors;
695   GetDeviceDescriptorsDirectShow(&direct_show_descriptors);
696   for (const auto& direct_show_descriptor : direct_show_descriptors) {
697     // DirectShow can produce two descriptors with same name and model.
698     // If those descriptors are missing from MediaFoundation, we want them both
699     // appended to the full descriptors list.
700     // Therefore, we prevent duplication by always comparing a DirectShow
701     // descriptor with a MediaFoundation one.
702 
703     Descriptors::const_iterator matching_non_direct_show_descriptor =
704         FindNonDirectShowDescriptorByNameAndModel(
705             *device_descriptors, direct_show_descriptor.GetNameAndModel());
706 
707     // Devices like the Pinnacle Dazzle, appear both in DirectShow and
708     // MediaFoundation. In MediaFoundation, they will have no supported video
709     // format while in DirectShow they will have at least one video format.
710     // Therefore, we must prioritize the MediaFoundation descriptor if it has at
711     // least one supported format
712     if (matching_non_direct_show_descriptor != device_descriptors->end()) {
713       if (GetNumberOfSupportedFormats(*matching_non_direct_show_descriptor) > 0)
714         continue;
715       if (GetNumberOfSupportedFormats(direct_show_descriptor) == 0)
716         continue;
717       device_descriptors->erase(matching_non_direct_show_descriptor);
718     }
719     device_descriptors->emplace_back(direct_show_descriptor);
720   }
721 }
722 
EnumerateVideoDevicesMediaFoundation(const std::vector<std::pair<GUID,GUID>> & attributes_data,IMFActivate *** devices,UINT32 * count)723 bool VideoCaptureDeviceFactoryWin::EnumerateVideoDevicesMediaFoundation(
724     const std::vector<std::pair<GUID, GUID>>& attributes_data,
725     IMFActivate*** devices,
726     UINT32* count) {
727   ComPtr<IMFAttributes> attributes;
728   if (!PrepareVideoCaptureAttributesMediaFoundation(
729           attributes_data, attributes_data.size(), &attributes)) {
730     return false;
731   }
732   return SUCCEEDED(
733       mf_enum_device_sources_func_(attributes.Get(), devices, count));
734 }
735 
GetDeviceDescriptorsDirectShow(Descriptors * device_descriptors)736 void VideoCaptureDeviceFactoryWin::GetDeviceDescriptorsDirectShow(
737     Descriptors* device_descriptors) {
738   DCHECK(device_descriptors);
739   DVLOG(1) << __func__;
740 
741   ComPtr<IEnumMoniker> enum_moniker;
742   HRESULT hr = direct_show_enum_devices_func_.Run(&enum_moniker);
743   // CreateClassEnumerator returns S_FALSE on some Windows OS
744   // when no camera exist. Therefore the FAILED macro can't be used.
745   if (hr != S_OK)
746     return;
747 
748   // Enumerate all video capture devices.
749   for (ComPtr<IMoniker> moniker; enum_moniker->Next(1, &moniker, NULL) == S_OK;
750        moniker.Reset()) {
751     ComPtr<IPropertyBag> prop_bag;
752     hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag));
753     if (FAILED(hr))
754       continue;
755 
756     // Find the description or friendly name.
757     ScopedVariant name;
758     hr = prop_bag->Read(L"Description", name.Receive(), 0);
759     if (FAILED(hr))
760       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
761 
762     if (FAILED(hr) || name.type() != VT_BSTR)
763       continue;
764 
765     const std::string device_name(base::SysWideToUTF8(V_BSTR(name.ptr())));
766     if (IsDeviceBlackListed(device_name))
767       continue;
768 
769     name.Reset();
770     hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
771     std::string id;
772     if (FAILED(hr) || name.type() != VT_BSTR) {
773       id = device_name;
774     } else {
775       DCHECK_EQ(name.type(), VT_BSTR);
776       id = base::SysWideToUTF8(V_BSTR(name.ptr()));
777     }
778 
779     const std::string model_id = GetDeviceModelId(id);
780 
781     device_descriptors->emplace_back(device_name, id, model_id,
782                                      VideoCaptureApi::WIN_DIRECT_SHOW);
783   }
784 }
785 
GetNumberOfSupportedFormats(const Descriptor & device)786 int VideoCaptureDeviceFactoryWin::GetNumberOfSupportedFormats(
787     const Descriptor& device) {
788   VideoCaptureFormats formats;
789   GetApiSpecificSupportedFormats(device, &formats);
790   return formats.size();
791 }
792 
GetApiSpecificSupportedFormats(const Descriptor & device,VideoCaptureFormats * formats)793 void VideoCaptureDeviceFactoryWin::GetApiSpecificSupportedFormats(
794     const Descriptor& device,
795     VideoCaptureFormats* formats) {
796   if (device.capture_api != VideoCaptureApi::WIN_DIRECT_SHOW)
797     mf_get_supported_formats_func_.Run(device, formats);
798   else
799     direct_show_get_supported_formats_func_.Run(device, formats);
800 }
801 
GetSupportedFormats(const Descriptor & device,VideoCaptureFormats * formats)802 void VideoCaptureDeviceFactoryWin::GetSupportedFormats(
803     const Descriptor& device,
804     VideoCaptureFormats* formats) {
805   DCHECK(thread_checker_.CalledOnValidThread());
806   GetApiSpecificSupportedFormats(device, formats);
807 }
808 
809 }  // namespace media
810