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