Commit 8f68d009 authored by Réda Housni Alaoui's avatar Réda Housni Alaoui Committed by Commit Bot

[Video Capture Win] Append DirectShow virtual cameras to MediaFoundation

enumeration

MediaFoundation can't handle DirectShow virtual cameras.
This is to append MediaFoundation unsupported DirectShow cameras to the
camera enumeration supplied to the consumer.

Bug: 730068
Change-Id: I3240e2158f822c04a193ec20a091c1f17ee2e373
Reviewed-on: https://chromium-review.googlesource.com/1000853
Commit-Queue: Christian Fremerey <chfremer@chromium.org>
Reviewed-by: default avatarChristian Fremerey <chfremer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549550}
parent 3a063d1b
...@@ -211,19 +211,27 @@ std::string GetDeviceModelId(const std::string& device_id) { ...@@ -211,19 +211,27 @@ std::string GetDeviceModelId(const std::string& device_id) {
return id_vendor + ":" + id_product; return id_vendor + ":" + id_product;
} }
void GetDeviceDescriptorsDirectShow(Descriptors* device_descriptors) { HRESULT EnumerateDirectShowDevices(IEnumMoniker** enum_moniker) {
DCHECK(device_descriptors);
DVLOG(1) << __func__;
ComPtr<ICreateDevEnum> dev_enum; ComPtr<ICreateDevEnum> dev_enum;
HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, HRESULT hr = ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_PPV_ARGS(&dev_enum)); IID_PPV_ARGS(&dev_enum));
if (FAILED(hr)) if (FAILED(hr))
return; return hr;
ComPtr<IEnumMoniker> enum_moniker;
hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
enum_moniker.GetAddressOf(), 0); enum_moniker, 0);
return hr;
}
void GetDeviceDescriptorsDirectShow(
VideoCaptureDeviceFactoryWin::DirectShowEnumDevicesFunc
direct_show_enum_devices_func,
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 // CreateClassEnumerator returns S_FALSE on some Windows OS
// when no camera exist. Therefore the FAILED macro can't be used. // when no camera exist. Therefore the FAILED macro can't be used.
if (hr != S_OK) if (hr != S_OK)
...@@ -277,9 +285,25 @@ bool DescriptorsContainDeviceId(const Descriptors& descriptors, ...@@ -277,9 +285,25 @@ bool DescriptorsContainDeviceId(const Descriptors& descriptors,
}) != descriptors.end(); }) != descriptors.end();
} }
// Returns true if the provided descriptors contains a non DirectShow descriptor
// using the provided name and model
bool DescriptorsContainNonDirectShowNameAndModel(
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();
}) != descriptors.end();
}
void GetDeviceDescriptorsMediaFoundation( void GetDeviceDescriptorsMediaFoundation(
VideoCaptureDeviceFactoryWin::MFEnumDeviceSourcesFunc VideoCaptureDeviceFactoryWin::MFEnumDeviceSourcesFunc
mf_enum_device_sources_func, mf_enum_device_sources_func,
VideoCaptureDeviceFactoryWin::DirectShowEnumDevicesFunc
direct_show_enum_devices_func,
Descriptors* device_descriptors) { Descriptors* device_descriptors) {
DVLOG(1) << " GetDeviceDescriptorsMediaFoundation"; DVLOG(1) << " GetDeviceDescriptorsMediaFoundation";
// Recent non-RGB (depth, IR) cameras could be marked as sensor cameras in // Recent non-RGB (depth, IR) cameras could be marked as sensor cameras in
...@@ -324,6 +348,25 @@ void GetDeviceDescriptorsMediaFoundation( ...@@ -324,6 +348,25 @@ void GetDeviceDescriptorsMediaFoundation(
devices[i]->Release(); devices[i]->Release();
} }
} }
// 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_enum_devices_func,
&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.
if (DescriptorsContainNonDirectShowNameAndModel(
*device_descriptors, direct_show_descriptor.GetNameAndModel())) {
continue;
}
device_descriptors->emplace_back(direct_show_descriptor);
}
} }
void GetDeviceSupportedFormatsDirectShow(const Descriptor& descriptor, void GetDeviceSupportedFormatsDirectShow(const Descriptor& descriptor,
...@@ -443,6 +486,8 @@ VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() ...@@ -443,6 +486,8 @@ VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin()
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
mf_enum_device_sources_func_ = mf_enum_device_sources_func_ =
PlatformSupportsMediaFoundation() ? MFEnumDeviceSources : nullptr; PlatformSupportsMediaFoundation() ? MFEnumDeviceSources : nullptr;
direct_show_enum_devices_func_ =
base::BindRepeating(&EnumerateDirectShowDevices);
if (!PlatformSupportsMediaFoundation()) { if (!PlatformSupportsMediaFoundation()) {
use_media_foundation_ = false; use_media_foundation_ = false;
LogVideoCaptureWinBackendUsed( LogVideoCaptureWinBackendUsed(
...@@ -500,9 +545,11 @@ void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors( ...@@ -500,9 +545,11 @@ void VideoCaptureDeviceFactoryWin::GetDeviceDescriptors(
if (use_media_foundation_) { if (use_media_foundation_) {
GetDeviceDescriptorsMediaFoundation(mf_enum_device_sources_func_, GetDeviceDescriptorsMediaFoundation(mf_enum_device_sources_func_,
direct_show_enum_devices_func_,
device_descriptors); device_descriptors);
} else { } else {
GetDeviceDescriptorsDirectShow(device_descriptors); GetDeviceDescriptorsDirectShow(direct_show_enum_devices_func_,
device_descriptors);
} }
} }
......
...@@ -30,6 +30,8 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin ...@@ -30,6 +30,8 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin
~VideoCaptureDeviceFactoryWin() override; ~VideoCaptureDeviceFactoryWin() override;
using MFEnumDeviceSourcesFunc = decltype(&MFEnumDeviceSources); using MFEnumDeviceSourcesFunc = decltype(&MFEnumDeviceSources);
using DirectShowEnumDevicesFunc =
base::RepeatingCallback<HRESULT(IEnumMoniker**)>;
std::unique_ptr<VideoCaptureDevice> CreateDevice( std::unique_ptr<VideoCaptureDevice> CreateDevice(
const VideoCaptureDeviceDescriptor& device_descriptor) override; const VideoCaptureDeviceDescriptor& device_descriptor) override;
...@@ -49,6 +51,10 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin ...@@ -49,6 +51,10 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin
MFEnumDeviceSourcesFunc func) { MFEnumDeviceSourcesFunc func) {
mf_enum_device_sources_func_ = func; mf_enum_device_sources_func_ = func;
} }
void set_direct_show_enum_devices_func_for_testing(
DirectShowEnumDevicesFunc func) {
direct_show_enum_devices_func_ = func;
}
private: private:
void EnumerateDevicesUWP( void EnumerateDevicesUWP(
...@@ -67,6 +73,7 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin ...@@ -67,6 +73,7 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryWin
// |mf_enum_device_sources_func_| points to MFEnumDeviceSources. It enables // |mf_enum_device_sources_func_| points to MFEnumDeviceSources. It enables
// mock of Media Foundation API in unit tests. // mock of Media Foundation API in unit tests.
MFEnumDeviceSourcesFunc mf_enum_device_sources_func_ = nullptr; MFEnumDeviceSourcesFunc mf_enum_device_sources_func_ = nullptr;
DirectShowEnumDevicesFunc direct_show_enum_devices_func_;
// For calling WinRT methods on a COM initiated thread. // For calling WinRT methods on a COM initiated thread.
base::Thread com_thread_; base::Thread com_thread_;
......
...@@ -20,12 +20,28 @@ namespace media { ...@@ -20,12 +20,28 @@ namespace media {
namespace { namespace {
const wchar_t* kDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00"; // MediaFoundation devices
const wchar_t* kDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00"; const wchar_t* kMFDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
const wchar_t* kDeviceId2 = L"\\\\?\\usb#vid_0002&pid_0002&mi_00"; const wchar_t* kMFDeviceName0 = L"Device 0";
const wchar_t* kDeviceName0 = L"Device 0";
const wchar_t* kDeviceName1 = L"Device 1"; const wchar_t* kMFDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00";
const wchar_t* kDeviceName2 = L"Device 2"; const wchar_t* kMFDeviceName1 = L"Device 1";
const wchar_t* kMFDeviceId2 = L"\\\\?\\usb#vid_0002&pid_0002&mi_00";
const wchar_t* kMFDeviceName2 = L"Device 2";
// DirectShow devices
const wchar_t* kDirectShowDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
const wchar_t* kDirectShowDeviceName0 = L"Device 0";
const wchar_t* kDirectShowDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00#1";
const wchar_t* kDirectShowDeviceName1 = L"Device 1";
const wchar_t* kDirectShowDeviceId3 = L"Virtual Camera 3";
const wchar_t* kDirectShowDeviceName3 = L"Virtual Camera";
const wchar_t* kDirectShowDeviceId4 = L"Virtual Camera 4";
const wchar_t* kDirectShowDeviceName4 = L"Virtual Camera";
using iterator = VideoCaptureDeviceDescriptors::const_iterator; using iterator = VideoCaptureDeviceDescriptors::const_iterator;
iterator FindDescriptorInRange(iterator begin, iterator FindDescriptorInRange(iterator begin,
...@@ -210,13 +226,189 @@ class MockMFActivate : public base::RefCountedThreadSafe<MockMFActivate>, ...@@ -210,13 +226,189 @@ class MockMFActivate : public base::RefCountedThreadSafe<MockMFActivate>,
const bool kscategory_sensor_camera_; const bool kscategory_sensor_camera_;
}; };
class StubPropertyBag : public base::RefCountedThreadSafe<StubPropertyBag>,
public IPropertyBag {
public:
StubPropertyBag(const wchar_t* device_path, const wchar_t* description)
: device_path_(device_path), description_(description) {}
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)(void) override {
base::RefCountedThreadSafe<StubPropertyBag>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)(void) override {
base::RefCountedThreadSafe<StubPropertyBag>::Release();
return 1U;
}
STDMETHOD(Read)
(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog) override {
if (pszPropName == std::wstring(L"Description")) {
pVar->vt = VT_BSTR;
pVar->bstrVal = SysAllocString(description_);
return S_OK;
}
if (pszPropName == std::wstring(L"DevicePath")) {
pVar->vt = VT_BSTR;
pVar->bstrVal = SysAllocString(device_path_);
return S_OK;
}
return E_NOTIMPL;
}
STDMETHOD(Write)(LPCOLESTR pszPropName, VARIANT* pVar) override {
return E_NOTIMPL;
}
private:
friend class base::RefCountedThreadSafe<StubPropertyBag>;
virtual ~StubPropertyBag() = default;
const wchar_t* device_path_;
const wchar_t* description_;
};
class StubMoniker : public base::RefCountedThreadSafe<StubMoniker>,
public IMoniker {
public:
StubMoniker(const wchar_t* device_path, const wchar_t* description)
: device_path_(device_path), description_(description) {}
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)(void) override {
base::RefCountedThreadSafe<StubMoniker>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)(void) override {
base::RefCountedThreadSafe<StubMoniker>::Release();
return 1U;
}
STDMETHOD(GetClassID)(CLSID* pClassID) override { return E_NOTIMPL; }
STDMETHOD(IsDirty)(void) override { return E_NOTIMPL; }
STDMETHOD(Load)(IStream* pStm) override { return E_NOTIMPL; }
STDMETHOD(Save)(IStream* pStm, BOOL fClearDirty) override {
return E_NOTIMPL;
}
STDMETHOD(GetSizeMax)(ULARGE_INTEGER* pcbSize) override { return E_NOTIMPL; }
STDMETHOD(BindToObject)
(IBindCtx* pbc,
IMoniker* pmkToLeft,
REFIID riidResult,
void** ppvResult) override {
return E_NOTIMPL;
}
STDMETHOD(BindToStorage)
(IBindCtx* pbc, IMoniker* pmkToLeft, REFIID riid, void** ppvObj) override {
StubPropertyBag* propertyBag =
new StubPropertyBag(device_path_, description_);
propertyBag->AddRef();
*ppvObj = propertyBag;
return S_OK;
}
STDMETHOD(Reduce)
(IBindCtx* pbc,
DWORD dwReduceHowFar,
IMoniker** ppmkToLeft,
IMoniker** ppmkReduced) override {
return E_NOTIMPL;
}
STDMETHOD(ComposeWith)
(IMoniker* pmkRight,
BOOL fOnlyIfNotGeneric,
IMoniker** ppmkComposite) override {
return E_NOTIMPL;
}
STDMETHOD(Enum)(BOOL fForward, IEnumMoniker** ppenumMoniker) override {
return E_NOTIMPL;
}
STDMETHOD(IsEqual)(IMoniker* pmkOtherMoniker) override { return E_NOTIMPL; }
STDMETHOD(Hash)(DWORD* pdwHash) override { return E_NOTIMPL; }
STDMETHOD(IsRunning)
(IBindCtx* pbc, IMoniker* pmkToLeft, IMoniker* pmkNewlyRunning) override {
return E_NOTIMPL;
}
STDMETHOD(GetTimeOfLastChange)
(IBindCtx* pbc, IMoniker* pmkToLeft, FILETIME* pFileTime) override {
return E_NOTIMPL;
}
STDMETHOD(Inverse)(IMoniker** ppmk) override { return E_NOTIMPL; }
STDMETHOD(CommonPrefixWith)
(IMoniker* pmkOther, IMoniker** ppmkPrefix) override { return E_NOTIMPL; }
STDMETHOD(RelativePathTo)
(IMoniker* pmkOther, IMoniker** ppmkRelPath) override { return E_NOTIMPL; }
STDMETHOD(GetDisplayName)
(IBindCtx* pbc, IMoniker* pmkToLeft, LPOLESTR* ppszDisplayName) override {
return E_NOTIMPL;
}
STDMETHOD(ParseDisplayName)
(IBindCtx* pbc,
IMoniker* pmkToLeft,
LPOLESTR pszDisplayName,
ULONG* pchEaten,
IMoniker** ppmkOut) override {
return E_NOTIMPL;
}
STDMETHOD(IsSystemMoniker)(DWORD* pdwMksys) override { return E_NOTIMPL; }
private:
friend class base::RefCountedThreadSafe<StubMoniker>;
virtual ~StubMoniker() = default;
const wchar_t* device_path_;
const wchar_t* description_;
};
class StubEnumMoniker : public base::RefCountedThreadSafe<StubEnumMoniker>,
public IEnumMoniker {
public:
void AddMoniker(StubMoniker* moniker) { monikers_.push_back(moniker); }
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<StubEnumMoniker>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<StubEnumMoniker>::Release();
return 1U;
}
STDMETHOD(Next)
(ULONG celt, IMoniker** rgelt, ULONG* pceltFetched) override {
cursor_position_ = cursor_position_ + celt;
if (cursor_position_ >= monikers_.size())
return E_FAIL;
IMoniker* moniker = monikers_.at(cursor_position_);
*rgelt = moniker;
moniker->AddRef();
return S_OK;
}
STDMETHOD(Skip)(ULONG celt) override { return E_NOTIMPL; }
STDMETHOD(Reset)(void) override {
cursor_position_ = unsigned(-1);
return S_OK;
}
STDMETHOD(Clone)(IEnumMoniker** ppenum) override { return E_NOTIMPL; }
private:
friend class base::RefCountedThreadSafe<StubEnumMoniker>;
virtual ~StubEnumMoniker() = default;
std::vector<IMoniker*> monikers_;
ULONG cursor_position_ = unsigned(-1);
};
HRESULT __stdcall MockMFEnumDeviceSources(IMFAttributes* attributes, HRESULT __stdcall MockMFEnumDeviceSources(IMFAttributes* attributes,
IMFActivate*** devices, IMFActivate*** devices,
UINT32* count) { UINT32* count) {
MockMFActivate* mock_devices[] = { MockMFActivate* mock_devices[] = {
new MockMFActivate(kDeviceId0, kDeviceName0, true, false), new MockMFActivate(kMFDeviceId0, kMFDeviceName0, true, false),
new MockMFActivate(kDeviceId1, kDeviceName1, true, true), new MockMFActivate(kMFDeviceId1, kMFDeviceName1, true, true),
new MockMFActivate(kDeviceId2, kDeviceName2, false, true)}; new MockMFActivate(kMFDeviceId2, kMFDeviceName2, false, true)};
// Iterate once to get the match count and check for errors. // Iterate once to get the match count and check for errors.
*count = 0U; *count = 0U;
HRESULT hr; HRESULT hr;
...@@ -240,6 +432,22 @@ HRESULT __stdcall MockMFEnumDeviceSources(IMFAttributes* attributes, ...@@ -240,6 +432,22 @@ HRESULT __stdcall MockMFEnumDeviceSources(IMFAttributes* attributes,
return S_OK; return S_OK;
} }
HRESULT EnumerateStubDirectShowDevices(IEnumMoniker** enum_moniker) {
StubMoniker* monikers[] = {
new StubMoniker(kDirectShowDeviceId0, kDirectShowDeviceName0),
new StubMoniker(kDirectShowDeviceId1, kDirectShowDeviceName1),
new StubMoniker(kDirectShowDeviceId3, kDirectShowDeviceName3),
new StubMoniker(kDirectShowDeviceId4, kDirectShowDeviceName4)};
StubEnumMoniker* stub_enum_moniker = new StubEnumMoniker();
for (StubMoniker* moniker : monikers)
stub_enum_moniker->AddMoniker(moniker);
stub_enum_moniker->AddRef();
*enum_moniker = stub_enum_moniker;
return S_OK;
}
} // namespace } // namespace
class VideoCaptureDeviceFactoryWinTest : public ::testing::Test { class VideoCaptureDeviceFactoryWinTest : public ::testing::Test {
...@@ -270,29 +478,45 @@ TEST_F(VideoCaptureDeviceFactoryMFWinTest, GetDeviceDescriptors) { ...@@ -270,29 +478,45 @@ TEST_F(VideoCaptureDeviceFactoryMFWinTest, GetDeviceDescriptors) {
return; return;
factory_.set_mf_enum_device_sources_func_for_testing( factory_.set_mf_enum_device_sources_func_for_testing(
&MockMFEnumDeviceSources); &MockMFEnumDeviceSources);
factory_.set_direct_show_enum_devices_func_for_testing(
base::BindRepeating(&EnumerateStubDirectShowDevices));
VideoCaptureDeviceDescriptors descriptors; VideoCaptureDeviceDescriptors descriptors;
factory_.GetDeviceDescriptors(&descriptors); factory_.GetDeviceDescriptors(&descriptors);
EXPECT_EQ(descriptors.size(), 3U); EXPECT_EQ(descriptors.size(), 5U);
for (auto it = descriptors.begin(); it != descriptors.end(); it++) { for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
// Verify that there are no duplicates. // Verify that there are no duplicates.
EXPECT_EQ(FindDescriptorInRange(descriptors.begin(), it, it->device_id), EXPECT_EQ(FindDescriptorInRange(descriptors.begin(), it, it->device_id),
it); it);
} }
iterator it = FindDescriptorInRange(descriptors.begin(), descriptors.end(), iterator it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
base::SysWideToUTF8(kDeviceId0)); base::SysWideToUTF8(kMFDeviceId0));
EXPECT_NE(it, descriptors.end()); EXPECT_NE(it, descriptors.end());
EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION); EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDeviceName0)); EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName0));
it = FindDescriptorInRange(descriptors.begin(), descriptors.end(), it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
base::SysWideToUTF8(kDeviceId1)); base::SysWideToUTF8(kMFDeviceId1));
EXPECT_NE(it, descriptors.end()); EXPECT_NE(it, descriptors.end());
EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION); EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDeviceName1)); EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName1));
it = FindDescriptorInRange(descriptors.begin(), descriptors.end(), it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
base::SysWideToUTF8(kDeviceId2)); base::SysWideToUTF8(kMFDeviceId2));
EXPECT_NE(it, descriptors.end()); EXPECT_NE(it, descriptors.end());
EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR); EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR);
EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDeviceName2)); EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kMFDeviceName2));
it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
base::SysWideToUTF8(kDirectShowDeviceId3));
EXPECT_NE(it, descriptors.end());
EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName3));
it = FindDescriptorInRange(descriptors.begin(), descriptors.end(),
base::SysWideToUTF8(kDirectShowDeviceId4));
EXPECT_NE(it, descriptors.end());
EXPECT_EQ(it->capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->display_name(), base::SysWideToUTF8(kDirectShowDeviceName4));
} }
} // namespace media } // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment