Home | History | Annotate | Download | only in win
      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 
     18 using base::win::ScopedCoMem;
     19 using base::win::ScopedComPtr;
     20 using base::win::ScopedVariant;
     21 
     22 namespace media {
     23 
     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.
     54 // static
     55 HRESULT 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
    119 ScopedComPtr<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
    148 VideoPixelFormat 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 
    175 void VideoCaptureDeviceWin::ScopedMediaType::Free() {
    176   if (!media_type_)
    177     return;
    178 
    179   DeleteMediaType(media_type_);
    180   media_type_= NULL;
    181 }
    182 
    183 AM_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
    190 void 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
    206 void VideoCaptureDeviceWin::ScopedMediaType::DeleteMediaType(
    207     AM_MEDIA_TYPE* mt) {
    208   if (mt != NULL) {
    209     FreeMediaType(mt);
    210     CoTaskMemFree(mt);
    211   }
    212 }
    213 
    214 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
    215     : device_name_(device_name),
    216       state_(kIdle) {
    217   DetachFromThread();
    218 }
    219 
    220 VideoCaptureDeviceWin::~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 
    242 bool 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 
    312 void 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 
    440 void 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.
    473 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
    474                                           int length) {
    475   client_->OnIncomingCapturedData(
    476       buffer, length, capture_format_, 0, base::TimeTicks::Now());
    477 }
    478 
    479 bool 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.
    571 void 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.
    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 
    657 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
    658   DCHECK(CalledOnValidThread());
    659   state_ = kError;
    660   client_->OnError(reason);
    661 }
    662 }  // namespace media
    663