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