chromium/media/capture/video/win/video_capture_device_factory_win.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "media/capture/video/win/video_capture_device_factory_win.h"

#include <objbase.h>

#include <mfapi.h>
#include <mferror.h>
#include <stddef.h>
#include <windows.devices.enumeration.h>
#include <windows.foundation.collections.h>
#include <wrl.h>
#include <wrl/client.h>

#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_set.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/not_fatal_until.h"
#include "base/ranges/algorithm.h"
#include "base/strings/cstring_view.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/system/system_monitor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/scoped_thread_priority.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_variant.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "media/base/media_switches.h"
#include "media/base/win/mf_helpers.h"
#include "media/base/win/mf_initializer.h"
#include "media/capture/capture_switches.h"
#include "media/capture/video/video_capture_device_descriptor.h"
#include "media/capture/video/video_capture_metrics.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 DevicesInfo = std::vector<media::VideoCaptureDeviceInfo>;
using base::win::GetActivationFactory;
using base::win::ScopedCoMem;
using base::win::ScopedHString;
using base::win::ScopedVariant;
using Microsoft::WRL::ComPtr;

namespace media {

BASE_FEATURE(kMediaFoundationD3D11VideoCaptureBlocklist,
             "MediaFoundationD3D11VideoCaptureBlocklist",
             base::FEATURE_ENABLED_BY_DEFAULT);

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;

// AQS device selector string to filter enumerated DeviceInformation objects to
// KSCATEGORY_SENSOR_CAMERA (Class GUID 24e552d7-6523-47F7-a647-d3465bf1f5ca)
// OR KSCATEGORY_VIDEO_CAMERA (Class GUID e5323777-f976-4f5b-9b55-b94699c46e44).
const wchar_t* kVideoAndSensorCamerasAqsString =
    L"(System.Devices.InterfaceClassGuid:="
    L"\"{e5323777-f976-4f5b-9b55-b94699c46e44}\" AND "
    L"(System.Devices.WinPhone8CameraFlags:=[] OR "
    L"System.Devices.WinPhone8CameraFlags:<4096)) OR "
    L"System.Devices.InterfaceClassGuid:="
    L"\"{24e552d7-6523-47f7-a647-d3465bf1f5ca}\" AND "
    L"System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";

// Class GUID for KSCATEGORY_VIDEO_CAMERA. Only devices from that category will
// contain this GUID in their |device_id|.
const char kVideoCameraGuid[] = "e5323777-f976-4f5b-9b55-b94699c46e44";

// 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 BlockedCameraNames {
  BLOCKED_CAMERA_GOOGLE_CAMERA_ADAPTER = 0,
  BLOCKED_CAMERA_IP_CAMERA = 1,
  BLOCKED_CAMERA_CYBERLINK_WEBCAM_SPLITTER = 2,
  BLOCKED_CAMERA_EPOCCAM = 3,
  // This one must be last, and equal to the previous enumerated value.
  BLOCKED_CAMERA_MAX = BLOCKED_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(std::move(device_info_callback), nullptr))

// Blocked devices are identified by a characteristic prefix of the name.
// This prefix is used case-insensitively. This list must be kept in sync with
// |BlockedCameraNames|.
const char* const kBlockedCameraNames[] = {
    // 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(std::size(kBlockedCameraNames) == BLOCKED_CAMERA_MAX + 1,
              "kBlockedCameraNames should be same size as "
              "BlockedCameraNames enum");

// Use this list only for USB webcams.
constexpr auto kModelIdsBlockedForMediaFoundation =
    base::MakeFixedFlatSet<std::string_view>(
        {// Devices using Empia 2860 or 2820 chips, see
         // https://crbug.com/849636.
         "eb1a:2860", "eb1a:2820", "1ce6:2820",
         // Elgato HD60 Pro
         "12ab:0380",
         // Sensoray 2253
         "1943:2253",
         // Dell E5440
         "0c45:64d0", "0c45:64d2",
         // Dell E7440
         "1bcf:2985",
         // Lenovo Thinkpad Model 20CG0006FMZ front and rear cameras, see
         // also https://crbug.com/924528.
         "04ca:7047", "04ca:7048",
         // HP Elitebook 840 G1
         "04f2:b3ed", "04f2:b3ca", "05c8:035d", "05c8:0369",
         // HP HD Camera. See https://crbug.com/1011888.
         "04ca:7095",
         // RBG/IR camera for Windows Hello Face Auth. See
         // https://crbug.com/984864.
         "13d3:5257",
         // Acer Aspire f5-573g. See https://crbug.com/1034644.
         "0bda:57f2",
         // Elgato Camlink 4k
         "0fd9:0066",
         // ACER Aspire VN7-571G. See https://crbug.com/1327948.
         "04f2:b469",
         // Hauppauge USB-Live2. See https://crbug.com/1447113.
         "2040:c200"});

// Use this list only for USB webcams.
constexpr auto kModelIdsBlockedForMediaFoundationD3D11VideoCapture =
    base::MakeFixedFlatSet<std::string_view>(
        {// D3D11 calls on textures produced by these cameras take so much time
         // that MFCaptureEngine fails with E_MF_SAMPLEALLOCATOREMPTY error
         "05a3:9331", "04f2:b6bf"});

