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/metro.h"
     14 #include "base/win/scoped_co_mem.h"
     15 #include "base/win/scoped_variant.h"
     16 #include "media/base/media_switches.h"
     17 #include "media/video/capture/win/video_capture_device_mf_win.h"
     18 
     19 using base::win::ScopedCoMem;
     20 using base::win::ScopedComPtr;
     21 using base::win::ScopedVariant;
     22 
     23 namespace media {
     24 namespace {
     25 
     26 // Finds and creates a DirectShow Video Capture filter matching the device_name.
     27 HRESULT GetDeviceFilter(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 bool PinMatchesCategory(IPin* pin, REFGUID category) {
     87   DCHECK(pin);
     88   bool found = false;
     89   ScopedComPtr<IKsPropertySet> ks_property;
     90   HRESULT hr = ks_property.QueryFrom(pin);
     91   if (SUCCEEDED(hr)) {
     92     GUID pin_category;
     93     DWORD return_value;
     94     hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
     95                           &pin_category, sizeof(pin_category), &return_value);
     96     if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
     97       found = (pin_category == category);
     98     }
     99   }
    100   return found;
    101 }
    102 
    103 // Finds a IPin on a IBaseFilter given the direction an category.
    104 HRESULT GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir, REFGUID category,
    105                IPin** pin) {
    106   DCHECK(pin);
    107   ScopedComPtr<IEnumPins> pin_emum;
    108   HRESULT hr = filter->EnumPins(pin_emum.Receive());
    109   if (pin_emum == NULL)
    110     return hr;
    111 
    112   // Get first unconnected pin.
    113   hr = pin_emum->Reset();  // set to first pin
    114   while ((hr = pin_emum->Next(1, pin, NULL)) == S_OK) {
    115     PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
    116     hr = (*pin)->QueryDirection(&this_pin_dir);
    117     if (pin_dir == this_pin_dir) {
    118       if (category == GUID_NULL || PinMatchesCategory(*pin, category))
    119         return S_OK;
    120     }
    121     (*pin)->Release();
    122   }
    123 
    124   return E_FAIL;
    125 }
    126 
    127 // Release the format block for a media type.
    128 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
    129 void FreeMediaType(AM_MEDIA_TYPE* mt) {
    130   if (mt->cbFormat != 0) {
    131     CoTaskMemFree(mt->pbFormat);
    132     mt->cbFormat = 0;
    133     mt->pbFormat = NULL;
    134   }
    135   if (mt->pUnk != NULL) {
    136     NOTREACHED();
    137     // pUnk should not be used.
    138     mt->pUnk->Release();
    139     mt->pUnk = NULL;
    140   }
    141 }
    142 
    143 // Delete a media type structure that was allocated on the heap.
    144 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
    145 void DeleteMediaType(AM_MEDIA_TYPE* mt) {
    146   if (mt != NULL) {
    147     FreeMediaType(mt);
    148     CoTaskMemFree(mt);
    149   }
    150 }
    151 
    152 }  // namespace
    153 
    154 // static
    155 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
    156   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
    157   // Use Media Foundation for Metro processes (after and including Win8)
    158   // and DirectShow for any other platforms.
    159   if (base::win::IsMetroProcess() &&
    160       !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) {
    161     VideoCaptureDeviceMFWin::GetDeviceNames(device_names);
    162   } else {
    163     VideoCaptureDeviceWin::GetDeviceNames(device_names);
    164   }
    165 }
    166 
    167 // static
    168 void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
    169     VideoCaptureFormats* formats) {
    170   NOTIMPLEMENTED();
    171 }
    172 
    173 // static
    174 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
    175   VideoCaptureDevice* ret = NULL;
    176   if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
    177     DCHECK(VideoCaptureDeviceMFWin::PlatformSupported());
    178     scoped_ptr<VideoCaptureDeviceMFWin> device(
    179         new VideoCaptureDeviceMFWin(device_name));
    180     DVLOG(1) << " MediaFoundation Device: " << device_name.name();
    181     if (device->Init())
    182       ret = device.release();
    183   } else if (device_name.capture_api_type() == Name::DIRECT_SHOW) {
    184     scoped_ptr<VideoCaptureDeviceWin> device(
    185         new VideoCaptureDeviceWin(device_name));
    186     DVLOG(1) << " DirectShow Device: " << device_name.name();
    187     if (device->Init())
    188       ret = device.release();
    189   } else{
    190     NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
    191   }
    192 
    193   return ret;
    194 }
    195 
    196 // static
    197 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
    198   DCHECK(device_names);
    199 
    200   ScopedComPtr<ICreateDevEnum> dev_enum;
    201   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
    202                                        CLSCTX_INPROC);
    203   if (FAILED(hr))
    204     return;
    205 
    206   ScopedComPtr<IEnumMoniker> enum_moniker;
    207   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
    208                                        enum_moniker.Receive(), 0);
    209   // CreateClassEnumerator returns S_FALSE on some Windows OS
    210   // when no camera exist. Therefore the FAILED macro can't be used.
    211   if (hr != S_OK)
    212     return;
    213 
    214   device_names->clear();
    215 
    216   // Name of a fake DirectShow filter that exist on computers with
    217   // GTalk installed.
    218   static const char kGoogleCameraAdapter[] = "google camera adapter";
    219 
    220   // Enumerate all video capture devices.
    221   ScopedComPtr<IMoniker> moniker;
    222   int index = 0;
    223   while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
    224     ScopedComPtr<IPropertyBag> prop_bag;
    225     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
    226     if (FAILED(hr)) {
    227       moniker.Release();
    228       continue;
    229     }
    230 
    231     // Find the description or friendly name.
    232     ScopedVariant name;
    233     hr = prop_bag->Read(L"Description", name.Receive(), 0);
    234     if (FAILED(hr))
    235       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
    236 
    237     if (SUCCEEDED(hr) && name.type() == VT_BSTR) {
    238       // Ignore all VFW drivers and the special Google Camera Adapter.
    239       // Google Camera Adapter is not a real DirectShow camera device.
    240       // VFW is very old Video for Windows drivers that can not be used.
    241       const wchar_t* str_ptr = V_BSTR(&name);
    242       const int name_length = arraysize(kGoogleCameraAdapter) - 1;
    243 
    244       if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
    245           lstrlenW(str_ptr) < name_length ||
    246           (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
    247                                   kGoogleCameraAdapter)))) {
    248         std::string id;
    249         std::string device_name(base::SysWideToUTF8(str_ptr));
    250         name.Reset();
    251         hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
    252         if (FAILED(hr) || name.type() != VT_BSTR) {
    253           id = device_name;
    254         } else {
    255           DCHECK_EQ(name.type(), VT_BSTR);
    256           id = base::SysWideToUTF8(V_BSTR(&name));
    257         }
    258 
    259         device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW));
    260       }
    261     }
    262     moniker.Release();
    263   }
    264 }
    265 
    266 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
    267     : device_name_(device_name),
    268       state_(kIdle) {
    269   DetachFromThread();
    270 }
    271 
    272 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
    273   DCHECK(CalledOnValidThread());
    274   if (media_control_)
    275     media_control_->Stop();
    276 
    277   if (graph_builder_) {
    278     if (sink_filter_) {
    279       graph_builder_->RemoveFilter(sink_filter_);
    280       sink_filter_ = NULL;
    281     }
    282 
    283     if (capture_filter_)
    284       graph_builder_->RemoveFilter(capture_filter_);
    285 
    286     if (mjpg_filter_)
    287       graph_builder_->RemoveFilter(mjpg_filter_);
    288   }
    289 }
    290 
    291 bool VideoCaptureDeviceWin::Init() {
    292   DCHECK(CalledOnValidThread());
    293   HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive());
    294   if (!capture_filter_) {
    295     DVLOG(2) << "Failed to create capture filter.";
    296     return false;
    297   }
    298 
    299   hr = GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE,
    300               output_capture_pin_.Receive());
    301   if (!output_capture_pin_) {
    302     DVLOG(2) << "Failed to get capture output pin";
    303     return false;
    304   }
    305 
    306   // Create the sink filter used for receiving Captured frames.
    307   sink_filter_ = new SinkFilter(this);
    308   if (sink_filter_ == NULL) {
    309     DVLOG(2) << "Failed to create send filter";
    310     return false;
    311   }
    312 
    313   input_sink_pin_ = sink_filter_->GetPin(0);
    314 
    315   hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL,
    316                                      CLSCTX_INPROC_SERVER);
    317   if (FAILED(hr)) {
    318     DVLOG(2) << "Failed to create graph builder.";
    319     return false;
    320   }
    321 
    322   hr = graph_builder_.QueryInterface(media_control_.Receive());
    323   if (FAILED(hr)) {
    324     DVLOG(2) << "Failed to create media control builder.";
    325     return false;
    326   }
    327 
    328   hr = graph_builder_->AddFilter(capture_filter_, NULL);
    329   if (FAILED(hr)) {
    330     DVLOG(2) << "Failed to add the capture device to the graph.";
    331     return false;
    332   }
    333 
    334   hr = graph_builder_->AddFilter(sink_filter_, NULL);
    335   if (FAILED(hr)) {
    336     DVLOG(2)<< "Failed to add the send filter to the graph.";
    337     return false;
    338   }
    339 
    340   return CreateCapabilityMap();
    341 }
    342 
    343 void VideoCaptureDeviceWin::AllocateAndStart(
    344     const VideoCaptureParams& params,
    345     scoped_ptr<VideoCaptureDevice::Client> client) {
    346   DCHECK(CalledOnValidThread());
    347   if (state_ != kIdle)
    348     return;
    349 
    350   client_ = client.Pass();
    351 
    352   // Get the camera capability that best match the requested resolution.
    353   const VideoCaptureCapabilityWin& found_capability =
    354       capabilities_.GetBestMatchedFormat(
    355           params.requested_format.frame_size.width(),
    356           params.requested_format.frame_size.height(),
    357           params.requested_format.frame_rate);
    358   VideoCaptureFormat format = found_capability.supported_format;
    359 
    360   // Reduce the frame rate if the requested frame rate is lower
    361   // than the capability.
    362   if (format.frame_rate > params.requested_format.frame_rate)
    363     format.frame_rate = params.requested_format.frame_rate;
    364 
    365   AM_MEDIA_TYPE* pmt = NULL;
    366   VIDEO_STREAM_CONFIG_CAPS caps;
    367 
    368   ScopedComPtr<IAMStreamConfig> stream_config;
    369   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
    370   if (FAILED(hr)) {
    371     SetErrorState("Can't get the Capture format settings");
    372     return;
    373   }
    374 
    375   // Get the windows capability from the capture device.
    376   hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt,
    377                                     reinterpret_cast<BYTE*>(&caps));
    378   if (SUCCEEDED(hr)) {
    379     if (pmt->formattype == FORMAT_VideoInfo) {
    380       VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
    381       if (format.frame_rate > 0)
    382         h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate;
    383     }
    384     // Set the sink filter to request this format.
    385     sink_filter_->SetRequestedMediaFormat(format);
    386     // Order the capture device to use this format.
    387     hr = stream_config->SetFormat(pmt);
    388   }
    389 
    390   if (FAILED(hr))
    391     SetErrorState("Failed to set capture device output format");
    392 
    393   if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) {
    394     // Create MJPG filter if we need it.
    395     hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
    396 
    397     if (SUCCEEDED(hr)) {
    398       GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive());
    399       GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL,
    400              output_mjpg_pin_.Receive());
    401       hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
    402     }
    403 
    404     if (FAILED(hr)) {
    405       mjpg_filter_.Release();
    406       input_mjpg_pin_.Release();
    407       output_mjpg_pin_.Release();
    408     }
    409   }
    410 
    411   if (format.pixel_format == PIXEL_FORMAT_MJPEG && 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 format back from the sink filter after the filter have been
    436   // connected.
    437   capture_format_ = sink_filter_->ResultingFormat();
    438 
    439   // Start capturing.
    440   hr = media_control_->Run();
    441   if (FAILED(hr)) {
    442     SetErrorState("Failed to start the Capture device.");
    443     return;
    444   }
    445 
    446   state_ = kCapturing;
    447 }
    448 
    449 void VideoCaptureDeviceWin::StopAndDeAllocate() {
    450   DCHECK(CalledOnValidThread());
    451   if (state_ != kCapturing)
    452     return;
    453 
    454   HRESULT hr = media_control_->Stop();
    455   if (FAILED(hr)) {
    456     SetErrorState("Failed to stop the capture graph.");
    457     return;
    458   }
    459 
    460   graph_builder_->Disconnect(output_capture_pin_);
    461   graph_builder_->Disconnect(input_sink_pin_);
    462 
    463   // If the _mjpg filter exist disconnect it even if it has not been used.
    464   if (mjpg_filter_) {
    465     graph_builder_->Disconnect(input_mjpg_pin_);
    466     graph_builder_->Disconnect(output_mjpg_pin_);
    467   }
    468 
    469   if (FAILED(hr)) {
    470     SetErrorState("Failed to Stop the Capture device");
    471     return;
    472   }
    473   client_.reset();
    474   state_ = kIdle;
    475 }
    476 
    477 // Implements SinkFilterObserver::SinkFilterObserver.
    478 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
    479                                           int length) {
    480   client_->OnIncomingCapturedFrame(
    481       buffer, length, base::Time::Now(), 0, capture_format_);
    482 }
    483 
    484 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
    485   DCHECK(CalledOnValidThread());
    486   ScopedComPtr<IAMStreamConfig> stream_config;
    487   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
    488   if (FAILED(hr)) {
    489     DVLOG(2) << "Failed to get IAMStreamConfig interface from "
    490                 "capture device";
    491     return false;
    492   }
    493 
    494   // Get interface used for getting the frame rate.
    495   ScopedComPtr<IAMVideoControl> video_control;
    496   hr = capture_filter_.QueryInterface(video_control.Receive());
    497   DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED";
    498 
    499   AM_MEDIA_TYPE* media_type = NULL;
    500   VIDEO_STREAM_CONFIG_CAPS caps;
    501   int count, size;
    502 
    503   hr = stream_config->GetNumberOfCapabilities(&count, &size);
    504   if (FAILED(hr)) {
    505     DVLOG(2) << "Failed to GetNumberOfCapabilities";
    506     return false;
    507   }
    508 
    509   for (int i = 0; i < count; ++i) {
    510     hr = stream_config->GetStreamCaps(i, &media_type,
    511                                       reinterpret_cast<BYTE*>(&caps));
    512     // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
    513     // macros here since they'll trigger incorrectly.
    514     if (hr != S_OK) {
    515       DVLOG(2) << "Failed to GetStreamCaps";
    516       return false;
    517     }
    518 
    519     if (media_type->majortype == MEDIATYPE_Video &&
    520         media_type->formattype == FORMAT_VideoInfo) {
    521       VideoCaptureCapabilityWin capability(i);
    522       VIDEOINFOHEADER* h =
    523           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
    524       capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth,
    525                                                      h->bmiHeader.biHeight);
    526 
    527       // Try to get a better |time_per_frame| from IAMVideoControl.  If not, use
    528       // the value from VIDEOINFOHEADER.
    529       REFERENCE_TIME time_per_frame = h->AvgTimePerFrame;
    530       if (video_control) {
    531         ScopedCoMem<LONGLONG> max_fps;
    532         LONG list_size = 0;
    533         SIZE size = {capability.supported_format.frame_size.width(),
    534                      capability.supported_format.frame_size.height()};
    535 
    536         // GetFrameRateList doesn't return max frame rate always
    537         // eg: Logitech Notebook. This may be due to a bug in that API
    538         // because GetFrameRateList array is reversed in the above camera. So
    539         // a util method written. Can't assume the first value will return
    540         // the max fps.
    541         hr = video_control->GetFrameRateList(output_capture_pin_, i, size,
    542                                              &list_size, &max_fps);
    543         // Sometimes |list_size| will be > 0, but max_fps will be NULL.  Some
    544         // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates
    545         // into success, so explicitly check S_OK.  See http://crbug.com/306237.
    546         if (hr == S_OK && list_size > 0 && max_fps) {
    547           time_per_frame = *std::min_element(max_fps.get(),
    548                                              max_fps.get() + list_size);
    549         }
    550       }
    551 
    552       capability.supported_format.frame_rate =
    553           (time_per_frame > 0)
    554               ? static_cast<int>(kSecondsToReferenceTime / time_per_frame)
    555               : 0;
    556 
    557       // DirectShow works at the moment only on integer frame_rate but the
    558       // best capability matching class works on rational frame rates.
    559       capability.frame_rate_numerator = capability.supported_format.frame_rate;
    560       capability.frame_rate_denominator = 1;
    561 
    562       // We can't switch MEDIATYPE :~(.
    563       if (media_type->subtype == kMediaSubTypeI420) {
    564         capability.supported_format.pixel_format = PIXEL_FORMAT_I420;
    565       } else if (media_type->subtype == MEDIASUBTYPE_IYUV) {
    566         // This is identical to PIXEL_FORMAT_I420.
    567         capability.supported_format.pixel_format = PIXEL_FORMAT_I420;
    568       } else if (media_type->subtype == MEDIASUBTYPE_RGB24) {
    569         capability.supported_format.pixel_format = PIXEL_FORMAT_RGB24;
    570       } else if (media_type->subtype == MEDIASUBTYPE_YUY2) {
    571         capability.supported_format.pixel_format = PIXEL_FORMAT_YUY2;
    572       } else if (media_type->subtype == MEDIASUBTYPE_MJPG) {
    573         capability.supported_format.pixel_format = PIXEL_FORMAT_MJPEG;
    574       } else if (media_type->subtype == MEDIASUBTYPE_UYVY) {
    575         capability.supported_format.pixel_format = PIXEL_FORMAT_UYVY;
    576       } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) {
    577         capability.supported_format.pixel_format = PIXEL_FORMAT_ARGB;
    578       } else {
    579         WCHAR guid_str[128];
    580         StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str));
    581         DVLOG(2) << "Device supports (also) an unknown media type " << guid_str;
    582         continue;
    583       }
    584       capabilities_.Add(capability);
    585     }
    586     DeleteMediaType(media_type);
    587     media_type = NULL;
    588   }
    589 
    590   return !capabilities_.empty();
    591 }
    592 
    593 void VideoCaptureDeviceWin::SetErrorState(const char* reason) {
    594   DCHECK(CalledOnValidThread());
    595   DVLOG(1) << reason;
    596   state_ = kError;
    597   client_->OnError();
    598 }
    599 }  // namespace media
    600