device_info_ds.cc revision 10987a851d64803899e9980ba342c389c377db35
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 "device_info_ds.h"
12
13#include "../video_capture_config.h"
14#include "../video_capture_delay.h"
15#include "help_functions_ds.h"
16#include "ref_count.h"
17#include "trace.h"
18
19#include <Streams.h>
20#include <Dvdmedia.h>
21
22namespace webrtc
23{
24namespace videocapturemodule
25{
26const WebRtc_Word32 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 WebRtc_Word32 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 WebRtc_Word32 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
111WebRtc_Word32 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}
123WebRtc_UWord32 DeviceInfoDS::NumberOfDevices()
124{
125    ReadLockScoped cs(_apiLock);
126    return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0);
127}
128
129WebRtc_Word32 DeviceInfoDS::GetDeviceName(
130                                       WebRtc_UWord32 deviceNumber,
131                                       char* deviceNameUTF8,
132                                       WebRtc_UWord32 deviceNameLength,
133                                       char* deviceUniqueIdUTF8,
134                                       WebRtc_UWord32 deviceUniqueIdUTF8Length,
135                                       char* productUniqueIdUTF8,
136                                       WebRtc_UWord32 productUniqueIdUTF8Length)
137{
138    ReadLockScoped cs(_apiLock);
139    const WebRtc_Word32 result = GetDeviceInfo(deviceNumber, deviceNameUTF8,
140                                               deviceNameLength,
141                                               deviceUniqueIdUTF8,
142                                               deviceUniqueIdUTF8Length,
143                                               productUniqueIdUTF8,
144                                               productUniqueIdUTF8Length);
145    return result > (WebRtc_Word32) deviceNumber ? 0 : -1;
146}
147
148WebRtc_Word32 DeviceInfoDS::GetDeviceInfo(
149                                       WebRtc_UWord32 deviceNumber,
150                                       char* deviceNameUTF8,
151                                       WebRtc_UWord32 deviceNameLength,
152                                       char* deviceUniqueIdUTF8,
153                                       WebRtc_UWord32 deviceUniqueIdUTF8Length,
154                                       char* productUniqueIdUTF8,
155                                       WebRtc_UWord32 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                                     WebRtc_UWord32 productUniqueIdUTF8Length)
279{
280
281    const WebRtc_Word32 deviceUniqueIdUTF8Length =
282        (WebRtc_Word32) 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
369WebRtc_Word32 DeviceInfoDS::GetWindowsCapability(
370                              const WebRtc_Word32 capabilityIndex,
371                              VideoCaptureCapabilityWindows& windowsCapability)
372
373{
374    ReadLockScoped cs(_apiLock);
375    // Make sure the number is valid
376    if (capabilityIndex >= _captureCapabilities.Size() || capabilityIndex < 0)
377        return -1;
378
379    MapItem* item = _captureCapabilities.Find(capabilityIndex);
380    if (!item)
381        return -1;
382
383    VideoCaptureCapabilityWindows* capPointer =
384                static_cast<VideoCaptureCapabilityWindows*> (item->GetItem());
385    windowsCapability = *capPointer;
386    return 0;
387}
388
389WebRtc_Word32 DeviceInfoDS::CreateCapabilityMap(
390                                         const char* deviceUniqueIdUTF8)
391
392{
393    // Reset old capability list
394    MapItem* item = NULL;
395    while (item = _captureCapabilities.Last())
396    {
397        VideoCaptureCapabilityWindows* cap =
398            static_cast<VideoCaptureCapabilityWindows*> (item->GetItem());
399        delete cap;
400        _captureCapabilities.Erase(item);
401    }
402
403    const WebRtc_Word32 deviceUniqueIdUTF8Length =
404        (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8);
405    if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
406    {
407        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
408                     "Device name too long");
409        return -1;
410    }
411    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
412                 "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8);
413
414
415    char productId[kVideoCaptureProductIdLength];
416    IBaseFilter* captureDevice = DeviceInfoDS::GetDeviceFilter(
417                                               deviceUniqueIdUTF8,
418                                               productId,
419                                               kVideoCaptureProductIdLength);
420    if (!captureDevice)
421        return -1;
422    IPin* outputCapturePin = GetOutputPin(captureDevice, GUID_NULL);
423    if (!outputCapturePin)
424    {
425        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
426                     "Failed to get capture device output pin");
427        RELEASE_AND_CLEAR(captureDevice);
428        return -1;
429    }
430    IAMExtDevice* extDevice = NULL;
431    HRESULT hr = captureDevice->QueryInterface(IID_IAMExtDevice,
432                                               (void **) &extDevice);
433    if (SUCCEEDED(hr) && extDevice)
434    {
435        WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
436                     "This is an external device");
437        extDevice->Release();
438    }
439
440    IAMStreamConfig* streamConfig = NULL;
441    hr = outputCapturePin->QueryInterface(IID_IAMStreamConfig,
442                                          (void**) &streamConfig);
443    if (FAILED(hr))
444    {
445        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
446                     "Failed to get IID_IAMStreamConfig interface from capture device");
447        return -1;
448    }
449
450    // this  gets the FPS
451    IAMVideoControl* videoControlConfig = NULL;
452    HRESULT hrVC = captureDevice->QueryInterface(IID_IAMVideoControl,
453                                      (void**) &videoControlConfig);
454    if (FAILED(hrVC))
455    {
456        WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
457                     "IID_IAMVideoControl Interface NOT SUPPORTED");
458    }
459
460    AM_MEDIA_TYPE *pmt = NULL;
461    VIDEO_STREAM_CONFIG_CAPS caps;
462    int count, size;
463
464    hr = streamConfig->GetNumberOfCapabilities(&count, &size);
465    if (FAILED(hr))
466    {
467        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
468                     "Failed to GetNumberOfCapabilities");
469        RELEASE_AND_CLEAR(videoControlConfig);
470        RELEASE_AND_CLEAR(streamConfig);
471        RELEASE_AND_CLEAR(outputCapturePin);
472        RELEASE_AND_CLEAR(captureDevice);
473        return -1;
474    }
475
476    WebRtc_Word32 index = 0; // Index in created _capabilities map
477    // Check if the device support formattype == FORMAT_VideoInfo2 and FORMAT_VideoInfo.
478    // Prefer FORMAT_VideoInfo since some cameras (ZureCam) has been seen having problem with MJPEG and FORMAT_VideoInfo2
479    // Interlace flag is only supported in FORMAT_VideoInfo2
480    bool supportFORMAT_VideoInfo2 = false;
481    bool supportFORMAT_VideoInfo = false;
482    bool foundInterlacedFormat = false;
483    GUID preferedVideoFormat = FORMAT_VideoInfo;
484    for (WebRtc_Word32 tmp = 0; tmp < count; ++tmp)
485    {
486        hr = streamConfig->GetStreamCaps(tmp, &pmt,
487                                         reinterpret_cast<BYTE*> (&caps));
488        if (!FAILED(hr))
489        {
490            if (pmt->majortype == MEDIATYPE_Video
491                && pmt->formattype == FORMAT_VideoInfo2)
492            {
493                WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
494                             " Device support FORMAT_VideoInfo2");
495                supportFORMAT_VideoInfo2 = true;
496                VIDEOINFOHEADER2* h =
497                    reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
498                assert(h);
499                foundInterlacedFormat |= h->dwInterlaceFlags
500                                        & (AMINTERLACE_IsInterlaced
501                                           | AMINTERLACE_DisplayModeBobOnly);
502            }
503            if (pmt->majortype == MEDIATYPE_Video
504                && pmt->formattype == FORMAT_VideoInfo)
505            {
506                WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
507                             " Device support FORMAT_VideoInfo2");
508                supportFORMAT_VideoInfo = true;
509            }
510        }
511    }
512    if (supportFORMAT_VideoInfo2)
513    {
514        if (supportFORMAT_VideoInfo && !foundInterlacedFormat)
515        {
516            preferedVideoFormat = FORMAT_VideoInfo;
517        }
518        else
519        {
520            preferedVideoFormat = FORMAT_VideoInfo2;
521        }
522    }
523
524    for (WebRtc_Word32 tmp = 0; tmp < count; ++tmp)
525    {
526        hr = streamConfig->GetStreamCaps(tmp, &pmt,
527                                         reinterpret_cast<BYTE*> (&caps));
528        if (FAILED(hr))
529        {
530            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
531                         "Failed to GetStreamCaps");
532            RELEASE_AND_CLEAR(videoControlConfig);
533            RELEASE_AND_CLEAR(streamConfig);
534            RELEASE_AND_CLEAR(outputCapturePin);
535            RELEASE_AND_CLEAR(captureDevice);
536            return -1;
537        }
538
539        if (pmt->majortype == MEDIATYPE_Video
540            && pmt->formattype == preferedVideoFormat)
541        {
542
543            VideoCaptureCapabilityWindows* capability =
544                                        new VideoCaptureCapabilityWindows();
545            WebRtc_Word64 avgTimePerFrame = 0;
546
547            if (pmt->formattype == FORMAT_VideoInfo)
548            {
549                VIDEOINFOHEADER* h =
550                    reinterpret_cast<VIDEOINFOHEADER*> (pmt->pbFormat);
551                assert(h);
552                capability->directShowCapabilityIndex = tmp;
553                capability->width = h->bmiHeader.biWidth;
554                capability->height = h->bmiHeader.biHeight;
555                avgTimePerFrame = h->AvgTimePerFrame;
556            }
557            if (pmt->formattype == FORMAT_VideoInfo2)
558            {
559                VIDEOINFOHEADER2* h =
560                    reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
561                assert(h);
562                capability->directShowCapabilityIndex = tmp;
563                capability->width = h->bmiHeader.biWidth;
564                capability->height = h->bmiHeader.biHeight;
565                capability->interlaced = h->dwInterlaceFlags
566                                        & (AMINTERLACE_IsInterlaced
567                                           | AMINTERLACE_DisplayModeBobOnly);
568                avgTimePerFrame = h->AvgTimePerFrame;
569            }
570
571            if (hrVC == S_OK)
572            {
573                LONGLONG *frameDurationList;
574                LONGLONG maxFPS;
575                long listSize;
576                SIZE size;
577                size.cx = capability->width;
578                size.cy = capability->height;
579
580                // GetMaxAvailableFrameRate doesn't return max frame rate always
581                // eg: Logitech Notebook. This may be due to a bug in that API
582                // because GetFrameRateList array is reversed in the above camera. So
583                // a util method written. Can't assume the first value will return
584                // the max fps.
585                hrVC = videoControlConfig->GetFrameRateList(outputCapturePin,
586                                                            tmp, size,
587                                                            &listSize,
588                                                            &frameDurationList);
589
590                // On some odd cameras, you may get a 0 for duration.
591                // GetMaxOfFrameArray returns the lowest duration (highest FPS)
592                if (hrVC == S_OK && listSize > 0 &&
593                    0 != (maxFPS = GetMaxOfFrameArray(frameDurationList,
594                                                      listSize)))
595                {
596                    capability->maxFPS = static_cast<int> (10000000
597                                                           / maxFPS);
598                    capability->supportFrameRateControl = true;
599                }
600                else // use existing method
601                {
602                    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
603                                 _id,
604                                 "GetMaxAvailableFrameRate NOT SUPPORTED");
605                    if (avgTimePerFrame > 0)
606                        capability->maxFPS = static_cast<int> (10000000
607                                                               / avgTimePerFrame);
608                    else
609                        capability->maxFPS = 0;
610                }
611            }
612            else // use existing method in case IAMVideoControl is not supported
613            {
614                if (avgTimePerFrame > 0)
615                    capability->maxFPS = static_cast<int> (10000000
616                                                           / avgTimePerFrame);
617                else
618                    capability->maxFPS = 0;
619            }
620
621            // can't switch MEDIATYPE :~(
622            if (pmt->subtype == MEDIASUBTYPE_I420)
623            {
624                capability->rawType = kVideoI420;
625            }
626            else if (pmt->subtype == MEDIASUBTYPE_IYUV)
627            {
628                capability->rawType = kVideoIYUV;
629            }
630            else if (pmt->subtype == MEDIASUBTYPE_RGB24)
631            {
632                capability->rawType = kVideoRGB24;
633            }
634            else if (pmt->subtype == MEDIASUBTYPE_YUY2)
635            {
636                capability->rawType = kVideoYUY2;
637            }
638            else if (pmt->subtype == MEDIASUBTYPE_RGB565)
639            {
640                capability->rawType = kVideoRGB565;
641            }
642            else if (pmt->subtype == MEDIASUBTYPE_MJPG)
643            {
644                capability->rawType = kVideoMJPEG;
645            }
646            else if (pmt->subtype == MEDIASUBTYPE_dvsl
647                    || pmt->subtype == MEDIASUBTYPE_dvsd
648                    || pmt->subtype == MEDIASUBTYPE_dvhd) // If this is an external DV camera
649            {
650                capability->rawType = kVideoYUY2;// MS DV filter seems to create this type
651            }
652            else if (pmt->subtype == MEDIASUBTYPE_UYVY) // Seen used by Declink capture cards
653            {
654                capability->rawType = kVideoUYVY;
655            }
656            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
657            {
658                WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
659                             "Device support HDYC.");
660                capability->rawType = kVideoUYVY;
661            }
662            else
663            {
664                WCHAR strGuid[39];
665                StringFromGUID2(pmt->subtype, strGuid, 39);
666                WEBRTC_TRACE( webrtc::kTraceWarning,
667                             webrtc::kTraceVideoCapture, _id,
668                             "Device support unknown media type %ls, width %d, height %d",
669                             strGuid);
670                delete capability;
671                continue;
672            }
673
674            // Get the expected capture delay from the static list
675            capability->expectedCaptureDelay
676                            = GetExpectedCaptureDelay(WindowsCaptureDelays,
677                                                      NoWindowsCaptureDelays,
678                                                      productId,
679                                                      capability->width,
680                                                      capability->height);
681            _captureCapabilities.Insert(index++, capability);
682            WEBRTC_TRACE( webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
683                         "Camera capability, width:%d height:%d type:%d fps:%d",
684                         capability->width, capability->height,
685                         capability->rawType, capability->maxFPS);
686        }
687        DeleteMediaType(pmt);
688        pmt = NULL;
689    }
690    RELEASE_AND_CLEAR(streamConfig);
691    RELEASE_AND_CLEAR(videoControlConfig);
692    RELEASE_AND_CLEAR(outputCapturePin);
693    RELEASE_AND_CLEAR(captureDevice); // Release the capture device
694
695    // Store the new used device name
696    _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
697    _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
698                                                   _lastUsedDeviceNameLength
699                                                       + 1);
700    memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength+ 1);
701    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
702                 "CreateCapabilityMap %d", _captureCapabilities.Size());
703
704    return _captureCapabilities.Size();
705}
706
707/* Constructs a product ID from the Windows DevicePath. on a USB device the devicePath contains product id and vendor id.
708 This seems to work for firewire as well
709 /* Example of device path
710 "\\?\usb#vid_0408&pid_2010&mi_00#7&258e7aaf&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
711 "\\?\avc#sony&dv-vcr&camcorder&dv#65b2d50301460008#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
712 */
713void DeviceInfoDS::GetProductId(const char* devicePath,
714                                      char* productUniqueIdUTF8,
715                                      WebRtc_UWord32 productUniqueIdUTF8Length)
716{
717    *productUniqueIdUTF8 = '\0';
718    char* startPos = strstr((char*) devicePath, "\\\\?\\");
719    if (!startPos)
720    {
721        strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
722        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
723                     "Failed to get the product Id");
724        return;
725    }
726    startPos += 4;
727
728    char* pos = strchr(startPos, '&');
729    if (!pos || pos >= (char*) devicePath + strlen((char*) devicePath))
730    {
731        strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
732        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
733                     "Failed to get the product Id");
734        return;
735    }
736    // Find the second occurrence.
737    pos = strchr(pos + 1, '&');
738    WebRtc_UWord32 bytesToCopy = (WebRtc_UWord32)(pos - startPos);
739    if (pos && (bytesToCopy <= productUniqueIdUTF8Length) && bytesToCopy
740        <= kVideoCaptureProductIdLength)
741    {
742        strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length,
743                  (char*) startPos, bytesToCopy);
744    }
745    else
746    {
747        strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
748        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
749                     "Failed to get the product Id");
750    }
751}
752
753WebRtc_Word32 DeviceInfoDS::DisplayCaptureSettingsDialogBox(
754                                         const char* deviceUniqueIdUTF8,
755                                         const char* dialogTitleUTF8,
756                                         void* parentWindow,
757                                         WebRtc_UWord32 positionX,
758                                         WebRtc_UWord32 positionY)
759{
760    ReadLockScoped cs(_apiLock);
761    HWND window = (HWND) parentWindow;
762
763    IBaseFilter* filter = GetDeviceFilter(deviceUniqueIdUTF8, NULL, 0);
764    if (!filter)
765        return -1;
766
767    ISpecifyPropertyPages* pPages = NULL;
768    CAUUID uuid;
769    HRESULT hr = S_OK;
770
771    hr = filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID*) &pPages);
772    if (!SUCCEEDED(hr))
773    {
774        filter->Release();
775        return -1;
776    }
777    hr = pPages->GetPages(&uuid);
778    if (!SUCCEEDED(hr))
779    {
780        filter->Release();
781        return -1;
782    }
783
784    WCHAR tempDialogTitleWide[256];
785    tempDialogTitleWide[0] = 0;
786    int size = 255;
787
788    // UTF-8 to wide char
789    MultiByteToWideChar(CP_UTF8, 0, (char*) dialogTitleUTF8, -1,
790                        tempDialogTitleWide, size);
791
792    // Invoke a dialog box to display.
793
794    hr = OleCreatePropertyFrame(window, // You must create the parent window.
795                                positionX, // Horizontal position for the dialog box.
796                                positionY, // Vertical position for the dialog box.
797                                tempDialogTitleWide,// String used for the dialog box caption.
798                                1, // Number of pointers passed in pPlugin.
799                                (LPUNKNOWN*) &filter, // Pointer to the filter.
800                                uuid.cElems, // Number of property pages.
801                                uuid.pElems, // Array of property page CLSIDs.
802                                LOCALE_USER_DEFAULT, // Locale ID for the dialog box.
803                                0, NULL); // Reserved
804    // Release memory.
805    if (uuid.pElems)
806    {
807        CoTaskMemFree(uuid.pElems);
808    }
809    filter->Release();
810    return 0;
811}
812} // namespace videocapturemodule
813} // namespace webrtc
814