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
Create(const int32_t id)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 
DeviceInfoDS(const int32_t id)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 
~DeviceInfoDS()101 DeviceInfoDS::~DeviceInfoDS()
102 {
103     RELEASE_AND_CLEAR(_dsMonikerDevEnum);
104     RELEASE_AND_CLEAR(_dsDevEnum);
105     if (_CoUninitializeIsRequired)
106     {
107         CoUninitialize();
108     }
109 }
110 
Init()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 }
NumberOfDevices()123 uint32_t DeviceInfoDS::NumberOfDevices()
124 {
125     ReadLockScoped cs(_apiLock);
126     return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0);
127 }
128 
GetDeviceName(uint32_t deviceNumber,char * deviceNameUTF8,uint32_t deviceNameLength,char * deviceUniqueIdUTF8,uint32_t deviceUniqueIdUTF8Length,char * productUniqueIdUTF8,uint32_t productUniqueIdUTF8Length)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 
GetDeviceInfo(uint32_t deviceNumber,char * deviceNameUTF8,uint32_t deviceNameLength,char * deviceUniqueIdUTF8,uint32_t deviceUniqueIdUTF8Length,char * productUniqueIdUTF8,uint32_t productUniqueIdUTF8Length)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 
GetDeviceFilter(const char * deviceUniqueIdUTF8,char * productUniqueIdUTF8,uint32_t productUniqueIdUTF8Length)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 
GetWindowsCapability(const int32_t capabilityIndex,VideoCaptureCapabilityWindows & windowsCapability)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 
CreateCapabilityMap(const char * deviceUniqueIdUTF8)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  */
GetProductId(const char * devicePath,char * productUniqueIdUTF8,uint32_t productUniqueIdUTF8Length)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 
DisplayCaptureSettingsDialogBox(const char * deviceUniqueIdUTF8,const char * dialogTitleUTF8,void * parentWindow,uint32_t positionX,uint32_t positionY)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