| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/capture/video/win/video_capture_device_factory_win.h" |
| |
| #include <mfapi.h> |
| #include <mferror.h> |
| #include <objbase.h> |
| #include <stddef.h> |
| #include <windows.devices.enumeration.h> |
| #include <windows.foundation.collections.h> |
| #include <wrl.h> |
| #include <wrl/client.h> |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/win/core_winrt_util.h" |
| #include "base/win/scoped_co_mem.h" |
| #include "base/win/scoped_variant.h" |
| #include "base/win/windows_version.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/win/mf_initializer.h" |
| #include "media/capture/video/win/metrics.h" |
| #include "media/capture/video/win/video_capture_device_mf_win.h" |
| #include "media/capture/video/win/video_capture_device_win.h" |
| |
| using Descriptor = media::VideoCaptureDeviceDescriptor; |
| using Descriptors = media::VideoCaptureDeviceDescriptors; |
| using base::win::GetActivationFactory; |
| using base::win::ScopedCoMem; |
| using base::win::ScopedHString; |
| using base::win::ScopedVariant; |
| using Microsoft::WRL::ComPtr; |
| |
| namespace media { |
| |
| namespace { |
| |
| // In Windows device identifiers, the USB VID and PID are preceded by the string |
| // "vid_" or "pid_". The identifiers are each 4 bytes long. |
| const char kVidPrefix[] = "vid_"; // Also contains '\0'. |
| const char kPidPrefix[] = "pid_"; // Also contains '\0'. |
| const size_t kVidPidSize = 4; |
| |
| // Avoid enumerating and/or using certain devices due to they provoking crashes |
| // or any other reason (http://crbug.com/378494). This enum is defined for the |
| // purposes of UMA collection. Existing entries cannot be removed. |
| enum BlacklistedCameraNames { |
| BLACKLISTED_CAMERA_GOOGLE_CAMERA_ADAPTER = 0, |
| BLACKLISTED_CAMERA_IP_CAMERA = 1, |
| BLACKLISTED_CAMERA_CYBERLINK_WEBCAM_SPLITTER = 2, |
| BLACKLISTED_CAMERA_EPOCCAM = 3, |
| // This one must be last, and equal to the previous enumerated value. |
| BLACKLISTED_CAMERA_MAX = BLACKLISTED_CAMERA_EPOCCAM, |
| }; |
| |
| #define UWP_ENUM_ERROR_HANDLER(hr, err_log) \ |
| DLOG(WARNING) << err_log << logging::SystemErrorCodeToString(hr); \ |
| origin_task_runner_->PostTask(FROM_HERE, \ |
| base::BindOnce(device_info_callback, nullptr)) |
| |
| // Blacklisted devices are identified by a characteristic prefix of the name. |
| // This prefix is used case-insensitively. This list must be kept in sync with |
| // |BlacklistedCameraNames|. |
| const char* const kBlacklistedCameraNames[] = { |
| // Name of a fake DirectShow filter on computers with GTalk installed. |
| "Google Camera Adapter", |
| // The following software WebCams cause crashes. |
| "IP Camera [JPEG/MJPEG]", "CyberLink Webcam Splitter", "EpocCam", |
| }; |
| static_assert(base::size(kBlacklistedCameraNames) == BLACKLISTED_CAMERA_MAX + 1, |
| "kBlacklistedCameraNames should be same size as " |
| "BlacklistedCameraNames enum"); |
| |
| const char* const kModelIdsBlacklistedForMediaFoundation[] = { |
| // Devices using Empia 2860 or 2820 chips, see https://crbug.com/849636. |
| "eb1a:2860", "eb1a:2820", "1ce6:2820"}; |
| |
| const std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>> |
| kMfAttributes[] = {{VideoCaptureApi::WIN_MEDIA_FOUNDATION, |
| { |
| {MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, |
| MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID}, |
| }}, |
| {VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR, |
| {{MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, |
| MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID}, |
| {MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY, |
| KSCATEGORY_SENSOR_CAMERA}}}}; |
| |
| bool IsDeviceBlacklistedForQueryingDetailedFrameRates( |
| const std::string& display_name) { |
| return display_name.find("WebcamMax") != std::string::npos; |
| } |
| |
| bool IsDeviceBlacklistedForMediaFoundationByModelId( |
| const std::string& model_id) { |
| return base::ContainsValue(kModelIdsBlacklistedForMediaFoundation, model_id); |
| } |
| |
| bool LoadMediaFoundationDlls() { |
| static const wchar_t* const kMfDLLs[] = { |
| L"%WINDIR%\\system32\\mf.dll", L"%WINDIR%\\system32\\mfplat.dll", |
| L"%WINDIR%\\system32\\mfreadwrite.dll", |
| L"%WINDIR%\\system32\\MFCaptureEngine.dll"}; |
| |
| for (const wchar_t* kMfDLL : kMfDLLs) { |
| wchar_t path[MAX_PATH] = {0}; |
| ExpandEnvironmentStringsW(kMfDLL, path, base::size(path)); |
| if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool PrepareVideoCaptureAttributesMediaFoundation( |
| const std::vector<std::pair<GUID, GUID>>& attributes_data, |
| int count, |
| IMFAttributes** attributes) { |
| // Once https://bugs.chromium.org/p/chromium/issues/detail?id=791615 is fixed, |
| // we must make sure that this method succeeds in capture_unittests context |
| // when MediaFoundation is enabled. |
| if (!VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() || |
| !InitializeMediaFoundation()) { |
| return false; |
| } |
| |
| if (FAILED(MFCreateAttributes(attributes, count))) |
| return false; |
| |
| for (const auto& value : attributes_data) { |
| if (!SUCCEEDED((*attributes)->SetGUID(value.first, value.second))) |
| return false; |
| } |
| return true; |
| } |
| |
| bool CreateVideoCaptureDeviceMediaFoundation(const Descriptor& descriptor, |
| IMFMediaSource** source) { |
| ComPtr<IMFAttributes> attributes; |
| static_assert( |
| base::size(kMfAttributes) == 2, |
| "Implementation here asumes that kMfAttributes has size of two."); |
| DCHECK_EQ(kMfAttributes[0].first, VideoCaptureApi::WIN_MEDIA_FOUNDATION); |
| const auto& attributes_data = |
| descriptor.capture_api == VideoCaptureApi::WIN_MEDIA_FOUNDATION |
| ? kMfAttributes[0].second |
| : kMfAttributes[1].second; |
| // We allocate attributes_data.size() + 1 (+1 is for sym_link below) elements |
| // in attributes store. |
| if (!PrepareVideoCaptureAttributesMediaFoundation( |
| attributes_data, attributes_data.size() + 1, |
| attributes.GetAddressOf())) { |
| return false; |
| } |
| |
| attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, |
| base::SysUTF8ToWide(descriptor.device_id).c_str()); |
| return SUCCEEDED(MFCreateDeviceSource(attributes.Get(), source)); |
| } |
| |
| bool IsDeviceBlackListed(const std::string& name) { |
| DCHECK_EQ(BLACKLISTED_CAMERA_MAX + 1, |
| static_cast<int>(base::size(kBlacklistedCameraNames))); |
| for (size_t i = 0; i < base::size(kBlacklistedCameraNames); ++i) { |
| if (base::StartsWith(name, kBlacklistedCameraNames[i], |
| base::CompareCase::INSENSITIVE_ASCII)) { |
| DVLOG(1) << "Enumerated blacklisted device: " << name; |
| UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.BlacklistedDevice", i, |
| BLACKLISTED_CAMERA_MAX + 1); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::string GetDeviceModelId(const std::string& device_id) { |
| const size_t vid_prefix_size = sizeof(kVidPrefix) - 1; |
| const size_t pid_prefix_size = sizeof(kPidPrefix) - 1; |
| const size_t vid_location = device_id.find(kVidPrefix); |
| if (vid_location == std::string::npos || |
| vid_location + vid_prefix_size + kVidPidSize > device_id.size()) { |
| return std::string(); |
| } |
| const size_t pid_location = device_id.find(kPidPrefix); |
| if (pid_location == std::string::npos || |
| pid_location + pid_prefix_size + kVidPidSize > device_id.size()) { |
| return std::string(); |
| } |
| const std::string id_vendor = |
| device_id.substr(vid_location + vid_prefix_size, kVidPidSize); |
| const std::string id_product = |
| device_id.substr(pid_location + pid_prefix_size, kVidPidSize); |
| return id_vendor + ":" + id_product; |
| } |
| |
| HRESULT EnumerateDirectShowDevices(IEnumMoniker** enum_moniker) { |
| ComPtr<ICreateDevEnum> dev_enum; |
| HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, |
| IID_PPV_ARGS(&dev_enum)); |
| if (FAILED(hr)) |
| return hr; |
| |
| hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
| enum_moniker, 0); |
| return hr; |
| } |
| |
| bool DescriptorsContainDeviceId(const Descriptors& descriptors, |
| const std::string& device_id) { |
| return std::find_if( |
| descriptors.begin(), descriptors.end(), |
| [device_id](const VideoCaptureDeviceDescriptor& descriptor) { |
| return device_id == descriptor.device_id; |
| }) != descriptors.end(); |
| } |
| |
| // Returns a non DirectShow descriptor using the provided name and model |
| Descriptors::const_iterator FindNonDirectShowDescriptorByNameAndModel( |
| const Descriptors& descriptors, |
| const std::string& name_and_model) { |
| return std::find_if( |
| descriptors.begin(), descriptors.end(), |
| [name_and_model](const VideoCaptureDeviceDescriptor& descriptor) { |
| return descriptor.capture_api != VideoCaptureApi::WIN_DIRECT_SHOW && |
| name_and_model == descriptor.GetNameAndModel(); |
| }); |
| } |
| |
| void GetDeviceSupportedFormatsDirectShow(const Descriptor& descriptor, |
| VideoCaptureFormats* formats) { |
| DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " |
| << descriptor.display_name(); |
| bool query_detailed_frame_rates = |
| !IsDeviceBlacklistedForQueryingDetailedFrameRates( |
| descriptor.display_name()); |
| CapabilityList capability_list; |
| VideoCaptureDeviceWin::GetDeviceCapabilityList( |
| descriptor.device_id, query_detailed_frame_rates, &capability_list); |
| for (const auto& entry : capability_list) { |
| formats->emplace_back(entry.supported_format); |
| DVLOG(1) << descriptor.display_name() << " " |
| << VideoCaptureFormat::ToString(entry.supported_format); |
| } |
| } |
| |
| void GetDeviceSupportedFormatsMediaFoundation(const Descriptor& descriptor, |
| VideoCaptureFormats* formats) { |
| DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " |
| << descriptor.display_name(); |
| ComPtr<IMFMediaSource> source; |
| if (!CreateVideoCaptureDeviceMediaFoundation(descriptor, |
| source.GetAddressOf())) { |
| return; |
| } |
| |
| ComPtr<IMFSourceReader> reader; |
| HRESULT hr = MFCreateSourceReaderFromMediaSource(source.Get(), NULL, |
| reader.GetAddressOf()); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: " |
| << logging::SystemErrorCodeToString(hr); |
| return; |
| } |
| |
| DWORD stream_index = 0; |
| ComPtr<IMFMediaType> type; |
| while (SUCCEEDED(hr = reader->GetNativeMediaType( |
| static_cast<DWORD>(MF_SOURCE_READER_FIRST_VIDEO_STREAM), |
| stream_index, type.GetAddressOf()))) { |
| UINT32 width, height; |
| hr = MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE, &width, &height); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "MFGetAttributeSize failed: " |
| << logging::SystemErrorCodeToString(hr); |
| return; |
| } |
| VideoCaptureFormat capture_format; |
| capture_format.frame_size.SetSize(width, height); |
| |
| UINT32 numerator, denominator; |
| hr = MFGetAttributeRatio(type.Get(), MF_MT_FRAME_RATE, &numerator, |
| &denominator); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "MFGetAttributeSize failed: " |
| << logging::SystemErrorCodeToString(hr); |
| return; |
| } |
| capture_format.frame_rate = |
| denominator ? static_cast<float>(numerator) / denominator : 0.0f; |
| |
| GUID type_guid; |
| hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "GetGUID failed: " << logging::SystemErrorCodeToString(hr); |
| return; |
| } |
| VideoCaptureDeviceMFWin::GetPixelFormatFromMFSourceMediaSubtype( |
| type_guid, &capture_format.pixel_format); |
| type.Reset(); |
| ++stream_index; |
| if (capture_format.pixel_format == PIXEL_FORMAT_UNKNOWN) |
| continue; |
| formats->push_back(capture_format); |
| |
| DVLOG(1) << descriptor.display_name() << " " |
| << VideoCaptureFormat::ToString(capture_format); |
| } |
| } |
| |
| bool IsEnclosureLocationSupported() { |
| // DeviceInformation class is only available in Win10 onwards (v10.0.10240.0). |
| if (base::win::GetVersion() < base::win::VERSION_WIN10) { |
| DVLOG(1) << "DeviceInformation not supported before Windows 10"; |
| return false; |
| } |
| |
| if (!(base::win::ResolveCoreWinRTDelayload() && |
| ScopedHString::ResolveCoreWinRTStringDelayload())) { |
| DLOG(ERROR) << "Failed loading functions from combase.dll"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| // Returns true if the current platform supports the Media Foundation API |
| // and that the DLLs are available. On Vista this API is an optional download |
| // but the API is advertised as a part of Windows 7 and onwards. However, |
| // we've seen that the required DLLs are not available in some Win7 |
| // distributions such as Windows 7 N and Windows 7 KN. |
| // static |
| bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() { |
| static bool g_dlls_available = LoadMediaFoundationDlls(); |
| return g_dlls_available; |
| } |
| |
| VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() |
| : use_media_foundation_( |
| base::FeatureList::IsEnabled(media::kMediaFoundationVideoCapture)), |
| com_thread_("Windows Video Capture COM Thread"), |
| weak_ptr_factory_(this) { |
| mf_enum_device_sources_func_ = |
| PlatformSupportsMediaFoundation() ? MFEnumDeviceSources : nullptr; |
| direct_show_enum_devices_func_ = |
| base::BindRepeating(&EnumerateDirectShowDevices); |
| |
| mf_get_supported_formats_func_ = |
| base::BindRepeating(&GetDeviceSupportedFormatsMediaFoundation); |
| direct_show_get_supported_formats_func_ = |
| base::BindRepeating(&GetDeviceSupportedFormatsDirectShow); |
| |
| if (use_media_foundation_ && !PlatformSupportsMediaFoundation()) { |
| use_media_foundation_ = false; |
| LogVideoCaptureWinBackendUsed( |
| VideoCaptureWinBackendUsed::kUsingDirectShowAsFallback); |
| } else if (use_media_foundation_) { |
| LogVideoCaptureWinBackendUsed( |
| VideoCaptureWinBackendUsed::kUsingMediaFoundationAsDefault); |
| } else { |
| LogVideoCaptureWinBackendUsed( |
| VideoCaptureWinBackendUsed::kUsingDirectShowAsDefault); |
| } |
| } |
| |
| VideoCaptureDeviceFactoryWin::~VideoCaptureDeviceFactoryWin() { |
| if (com_thread_.IsRunning()) { |
| com_thread_.Stop(); |
| } |
| } |
| |
| std::unique_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::CreateDevice( |
| const Descriptor& device_descriptor) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| switch (device_descriptor.capture_api) { |
| case VideoCaptureApi::WIN_MEDIA_FOUNDATION: |
| FALLTHROUGH; |
| case VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR: { |
| DCHECK(PlatformSupportsMediaFoundation()); |
| ComPtr<IMFMediaSource> source; |
| if (!CreateVideoCaptureDeviceMediaFoundation(device_descriptor, |
| source.GetAddressOf())) { |
| break; |
| } |
| std::unique_ptr<VideoCaptureDevice> device( |
| new VideoCaptureDeviceMFWin(device_descriptor, source)); |
| DVLOG(1) << " MediaFoundation Device: " |
| << device_descriptor.display_name(); |
| if (static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init()) |
| return device; |
| break; |
| } |
| case VideoCaptureApi::WIN_DIRECT_SHOW: { |
| DVLOG(1) << " DirectShow Device: " << device_descriptor.display_name(); |
| std::unique_ptr<VideoCaptureDevice> device( |
| new VideoCaptureDeviceWin(device_descriptor)); |
| if (static_cast<VideoCaptureDeviceWin*>(device.get())->Init()) |
| return device; |
| break; |
| } |
| default: |
| NOTREACHED(); |
| break; |
| } |
| return nullptr; |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors( |
| VideoCaptureDeviceDescriptors* device_descriptors) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (use_media_foundation_) { |
| GetDeviceDescriptorsMediaFoundation(device_descriptors); |
| AugmentDescriptorListWithDirectShowOnlyDevices(device_descriptors); |
| } else { |
| GetDeviceDescriptorsDirectShow(device_descriptors); |
| } |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetCameraLocationsAsync( |
| std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors, |
| DeviceDescriptorsCallback result_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (IsEnclosureLocationSupported()) { |
| origin_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| com_thread_.init_com_with_mta(true); |
| com_thread_.Start(); |
| com_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP, |
| base::Unretained(this), std::move(device_descriptors), |
| std::move(result_callback))); |
| } else { |
| DeviceInfoReady(std::move(device_descriptors), std::move(result_callback)); |
| } |
| } |
| |
| void VideoCaptureDeviceFactoryWin::EnumerateDevicesUWP( |
| std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors, |
| DeviceDescriptorsCallback result_callback) { |
| DCHECK_GE(base::win::OSInfo::GetInstance()->version_number().build, 10240); |
| |
| VideoCaptureDeviceFactoryWin* factory = this; |
| scoped_refptr<base::SingleThreadTaskRunner> com_thread_runner = |
| com_thread_.task_runner(); |
| |
| // The |device_info_callback| created by base::BindRepeating() is copyable, |
| // which is necessary for the below lambda function of |callback| for the |
| // asynchronous operation. The reason is to permanently capture anything in a |
| // lambda, it must be copyable, merely movable is insufficient. |
| auto device_info_callback = base::BindRepeating( |
| &VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP, |
| base::Unretained(factory), base::Passed(&device_descriptors), |
| base::Passed(&result_callback)); |
| auto callback = |
| Microsoft::WRL::Callback< |
| ABI::Windows::Foundation::IAsyncOperationCompletedHandler< |
| DeviceInformationCollection*>>( |
| [com_thread_runner, device_info_callback]( |
| IAsyncOperation<DeviceInformationCollection*>* operation, |
| AsyncStatus status) -> HRESULT { |
| com_thread_runner->PostTask( |
| FROM_HERE, base::BindOnce(device_info_callback, |
| base::Unretained(operation))); |
| return S_OK; |
| }); |
| |
| ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformationStatics> |
| dev_info_statics; |
| HRESULT hr = GetActivationFactory< |
| ABI::Windows::Devices::Enumeration::IDeviceInformationStatics, |
| RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>( |
| &dev_info_statics); |
| if (FAILED(hr)) { |
| UWP_ENUM_ERROR_HANDLER(hr, "DeviceInformation factory failed: "); |
| return; |
| } |
| |
| IAsyncOperation<DeviceInformationCollection*>* async_op; |
| hr = dev_info_statics->FindAllAsyncDeviceClass( |
| ABI::Windows::Devices::Enumeration::DeviceClass_VideoCapture, &async_op); |
| if (FAILED(hr)) { |
| UWP_ENUM_ERROR_HANDLER(hr, "Find all devices asynchronously failed: "); |
| return; |
| } |
| |
| hr = async_op->put_Completed(callback.Get()); |
| if (FAILED(hr)) { |
| UWP_ENUM_ERROR_HANDLER(hr, "Register async operation callback failed: "); |
| return; |
| } |
| |
| // Keep a reference to incomplete |asyn_op| for releasing later. |
| async_ops_.insert(async_op); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::FoundAllDevicesUWP( |
| std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors, |
| DeviceDescriptorsCallback result_callback, |
| IAsyncOperation<DeviceInformationCollection*>* operation) { |
| if (!operation) { |
| origin_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(device_descriptors), |
| std::move(result_callback))); |
| return; |
| } |
| |
| ComPtr<ABI::Windows::Foundation::Collections::IVectorView< |
| ABI::Windows::Devices::Enumeration::DeviceInformation*>> |
| devices; |
| operation->GetResults(devices.GetAddressOf()); |
| |
| unsigned int count = 0; |
| if (devices) { |
| devices->get_Size(&count); |
| } |
| |
| for (unsigned int j = 0; j < count; ++j) { |
| ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformation> device_info; |
| HRESULT hr = devices->GetAt(j, device_info.GetAddressOf()); |
| if (SUCCEEDED(hr)) { |
| HSTRING id; |
| device_info->get_Id(&id); |
| |
| std::string device_id = ScopedHString(id).GetAsUTF8(); |
| transform(device_id.begin(), device_id.end(), device_id.begin(), |
| ::tolower); |
| const std::string model_id = GetDeviceModelId(device_id); |
| |
| ComPtr<ABI::Windows::Devices::Enumeration::IEnclosureLocation> |
| enclosure_location; |
| hr = |
| device_info->get_EnclosureLocation(enclosure_location.GetAddressOf()); |
| if (FAILED(hr)) { |
| break; |
| } |
| |
| VideoFacingMode facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE; |
| if (enclosure_location) { |
| ABI::Windows::Devices::Enumeration::Panel panel; |
| enclosure_location->get_Panel(&panel); |
| switch (panel) { |
| case ABI::Windows::Devices::Enumeration::Panel_Unknown: |
| facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE; |
| break; |
| case ABI::Windows::Devices::Enumeration::Panel_Front: |
| facing = VideoFacingMode::MEDIA_VIDEO_FACING_USER; |
| break; |
| case ABI::Windows::Devices::Enumeration::Panel_Back: |
| facing = VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT; |
| break; |
| default: |
| facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE; |
| } |
| } |
| |
| for (Descriptor& descriptor : *device_descriptors) { |
| if (!descriptor.model_id.compare(model_id)) { |
| descriptor.facing = facing; |
| break; |
| } |
| } |
| } |
| } |
| |
| origin_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady, |
| base::Unretained(this), std::move(device_descriptors), |
| std::move(result_callback))); |
| |
| auto it = async_ops_.find(operation); |
| DCHECK(it != async_ops_.end()); |
| (*it)->Release(); |
| async_ops_.erase(it); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::DeviceInfoReady( |
| std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors, |
| DeviceDescriptorsCallback result_callback) { |
| if (com_thread_.IsRunning()) { |
| com_thread_.Stop(); |
| } |
| |
| std::move(result_callback).Run(std::move(device_descriptors)); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetDeviceDescriptorsMediaFoundation( |
| Descriptors* device_descriptors) { |
| DVLOG(1) << " GetDeviceDescriptorsMediaFoundation"; |
| // Recent non-RGB (depth, IR) cameras could be marked as sensor cameras in |
| // driver inf file and MFEnumDeviceSources enumerates them only if attribute |
| // KSCATEGORY_SENSOR_CAMERA is supplied. We enumerate twice. As it is possible |
| // that SENSOR_CAMERA is also in VIDEO_CAMERA category, we prevent duplicate |
| // entries. https://crbug.com/807293 |
| for (const auto& api_attributes : kMfAttributes) { |
| ScopedCoMem<IMFActivate*> devices; |
| UINT32 count; |
| if (!EnumerateVideoDevicesMediaFoundation(api_attributes.second, &devices, |
| &count)) { |
| return; |
| } |
| const bool list_was_empty = !device_descriptors->size(); |
| for (UINT32 i = 0; i < count; ++i) { |
| ScopedCoMem<wchar_t> name; |
| UINT32 name_size; |
| HRESULT hr = devices[i]->GetAllocatedString( |
| MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size); |
| if (SUCCEEDED(hr)) { |
| ScopedCoMem<wchar_t> id; |
| UINT32 id_size; |
| hr = devices[i]->GetAllocatedString( |
| MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id, |
| &id_size); |
| if (SUCCEEDED(hr)) { |
| const std::string device_id = |
| base::SysWideToUTF8(std::wstring(id, id_size)); |
| const std::string model_id = GetDeviceModelId(device_id); |
| if (IsDeviceBlacklistedForMediaFoundationByModelId(model_id)) |
| continue; |
| if (list_was_empty || |
| !DescriptorsContainDeviceId(*device_descriptors, device_id)) { |
| device_descriptors->emplace_back( |
| base::SysWideToUTF8(std::wstring(name, name_size)), device_id, |
| model_id, api_attributes.first); |
| } |
| } |
| } |
| DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: " |
| << logging::SystemErrorCodeToString(hr); |
| devices[i]->Release(); |
| } |
| } |
| } |
| |
| // Adds descriptors that are only reported by the DirectShow API. |
| // Replaces MediaFoundation descriptors with corresponding DirectShow |
| // ones if the MediaFoundation one has no supported formats, |
| // but the DirectShow one does. |
| void VideoCaptureDeviceFactoryWin:: |
| AugmentDescriptorListWithDirectShowOnlyDevices( |
| VideoCaptureDeviceDescriptors* device_descriptors) { |
| // DirectShow virtual cameras are not supported by MediaFoundation. |
| // To overcome this, based on device name and model, we append |
| // missing DirectShow device descriptor to full descriptors list. |
| Descriptors direct_show_descriptors; |
| GetDeviceDescriptorsDirectShow(&direct_show_descriptors); |
| for (const auto& direct_show_descriptor : direct_show_descriptors) { |
| // DirectShow can produce two descriptors with same name and model. |
| // If those descriptors are missing from MediaFoundation, we want them both |
| // appended to the full descriptors list. |
| // Therefore, we prevent duplication by always comparing a DirectShow |
| // descriptor with a MediaFoundation one. |
| |
| Descriptors::const_iterator matching_non_direct_show_descriptor = |
| FindNonDirectShowDescriptorByNameAndModel( |
| *device_descriptors, direct_show_descriptor.GetNameAndModel()); |
| |
| // Devices like the Pinnacle Dazzle, appear both in DirectShow and |
| // MediaFoundation. In MediaFoundation, they will have no supported video |
| // format while in DirectShow they will have at least one video format. |
| // Therefore, we must prioritize the MediaFoundation descriptor if it has at |
| // least one supported format |
| if (matching_non_direct_show_descriptor != device_descriptors->end()) { |
| if (GetNumberOfSupportedFormats(*matching_non_direct_show_descriptor) > 0) |
| continue; |
| if (GetNumberOfSupportedFormats(direct_show_descriptor) == 0) |
| continue; |
| device_descriptors->erase(matching_non_direct_show_descriptor); |
| } |
| device_descriptors->emplace_back(direct_show_descriptor); |
| } |
| } |
| |
| bool VideoCaptureDeviceFactoryWin::EnumerateVideoDevicesMediaFoundation( |
| const std::vector<std::pair<GUID, GUID>>& attributes_data, |
| IMFActivate*** devices, |
| UINT32* count) { |
| ComPtr<IMFAttributes> attributes; |
| if (!PrepareVideoCaptureAttributesMediaFoundation( |
| attributes_data, attributes_data.size(), attributes.GetAddressOf())) { |
| return false; |
| } |
| return SUCCEEDED( |
| mf_enum_device_sources_func_(attributes.Get(), devices, count)); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetDeviceDescriptorsDirectShow( |
| Descriptors* device_descriptors) { |
| DCHECK(device_descriptors); |
| DVLOG(1) << __func__; |
| |
| ComPtr<IEnumMoniker> enum_moniker; |
| HRESULT hr = direct_show_enum_devices_func_.Run(enum_moniker.GetAddressOf()); |
| // CreateClassEnumerator returns S_FALSE on some Windows OS |
| // when no camera exist. Therefore the FAILED macro can't be used. |
| if (hr != S_OK) |
| return; |
| |
| // Enumerate all video capture devices. |
| for (ComPtr<IMoniker> moniker; |
| enum_moniker->Next(1, moniker.GetAddressOf(), NULL) == S_OK; |
| moniker.Reset()) { |
| ComPtr<IPropertyBag> prop_bag; |
| hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); |
| if (FAILED(hr)) |
| continue; |
| |
| // Find the description or friendly name. |
| ScopedVariant name; |
| hr = prop_bag->Read(L"Description", name.Receive(), 0); |
| if (FAILED(hr)) |
| hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0); |
| |
| if (FAILED(hr) || name.type() != VT_BSTR) |
| continue; |
| |
| const std::string device_name(base::SysWideToUTF8(V_BSTR(name.ptr()))); |
| if (IsDeviceBlackListed(device_name)) |
| continue; |
| |
| name.Reset(); |
| hr = prop_bag->Read(L"DevicePath", name.Receive(), 0); |
| std::string id; |
| if (FAILED(hr) || name.type() != VT_BSTR) { |
| id = device_name; |
| } else { |
| DCHECK_EQ(name.type(), VT_BSTR); |
| id = base::SysWideToUTF8(V_BSTR(name.ptr())); |
| } |
| |
| const std::string model_id = GetDeviceModelId(id); |
| |
| device_descriptors->emplace_back(device_name, id, model_id, |
| VideoCaptureApi::WIN_DIRECT_SHOW); |
| } |
| } |
| |
| int VideoCaptureDeviceFactoryWin::GetNumberOfSupportedFormats( |
| const Descriptor& device) { |
| VideoCaptureFormats formats; |
| GetApiSpecificSupportedFormats(device, &formats); |
| return formats.size(); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetApiSpecificSupportedFormats( |
| const Descriptor& device, |
| VideoCaptureFormats* formats) { |
| if (device.capture_api != VideoCaptureApi::WIN_DIRECT_SHOW) |
| mf_get_supported_formats_func_.Run(device, formats); |
| else |
| direct_show_get_supported_formats_func_.Run(device, formats); |
| } |
| |
| void VideoCaptureDeviceFactoryWin::GetSupportedFormats( |
| const Descriptor& device, |
| VideoCaptureFormats* formats) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| GetApiSpecificSupportedFormats(device, formats); |
| } |
| |
| } // namespace media |