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