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