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

Win Video Capture: add support for WDM capture devices using a WDM Crossbar filter.

BUG=402684

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

Cr-Commit-Position: refs/heads/master@{#294356}
parent cf056b97
...@@ -209,6 +209,7 @@ static void GetDeviceSupportedFormatsDirectShow(const Name& device, ...@@ -209,6 +209,7 @@ static void GetDeviceSupportedFormatsDirectShow(const Name& device,
// VFW devices are already skipped previously in GetDeviceNames() enumeration. // VFW devices are already skipped previously in GetDeviceNames() enumeration.
base::win::ScopedComPtr<IBaseFilter> capture_filter; base::win::ScopedComPtr<IBaseFilter> capture_filter;
hr = VideoCaptureDeviceWin::GetDeviceFilter(device.capabilities_id(), hr = VideoCaptureDeviceWin::GetDeviceFilter(device.capabilities_id(),
CLSID_VideoInputDeviceCategory,
capture_filter.Receive()); capture_filter.Receive());
if (!capture_filter) { if (!capture_filter) {
DLOG(ERROR) << "Failed to create capture filter: " DLOG(ERROR) << "Failed to create capture filter: "
...@@ -219,7 +220,8 @@ static void GetDeviceSupportedFormatsDirectShow(const Name& device, ...@@ -219,7 +220,8 @@ static void GetDeviceSupportedFormatsDirectShow(const Name& device,
base::win::ScopedComPtr<IPin> output_capture_pin( base::win::ScopedComPtr<IPin> output_capture_pin(
VideoCaptureDeviceWin::GetPin(capture_filter, VideoCaptureDeviceWin::GetPin(capture_filter,
PINDIR_OUTPUT, PINDIR_OUTPUT,
PIN_CATEGORY_CAPTURE)); PIN_CATEGORY_CAPTURE,
GUID_NULL));
if (!output_capture_pin) { if (!output_capture_pin) {
DLOG(ERROR) << "Failed to get capture output pin"; DLOG(ERROR) << "Failed to get capture output pin";
return; return;
......
...@@ -21,9 +21,39 @@ using base::win::ScopedVariant; ...@@ -21,9 +21,39 @@ using base::win::ScopedVariant;
namespace media { namespace media {
// Finds and creates a DirectShow Video Capture filter matching the device_name. // Check if a Pin matches a category.
bool PinMatchesCategory(IPin* pin, REFGUID category) {
DCHECK(pin);
bool found = false;
ScopedComPtr<IKsPropertySet> ks_property;
HRESULT hr = ks_property.QueryFrom(pin);
if (SUCCEEDED(hr)) {
GUID pin_category;
DWORD return_value;
hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
&pin_category, sizeof(pin_category), &return_value);
if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
found = (pin_category == category);
}
}
return found;
}
// Check if a Pin's MediaType matches a given |major_type|.
bool PinMatchesMajorType(IPin* pin, REFGUID major_type) {
DCHECK(pin);
AM_MEDIA_TYPE connection_media_type;
HRESULT hr = pin->ConnectionMediaType(&connection_media_type);
return SUCCEEDED(hr) && connection_media_type.majortype == major_type;
}
// Finds and creates a DirectShow Video Capture filter matching the |device_id|.
// |class_id| is usually CLSID_VideoInputDeviceCategory for standard DirectShow
// devices but might also be AM_KSCATEGORY_CAPTURE or AM_KSCATEGORY_CROSSBAR, to
// enumerate WDM capture devices or WDM crossbars, respectively.
// static // static
HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id,
const CLSID device_class_id,
IBaseFilter** filter) { IBaseFilter** filter) {
DCHECK(filter); DCHECK(filter);
...@@ -34,8 +64,8 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, ...@@ -34,8 +64,8 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id,
return hr; return hr;
ScopedComPtr<IEnumMoniker> enum_moniker; ScopedComPtr<IEnumMoniker> enum_moniker;
hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, hr = dev_enum->CreateClassEnumerator(device_class_id, enum_moniker.Receive(),
enum_moniker.Receive(), 0); 0);
// 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)
...@@ -83,30 +113,13 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, ...@@ -83,30 +113,13 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id,
return hr; return hr;
} }
// Check if a Pin matches a category. // Finds an IPin on an IBaseFilter given the direction, Category and/or Major
// static // Type. If either |category| or |major_type| are GUID_NULL, they are ignored.
bool VideoCaptureDeviceWin::PinMatchesCategory(IPin* pin, REFGUID category) {
DCHECK(pin);
bool found = false;
ScopedComPtr<IKsPropertySet> ks_property;
HRESULT hr = ks_property.QueryFrom(pin);
if (SUCCEEDED(hr)) {
GUID pin_category;
DWORD return_value;
hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
&pin_category, sizeof(pin_category), &return_value);
if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
found = (pin_category == category);
}
}
return found;
}
// Finds an IPin on an IBaseFilter given the direction and category.
// static // static
ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter,
PIN_DIRECTION pin_dir, PIN_DIRECTION pin_dir,
REFGUID category) { REFGUID category,
REFGUID major_type) {
ScopedComPtr<IPin> pin; ScopedComPtr<IPin> pin;
ScopedComPtr<IEnumPins> pin_enum; ScopedComPtr<IEnumPins> pin_enum;
HRESULT hr = filter->EnumPins(pin_enum.Receive()); HRESULT hr = filter->EnumPins(pin_enum.Receive());
...@@ -119,8 +132,10 @@ ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, ...@@ -119,8 +132,10 @@ ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter,
PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
hr = pin->QueryDirection(&this_pin_dir); hr = pin->QueryDirection(&this_pin_dir);
if (pin_dir == this_pin_dir) { if (pin_dir == this_pin_dir) {
if (category == GUID_NULL || PinMatchesCategory(pin, category)) if ((category == GUID_NULL || PinMatchesCategory(pin, category)) &&
(major_type == GUID_NULL || PinMatchesMajorType(pin, major_type))) {
return pin; return pin;
}
} }
pin.Release(); pin.Release();
} }
...@@ -218,12 +233,22 @@ VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { ...@@ -218,12 +233,22 @@ VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
if (mjpg_filter_) if (mjpg_filter_)
graph_builder_->RemoveFilter(mjpg_filter_); graph_builder_->RemoveFilter(mjpg_filter_);
if (crossbar_filter_)
graph_builder_->RemoveFilter(crossbar_filter_);
} }
} }
bool VideoCaptureDeviceWin::Init() { bool VideoCaptureDeviceWin::Init() {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
HRESULT hr = GetDeviceFilter(device_name_.id(), capture_filter_.Receive()); HRESULT hr;
if (device_name_.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR) {
hr = InstantiateWDMFiltersAndPins();
} else {
hr = GetDeviceFilter(device_name_.id(), CLSID_VideoInputDeviceCategory,
capture_filter_.Receive());
}
if (!capture_filter_) { if (!capture_filter_) {
DLOG(ERROR) << "Failed to create capture filter: " DLOG(ERROR) << "Failed to create capture filter: "
<< logging::SystemErrorCodeToString(hr); << logging::SystemErrorCodeToString(hr);
...@@ -231,7 +256,7 @@ bool VideoCaptureDeviceWin::Init() { ...@@ -231,7 +256,7 @@ bool VideoCaptureDeviceWin::Init() {
} }
output_capture_pin_ = output_capture_pin_ =
GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, GUID_NULL);
if (!output_capture_pin_) { if (!output_capture_pin_) {
DLOG(ERROR) << "Failed to get capture output pin"; DLOG(ERROR) << "Failed to get capture output pin";
return false; return false;
...@@ -268,6 +293,12 @@ bool VideoCaptureDeviceWin::Init() { ...@@ -268,6 +293,12 @@ bool VideoCaptureDeviceWin::Init() {
return false; return false;
} }
if (device_name_.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR &&
FAILED(AddWDMCrossbarFilterToGraphAndConnect())) {
DLOG(ERROR) << "Failed to add the WDM Crossbar filter to the graph.";
return false;
}
hr = graph_builder_->AddFilter(sink_filter_, NULL); hr = graph_builder_->AddFilter(sink_filter_, NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
DLOG(ERROR) << "Failed to add the send filter to the graph: " DLOG(ERROR) << "Failed to add the send filter to the graph: "
...@@ -348,8 +379,10 @@ void VideoCaptureDeviceWin::AllocateAndStart( ...@@ -348,8 +379,10 @@ void VideoCaptureDeviceWin::AllocateAndStart(
hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL); input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL,
output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL); GUID_NULL);
output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL,
GUID_NULL);
hr = graph_builder_->AddFilter(mjpg_filter_, NULL); hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
} }
...@@ -423,6 +456,10 @@ void VideoCaptureDeviceWin::StopAndDeAllocate() { ...@@ -423,6 +456,10 @@ void VideoCaptureDeviceWin::StopAndDeAllocate() {
graph_builder_->Disconnect(input_mjpg_pin_); graph_builder_->Disconnect(input_mjpg_pin_);
graph_builder_->Disconnect(output_mjpg_pin_); graph_builder_->Disconnect(output_mjpg_pin_);
} }
if (crossbar_filter_) {
graph_builder_->Disconnect(analog_video_input_pin_);
graph_builder_->Disconnect(crossbar_video_output_pin_);
}
if (FAILED(hr)) { if (FAILED(hr)) {
SetErrorState("Failed to Stop the Capture device"); SetErrorState("Failed to Stop the Capture device");
...@@ -561,6 +598,62 @@ void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { ...@@ -561,6 +598,62 @@ void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() {
} }
} }
// Instantiate a WDM Crossbar Filter and the associated WDM Capture Filter,
// extract the correct pins from each. The necessary pins are device specific
// and usually the first Crossbar output pin, with a name similar to "Video
// Decoder Out" and the first Capture input pin, with a name like "Analog Video
// In". These pins have no special Category.
HRESULT VideoCaptureDeviceWin::InstantiateWDMFiltersAndPins() {
HRESULT hr = VideoCaptureDeviceWin::GetDeviceFilter(
device_name_.id(),
AM_KSCATEGORY_CROSSBAR,
crossbar_filter_.Receive());
DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind WDM Crossbar filter";
if (FAILED(hr) || !crossbar_filter_)
return E_FAIL;
// Find Crossbar Video Output Pin: This is usually the first output pin.
crossbar_video_output_pin_ = GetPin(crossbar_filter_, PINDIR_OUTPUT,
GUID_NULL, MEDIATYPE_AnalogVideo);
DLOG_IF(ERROR, !crossbar_video_output_pin_)
<< "Failed to find Crossbar Video Output pin";
if (!crossbar_video_output_pin_)
return E_FAIL;
// Use the WDM capture filter associated to the WDM Crossbar filter.
hr = VideoCaptureDeviceWin::GetDeviceFilter(device_name_.capabilities_id(),
AM_KSCATEGORY_CAPTURE,
capture_filter_.Receive());
DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind WDM Capture filter";
if (FAILED(hr) || !capture_filter_)
return E_FAIL;
// Find the WDM Capture Filter's Analog Video input Pin: usually the first
// input pin.
analog_video_input_pin_ = GetPin(capture_filter_, PINDIR_INPUT, GUID_NULL,
MEDIATYPE_AnalogVideo);
DLOG_IF(ERROR, !analog_video_input_pin_) << "Failed to find WDM Video Input";
if (!analog_video_input_pin_)
return E_FAIL;
return S_OK;
}
// Add the WDM Crossbar filter to the Graph and connect the pins previously
// found.
HRESULT VideoCaptureDeviceWin::AddWDMCrossbarFilterToGraphAndConnect() {
HRESULT hr = graph_builder_->AddFilter(crossbar_filter_, NULL);
DPLOG_IF(ERROR, FAILED(hr)) << "Failed to add Crossbar filter to the graph";
if (FAILED(hr))
return E_FAIL;
hr = graph_builder_->ConnectDirect(
crossbar_video_output_pin_, analog_video_input_pin_, NULL);
DPLOG_IF(ERROR, FAILED(hr)) << "Failed to plug WDM filters to each other";
if (FAILED(hr))
return E_FAIL;
return S_OK;
}
void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
state_ = kError; state_ = kError;
......
...@@ -54,11 +54,12 @@ class VideoCaptureDeviceWin ...@@ -54,11 +54,12 @@ class VideoCaptureDeviceWin
}; };
static HRESULT GetDeviceFilter(const std::string& device_id, static HRESULT GetDeviceFilter(const std::string& device_id,
const CLSID device_class_id,
IBaseFilter** filter); IBaseFilter** filter);
static bool PinMatchesCategory(IPin* pin, REFGUID category);
static base::win::ScopedComPtr<IPin> GetPin(IBaseFilter* filter, static base::win::ScopedComPtr<IPin> GetPin(IBaseFilter* filter,
PIN_DIRECTION pin_dir, PIN_DIRECTION pin_dir,
REFGUID category); REFGUID category,
REFGUID major_type);
static VideoPixelFormat TranslateMediaSubtypeToPixelFormat( static VideoPixelFormat TranslateMediaSubtypeToPixelFormat(
const GUID& sub_type); const GUID& sub_type);
...@@ -86,6 +87,8 @@ class VideoCaptureDeviceWin ...@@ -86,6 +87,8 @@ class VideoCaptureDeviceWin
bool CreateCapabilityMap(); bool CreateCapabilityMap();
void SetAntiFlickerInCaptureFilter(); void SetAntiFlickerInCaptureFilter();
HRESULT InstantiateWDMFiltersAndPins();
HRESULT AddWDMCrossbarFilterToGraphAndConnect();
void SetErrorState(const std::string& reason); void SetErrorState(const std::string& reason);
Name device_name_; Name device_name_;
...@@ -101,6 +104,11 @@ class VideoCaptureDeviceWin ...@@ -101,6 +104,11 @@ class VideoCaptureDeviceWin
base::win::ScopedComPtr<IBaseFilter> mjpg_filter_; base::win::ScopedComPtr<IBaseFilter> mjpg_filter_;
base::win::ScopedComPtr<IPin> input_mjpg_pin_; base::win::ScopedComPtr<IPin> input_mjpg_pin_;
base::win::ScopedComPtr<IPin> output_mjpg_pin_; base::win::ScopedComPtr<IPin> output_mjpg_pin_;
// Used for WDM devices as specified by |device_name_|. These devices need a
// WDM Crossbar Filter upstream from the Capture filter.
base::win::ScopedComPtr<IBaseFilter> crossbar_filter_;
base::win::ScopedComPtr<IPin> crossbar_video_output_pin_;
base::win::ScopedComPtr<IPin> analog_video_input_pin_;
scoped_refptr<SinkFilter> sink_filter_; scoped_refptr<SinkFilter> sink_filter_;
......
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