tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

device_info_ds.cc (26975B)


      1 /*
      2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/video_capture/windows/device_info_ds.h"
     12 
     13 #include <dvdmedia.h>
     14 
     15 #include "modules/video_capture/video_capture_config.h"
     16 #include "modules/video_capture/windows/help_functions_ds.h"
     17 #include "rtc_base/logging.h"
     18 #include "rtc_base/string_utils.h"
     19 
     20 namespace webrtc {
     21 namespace videocapturemodule {
     22 
     23 BOOL isVideoDevice(DEV_BROADCAST_HDR *pHdr)
     24 {
     25  if (pHdr == NULL) {
     26    return FALSE;
     27  }
     28  if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) {
     29    return FALSE;
     30  }
     31  DEV_BROADCAST_DEVICEINTERFACE* pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr;
     32  return pDi->dbcc_classguid == KSCATEGORY_VIDEO_CAMERA;
     33 }
     34 
     35 LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
     36 {
     37    DeviceInfoDS* pParent;
     38    if (uiMsg == WM_CREATE)
     39    {
     40        pParent = (DeviceInfoDS*)((LPCREATESTRUCT)lParam)->lpCreateParams;
     41        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pParent);
     42    }
     43    else if (uiMsg == WM_DESTROY)
     44    {
     45        SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL);
     46    }
     47    else if (uiMsg == WM_DEVICECHANGE)
     48    {
     49        pParent = (DeviceInfoDS*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
     50        if (pParent && isVideoDevice((PDEV_BROADCAST_HDR)lParam))
     51        {
     52            pParent->DeviceChange();
     53        }
     54    }
     55    return DefWindowProc(hWnd, uiMsg, wParam, lParam);
     56 }
     57 
     58 // static
     59 DeviceInfoDS* DeviceInfoDS::Create() {
     60  DeviceInfoDS* dsInfo = new DeviceInfoDS();
     61  if (!dsInfo || dsInfo->Init() != 0) {
     62    delete dsInfo;
     63    dsInfo = NULL;
     64  }
     65  return dsInfo;
     66 }
     67 
     68 DeviceInfoDS::DeviceInfoDS()
     69    : _dsDevEnum(NULL),
     70      _dsMonikerDevEnum(NULL),
     71      _CoUninitializeIsRequired(true),
     72      _hdevnotify(NULL) {
     73  // 1) Initialize the COM library (make Windows load the DLLs).
     74  //
     75  // CoInitializeEx must be called at least once, and is usually called only
     76  // once, for each thread that uses the COM library. Multiple calls to
     77  // CoInitializeEx by the same thread are allowed as long as they pass the same
     78  // concurrency flag, but subsequent valid calls return S_FALSE. To close the
     79  // COM library gracefully on a thread, each successful call to CoInitializeEx,
     80  // including any call that returns S_FALSE, must be balanced by a
     81  // corresponding call to CoUninitialize.
     82  //
     83 
     84  /*Apartment-threading, while allowing for multiple threads of execution,
     85   serializes all incoming calls by requiring that calls to methods of objects
     86   created by this thread always run on the same thread the apartment/thread
     87   that created them. In addition, calls can arrive only at message-queue
     88   boundaries (i.e., only during a PeekMessage, SendMessage, DispatchMessage,
     89   etc.). Because of this serialization, it is not typically necessary to write
     90   concurrency control into the code for the object, other than to avoid calls
     91   to PeekMessage and SendMessage during processing that must not be interrupted
     92   by other method invocations or calls to other objects in the same
     93   apartment/thread.*/
     94 
     95  /// CoInitializeEx(NULL, COINIT_APARTMENTTHREADED ); //|
     96  /// COINIT_SPEED_OVER_MEMORY
     97  HRESULT hr = CoInitializeEx(
     98      NULL, COINIT_MULTITHREADED);  // Use COINIT_MULTITHREADED since Voice
     99                                    // Engine uses COINIT_MULTITHREADED
    100  if (FAILED(hr)) {
    101    // Avoid calling CoUninitialize() since CoInitializeEx() failed.
    102    _CoUninitializeIsRequired = FALSE;
    103 
    104    if (hr == RPC_E_CHANGED_MODE) {
    105      // Calling thread has already initialized COM to be used in a
    106      // single-threaded apartment (STA). We are then prevented from using STA.
    107      // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is
    108      // set".
    109      //
    110      RTC_DLOG(LS_INFO) << __FUNCTION__
    111                        << ": CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)"
    112                           " => RPC_E_CHANGED_MODE, error 0x"
    113                        << webrtc::ToHex(hr);
    114    }
    115  }
    116 
    117  _hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL));
    118  _wndClass = {0};
    119  _wndClass.lpfnWndProc = &WndProc;
    120  _wndClass.lpszClassName = TEXT("DeviceInfoDS");
    121  _wndClass.hInstance = _hInstance;
    122 
    123  if (RegisterClass(&_wndClass)) {
    124    _hwnd = CreateWindow(_wndClass.lpszClassName, NULL, 0, CW_USEDEFAULT,
    125                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
    126                         NULL, _hInstance, this);
    127 
    128    DEV_BROADCAST_DEVICEINTERFACE di = { 0 };
    129    di.dbcc_size = sizeof(di);
    130    di.dbcc_devicetype  = DBT_DEVTYP_DEVICEINTERFACE;
    131    di.dbcc_classguid  = KSCATEGORY_VIDEO_CAMERA;
    132 
    133    _hdevnotify = RegisterDeviceNotification(_hwnd, &di,
    134                                             DEVICE_NOTIFY_WINDOW_HANDLE);
    135  }
    136 }
    137 
    138 DeviceInfoDS::~DeviceInfoDS() {
    139  RELEASE_AND_CLEAR(_dsMonikerDevEnum);
    140  RELEASE_AND_CLEAR(_dsDevEnum);
    141  if (_CoUninitializeIsRequired) {
    142    CoUninitialize();
    143  }
    144  if (_hdevnotify)
    145  {
    146    UnregisterDeviceNotification(_hdevnotify);
    147  }
    148  if (_hwnd != NULL) {
    149    DestroyWindow(_hwnd);
    150  }
    151  UnregisterClass(_wndClass.lpszClassName, _hInstance);
    152 }
    153 
    154 int32_t DeviceInfoDS::Init() {
    155  HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
    156                                IID_ICreateDevEnum, (void**)&_dsDevEnum);
    157  if (hr != NOERROR) {
    158    RTC_LOG(LS_INFO) << "Failed to create CLSID_SystemDeviceEnum, error 0x"
    159                     << webrtc::ToHex(hr);
    160    return -1;
    161  }
    162  return 0;
    163 }
    164 uint32_t DeviceInfoDS::NumberOfDevices() {
    165  MutexLock lock(&_apiLock);
    166  return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0);
    167 }
    168 
    169 int32_t DeviceInfoDS::GetDeviceName(uint32_t deviceNumber,
    170                                    char* deviceNameUTF8,
    171                                    uint32_t deviceNameLength,
    172                                    char* deviceUniqueIdUTF8,
    173                                    uint32_t deviceUniqueIdUTF8Length,
    174                                    char* productUniqueIdUTF8,
    175                                    uint32_t productUniqueIdUTF8Length,
    176                                    pid_t* pid,
    177                                    bool* deviceIsPlaceholder) {
    178  MutexLock lock(&_apiLock);
    179  const int32_t result = GetDeviceInfo(
    180      deviceNumber, deviceNameUTF8, deviceNameLength, deviceUniqueIdUTF8,
    181      deviceUniqueIdUTF8Length, productUniqueIdUTF8, productUniqueIdUTF8Length);
    182  return result > (int32_t)deviceNumber ? 0 : -1;
    183 }
    184 
    185 int32_t DeviceInfoDS::GetDeviceInfo(uint32_t deviceNumber,
    186                                    char* deviceNameUTF8,
    187                                    uint32_t deviceNameLength,
    188                                    char* deviceUniqueIdUTF8,
    189                                    uint32_t deviceUniqueIdUTF8Length,
    190                                    char* productUniqueIdUTF8,
    191                                    uint32_t productUniqueIdUTF8Length)
    192 
    193 {
    194  // enumerate all video capture devices
    195  RELEASE_AND_CLEAR(_dsMonikerDevEnum);
    196  HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
    197                                                 &_dsMonikerDevEnum, 0);
    198  if (hr != NOERROR) {
    199    RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"
    200                     << webrtc::ToHex(hr) << ". No webcam exist?";
    201    return 0;
    202  }
    203 
    204  _dsMonikerDevEnum->Reset();
    205  ULONG cFetched;
    206  IMoniker* pM;
    207  int index = 0;
    208  while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched)) {
    209    IPropertyBag* pBag;
    210    hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
    211    if (S_OK == hr) {
    212      // Find the description or friendly name.
    213      VARIANT varName;
    214      VariantInit(&varName);
    215      hr = pBag->Read(L"Description", &varName, 0);
    216      if (FAILED(hr)) {
    217        hr = pBag->Read(L"FriendlyName", &varName, 0);
    218      }
    219      if (SUCCEEDED(hr)) {
    220        // ignore all VFW drivers
    221        if ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) &&
    222            (_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"), 21) != 0)) {
    223          // Found a valid device.
    224          if (index == static_cast<int>(deviceNumber)) {
    225            int convResult = 0;
    226            if (deviceNameLength > 0) {
    227              convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1,
    228                                               (char*)deviceNameUTF8,
    229                                               deviceNameLength, NULL, NULL);
    230              if (convResult == 0) {
    231                RTC_LOG(LS_INFO) << "Failed to convert device name to UTF8, "
    232                                    "error = "
    233                                 << GetLastError();
    234                return -1;
    235              }
    236            }
    237            if (deviceUniqueIdUTF8Length > 0) {
    238              hr = pBag->Read(L"DevicePath", &varName, 0);
    239              if (FAILED(hr)) {
    240                strncpy_s((char*)deviceUniqueIdUTF8, deviceUniqueIdUTF8Length,
    241                          (char*)deviceNameUTF8, convResult);
    242                RTC_LOG(LS_INFO) << "Failed to get "
    243                                    "deviceUniqueIdUTF8 using "
    244                                    "deviceNameUTF8";
    245              } else {
    246                convResult = WideCharToMultiByte(
    247                    CP_UTF8, 0, varName.bstrVal, -1, (char*)deviceUniqueIdUTF8,
    248                    deviceUniqueIdUTF8Length, NULL, NULL);
    249                if (convResult == 0) {
    250                  RTC_LOG(LS_INFO) << "Failed to convert device "
    251                                      "name to UTF8, error = "
    252                                   << GetLastError();
    253                  return -1;
    254                }
    255                if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) {
    256                  GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8,
    257                               productUniqueIdUTF8Length);
    258                }
    259              }
    260            }
    261          }
    262          ++index;  // increase the number of valid devices
    263        }
    264      }
    265      VariantClear(&varName);
    266      pBag->Release();
    267      pM->Release();
    268    }
    269  }
    270  if (deviceNameLength) {
    271    RTC_DLOG(LS_INFO) << __FUNCTION__ << " " << deviceNameUTF8;
    272  }
    273  return index;
    274 }
    275 
    276 IBaseFilter* DeviceInfoDS::GetDeviceFilter(const char* deviceUniqueIdUTF8,
    277                                           char* productUniqueIdUTF8,
    278                                           uint32_t productUniqueIdUTF8Length) {
    279  const int32_t deviceUniqueIdUTF8Length = (int32_t)strlen(
    280      (char*)deviceUniqueIdUTF8);  // UTF8 is also NULL terminated
    281  if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {
    282    RTC_LOG(LS_INFO) << "Device name too long";
    283    return NULL;
    284  }
    285 
    286  // enumerate all video capture devices
    287  RELEASE_AND_CLEAR(_dsMonikerDevEnum);
    288  HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
    289                                                 &_dsMonikerDevEnum, 0);
    290  if (hr != NOERROR) {
    291    RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"
    292                     << webrtc::ToHex(hr) << ". No webcam exist?";
    293    return 0;
    294  }
    295  _dsMonikerDevEnum->Reset();
    296  ULONG cFetched;
    297  IMoniker* pM;
    298 
    299  IBaseFilter* captureFilter = NULL;
    300  bool deviceFound = false;
    301  while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) {
    302    IPropertyBag* pBag;
    303    hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
    304    if (S_OK == hr) {
    305      // Find the description or friendly name.
    306      VARIANT varName;
    307      VariantInit(&varName);
    308      if (deviceUniqueIdUTF8Length > 0) {
    309        hr = pBag->Read(L"DevicePath", &varName, 0);
    310        if (FAILED(hr)) {
    311          hr = pBag->Read(L"Description", &varName, 0);
    312          if (FAILED(hr)) {
    313            hr = pBag->Read(L"FriendlyName", &varName, 0);
    314          }
    315        }
    316        if (SUCCEEDED(hr)) {
    317          char tempDevicePathUTF8[256];
    318          tempDevicePathUTF8[0] = 0;
    319          WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1,
    320                              tempDevicePathUTF8, sizeof(tempDevicePathUTF8),
    321                              NULL, NULL);
    322          if (strncmp(tempDevicePathUTF8, (const char*)deviceUniqueIdUTF8,
    323                      deviceUniqueIdUTF8Length) == 0) {
    324            // We have found the requested device
    325            deviceFound = true;
    326            hr =
    327                pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter);
    328            if FAILED (hr) {
    329              RTC_LOG(LS_ERROR) << "Failed to bind to the selected "
    330                                   "capture device "
    331                                << hr;
    332            }
    333 
    334            if (productUniqueIdUTF8 &&
    335                productUniqueIdUTF8Length > 0)  // Get the device name
    336            {
    337              GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8,
    338                           productUniqueIdUTF8Length);
    339            }
    340          }
    341        }
    342      }
    343      VariantClear(&varName);
    344      pBag->Release();
    345    }
    346    pM->Release();
    347  }
    348  return captureFilter;
    349 }
    350 
    351 int32_t DeviceInfoDS::GetWindowsCapability(
    352    const int32_t capabilityIndex,
    353    VideoCaptureCapabilityWindows& windowsCapability) {
    354  MutexLock lock(&_apiLock);
    355 
    356  if (capabilityIndex < 0 || static_cast<size_t>(capabilityIndex) >=
    357                                 _captureCapabilitiesWindows.size()) {
    358    return -1;
    359  }
    360 
    361  windowsCapability = _captureCapabilitiesWindows[capabilityIndex];
    362  return 0;
    363 }
    364 
    365 int32_t DeviceInfoDS::CreateCapabilityMap(const char* deviceUniqueIdUTF8)
    366 
    367 {
    368  // Reset old capability list
    369  _captureCapabilities.clear();
    370 
    371  const int32_t deviceUniqueIdUTF8Length =
    372      (int32_t)strlen((char*)deviceUniqueIdUTF8);
    373  if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {
    374    RTC_LOG(LS_INFO) << "Device name too long";
    375    return -1;
    376  }
    377  RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device "
    378                   << deviceUniqueIdUTF8;
    379 
    380  char productId[kVideoCaptureProductIdLength];
    381  IBaseFilter* captureDevice = DeviceInfoDS::GetDeviceFilter(
    382      deviceUniqueIdUTF8, productId, kVideoCaptureProductIdLength);
    383  if (!captureDevice)
    384    return -1;
    385  IPin* outputCapturePin = GetOutputPin(captureDevice, GUID_NULL);
    386  if (!outputCapturePin) {
    387    RTC_LOG(LS_INFO) << "Failed to get capture device output pin";
    388    RELEASE_AND_CLEAR(captureDevice);
    389    return -1;
    390  }
    391  IAMExtDevice* extDevice = NULL;
    392  HRESULT hr =
    393      captureDevice->QueryInterface(IID_IAMExtDevice, (void**)&extDevice);
    394  if (SUCCEEDED(hr) && extDevice) {
    395    RTC_LOG(LS_INFO) << "This is an external device";
    396    extDevice->Release();
    397  }
    398 
    399  IAMStreamConfig* streamConfig = NULL;
    400  hr = outputCapturePin->QueryInterface(IID_IAMStreamConfig,
    401                                        (void**)&streamConfig);
    402  if (FAILED(hr)) {
    403    RTC_LOG(LS_INFO) << "Failed to get IID_IAMStreamConfig interface "
    404                        "from capture device";
    405    return -1;
    406  }
    407 
    408  // this  gets the FPS
    409  IAMVideoControl* videoControlConfig = NULL;
    410  HRESULT hrVC = captureDevice->QueryInterface(IID_IAMVideoControl,
    411                                               (void**)&videoControlConfig);
    412  if (FAILED(hrVC)) {
    413    RTC_LOG(LS_INFO) << "IID_IAMVideoControl Interface NOT SUPPORTED";
    414  }
    415 
    416  AM_MEDIA_TYPE* pmt = NULL;
    417  VIDEO_STREAM_CONFIG_CAPS caps;
    418  int count, size;
    419 
    420  hr = streamConfig->GetNumberOfCapabilities(&count, &size);
    421  if (FAILED(hr)) {
    422    RTC_LOG(LS_INFO) << "Failed to GetNumberOfCapabilities";
    423    RELEASE_AND_CLEAR(videoControlConfig);
    424    RELEASE_AND_CLEAR(streamConfig);
    425    RELEASE_AND_CLEAR(outputCapturePin);
    426    RELEASE_AND_CLEAR(captureDevice);
    427    return -1;
    428  }
    429 
    430  // Check if the device support formattype == FORMAT_VideoInfo2 and
    431  // FORMAT_VideoInfo. Prefer FORMAT_VideoInfo since some cameras (ZureCam) has
    432  // been seen having problem with MJPEG and FORMAT_VideoInfo2 Interlace flag is
    433  // only supported in FORMAT_VideoInfo2
    434  bool supportFORMAT_VideoInfo2 = false;
    435  bool supportFORMAT_VideoInfo = false;
    436  bool foundInterlacedFormat = false;
    437  GUID preferedVideoFormat = FORMAT_VideoInfo;
    438  for (int32_t tmp = 0; tmp < count; ++tmp) {
    439    hr = streamConfig->GetStreamCaps(tmp, &pmt, reinterpret_cast<BYTE*>(&caps));
    440    if (hr == S_OK) {
    441      if (pmt->majortype == MEDIATYPE_Video &&
    442          pmt->formattype == FORMAT_VideoInfo2) {
    443        RTC_LOG(LS_INFO) << "Device support FORMAT_VideoInfo2";
    444        supportFORMAT_VideoInfo2 = true;
    445        VIDEOINFOHEADER2* h =
    446            reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat);
    447        RTC_DCHECK(h);
    448        foundInterlacedFormat |=
    449            h->dwInterlaceFlags &
    450            (AMINTERLACE_IsInterlaced | AMINTERLACE_DisplayModeBobOnly);
    451      }
    452      if (pmt->majortype == MEDIATYPE_Video &&
    453          pmt->formattype == FORMAT_VideoInfo) {
    454        RTC_LOG(LS_INFO) << "Device support FORMAT_VideoInfo2";
    455        supportFORMAT_VideoInfo = true;
    456      }
    457 
    458      FreeMediaType(pmt);
    459      pmt = NULL;
    460    }
    461  }
    462  if (supportFORMAT_VideoInfo2) {
    463    if (supportFORMAT_VideoInfo && !foundInterlacedFormat) {
    464      preferedVideoFormat = FORMAT_VideoInfo;
    465    } else {
    466      preferedVideoFormat = FORMAT_VideoInfo2;
    467    }
    468  }
    469 
    470  for (int32_t tmp = 0; tmp < count; ++tmp) {
    471    hr = streamConfig->GetStreamCaps(tmp, &pmt, reinterpret_cast<BYTE*>(&caps));
    472    if (hr != S_OK) {
    473      RTC_LOG(LS_INFO) << "Failed to GetStreamCaps";
    474      RELEASE_AND_CLEAR(videoControlConfig);
    475      RELEASE_AND_CLEAR(streamConfig);
    476      RELEASE_AND_CLEAR(outputCapturePin);
    477      RELEASE_AND_CLEAR(captureDevice);
    478      return -1;
    479    }
    480 
    481    if (pmt->majortype == MEDIATYPE_Video &&
    482        pmt->formattype == preferedVideoFormat) {
    483      VideoCaptureCapabilityWindows capability;
    484      int64_t avgTimePerFrame = 0;
    485 
    486      if (pmt->formattype == FORMAT_VideoInfo) {
    487        VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
    488        RTC_DCHECK(h);
    489        capability.directShowCapabilityIndex = tmp;
    490        capability.width = h->bmiHeader.biWidth;
    491        capability.height = h->bmiHeader.biHeight;
    492        avgTimePerFrame = h->AvgTimePerFrame;
    493      }
    494      if (pmt->formattype == FORMAT_VideoInfo2) {
    495        VIDEOINFOHEADER2* h =
    496            reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat);
    497        RTC_DCHECK(h);
    498        capability.directShowCapabilityIndex = tmp;
    499        capability.width = h->bmiHeader.biWidth;
    500        capability.height = h->bmiHeader.biHeight;
    501        capability.interlaced =
    502            h->dwInterlaceFlags &
    503            (AMINTERLACE_IsInterlaced | AMINTERLACE_DisplayModeBobOnly);
    504        avgTimePerFrame = h->AvgTimePerFrame;
    505      }
    506 
    507      if (hrVC == S_OK) {
    508        LONGLONG* frameDurationList = NULL;
    509        LONGLONG maxFPS = 0;
    510        long listSize = 0;
    511        SIZE requested_size;
    512        requested_size.cx = capability.width;
    513        requested_size.cy = capability.height;
    514 
    515        // GetMaxAvailableFrameRate doesn't return max frame rate always
    516        // eg: Logitech Notebook. This may be due to a bug in that API
    517        // because GetFrameRateList array is reversed in the above camera. So
    518        // a util method written. Can't assume the first value will return
    519        // the max fps.
    520        hrVC = videoControlConfig->GetFrameRateList(outputCapturePin, tmp,
    521                                                    requested_size, &listSize,
    522                                                    &frameDurationList);
    523 
    524        if (hrVC == S_OK) {
    525          maxFPS = GetMaxOfFrameArray(frameDurationList, listSize);
    526        }
    527 
    528        CoTaskMemFree(frameDurationList);
    529        frameDurationList = NULL;
    530        listSize = 0;
    531 
    532        // On some odd cameras, you may get a 0 for duration. Some others may
    533        // not update the out vars. GetMaxOfFrameArray returns the lowest
    534        // duration (highest FPS), or 0 if there was no list with elements.
    535        if (0 != maxFPS) {
    536          capability.maxFPS = static_cast<int>(10000000 / maxFPS);
    537          capability.supportFrameRateControl = true;
    538        } else  // use existing method
    539        {
    540          RTC_LOG(LS_INFO) << "GetMaxAvailableFrameRate NOT SUPPORTED";
    541          if (avgTimePerFrame > 0)
    542            capability.maxFPS = static_cast<int>(10000000 / avgTimePerFrame);
    543          else
    544            capability.maxFPS = 0;
    545        }
    546      } else  // use existing method in case IAMVideoControl is not supported
    547      {
    548        if (avgTimePerFrame > 0)
    549          capability.maxFPS = static_cast<int>(10000000 / avgTimePerFrame);
    550        else
    551          capability.maxFPS = 0;
    552      }
    553 
    554      // can't switch MEDIATYPE :~(
    555      if (pmt->subtype == MEDIASUBTYPE_I420) {
    556        capability.videoType = VideoType::kI420;
    557      } else if (pmt->subtype == MEDIASUBTYPE_IYUV) {
    558        capability.videoType = VideoType::kIYUV;
    559      } else if (pmt->subtype == MEDIASUBTYPE_RGB24) {
    560        capability.videoType = VideoType::kRGB24;
    561      } else if (pmt->subtype == MEDIASUBTYPE_YUY2) {
    562        capability.videoType = VideoType::kYUY2;
    563      } else if (pmt->subtype == MEDIASUBTYPE_RGB565) {
    564        capability.videoType = VideoType::kRGB565;
    565      } else if (pmt->subtype == MEDIASUBTYPE_MJPG) {
    566        capability.videoType = VideoType::kMJPEG;
    567      } else if (pmt->subtype == MEDIASUBTYPE_dvsl ||
    568                 pmt->subtype == MEDIASUBTYPE_dvsd ||
    569                 pmt->subtype ==
    570                     MEDIASUBTYPE_dvhd)  // If this is an external DV camera
    571      {
    572        capability.videoType =
    573            VideoType::kYUY2;  // MS DV filter seems to create this type
    574      } else if (pmt->subtype ==
    575                 MEDIASUBTYPE_UYVY)  // Seen used by Declink capture cards
    576      {
    577        capability.videoType = VideoType::kUYVY;
    578      } else if (pmt->subtype ==
    579                 MEDIASUBTYPE_HDYC)  // Seen used by Declink capture cards. Uses
    580                                     // BT. 709 color. Not entiry correct to use
    581                                     // UYVY. http://en.wikipedia.org/wiki/YCbCr
    582      {
    583        RTC_LOG(LS_INFO) << "Device support HDYC.";
    584        capability.videoType = VideoType::kUYVY;
    585      } else {
    586        WCHAR strGuid[39];
    587        StringFromGUID2(pmt->subtype, strGuid, 39);
    588        RTC_LOG(LS_WARNING)
    589            << "Device support unknown media type " << strGuid << ", width "
    590            << capability.width << ", height " << capability.height;
    591        continue;
    592      }
    593 
    594      _captureCapabilities.push_back(capability);
    595      _captureCapabilitiesWindows.push_back(capability);
    596      RTC_LOG(LS_INFO) << "Camera capability, width:" << capability.width
    597                       << " height:" << capability.height
    598                       << " type:" << static_cast<int>(capability.videoType)
    599                       << " fps:" << capability.maxFPS;
    600    }
    601    FreeMediaType(pmt);
    602    pmt = NULL;
    603  }
    604  RELEASE_AND_CLEAR(streamConfig);
    605  RELEASE_AND_CLEAR(videoControlConfig);
    606  RELEASE_AND_CLEAR(outputCapturePin);
    607  RELEASE_AND_CLEAR(captureDevice);  // Release the capture device
    608 
    609  // Store the new used device name
    610  _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
    611  _lastUsedDeviceName =
    612      (char*)realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1);
    613  memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8,
    614         _lastUsedDeviceNameLength + 1);
    615  RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
    616 
    617  return static_cast<int32_t>(_captureCapabilities.size());
    618 }
    619 
    620 // Constructs a product ID from the Windows DevicePath. on a USB device the
    621 // devicePath contains product id and vendor id. This seems to work for firewire
    622 // as well.
    623 // Example of device path:
    624 // "\\?\usb#vid_0408&pid_2010&mi_00#7&258e7aaf&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
    625 // "\\?\avc#sony&dv-vcr&camcorder&dv#65b2d50301460008#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
    626 void DeviceInfoDS::GetProductId(const char* devicePath,
    627                                char* productUniqueIdUTF8,
    628                                uint32_t productUniqueIdUTF8Length) {
    629  *productUniqueIdUTF8 = '\0';
    630  char* startPos = strstr((char*)devicePath, "\\\\?\\");
    631  if (!startPos) {
    632    strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    633    RTC_LOG(LS_INFO) << "Failed to get the product Id";
    634    return;
    635  }
    636  startPos += 4;
    637 
    638  char* pos = strchr(startPos, '&');
    639  if (!pos || pos >= (char*)devicePath + strlen((char*)devicePath)) {
    640    strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    641    RTC_LOG(LS_INFO) << "Failed to get the product Id";
    642    return;
    643  }
    644  // Find the second occurrence.
    645  pos = strchr(pos + 1, '&');
    646  uint32_t bytesToCopy = (uint32_t)(pos - startPos);
    647  if (pos && (bytesToCopy < productUniqueIdUTF8Length) &&
    648      bytesToCopy <= kVideoCaptureProductIdLength) {
    649    strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length,
    650              (char*)startPos, bytesToCopy);
    651  } else {
    652    strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    653    RTC_LOG(LS_INFO) << "Failed to get the product Id";
    654  }
    655 }
    656 
    657 int32_t DeviceInfoDS::DisplayCaptureSettingsDialogBox(
    658    const char* deviceUniqueIdUTF8,
    659    const char* dialogTitleUTF8,
    660    void* parentWindow,
    661    uint32_t positionX,
    662    uint32_t positionY) {
    663  MutexLock lock(&_apiLock);
    664  HWND window = (HWND)parentWindow;
    665 
    666  IBaseFilter* filter = GetDeviceFilter(deviceUniqueIdUTF8, NULL, 0);
    667  if (!filter)
    668    return -1;
    669 
    670  ISpecifyPropertyPages* pPages = NULL;
    671  CAUUID uuid;
    672  HRESULT hr = S_OK;
    673 
    674  hr = filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID*)&pPages);
    675  if (!SUCCEEDED(hr)) {
    676    filter->Release();
    677    return -1;
    678  }
    679  hr = pPages->GetPages(&uuid);
    680  if (!SUCCEEDED(hr)) {
    681    filter->Release();
    682    return -1;
    683  }
    684 
    685  WCHAR tempDialogTitleWide[256];
    686  tempDialogTitleWide[0] = 0;
    687  int size = 255;
    688 
    689  // UTF-8 to wide char
    690  MultiByteToWideChar(CP_UTF8, 0, (char*)dialogTitleUTF8, -1,
    691                      tempDialogTitleWide, size);
    692 
    693  // Invoke a dialog box to display.
    694 
    695  hr = OleCreatePropertyFrame(
    696      window,               // You must create the parent window.
    697      positionX,            // Horizontal position for the dialog box.
    698      positionY,            // Vertical position for the dialog box.
    699      tempDialogTitleWide,  // String used for the dialog box caption.
    700      1,                    // Number of pointers passed in pPlugin.
    701      (LPUNKNOWN*)&filter,  // Pointer to the filter.
    702      uuid.cElems,          // Number of property pages.
    703      uuid.pElems,          // Array of property page CLSIDs.
    704      LOCALE_USER_DEFAULT,  // Locale ID for the dialog box.
    705      0, NULL);             // Reserved
    706  // Release memory.
    707  if (uuid.pElems) {
    708    CoTaskMemFree(uuid.pElems);
    709  }
    710  filter->Release();
    711  return 0;
    712 }
    713 }  // namespace videocapturemodule
    714 }  // namespace webrtc