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