// Use this list only for non-USB webcams.
constexpr auto kDisplayNamesBlockedForMediaFoundation =
    base::MakeFixedFlatSet<std::string_view>(
        {// VMware Virtual Webcams cause hangs when there is no physical Webcam.
         // See https://crbug.com/1044974.
         "VMware Virtual Webcam"});

const std::vector<
    std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>>&
GetMFAttributes() {
  if (base::FeatureList::IsEnabled(
          media::kIncludeIRCamerasInDeviceEnumeration)) {
    static const base::NoDestructor<std::vector<
        std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>>>
        mf_attributes({{{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}}}}});
    return *mf_attributes;
  }

  static const base::NoDestructor<std::vector<
      std::pair<VideoCaptureApi, std::vector<std::pair<GUID, GUID>>>>>
      mf_attributes({{VideoCaptureApi::WIN_MEDIA_FOUNDATION,
                      {
                          {MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
                           MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID},
                      }}});
  return *mf_attributes;
}

bool IsDeviceBlockedForQueryingDetailedFrameRates(
    const std::string& display_name) {
  return display_name.find("WebcamMax") != std::string::npos;
}

bool IsDeviceBlockedForMediaFoundationByModelId(const std::string& model_id) {
  return kModelIdsBlockedForMediaFoundation.contains(model_id);
}

bool IsDeviceBlockedForMediaFoundationD3D11ByModelId(
    const std::string& model_id) {
  return base::FeatureList::IsEnabled(
             kMediaFoundationD3D11VideoCaptureBlocklist) &&
         kModelIdsBlockedForMediaFoundationD3D11VideoCapture.contains(model_id);
}

bool IsDeviceBlockedForMediaFoundationByDisplayName(
    const std::string& display_name) {
  return kDisplayNamesBlockedForMediaFoundation.contains(display_name);
}

HMODULE ExpandEnvironmentStringsAndLoadLibrary(base::wcstring_view path) {
  auto expanded_path = base::win::ExpandEnvironmentVariables(path);
  if (!expanded_path) {
    return nullptr;
  }

  return LoadLibraryExW(expanded_path->c_str(), nullptr,
                        LOAD_WITH_ALTERED_SEARCH_PATH);
}

bool LoadMediaFoundationDlls() {
  static constexpr base::wcstring_view kMfDLLs[] = {
      L"%WINDIR%\\system32\\mf.dll", L"%WINDIR%\\system32\\mfplat.dll",
      L"%WINDIR%\\system32\\mfreadwrite.dll",
      L"%WINDIR%\\system32\\MFCaptureEngine.dll"};

  // Mitigate the issues caused by loading DLLs on a background thread
  // (http://crbug/973868).
  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();

  // Load required DLLs.
  for (const auto& kMfDLL : kMfDLLs) {
    if (!ExpandEnvironmentStringsAndLoadLibrary(kMfDLL)) {
      return false;
    }
  }

  // Load optional DLLs whose availability depends on Windows version.
  ExpandEnvironmentStringsAndLoadLibrary(
      L"%WINDIR%\\system32\\mfsensorgroup.dll");

  return true;
}

