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); |
} |