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

Side by Side 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 nits 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/video/capture/win/video_capture_device_win.h" 5 #include "media/video/capture/win/video_capture_device_win.h"
6 6
7 #include <ks.h> 7 #include <ks.h>
8 #include <ksmedia.h> 8 #include <ksmedia.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
11 #include <list> 11 #include <list>
12 12
13 #include "base/strings/sys_string_conversions.h" 13 #include "base/strings/sys_string_conversions.h"
14 #include "base/win/scoped_co_mem.h" 14 #include "base/win/scoped_co_mem.h"
15 #include "base/win/scoped_variant.h" 15 #include "base/win/scoped_variant.h"
16 #include "media/video/capture/win/video_capture_device_mf_win.h" 16 #include "media/video/capture/win/video_capture_device_mf_win.h"
17 17
18 using base::win::ScopedCoMem; 18 using base::win::ScopedCoMem;
19 using base::win::ScopedComPtr; 19 using base::win::ScopedComPtr;
20 using base::win::ScopedVariant; 20 using base::win::ScopedVariant;
21 21
22 namespace media { 22 namespace media {
23 23
24 // Finds and creates a DirectShow Video Capture filter matching the device_name. 24 // Check if a Pin matches a category.
25 bool PinMatchesCategory(IPin* pin, REFGUID category) {
26 DCHECK(pin);
27 bool found = false;
28 ScopedComPtr<IKsPropertySet> ks_property;
29 HRESULT hr = ks_property.QueryFrom(pin);
30 if (SUCCEEDED(hr)) {
31 GUID pin_category;
32 DWORD return_value;
33 hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
34 &pin_category, sizeof(pin_category), &return_value);
35 if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
36 found = (pin_category == category);
37 }
38 }
39 return found;
40 }
41
42 // Check if a Pin's MediaType matches a given |major_type|.
43 bool PinMatchesMajorType(IPin* pin, REFGUID major_type) {
44 DCHECK(pin);
45 AM_MEDIA_TYPE connection_media_type;
46 HRESULT hr = pin->ConnectionMediaType(&connection_media_type);
47 return SUCCEEDED(hr) && connection_media_type.majortype == major_type;
48 }
49
50 // Finds and creates a DirectShow Video Capture filter matching the |device_id|.
51 // |class_id| is usually CLSID_VideoInputDeviceCategory for standard DirectShow
52 // devices but might also be AM_KSCATEGORY_CAPTURE or AM_KSCATEGORY_CROSSBAR, to
53 // enumerate WDM capture devices or WDM crossbars, respectively.
25 // static 54 // static
26 HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, 55 HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id,
56 const CLSID device_class_id,
27 IBaseFilter** filter) { 57 IBaseFilter** filter) {
28 DCHECK(filter); 58 DCHECK(filter);
29 59
30 ScopedComPtr<ICreateDevEnum> dev_enum; 60 ScopedComPtr<ICreateDevEnum> dev_enum;
31 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, 61 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
32 CLSCTX_INPROC); 62 CLSCTX_INPROC);
33 if (FAILED(hr)) 63 if (FAILED(hr))
34 return hr; 64 return hr;
35 65
36 ScopedComPtr<IEnumMoniker> enum_moniker; 66 ScopedComPtr<IEnumMoniker> enum_moniker;
37 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, 67 hr = dev_enum->CreateClassEnumerator(device_class_id, enum_moniker.Receive(),
38 enum_moniker.Receive(), 0); 68 0);
39 // CreateClassEnumerator returns S_FALSE on some Windows OS 69 // CreateClassEnumerator returns S_FALSE on some Windows OS
40 // when no camera exist. Therefore the FAILED macro can't be used. 70 // when no camera exist. Therefore the FAILED macro can't be used.
41 if (hr != S_OK) 71 if (hr != S_OK)
42 return NULL; 72 return NULL;
43 73
44 ScopedComPtr<IMoniker> moniker; 74 ScopedComPtr<IMoniker> moniker;
45 ScopedComPtr<IBaseFilter> capture_filter; 75 ScopedComPtr<IBaseFilter> capture_filter;
46 DWORD fetched = 0; 76 DWORD fetched = 0;
47 while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) { 77 while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) {
48 ScopedComPtr<IPropertyBag> prop_bag; 78 ScopedComPtr<IPropertyBag> prop_bag;
(...skipping 27 matching lines...) Expand all
76 moniker.Release(); 106 moniker.Release();
77 } 107 }
78 108
79 *filter = capture_filter.Detach(); 109 *filter = capture_filter.Detach();
80 if (!*filter && SUCCEEDED(hr)) 110 if (!*filter && SUCCEEDED(hr))
81 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); 111 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
82 112
83 return hr; 113 return hr;
84 } 114 }
85 115
86 // Check if a Pin matches a category. 116 // Finds an IPin on an IBaseFilter given the direction, Category and/or Major
87 // static 117 // Type. If either |category| or |major_type| are GUID_NULL, they are ignored.
88 bool VideoCaptureDeviceWin::PinMatchesCategory(IPin* pin, REFGUID category) {
89 DCHECK(pin);
90 bool found = false;
91 ScopedComPtr<IKsPropertySet> ks_property;
92 HRESULT hr = ks_property.QueryFrom(pin);
93 if (SUCCEEDED(hr)) {
94 GUID pin_category;
95 DWORD return_value;
96 hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
97 &pin_category, sizeof(pin_category), &return_value);
98 if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
99 found = (pin_category == category);
100 }
101 }
102 return found;
103 }
104
105 // Finds an IPin on an IBaseFilter given the direction and category.
106 // static 118 // static
107 ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, 119 ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter,
108 PIN_DIRECTION pin_dir, 120 PIN_DIRECTION pin_dir,
109 REFGUID category) { 121 REFGUID category,
122 REFGUID major_type) {
110 ScopedComPtr<IPin> pin; 123 ScopedComPtr<IPin> pin;
111 ScopedComPtr<IEnumPins> pin_enum; 124 ScopedComPtr<IEnumPins> pin_enum;
112 HRESULT hr = filter->EnumPins(pin_enum.Receive()); 125 HRESULT hr = filter->EnumPins(pin_enum.Receive());
113 if (pin_enum == NULL) 126 if (pin_enum == NULL)
114 return pin; 127 return pin;
115 128
116 // Get first unconnected pin. 129 // Get first unconnected pin.
117 hr = pin_enum->Reset(); // set to first pin 130 hr = pin_enum->Reset(); // set to first pin
118 while ((hr = pin_enum->Next(1, pin.Receive(), NULL)) == S_OK) { 131 while ((hr = pin_enum->Next(1, pin.Receive(), NULL)) == S_OK) {
119 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); 132 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
120 hr = pin->QueryDirection(&this_pin_dir); 133 hr = pin->QueryDirection(&this_pin_dir);
121 if (pin_dir == this_pin_dir) { 134 if (pin_dir == this_pin_dir) {
122 if (category == GUID_NULL || PinMatchesCategory(pin, category)) 135 if ((category == GUID_NULL || PinMatchesCategory(pin, category)) &&
136 (major_type == GUID_NULL || PinMatchesMajorType(pin, major_type))) {
123 return pin; 137 return pin;
138 }
124 } 139 }
125 pin.Release(); 140 pin.Release();
126 } 141 }
127 142
128 DCHECK(!pin); 143 DCHECK(!pin);
129 return pin; 144 return pin;
130 } 145 }
131 146
132 // static 147 // static
133 VideoPixelFormat VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat( 148 VideoPixelFormat VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 if (sink_filter_) { 226 if (sink_filter_) {
212 graph_builder_->RemoveFilter(sink_filter_); 227 graph_builder_->RemoveFilter(sink_filter_);
213 sink_filter_ = NULL; 228 sink_filter_ = NULL;
214 } 229 }
215 230
216 if (capture_filter_) 231 if (capture_filter_)
217 graph_builder_->RemoveFilter(capture_filter_); 232 graph_builder_->RemoveFilter(capture_filter_);
218 233
219 if (mjpg_filter_) 234 if (mjpg_filter_)
220 graph_builder_->RemoveFilter(mjpg_filter_); 235 graph_builder_->RemoveFilter(mjpg_filter_);
236
237 if (crossbar_filter_)
238 graph_builder_->RemoveFilter(crossbar_filter_);
221 } 239 }
222 } 240 }
223 241
224 bool VideoCaptureDeviceWin::Init() { 242 bool VideoCaptureDeviceWin::Init() {
225 DCHECK(CalledOnValidThread()); 243 DCHECK(CalledOnValidThread());
226 HRESULT hr = GetDeviceFilter(device_name_.id(), capture_filter_.Receive()); 244 HRESULT hr;
245
246 if (device_name_.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR) {
247 hr = InstantiateWDMFiltersAndPins();
248 } else {
249 hr = GetDeviceFilter(device_name_.id(), CLSID_VideoInputDeviceCategory,
250 capture_filter_.Receive());
251 }
227 if (!capture_filter_) { 252 if (!capture_filter_) {
228 DLOG(ERROR) << "Failed to create capture filter: " 253 DLOG(ERROR) << "Failed to create capture filter: "
229 << logging::SystemErrorCodeToString(hr); 254 << logging::SystemErrorCodeToString(hr);
230 return false; 255 return false;
231 } 256 }
232 257
233 output_capture_pin_ = 258 output_capture_pin_ =
234 GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); 259 GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, GUID_NULL);
235 if (!output_capture_pin_) { 260 if (!output_capture_pin_) {
236 DLOG(ERROR) << "Failed to get capture output pin"; 261 DLOG(ERROR) << "Failed to get capture output pin";
237 return false; 262 return false;
238 } 263 }
239 264
240 // Create the sink filter used for receiving Captured frames. 265 // Create the sink filter used for receiving Captured frames.
241 sink_filter_ = new SinkFilter(this); 266 sink_filter_ = new SinkFilter(this);
242 if (sink_filter_ == NULL) { 267 if (sink_filter_ == NULL) {
243 DLOG(ERROR) << "Failed to create send filter"; 268 DLOG(ERROR) << "Failed to create send filter";
244 return false; 269 return false;
(...skipping 16 matching lines...) Expand all
261 return false; 286 return false;
262 } 287 }
263 288
264 hr = graph_builder_->AddFilter(capture_filter_, NULL); 289 hr = graph_builder_->AddFilter(capture_filter_, NULL);
265 if (FAILED(hr)) { 290 if (FAILED(hr)) {
266 DLOG(ERROR) << "Failed to add the capture device to the graph: " 291 DLOG(ERROR) << "Failed to add the capture device to the graph: "
267 << logging::SystemErrorCodeToString(hr); 292 << logging::SystemErrorCodeToString(hr);
268 return false; 293 return false;
269 } 294 }
270 295
296 if (device_name_.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR &&
297 FAILED(AddWDMCrossbarFilterToGraphAndConnect())) {
298 DLOG(ERROR) << "Failed to add the WDM Crossbar filter to the graph.";
299 return false;
300 }
301
271 hr = graph_builder_->AddFilter(sink_filter_, NULL); 302 hr = graph_builder_->AddFilter(sink_filter_, NULL);
272 if (FAILED(hr)) { 303 if (FAILED(hr)) {
273 DLOG(ERROR) << "Failed to add the send filter to the graph: " 304 DLOG(ERROR) << "Failed to add the send filter to the graph: "
274 << logging::SystemErrorCodeToString(hr); 305 << logging::SystemErrorCodeToString(hr);
275 return false; 306 return false;
276 } 307 }
277 308
278 return CreateCapabilityMap(); 309 return CreateCapabilityMap();
279 } 310 }
280 311
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 SetErrorState("Failed to set capture device output format"); 372 SetErrorState("Failed to set capture device output format");
342 return; 373 return;
343 } 374 }
344 } 375 }
345 376
346 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) { 377 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) {
347 // Create MJPG filter if we need it. 378 // Create MJPG filter if we need it.
348 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); 379 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
349 380
350 if (SUCCEEDED(hr)) { 381 if (SUCCEEDED(hr)) {
351 input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL); 382 input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL,
352 output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL); 383 GUID_NULL);
384 output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL,
385 GUID_NULL);
353 hr = graph_builder_->AddFilter(mjpg_filter_, NULL); 386 hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
354 } 387 }
355 388
356 if (FAILED(hr)) { 389 if (FAILED(hr)) {
357 mjpg_filter_.Release(); 390 mjpg_filter_.Release();
358 input_mjpg_pin_.Release(); 391 input_mjpg_pin_.Release();
359 output_mjpg_pin_.Release(); 392 output_mjpg_pin_.Release();
360 } 393 }
361 } 394 }
362 395
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 } 449 }
417 450
418 graph_builder_->Disconnect(output_capture_pin_); 451 graph_builder_->Disconnect(output_capture_pin_);
419 graph_builder_->Disconnect(input_sink_pin_); 452 graph_builder_->Disconnect(input_sink_pin_);
420 453
421 // If the _mjpg filter exist disconnect it even if it has not been used. 454 // If the _mjpg filter exist disconnect it even if it has not been used.
422 if (mjpg_filter_) { 455 if (mjpg_filter_) {
423 graph_builder_->Disconnect(input_mjpg_pin_); 456 graph_builder_->Disconnect(input_mjpg_pin_);
424 graph_builder_->Disconnect(output_mjpg_pin_); 457 graph_builder_->Disconnect(output_mjpg_pin_);
425 } 458 }
459 if (crossbar_filter_) {
460 graph_builder_->Disconnect(analog_video_input_pin_);
461 graph_builder_->Disconnect(crossbar_video_output_pin_);
462 }
426 463
427 if (FAILED(hr)) { 464 if (FAILED(hr)) {
428 SetErrorState("Failed to Stop the Capture device"); 465 SetErrorState("Failed to Stop the Capture device");
429 return; 466 return;
430 } 467 }
431 client_.reset(); 468 client_.reset();
432 state_ = kIdle; 469 state_ = kIdle;
433 } 470 }
434 471
435 // Implements SinkFilterObserver::SinkFilterObserver. 472 // Implements SinkFilterObserver::SinkFilterObserver.
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, 591 KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY,
555 &data, sizeof(data), &data, sizeof(data)); 592 &data, sizeof(data), &data, sizeof(data));
556 DLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed: " 593 DLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed: "
557 << logging::SystemErrorCodeToString(hr); 594 << logging::SystemErrorCodeToString(hr);
558 DVLOG_IF(2, SUCCEEDED(hr)) << "Anti-flicker set correctly."; 595 DVLOG_IF(2, SUCCEEDED(hr)) << "Anti-flicker set correctly.";
559 } else { 596 } else {
560 DVLOG(2) << "Anti-flicker setting not supported."; 597 DVLOG(2) << "Anti-flicker setting not supported.";
561 } 598 }
562 } 599 }
563 600
601 // Instantiate a WDM Crossbar Filter and the associated WDM Capture Filter,
602 // extract the correct pins from each. The necessary pins are device specific
603 // and usually the first Crossbar output pin, with a name similar to "Video
604 // Decoder Out" and the first Capture input pin, with a name like "Analog Video
605 // In". These pins have no special Category.
606 HRESULT VideoCaptureDeviceWin::InstantiateWDMFiltersAndPins() {
607 HRESULT hr = VideoCaptureDeviceWin::GetDeviceFilter(
608 device_name_.id(),
609 AM_KSCATEGORY_CROSSBAR,
610 crossbar_filter_.Receive());
611 DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind WDM Crossbar filter";
612 if (FAILED(hr) || !crossbar_filter_)
613 return E_FAIL;
614
615 // Find Crossbar Video Output Pin: This is usually the first output pin.
616 crossbar_video_output_pin_ = GetPin(crossbar_filter_, PINDIR_OUTPUT,
617 GUID_NULL, MEDIATYPE_AnalogVideo);
618 DLOG_IF(ERROR, !crossbar_video_output_pin_)
619 << "Failed to find Crossbar Video Output pin";
620 if (!crossbar_video_output_pin_)
621 return E_FAIL;
622
623 // Use the WDM capture filter associated to the WDM Crossbar filter.
624 hr = VideoCaptureDeviceWin::GetDeviceFilter(device_name_.capabilities_id(),
625 AM_KSCATEGORY_CAPTURE,
626 capture_filter_.Receive());
627 DPLOG_IF(ERROR, FAILED(hr)) << "Failed to bind WDM Capture filter";
628 if (FAILED(hr) || !capture_filter_)
629 return E_FAIL;
630
631 // Find the WDM Capture Filter's Analog Video input Pin: usually the first
632 // input pin.
633 analog_video_input_pin_ = GetPin(capture_filter_, PINDIR_INPUT, GUID_NULL,
634 MEDIATYPE_AnalogVideo);
635 DLOG_IF(ERROR, !analog_video_input_pin_) << "Failed to find WDM Video Input";
636 if (!analog_video_input_pin_)
637 return E_FAIL;
638 return S_OK;
639 }
640
641 // Add the WDM Crossbar filter to the Graph and connect the pins previously
642 // found.
643 HRESULT VideoCaptureDeviceWin::AddWDMCrossbarFilterToGraphAndConnect() {
644 HRESULT hr = graph_builder_->AddFilter(crossbar_filter_, NULL);
645 DPLOG_IF(ERROR, FAILED(hr)) << "Failed to add Crossbar filter to the graph";
646 if (FAILED(hr))
647 return E_FAIL;
648
649 hr = graph_builder_->ConnectDirect(
650 crossbar_video_output_pin_, analog_video_input_pin_, NULL);
651 DPLOG_IF(ERROR, FAILED(hr)) << "Failed to plug WDM filters to each other";
652 if (FAILED(hr))
653 return E_FAIL;
654 return S_OK;
655 }
656
564 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { 657 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
565 DCHECK(CalledOnValidThread()); 658 DCHECK(CalledOnValidThread());
566 state_ = kError; 659 state_ = kError;
567 client_->OnError(reason); 660 client_->OnError(reason);
568 } 661 }
569 } // namespace media 662 } // namespace media
OLDNEW
« 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