bool PrepareVideoCaptureAttributesMediaFoundation(
    const std::vector<std::pair<GUID, GUID>>& attributes_data,
    int count,
    IMFAttributes** attributes) {
  DCHECK(attributes);
  DCHECK(!*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())
    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 IsDeviceBlocked(const std::string& name) {
  DCHECK_EQ(BLOCKED_CAMERA_MAX + 1,
            static_cast<int>(std::size(kBlockedCameraNames)));
  for (size_t i = 0; i < std::size(kBlockedCameraNames); ++i) {
    if (base::StartsWith(name, kBlockedCameraNames[i],
                         base::CompareCase::INSENSITIVE_ASCII)) {
      DVLOG(1) << "Enumerated blocked device: " << name;
      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;
}

bool DevicesInfoContainsDeviceId(const DevicesInfo& devices_info,
                                 const std::string& device_id) {
  return base::Contains(devices_info, device_id,
                        [](const VideoCaptureDeviceInfo& device_info) {
                          return device_info.descriptor.device_id;
                        });
}

// Returns a non DirectShow descriptor DevicesInfo with the provided name and
// model.
DevicesInfo::const_iterator FindNonDirectShowDeviceInfoByNameAndModel(
    const DevicesInfo& devices_info,
    const std::string& name_and_model) {
  return base::ranges::find_if(
      devices_info,
      [name_and_model](const VideoCaptureDeviceInfo& device_info) {
        return device_info.descriptor.capture_api !=
                   VideoCaptureApi::WIN_DIRECT_SHOW &&
               name_and_model == device_info.descriptor.GetNameAndModel();
      });
}

void FindAndSetDefaultVideoCamera(
    std::vector<VideoCaptureDeviceInfo>* devices_info) {
  // When available, the default video camera should be external with
  // MEDIA_VIDEO_FACING_NONE. Otherwise, it should be internal with
  // MEDIA_VIDEO_FACING_USER. It occupies the first index in |devices_info|.
  for (auto it = devices_info->begin(); it != devices_info->end(); ++it) {
    // Default video camera belongs to KSCATEGORY_VIDEO_CAMERA.
    if (it->descriptor.device_id.find(kVideoCameraGuid) != std::string::npos) {
      if (it->descriptor.facing == VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {
        std::iter_swap(devices_info->begin(), it);
        break;  // Stop iterating once an external video camera is found.
      } else if (it->descriptor.facing ==
                 VideoFacingMode::MEDIA_VIDEO_FACING_USER) {
        std::iter_swap(devices_info->begin(), it);
      }
    }
  }
}

}  // namespace

class VideoCaptureDeviceFactoryWin::ComThreadData
    : public base::RefCountedThreadSafe<ComThreadData> {
 public:
  ComThreadData(base::WeakPtr<VideoCaptureDeviceFactoryWin> device_factory,
                scoped_refptr<base::SingleThreadTaskRunner> com_thread_runner,
                scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner)
      : device_factory_(std::move(device_factory)),
        com_thread_runner_(std::move(com_thread_runner)),
        origin_task_runner_(std::move(origin_task_runner)) {}

  void EnumerateDevicesUWP(std::vector<VideoCaptureDeviceInfo> devices_info,
                           GetDevicesInfoCallback result_callback);

  void FoundAllDevicesUWP(
      std::vector<VideoCaptureDeviceInfo> devices_info,
      GetDevicesInfoCallback result_callback,
      IAsyncOperation<DeviceInformationCollection*>* operation);

 private:
  friend class base::RefCountedThreadSafe<ComThreadData>;
  ~ComThreadData() = default;

  std::unordered_set<
      raw_ptr<IAsyncOperation<DeviceInformationCollection*>, CtnExperimental>>
      async_ops_;
  base::WeakPtr<VideoCaptureDeviceFactoryWin> device_factory_;
  scoped_refptr<base::SingleThreadTaskRunner> com_thread_runner_;
  scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
};

class VideoCaptureDeviceFactoryWin::UsageReportHandler
    : public base::RefCountedThreadSafe<UsageReportHandler>,
      public IMFSensorActivitiesReportCallback {
 public:
  UsageReportHandler() : my_pid_(base::GetCurrentProcId()) {}

  // IUnknown
  IFACEMETHODIMP QueryInterface(REFIID riid, void** object) override {
    HRESULT hr = E_NOINTERFACE;
    if (riid == IID_IUnknown) {
      *object = this;
      hr = S_OK;
    } else if (riid == IID_IMFSensorActivitiesReportCallback) {
      *object = static_cast<IMFSensorActivitiesReportCallback*>(this);
      hr = S_OK;
    }
    if (SUCCEEDED(hr)) {
      AddRef();
    }

    return hr;
  }

  IFACEMETHODIMP_(ULONG) AddRef() override {
    base::RefCountedThreadSafe<UsageReportHandler>::AddRef();
    return 1U;
  }

  IFACEMETHODIMP_(ULONG) Release() override {
    base::RefCountedThreadSafe<UsageReportHandler>::Release();
    return 1U;
  }

  // IMFSensorActivitiesReportCallback
  IFACEMETHODIMP_(HRESULT)
  OnActivitiesReport(IMFSensorActivitiesReport* report) override {
    ULONG num_reports;
    std::map<std::string, CameraAvailability> report_availabilities;
    RETURN_IF_FAILED(report->GetCount(&num_reports));
    for (ULONG i = 0; i < num_reports; i++) {
      ComPtr<IMFSensorActivityReport> activity_report;
      WCHAR symbolic_name[1000] = L"";
      ULONG num_written;
      ULONG process_count;
      bool got_activity_report =
          SUCCEEDED(report->GetActivityReport(i, &activity_report)) &&
          SUCCEEDED(activity_report->GetSymbolicLink(symbolic_name, 1000,
                                                     &num_written)) &&
          SUCCEEDED(activity_report->GetProcessCount(&process_count));
      if (!got_activity_report) {
        continue;
      }
      std::optional<CameraAvailability> availability;
      for (ULONG j = 0; j < process_count; j++) {
        ComPtr<IMFSensorProcessActivity> process_activity;
        ULONG pid;
        BOOL is_streaming;
        bool got_process_info =
            SUCCEEDED(
                activity_report->GetProcessActivity(j, &process_activity)) &&
            SUCCEEDED(process_activity->GetProcessId(&pid)) &&
            SUCCEEDED(process_activity->GetStreamingState(&is_streaming));
        if (!got_process_info) {
          continue;
        }
        if (pid == my_pid_) {
          if (is_streaming) {
            // If this process is using the camera, it is known to be available.
            // No need to look at other processes.
            availability = CameraAvailability::kAvailable;
            break;
          }
        } else {
          if (is_streaming) {
            // If another process is using the camera, it is known to be
            // unavailable. No need to look at other processes.
            availability = CameraAvailability::
                kUnavailableExclusivelyUsedByOtherApplication;
            break;
          }
          // If another process is not using the camera, it might be available,
          // but need to continue looking at other processes.
          availability = CameraAvailability::kAvailable;
        }
      }
      if (!availability.has_value()) {
        continue;
      }
      std::string device_id = base::SysWideToUTF8(symbolic_name);
      std::transform(device_id.begin(), device_id.end(), device_id.begin(),
                     ::tolower);
      // It has been observed that different activity reports in the same
      // notification and for the same device can be contradictory. For example,
      // activity report 0 for device D can say process P (not self) is not
      // streaming, and activity report 1 for the same device D can say that the
      // same process P is streaming. In this case, experience shows that
      // process P is indeed streaming and the camera is unavailable.
      // To avoid replacing a correct unavailable state with an incorrect
      // available state from another report, only update the map if there is
      // no entry yet for the device or if the new state is that the device is
      // unavailable. See https://crbug.com/325590346.
      if ((report_availabilities.find(device_id) ==
           report_availabilities.end()) ||
          (*availability ==
           CameraAvailability::kUnavailableExclusivelyUsedByOtherApplication)) {
        report_availabilities[device_id] = *availability;
      }
    }

    if (report_availabilities.empty()) {
      // No state updates. Return.
      return S_OK;
    }

    UpdateAvailabilityCache(report_availabilities);
    return S_OK;
  }

  void UpdateDevicesInfoAvailability(
      std::vector<VideoCaptureDeviceInfo>* devices_info) {
    base::AutoLock lock(cache_lock_);
    std::set<std::string> device_ids;
    for (auto& info : *devices_info) {
      device_ids.insert(info.descriptor.device_id);
      auto it = availability_cache_.find(info.descriptor.device_id);
      if (it != availability_cache_.end()) {
        info.descriptor.availability = it->second;
      }
    }
    std::erase_if(availability_cache_, [&device_ids](const auto& entry) {
      return !base::Contains(device_ids, entry.first);
    });
  }

 protected:
  friend class base::RefCountedThreadSafe<UsageReportHandler>;
  virtual ~UsageReportHandler() = default;

 private:
  void UpdateAvailabilityCache(
      const std::map<std::string, CameraAvailability>& report_availabilities) {
    bool should_invoke_system_monitor = false;
    {
      base::AutoLock lock(cache_lock_);
      for (const auto& [device_id, availability] : report_availabilities) {
        auto it = availability_cache_.find(device_id);
        if (it == availability_cache_.end()) {
          availability_cache_[device_id] = availability;
          should_invoke_system_monitor = true;
        } else if (it->second != availability) {
          it->second = availability;
          should_invoke_system_monitor = true;
        }
      }
    }
    if (should_invoke_system_monitor) {
      if (auto* system_monitor = base::SystemMonitor::Get()) {
        system_monitor->ProcessDevicesChanged(
            base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE);
      }
    }
  }

  const base::ProcessId my_pid_;
  base::Lock cache_lock_;
  std::map<std::string, media::CameraAvailability> availability_cache_
      GUARDED_BY(cache_lock_);
};

// 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 const bool has_media_foundation =
      InitializeMediaFoundation() && LoadMediaFoundationDlls();
  return has_media_foundation;
}

VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin()
    : use_media_foundation_(
          base::FeatureList::IsEnabled(media::kMediaFoundationVideoCapture)),
      use_d3d11_with_media_foundation_(
          media::IsMediaFoundationD3D11VideoCaptureEnabled() &&
          switches::IsVideoCaptureUseGpuMemoryBufferEnabled()),
      com_thread_("Windows Video Capture COM Thread") {
  if (use_media_foundation_ && !PlatformSupportsMediaFoundation()) {
    use_media_foundation_ = false;
  }
  if (use_media_foundation_ &&
      switches::IsMediaFoundationCameraUsageMonitoringEnabled()) {
    CreateUsageMonitorAndReportHandler();
  }
}

VideoCaptureDeviceFactoryWin::~VideoCaptureDeviceFactoryWin() {
  if (monitor_) {
    monitor_->Stop();
  }
  if (com_thread_.IsRunning()) {
    com_thread_.Stop();
  }
}

VideoCaptureErrorOrDevice VideoCaptureDeviceFactoryWin::CreateDevice(
    const VideoCaptureDeviceDescriptor& device_descriptor) {
  DCHECK(thread_checker_.CalledOnValidThread());
  UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.Win.DeviceFactory.CaptureApi",
                            device_descriptor.capture_api);

  switch (device_descriptor.capture_api) {
    case VideoCaptureApi::WIN_MEDIA_FOUNDATION:
      [[fallthrough]];
    case VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR: {
      DCHECK(PlatformSupportsMediaFoundation());
      ComPtr<IMFMediaSource> source;
      const bool banned_for_d3d11 =
          IsDeviceBlockedForMediaFoundationD3D11ByModelId(
              GetDeviceModelId(device_descriptor.device_id));

      MFSourceOutcome outcome = CreateDeviceSourceMediaFoundation(
          device_descriptor.device_id, device_descriptor.capture_api,
          banned_for_d3d11, &source);
      switch (outcome) {
        case MFSourceOutcome::kSuccess: {
          auto device = std::make_unique<VideoCaptureDeviceMFWin>(
              device_descriptor, std::move(source),
              banned_for_d3d11 ? nullptr : dxgi_device_manager_,
              base::SingleThreadTaskRunner::GetCurrentDefault());
          DVLOG(1) << " MediaFoundation Device: "
                   << device_descriptor.display_name();
          if (device->Init()) {
            LogCaptureDeviceHashedModelId(device_descriptor);
            return VideoCaptureErrorOrDevice(std::move(device));
          }
          return VideoCaptureErrorOrDevice(
              VideoCaptureError::kWinMediaFoundationDeviceInitializationFailed);
        }
        case MFSourceOutcome::kFailedSystemPermissions:
          return VideoCaptureErrorOrDevice(
              VideoCaptureError::kWinMediaFoundationSystemPermissionDenied);
        case MFSourceOutcome::kFailed:
          return VideoCaptureErrorOrDevice(
              VideoCaptureError::kWinMediaFoundationSourceCreationFailed);
      }
      NOTREACHED_IN_MIGRATION();
      break;
    }
    case VideoCaptureApi::WIN_DIRECT_SHOW: {
      ComPtr<IBaseFilter> capture_filter;
      if (!CreateDeviceFilterDirectShow(device_descriptor.device_id,
                                        &capture_filter)) {
        return VideoCaptureErrorOrDevice(
            VideoCaptureError::kWinDirectShowDeviceFilterCreationFailed);
      }
      auto device = std::make_unique<VideoCaptureDeviceWin>(
          device_descriptor, std::move(capture_filter));
      DVLOG(1) << " DirectShow Device: " << device_descriptor.display_name();
      if (device->Init()) {
        LogCaptureDeviceHashedModelId(device_descriptor);
        return VideoCaptureErrorOrDevice(std::move(device));
      }
      return VideoCaptureErrorOrDevice(
          VideoCaptureError::kWinDirectShowDeviceInitializationFailed);
    }
    default:
      NOTREACHED_IN_MIGRATION();
      break;
  }
  NOTREACHED_IN_MIGRATION();
  return VideoCaptureErrorOrDevice(
      VideoCaptureError::kVideoCaptureDeviceFactoryWinUnknownError);
}

bool VideoCaptureDeviceFactoryWin::CreateDeviceEnumMonikerDirectShow(
    IEnumMoniker** enum_moniker) {
  DCHECK(enum_moniker);
  DCHECK(!*enum_moniker);

  // Mitigate the issues caused by loading DLLs on a background thread
  // (http://crbug/973868).
  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();

  ComPtr<ICreateDevEnum> dev_enum;
  HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
                                  CLSCTX_INPROC, IID_PPV_ARGS(&dev_enum));
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to create system device enumerator: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }

  hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
                                       enum_moniker, 0);
  // CreateClassEnumerator returns S_FALSE on some Windows OS
  // when no camera exist. Therefore the FAILED macro can't be used.
  if (hr != S_OK) {
    DLOG(ERROR) << "Failed to create video input device enum moniker: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }

  return true;
}

