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