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/interface/ref_count.h"
17#include "webrtc/system_wrappers/interface/trace.h"
18
19#include <Dvdmedia.h>
20#include <Streams.h>
21
22namespace webrtc
23{
24namespace videocapturemodule
25{
26const int32_t NoWindowsCaptureDelays = 1;
27const 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
46DeviceInfoDS* 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
57DeviceInfoDS::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
101DeviceInfoDS::~DeviceInfoDS()
102{
103    RELEASE_AND_CLEAR(_dsMonikerDevEnum);
104    RELEASE_AND_CLEAR(_dsDevEnum);
105    if (_CoUninitializeIsRequired)
106    {
107        CoUninitialize();
108    }
109}
110
111int32_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}
123uint32_t DeviceInfoDS::NumberOfDevices()
124{
125    ReadLockScoped cs(_apiLock);
126    return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0);
127}
128
129int32_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
148int32_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
275IBaseFilter * 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
369int32_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
383int32_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 */
698void 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
738int32_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