bool VideoCaptureDeviceFactoryWin::CreateDeviceFilterDirectShow(
    const std::string& device_id,
    IBaseFilter** capture_filter) {
  DCHECK(capture_filter);
  DCHECK(!*capture_filter);

  ComPtr<IEnumMoniker> enum_moniker;
  if (!CreateDeviceEnumMonikerDirectShow(&enum_moniker))
    return false;

  HRESULT hr = S_OK;
  for (ComPtr<IMoniker> moniker;
       enum_moniker->Next(1, &moniker, nullptr) == S_OK; moniker.Reset()) {
    ComPtr<IPropertyBag> prop_bag;
    hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag));
    if (FAILED(hr))
      continue;

    // Find |device_id| via DevicePath, Description or FriendlyName, whichever
    // is available first and is a VT_BSTR (i.e. String) type.
    static const wchar_t* kPropertyNames[] = {L"DevicePath", L"Description",
                                              L"FriendlyName"};

    ScopedVariant name;
    for (const auto* property_name : kPropertyNames) {
      prop_bag->Read(property_name, name.Receive(), 0);
      if (name.type() != VT_BSTR)
        continue;  // Continue to the next property.
      const std::string device_path(base::SysWideToUTF8(V_BSTR(name.ptr())));
      if (device_path.compare(device_id) != 0)
        break;  // Continue to the next moniker.
      // We have found the requested device.
      return CreateDeviceFilterDirectShow(std::move(moniker), capture_filter);
    }
  }

  if (SUCCEEDED(hr))
    hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  DLOG(ERROR) << "Failed to find camera filter: "
              << logging::SystemErrorCodeToString(hr);
  return false;
}

