OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |