Chromium Code Reviews| Index: media/video/capture/win/video_capture_device_win.cc |
| diff --git a/media/video/capture/win/video_capture_device_win.cc b/media/video/capture/win/video_capture_device_win.cc |
| index 877c543cc187a34fc4b4ef7926fb7bb6628d4d44..55fad6079b6041d984f693a1d587ba22bcabcab2 100644 |
| --- a/media/video/capture/win/video_capture_device_win.cc |
| +++ b/media/video/capture/win/video_capture_device_win.cc |
| @@ -21,9 +21,44 @@ using base::win::ScopedVariant; |
| namespace media { |
| -// Finds and creates a DirectShow Video Capture filter matching the device_name. |
| +// Check if a Pin matches a category. |
| +// static |
|
perkj_chrome
2014/09/10 14:58:26
nit. Remove static
mcasas
2014/09/10 15:52:45
Done.
|
| +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|. |
| +// static |
|
perkj_chrome
2014/09/10 14:58:26
dito
mcasas
2014/09/10 15:52:45
Done.
|
| +bool PinMatchesMajorType(IPin* pin, REFGUID major_type) { |
| + DCHECK(pin); |
| + AM_MEDIA_TYPE connection_media_type; |
| + HRESULT hr = pin->ConnectionMediaType(&connection_media_type); |
| + if (SUCCEEDED(hr)) |
| + return connection_media_type.majortype == major_type; |
|
perkj_chrome
2014/09/10 14:58:26
suggest return SUCCEEDED(hr) && connection_media_t
mcasas
2014/09/10 15:52:45
Done.
|
| + DVLOG(1) << "The pin does not coincide with the type"; |
| + return false; |
| +} |
| + |
| +// 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 |
| HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| + const CLSID device_class_id, |
| IBaseFilter** filter) { |
| DCHECK(filter); |
| @@ -34,8 +69,8 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| return hr; |
| ScopedComPtr<IEnumMoniker> enum_moniker; |
| - hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
| - enum_moniker.Receive(), 0); |
| + hr = dev_enum->CreateClassEnumerator(device_class_id, enum_moniker.Receive(), |
| + 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) |
| @@ -83,30 +118,13 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| return hr; |
| } |
| -// Check if a Pin matches a category. |
| -// static |
| -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. |
| +// Finds an IPin on an IBaseFilter given the direction, Category and/or Major |
| +// Type. If either |category| or |major_type| are GUID_NULL, they are ignored. |
| // static |
| ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, |
| PIN_DIRECTION pin_dir, |
| - REFGUID category) { |
| + REFGUID category, |
| + REFGUID major_type) { |
| ScopedComPtr<IPin> pin; |
| ScopedComPtr<IEnumPins> pin_enum; |
| HRESULT hr = filter->EnumPins(pin_enum.Receive()); |
| @@ -119,8 +137,10 @@ ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, |
| PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); |
| hr = pin->QueryDirection(&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; |
| + } |
| } |
| pin.Release(); |
| } |
| @@ -218,12 +238,22 @@ VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { |
| if (mjpg_filter_) |
| graph_builder_->RemoveFilter(mjpg_filter_); |
| + |
| + if (crossbar_filter_) |
| + graph_builder_->RemoveFilter(crossbar_filter_); |
| } |
| } |
| bool VideoCaptureDeviceWin::Init() { |
| 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_) { |
| DLOG(ERROR) << "Failed to create capture filter: " |
| << logging::SystemErrorCodeToString(hr); |
| @@ -231,7 +261,7 @@ bool VideoCaptureDeviceWin::Init() { |
| } |
| 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_) { |
| DLOG(ERROR) << "Failed to get capture output pin"; |
| return false; |
| @@ -268,6 +298,12 @@ bool VideoCaptureDeviceWin::Init() { |
| 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); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "Failed to add the send filter to the graph: " |
| @@ -348,8 +384,10 @@ void VideoCaptureDeviceWin::AllocateAndStart( |
| hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); |
| if (SUCCEEDED(hr)) { |
| - input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL); |
| - output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL); |
| + input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, |
| + GUID_NULL); |
| + output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL, |
| + GUID_NULL); |
| hr = graph_builder_->AddFilter(mjpg_filter_, NULL); |
| } |
| @@ -423,6 +461,10 @@ void VideoCaptureDeviceWin::StopAndDeAllocate() { |
| graph_builder_->Disconnect(input_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)) { |
| SetErrorState("Failed to Stop the Capture device"); |
| @@ -561,6 +603,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) { |
| DCHECK(CalledOnValidThread()); |
| state_ = kError; |