bool VideoCaptureDeviceFactoryWin::CreateDeviceFilterDirectShow(
    ComPtr<IMoniker> moniker,
    IBaseFilter** capture_filter) {
  DCHECK(capture_filter);
  DCHECK(!*capture_filter);

  // Mitigate the issues caused by loading DLLs on a background thread
  // (http://crbug/973868).
  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();

  HRESULT hr = moniker->BindToObject(0, 0, IID_PPV_ARGS(capture_filter));
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to bind camera filter: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }
  return true;
}

MFSourceOutcome VideoCaptureDeviceFactoryWin::CreateDeviceSourceMediaFoundation(
    const std::string& device_id,
    VideoCaptureApi capture_api,
    const bool banned_for_d3d11,
    IMFMediaSource** source) {
  DCHECK(source);
  DCHECK(!*source);

  ComPtr<IMFAttributes> attributes;
  DCHECK_EQ(GetMFAttributes()[0].first, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
  const auto& attributes_data =
      capture_api == VideoCaptureApi::WIN_MEDIA_FOUNDATION
          ? GetMFAttributes()[0].second
          : GetMFAttributes()[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)) {
    return MFSourceOutcome::kFailed;
  }

  attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
                        base::SysUTF8ToWide(device_id).c_str());

  return CreateDeviceSourceMediaFoundation(std::move(attributes),
                                           banned_for_d3d11, source);
}

MFSourceOutcome VideoCaptureDeviceFactoryWin::CreateDeviceSourceMediaFoundation(
    ComPtr<IMFAttributes> attributes,
    const bool banned_for_d3d11,
    IMFMediaSource** source_out) {
  ComPtr<IMFMediaSource> source;
  HRESULT hr = MFCreateDeviceSource(attributes.Get(), &source);
  DLOG_IF(ERROR, FAILED(hr)) << "MFCreateDeviceSource failed: "
                             << logging::SystemErrorCodeToString(hr);
  if (hr == E_ACCESSDENIED)
    return MFSourceOutcome::kFailedSystemPermissions;

  if (SUCCEEDED(hr) && use_d3d11_with_media_foundation_ &&
      dxgi_device_manager_ && !banned_for_d3d11) {
    dxgi_device_manager_->RegisterWithMediaSource(source);
  }
  *source_out = source.Detach();
  return SUCCEEDED(hr) ? MFSourceOutcome::kSuccess : MFSourceOutcome::kFailed;
}

bool VideoCaptureDeviceFactoryWin::EnumerateDeviceSourcesMediaFoundation(
    Microsoft::WRL::ComPtr<IMFAttributes> attributes,
    IMFActivate*** devices,
    UINT32* count) {
  HRESULT hr = MFEnumDeviceSources(attributes.Get(), devices, count);
  DLOG_IF(ERROR, FAILED(hr))
      << "MFEnumDeviceSources failed: " << logging::SystemErrorCodeToString(hr);
  return SUCCEEDED(hr);
}

void VideoCaptureDeviceFactoryWin::GetDevicesInfo(
    GetDevicesInfoCallback callback) {
  DCHECK(thread_checker_.CalledOnValidThread());

  std::vector<VideoCaptureDeviceInfo> devices_info;

  if (use_media_foundation_) {
    DCHECK(PlatformSupportsMediaFoundation());
    devices_info = GetDevicesInfoMediaFoundation();
    AugmentDevicesListWithDirectShowOnlyDevices(&devices_info);
  } else {
    devices_info = GetDevicesInfoDirectShow(devices_info);
  }

  com_thread_.init_com_with_mta(true);
  com_thread_.Start();
  com_thread_data_ =
      base::MakeRefCounted<VideoCaptureDeviceFactoryWin::ComThreadData>(
          weak_ptr_factory_.GetWeakPtr(), com_thread_.task_runner(),
          base::SingleThreadTaskRunner::GetCurrentDefault());
  com_thread_.task_runner()->PostTask(
      FROM_HERE,
      base::BindOnce(
          &VideoCaptureDeviceFactoryWin::ComThreadData::EnumerateDevicesUWP,
          com_thread_data_, std::move(devices_info), std::move(callback)));
}

void VideoCaptureDeviceFactoryWin::ComThreadData::EnumerateDevicesUWP(
    std::vector<VideoCaptureDeviceInfo> devices_info,
    GetDevicesInfoCallback result_callback) {
  DCHECK_GE(base::win::OSInfo::GetInstance()->version_number().build, 10240u);

  // When an error occurs below, the `UWP_ENUM_ERROR_HANDLER()` macro runs
  // `device_info_callback` with a `nullptr` operation.
  auto device_info_callback = base::BindOnce(
      &VideoCaptureDeviceFactoryWin::ComThreadData::FoundAllDevicesUWP,
      scoped_refptr<ComThreadData>(this), std::move(devices_info),
      std::move(result_callback));

  ComPtr<ABI::Windows::Devices::Enumeration::IDeviceInformationStatics>
      dev_info_statics;
  // Calling `GetActivationFactory` may load the DLL containing the
  // `IDeviceInformationStatics` APIs. Temporarily increase the priority
  // of this background thread to prevent hangs caused by priority inversion.
  SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
  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;
  ScopedHString aqs_filter =
      ScopedHString::Create(kVideoAndSensorCamerasAqsString);
  hr = dev_info_statics->FindAllAsyncAqsFilter(aqs_filter.get(), &async_op);
  if (FAILED(hr)) {
    UWP_ENUM_ERROR_HANDLER(hr, "Find all devices asynchronously failed: ");
    return;
  }

  // Keep a reference to incomplete |asyn_op| for releasing later.
  async_ops_.insert(async_op);

  auto callback = Microsoft::WRL::Callback<
      ABI::Windows::Foundation::IAsyncOperationCompletedHandler<
          DeviceInformationCollection*>>(
      [com_thread_runner = com_thread_runner_,
       device_info_callback = std::move(device_info_callback)](
          IAsyncOperation<DeviceInformationCollection*>* operation,
          AsyncStatus status) mutable -> HRESULT {
        com_thread_runner->PostTask(
            FROM_HERE, base::BindOnce(std::move(device_info_callback),
                                      base::Unretained(operation)));
        return S_OK;
      });

  hr = async_op->put_Completed(callback.Get());
  if (FAILED(hr)) {
    DLOG(WARNING) << "Register async operation callback failed: "
                  << logging::SystemErrorCodeToString(hr);
    // Run the callback after the error to report no devices found.
    callback->Invoke(async_op, AsyncStatus::Completed);
  }
}

