Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Unified Diff: media/video/capture/win/video_capture_device_win.cc

Issue 546803002: Win Video Capture: add support for WDM capture devices using a WDM Crossbar filter. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: perkj@s comments Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/video/capture/win/video_capture_device_win.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..d9156ad5f95e3993843b9031af4c5838ef6488c9 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_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);
+ 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,
+ 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_)
magjed_chromium 2014/09/09 16:50:24 nit: should have space between if and '('
+ 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");
@@ -445,7 +484,7 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() {
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);
+ "capture device: " << logging::SystemErrorCodeToString(hr);
return false;
}
@@ -561,8 +600,79 @@ 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() {
+ ScopedComPtr<IMoniker> crossbar_moniker = FindDeviceAndReturnMoniker(
+ device_name_.id(), AM_KSCATEGORY_CROSSBAR, L"DevicePath", true);
+ 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()) {
+ 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);
}
« no previous file with comments | « media/video/capture/win/video_capture_device_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698