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 3// found in the LICENSE file. 4 5#include "media/video/capture/win/video_capture_device_win.h" 6 7#include <ks.h> 8#include <ksmedia.h> 9 10#include <algorithm> 11#include <list> 12 13#include "base/strings/sys_string_conversions.h" 14#include "base/win/scoped_co_mem.h" 15#include "base/win/scoped_variant.h" 16#include "media/video/capture/win/video_capture_device_mf_win.h" 17 18using base::win::ScopedCoMem; 19using base::win::ScopedComPtr; 20using base::win::ScopedVariant; 21 22namespace media { 23 24// Check if a Pin matches a category. 25bool 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|. 43bool 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. 54// static 55HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, 56 const CLSID device_class_id, 57 IBaseFilter** filter) { 58 DCHECK(filter); 59 60 ScopedComPtr<ICreateDevEnum> dev_enum; 61 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, 62 CLSCTX_INPROC); 63 if (FAILED(hr)) 64 return hr; 65 66 ScopedComPtr<IEnumMoniker> enum_moniker; 67 hr = dev_enum->CreateClassEnumerator(device_class_id, enum_moniker.Receive(), 68 0); 69 // CreateClassEnumerator returns S_FALSE on some Windows OS 70 // when no camera exist. Therefore the FAILED macro can't be used. 71 if (hr != S_OK) 72 return NULL; 73 74 ScopedComPtr<IMoniker> moniker; 75 ScopedComPtr<IBaseFilter> capture_filter; 76 DWORD fetched = 0; 77 while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) { 78 ScopedComPtr<IPropertyBag> prop_bag; 79 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); 80 if (FAILED(hr)) { 81 moniker.Release(); 82 continue; 83 } 84 85 // Find the device via DevicePath, Description or FriendlyName, whichever is 86 // available first. 87 static const wchar_t* kPropertyNames[] = { 88 L"DevicePath", L"Description", L"FriendlyName" 89 }; 90 ScopedVariant name; 91 for (size_t i = 0; 92 i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) { 93 prop_bag->Read(kPropertyNames[i], name.Receive(), 0); 94 } 95 if (name.type() == VT_BSTR) { 96 std::string device_path(base::SysWideToUTF8(V_BSTR(&name))); 97 if (device_path.compare(device_id) == 0) { 98 // We have found the requested device 99 hr = moniker->BindToObject(0, 0, IID_IBaseFilter, 100 capture_filter.ReceiveVoid()); 101 DLOG_IF(ERROR, FAILED(hr)) << "Failed to bind camera filter: " 102 << logging::SystemErrorCodeToString(hr); 103 break; 104 } 105 } 106 moniker.Release(); 107 } 108 109 *filter = capture_filter.Detach(); 110 if (!*filter && SUCCEEDED(hr)) 111 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); 112 113 return hr; 114} 115 116// Finds an IPin on an IBaseFilter given the direction, Category and/or Major 117// Type. If either |category| or |major_type| are GUID_NULL, they are ignored. 118// static 119ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, 120 PIN_DIRECTION pin_dir, 121 REFGUID category, 122 REFGUID major_type) { 123 ScopedComPtr<IPin> pin; 124 ScopedComPtr<IEnumPins> pin_enum; 125 HRESULT hr = filter->EnumPins(pin_enum.Receive()); 126 if (pin_enum == NULL) 127 return pin; 128 129 // Get first unconnected pin. 130 hr = pin_enum->Reset(); // set to first pin 131 while ((hr = pin_enum->Next(1, pin.Receive(), NULL)) == S_OK) { 132 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); 133 hr = pin->QueryDirection(&this_pin_dir); 134 if (pin_dir == this_pin_dir) { 135 if ((category == GUID_NULL || PinMatchesCategory(pin, category)) && 136 (major_type == GUID_NULL || PinMatchesMajorType(pin, major_type))) { 137 return pin; 138 } 139 } 140 pin.Release(); 141 } 142 143 DCHECK(!pin); 144 return pin; 145} 146 147// static 148VideoPixelFormat VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat( 149 const GUID& sub_type) { 150 static struct { 151 const GUID& sub_type; 152 VideoPixelFormat format; 153 } pixel_formats[] = { 154 { kMediaSubTypeI420, PIXEL_FORMAT_I420 }, 155 { MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420 }, 156 { MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24 }, 157 { MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2 }, 158 { MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG }, 159 { MEDIASUBTYPE_UYVY, PIXEL_FORMAT_UYVY }, 160 { MEDIASUBTYPE_ARGB32, PIXEL_FORMAT_ARGB }, 161 { kMediaSubTypeHDYC, PIXEL_FORMAT_UYVY }, 162 }; 163 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(pixel_formats); ++i) { 164 if (sub_type == pixel_formats[i].sub_type) 165 return pixel_formats[i].format; 166 } 167#ifndef NDEBUG 168 WCHAR guid_str[128]; 169 StringFromGUID2(sub_type, guid_str, arraysize(guid_str)); 170 DVLOG(2) << "Device (also) supports an unknown media type " << guid_str; 171#endif 172 return PIXEL_FORMAT_UNKNOWN; 173} 174 175void VideoCaptureDeviceWin::ScopedMediaType::Free() { 176 if (!media_type_) 177 return; 178 179 DeleteMediaType(media_type_); 180 media_type_= NULL; 181} 182 183AM_MEDIA_TYPE** VideoCaptureDeviceWin::ScopedMediaType::Receive() { 184 DCHECK(!media_type_); 185 return &media_type_; 186} 187 188// Release the format block for a media type. 189// http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx 190void VideoCaptureDeviceWin::ScopedMediaType::FreeMediaType(AM_MEDIA_TYPE* mt) { 191 if (mt->cbFormat != 0) { 192 CoTaskMemFree(mt->pbFormat); 193 mt->cbFormat = 0; 194 mt->pbFormat = NULL; 195 } 196 if (mt->pUnk != NULL) { 197 NOTREACHED(); 198 // pUnk should not be used. 199 mt->pUnk->Release(); 200 mt->pUnk = NULL; 201 } 202} 203 204// Delete a media type structure that was allocated on the heap. 205// http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx 206void VideoCaptureDeviceWin::ScopedMediaType::DeleteMediaType( 207 AM_MEDIA_TYPE* mt) { 208 if (mt != NULL) { 209 FreeMediaType(mt); 210 CoTaskMemFree(mt); 211 } 212} 213 214VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) 215 : device_name_(device_name), 216 state_(kIdle) { 217 DetachFromThread(); 218} 219 220VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { 221 DCHECK(CalledOnValidThread()); 222 if (media_control_) 223 media_control_->Stop(); 224 225 if (graph_builder_) { 226 if (sink_filter_) { 227 graph_builder_->RemoveFilter(sink_filter_); 228 sink_filter_ = NULL; 229 } 230 231 if (capture_filter_) 232 graph_builder_->RemoveFilter(capture_filter_); 233 234 if (mjpg_filter_) 235 graph_builder_->RemoveFilter(mjpg_filter_); 236 237 if (crossbar_filter_) 238 graph_builder_->RemoveFilter(crossbar_filter_); 239 } 240} 241 242bool VideoCaptureDeviceWin::Init() { 243 DCHECK(CalledOnValidThread()); 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 } 252 if (!capture_filter_) { 253 DLOG(ERROR) << "Failed to create capture filter: " 254 << logging::SystemErrorCodeToString(hr); 255 return false; 256 } 257 258 output_capture_pin_ = 259 GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, GUID_NULL); 260 if (!output_capture_pin_) { 261 DLOG(ERROR) << "Failed to get capture output pin"; 262 return false; 263 } 264 265 // Create the sink filter used for receiving Captured frames. 266 sink_filter_ = new SinkFilter(this); 267 if (sink_filter_ == NULL) { 268 DLOG(ERROR) << "Failed to create send filter"; 269 return false; 270 } 271 272 input_sink_pin_ = sink_filter_->GetPin(0); 273 274 hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL, 275 CLSCTX_INPROC_SERVER); 276 if (FAILED(hr)) { 277 DLOG(ERROR) << "Failed to create graph builder: " 278 << logging::SystemErrorCodeToString(hr); 279 return false; 280 } 281 282 hr = graph_builder_.QueryInterface(media_control_.Receive()); 283 if (FAILED(hr)) { 284 DLOG(ERROR) << "Failed to create media control builder: " 285 << logging::SystemErrorCodeToString(hr); 286 return false; 287 } 288 289 hr = graph_builder_->AddFilter(capture_filter_, NULL); 290 if (FAILED(hr)) { 291 DLOG(ERROR) << "Failed to add the capture device to the graph: " 292 << logging::SystemErrorCodeToString(hr); 293 return false; 294 } 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 302 hr = graph_builder_->AddFilter(sink_filter_, NULL); 303 if (FAILED(hr)) { 304 DLOG(ERROR) << "Failed to add the send filter to the graph: " 305 << logging::SystemErrorCodeToString(hr); 306 return false; 307 } 308 309 return CreateCapabilityMap(); 310} 311 312void VideoCaptureDeviceWin::AllocateAndStart( 313 const VideoCaptureParams& params, 314 scoped_ptr<VideoCaptureDevice::Client> client) { 315 DCHECK(CalledOnValidThread()); 316 if (state_ != kIdle) 317 return; 318 319 client_ = client.Pass(); 320 321 // Get the camera capability that best match the requested resolution. 322 const VideoCaptureCapabilityWin& found_capability = 323 capabilities_.GetBestMatchedFormat( 324 params.requested_format.frame_size.width(), 325 params.requested_format.frame_size.height(), 326 params.requested_format.frame_rate); 327 VideoCaptureFormat format = found_capability.supported_format; 328 329 // Reduce the frame rate if the requested frame rate is lower 330 // than the capability. 331 if (format.frame_rate > params.requested_format.frame_rate) 332 format.frame_rate = params.requested_format.frame_rate; 333 334 ScopedComPtr<IAMStreamConfig> stream_config; 335 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); 336 if (FAILED(hr)) { 337 SetErrorState("Can't get the Capture format settings"); 338 return; 339 } 340 341 int count = 0, size = 0; 342 hr = stream_config->GetNumberOfCapabilities(&count, &size); 343 if (FAILED(hr)) { 344 SetErrorState("Failed to GetNumberOfCapabilities"); 345 return; 346 } 347 348 scoped_ptr<BYTE[]> caps(new BYTE[size]); 349 ScopedMediaType media_type; 350 351 // Get the windows capability from the capture device. 352 // GetStreamCaps can return S_FALSE which we consider an error. Therefore the 353 // FAILED macro can't be used. 354 hr = stream_config->GetStreamCaps( 355 found_capability.stream_index, media_type.Receive(), caps.get()); 356 if (hr != S_OK) { 357 SetErrorState("Failed to get capture device capabilities"); 358 return; 359 } else { 360 if (media_type->formattype == FORMAT_VideoInfo) { 361 VIDEOINFOHEADER* h = 362 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); 363 if (format.frame_rate > 0) 364 h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate; 365 } 366 // Set the sink filter to request this format. 367 sink_filter_->SetRequestedMediaFormat(format); 368 // Order the capture device to use this format. 369 hr = stream_config->SetFormat(media_type.get()); 370 if (FAILED(hr)) { 371 // TODO(grunell): Log the error. http://crbug.com/405016. 372 SetErrorState("Failed to set capture device output format"); 373 return; 374 } 375 } 376 377 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) { 378 // Create MJPG filter if we need it. 379 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); 380 381 if (SUCCEEDED(hr)) { 382 input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, 383 GUID_NULL); 384 output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL, 385 GUID_NULL); 386 hr = graph_builder_->AddFilter(mjpg_filter_, NULL); 387 } 388 389 if (FAILED(hr)) { 390 mjpg_filter_.Release(); 391 input_mjpg_pin_.Release(); 392 output_mjpg_pin_.Release(); 393 } 394 } 395 396 SetAntiFlickerInCaptureFilter(); 397 398 if (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) { 399 // Connect the camera to the MJPEG decoder. 400 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, 401 NULL); 402 // Connect the MJPEG filter to the Capture filter. 403 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, 404 NULL); 405 } else if (media_type->subtype == kMediaSubTypeHDYC) { 406 // HDYC pixel format, used by the DeckLink capture card, needs an AVI 407 // decompressor filter after source, let |graph_builder_| add it. 408 hr = graph_builder_->Connect(output_capture_pin_, input_sink_pin_); 409 } else { 410 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, 411 NULL); 412 } 413 414 if (FAILED(hr)) { 415 SetErrorState("Failed to connect the Capture graph."); 416 return; 417 } 418 419 hr = media_control_->Pause(); 420 if (FAILED(hr)) { 421 SetErrorState("Failed to Pause the Capture device. " 422 "Is it already occupied?"); 423 return; 424 } 425 426 // Get the format back from the sink filter after the filter have been 427 // connected. 428 capture_format_ = sink_filter_->ResultingFormat(); 429 430 // Start capturing. 431 hr = media_control_->Run(); 432 if (FAILED(hr)) { 433 SetErrorState("Failed to start the Capture device."); 434 return; 435 } 436 437 state_ = kCapturing; 438} 439 440void VideoCaptureDeviceWin::StopAndDeAllocate() { 441 DCHECK(CalledOnValidThread()); 442 if (state_ != kCapturing) 443 return; 444 445 HRESULT hr = media_control_->Stop(); 446 if (FAILED(hr)) { 447 SetErrorState("Failed to stop the capture graph."); 448 return; 449 } 450 451 graph_builder_->Disconnect(output_capture_pin_); 452 graph_builder_->Disconnect(input_sink_pin_); 453 454 // If the _mjpg filter exist disconnect it even if it has not been used. 455 if (mjpg_filter_) { 456 graph_builder_->Disconnect(input_mjpg_pin_); 457 graph_builder_->Disconnect(output_mjpg_pin_); 458 } 459 if (crossbar_filter_) { 460 graph_builder_->Disconnect(analog_video_input_pin_); 461 graph_builder_->Disconnect(crossbar_video_output_pin_); 462 } 463 464 if (FAILED(hr)) { 465 SetErrorState("Failed to Stop the Capture device"); 466 return; 467 } 468 client_.reset(); 469 state_ = kIdle; 470} 471 472// Implements SinkFilterObserver::SinkFilterObserver. 473void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, 474 int length) { 475 client_->OnIncomingCapturedData( 476 buffer, length, capture_format_, 0, base::TimeTicks::Now()); 477} 478 479bool VideoCaptureDeviceWin::CreateCapabilityMap() { 480 DCHECK(CalledOnValidThread()); 481 ScopedComPtr<IAMStreamConfig> stream_config; 482 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); 483 if (FAILED(hr)) { 484 DPLOG(ERROR) << "Failed to get IAMStreamConfig interface from " 485 "capture device: " << logging::SystemErrorCodeToString(hr); 486 return false; 487 } 488 489 // Get interface used for getting the frame rate. 490 ScopedComPtr<IAMVideoControl> video_control; 491 hr = capture_filter_.QueryInterface(video_control.Receive()); 492 DLOG_IF(WARNING, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED: " 493 << logging::SystemErrorCodeToString(hr); 494 495 int count = 0, size = 0; 496 hr = stream_config->GetNumberOfCapabilities(&count, &size); 497 if (FAILED(hr)) { 498 DLOG(ERROR) << "Failed to GetNumberOfCapabilities: " 499 << logging::SystemErrorCodeToString(hr); 500 return false; 501 } 502 503 scoped_ptr<BYTE[]> caps(new BYTE[size]); 504 for (int i = 0; i < count; ++i) { 505 ScopedMediaType media_type; 506 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get()); 507 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() 508 // macros here since they'll trigger incorrectly. 509 if (hr != S_OK) { 510 DLOG(ERROR) << "Failed to GetStreamCaps: " 511 << logging::SystemErrorCodeToString(hr); 512 return false; 513 } 514 515 if (media_type->majortype == MEDIATYPE_Video && 516 media_type->formattype == FORMAT_VideoInfo) { 517 VideoCaptureCapabilityWin capability(i); 518 capability.supported_format.pixel_format = 519 TranslateMediaSubtypeToPixelFormat(media_type->subtype); 520 if (capability.supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) 521 continue; 522 523 VIDEOINFOHEADER* h = 524 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); 525 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, 526 h->bmiHeader.biHeight); 527 528 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use 529 // the value from VIDEOINFOHEADER. 530 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; 531 if (video_control) { 532 ScopedCoMem<LONGLONG> max_fps; 533 LONG list_size = 0; 534 SIZE size = {capability.supported_format.frame_size.width(), 535 capability.supported_format.frame_size.height()}; 536 537 // GetFrameRateList doesn't return max frame rate always 538 // eg: Logitech Notebook. This may be due to a bug in that API 539 // because GetFrameRateList array is reversed in the above camera. So 540 // a util method written. Can't assume the first value will return 541 // the max fps. 542 hr = video_control->GetFrameRateList(output_capture_pin_, i, size, 543 &list_size, &max_fps); 544 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some 545 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates 546 // into success, so explicitly check S_OK. See http://crbug.com/306237. 547 if (hr == S_OK && list_size > 0 && max_fps) { 548 time_per_frame = *std::min_element(max_fps.get(), 549 max_fps.get() + list_size); 550 } 551 } 552 553 capability.supported_format.frame_rate = 554 (time_per_frame > 0) 555 ? (kSecondsToReferenceTime / static_cast<float>(time_per_frame)) 556 : 0.0; 557 558 // DirectShow works at the moment only on integer frame_rate but the 559 // best capability matching class works on rational frame rates. 560 capability.frame_rate_numerator = capability.supported_format.frame_rate; 561 capability.frame_rate_denominator = 1; 562 563 capabilities_.Add(capability); 564 } 565 } 566 567 return !capabilities_.empty(); 568} 569 570// Set the power line frequency removal in |capture_filter_| if available. 571void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { 572 const int power_line_frequency = GetPowerLineFrequencyForLocation(); 573 if (power_line_frequency != kPowerLine50Hz && 574 power_line_frequency != kPowerLine60Hz) { 575 return; 576 } 577 ScopedComPtr<IKsPropertySet> ks_propset; 578 DWORD type_support = 0; 579 HRESULT hr; 580 if (SUCCEEDED(hr = ks_propset.QueryFrom(capture_filter_)) && 581 SUCCEEDED(hr = ks_propset->QuerySupported(PROPSETID_VIDCAP_VIDEOPROCAMP, 582 KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, &type_support)) && 583 (type_support & KSPROPERTY_SUPPORT_SET)) { 584 KSPROPERTY_VIDEOPROCAMP_S data = {}; 585 data.Property.Set = PROPSETID_VIDCAP_VIDEOPROCAMP; 586 data.Property.Id = KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY; 587 data.Property.Flags = KSPROPERTY_TYPE_SET; 588 data.Value = (power_line_frequency == kPowerLine50Hz) ? 1 : 2; 589 data.Flags = KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL; 590 hr = ks_propset->Set(PROPSETID_VIDCAP_VIDEOPROCAMP, 591 KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, 592 &data, sizeof(data), &data, sizeof(data)); 593 DLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed: " 594 << logging::SystemErrorCodeToString(hr); 595 DVLOG_IF(2, SUCCEEDED(hr)) << "Anti-flicker set correctly."; 596 } else { 597 DVLOG(2) << "Anti-flicker setting not supported."; 598 } 599} 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. 606HRESULT 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. 643HRESULT 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 657void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { 658 DCHECK(CalledOnValidThread()); 659 state_ = kError; 660 client_->OnError(reason); 661} 662} // namespace media 663