void VideoCaptureDeviceFactoryWin::ComThreadData::FoundAllDevicesUWP(
    std::vector<VideoCaptureDeviceInfo> devices_info,
    GetDevicesInfoCallback result_callback,
    IAsyncOperation<DeviceInformationCollection*>* operation) {
  if (!operation) {
    origin_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
                       device_factory_, std::move(devices_info),
                       std::move(result_callback)));
    return;
  }

  ComPtr<ABI::Windows::Foundation::Collections::IVectorView<
      ABI::Windows::Devices::Enumeration::DeviceInformation*>>
      devices;
  operation->GetResults(&devices);

  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);
    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);

      ComPtr<ABI::Windows::Devices::Enumeration::IEnclosureLocation>
          enclosure_location;
      hr = device_info->get_EnclosureLocation(&enclosure_location);
      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 (auto& device : devices_info) {
        if (!device.descriptor.device_id.compare(device_id)) {
          device.descriptor.facing = facing;
          break;
        }
      }
    }
  }

  FindAndSetDefaultVideoCamera(&devices_info);

  origin_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&VideoCaptureDeviceFactoryWin::DeviceInfoReady,
                                device_factory_, std::move(devices_info),
                                std::move(result_callback)));

  auto it = async_ops_.find(operation);
  CHECK(it != async_ops_.end(), base::NotFatalUntil::M130);
  (*it)->Release();
  async_ops_.erase(it);
}

void VideoCaptureDeviceFactoryWin::UpdateDevicesInfoAvailability(
    std::vector<VideoCaptureDeviceInfo>* devices_info) {
  if (report_handler_) {
    report_handler_->UpdateDevicesInfoAvailability(devices_info);
  }
}

void VideoCaptureDeviceFactoryWin::DeviceInfoReady(
    std::vector<VideoCaptureDeviceInfo> devices_info,
    GetDevicesInfoCallback result_callback) {
  if (com_thread_.IsRunning()) {
    com_thread_.Stop();
    com_thread_data_.reset();
  }
  UpdateDevicesInfoAvailability(&devices_info);

  std::move(result_callback).Run(std::move(devices_info));
}

DevicesInfo VideoCaptureDeviceFactoryWin::GetDevicesInfoMediaFoundation() {
  DVLOG(1) << " GetDevicesInfoMediaFoundation";

  DevicesInfo devices_info;

  if (use_d3d11_with_media_foundation_ && !dxgi_device_manager_) {
    dxgi_device_manager_ = DXGIDeviceManager::Create(luid_);
  }

  // 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 : GetMFAttributes()) {
    ComPtr<IMFAttributes> attributes;
    if (!PrepareVideoCaptureAttributesMediaFoundation(
            api_attributes.second, api_attributes.second.size(), &attributes)) {
      return {};
    }
    ScopedCoMem<IMFActivate*> devices;
    UINT32 count;
    if (!EnumerateDeviceSourcesMediaFoundation(std::move(attributes), &devices,
                                               &count)) {
      return {};
    }
    const bool list_was_empty = devices_info.empty();
    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);
          const std::string display_name =
              base::SysWideToUTF8(std::wstring(name, name_size));
          if (IsDeviceBlockedForMediaFoundationByModelId(model_id) ||
              IsDeviceBlockedForMediaFoundationByDisplayName(display_name)) {
            continue;
          }
          if (list_was_empty ||
              !DevicesInfoContainsDeviceId(devices_info, device_id)) {
            ComPtr<IMFMediaSource> source;
            VideoCaptureControlSupport control_support;
            VideoCaptureFormats supported_formats;
            const bool banned_for_d3d11 =
                IsDeviceBlockedForMediaFoundationD3D11ByModelId(model_id);
            if (CreateDeviceSourceMediaFoundation(
                    device_id, api_attributes.first, banned_for_d3d11,
                    &source) == MFSourceOutcome::kSuccess) {
              control_support =
                  VideoCaptureDeviceMFWin::GetControlSupport(source);
              supported_formats = GetSupportedFormatsMediaFoundation(
                  source, banned_for_d3d11, display_name);
            }
            devices_info.emplace_back(VideoCaptureDeviceDescriptor(
                display_name, device_id, model_id, api_attributes.first,
                control_support));
            devices_info.back().supported_formats =
                std::move(supported_formats);
          }
        }
      }
      DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: "
                                 << logging::SystemErrorCodeToString(hr);
      devices[i]->Release();
    }
  }

  return devices_info;
}

