blob: 22ff2fea51ceb926116a447bc27d020bd93bb519 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
xians@webrtc.org843c8c72012-02-20 08:45:02 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000011#include "webrtc/modules/video_capture/windows/device_info_ds.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020013#include <ios> // std::hex
14
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000015#include "webrtc/modules/video_capture/video_capture_config.h"
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000016#include "webrtc/modules/video_capture/windows/help_functions_ds.h"
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020017#include "webrtc/rtc_base/logging.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000018
niklase@google.com470e71d2011-07-07 08:21:25 +000019#include <Dvdmedia.h>
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000020#include <Streams.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22namespace webrtc
23{
perkj@webrtc.org0cc68dc2011-09-12 08:53:36 +000024namespace videocapturemodule
25{
tommi@webrtc.org951b6c42012-11-20 12:17:05 +000026
tommi@webrtc.org81878772012-11-20 13:35:33 +000027// static
nisseb29b9c82016-12-12 00:22:56 -080028DeviceInfoDS* DeviceInfoDS::Create()
tommi@webrtc.org81878772012-11-20 13:35:33 +000029{
nisseb29b9c82016-12-12 00:22:56 -080030 DeviceInfoDS* dsInfo = new DeviceInfoDS();
niklase@google.com470e71d2011-07-07 08:21:25 +000031 if (!dsInfo || dsInfo->Init() != 0)
32 {
wu@webrtc.org77d7d542011-10-12 16:57:53 +000033 delete dsInfo;
niklase@google.com470e71d2011-07-07 08:21:25 +000034 dsInfo = NULL;
35 }
36 return dsInfo;
37}
38
nisseb29b9c82016-12-12 00:22:56 -080039DeviceInfoDS::DeviceInfoDS()
40 : _dsDevEnum(NULL), _dsMonikerDevEnum(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +000041 _CoUninitializeIsRequired(true)
42{
43 // 1) Initialize the COM library (make Windows load the DLLs).
44 //
45 // CoInitializeEx must be called at least once, and is usually called only once,
46 // for each thread that uses the COM library. Multiple calls to CoInitializeEx
47 // by the same thread are allowed as long as they pass the same concurrency flag,
48 // but subsequent valid calls return S_FALSE.
49 // To close the COM library gracefully on a thread, each successful call to
50 // CoInitializeEx, including any call that returns S_FALSE, must be balanced
51 // by a corresponding call to CoUninitialize.
52 //
53
54 /*Apartment-threading, while allowing for multiple threads of execution,
55 serializes all incoming calls by requiring that calls to methods of objects created by this thread always run on the same thread
56 the apartment/thread that created them. In addition, calls can arrive only at message-queue boundaries (i.e., only during a
57 PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serialization, it is not typically necessary to write concurrency control into
58 the code for the object, other than to avoid calls to PeekMessage and SendMessage during processing that must not be interrupted by other method
59 invocations or calls to other objects in the same apartment/thread.*/
60
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +000061 ///CoInitializeEx(NULL, COINIT_APARTMENTTHREADED ); //| COINIT_SPEED_OVER_MEMORY
niklase@google.com470e71d2011-07-07 08:21:25 +000062 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); // Use COINIT_MULTITHREADED since Voice Engine uses COINIT_MULTITHREADED
63 if (FAILED(hr))
64 {
65 // Avoid calling CoUninitialize() since CoInitializeEx() failed.
66 _CoUninitializeIsRequired = FALSE;
67
68 if (hr == RPC_E_CHANGED_MODE)
69 {
70 // Calling thread has already initialized COM to be used in a single-threaded
71 // apartment (STA). We are then prevented from using STA.
72 // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is set".
73 //
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020074 LOG(LS_INFO) << __FUNCTION__
75 << ": CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)"
76 << " => RPC_E_CHANGED_MODE, error 0x" << std::hex
77 << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +000078 }
79 }
80}
81
tommi@webrtc.org81878772012-11-20 13:35:33 +000082DeviceInfoDS::~DeviceInfoDS()
niklase@google.com470e71d2011-07-07 08:21:25 +000083{
84 RELEASE_AND_CLEAR(_dsMonikerDevEnum);
85 RELEASE_AND_CLEAR(_dsDevEnum);
86 if (_CoUninitializeIsRequired)
87 {
88 CoUninitialize();
89 }
90}
91
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +000092int32_t DeviceInfoDS::Init()
niklase@google.com470e71d2011-07-07 08:21:25 +000093{
94 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
95 IID_ICreateDevEnum, (void **) &_dsDevEnum);
96 if (hr != NOERROR)
97 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +020098 LOG(LS_INFO) << "Failed to create CLSID_SystemDeviceEnum, error 0x"
99 << std::hex << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000100 return -1;
101 }
102 return 0;
103}
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000104uint32_t DeviceInfoDS::NumberOfDevices()
niklase@google.com470e71d2011-07-07 08:21:25 +0000105{
106 ReadLockScoped cs(_apiLock);
niklase@google.com470e71d2011-07-07 08:21:25 +0000107 return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0);
tommi@webrtc.org951b6c42012-11-20 12:17:05 +0000108}
tommi@webrtc.org81878772012-11-20 13:35:33 +0000109
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000110int32_t DeviceInfoDS::GetDeviceName(
111 uint32_t deviceNumber,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000112 char* deviceNameUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000113 uint32_t deviceNameLength,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000114 char* deviceUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000115 uint32_t deviceUniqueIdUTF8Length,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000116 char* productUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000117 uint32_t productUniqueIdUTF8Length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000118{
niklase@google.com470e71d2011-07-07 08:21:25 +0000119 ReadLockScoped cs(_apiLock);
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000120 const int32_t result = GetDeviceInfo(deviceNumber, deviceNameUTF8,
121 deviceNameLength,
122 deviceUniqueIdUTF8,
123 deviceUniqueIdUTF8Length,
124 productUniqueIdUTF8,
125 productUniqueIdUTF8Length);
126 return result > (int32_t) deviceNumber ? 0 : -1;
niklase@google.com470e71d2011-07-07 08:21:25 +0000127}
128
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000129int32_t DeviceInfoDS::GetDeviceInfo(
130 uint32_t deviceNumber,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000131 char* deviceNameUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000132 uint32_t deviceNameLength,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000133 char* deviceUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000134 uint32_t deviceUniqueIdUTF8Length,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000135 char* productUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000136 uint32_t productUniqueIdUTF8Length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000137
138{
139
140 // enumerate all video capture devices
141 RELEASE_AND_CLEAR(_dsMonikerDevEnum);
142 HRESULT hr =
143 _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
144 &_dsMonikerDevEnum, 0);
145 if (hr != NOERROR)
146 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200147 LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"
148 << std::hex << hr << ". No webcam exist?";
niklase@google.com470e71d2011-07-07 08:21:25 +0000149 return 0;
150 }
151
152 _dsMonikerDevEnum->Reset();
153 ULONG cFetched;
154 IMoniker *pM;
155 int index = 0;
156 while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched))
157 {
158 IPropertyBag *pBag;
159 hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **) &pBag);
160 if (S_OK == hr)
161 {
162 // Find the description or friendly name.
163 VARIANT varName;
164 VariantInit(&varName);
165 hr = pBag->Read(L"Description", &varName, 0);
166 if (FAILED(hr))
167 {
168 hr = pBag->Read(L"FriendlyName", &varName, 0);
169 }
170 if (SUCCEEDED(hr))
171 {
172 // ignore all VFW drivers
173 if ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) &&
174 (_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"),21)
175 != 0))
176 {
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000177 // Found a valid device.
178 if (index == static_cast<int>(deviceNumber))
niklase@google.com470e71d2011-07-07 08:21:25 +0000179 {
180 int convResult = 0;
181 if (deviceNameLength > 0)
182 {
183 convResult = WideCharToMultiByte(CP_UTF8, 0,
184 varName.bstrVal, -1,
185 (char*) deviceNameUTF8,
186 deviceNameLength, NULL,
187 NULL);
188 if (convResult == 0)
189 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200190 LOG(LS_INFO)
191 << "Failed to convert device name to UTF8, "
192 << "error = " << GetLastError();
niklase@google.com470e71d2011-07-07 08:21:25 +0000193 return -1;
194 }
195 }
196 if (deviceUniqueIdUTF8Length > 0)
197 {
198 hr = pBag->Read(L"DevicePath", &varName, 0);
199 if (FAILED(hr))
200 {
201 strncpy_s((char *) deviceUniqueIdUTF8,
202 deviceUniqueIdUTF8Length,
203 (char *) deviceNameUTF8, convResult);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200204 LOG(LS_INFO) << "Failed to get "
205 << "deviceUniqueIdUTF8 using "
206 << "deviceNameUTF8";
niklase@google.com470e71d2011-07-07 08:21:25 +0000207 }
208 else
209 {
210 convResult = WideCharToMultiByte(
211 CP_UTF8,
212 0,
213 varName.bstrVal,
214 -1,
215 (char*) deviceUniqueIdUTF8,
216 deviceUniqueIdUTF8Length,
217 NULL, NULL);
218 if (convResult == 0)
219 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200220 LOG(LS_INFO) << "Failed to convert device "
221 << "name to UTF8, error = "
222 << GetLastError();
niklase@google.com470e71d2011-07-07 08:21:25 +0000223 return -1;
224 }
225 if (productUniqueIdUTF8
226 && productUniqueIdUTF8Length > 0)
227 {
228 GetProductId(deviceUniqueIdUTF8,
229 productUniqueIdUTF8,
230 productUniqueIdUTF8Length);
231 }
232 }
233 }
234
235 }
236 ++index; // increase the number of valid devices
237 }
238 }
239 VariantClear(&varName);
240 pBag->Release();
241 pM->Release();
242 }
243
244 }
245 if (deviceNameLength)
246 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200247 LOG(LS_INFO) << __FUNCTION__ << " " << deviceNameUTF8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000248 }
249 return index;
250}
251
tommi@webrtc.org81878772012-11-20 13:35:33 +0000252IBaseFilter * DeviceInfoDS::GetDeviceFilter(
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000253 const char* deviceUniqueIdUTF8,
254 char* productUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000255 uint32_t productUniqueIdUTF8Length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000256{
257
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000258 const int32_t deviceUniqueIdUTF8Length =
259 (int32_t) strlen((char*) deviceUniqueIdUTF8); // UTF8 is also NULL terminated
niklase@google.com470e71d2011-07-07 08:21:25 +0000260 if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
261 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200262 LOG(LS_INFO) << "Device name too long";
niklase@google.com470e71d2011-07-07 08:21:25 +0000263 return NULL;
264 }
265
266 // enumerate all video capture devices
267 RELEASE_AND_CLEAR(_dsMonikerDevEnum);
268 HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
269 &_dsMonikerDevEnum, 0);
270 if (hr != NOERROR)
271 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200272 LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"
273 << std::hex << hr << ". No webcam exist?";
niklase@google.com470e71d2011-07-07 08:21:25 +0000274 return 0;
275 }
276 _dsMonikerDevEnum->Reset();
277 ULONG cFetched;
278 IMoniker *pM;
279
280 IBaseFilter *captureFilter = NULL;
281 bool deviceFound = false;
282 while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound)
283 {
284 IPropertyBag *pBag;
285 hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **) &pBag);
286 if (S_OK == hr)
287 {
288 // Find the description or friendly name.
289 VARIANT varName;
290 VariantInit(&varName);
291 if (deviceUniqueIdUTF8Length > 0)
292 {
293 hr = pBag->Read(L"DevicePath", &varName, 0);
294 if (FAILED(hr))
295 {
296 hr = pBag->Read(L"Description", &varName, 0);
297 if (FAILED(hr))
298 {
299 hr = pBag->Read(L"FriendlyName", &varName, 0);
300 }
301 }
302 if (SUCCEEDED(hr))
303 {
304 char tempDevicePathUTF8[256];
305 tempDevicePathUTF8[0] = 0;
andrew@webrtc.orge713fd02012-04-10 07:13:46 +0000306 WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1,
307 tempDevicePathUTF8,
308 sizeof(tempDevicePathUTF8), NULL,
309 NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000310 if (strncmp(tempDevicePathUTF8,
311 (const char*) deviceUniqueIdUTF8,
312 deviceUniqueIdUTF8Length) == 0)
313 {
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000314 // We have found the requested device
niklase@google.com470e71d2011-07-07 08:21:25 +0000315 deviceFound = true;
316 hr = pM->BindToObject(0, 0, IID_IBaseFilter,
317 (void**) &captureFilter);
318 if FAILED(hr)
319 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200320 LOG(LS_ERROR) << "Failed to bind to the selected "
321 << "capture device " << hr;
niklase@google.com470e71d2011-07-07 08:21:25 +0000322 }
323
324 if (productUniqueIdUTF8
325 && productUniqueIdUTF8Length > 0) // Get the device name
326 {
327
328 GetProductId(deviceUniqueIdUTF8,
329 productUniqueIdUTF8,
330 productUniqueIdUTF8Length);
331 }
332
333 }
334 }
335 }
336 VariantClear(&varName);
337 pBag->Release();
338 pM->Release();
339 }
340 }
341 return captureFilter;
342}
343
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000344int32_t DeviceInfoDS::GetWindowsCapability(
pbos@webrtc.org4ca7d3f2013-08-12 19:51:57 +0000345 const int32_t capabilityIndex,
346 VideoCaptureCapabilityWindows& windowsCapability) {
347 ReadLockScoped cs(_apiLock);
niklase@google.com470e71d2011-07-07 08:21:25 +0000348
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000349 if (capabilityIndex < 0 || static_cast<size_t>(capabilityIndex) >=
350 _captureCapabilitiesWindows.size()) {
pbos@webrtc.org4ca7d3f2013-08-12 19:51:57 +0000351 return -1;
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000352 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000353
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000354 windowsCapability = _captureCapabilitiesWindows[capabilityIndex];
pbos@webrtc.org4ca7d3f2013-08-12 19:51:57 +0000355 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000356}
357
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000358int32_t DeviceInfoDS::CreateCapabilityMap(
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000359 const char* deviceUniqueIdUTF8)
niklase@google.com470e71d2011-07-07 08:21:25 +0000360
361{
362 // Reset old capability list
pbos@webrtc.org4ca7d3f2013-08-12 19:51:57 +0000363 _captureCapabilities.clear();
364
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000365 const int32_t deviceUniqueIdUTF8Length =
366 (int32_t) strlen((char*) deviceUniqueIdUTF8);
niklase@google.com470e71d2011-07-07 08:21:25 +0000367 if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
368 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200369 LOG(LS_INFO) << "Device name too long";
niklase@google.com470e71d2011-07-07 08:21:25 +0000370 return -1;
371 }
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200372 LOG(LS_INFO) << "CreateCapabilityMap called for device "
373 << deviceUniqueIdUTF8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000374
375
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000376 char productId[kVideoCaptureProductIdLength];
tommi@webrtc.org81878772012-11-20 13:35:33 +0000377 IBaseFilter* captureDevice = DeviceInfoDS::GetDeviceFilter(
niklase@google.com470e71d2011-07-07 08:21:25 +0000378 deviceUniqueIdUTF8,
379 productId,
380 kVideoCaptureProductIdLength);
381 if (!captureDevice)
382 return -1;
tommi@webrtc.org81878772012-11-20 13:35:33 +0000383 IPin* outputCapturePin = GetOutputPin(captureDevice, GUID_NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000384 if (!outputCapturePin)
385 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200386 LOG(LS_INFO) << "Failed to get capture device output pin";
niklase@google.com470e71d2011-07-07 08:21:25 +0000387 RELEASE_AND_CLEAR(captureDevice);
388 return -1;
389 }
390 IAMExtDevice* extDevice = NULL;
391 HRESULT hr = captureDevice->QueryInterface(IID_IAMExtDevice,
392 (void **) &extDevice);
393 if (SUCCEEDED(hr) && extDevice)
394 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200395 LOG(LS_INFO) << "This is an external device";
niklase@google.com470e71d2011-07-07 08:21:25 +0000396 extDevice->Release();
397 }
398
399 IAMStreamConfig* streamConfig = NULL;
400 hr = outputCapturePin->QueryInterface(IID_IAMStreamConfig,
401 (void**) &streamConfig);
402 if (FAILED(hr))
403 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200404 LOG(LS_INFO) << "Failed to get IID_IAMStreamConfig interface "
405 <<"from capture device";
niklase@google.com470e71d2011-07-07 08:21:25 +0000406 return -1;
407 }
408
409 // this gets the FPS
410 IAMVideoControl* videoControlConfig = NULL;
411 HRESULT hrVC = captureDevice->QueryInterface(IID_IAMVideoControl,
412 (void**) &videoControlConfig);
413 if (FAILED(hrVC))
414 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200415 LOG(LS_INFO) << "IID_IAMVideoControl Interface NOT SUPPORTED";
niklase@google.com470e71d2011-07-07 08:21:25 +0000416 }
417
418 AM_MEDIA_TYPE *pmt = NULL;
419 VIDEO_STREAM_CONFIG_CAPS caps;
420 int count, size;
421
422 hr = streamConfig->GetNumberOfCapabilities(&count, &size);
423 if (FAILED(hr))
424 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200425 LOG(LS_INFO) << "Failed to GetNumberOfCapabilities";
niklase@google.com470e71d2011-07-07 08:21:25 +0000426 RELEASE_AND_CLEAR(videoControlConfig);
427 RELEASE_AND_CLEAR(streamConfig);
428 RELEASE_AND_CLEAR(outputCapturePin);
429 RELEASE_AND_CLEAR(captureDevice);
430 return -1;
431 }
432
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000433 // Check if the device support formattype == FORMAT_VideoInfo2 and FORMAT_VideoInfo.
niklase@google.com470e71d2011-07-07 08:21:25 +0000434 // Prefer FORMAT_VideoInfo since some cameras (ZureCam) has been seen having problem with MJPEG and FORMAT_VideoInfo2
435 // Interlace flag is only supported in FORMAT_VideoInfo2
436 bool supportFORMAT_VideoInfo2 = false;
437 bool supportFORMAT_VideoInfo = false;
438 bool foundInterlacedFormat = false;
439 GUID preferedVideoFormat = FORMAT_VideoInfo;
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000440 for (int32_t tmp = 0; tmp < count; ++tmp)
niklase@google.com470e71d2011-07-07 08:21:25 +0000441 {
442 hr = streamConfig->GetStreamCaps(tmp, &pmt,
443 reinterpret_cast<BYTE*> (&caps));
444 if (!FAILED(hr))
445 {
446 if (pmt->majortype == MEDIATYPE_Video
447 && pmt->formattype == FORMAT_VideoInfo2)
448 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200449 LOG(LS_INFO) << "Device support FORMAT_VideoInfo2";
niklase@google.com470e71d2011-07-07 08:21:25 +0000450 supportFORMAT_VideoInfo2 = true;
451 VIDEOINFOHEADER2* h =
452 reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
453 assert(h);
454 foundInterlacedFormat |= h->dwInterlaceFlags
455 & (AMINTERLACE_IsInterlaced
456 | AMINTERLACE_DisplayModeBobOnly);
457 }
458 if (pmt->majortype == MEDIATYPE_Video
459 && pmt->formattype == FORMAT_VideoInfo)
460 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200461 LOG(LS_INFO) << "Device support FORMAT_VideoInfo2";
niklase@google.com470e71d2011-07-07 08:21:25 +0000462 supportFORMAT_VideoInfo = true;
463 }
464 }
465 }
466 if (supportFORMAT_VideoInfo2)
467 {
468 if (supportFORMAT_VideoInfo && !foundInterlacedFormat)
469 {
470 preferedVideoFormat = FORMAT_VideoInfo;
471 }
472 else
473 {
474 preferedVideoFormat = FORMAT_VideoInfo2;
475 }
476 }
477
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000478 for (int32_t tmp = 0; tmp < count; ++tmp)
niklase@google.com470e71d2011-07-07 08:21:25 +0000479 {
480 hr = streamConfig->GetStreamCaps(tmp, &pmt,
481 reinterpret_cast<BYTE*> (&caps));
482 if (FAILED(hr))
483 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200484 LOG(LS_INFO) << "Failed to GetStreamCaps";
niklase@google.com470e71d2011-07-07 08:21:25 +0000485 RELEASE_AND_CLEAR(videoControlConfig);
486 RELEASE_AND_CLEAR(streamConfig);
487 RELEASE_AND_CLEAR(outputCapturePin);
488 RELEASE_AND_CLEAR(captureDevice);
489 return -1;
490 }
491
492 if (pmt->majortype == MEDIATYPE_Video
493 && pmt->formattype == preferedVideoFormat)
494 {
495
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000496 VideoCaptureCapabilityWindows capability;
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000497 int64_t avgTimePerFrame = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000498
499 if (pmt->formattype == FORMAT_VideoInfo)
500 {
501 VIDEOINFOHEADER* h =
502 reinterpret_cast<VIDEOINFOHEADER*> (pmt->pbFormat);
503 assert(h);
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000504 capability.directShowCapabilityIndex = tmp;
505 capability.width = h->bmiHeader.biWidth;
506 capability.height = h->bmiHeader.biHeight;
niklase@google.com470e71d2011-07-07 08:21:25 +0000507 avgTimePerFrame = h->AvgTimePerFrame;
508 }
509 if (pmt->formattype == FORMAT_VideoInfo2)
510 {
511 VIDEOINFOHEADER2* h =
512 reinterpret_cast<VIDEOINFOHEADER2*> (pmt->pbFormat);
513 assert(h);
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000514 capability.directShowCapabilityIndex = tmp;
515 capability.width = h->bmiHeader.biWidth;
516 capability.height = h->bmiHeader.biHeight;
517 capability.interlaced = h->dwInterlaceFlags
niklase@google.com470e71d2011-07-07 08:21:25 +0000518 & (AMINTERLACE_IsInterlaced
519 | AMINTERLACE_DisplayModeBobOnly);
520 avgTimePerFrame = h->AvgTimePerFrame;
521 }
522
523 if (hrVC == S_OK)
524 {
vikasmarwaha@webrtc.orgbf3a9b32013-02-21 20:25:54 +0000525 LONGLONG *frameDurationList;
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000526 LONGLONG maxFPS;
niklase@google.com470e71d2011-07-07 08:21:25 +0000527 long listSize;
528 SIZE size;
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000529 size.cx = capability.width;
530 size.cy = capability.height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000531
532 // GetMaxAvailableFrameRate doesn't return max frame rate always
533 // eg: Logitech Notebook. This may be due to a bug in that API
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000534 // because GetFrameRateList array is reversed in the above camera. So
niklase@google.com470e71d2011-07-07 08:21:25 +0000535 // a util method written. Can't assume the first value will return
536 // the max fps.
537 hrVC = videoControlConfig->GetFrameRateList(outputCapturePin,
538 tmp, size,
539 &listSize,
vikasmarwaha@webrtc.orgbf3a9b32013-02-21 20:25:54 +0000540 &frameDurationList);
niklase@google.com470e71d2011-07-07 08:21:25 +0000541
vikasmarwaha@webrtc.orgbf3a9b32013-02-21 20:25:54 +0000542 // On some odd cameras, you may get a 0 for duration.
543 // GetMaxOfFrameArray returns the lowest duration (highest FPS)
544 if (hrVC == S_OK && listSize > 0 &&
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000545 0 != (maxFPS = GetMaxOfFrameArray(frameDurationList,
vikasmarwaha@webrtc.orgbf3a9b32013-02-21 20:25:54 +0000546 listSize)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000547 {
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000548 capability.maxFPS = static_cast<int> (10000000
niklase@google.com470e71d2011-07-07 08:21:25 +0000549 / maxFPS);
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000550 capability.supportFrameRateControl = true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000551 }
552 else // use existing method
553 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200554 LOG(LS_INFO) << "GetMaxAvailableFrameRate NOT SUPPORTED";
niklase@google.com470e71d2011-07-07 08:21:25 +0000555 if (avgTimePerFrame > 0)
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000556 capability.maxFPS = static_cast<int> (10000000
niklase@google.com470e71d2011-07-07 08:21:25 +0000557 / avgTimePerFrame);
558 else
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000559 capability.maxFPS = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000560 }
561 }
562 else // use existing method in case IAMVideoControl is not supported
563 {
564 if (avgTimePerFrame > 0)
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000565 capability.maxFPS = static_cast<int> (10000000
niklase@google.com470e71d2011-07-07 08:21:25 +0000566 / avgTimePerFrame);
567 else
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000568 capability.maxFPS = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000569 }
570
571 // can't switch MEDIATYPE :~(
572 if (pmt->subtype == MEDIASUBTYPE_I420)
573 {
nisseeb44b392017-04-28 07:18:05 -0700574 capability.videoType = VideoType::kI420;
niklase@google.com470e71d2011-07-07 08:21:25 +0000575 }
576 else if (pmt->subtype == MEDIASUBTYPE_IYUV)
577 {
nisseeb44b392017-04-28 07:18:05 -0700578 capability.videoType = VideoType::kIYUV;
niklase@google.com470e71d2011-07-07 08:21:25 +0000579 }
580 else if (pmt->subtype == MEDIASUBTYPE_RGB24)
581 {
nisseeb44b392017-04-28 07:18:05 -0700582 capability.videoType = VideoType::kRGB24;
niklase@google.com470e71d2011-07-07 08:21:25 +0000583 }
584 else if (pmt->subtype == MEDIASUBTYPE_YUY2)
585 {
nisseeb44b392017-04-28 07:18:05 -0700586 capability.videoType = VideoType::kYUY2;
niklase@google.com470e71d2011-07-07 08:21:25 +0000587 }
588 else if (pmt->subtype == MEDIASUBTYPE_RGB565)
589 {
nisseeb44b392017-04-28 07:18:05 -0700590 capability.videoType = VideoType::kRGB565;
niklase@google.com470e71d2011-07-07 08:21:25 +0000591 }
592 else if (pmt->subtype == MEDIASUBTYPE_MJPG)
593 {
nisseeb44b392017-04-28 07:18:05 -0700594 capability.videoType = VideoType::kMJPEG;
niklase@google.com470e71d2011-07-07 08:21:25 +0000595 }
596 else if (pmt->subtype == MEDIASUBTYPE_dvsl
597 || pmt->subtype == MEDIASUBTYPE_dvsd
598 || pmt->subtype == MEDIASUBTYPE_dvhd) // If this is an external DV camera
599 {
nisseeb44b392017-04-28 07:18:05 -0700600 capability.videoType =
601 VideoType::kYUY2; // MS DV filter seems to create this type
niklase@google.com470e71d2011-07-07 08:21:25 +0000602 }
603 else if (pmt->subtype == MEDIASUBTYPE_UYVY) // Seen used by Declink capture cards
604 {
nisseeb44b392017-04-28 07:18:05 -0700605 capability.videoType = VideoType::kUYVY;
niklase@google.com470e71d2011-07-07 08:21:25 +0000606 }
607 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
608 {
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200609 LOG(LS_INFO) << "Device support HDYC.";
nisseeb44b392017-04-28 07:18:05 -0700610 capability.videoType = VideoType::kUYVY;
niklase@google.com470e71d2011-07-07 08:21:25 +0000611 }
612 else
613 {
614 WCHAR strGuid[39];
615 StringFromGUID2(pmt->subtype, strGuid, 39);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200616 LOG(LS_WARNING) << "Device support unknown media type "
617 << strGuid << ", width " << capability.width
618 << ", height " << capability.height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000619 continue;
620 }
621
fischman@webrtc.org69fc3152013-09-25 17:01:42 +0000622 _captureCapabilities.push_back(capability);
623 _captureCapabilitiesWindows.push_back(capability);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200624 LOG(LS_INFO) << "Camera capability, width:" << capability.width
625 << " height:" << capability.height << " type:"
626 << static_cast<int>(capability.videoType) << " fps:"
627 << capability.maxFPS;
niklase@google.com470e71d2011-07-07 08:21:25 +0000628 }
629 DeleteMediaType(pmt);
630 pmt = NULL;
631 }
632 RELEASE_AND_CLEAR(streamConfig);
633 RELEASE_AND_CLEAR(videoControlConfig);
634 RELEASE_AND_CLEAR(outputCapturePin);
635 RELEASE_AND_CLEAR(captureDevice); // Release the capture device
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000636
niklase@google.com470e71d2011-07-07 08:21:25 +0000637 // Store the new used device name
638 _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000639 _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
niklase@google.com470e71d2011-07-07 08:21:25 +0000640 _lastUsedDeviceNameLength
641 + 1);
642 memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength+ 1);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200643 LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
niklase@google.com470e71d2011-07-07 08:21:25 +0000644
pbos@webrtc.org4ca7d3f2013-08-12 19:51:57 +0000645 return static_cast<int32_t>(_captureCapabilities.size());
niklase@google.com470e71d2011-07-07 08:21:25 +0000646}
647
henrika@webrtc.org5ba3dec2012-11-26 09:12:02 +0000648/* Constructs a product ID from the Windows DevicePath. on a USB device the devicePath contains product id and vendor id.
niklase@google.com470e71d2011-07-07 08:21:25 +0000649 This seems to work for firewire as well
650 /* Example of device path
651 "\\?\usb#vid_0408&pid_2010&mi_00#7&258e7aaf&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
652 "\\?\avc#sony&dv-vcr&camcorder&dv#65b2d50301460008#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
653 */
tommi@webrtc.org81878772012-11-20 13:35:33 +0000654void DeviceInfoDS::GetProductId(const char* devicePath,
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000655 char* productUniqueIdUTF8,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000656 uint32_t productUniqueIdUTF8Length)
niklase@google.com470e71d2011-07-07 08:21:25 +0000657{
658 *productUniqueIdUTF8 = '\0';
659 char* startPos = strstr((char*) devicePath, "\\\\?\\");
660 if (!startPos)
661 {
662 strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200663 LOG(LS_INFO) << "Failed to get the product Id";
niklase@google.com470e71d2011-07-07 08:21:25 +0000664 return;
665 }
666 startPos += 4;
667
668 char* pos = strchr(startPos, '&');
669 if (!pos || pos >= (char*) devicePath + strlen((char*) devicePath))
670 {
671 strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200672 LOG(LS_INFO) << "Failed to get the product Id";
niklase@google.com470e71d2011-07-07 08:21:25 +0000673 return;
674 }
tommi@webrtc.org12773ea2012-11-16 09:03:12 +0000675 // Find the second occurrence.
niklase@google.com470e71d2011-07-07 08:21:25 +0000676 pos = strchr(pos + 1, '&');
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000677 uint32_t bytesToCopy = (uint32_t)(pos - startPos);
niklase@google.com470e71d2011-07-07 08:21:25 +0000678 if (pos && (bytesToCopy <= productUniqueIdUTF8Length) && bytesToCopy
679 <= kVideoCaptureProductIdLength)
680 {
681 strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length,
682 (char*) startPos, bytesToCopy);
683 }
684 else
685 {
686 strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
Sam Zackrissondcbb66f2017-08-08 12:00:28 +0200687 LOG(LS_INFO) << "Failed to get the product Id";
niklase@google.com470e71d2011-07-07 08:21:25 +0000688 }
689}
690
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000691int32_t DeviceInfoDS::DisplayCaptureSettingsDialogBox(
leozwang@webrtc.org1745e932012-03-01 16:30:40 +0000692 const char* deviceUniqueIdUTF8,
693 const char* dialogTitleUTF8,
niklase@google.com470e71d2011-07-07 08:21:25 +0000694 void* parentWindow,
pbos@webrtc.orgdfc5bb92013-04-10 08:23:13 +0000695 uint32_t positionX,
696 uint32_t positionY)
niklase@google.com470e71d2011-07-07 08:21:25 +0000697{
698 ReadLockScoped cs(_apiLock);
699 HWND window = (HWND) parentWindow;
700
701 IBaseFilter* filter = GetDeviceFilter(deviceUniqueIdUTF8, NULL, 0);
702 if (!filter)
703 return -1;
704
705 ISpecifyPropertyPages* pPages = NULL;
706 CAUUID uuid;
707 HRESULT hr = S_OK;
708
709 hr = filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID*) &pPages);
710 if (!SUCCEEDED(hr))
711 {
712 filter->Release();
713 return -1;
714 }
715 hr = pPages->GetPages(&uuid);
716 if (!SUCCEEDED(hr))
717 {
718 filter->Release();
719 return -1;
720 }
721
722 WCHAR tempDialogTitleWide[256];
723 tempDialogTitleWide[0] = 0;
724 int size = 255;
725
726 // UTF-8 to wide char
727 MultiByteToWideChar(CP_UTF8, 0, (char*) dialogTitleUTF8, -1,
728 tempDialogTitleWide, size);
729
730 // Invoke a dialog box to display.
731
732 hr = OleCreatePropertyFrame(window, // You must create the parent window.
733 positionX, // Horizontal position for the dialog box.
734 positionY, // Vertical position for the dialog box.
735 tempDialogTitleWide,// String used for the dialog box caption.
736 1, // Number of pointers passed in pPlugin.
737 (LPUNKNOWN*) &filter, // Pointer to the filter.
738 uuid.cElems, // Number of property pages.
739 uuid.pElems, // Array of property page CLSIDs.
740 LOCALE_USER_DEFAULT, // Locale ID for the dialog box.
741 0, NULL); // Reserved
742 // Release memory.
743 if (uuid.pElems)
744 {
745 CoTaskMemFree(uuid.pElems);
746 }
747 filter->Release();
748 return 0;
749}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000750} // namespace videocapturemodule
751} // namespace webrtc