Commit a4b9b88c authored by mcasas's avatar mcasas Committed by Commit bot

Win Video Capture: add support for enumerating DirectShow WDM devices

DirectShow supports a certain type of Video Capture Cards, in general
video digitizers (a.k.a. Analog video capture cards). These cards need
extra configuration of the "remote" ports in and out. For this purpose,
the Windows Driver Model is used [1], where special DS Filters are
added upstream of the capture source.

This CL adds support for enumerating these devices, which will show up
as another camera in the system list.

Other posterior CLs will figure out other parts needed, namely:

- which "normal" Filter is associated to the WDM Filter, for the
purpose of capabilities enumeration.

- if the VideoCaptureDevice::Name passed to VideoCaptureDeviceWin
is labelled as being WDM, a slightly different capture graph is
needed.

For the Hauppage USB-Live 2, this means another camera called
"Hauppauge Cx23100 Crossbar", in addition to the normal "Hauppauge
USB-Live2"

[1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373405(v=vs.85).aspx

BUG=402684

Review URL: https://codereview.chromium.org/510723003

Cr-Commit-Position: refs/heads/master@{#292627}
parent b5400796
...@@ -46,6 +46,7 @@ class MEDIA_EXPORT VideoCaptureDevice { ...@@ -46,6 +46,7 @@ class MEDIA_EXPORT VideoCaptureDevice {
enum CaptureApiType { enum CaptureApiType {
MEDIA_FOUNDATION, MEDIA_FOUNDATION,
DIRECT_SHOW, DIRECT_SHOW,
DIRECT_SHOW_WDM,
API_TYPE_UNKNOWN API_TYPE_UNKNOWN
}; };
#endif #endif
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
using base::win::ScopedCoMem; using base::win::ScopedCoMem;
using base::win::ScopedComPtr; using base::win::ScopedComPtr;
using base::win::ScopedVariant; using base::win::ScopedVariant;
using Name = media::VideoCaptureDevice::Name;
using Names = media::VideoCaptureDevice::Names;
namespace media { namespace media {
...@@ -88,7 +90,7 @@ static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices, ...@@ -88,7 +90,7 @@ static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices,
return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count)); return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
} }
static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) { static void GetDeviceNamesDirectShow(Names* device_names) {
DCHECK(device_names); DCHECK(device_names);
DVLOG(1) << " GetDeviceNamesDirectShow"; DVLOG(1) << " GetDeviceNamesDirectShow";
...@@ -98,69 +100,81 @@ static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) { ...@@ -98,69 +100,81 @@ static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) {
if (FAILED(hr)) if (FAILED(hr))
return; return;
ScopedComPtr<IEnumMoniker> enum_moniker; static const struct{
hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, CLSID class_id;
enum_moniker.Receive(), 0); Name::CaptureApiType capture_api_type;
// CreateClassEnumerator returns S_FALSE on some Windows OS } kDirectShowFilterClasses[] = {
// when no camera exist. Therefore the FAILED macro can't be used. { CLSID_VideoInputDeviceCategory, Name::DIRECT_SHOW },
if (hr != S_OK) { AM_KSCATEGORY_CROSSBAR, Name::DIRECT_SHOW_WDM}
return; };
device_names->clear();
// Name of a fake DirectShow filter that exist on computers with
// GTalk installed.
static const char kGoogleCameraAdapter[] = "google camera adapter";
// Enumerate all video capture devices. device_names->clear();
ScopedComPtr<IMoniker> moniker; for (int class_index = 0; class_index < arraysize(kDirectShowFilterClasses);
int index = 0; ++class_index) {
while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) { ScopedComPtr<IEnumMoniker> enum_moniker;
ScopedComPtr<IPropertyBag> prop_bag; hr = dev_enum->CreateClassEnumerator(
hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); kDirectShowFilterClasses[class_index].class_id,
if (FAILED(hr)) { enum_moniker.Receive(),
moniker.Release(); 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)
continue; continue;
}
// Find the description or friendly name. // Name of a fake DirectShow filter that exist on computers with
ScopedVariant name; // GTalk installed.
hr = prop_bag->Read(L"Description", name.Receive(), 0); static const char kGoogleCameraAdapter[] = "google camera adapter";
if (FAILED(hr))
hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0); // Enumerate all video capture devices.
ScopedComPtr<IMoniker> moniker;
if (SUCCEEDED(hr) && name.type() == VT_BSTR) { int index = 0;
// Ignore all VFW drivers and the special Google Camera Adapter. while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
// Google Camera Adapter is not a real DirectShow camera device. ScopedComPtr<IPropertyBag> prop_bag;
// VFW are very old Video for Windows drivers that can not be used. hr = moniker->BindToStorage(0, 0, IID_IPropertyBag,
const wchar_t* str_ptr = V_BSTR(&name); prop_bag.ReceiveVoid());
const int name_length = arraysize(kGoogleCameraAdapter) - 1; if (FAILED(hr)) {
moniker.Release();
if ((wcsstr(str_ptr, L"(VFW)") == NULL) && continue;
lstrlenW(str_ptr) < name_length || }
(!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
kGoogleCameraAdapter)))) {
std::string id;
std::string device_name(base::SysWideToUTF8(str_ptr));
name.Reset();
hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
if (FAILED(hr) || name.type() != VT_BSTR) {
id = device_name;
} else {
DCHECK_EQ(name.type(), VT_BSTR);
id = base::SysWideToUTF8(V_BSTR(&name));
}
device_names->push_back(VideoCaptureDevice::Name(device_name, id, // Find the description or friendly name.
VideoCaptureDevice::Name::DIRECT_SHOW)); ScopedVariant name;
hr = prop_bag->Read(L"Description", name.Receive(), 0);
if (FAILED(hr))
hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
if (SUCCEEDED(hr) && name.type() == VT_BSTR) {
// Ignore all VFW drivers and the special Google Camera Adapter.
// Google Camera Adapter is not a real DirectShow camera device.
// VFW are very old Video for Windows drivers that can not be used.
const wchar_t* str_ptr = V_BSTR(&name);
const int name_length = arraysize(kGoogleCameraAdapter) - 1;
if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
lstrlenW(str_ptr) < name_length ||
(!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
kGoogleCameraAdapter)))) {
std::string id;
std::string device_name(base::SysWideToUTF8(str_ptr));
name.Reset();
hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
if (FAILED(hr) || name.type() != VT_BSTR) {
id = device_name;
} else {
DCHECK_EQ(name.type(), VT_BSTR);
id = base::SysWideToUTF8(V_BSTR(&name));
}
device_names->push_back(Name(device_name, id,
kDirectShowFilterClasses[class_index].capture_api_type));
}
} }
moniker.Release();
} }
moniker.Release();
} }
} }
static void GetDeviceNamesMediaFoundation( static void GetDeviceNamesMediaFoundation(Names* device_names) {
VideoCaptureDevice::Names* device_names) {
DVLOG(1) << " GetDeviceNamesMediaFoundation"; DVLOG(1) << " GetDeviceNamesMediaFoundation";
ScopedCoMem<IMFActivate*> devices; ScopedCoMem<IMFActivate*> devices;
UINT32 count; UINT32 count;
...@@ -179,10 +193,10 @@ static void GetDeviceNamesMediaFoundation( ...@@ -179,10 +193,10 @@ static void GetDeviceNamesMediaFoundation(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
&id_size); &id_size);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
device_names->push_back(VideoCaptureDevice::Name( device_names->push_back(Name(
base::SysWideToUTF8(std::wstring(name, name_size)), base::SysWideToUTF8(std::wstring(name, name_size)),
base::SysWideToUTF8(std::wstring(id, id_size)), base::SysWideToUTF8(std::wstring(id, id_size)),
VideoCaptureDevice::Name::MEDIA_FOUNDATION)); Name::MEDIA_FOUNDATION));
} }
} }
if (FAILED(hr)) if (FAILED(hr))
...@@ -191,9 +205,8 @@ static void GetDeviceNamesMediaFoundation( ...@@ -191,9 +205,8 @@ static void GetDeviceNamesMediaFoundation(
} }
} }
static void GetDeviceSupportedFormatsDirectShow( static void GetDeviceSupportedFormatsDirectShow(const Name& device,
const VideoCaptureDevice::Name& device, VideoCaptureFormats* formats) {
VideoCaptureFormats* formats) {
DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name(); DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name();
ScopedComPtr<ICreateDevEnum> dev_enum; ScopedComPtr<ICreateDevEnum> dev_enum;
HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
...@@ -280,7 +293,7 @@ static void GetDeviceSupportedFormatsDirectShow( ...@@ -280,7 +293,7 @@ static void GetDeviceSupportedFormatsDirectShow(
} }
static void GetDeviceSupportedFormatsMediaFoundation( static void GetDeviceSupportedFormatsMediaFoundation(
const VideoCaptureDevice::Name& device, const Name& device,
VideoCaptureFormats* formats) { VideoCaptureFormats* formats) {
DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name(); DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name();
ScopedComPtr<IMFMediaSource> source; ScopedComPtr<IMFMediaSource> source;
...@@ -368,11 +381,10 @@ VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() { ...@@ -368,11 +381,10 @@ VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() {
scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create( scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
const VideoCaptureDevice::Name& device_name) { const Name& device_name) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
scoped_ptr<VideoCaptureDevice> device; scoped_ptr<VideoCaptureDevice> device;
if (device_name.capture_api_type() == if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
VideoCaptureDevice::Name::MEDIA_FOUNDATION) {
DCHECK(PlatformSupportsMediaFoundation()); DCHECK(PlatformSupportsMediaFoundation());
device.reset(new VideoCaptureDeviceMFWin(device_name)); device.reset(new VideoCaptureDeviceMFWin(device_name));
DVLOG(1) << " MediaFoundation Device: " << device_name.name(); DVLOG(1) << " MediaFoundation Device: " << device_name.name();
...@@ -384,8 +396,8 @@ scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create( ...@@ -384,8 +396,8 @@ scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source)) if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
device.reset(); device.reset();
} else { } else {
DCHECK_EQ(device_name.capture_api_type(), DCHECK(device_name.capture_api_type() == Name::DIRECT_SHOW ||
VideoCaptureDevice::Name::DIRECT_SHOW); device_name.capture_api_type() == Name::DIRECT_SHOW_WDM);
device.reset(new VideoCaptureDeviceWin(device_name)); device.reset(new VideoCaptureDeviceWin(device_name));
DVLOG(1) << " DirectShow Device: " << device_name.name(); DVLOG(1) << " DirectShow Device: " << device_name.name();
if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init()) if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init())
...@@ -394,8 +406,7 @@ scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create( ...@@ -394,8 +406,7 @@ scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
return device.Pass(); return device.Pass();
} }
void VideoCaptureDeviceFactoryWin::GetDeviceNames( void VideoCaptureDeviceFactoryWin::GetDeviceNames(Names* device_names) {
VideoCaptureDevice::Names* device_names) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (use_media_foundation_) if (use_media_foundation_)
GetDeviceNamesMediaFoundation(device_names); GetDeviceNamesMediaFoundation(device_names);
...@@ -404,7 +415,7 @@ void VideoCaptureDeviceFactoryWin::GetDeviceNames( ...@@ -404,7 +415,7 @@ void VideoCaptureDeviceFactoryWin::GetDeviceNames(
} }
void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats( void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
const VideoCaptureDevice::Name& device, const Name& device,
VideoCaptureFormats* formats) { VideoCaptureFormats* formats) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (use_media_foundation_) if (use_media_foundation_)
......
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