// 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::AugmentDevicesListWithDirectShowOnlyDevices(
    DevicesInfo* devices_info) {
  // 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 devices list.
  DevicesInfo direct_show_devices_info =
      GetDevicesInfoDirectShow(*devices_info);
  for (const auto& direct_show_device_info : direct_show_devices_info) {
    // 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.

    DevicesInfo::const_iterator matching_non_direct_show_device =
        FindNonDirectShowDeviceInfoByNameAndModel(
            *devices_info,
            direct_show_device_info.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.
    // We should delete MediaFoundation descriptor with no supported formats
    // and use the DirectShow instead.
    if (matching_non_direct_show_device != devices_info->end()) {
      if (direct_show_device_info.supported_formats.size() == 0) {
        // Skip this DirectShow device if it has no supported formats,
        // because the MediaFoundation one should be used instead.
        continue;
      }
      // Devices, already known from MediaFoundation, shouldn't be queried with
      // DirectShow.
      DCHECK(matching_non_direct_show_device->supported_formats.size() == 0);
      devices_info->erase(matching_non_direct_show_device);
    }
    devices_info->emplace_back(direct_show_device_info);
  }
}

DevicesInfo VideoCaptureDeviceFactoryWin::GetDevicesInfoDirectShow(
    const DevicesInfo& known_devices) {
  DVLOG(1) << __func__;

  ComPtr<IEnumMoniker> enum_moniker;
  if (!CreateDeviceEnumMonikerDirectShow(&enum_moniker))
    return {};

  DevicesInfo devices_info;

  // Enumerate all video capture devices.
  for (ComPtr<IMoniker> moniker;
       enum_moniker->Next(1, &moniker, nullptr) == S_OK; moniker.Reset()) {
    ComPtr<IPropertyBag> prop_bag;
    HRESULT 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 (IsDeviceBlocked(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);

    auto device_descriptor = VideoCaptureDeviceDescriptor(
        device_name, id, model_id, VideoCaptureApi::WIN_DIRECT_SHOW,
        VideoCaptureControlSupport());

    DevicesInfo::const_iterator matching_non_direct_show_device =
        FindNonDirectShowDeviceInfoByNameAndModel(
            known_devices, device_descriptor.GetNameAndModel());

    // Skip the DirectShow device, if the same device is already known from
    // MediaFoundation and has some supported formats, since the MediaFoundation
    // descriptor would be used in the end.
    if (matching_non_direct_show_device != known_devices.end() &&
        matching_non_direct_show_device->supported_formats.size() > 0) {
      continue;
    }

    VideoCaptureControlSupport control_support;
    VideoCaptureFormats supported_formats;
    ComPtr<IBaseFilter> capture_filter;
    if (CreateDeviceFilterDirectShow(std::move(moniker), &capture_filter)) {
      control_support =
          VideoCaptureDeviceWin::GetControlSupport(capture_filter);
      supported_formats =
          GetSupportedFormatsDirectShow(capture_filter, device_name);
    }
    device_descriptor.set_control_support(control_support);
    devices_info.emplace_back(device_descriptor);
    devices_info.back().supported_formats = std::move(supported_formats);
  }

  return devices_info;
}

VideoCaptureFormats VideoCaptureDeviceFactoryWin::GetSupportedFormatsDirectShow(
    ComPtr<IBaseFilter> capture_filter,
    const std::string& display_name) {
  VideoCaptureFormats formats;
  bool query_detailed_frame_rates =
      !IsDeviceBlockedForQueryingDetailedFrameRates(display_name);
  CapabilityList capability_list;
  VideoCaptureDeviceWin::GetDeviceCapabilityList(
      capture_filter, query_detailed_frame_rates, &capability_list);
  for (const auto& entry : capability_list) {
    formats.emplace_back(entry.supported_format);
    DVLOG(1) << display_name << " "
             << VideoCaptureFormat::ToString(entry.supported_format);
  }
  return formats;
}

VideoCaptureFormats
VideoCaptureDeviceFactoryWin::GetSupportedFormatsMediaFoundation(
    ComPtr<IMFMediaSource> source,
    const bool banned_for_d3d11,
    const std::string& display_name) {
  ComPtr<IMFAttributes> source_reader_attributes;
  const bool dxgi_device_manager_available =
      (dxgi_device_manager_ != nullptr) && !banned_for_d3d11;
  if (dxgi_device_manager_available) {
    dxgi_device_manager_->RegisterWithMediaSource(source);

    HRESULT hr = MFCreateAttributes(&source_reader_attributes, 1);
    if (SUCCEEDED(hr)) {
      dxgi_device_manager_->RegisterInSourceReaderAttributes(
          source_reader_attributes.Get());
    } else {
      DLOG(ERROR) << "MFCreateAttributes failed: "
                  << logging::SystemErrorCodeToString(hr);
    }
  }

  ComPtr<IMFSourceReader> reader;
  HRESULT hr = MFCreateSourceReaderFromMediaSource(
      source.Get(), source_reader_attributes.Get(), &reader);
  if (FAILED(hr)) {
    DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: "
                << logging::SystemErrorCodeToString(hr);
    return {};
  }

  VideoCaptureFormats formats;

  DWORD stream_index = 0;
  ComPtr<IMFMediaType> type;

  while (SUCCEEDED(hr = reader->GetNativeMediaType(
                       static_cast<DWORD>(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
                       stream_index, &type))) {
    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, /*use_hardware_format=*/dxgi_device_manager_available,
        &capture_format.pixel_format);
    type.Reset();
    ++stream_index;
    if (capture_format.pixel_format == PIXEL_FORMAT_UNKNOWN)
      continue;
    formats.push_back(capture_format);

    DVLOG(1) << display_name << " "
             << VideoCaptureFormat::ToString(capture_format);
  }

  return formats;
}

scoped_refptr<DXGIDeviceManager>
VideoCaptureDeviceFactoryWin::GetDxgiDeviceManager() {
  return dxgi_device_manager_;
}

void VideoCaptureDeviceFactoryWin::OnGpuInfoUpdate(const CHROME_LUID& luid) {
  luid_ = luid;
  if (dxgi_device_manager_) {
    dxgi_device_manager_->OnGpuInfoUpdate(luid_);
  }
}

void VideoCaptureDeviceFactoryWin::CreateUsageMonitorAndReportHandler() {
  scoped_refptr<UsageReportHandler> report_handler =
      base::MakeRefCounted<UsageReportHandler>();
  if (CreateMFSensorActivityMonitor(report_handler.get(), &monitor_)) {
    report_handler_ = std::move(report_handler);
    HRESULT hr = monitor_->Start();
    if (FAILED(hr)) {
      DLOG(ERROR) << "Failed to start usage monitor";
    }
  } else {
    DLOG(ERROR) << "Failed to create usage monitor";
  }
}

}  // namespace media