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 <algorithm>
      8 #include <list>
      9 
     10 #include "base/command_line.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/sys_string_conversions.h"
     13 #include "base/win/scoped_variant.h"
     14 #include "media/base/media_switches.h"
     15 #include "media/video/capture/win/video_capture_device_mf_win.h"
     16 
     17 using base::win::ScopedComPtr;
     18 using base::win::ScopedVariant;
     19 
     20 namespace media {
     21 namespace {
     22 
     23 // Finds and creates a DirectShow Video Capture filter matching the device_name.
     24 HRESULT GetDeviceFilter(const VideoCaptureDevice::Name& device_name,
     25                         IBaseFilter** filter) {
     26   DCHECK(filter);
     27 
     28   ScopedComPtr<ICreateDevEnum> dev_enum;
     29   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
     30                                        CLSCTX_INPROC);
     31   if (FAILED(hr))
     32     return hr;
     33 
     34   ScopedComPtr<IEnumMoniker> enum_moniker;
     35   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
     36                                        enum_moniker.Receive(), 0);
     37   // CreateClassEnumerator returns S_FALSE on some Windows OS
     38   // when no camera exist. Therefore the FAILED macro can't be used.
     39   if (hr != S_OK)
     40     return NULL;
     41 
     42   ScopedComPtr<IMoniker> moniker;
     43   ScopedComPtr<IBaseFilter> capture_filter;
     44   DWORD fetched = 0;
     45   while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) {
     46     ScopedComPtr<IPropertyBag> prop_bag;
     47     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
     48     if (FAILED(hr)) {
     49       moniker.Release();
     50       continue;
     51     }
     52 
     53     // Find the description or friendly name.
     54     static const wchar_t* kPropertyNames[] = {
     55       L"DevicePath", L"Description", L"FriendlyName"
     56     };
     57     ScopedVariant name;
     58     for (size_t i = 0;
     59          i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) {
     60       prop_bag->Read(kPropertyNames[i], name.Receive(), 0);
     61     }
     62     if (name.type() == VT_BSTR) {
     63       std::string device_path(base::SysWideToUTF8(V_BSTR(&name)));
     64       if (device_path.compare(device_name.id()) == 0) {
     65         // We have found the requested device
     66         hr = moniker->BindToObject(0, 0, IID_IBaseFilter,
     67                                    capture_filter.ReceiveVoid());
     68         DVPLOG_IF(2, FAILED(hr)) << "Failed to bind camera filter.";
     69         break;
     70       }
     71     }
     72     moniker.Release();
     73   }
     74 
     75   *filter = capture_filter.Detach();
     76   if (!*filter && SUCCEEDED(hr))
     77     hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
     78 
     79   return hr;
     80 }
     81 
     82 // Check if a Pin matches a category.
     83 bool PinMatchesCategory(IPin* pin, REFGUID category) {
     84   DCHECK(pin);
     85   bool found = false;
     86   ScopedComPtr<IKsPropertySet> ks_property;
     87   HRESULT hr = ks_property.QueryFrom(pin);
     88   if (SUCCEEDED(hr)) {
     89     GUID pin_category;
     90     DWORD return_value;
     91     hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
     92                           &pin_category, sizeof(pin_category), &return_value);
     93     if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
     94       found = (pin_category == category);
     95     }
     96   }
     97   return found;
     98 }
     99 
    100 // Finds a IPin on a IBaseFilter given the direction an category.
    101 HRESULT GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir, REFGUID category,
    102                IPin** pin) {
    103   DCHECK(pin);
    104   ScopedComPtr<IEnumPins> pin_emum;
    105   HRESULT hr = filter->EnumPins(pin_emum.Receive());
    106   if (pin_emum == NULL)
    107     return hr;
    108 
    109   // Get first unconnected pin.
    110   hr = pin_emum->Reset();  // set to first pin
    111   while ((hr = pin_emum->Next(1, pin, NULL)) == S_OK) {
    112     PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
    113     hr = (*pin)->QueryDirection(&this_pin_dir);
    114     if (pin_dir == this_pin_dir) {
    115       if (category == GUID_NULL || PinMatchesCategory(*pin, category))
    116         return S_OK;
    117     }
    118     (*pin)->Release();
    119   }
    120 
    121   return E_FAIL;
    122 }
    123 
    124 // Release the format block for a media type.
    125 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
    126 void FreeMediaType(AM_MEDIA_TYPE* mt) {
    127   if (mt->cbFormat != 0) {
    128     CoTaskMemFree(mt->pbFormat);
    129     mt->cbFormat = 0;
    130     mt->pbFormat = NULL;
    131   }
    132   if (mt->pUnk != NULL) {
    133     NOTREACHED();
    134     // pUnk should not be used.
    135     mt->pUnk->Release();
    136     mt->pUnk = NULL;
    137   }
    138 }
    139 
    140 // Delete a media type structure that was allocated on the heap.
    141 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
    142 void DeleteMediaType(AM_MEDIA_TYPE* mt) {
    143   if (mt != NULL) {
    144     FreeMediaType(mt);
    145     CoTaskMemFree(mt);
    146   }
    147 }
    148 
    149 }  // namespace
    150 
    151 // static
    152 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
    153   Names::iterator it;
    154 
    155   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
    156   if (VideoCaptureDeviceMFWin::PlatformSupported() &&
    157       !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) {
    158     VideoCaptureDeviceMFWin::GetDeviceNames(device_names);
    159   }
    160   // Retrieve the devices with DirectShow (DS) interface. They might (partially)
    161   // overlap with the MediaFoundation (MF), so the list has to be consolidated.
    162   Names temp_names;
    163   VideoCaptureDeviceWin::GetDeviceNames(&temp_names);
    164 
    165   // Merge the DS devices into the MF device list, and next remove
    166   // the duplicates, giving priority to the MF "versions".
    167   device_names->merge(temp_names);
    168   device_names->unique();
    169 }
    170 
    171 // static
    172 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
    173   VideoCaptureDevice* ret = NULL;
    174   if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
    175     DCHECK(VideoCaptureDeviceMFWin::PlatformSupported());
    176     scoped_ptr<VideoCaptureDeviceMFWin> device(
    177         new VideoCaptureDeviceMFWin(device_name));
    178     DVLOG(1) << " MediaFoundation Device: " << device_name.name();
    179     if (device->Init())
    180       ret = device.release();
    181   } else if (device_name.capture_api_type() == Name::DIRECT_SHOW) {
    182     scoped_ptr<VideoCaptureDeviceWin> device(
    183         new VideoCaptureDeviceWin(device_name));
    184     DVLOG(1) << " DirectShow Device: " << device_name.name();
    185     if (device->Init())
    186       ret = device.release();
    187   } else{
    188     NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
    189   }
    190 
    191   return ret;
    192 }
    193 
    194 // static
    195 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
    196   DCHECK(device_names);
    197 
    198   ScopedComPtr<ICreateDevEnum> dev_enum;
    199   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
    200                                        CLSCTX_INPROC);
    201   if (FAILED(hr))
    202     return;
    203 
    204   ScopedComPtr<IEnumMoniker> enum_moniker;
    205   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
    206                                        enum_moniker.Receive(), 0);
    207   // CreateClassEnumerator returns S_FALSE on some Windows OS
    208   // when no camera exist. Therefore the FAILED macro can't be used.
    209   if (hr != S_OK)
    210     return;
    211 
    212   device_names->clear();
    213 
    214   // Name of a fake DirectShow filter that exist on computers with
    215   // GTalk installed.
    216   static const char kGoogleCameraAdapter[] = "google camera adapter";
    217 
    218   // Enumerate all video capture devices.
    219   ScopedComPtr<IMoniker> moniker;
    220   int index = 0;
    221   while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
    222     ScopedComPtr<IPropertyBag> prop_bag;
    223     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
    224     if (FAILED(hr)) {
    225       moniker.Release();
    226       continue;
    227     }
    228 
    229     // Find the description or friendly name.
    230     ScopedVariant name;
    231     hr = prop_bag->Read(L"Description", name.Receive(), 0);
    232     if (FAILED(hr))
    233       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
    234 
    235     if (SUCCEEDED(hr) && name.type() == VT_BSTR) {
    236       // Ignore all VFW drivers and the special Google Camera Adapter.
    237       // Google Camera Adapter is not a real DirectShow camera device.
    238       // VFW is very old Video for Windows drivers that can not be used.
    239       const wchar_t* str_ptr = V_BSTR(&name);
    240       const int name_length = arraysize(kGoogleCameraAdapter) - 1;
    241 
    242       if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
    243           lstrlenW(str_ptr) < name_length ||
    244           (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
    245                                   kGoogleCameraAdapter)))) {
    246         std::string id;
    247         std::string device_name(base::SysWideToUTF8(str_ptr));
    248         name.Reset();
    249         hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
    250         if (FAILED(hr) || name.type() != VT_BSTR) {
    251           id = device_name;
    252         } else {
    253           DCHECK_EQ(name.type(), VT_BSTR);
    254           id = base::SysWideToUTF8(V_BSTR(&name));
    255         }
    256 
    257         device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW));
    258       }
    259     }
    260     moniker.Release();
    261   }
    262 }
    263 
    264 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
    265     : device_name_(device_name),
    266       state_(kIdle),
    267       observer_(NULL) {
    268   DetachFromThread();
    269 }
    270 
    271 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
    272   DCHECK(CalledOnValidThread());
    273   if (media_control_)
    274     media_control_->Stop();
    275 
    276   if (graph_builder_) {
    277     if (sink_filter_) {
    278       graph_builder_->RemoveFilter(sink_filter_);
    279       sink_filter_ = NULL;
    280     }
    281 
    282     if (capture_filter_)
    283       graph_builder_->RemoveFilter(capture_filter_);
    284 
    285     if (mjpg_filter_)
    286       graph_builder_->RemoveFilter(mjpg_filter_);
    287   }
    288 }
    289 
    290 bool VideoCaptureDeviceWin::Init() {
    291   DCHECK(CalledOnValidThread());
    292   HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive());
    293   if (!capture_filter_) {
    294     DVLOG(2) << "Failed to create capture filter.";
    295     return false;
    296   }
    297 
    298   hr = GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE,
    299               output_capture_pin_.Receive());
    300   if (!output_capture_pin_) {
    301     DVLOG(2) << "Failed to get capture output pin";
    302     return false;
    303   }
    304 
    305   // Create the sink filter used for receiving Captured frames.
    306   sink_filter_ = new SinkFilter(this);
    307   if (sink_filter_ == NULL) {
    308     DVLOG(2) << "Failed to create send filter";
    309     return false;
    310   }
    311 
    312   input_sink_pin_ = sink_filter_->GetPin(0);
    313 
    314   hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL,
    315                                      CLSCTX_INPROC_SERVER);
    316   if (FAILED(hr)) {
    317     DVLOG(2) << "Failed to create graph builder.";
    318     return false;
    319   }
    320 
    321   hr = graph_builder_.QueryInterface(media_control_.Receive());
    322   if (FAILED(hr)) {
    323     DVLOG(2) << "Failed to create media control builder.";
    324     return false;
    325   }
    326 
    327   hr = graph_builder_->AddFilter(capture_filter_, NULL);
    328   if (FAILED(hr)) {
    329     DVLOG(2) << "Failed to add the capture device to the graph.";
    330     return false;
    331   }
    332 
    333   hr = graph_builder_->AddFilter(sink_filter_, NULL);
    334   if (FAILED(hr)) {
    335     DVLOG(2)<< "Failed to add the send filter to the graph.";
    336     return false;
    337   }
    338 
    339   return CreateCapabilityMap();
    340 }
    341 
    342 void VideoCaptureDeviceWin::Allocate(
    343     const VideoCaptureCapability& capture_format,
    344     VideoCaptureDevice::EventHandler* observer) {
    345   DCHECK(CalledOnValidThread());
    346   if (state_ != kIdle)
    347     return;
    348 
    349   observer_ = observer;
    350 
    351   // Get the camera capability that best match the requested resolution.
    352   const VideoCaptureCapabilityWin& found_capability =
    353       capabilities_.GetBestMatchedCapability(capture_format.width,
    354                                              capture_format.height,
    355                                              capture_format.frame_rate);
    356   VideoCaptureCapability capability = found_capability;
    357 
    358   // Reduce the frame rate if the requested frame rate is lower
    359   // than the capability.
    360   if (capability.frame_rate > capture_format.frame_rate)
    361     capability.frame_rate = capture_format.frame_rate;
    362 
    363   AM_MEDIA_TYPE* pmt = NULL;
    364   VIDEO_STREAM_CONFIG_CAPS caps;
    365 
    366   ScopedComPtr<IAMStreamConfig> stream_config;
    367   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
    368   if (FAILED(hr)) {
    369     SetErrorState("Can't get the Capture format settings");
    370     return;
    371   }
    372 
    373   // Get the windows capability from the capture device.
    374   hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt,
    375                                     reinterpret_cast<BYTE*>(&caps));
    376   if (SUCCEEDED(hr)) {
    377     if (pmt->formattype == FORMAT_VideoInfo) {
    378       VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
    379       if (capability.frame_rate > 0)
    380         h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate;
    381     }
    382     // Set the sink filter to request this capability.
    383     sink_filter_->SetRequestedMediaCapability(capability);
    384     // Order the capture device to use this capability.
    385     hr = stream_config->SetFormat(pmt);
    386   }
    387 
    388   if (FAILED(hr))
    389     SetErrorState("Failed to set capture device output format");
    390 
    391   if (capability.color == VideoCaptureCapability::kMJPEG &&
    392       !mjpg_filter_.get()) {
    393     // Create MJPG filter if we need it.
    394     hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
    395 
    396     if (SUCCEEDED(hr)) {
    397       GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive());
    398       GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL,
    399              output_mjpg_pin_.Receive());
    400       hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
    401     }
    402 
    403     if (FAILED(hr)) {
    404       mjpg_filter_.Release();
    405       input_mjpg_pin_.Release();
    406       output_mjpg_pin_.Release();
    407     }
    408   }
    409 
    410   if (capability.color == VideoCaptureCapability::kMJPEG &&
    411       mjpg_filter_.get()) {
    412     // Connect the camera to the MJPEG decoder.
    413     hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_,
    414                                        NULL);
    415     // Connect the MJPEG filter to the Capture filter.
    416     hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_,
    417                                         NULL);
    418   } else {
    419     hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_,
    420                                        NULL);
    421   }
    422 
    423   if (FAILED(hr)) {
    424     SetErrorState("Failed to connect the Capture graph.");
    425     return;
    426   }
    427 
    428   hr = media_control_->Pause();
    429   if (FAILED(hr)) {
    430     SetErrorState("Failed to Pause the Capture device. "
    431                   "Is it already occupied?");
    432     return;
    433   }
    434 
    435   // Get the capability back from the sink filter after the filter have been
    436   // connected.
    437   const VideoCaptureCapability& used_capability
    438       = sink_filter_->ResultingCapability();
    439   observer_->OnFrameInfo(used_capability);
    440 
    441   state_ = kAllocated;
    442 }
    443 
    444 void VideoCaptureDeviceWin::Start() {
    445   DCHECK(CalledOnValidThread());
    446   if (state_ != kAllocated)
    447     return;
    448 
    449   HRESULT hr = media_control_->Run();
    450   if (FAILED(hr)) {
    451     SetErrorState("Failed to start the Capture device.");
    452     return;
    453   }
    454 
    455   state_ = kCapturing;
    456 }
    457 
    458 void VideoCaptureDeviceWin::Stop() {
    459   DCHECK(CalledOnValidThread());
    460   if (state_ != kCapturing)
    461     return;
    462 
    463   HRESULT hr = media_control_->Stop();
    464   if (FAILED(hr)) {
    465     SetErrorState("Failed to stop the capture graph.");
    466     return;
    467   }
    468 
    469   state_ = kAllocated;
    470 }
    471 
    472 void VideoCaptureDeviceWin::DeAllocate() {
    473   DCHECK(CalledOnValidThread());
    474   if (state_ == kIdle)
    475     return;
    476 
    477   HRESULT hr = media_control_->Stop();
    478   graph_builder_->Disconnect(output_capture_pin_);
    479   graph_builder_->Disconnect(input_sink_pin_);
    480 
    481   // If the _mjpg filter exist disconnect it even if it has not been used.
    482   if (mjpg_filter_) {
    483     graph_builder_->Disconnect(input_mjpg_pin_);
    484     graph_builder_->Disconnect(output_mjpg_pin_);
    485   }
    486 
    487   if (FAILED(hr)) {
    488     SetErrorState("Failed to Stop the Capture device");
    489     return;
    490   }
    491 
    492   state_ = kIdle;
    493 }
    494 
    495 const VideoCaptureDevice::Name& VideoCaptureDeviceWin::device_name() {
    496   DCHECK(CalledOnValidThread());
    497   return device_name_;
    498 }
    499 
    500 // Implements SinkFilterObserver::SinkFilterObserver.
    501 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
    502                                           int length) {
    503   observer_->OnIncomingCapturedFrame(buffer, length, base::Time::Now(),
    504                                      0, false, false);
    505 }
    506 
    507 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
    508   DCHECK(CalledOnValidThread());
    509   ScopedComPtr<IAMStreamConfig> stream_config;
    510   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
    511   if (FAILED(hr)) {
    512     DVLOG(2) << "Failed to get IAMStreamConfig interface from "
    513                 "capture device";
    514     return false;
    515   }
    516 
    517   // Get interface used for getting the frame rate.
    518   ScopedComPtr<IAMVideoControl> video_control;
    519   hr = capture_filter_.QueryInterface(video_control.Receive());
    520   DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED";
    521 
    522   AM_MEDIA_TYPE* media_type = NULL;
    523   VIDEO_STREAM_CONFIG_CAPS caps;
    524   int count, size;
    525 
    526   hr = stream_config->GetNumberOfCapabilities(&count, &size);
    527   if (FAILED(hr)) {
    528     DVLOG(2) << "Failed to GetNumberOfCapabilities";
    529     return false;
    530   }
    531 
    532   for (int i = 0; i < count; ++i) {
    533     hr = stream_config->GetStreamCaps(i, &media_type,
    534                                       reinterpret_cast<BYTE*>(&caps));
    535     if (FAILED(hr)) {
    536       DVLOG(2) << "Failed to GetStreamCaps";
    537       return false;
    538     }
    539 
    540     if (media_type->majortype == MEDIATYPE_Video &&
    541         media_type->formattype == FORMAT_VideoInfo) {
    542       VideoCaptureCapabilityWin capability(i);
    543       REFERENCE_TIME time_per_frame = 0;
    544 
    545       VIDEOINFOHEADER* h =
    546           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
    547       capability.width = h->bmiHeader.biWidth;
    548       capability.height = h->bmiHeader.biHeight;
    549       time_per_frame = h->AvgTimePerFrame;
    550 
    551       // Try to get the max frame rate from IAMVideoControl.
    552       if (video_control) {
    553         LONGLONG* max_fps_ptr;
    554         LONG list_size;
    555         SIZE size;
    556         size.cx = capability.width;
    557         size.cy = capability.height;
    558 
    559         // GetFrameRateList doesn't return max frame rate always
    560         // eg: Logitech Notebook. This may be due to a bug in that API
    561         // because GetFrameRateList array is reversed in the above camera. So
    562         // a util method written. Can't assume the first value will return
    563         // the max fps.
    564         hr = video_control->GetFrameRateList(output_capture_pin_, i, size,
    565                                              &list_size, &max_fps_ptr);
    566 
    567         if (SUCCEEDED(hr) && list_size > 0) {
    568           int min_time =  *std::min_element(max_fps_ptr,
    569                                             max_fps_ptr + list_size);
    570           capability.frame_rate = (min_time > 0) ?
    571               kSecondsToReferenceTime / min_time : 0;
    572         } else {
    573           // Get frame rate from VIDEOINFOHEADER.
    574           capability.frame_rate = (time_per_frame > 0) ?
    575               static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0;
    576         }
    577       } else {
    578         // Get frame rate from VIDEOINFOHEADER since IAMVideoControl is
    579         // not supported.
    580         capability.frame_rate = (time_per_frame > 0) ?
    581             static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0;
    582       }
    583       // DirectShow works at the moment only on integer frame_rate but the
    584       // best capability matching class works on rational frame rates.
    585       capability.frame_rate_numerator = capability.frame_rate;
    586       capability.frame_rate_denominator = 1;
    587 
    588       // We can't switch MEDIATYPE :~(.
    589       if (media_type->subtype == kMediaSubTypeI420) {
    590         capability.color = VideoCaptureCapability::kI420;
    591       } else if (media_type->subtype == MEDIASUBTYPE_IYUV) {
    592         // This is identical to kI420.
    593         capability.color = VideoCaptureCapability::kI420;
    594       } else if (media_type->subtype == MEDIASUBTYPE_RGB24) {
    595         capability.color = VideoCaptureCapability::kRGB24;
    596       } else if (media_type->subtype == MEDIASUBTYPE_YUY2) {
    597         capability.color = VideoCaptureCapability::kYUY2;
    598       } else if (media_type->subtype == MEDIASUBTYPE_MJPG) {
    599         capability.color = VideoCaptureCapability::kMJPEG;
    600       } else if (media_type->subtype == MEDIASUBTYPE_UYVY) {
    601         capability.color = VideoCaptureCapability::kUYVY;
    602       } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) {
    603         capability.color = VideoCaptureCapability::kARGB;
    604       } else {
    605         WCHAR guid_str[128];
    606         StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str));
    607         DVLOG(2) << "Device supports (also) an unknown media type " << guid_str;
    608         continue;
    609       }
    610       capabilities_.Add(capability);
    611     }
    612     DeleteMediaType(media_type);
    613     media_type = NULL;
    614   }
    615 
    616   return !capabilities_.empty();
    617 }
    618 
    619 void VideoCaptureDeviceWin::SetErrorState(const char* reason) {
    620   DCHECK(CalledOnValidThread());
    621   DVLOG(1) << reason;
    622   state_ = kError;
    623   observer_->OnError();
    624 }
    625 }  // namespace media
    626