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