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..6bba9f9a37dd9c22a535abacd9910da5bfb7f601 100644 |
| --- a/media/video/capture/win/video_capture_device_win.cc |
| +++ b/media/video/capture/win/video_capture_device_win.cc |
| @@ -10,6 +10,7 @@ |
| #include <algorithm> |
| #include <list> |
| +#include "base/strings/string_tokenizer.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/win/scoped_co_mem.h" |
| #include "base/win/scoped_variant.h" |
| @@ -21,28 +22,34 @@ using base::win::ScopedVariant; |
| namespace media { |
| -// Finds and creates a DirectShow Video Capture filter matching the device_name. |
| +// Tries to find a |device_id| of class |device_class_id|. For this, it |
| +// enumerates all system devices of the given class and does a string comparison |
| +// of its |property_name| tag. This comparison can be exact or substring-wise |
| +// depending on |exact_name_comparison|. If such a device is found, a moniker |
| +// to it is returned. |
| // static |
| -HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| - IBaseFilter** filter) { |
| - DCHECK(filter); |
| +ScopedComPtr<IMoniker> FindDeviceAndReturnMoniker(const std::string& device_id, |
| + const CLSID device_class_id, |
| + const wchar_t* property_name, |
| + bool exact_name_comparison) { |
| + ScopedComPtr<IMoniker> moniker; |
| ScopedComPtr<ICreateDevEnum> dev_enum; |
| HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
| CLSCTX_INPROC); |
| + DPLOG_IF(ERROR, FAILED(hr)) << "Create SystemDeviceEnum"; |
| if (FAILED(hr)) |
| - return hr; |
| + return moniker; |
| ScopedComPtr<IEnumMoniker> enum_moniker; |
| - hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
| + 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. |
| + // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera |
| + // exist. Therefore the FAILED macro can't be used. |
| + DPLOG_IF(ERROR, hr != S_OK) << "CreateClassEnumerator"; |
| if (hr != S_OK) |
| - return NULL; |
| + return moniker; |
| - ScopedComPtr<IMoniker> moniker; |
| - ScopedComPtr<IBaseFilter> capture_filter; |
| DWORD fetched = 0; |
| while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) { |
| ScopedComPtr<IPropertyBag> prop_bag; |
| @@ -52,35 +59,52 @@ HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| continue; |
| } |
| - // Find the device via DevicePath, Description or FriendlyName, whichever is |
| - // available first. |
| - static const wchar_t* kPropertyNames[] = { |
| - L"DevicePath", L"Description", L"FriendlyName" |
| - }; |
| ScopedVariant name; |
| - for (size_t i = 0; |
| - i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) { |
| - prop_bag->Read(kPropertyNames[i], name.Receive(), 0); |
| - } |
| + prop_bag->Read(property_name, name.Receive(), 0); |
| + |
| if (name.type() == VT_BSTR) { |
| std::string device_path(base::SysWideToUTF8(V_BSTR(&name))); |
| - if (device_path.compare(device_id) == 0) { |
| - // We have found the requested device |
| - hr = moniker->BindToObject(0, 0, IID_IBaseFilter, |
| - capture_filter.ReceiveVoid()); |
| - DLOG_IF(ERROR, FAILED(hr)) << "Failed to bind camera filter: " |
| - << logging::SystemErrorCodeToString(hr); |
| - break; |
| + if ((exact_name_comparison && device_path == device_id) || |
| + (!exact_name_comparison && |
| + device_path.find(device_id) != std::string::npos)) { |
| + return moniker; |
| } |
| } |
| moniker.Release(); |
| } |
| + return moniker; |
| +} |
| - *filter = capture_filter.Detach(); |
| - if (!*filter && SUCCEEDED(hr)) |
| - hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| +// Finds and creates a DirectShow Video Capture filter matching the device_name. |
|
perkj_chrome
2014/09/09 07:54:43
|device_id|
mcasas
2014/09/09 08:22:26
Done.
|
| +// |class_id| is usually CLSID_VideoInputDeviceCategory for standard DirectShow |
| +// device but might also be AM_KSCATEGORY_CAPTURE or AM_KSCATEGORY_CROSSBAR, to |
|
perkj_chrome
2014/09/08 13:32:11
nit devices
mcasas
2014/09/09 08:22:26
Done.
|
| +// 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); |
| + const bool kExactNameComparison = true; |
| + ScopedComPtr<IMoniker> moniker; |
| - return hr; |
| + static const wchar_t* kPropertyNames[] = { |
| + L"DevicePath", L"Description", L"FriendlyName" |
| + }; |
| + for (size_t i = 0; i < arraysize(kPropertyNames); ++i) { |
| + moniker = FindDeviceAndReturnMoniker(device_id, device_class_id, |
|
perkj_chrome
2014/09/08 13:32:11
It feels wrong look in the Description and friendl
mcasas
2014/09/09 08:22:26
This loop looks for a device by DevicePath, then D
perkj_chrome
2014/09/09 10:51:08
Acknowledged.
|
| + kPropertyNames[i], kExactNameComparison); |
| + if (!moniker) |
| + continue; |
| + ScopedComPtr<IBaseFilter> capture_filter; |
| + HRESULT hr = moniker->BindToObject(0, 0, IID_IBaseFilter, |
| + capture_filter.ReceiveVoid()); |
| + *filter = capture_filter.Detach(); |
| + if (!*filter && SUCCEEDED(hr)) |
| + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| + return hr; |
| + } |
| + DLOG(ERROR) << "Failed to find device " << device_id; |
| + return E_FAIL; |
| } |
| // Check if a Pin matches a category. |
| @@ -218,29 +242,38 @@ 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); |
| + DVLOG(2) << "Failed to create capture filter."; |
| return false; |
| } |
| output_capture_pin_ = |
| GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); |
| if (!output_capture_pin_) { |
| - DLOG(ERROR) << "Failed to get capture output pin"; |
| + DVLOG(2) << "Failed to get capture output pin"; |
| return false; |
| } |
| // Create the sink filter used for receiving Captured frames. |
| sink_filter_ = new SinkFilter(this); |
| if (sink_filter_ == NULL) { |
| - DLOG(ERROR) << "Failed to create send filter"; |
| + DVLOG(2) << "Failed to create send filter"; |
| return false; |
| } |
| @@ -249,29 +282,31 @@ bool VideoCaptureDeviceWin::Init() { |
| hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL, |
| CLSCTX_INPROC_SERVER); |
| if (FAILED(hr)) { |
| - DLOG(ERROR) << "Failed to create graph builder: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to create graph builder."; |
| return false; |
| } |
| hr = graph_builder_.QueryInterface(media_control_.Receive()); |
| if (FAILED(hr)) { |
| - DLOG(ERROR) << "Failed to create media control builder: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to create media control builder."; |
| return false; |
| } |
| hr = graph_builder_->AddFilter(capture_filter_, NULL); |
| if (FAILED(hr)) { |
| - DLOG(ERROR) << "Failed to add the capture device to the graph: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to add the capture device to the graph."; |
| + return false; |
| + } |
| + |
| + if (device_name_.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR && |
| + FAILED(AddWDMCrossbarFilterToGraphAndConnect())) { |
| + DVLOG(2)<< "Failed to add/connect 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: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2)<< "Failed to add the send filter to the graph."; |
| return false; |
| } |
| @@ -423,6 +458,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"); |
| @@ -444,22 +483,20 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
| ScopedComPtr<IAMStreamConfig> stream_config; |
| HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| if (FAILED(hr)) { |
| - DPLOG(ERROR) << "Failed to get IAMStreamConfig interface from " |
| - "capture device: " << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to get IAMStreamConfig interface from " |
| + "capture device"; |
| return false; |
| } |
| // Get interface used for getting the frame rate. |
| ScopedComPtr<IAMVideoControl> video_control; |
| hr = capture_filter_.QueryInterface(video_control.Receive()); |
| - DLOG_IF(WARNING, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED"; |
| int count = 0, size = 0; |
| hr = stream_config->GetNumberOfCapabilities(&count, &size); |
| if (FAILED(hr)) { |
| - DLOG(ERROR) << "Failed to GetNumberOfCapabilities: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to GetNumberOfCapabilities"; |
| return false; |
| } |
| @@ -470,8 +507,7 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
| // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() |
| // macros here since they'll trigger incorrectly. |
| if (hr != S_OK) { |
| - DLOG(ERROR) << "Failed to GetStreamCaps: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG(2) << "Failed to GetStreamCaps"; |
| return false; |
| } |
| @@ -553,16 +589,84 @@ void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { |
| hr = ks_propset->Set(PROPSETID_VIDCAP_VIDEOPROCAMP, |
| KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, |
| &data, sizeof(data), &data, sizeof(data)); |
| - DLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed: " |
| - << logging::SystemErrorCodeToString(hr); |
| + DVLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed."; |
|
perkj_chrome
2014/09/08 13:32:11
Why are you changing the log macros?
mcasas
2014/09/09 08:22:27
Not intentional, reverted.
|
| DVLOG_IF(2, SUCCEEDED(hr)) << "Anti-flicker set correctly."; |
| } else { |
| DVLOG(2) << "Anti-flicker setting not supported."; |
| } |
| } |
| +// Instantiate a WDM Crossbar Filter and the associated WDM Capture Filter, |
| +// extract the correct pins from each. The necessary pins are device specific |
|
perkj_chrome
2014/09/08 13:32:11
Can you document the pins you are looking for?
mcasas
2014/09/09 08:22:26
Done.
|
| +// and usually the first Crossbar output pin and the first Capture input pin. |
| +HRESULT VideoCaptureDeviceWin::InstantiateWDMFiltersAndPins() { |
| + ScopedComPtr<IMoniker> crossbar_moniker = FindDeviceAndReturnMoniker( |
| + device_name_.id(), AM_KSCATEGORY_CROSSBAR, L"DevicePath", false); |
|
perkj_chrome
2014/09/09 07:54:43
shouldnt this be an exact id comparison of the Dev
mcasas
2014/09/09 08:22:26
Done.
|
| + if (!crossbar_moniker) |
| + return E_FAIL; |
| + |
| + HRESULT hr = crossbar_moniker->BindToObject(0, 0, IID_IBaseFilter, |
| + crossbar_filter_.ReceiveVoid()); |
| + DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind 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); |
| + DLOG_IF(ERROR, !crossbar_video_output_pin_) |
| + << "Failed to find Crossbar Video Output pin"; |
| + if (!crossbar_video_output_pin_) |
| + return E_FAIL; |
| + |
| + // Find the WDM capture filter associated to the WDM Crossbar filter. This |
| + // is a fuzzy matching: they have similar names to the naked eye. Empirically, |
| + // use the words of the Crossbar Filter name one by one; they are usually |
| + // Vendor, Chip model etc to search in the WDM Filters list. |
| + base::StringTokenizer t(device_name_.name(), " "); |
| + ScopedComPtr<IMoniker> wdm_source_moniker; |
| + while (!wdm_source_moniker && t.GetNext()) { |
|
perkj_chrome
2014/09/09 07:54:43
Does this mean that you first just look for the ve
mcasas
2014/09/09 08:22:26
This is a fuzzy matching so it isn't perfect. We d
perkj_chrome
2014/09/09 10:51:08
For your example- wouldn't "WDM Card Crossbar" mak
mcasas
2014/09/10 13:42:58
OK after some offline discussion, we have decided
|
| + wdm_source_moniker = FindDeviceAndReturnMoniker( |
| + t.token(), AM_KSCATEGORY_CAPTURE, L"FriendlyName", false); |
| + } |
| + DLOG_IF(ERROR, wdm_source_moniker) << "Couldn't find WDM device named"; |
| + if (!wdm_source_moniker) |
| + return E_FAIL; |
| + |
| + hr = wdm_source_moniker->BindToObject(0, 0, IID_IBaseFilter, |
| + capture_filter_.ReceiveVoid()); |
| + DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind WDM 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); |
| + 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()); |
| + DVLOG(1) << reason; |
| state_ = kError; |
| client_->OnError(reason); |
| } |