diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/modules/video_capture/windows | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/video_capture/windows')
9 files changed, 2651 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.cc b/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.cc new file mode 100644 index 0000000000..2b01fc6930 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.cc @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_capture/windows/device_info_ds.h" + +#include <dvdmedia.h> + +#include "modules/video_capture/video_capture_config.h" +#include "modules/video_capture/windows/help_functions_ds.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_utils.h" + +namespace webrtc { +namespace videocapturemodule { + +BOOL isVideoDevice(DEV_BROADCAST_HDR *pHdr) +{ + if (pHdr == NULL) { + return FALSE; + } + if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) { + return FALSE; + } + DEV_BROADCAST_DEVICEINTERFACE* pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr; + return pDi->dbcc_classguid == KSCATEGORY_VIDEO_CAMERA; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) +{ + DeviceInfoDS* pParent; + if (uiMsg == WM_CREATE) + { + pParent = (DeviceInfoDS*)((LPCREATESTRUCT)lParam)->lpCreateParams; + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pParent); + } + else if (uiMsg == WM_DESTROY) + { + SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL); + } + else if (uiMsg == WM_DEVICECHANGE) + { + pParent = (DeviceInfoDS*)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (pParent && isVideoDevice((PDEV_BROADCAST_HDR)lParam)) + { + pParent->DeviceChange(); + } + } + return DefWindowProc(hWnd, uiMsg, wParam, lParam); +} + +// static +DeviceInfoDS* DeviceInfoDS::Create() { + DeviceInfoDS* dsInfo = new DeviceInfoDS(); + if (!dsInfo || dsInfo->Init() != 0) { + delete dsInfo; + dsInfo = NULL; + } + return dsInfo; +} + +DeviceInfoDS::DeviceInfoDS() + : _dsDevEnum(NULL), + _dsMonikerDevEnum(NULL), + _CoUninitializeIsRequired(true), + _hdevnotify(NULL) { + // 1) Initialize the COM library (make Windows load the DLLs). + // + // CoInitializeEx must be called at least once, and is usually called only + // once, for each thread that uses the COM library. Multiple calls to + // CoInitializeEx by the same thread are allowed as long as they pass the same + // concurrency flag, but subsequent valid calls return S_FALSE. To close the + // COM library gracefully on a thread, each successful call to CoInitializeEx, + // including any call that returns S_FALSE, must be balanced by a + // corresponding call to CoUninitialize. + // + + /*Apartment-threading, while allowing for multiple threads of execution, + serializes all incoming calls by requiring that calls to methods of objects + created by this thread always run on the same thread the apartment/thread + that created them. In addition, calls can arrive only at message-queue + boundaries (i.e., only during a PeekMessage, SendMessage, DispatchMessage, + etc.). Because of this serialization, it is not typically necessary to write + concurrency control into the code for the object, other than to avoid calls + to PeekMessage and SendMessage during processing that must not be interrupted + by other method invocations or calls to other objects in the same + apartment/thread.*/ + + /// CoInitializeEx(NULL, COINIT_APARTMENTTHREADED ); //| + /// COINIT_SPEED_OVER_MEMORY + HRESULT hr = CoInitializeEx( + NULL, COINIT_MULTITHREADED); // Use COINIT_MULTITHREADED since Voice + // Engine uses COINIT_MULTITHREADED + if (FAILED(hr)) { + // Avoid calling CoUninitialize() since CoInitializeEx() failed. + _CoUninitializeIsRequired = FALSE; + + if (hr == RPC_E_CHANGED_MODE) { + // Calling thread has already initialized COM to be used in a + // single-threaded apartment (STA). We are then prevented from using STA. + // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is + // set". + // + RTC_DLOG(LS_INFO) << __FUNCTION__ + << ": CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)" + " => RPC_E_CHANGED_MODE, error 0x" + << rtc::ToHex(hr); + } + } + + _hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL)); + _wndClass = {0}; + _wndClass.lpfnWndProc = &WndProc; + _wndClass.lpszClassName = TEXT("DeviceInfoDS"); + _wndClass.hInstance = _hInstance; + + if (RegisterClass(&_wndClass)) { + _hwnd = CreateWindow(_wndClass.lpszClassName, NULL, 0, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, + NULL, _hInstance, this); + + DEV_BROADCAST_DEVICEINTERFACE di = { 0 }; + di.dbcc_size = sizeof(di); + di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + di.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA; + + _hdevnotify = RegisterDeviceNotification(_hwnd, &di, + DEVICE_NOTIFY_WINDOW_HANDLE); + } +} + +DeviceInfoDS::~DeviceInfoDS() { + RELEASE_AND_CLEAR(_dsMonikerDevEnum); + RELEASE_AND_CLEAR(_dsDevEnum); + if (_CoUninitializeIsRequired) { + CoUninitialize(); + } + if (_hdevnotify) + { + UnregisterDeviceNotification(_hdevnotify); + } + if (_hwnd != NULL) { + DestroyWindow(_hwnd); + } + UnregisterClass(_wndClass.lpszClassName, _hInstance); +} + +int32_t DeviceInfoDS::Init() { + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, + IID_ICreateDevEnum, (void**)&_dsDevEnum); + if (hr != NOERROR) { + RTC_LOG(LS_INFO) << "Failed to create CLSID_SystemDeviceEnum, error 0x" + << rtc::ToHex(hr); + return -1; + } + return 0; +} +uint32_t DeviceInfoDS::NumberOfDevices() { + MutexLock lock(&_apiLock); + return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0); +} + +int32_t DeviceInfoDS::GetDeviceName(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length, + pid_t* pid) { + MutexLock lock(&_apiLock); + const int32_t result = GetDeviceInfo( + deviceNumber, deviceNameUTF8, deviceNameLength, deviceUniqueIdUTF8, + deviceUniqueIdUTF8Length, productUniqueIdUTF8, productUniqueIdUTF8Length); + return result > (int32_t)deviceNumber ? 0 : -1; +} + +int32_t DeviceInfoDS::GetDeviceInfo(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length) + +{ + // enumerate all video capture devices + RELEASE_AND_CLEAR(_dsMonikerDevEnum); + HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, + &_dsMonikerDevEnum, 0); + if (hr != NOERROR) { + RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x" + << rtc::ToHex(hr) << ". No webcam exist?"; + return 0; + } + + _dsMonikerDevEnum->Reset(); + ULONG cFetched; + IMoniker* pM; + int index = 0; + while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched)) { + IPropertyBag* pBag; + hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag); + if (S_OK == hr) { + // Find the description or friendly name. + VARIANT varName; + VariantInit(&varName); + hr = pBag->Read(L"Description", &varName, 0); + if (FAILED(hr)) { + hr = pBag->Read(L"FriendlyName", &varName, 0); + } + if (SUCCEEDED(hr)) { + // ignore all VFW drivers + if ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) && + (_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"), 21) != 0)) { + // Found a valid device. + if (index == static_cast<int>(deviceNumber)) { + int convResult = 0; + if (deviceNameLength > 0) { + convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, + (char*)deviceNameUTF8, + deviceNameLength, NULL, NULL); + if (convResult == 0) { + RTC_LOG(LS_INFO) << "Failed to convert device name to UTF8, " + "error = " + << GetLastError(); + return -1; + } + } + if (deviceUniqueIdUTF8Length > 0) { + hr = pBag->Read(L"DevicePath", &varName, 0); + if (FAILED(hr)) { + strncpy_s((char*)deviceUniqueIdUTF8, deviceUniqueIdUTF8Length, + (char*)deviceNameUTF8, convResult); + RTC_LOG(LS_INFO) << "Failed to get " + "deviceUniqueIdUTF8 using " + "deviceNameUTF8"; + } else { + convResult = WideCharToMultiByte( + CP_UTF8, 0, varName.bstrVal, -1, (char*)deviceUniqueIdUTF8, + deviceUniqueIdUTF8Length, NULL, NULL); + if (convResult == 0) { + RTC_LOG(LS_INFO) << "Failed to convert device " + "name to UTF8, error = " + << GetLastError(); + return -1; + } + if (productUniqueIdUTF8 && productUniqueIdUTF8Length > 0) { + GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8, + productUniqueIdUTF8Length); + } + } + } + } + ++index; // increase the number of valid devices + } + } + VariantClear(&varName); + pBag->Release(); + pM->Release(); + } + } + if (deviceNameLength) { + RTC_DLOG(LS_INFO) << __FUNCTION__ << " " << deviceNameUTF8; + } + return index; +} + +IBaseFilter* DeviceInfoDS::GetDeviceFilter(const char* deviceUniqueIdUTF8, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length) { + const int32_t deviceUniqueIdUTF8Length = (int32_t)strlen( + (char*)deviceUniqueIdUTF8); // UTF8 is also NULL terminated + if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) { + RTC_LOG(LS_INFO) << "Device name too long"; + return NULL; + } + + // enumerate all video capture devices + RELEASE_AND_CLEAR(_dsMonikerDevEnum); + HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, + &_dsMonikerDevEnum, 0); + if (hr != NOERROR) { + RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x" + << rtc::ToHex(hr) << ". No webcam exist?"; + return 0; + } + _dsMonikerDevEnum->Reset(); + ULONG cFetched; + IMoniker* pM; + + IBaseFilter* captureFilter = NULL; + bool deviceFound = false; + while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) { + IPropertyBag* pBag; + hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag); + if (S_OK == hr) { + // Find the description or friendly name. + VARIANT varName; + VariantInit(&varName); + if (deviceUniqueIdUTF8Length > 0) { + hr = pBag->Read(L"DevicePath", &varName, 0); + if (FAILED(hr)) { + hr = pBag->Read(L"Description", &varName, 0); + if (FAILED(hr)) { + hr = pBag->Read(L"FriendlyName", &varName, 0); + } + } + if (SUCCEEDED(hr)) { + char tempDevicePathUTF8[256]; + tempDevicePathUTF8[0] = 0; + WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, + tempDevicePathUTF8, sizeof(tempDevicePathUTF8), + NULL, NULL); + if (strncmp(tempDevicePathUTF8, (const char*)deviceUniqueIdUTF8, + deviceUniqueIdUTF8Length) == 0) { + // We have found the requested device + deviceFound = true; + hr = + pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter); + if + FAILED(hr) { + RTC_LOG(LS_ERROR) << "Failed to bind to the selected " + "capture device " + << hr; + } + + if (productUniqueIdUTF8 && + productUniqueIdUTF8Length > 0) // Get the device name + { + GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8, + productUniqueIdUTF8Length); + } + } + } + } + VariantClear(&varName); + pBag->Release(); + } + pM->Release(); + } + return captureFilter; +} + +int32_t DeviceInfoDS::GetWindowsCapability( + const int32_t capabilityIndex, + VideoCaptureCapabilityWindows& windowsCapability) { + MutexLock lock(&_apiLock); + + if (capabilityIndex < 0 || static_cast<size_t>(capabilityIndex) >= + _captureCapabilitiesWindows.size()) { + return -1; + } + + windowsCapability = _captureCapabilitiesWindows[capabilityIndex]; + return 0; +} + +int32_t DeviceInfoDS::CreateCapabilityMap(const char* deviceUniqueIdUTF8) + +{ + // Reset old capability list + _captureCapabilities.clear(); + + const int32_t deviceUniqueIdUTF8Length = + (int32_t)strlen((char*)deviceUniqueIdUTF8); + if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) { + RTC_LOG(LS_INFO) << "Device name too long"; + return -1; + } + RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device " + << deviceUniqueIdUTF8; + + char productId[kVideoCaptureProductIdLength]; + IBaseFilter* captureDevice = DeviceInfoDS::GetDeviceFilter( + deviceUniqueIdUTF8, productId, kVideoCaptureProductIdLength); + if (!captureDevice) + return -1; + IPin* outputCapturePin = GetOutputPin(captureDevice, GUID_NULL); + if (!outputCapturePin) { + RTC_LOG(LS_INFO) << "Failed to get capture device output pin"; + RELEASE_AND_CLEAR(captureDevice); + return -1; + } + IAMExtDevice* extDevice = NULL; + HRESULT hr = + captureDevice->QueryInterface(IID_IAMExtDevice, (void**)&extDevice); + if (SUCCEEDED(hr) && extDevice) { + RTC_LOG(LS_INFO) << "This is an external device"; + extDevice->Release(); + } + + IAMStreamConfig* streamConfig = NULL; + hr = outputCapturePin->QueryInterface(IID_IAMStreamConfig, + (void**)&streamConfig); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to get IID_IAMStreamConfig interface " + "from capture device"; + return -1; + } + + // this gets the FPS + IAMVideoControl* videoControlConfig = NULL; + HRESULT hrVC = captureDevice->QueryInterface(IID_IAMVideoControl, + (void**)&videoControlConfig); + if (FAILED(hrVC)) { + RTC_LOG(LS_INFO) << "IID_IAMVideoControl Interface NOT SUPPORTED"; + } + + AM_MEDIA_TYPE* pmt = NULL; + VIDEO_STREAM_CONFIG_CAPS caps; + int count, size; + + hr = streamConfig->GetNumberOfCapabilities(&count, &size); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to GetNumberOfCapabilities"; + RELEASE_AND_CLEAR(videoControlConfig); + RELEASE_AND_CLEAR(streamConfig); + RELEASE_AND_CLEAR(outputCapturePin); + RELEASE_AND_CLEAR(captureDevice); + return -1; + } + + // Check if the device support formattype == FORMAT_VideoInfo2 and + // FORMAT_VideoInfo. Prefer FORMAT_VideoInfo since some cameras (ZureCam) has + // been seen having problem with MJPEG and FORMAT_VideoInfo2 Interlace flag is + // only supported in FORMAT_VideoInfo2 + bool supportFORMAT_VideoInfo2 = false; + bool supportFORMAT_VideoInfo = false; + bool foundInterlacedFormat = false; + GUID preferedVideoFormat = FORMAT_VideoInfo; + for (int32_t tmp = 0; tmp < count; ++tmp) { + hr = streamConfig->GetStreamCaps(tmp, &pmt, reinterpret_cast<BYTE*>(&caps)); + if (hr == S_OK) { + if (pmt->majortype == MEDIATYPE_Video && + pmt->formattype == FORMAT_VideoInfo2) { + RTC_LOG(LS_INFO) << "Device support FORMAT_VideoInfo2"; + supportFORMAT_VideoInfo2 = true; + VIDEOINFOHEADER2* h = + reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat); + RTC_DCHECK(h); + foundInterlacedFormat |= + h->dwInterlaceFlags & + (AMINTERLACE_IsInterlaced | AMINTERLACE_DisplayModeBobOnly); + } + if (pmt->majortype == MEDIATYPE_Video && + pmt->formattype == FORMAT_VideoInfo) { + RTC_LOG(LS_INFO) << "Device support FORMAT_VideoInfo2"; + supportFORMAT_VideoInfo = true; + } + + FreeMediaType(pmt); + pmt = NULL; + } + } + if (supportFORMAT_VideoInfo2) { + if (supportFORMAT_VideoInfo && !foundInterlacedFormat) { + preferedVideoFormat = FORMAT_VideoInfo; + } else { + preferedVideoFormat = FORMAT_VideoInfo2; + } + } + + for (int32_t tmp = 0; tmp < count; ++tmp) { + hr = streamConfig->GetStreamCaps(tmp, &pmt, reinterpret_cast<BYTE*>(&caps)); + if (hr != S_OK) { + RTC_LOG(LS_INFO) << "Failed to GetStreamCaps"; + RELEASE_AND_CLEAR(videoControlConfig); + RELEASE_AND_CLEAR(streamConfig); + RELEASE_AND_CLEAR(outputCapturePin); + RELEASE_AND_CLEAR(captureDevice); + return -1; + } + + if (pmt->majortype == MEDIATYPE_Video && + pmt->formattype == preferedVideoFormat) { + VideoCaptureCapabilityWindows capability; + int64_t avgTimePerFrame = 0; + + if (pmt->formattype == FORMAT_VideoInfo) { + VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); + RTC_DCHECK(h); + capability.directShowCapabilityIndex = tmp; + capability.width = h->bmiHeader.biWidth; + capability.height = h->bmiHeader.biHeight; + avgTimePerFrame = h->AvgTimePerFrame; + } + if (pmt->formattype == FORMAT_VideoInfo2) { + VIDEOINFOHEADER2* h = + reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat); + RTC_DCHECK(h); + capability.directShowCapabilityIndex = tmp; + capability.width = h->bmiHeader.biWidth; + capability.height = h->bmiHeader.biHeight; + capability.interlaced = + h->dwInterlaceFlags & + (AMINTERLACE_IsInterlaced | AMINTERLACE_DisplayModeBobOnly); + avgTimePerFrame = h->AvgTimePerFrame; + } + + if (hrVC == S_OK) { + LONGLONG* frameDurationList = NULL; + LONGLONG maxFPS = 0; + long listSize = 0; + SIZE size; + size.cx = capability.width; + size.cy = capability.height; + + // GetMaxAvailableFrameRate doesn't return max frame rate always + // eg: Logitech Notebook. This may be due to a bug in that API + // because GetFrameRateList array is reversed in the above camera. So + // a util method written. Can't assume the first value will return + // the max fps. + hrVC = videoControlConfig->GetFrameRateList( + outputCapturePin, tmp, size, &listSize, &frameDurationList); + + if (hrVC == S_OK) { + maxFPS = GetMaxOfFrameArray(frameDurationList, listSize); + } + + CoTaskMemFree(frameDurationList); + frameDurationList = NULL; + listSize = 0; + + // On some odd cameras, you may get a 0 for duration. Some others may + // not update the out vars. GetMaxOfFrameArray returns the lowest + // duration (highest FPS), or 0 if there was no list with elements. + if (0 != maxFPS) { + capability.maxFPS = static_cast<int>(10000000 / maxFPS); + capability.supportFrameRateControl = true; + } else // use existing method + { + RTC_LOG(LS_INFO) << "GetMaxAvailableFrameRate NOT SUPPORTED"; + if (avgTimePerFrame > 0) + capability.maxFPS = static_cast<int>(10000000 / avgTimePerFrame); + else + capability.maxFPS = 0; + } + } else // use existing method in case IAMVideoControl is not supported + { + if (avgTimePerFrame > 0) + capability.maxFPS = static_cast<int>(10000000 / avgTimePerFrame); + else + capability.maxFPS = 0; + } + + // can't switch MEDIATYPE :~( + if (pmt->subtype == MEDIASUBTYPE_I420) { + capability.videoType = VideoType::kI420; + } else if (pmt->subtype == MEDIASUBTYPE_IYUV) { + capability.videoType = VideoType::kIYUV; + } else if (pmt->subtype == MEDIASUBTYPE_RGB24) { + capability.videoType = VideoType::kRGB24; + } else if (pmt->subtype == MEDIASUBTYPE_YUY2) { + capability.videoType = VideoType::kYUY2; + } else if (pmt->subtype == MEDIASUBTYPE_RGB565) { + capability.videoType = VideoType::kRGB565; + } else if (pmt->subtype == MEDIASUBTYPE_MJPG) { + capability.videoType = VideoType::kMJPEG; + } else if (pmt->subtype == MEDIASUBTYPE_dvsl || + pmt->subtype == MEDIASUBTYPE_dvsd || + pmt->subtype == + MEDIASUBTYPE_dvhd) // If this is an external DV camera + { + capability.videoType = + VideoType::kYUY2; // MS DV filter seems to create this type + } else if (pmt->subtype == + MEDIASUBTYPE_UYVY) // Seen used by Declink capture cards + { + capability.videoType = VideoType::kUYVY; + } 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 + { + RTC_LOG(LS_INFO) << "Device support HDYC."; + capability.videoType = VideoType::kUYVY; + } else { + WCHAR strGuid[39]; + StringFromGUID2(pmt->subtype, strGuid, 39); + RTC_LOG(LS_WARNING) + << "Device support unknown media type " << strGuid << ", width " + << capability.width << ", height " << capability.height; + continue; + } + + _captureCapabilities.push_back(capability); + _captureCapabilitiesWindows.push_back(capability); + RTC_LOG(LS_INFO) << "Camera capability, width:" << capability.width + << " height:" << capability.height + << " type:" << static_cast<int>(capability.videoType) + << " fps:" << capability.maxFPS; + } + FreeMediaType(pmt); + pmt = NULL; + } + RELEASE_AND_CLEAR(streamConfig); + RELEASE_AND_CLEAR(videoControlConfig); + RELEASE_AND_CLEAR(outputCapturePin); + RELEASE_AND_CLEAR(captureDevice); // Release the capture device + + // Store the new used device name + _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; + _lastUsedDeviceName = + (char*)realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1); + memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, + _lastUsedDeviceNameLength + 1); + RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size(); + + return static_cast<int32_t>(_captureCapabilities.size()); +} + +// Constructs a product ID from the Windows DevicePath. on a USB device the +// devicePath contains product id and vendor id. This seems to work for firewire +// as well. +// Example of device path: +// "\\?\usb#vid_0408&pid_2010&mi_00#7&258e7aaf&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" +// "\\?\avc#sony&dv-vcr&camcorder&dv#65b2d50301460008#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" +void DeviceInfoDS::GetProductId(const char* devicePath, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length) { + *productUniqueIdUTF8 = '\0'; + char* startPos = strstr((char*)devicePath, "\\\\?\\"); + if (!startPos) { + strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); + RTC_LOG(LS_INFO) << "Failed to get the product Id"; + return; + } + startPos += 4; + + char* pos = strchr(startPos, '&'); + if (!pos || pos >= (char*)devicePath + strlen((char*)devicePath)) { + strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); + RTC_LOG(LS_INFO) << "Failed to get the product Id"; + return; + } + // Find the second occurrence. + pos = strchr(pos + 1, '&'); + uint32_t bytesToCopy = (uint32_t)(pos - startPos); + if (pos && (bytesToCopy < productUniqueIdUTF8Length) && + bytesToCopy <= kVideoCaptureProductIdLength) { + strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, + (char*)startPos, bytesToCopy); + } else { + strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); + RTC_LOG(LS_INFO) << "Failed to get the product Id"; + } +} + +int32_t DeviceInfoDS::DisplayCaptureSettingsDialogBox( + const char* deviceUniqueIdUTF8, + const char* dialogTitleUTF8, + void* parentWindow, + uint32_t positionX, + uint32_t positionY) { + MutexLock lock(&_apiLock); + HWND window = (HWND)parentWindow; + + IBaseFilter* filter = GetDeviceFilter(deviceUniqueIdUTF8, NULL, 0); + if (!filter) + return -1; + + ISpecifyPropertyPages* pPages = NULL; + CAUUID uuid; + HRESULT hr = S_OK; + + hr = filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID*)&pPages); + if (!SUCCEEDED(hr)) { + filter->Release(); + return -1; + } + hr = pPages->GetPages(&uuid); + if (!SUCCEEDED(hr)) { + filter->Release(); + return -1; + } + + WCHAR tempDialogTitleWide[256]; + tempDialogTitleWide[0] = 0; + int size = 255; + + // UTF-8 to wide char + MultiByteToWideChar(CP_UTF8, 0, (char*)dialogTitleUTF8, -1, + tempDialogTitleWide, size); + + // Invoke a dialog box to display. + + hr = OleCreatePropertyFrame( + window, // You must create the parent window. + positionX, // Horizontal position for the dialog box. + positionY, // Vertical position for the dialog box. + tempDialogTitleWide, // String used for the dialog box caption. + 1, // Number of pointers passed in pPlugin. + (LPUNKNOWN*)&filter, // Pointer to the filter. + uuid.cElems, // Number of property pages. + uuid.pElems, // Array of property page CLSIDs. + LOCALE_USER_DEFAULT, // Locale ID for the dialog box. + 0, NULL); // Reserved + // Release memory. + if (uuid.pElems) { + CoTaskMemFree(uuid.pElems); + } + filter->Release(); + return 0; +} +} // namespace videocapturemodule +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.h b/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.h new file mode 100644 index 0000000000..e6dfaed366 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/device_info_ds.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_ +#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_ + +#include <dshow.h> +#include <Ks.h> +#include <dbt.h> + +#include "modules/video_capture/device_info_impl.h" +#include "modules/video_capture/video_capture.h" +#include "modules/video_capture/video_capture_impl.h" + +namespace webrtc { +namespace videocapturemodule { +struct VideoCaptureCapabilityWindows : public VideoCaptureCapability { + uint32_t directShowCapabilityIndex; + bool supportFrameRateControl; + VideoCaptureCapabilityWindows() { + directShowCapabilityIndex = 0; + supportFrameRateControl = false; + } +}; + +class DeviceInfoDS : public DeviceInfoImpl { + public: + // Factory function. + static DeviceInfoDS* Create(); + + DeviceInfoDS(); + ~DeviceInfoDS() override; + + int32_t Init() override; + uint32_t NumberOfDevices() override; + + /* + * Returns the available capture devices. + */ + int32_t GetDeviceName(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length, + pid_t* pid) override; + + /* + * Display OS /capture device specific settings dialog + */ + int32_t DisplayCaptureSettingsDialogBox(const char* deviceUniqueIdUTF8, + const char* dialogTitleUTF8, + void* parentWindow, + uint32_t positionX, + uint32_t positionY) override; + + // Windows specific + + /* Gets a capture device filter + The user of this API is responsible for releasing the filter when it not + needed. + */ + IBaseFilter* GetDeviceFilter(const char* deviceUniqueIdUTF8, + char* productUniqueIdUTF8 = NULL, + uint32_t productUniqueIdUTF8Length = 0); + + int32_t GetWindowsCapability( + int32_t capabilityIndex, + VideoCaptureCapabilityWindows& windowsCapability); + + static void GetProductId(const char* devicePath, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length); + + protected: + int32_t GetDeviceInfo(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* productUniqueIdUTF8, + uint32_t productUniqueIdUTF8Length); + + int32_t CreateCapabilityMap(const char* deviceUniqueIdUTF8) override + RTC_EXCLUSIVE_LOCKS_REQUIRED(_apiLock); + + private: + ICreateDevEnum* _dsDevEnum; + IEnumMoniker* _dsMonikerDevEnum; + bool _CoUninitializeIsRequired; + std::vector<VideoCaptureCapabilityWindows> _captureCapabilitiesWindows; + HWND _hwnd; + WNDCLASS _wndClass; + HINSTANCE _hInstance; + HDEVNOTIFY _hdevnotify; +}; +} // namespace videocapturemodule +} // namespace webrtc +#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_ diff --git a/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.cc b/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.cc new file mode 100644 index 0000000000..47fecfe4a1 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <initguid.h> // Must come before the help_functions_ds.h include so + // that DEFINE_GUID() entries will be defined in this + // object file. + +#include <cguid.h> + +#include "modules/video_capture/windows/help_functions_ds.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace videocapturemodule { +// This returns minimum :), which will give max frame rate... +LONGLONG GetMaxOfFrameArray(LONGLONG* maxFps, long size) { + if (!maxFps || size <= 0) { + return 0; + } + LONGLONG maxFPS = maxFps[0]; + for (int i = 0; i < size; i++) { + if (maxFPS > maxFps[i]) + maxFPS = maxFps[i]; + } + return maxFPS; +} + +IPin* GetInputPin(IBaseFilter* filter) { + IPin* pin = NULL; + IEnumPins* pPinEnum = NULL; + filter->EnumPins(&pPinEnum); + if (pPinEnum == NULL) { + return NULL; + } + + // get first unconnected pin + pPinEnum->Reset(); // set to first pin + + while (S_OK == pPinEnum->Next(1, &pin, NULL)) { + PIN_DIRECTION pPinDir; + pin->QueryDirection(&pPinDir); + if (PINDIR_INPUT == pPinDir) // This is an input pin + { + IPin* tempPin = NULL; + if (S_OK != pin->ConnectedTo(&tempPin)) // The pint is not connected + { + pPinEnum->Release(); + return pin; + } + } + pin->Release(); + } + pPinEnum->Release(); + return NULL; +} + +IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category) { + IPin* pin = NULL; + IEnumPins* pPinEnum = NULL; + filter->EnumPins(&pPinEnum); + if (pPinEnum == NULL) { + return NULL; + } + // get first unconnected pin + pPinEnum->Reset(); // set to first pin + while (S_OK == pPinEnum->Next(1, &pin, NULL)) { + PIN_DIRECTION pPinDir; + pin->QueryDirection(&pPinDir); + if (PINDIR_OUTPUT == pPinDir) // This is an output pin + { + if (Category == GUID_NULL || PinMatchesCategory(pin, Category)) { + pPinEnum->Release(); + return pin; + } + } + pin->Release(); + pin = NULL; + } + pPinEnum->Release(); + return NULL; +} + +BOOL PinMatchesCategory(IPin* pPin, REFGUID Category) { + BOOL bFound = FALSE; + IKsPropertySet* pKs = NULL; + HRESULT hr = pPin->QueryInterface(IID_PPV_ARGS(&pKs)); + if (SUCCEEDED(hr)) { + GUID PinCategory; + DWORD cbReturned; + hr = pKs->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, + &PinCategory, sizeof(GUID), &cbReturned); + if (SUCCEEDED(hr) && (cbReturned == sizeof(GUID))) { + bFound = (PinCategory == Category); + } + pKs->Release(); + } + return bFound; +} + +void ResetMediaType(AM_MEDIA_TYPE* media_type) { + if (!media_type) + return; + if (media_type->cbFormat != 0) { + CoTaskMemFree(media_type->pbFormat); + media_type->cbFormat = 0; + media_type->pbFormat = nullptr; + } + if (media_type->pUnk) { + media_type->pUnk->Release(); + media_type->pUnk = nullptr; + } +} + +void FreeMediaType(AM_MEDIA_TYPE* media_type) { + if (!media_type) + return; + ResetMediaType(media_type); + CoTaskMemFree(media_type); +} + +HRESULT CopyMediaType(AM_MEDIA_TYPE* target, const AM_MEDIA_TYPE* source) { + RTC_DCHECK_NE(source, target); + *target = *source; + if (source->cbFormat != 0) { + RTC_DCHECK(source->pbFormat); + target->pbFormat = + reinterpret_cast<BYTE*>(CoTaskMemAlloc(source->cbFormat)); + if (target->pbFormat == nullptr) { + target->cbFormat = 0; + return E_OUTOFMEMORY; + } else { + CopyMemory(target->pbFormat, source->pbFormat, target->cbFormat); + } + } + + if (target->pUnk != nullptr) + target->pUnk->AddRef(); + + return S_OK; +} + +wchar_t* DuplicateWideString(const wchar_t* str) { + size_t len = lstrlenW(str); + wchar_t* ret = + reinterpret_cast<LPWSTR>(CoTaskMemAlloc((len + 1) * sizeof(wchar_t))); + lstrcpyW(ret, str); + return ret; +} + +} // namespace videocapturemodule +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.h b/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.h new file mode 100644 index 0000000000..29479157a8 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/help_functions_ds.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_DS_H_ +#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_DS_H_ + +#include <dshow.h> + +#include <type_traits> +#include <utility> + +#include "api/scoped_refptr.h" +#include "rtc_base/ref_counter.h" + +DEFINE_GUID(MEDIASUBTYPE_I420, + 0x30323449, + 0x0000, + 0x0010, + 0x80, + 0x00, + 0x00, + 0xAA, + 0x00, + 0x38, + 0x9B, + 0x71); +DEFINE_GUID(MEDIASUBTYPE_HDYC, + 0x43594448, + 0x0000, + 0x0010, + 0x80, + 0x00, + 0x00, + 0xAA, + 0x00, + 0x38, + 0x9B, + 0x71); + +#define RELEASE_AND_CLEAR(p) \ + if (p) { \ + (p)->Release(); \ + (p) = NULL; \ + } + +namespace webrtc { +namespace videocapturemodule { +LONGLONG GetMaxOfFrameArray(LONGLONG* maxFps, long size); + +IPin* GetInputPin(IBaseFilter* filter); +IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category); +BOOL PinMatchesCategory(IPin* pPin, REFGUID Category); +void ResetMediaType(AM_MEDIA_TYPE* media_type); +void FreeMediaType(AM_MEDIA_TYPE* media_type); +HRESULT CopyMediaType(AM_MEDIA_TYPE* target, const AM_MEDIA_TYPE* source); + +// Helper function to make using scoped_refptr with COM interface pointers +// a little less awkward. rtc::scoped_refptr doesn't support the & operator +// or a way to receive values via an out ptr. +// The function is intentionally not called QueryInterface to make things less +// confusing for the compiler to figure out what the caller wants to do when +// called from within the context of a class that also implements COM +// interfaces. +template <class T> +HRESULT GetComInterface(IUnknown* object, rtc::scoped_refptr<T>* ptr) { + // This helper function is not meant to magically free ptr. If we do that + // we add code bloat to most places where it's not needed and make the code + // less readable since it's not clear at the call site that the pointer + // would get freed even inf QI() fails. + RTC_DCHECK(!ptr->get()); + void* new_ptr = nullptr; + HRESULT hr = object->QueryInterface(__uuidof(T), &new_ptr); + if (SUCCEEDED(hr)) + ptr->swap(reinterpret_cast<T**>(&new_ptr)); + return hr; +} + +// Provides a reference count implementation for COM (IUnknown derived) classes. +// The implementation uses atomics for managing the ref count. +template <class T> +class ComRefCount : public T { + public: + ComRefCount() {} + + template <class P0> + explicit ComRefCount(P0&& p0) : T(std::forward<P0>(p0)) {} + + STDMETHOD_(ULONG, AddRef)() override { + ref_count_.IncRef(); + return 1; + } + + STDMETHOD_(ULONG, Release)() override { + const auto status = ref_count_.DecRef(); + if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) { + delete this; + return 0; + } + return 1; + } + + protected: + ~ComRefCount() {} + + private: + webrtc::webrtc_impl::RefCounter ref_count_{0}; +}; + +} // namespace videocapturemodule +} // namespace webrtc +#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_DS_H_ diff --git a/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.cc b/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.cc new file mode 100644 index 0000000000..0c5acb668d --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.cc @@ -0,0 +1,959 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_capture/windows/sink_filter_ds.h" + +#include <dvdmedia.h> // VIDEOINFOHEADER2 +#include <initguid.h> + +#include <algorithm> +#include <list> + +#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/string_utils.h" + +DEFINE_GUID(CLSID_SINKFILTER, + 0x88cdbbdc, + 0xa73b, + 0x4afa, + 0xac, + 0xbf, + 0x15, + 0xd5, + 0xe2, + 0xce, + 0x12, + 0xc3); + +namespace webrtc { +namespace videocapturemodule { +namespace { + +// Simple enumeration implementation that enumerates over a single pin :-/ +class EnumPins : public IEnumPins { + public: + EnumPins(IPin* pin) : pin_(pin) {} + + protected: + virtual ~EnumPins() {} + + private: + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override { + if (riid == IID_IUnknown || riid == IID_IEnumPins) { + *ppv = static_cast<IEnumPins*>(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; + } + + STDMETHOD(Clone)(IEnumPins** pins) { + RTC_DCHECK_NOTREACHED(); + return E_NOTIMPL; + } + + STDMETHOD(Next)(ULONG count, IPin** pins, ULONG* fetched) { + RTC_DCHECK(count > 0); + RTC_DCHECK(pins); + // fetched may be NULL. + + if (pos_ > 0) { + if (fetched) + *fetched = 0; + return S_FALSE; + } + + ++pos_; + pins[0] = pin_.get(); + pins[0]->AddRef(); + if (fetched) + *fetched = 1; + + return count == 1 ? S_OK : S_FALSE; + } + + STDMETHOD(Skip)(ULONG count) { + RTC_DCHECK_NOTREACHED(); + return E_NOTIMPL; + } + + STDMETHOD(Reset)() { + pos_ = 0; + return S_OK; + } + + rtc::scoped_refptr<IPin> pin_; + int pos_ = 0; +}; + +bool IsMediaTypePartialMatch(const AM_MEDIA_TYPE& a, const AM_MEDIA_TYPE& b) { + if (b.majortype != GUID_NULL && a.majortype != b.majortype) + return false; + + if (b.subtype != GUID_NULL && a.subtype != b.subtype) + return false; + + if (b.formattype != GUID_NULL) { + // if the format block is specified then it must match exactly + if (a.formattype != b.formattype) + return false; + + if (a.cbFormat != b.cbFormat) + return false; + + if (a.cbFormat != 0 && memcmp(a.pbFormat, b.pbFormat, a.cbFormat) != 0) + return false; + } + + return true; +} + +bool IsMediaTypeFullySpecified(const AM_MEDIA_TYPE& type) { + return type.majortype != GUID_NULL && type.formattype != GUID_NULL; +} + +BYTE* AllocMediaTypeFormatBuffer(AM_MEDIA_TYPE* media_type, ULONG length) { + RTC_DCHECK(length); + if (media_type->cbFormat == length) + return media_type->pbFormat; + + BYTE* buffer = static_cast<BYTE*>(CoTaskMemAlloc(length)); + if (!buffer) + return nullptr; + + if (media_type->pbFormat) { + RTC_DCHECK(media_type->cbFormat); + CoTaskMemFree(media_type->pbFormat); + media_type->pbFormat = nullptr; + } + + media_type->cbFormat = length; + media_type->pbFormat = buffer; + return buffer; +} + +void GetSampleProperties(IMediaSample* sample, AM_SAMPLE2_PROPERTIES* props) { + rtc::scoped_refptr<IMediaSample2> sample2; + if (SUCCEEDED(GetComInterface(sample, &sample2))) { + sample2->GetProperties(sizeof(*props), reinterpret_cast<BYTE*>(props)); + return; + } + + // Get the properties the hard way. + props->cbData = sizeof(*props); + props->dwTypeSpecificFlags = 0; + props->dwStreamId = AM_STREAM_MEDIA; + props->dwSampleFlags = 0; + + if (sample->IsDiscontinuity() == S_OK) + props->dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY; + + if (sample->IsPreroll() == S_OK) + props->dwSampleFlags |= AM_SAMPLE_PREROLL; + + if (sample->IsSyncPoint() == S_OK) + props->dwSampleFlags |= AM_SAMPLE_SPLICEPOINT; + + if (SUCCEEDED(sample->GetTime(&props->tStart, &props->tStop))) + props->dwSampleFlags |= AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID; + + if (sample->GetMediaType(&props->pMediaType) == S_OK) + props->dwSampleFlags |= AM_SAMPLE_TYPECHANGED; + + sample->GetPointer(&props->pbBuffer); + props->lActual = sample->GetActualDataLength(); + props->cbBuffer = sample->GetSize(); +} + +// Returns true if the media type is supported, false otherwise. +// For supported types, the `capability` will be populated accordingly. +bool TranslateMediaTypeToVideoCaptureCapability( + const AM_MEDIA_TYPE* media_type, + VideoCaptureCapability* capability) { + RTC_DCHECK(capability); + if (!media_type || media_type->majortype != MEDIATYPE_Video || + !media_type->pbFormat) { + return false; + } + + const BITMAPINFOHEADER* bih = nullptr; + if (media_type->formattype == FORMAT_VideoInfo) { + bih = &reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat)->bmiHeader; + } else if (media_type->formattype != FORMAT_VideoInfo2) { + bih = &reinterpret_cast<VIDEOINFOHEADER2*>(media_type->pbFormat)->bmiHeader; + } else { + return false; + } + + RTC_LOG(LS_INFO) << "TranslateMediaTypeToVideoCaptureCapability width:" + << bih->biWidth << " height:" << bih->biHeight + << " Compression:0x" << rtc::ToHex(bih->biCompression); + + const GUID& sub_type = media_type->subtype; + if (sub_type == MEDIASUBTYPE_MJPG && + bih->biCompression == MAKEFOURCC('M', 'J', 'P', 'G')) { + capability->videoType = VideoType::kMJPEG; + } else if (sub_type == MEDIASUBTYPE_I420 && + bih->biCompression == MAKEFOURCC('I', '4', '2', '0')) { + capability->videoType = VideoType::kI420; + } else if (sub_type == MEDIASUBTYPE_YUY2 && + bih->biCompression == MAKEFOURCC('Y', 'U', 'Y', '2')) { + capability->videoType = VideoType::kYUY2; + } else if (sub_type == MEDIASUBTYPE_UYVY && + bih->biCompression == MAKEFOURCC('U', 'Y', 'V', 'Y')) { + capability->videoType = VideoType::kUYVY; + } else if (sub_type == MEDIASUBTYPE_HDYC) { + capability->videoType = VideoType::kUYVY; + } else if (sub_type == MEDIASUBTYPE_RGB24 && bih->biCompression == BI_RGB) { + capability->videoType = VideoType::kRGB24; + } else { + return false; + } + + // Store the incoming width and height + capability->width = bih->biWidth; + + // Store the incoming height, + // for RGB24 we assume the frame to be upside down + if (sub_type == MEDIASUBTYPE_RGB24 && bih->biHeight > 0) { + capability->height = -(bih->biHeight); + } else { + capability->height = abs(bih->biHeight); + } + + return true; +} + +class MediaTypesEnum : public IEnumMediaTypes { + public: + MediaTypesEnum(const VideoCaptureCapability& capability) + : capability_(capability), + format_preference_order_( + {// Default preferences, sorted by cost-to-convert-to-i420. + VideoType::kI420, VideoType::kYUY2, VideoType::kRGB24, + VideoType::kUYVY, VideoType::kMJPEG}) { + // Use the preferred video type, if supported. + auto it = std::find(format_preference_order_.begin(), + format_preference_order_.end(), capability_.videoType); + if (it != format_preference_order_.end()) { + RTC_LOG(LS_INFO) << "Selected video type: " << *it; + // Move it to the front of the list, if it isn't already there. + if (it != format_preference_order_.begin()) { + format_preference_order_.splice(format_preference_order_.begin(), + format_preference_order_, it, + std::next(it)); + } + } else { + RTC_LOG(LS_WARNING) << "Unsupported video type: " << *it + << ", using default preference list."; + } + } + + protected: + virtual ~MediaTypesEnum() {} + + private: + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override { + if (riid == IID_IUnknown || riid == IID_IEnumMediaTypes) { + *ppv = static_cast<IEnumMediaTypes*>(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; + } + + // IEnumMediaTypes + STDMETHOD(Clone)(IEnumMediaTypes** pins) { + RTC_DCHECK_NOTREACHED(); + return E_NOTIMPL; + } + + STDMETHOD(Next)(ULONG count, AM_MEDIA_TYPE** types, ULONG* fetched) { + RTC_DCHECK(count > 0); + RTC_DCHECK(types); + // fetched may be NULL. + if (fetched) + *fetched = 0; + + for (ULONG i = 0; + i < count && pos_ < static_cast<int>(format_preference_order_.size()); + ++i) { + AM_MEDIA_TYPE* media_type = reinterpret_cast<AM_MEDIA_TYPE*>( + CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); + ZeroMemory(media_type, sizeof(*media_type)); + types[i] = media_type; + VIDEOINFOHEADER* vih = reinterpret_cast<VIDEOINFOHEADER*>( + AllocMediaTypeFormatBuffer(media_type, sizeof(VIDEOINFOHEADER))); + ZeroMemory(vih, sizeof(*vih)); + vih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + vih->bmiHeader.biPlanes = 1; + vih->bmiHeader.biClrImportant = 0; + vih->bmiHeader.biClrUsed = 0; + if (capability_.maxFPS != 0) + vih->AvgTimePerFrame = 10000000 / capability_.maxFPS; + + SetRectEmpty(&vih->rcSource); // we want the whole image area rendered. + SetRectEmpty(&vih->rcTarget); // no particular destination rectangle + + media_type->majortype = MEDIATYPE_Video; + media_type->formattype = FORMAT_VideoInfo; + media_type->bTemporalCompression = FALSE; + + // Set format information. + auto format_it = std::next(format_preference_order_.begin(), pos_++); + SetMediaInfoFromVideoType(*format_it, &vih->bmiHeader, media_type); + + vih->bmiHeader.biWidth = capability_.width; + vih->bmiHeader.biHeight = capability_.height; + vih->bmiHeader.biSizeImage = ((vih->bmiHeader.biBitCount / 4) * + capability_.height * capability_.width) / + 2; + + RTC_DCHECK(vih->bmiHeader.biSizeImage); + media_type->lSampleSize = vih->bmiHeader.biSizeImage; + media_type->bFixedSizeSamples = true; + if (fetched) + ++(*fetched); + } + return pos_ == static_cast<int>(format_preference_order_.size()) ? S_FALSE + : S_OK; + } + + static void SetMediaInfoFromVideoType(VideoType video_type, + BITMAPINFOHEADER* bitmap_header, + AM_MEDIA_TYPE* media_type) { + switch (video_type) { + case VideoType::kI420: + bitmap_header->biCompression = MAKEFOURCC('I', '4', '2', '0'); + bitmap_header->biBitCount = 12; // bit per pixel + media_type->subtype = MEDIASUBTYPE_I420; + break; + case VideoType::kYUY2: + bitmap_header->biCompression = MAKEFOURCC('Y', 'U', 'Y', '2'); + bitmap_header->biBitCount = 16; // bit per pixel + media_type->subtype = MEDIASUBTYPE_YUY2; + break; + case VideoType::kRGB24: + bitmap_header->biCompression = BI_RGB; + bitmap_header->biBitCount = 24; // bit per pixel + media_type->subtype = MEDIASUBTYPE_RGB24; + break; + case VideoType::kUYVY: + bitmap_header->biCompression = MAKEFOURCC('U', 'Y', 'V', 'Y'); + bitmap_header->biBitCount = 16; // bit per pixel + media_type->subtype = MEDIASUBTYPE_UYVY; + break; + case VideoType::kMJPEG: + bitmap_header->biCompression = MAKEFOURCC('M', 'J', 'P', 'G'); + bitmap_header->biBitCount = 12; // bit per pixel + media_type->subtype = MEDIASUBTYPE_MJPG; + break; + default: + RTC_DCHECK_NOTREACHED(); + } + } + + STDMETHOD(Skip)(ULONG count) { + RTC_DCHECK_NOTREACHED(); + return E_NOTIMPL; + } + + STDMETHOD(Reset)() { + pos_ = 0; + return S_OK; + } + + int pos_ = 0; + const VideoCaptureCapability capability_; + std::list<VideoType> format_preference_order_; +}; + +} // namespace + +CaptureInputPin::CaptureInputPin(CaptureSinkFilter* filter) { + capture_checker_.Detach(); + // No reference held to avoid circular references. + info_.pFilter = filter; + info_.dir = PINDIR_INPUT; +} + +CaptureInputPin::~CaptureInputPin() { + RTC_DCHECK_RUN_ON(&main_checker_); + ResetMediaType(&media_type_); +} + +HRESULT CaptureInputPin::SetRequestedCapability( + const VideoCaptureCapability& capability) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(Filter()->IsStopped()); + requested_capability_ = capability; + resulting_capability_ = VideoCaptureCapability(); + return S_OK; +} + +void CaptureInputPin::OnFilterActivated() { + RTC_DCHECK_RUN_ON(&main_checker_); + runtime_error_ = false; + flushing_ = false; + capture_checker_.Detach(); + capture_thread_id_ = 0; +} + +void CaptureInputPin::OnFilterDeactivated() { + RTC_DCHECK_RUN_ON(&main_checker_); + // Expedite shutdown by raising the flushing flag so no further processing + // on the capture thread occurs. When the graph is stopped and all filters + // have been told to stop, the media controller (graph) will wait for the + // capture thread to stop. + flushing_ = true; + if (allocator_) + allocator_->Decommit(); +} + +CaptureSinkFilter* CaptureInputPin::Filter() const { + return static_cast<CaptureSinkFilter*>(info_.pFilter); +} + +HRESULT CaptureInputPin::AttemptConnection(IPin* receive_pin, + const AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(Filter()->IsStopped()); + + // Check that the connection is valid -- need to do this for every + // connect attempt since BreakConnect will undo it. + HRESULT hr = CheckDirection(receive_pin); + if (FAILED(hr)) + return hr; + + if (!TranslateMediaTypeToVideoCaptureCapability(media_type, + &resulting_capability_)) { + ClearAllocator(true); + return VFW_E_TYPE_NOT_ACCEPTED; + } + + // See if the other pin will accept this type. + hr = receive_pin->ReceiveConnection(static_cast<IPin*>(this), media_type); + if (FAILED(hr)) { + receive_pin_ = nullptr; // Should already be null, but just in case. + return hr; + } + + // Should have been set as part of the connect process. + RTC_DCHECK_EQ(receive_pin_, receive_pin); + + ResetMediaType(&media_type_); + CopyMediaType(&media_type_, media_type); + + return S_OK; +} + +std::vector<AM_MEDIA_TYPE*> CaptureInputPin::DetermineCandidateFormats( + IPin* receive_pin, + const AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(receive_pin); + RTC_DCHECK(media_type); + + std::vector<AM_MEDIA_TYPE*> ret; + + for (int i = 0; i < 2; i++) { + IEnumMediaTypes* types = nullptr; + if (i == 0) { + // First time around, try types from receive_pin. + receive_pin->EnumMediaTypes(&types); + } else { + // Then try ours. + EnumMediaTypes(&types); + } + + if (types) { + while (true) { + ULONG fetched = 0; + AM_MEDIA_TYPE* this_type = nullptr; + if (types->Next(1, &this_type, &fetched) != S_OK) + break; + + if (IsMediaTypePartialMatch(*this_type, *media_type)) { + ret.push_back(this_type); + } else { + FreeMediaType(this_type); + } + } + types->Release(); + } + } + + return ret; +} + +void CaptureInputPin::ClearAllocator(bool decommit) { + RTC_DCHECK_RUN_ON(&main_checker_); + if (!allocator_) + return; + if (decommit) + allocator_->Decommit(); + allocator_ = nullptr; +} + +HRESULT CaptureInputPin::CheckDirection(IPin* pin) const { + RTC_DCHECK_RUN_ON(&main_checker_); + PIN_DIRECTION pd; + pin->QueryDirection(&pd); + // Fairly basic check, make sure we don't pair input with input etc. + return pd == info_.dir ? VFW_E_INVALID_DIRECTION : S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryInterface(REFIID riid, + void** ppv) { + (*ppv) = nullptr; + if (riid == IID_IUnknown || riid == IID_IMemInputPin) { + *ppv = static_cast<IMemInputPin*>(this); + } else if (riid == IID_IPin) { + *ppv = static_cast<IPin*>(this); + } + + if (!(*ppv)) + return E_NOINTERFACE; + + static_cast<IMemInputPin*>(this)->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::Connect(IPin* receive_pin, const AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + if (!media_type || !receive_pin) + return E_POINTER; + + if (!Filter()->IsStopped()) + return VFW_E_NOT_STOPPED; + + if (receive_pin_) { + RTC_DCHECK_NOTREACHED(); + return VFW_E_ALREADY_CONNECTED; + } + + if (IsMediaTypeFullySpecified(*media_type)) + return AttemptConnection(receive_pin, media_type); + + auto types = DetermineCandidateFormats(receive_pin, media_type); + bool connected = false; + for (auto* type : types) { + if (!connected && AttemptConnection(receive_pin, media_type) == S_OK) + connected = true; + + FreeMediaType(type); + } + + return connected ? S_OK : VFW_E_NO_ACCEPTABLE_TYPES; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ReceiveConnection(IPin* connector, + const AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(Filter()->IsStopped()); + + if (receive_pin_) { + RTC_DCHECK_NOTREACHED(); + return VFW_E_ALREADY_CONNECTED; + } + + HRESULT hr = CheckDirection(connector); + if (FAILED(hr)) + return hr; + + if (!TranslateMediaTypeToVideoCaptureCapability(media_type, + &resulting_capability_)) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Complete the connection + + receive_pin_ = connector; + ResetMediaType(&media_type_); + CopyMediaType(&media_type_, media_type); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::Disconnect() { + RTC_DCHECK_RUN_ON(&main_checker_); + if (!Filter()->IsStopped()) + return VFW_E_NOT_STOPPED; + + if (!receive_pin_) + return S_FALSE; + + ClearAllocator(true); + receive_pin_ = nullptr; + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ConnectedTo(IPin** pin) { + RTC_DCHECK_RUN_ON(&main_checker_); + + if (!receive_pin_) + return VFW_E_NOT_CONNECTED; + + *pin = receive_pin_.get(); + receive_pin_->AddRef(); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ConnectionMediaType(AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + + if (!receive_pin_) + return VFW_E_NOT_CONNECTED; + + CopyMediaType(media_type, &media_type_); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryPinInfo(PIN_INFO* info) { + RTC_DCHECK_RUN_ON(&main_checker_); + *info = info_; + if (info_.pFilter) + info_.pFilter->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryDirection(PIN_DIRECTION* pin_dir) { + RTC_DCHECK_RUN_ON(&main_checker_); + *pin_dir = info_.dir; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryId(LPWSTR* id) { + RTC_DCHECK_RUN_ON(&main_checker_); + size_t len = lstrlenW(info_.achName); + *id = reinterpret_cast<LPWSTR>(CoTaskMemAlloc((len + 1) * sizeof(wchar_t))); + lstrcpyW(*id, info_.achName); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryAccept(const AM_MEDIA_TYPE* media_type) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(Filter()->IsStopped()); + VideoCaptureCapability capability(resulting_capability_); + return TranslateMediaTypeToVideoCaptureCapability(media_type, &capability) + ? S_FALSE + : S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::EnumMediaTypes(IEnumMediaTypes** types) { + RTC_DCHECK_RUN_ON(&main_checker_); + *types = new ComRefCount<MediaTypesEnum>(requested_capability_); + (*types)->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::QueryInternalConnections(IPin** pins, ULONG* count) { + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndOfStream() { + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::BeginFlush() { + RTC_DCHECK_RUN_ON(&main_checker_); + flushing_ = true; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndFlush() { + RTC_DCHECK_RUN_ON(&main_checker_); + flushing_ = false; + runtime_error_ = false; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::NewSegment(REFERENCE_TIME start, + REFERENCE_TIME stop, + double rate) { + RTC_DCHECK_RUN_ON(&main_checker_); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::GetAllocator(IMemAllocator** allocator) { + RTC_DCHECK_RUN_ON(&main_checker_); + if (allocator_ == nullptr) { + HRESULT hr = CoCreateInstance(CLSID_MemoryAllocator, 0, + CLSCTX_INPROC_SERVER, IID_IMemAllocator, + reinterpret_cast<void**>(allocator)); + if (FAILED(hr)) + return hr; + allocator_.swap(allocator); + } + *allocator = allocator_.get(); + allocator_->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::NotifyAllocator(IMemAllocator* allocator, BOOL read_only) { + RTC_DCHECK_RUN_ON(&main_checker_); + allocator_.swap(&allocator); + if (allocator_) + allocator_->AddRef(); + if (allocator) + allocator->Release(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* props) { + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::Receive(IMediaSample* media_sample) { + RTC_DCHECK_RUN_ON(&capture_checker_); + + CaptureSinkFilter* const filter = static_cast<CaptureSinkFilter*>(Filter()); + + if (flushing_.load(std::memory_order_relaxed)) + return S_FALSE; + + if (runtime_error_.load(std::memory_order_relaxed)) + return VFW_E_RUNTIME_ERROR; + + if (!capture_thread_id_) { + // Make sure we set the thread name only once. + capture_thread_id_ = GetCurrentThreadId(); + rtc::SetCurrentThreadName("webrtc_video_capture"); + } + + AM_SAMPLE2_PROPERTIES sample_props = {}; + GetSampleProperties(media_sample, &sample_props); + // Has the format changed in this sample? + if (sample_props.dwSampleFlags & AM_SAMPLE_TYPECHANGED) { + // Check the derived class accepts the new format. + // This shouldn't fail as the source must call QueryAccept first. + + // Note: This will modify resulting_capability_. + // That should be OK as long as resulting_capability_ is only modified + // on this thread while it is running (filter is not stopped), and only + // modified on the main thread when the filter is stopped (i.e. this thread + // is not running). + if (!TranslateMediaTypeToVideoCaptureCapability(sample_props.pMediaType, + &resulting_capability_)) { + // Raise a runtime error if we fail the media type + runtime_error_ = true; + EndOfStream(); + Filter()->NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0); + return VFW_E_INVALIDMEDIATYPE; + } + } + + filter->ProcessCapturedFrame(sample_props.pbBuffer, sample_props.lActual, + resulting_capability_); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureInputPin::ReceiveMultiple(IMediaSample** samples, + long count, + long* processed) { + HRESULT hr = S_OK; + *processed = 0; + while (count-- > 0) { + hr = Receive(samples[*processed]); + if (hr != S_OK) + break; + ++(*processed); + } + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ReceiveCanBlock() { + return S_FALSE; +} + +// ---------------------------------------------------------------------------- + +CaptureSinkFilter::CaptureSinkFilter(VideoCaptureImpl* capture_observer) + : input_pin_(new ComRefCount<CaptureInputPin>(this)), + capture_observer_(capture_observer) {} + +CaptureSinkFilter::~CaptureSinkFilter() { + RTC_DCHECK_RUN_ON(&main_checker_); +} + +HRESULT CaptureSinkFilter::SetRequestedCapability( + const VideoCaptureCapability& capability) { + RTC_DCHECK_RUN_ON(&main_checker_); + // Called on the same thread as capture is started on. + return input_pin_->SetRequestedCapability(capability); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::GetState(DWORD msecs, FILTER_STATE* state) { + RTC_DCHECK_RUN_ON(&main_checker_); + *state = state_; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::SetSyncSource(IReferenceClock* clock) { + RTC_DCHECK_RUN_ON(&main_checker_); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::GetSyncSource(IReferenceClock** clock) { + RTC_DCHECK_RUN_ON(&main_checker_); + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Pause() { + RTC_DCHECK_RUN_ON(&main_checker_); + state_ = State_Paused; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::Run(REFERENCE_TIME tStart) { + RTC_DCHECK_RUN_ON(&main_checker_); + if (state_ == State_Stopped) + Pause(); + + state_ = State_Running; + input_pin_->OnFilterActivated(); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Stop() { + RTC_DCHECK_RUN_ON(&main_checker_); + if (state_ == State_Stopped) + return S_OK; + + state_ = State_Stopped; + input_pin_->OnFilterDeactivated(); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::EnumPins(IEnumPins** pins) { + RTC_DCHECK_RUN_ON(&main_checker_); + *pins = new ComRefCount<class EnumPins>(input_pin_.get()); + (*pins)->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::FindPin(LPCWSTR id, + IPin** pin) { + RTC_DCHECK_RUN_ON(&main_checker_); + // There's no ID assigned to our input pin, so looking it up based on one + // is pointless (and in practice, this method isn't being used). + return VFW_E_NOT_FOUND; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryFilterInfo(FILTER_INFO* info) { + RTC_DCHECK_RUN_ON(&main_checker_); + *info = info_; + if (info->pGraph) + info->pGraph->AddRef(); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::JoinFilterGraph(IFilterGraph* graph, LPCWSTR name) { + RTC_DCHECK_RUN_ON(&main_checker_); + RTC_DCHECK(IsStopped()); + + // Note, since a reference to the filter is held by the graph manager, + // filters must not hold a reference to the graph. If they would, we'd have + // a circular reference. Instead, a pointer to the graph can be held without + // reference. See documentation for IBaseFilter::JoinFilterGraph for more. + info_.pGraph = graph; // No AddRef(). + sink_ = nullptr; + + if (info_.pGraph) { + // make sure we don't hold on to the reference we may receive. + // Note that this assumes the same object identity, but so be it. + rtc::scoped_refptr<IMediaEventSink> sink; + GetComInterface(info_.pGraph, &sink); + sink_ = sink.get(); + } + + info_.achName[0] = L'\0'; + if (name) + lstrcpynW(info_.achName, name, arraysize(info_.achName)); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryVendorInfo(LPWSTR* vendor_info) { + return E_NOTIMPL; +} + +void CaptureSinkFilter::ProcessCapturedFrame( + unsigned char* buffer, + size_t length, + const VideoCaptureCapability& frame_info) { + // Called on the capture thread. + capture_observer_->IncomingFrame(buffer, length, frame_info); +} + +void CaptureSinkFilter::NotifyEvent(long code, + LONG_PTR param1, + LONG_PTR param2) { + // Called on the capture thread. + if (!sink_) + return; + + if (EC_COMPLETE == code) + param2 = reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter*>(this)); + sink_->Notify(code, param1, param2); +} + +bool CaptureSinkFilter::IsStopped() const { + RTC_DCHECK_RUN_ON(&main_checker_); + return state_ == State_Stopped; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP +CaptureSinkFilter::QueryInterface(REFIID riid, void** ppv) { + if (riid == IID_IUnknown || riid == IID_IPersist || riid == IID_IBaseFilter) { + *ppv = static_cast<IBaseFilter*>(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::GetClassID(CLSID* clsid) { + *clsid = CLSID_SINKFILTER; + return S_OK; +} + +} // namespace videocapturemodule +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.h b/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.h new file mode 100644 index 0000000000..b0fabda3cd --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/sink_filter_ds.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_DS_H_ +#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_DS_H_ + +#include <dshow.h> + +#include <atomic> +#include <memory> +#include <vector> + +#include "api/sequence_checker.h" +#include "modules/video_capture/video_capture_impl.h" +#include "modules/video_capture/windows/help_functions_ds.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { +namespace videocapturemodule { +// forward declarations +class CaptureSinkFilter; + +// Input pin for camera input +// Implements IMemInputPin, IPin. +class CaptureInputPin : public IMemInputPin, public IPin { + public: + CaptureInputPin(CaptureSinkFilter* filter); + + HRESULT SetRequestedCapability(const VideoCaptureCapability& capability); + + // Notifications from the filter. + void OnFilterActivated(); + void OnFilterDeactivated(); + + protected: + virtual ~CaptureInputPin(); + + private: + CaptureSinkFilter* Filter() const; + + HRESULT AttemptConnection(IPin* receive_pin, const AM_MEDIA_TYPE* media_type); + std::vector<AM_MEDIA_TYPE*> DetermineCandidateFormats( + IPin* receive_pin, + const AM_MEDIA_TYPE* media_type); + void ClearAllocator(bool decommit); + HRESULT CheckDirection(IPin* pin) const; + + // IUnknown + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override; + + // clang-format off + // clang isn't sure what to do with the longer STDMETHOD() function + // declarations. + + // IPin + STDMETHOD(Connect)(IPin* receive_pin, + const AM_MEDIA_TYPE* media_type) override; + STDMETHOD(ReceiveConnection)(IPin* connector, + const AM_MEDIA_TYPE* media_type) override; + STDMETHOD(Disconnect)() override; + STDMETHOD(ConnectedTo)(IPin** pin) override; + STDMETHOD(ConnectionMediaType)(AM_MEDIA_TYPE* media_type) override; + STDMETHOD(QueryPinInfo)(PIN_INFO* info) override; + STDMETHOD(QueryDirection)(PIN_DIRECTION* pin_dir) override; + STDMETHOD(QueryId)(LPWSTR* id) override; + STDMETHOD(QueryAccept)(const AM_MEDIA_TYPE* media_type) override; + STDMETHOD(EnumMediaTypes)(IEnumMediaTypes** types) override; + STDMETHOD(QueryInternalConnections)(IPin** pins, ULONG* count) override; + STDMETHOD(EndOfStream)() override; + STDMETHOD(BeginFlush)() override; + STDMETHOD(EndFlush)() override; + STDMETHOD(NewSegment)(REFERENCE_TIME start, REFERENCE_TIME stop, + double rate) override; + + // IMemInputPin + STDMETHOD(GetAllocator)(IMemAllocator** allocator) override; + STDMETHOD(NotifyAllocator)(IMemAllocator* allocator, BOOL read_only) override; + STDMETHOD(GetAllocatorRequirements)(ALLOCATOR_PROPERTIES* props) override; + STDMETHOD(Receive)(IMediaSample* sample) override; + STDMETHOD(ReceiveMultiple)(IMediaSample** samples, long count, + long* processed) override; + STDMETHOD(ReceiveCanBlock)() override; + // clang-format on + + SequenceChecker main_checker_; + SequenceChecker capture_checker_; + + VideoCaptureCapability requested_capability_ RTC_GUARDED_BY(main_checker_); + // Accessed on the main thread when Filter()->IsStopped() (capture thread not + // running), otherwise accessed on the capture thread. + VideoCaptureCapability resulting_capability_; + DWORD capture_thread_id_ = 0; + rtc::scoped_refptr<IMemAllocator> allocator_ RTC_GUARDED_BY(main_checker_); + rtc::scoped_refptr<IPin> receive_pin_ RTC_GUARDED_BY(main_checker_); + std::atomic_bool flushing_{false}; + std::atomic_bool runtime_error_{false}; + // Holds a referenceless pointer to the owning filter, the name and + // direction of the pin. The filter pointer can be considered const. + PIN_INFO info_ = {}; + AM_MEDIA_TYPE media_type_ RTC_GUARDED_BY(main_checker_) = {}; +}; + +// Implement IBaseFilter (including IPersist and IMediaFilter). +class CaptureSinkFilter : public IBaseFilter { + public: + CaptureSinkFilter(VideoCaptureImpl* capture_observer); + + HRESULT SetRequestedCapability(const VideoCaptureCapability& capability); + + // Called on the capture thread. + void ProcessCapturedFrame(unsigned char* buffer, + size_t length, + const VideoCaptureCapability& frame_info); + + void NotifyEvent(long code, LONG_PTR param1, LONG_PTR param2); + bool IsStopped() const; + + // IUnknown + STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override; + + // IPersist + STDMETHOD(GetClassID)(CLSID* clsid) override; + + // IMediaFilter. + STDMETHOD(GetState)(DWORD msecs, FILTER_STATE* state) override; + STDMETHOD(SetSyncSource)(IReferenceClock* clock) override; + STDMETHOD(GetSyncSource)(IReferenceClock** clock) override; + STDMETHOD(Pause)() override; + STDMETHOD(Run)(REFERENCE_TIME start) override; + STDMETHOD(Stop)() override; + + // IBaseFilter + STDMETHOD(EnumPins)(IEnumPins** pins) override; + STDMETHOD(FindPin)(LPCWSTR id, IPin** pin) override; + STDMETHOD(QueryFilterInfo)(FILTER_INFO* info) override; + STDMETHOD(JoinFilterGraph)(IFilterGraph* graph, LPCWSTR name) override; + STDMETHOD(QueryVendorInfo)(LPWSTR* vendor_info) override; + + protected: + virtual ~CaptureSinkFilter(); + + private: + SequenceChecker main_checker_; + const rtc::scoped_refptr<ComRefCount<CaptureInputPin>> input_pin_; + VideoCaptureImpl* const capture_observer_; + FILTER_INFO info_ RTC_GUARDED_BY(main_checker_) = {}; + // Set/cleared in JoinFilterGraph. The filter must be stopped (no capture) + // at that time, so no lock is required. While the state is not stopped, + // the sink will be used from the capture thread. + IMediaEventSink* sink_ = nullptr; + FILTER_STATE state_ RTC_GUARDED_BY(main_checker_) = State_Stopped; +}; +} // namespace videocapturemodule +} // namespace webrtc +#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_DS_H_ diff --git a/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.cc b/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.cc new file mode 100644 index 0000000000..b13ac074f8 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.cc @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_capture/windows/video_capture_ds.h" + +#include <dvdmedia.h> // VIDEOINFOHEADER2 + +#include "modules/video_capture/video_capture_config.h" +#include "modules/video_capture/windows/help_functions_ds.h" +#include "modules/video_capture/windows/sink_filter_ds.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace videocapturemodule { +VideoCaptureDS::VideoCaptureDS() + : _captureFilter(NULL), + _graphBuilder(NULL), + _mediaControl(NULL), + _inputSendPin(NULL), + _outputCapturePin(NULL), + _dvFilter(NULL), + _inputDvPin(NULL), + _outputDvPin(NULL) {} + +VideoCaptureDS::~VideoCaptureDS() { + if (_mediaControl) { + _mediaControl->Stop(); + } + if (_graphBuilder) { + if (sink_filter_) + _graphBuilder->RemoveFilter(sink_filter_.get()); + if (_captureFilter) + _graphBuilder->RemoveFilter(_captureFilter); + if (_dvFilter) + _graphBuilder->RemoveFilter(_dvFilter); + } + RELEASE_AND_CLEAR(_inputSendPin); + RELEASE_AND_CLEAR(_outputCapturePin); + + RELEASE_AND_CLEAR(_captureFilter); // release the capture device + RELEASE_AND_CLEAR(_dvFilter); + + RELEASE_AND_CLEAR(_mediaControl); + + RELEASE_AND_CLEAR(_inputDvPin); + RELEASE_AND_CLEAR(_outputDvPin); + + RELEASE_AND_CLEAR(_graphBuilder); +} + +int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8) { + const int32_t nameLength = (int32_t)strlen((char*)deviceUniqueIdUTF8); + if (nameLength >= kVideoCaptureUniqueNameLength) + return -1; + + // Store the device name + _deviceUniqueId = new (std::nothrow) char[nameLength + 1]; + memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); + + if (_dsInfo.Init() != 0) + return -1; + + _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8); + if (!_captureFilter) { + RTC_LOG(LS_INFO) << "Failed to create capture filter."; + return -1; + } + + // Get the interface for DirectShow's GraphBuilder + HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, + IID_IGraphBuilder, (void**)&_graphBuilder); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to create graph builder."; + return -1; + } + + hr = _graphBuilder->QueryInterface(IID_IMediaControl, (void**)&_mediaControl); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to create media control builder."; + return -1; + } + hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to add the capture device to the graph."; + return -1; + } + + _outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE); + if (!_outputCapturePin) { + RTC_LOG(LS_INFO) << "Failed to get output capture pin"; + return -1; + } + + // Create the sink filte used for receiving Captured frames. + sink_filter_ = new ComRefCount<CaptureSinkFilter>(this); + + hr = _graphBuilder->AddFilter(sink_filter_.get(), SINK_FILTER_NAME); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to add the send filter to the graph."; + return -1; + } + + _inputSendPin = GetInputPin(sink_filter_.get()); + if (!_inputSendPin) { + RTC_LOG(LS_INFO) << "Failed to get input send pin"; + return -1; + } + + if (SetCameraOutput(_requestedCapability) != 0) { + return -1; + } + RTC_LOG(LS_INFO) << "Capture device '" << deviceUniqueIdUTF8 + << "' initialized."; + return 0; +} + +int32_t VideoCaptureDS::StartCapture(const VideoCaptureCapability& capability) { + if (capability != _requestedCapability) { + DisconnectGraph(); + + if (SetCameraOutput(capability) != 0) { + return -1; + } + } + HRESULT hr = _mediaControl->Pause(); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) + << "Failed to Pause the Capture device. Is it already occupied? " << hr; + return -1; + } + hr = _mediaControl->Run(); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to start the Capture device."; + return -1; + } + return 0; +} + +int32_t VideoCaptureDS::StopCapture() { + HRESULT hr = _mediaControl->StopWhenReady(); + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to stop the capture graph. " << hr; + return -1; + } + return 0; +} + +bool VideoCaptureDS::CaptureStarted() { + OAFilterState state = 0; + HRESULT hr = _mediaControl->GetState(1000, &state); + if (hr != S_OK && hr != VFW_S_CANT_CUE) { + RTC_LOG(LS_INFO) << "Failed to get the CaptureStarted status"; + } + RTC_LOG(LS_INFO) << "CaptureStarted " << state; + return state == State_Running; +} + +int32_t VideoCaptureDS::CaptureSettings(VideoCaptureCapability& settings) { + settings = _requestedCapability; + return 0; +} + +int32_t VideoCaptureDS::SetCameraOutput( + const VideoCaptureCapability& requestedCapability) { + // Get the best matching capability + VideoCaptureCapability capability; + int32_t capabilityIndex; + + // Store the new requested size + _requestedCapability = requestedCapability; + // Match the requested capability with the supported. + if ((capabilityIndex = _dsInfo.GetBestMatchedCapability( + _deviceUniqueId, _requestedCapability, capability)) < 0) { + return -1; + } + // Reduce the frame rate if possible. + if (capability.maxFPS > requestedCapability.maxFPS) { + capability.maxFPS = requestedCapability.maxFPS; + } else if (capability.maxFPS <= 0) { + capability.maxFPS = 30; + } + + // Convert it to the windows capability index since they are not nexessary + // the same + VideoCaptureCapabilityWindows windowsCapability; + if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0) { + return -1; + } + + IAMStreamConfig* streamConfig = NULL; + AM_MEDIA_TYPE* pmt = NULL; + VIDEO_STREAM_CONFIG_CAPS caps; + + HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig, + (void**)&streamConfig); + if (hr) { + RTC_LOG(LS_INFO) << "Can't get the Capture format settings."; + return -1; + } + + // Get the windows capability from the capture device + bool isDVCamera = false; + hr = streamConfig->GetStreamCaps(windowsCapability.directShowCapabilityIndex, + &pmt, reinterpret_cast<BYTE*>(&caps)); + if (hr == S_OK) { + if (pmt->formattype == FORMAT_VideoInfo2) { + VIDEOINFOHEADER2* h = reinterpret_cast<VIDEOINFOHEADER2*>(pmt->pbFormat); + if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) { + h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS); + } + } else { + VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); + if (capability.maxFPS > 0 && windowsCapability.supportFrameRateControl) { + h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 / capability.maxFPS); + } + } + + // Set the sink filter to request this capability + sink_filter_->SetRequestedCapability(capability); + // Order the capture device to use this capability + hr += streamConfig->SetFormat(pmt); + + // Check if this is a DV camera and we need to add MS DV Filter + if (pmt->subtype == MEDIASUBTYPE_dvsl || + pmt->subtype == MEDIASUBTYPE_dvsd || + pmt->subtype == MEDIASUBTYPE_dvhd) { + isDVCamera = true; // This is a DV camera. Use MS DV filter + } + + FreeMediaType(pmt); + pmt = NULL; + } + RELEASE_AND_CLEAR(streamConfig); + + if (FAILED(hr)) { + RTC_LOG(LS_INFO) << "Failed to set capture device output format"; + return -1; + } + + if (isDVCamera) { + hr = ConnectDVCamera(); + } else { + hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin, NULL); + } + if (hr != S_OK) { + RTC_LOG(LS_INFO) << "Failed to connect the Capture graph " << hr; + return -1; + } + return 0; +} + +int32_t VideoCaptureDS::DisconnectGraph() { + HRESULT hr = _mediaControl->Stop(); + hr += _graphBuilder->Disconnect(_outputCapturePin); + hr += _graphBuilder->Disconnect(_inputSendPin); + + // if the DV camera filter exist + if (_dvFilter) { + _graphBuilder->Disconnect(_inputDvPin); + _graphBuilder->Disconnect(_outputDvPin); + } + if (hr != S_OK) { + RTC_LOG(LS_ERROR) + << "Failed to Stop the Capture device for reconfiguration " << hr; + return -1; + } + return 0; +} + +HRESULT VideoCaptureDS::ConnectDVCamera() { + HRESULT hr = S_OK; + + if (!_dvFilter) { + hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC, + IID_IBaseFilter, (void**)&_dvFilter); + if (hr != S_OK) { + RTC_LOG(LS_INFO) << "Failed to create the dv decoder: " << hr; + return hr; + } + hr = _graphBuilder->AddFilter(_dvFilter, L"VideoDecoderDV"); + if (hr != S_OK) { + RTC_LOG(LS_INFO) << "Failed to add the dv decoder to the graph: " << hr; + return hr; + } + _inputDvPin = GetInputPin(_dvFilter); + if (_inputDvPin == NULL) { + RTC_LOG(LS_INFO) << "Failed to get input pin from DV decoder"; + return -1; + } + _outputDvPin = GetOutputPin(_dvFilter, GUID_NULL); + if (_outputDvPin == NULL) { + RTC_LOG(LS_INFO) << "Failed to get output pin from DV decoder"; + return -1; + } + } + hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputDvPin, NULL); + if (hr != S_OK) { + RTC_LOG(LS_INFO) << "Failed to connect capture device to the dv devoder: " + << hr; + return hr; + } + + hr = _graphBuilder->ConnectDirect(_outputDvPin, _inputSendPin, NULL); + if (hr != S_OK) { + if (hr == HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES)) { + RTC_LOG(LS_INFO) << "Failed to connect the capture device, busy"; + } else { + RTC_LOG(LS_INFO) << "Failed to connect capture device to the send graph: " + << hr; + } + } + return hr; +} +} // namespace videocapturemodule +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.h b/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.h new file mode 100644 index 0000000000..0f01cfaf67 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/video_capture_ds.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_DS_H_ +#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_DS_H_ + +#include "api/scoped_refptr.h" +#include "modules/video_capture/video_capture_impl.h" +#include "modules/video_capture/windows/device_info_ds.h" + +#define CAPTURE_FILTER_NAME L"VideoCaptureFilter" +#define SINK_FILTER_NAME L"SinkFilter" + +namespace webrtc { +namespace videocapturemodule { +// Forward declaraion +class CaptureSinkFilter; + +class VideoCaptureDS : public VideoCaptureImpl { + public: + VideoCaptureDS(); + + virtual int32_t Init(const char* deviceUniqueIdUTF8); + + /************************************************************************* + * + * Start/Stop + * + *************************************************************************/ + int32_t StartCapture(const VideoCaptureCapability& capability) override; + int32_t StopCapture() override; + + /************************************************************************** + * + * Properties of the set device + * + **************************************************************************/ + + bool CaptureStarted() override; + int32_t CaptureSettings(VideoCaptureCapability& settings) override; + + protected: + ~VideoCaptureDS() override; + + // Help functions + + int32_t SetCameraOutput(const VideoCaptureCapability& requestedCapability); + int32_t DisconnectGraph(); + HRESULT ConnectDVCamera(); + + DeviceInfoDS _dsInfo; + + IBaseFilter* _captureFilter; + IGraphBuilder* _graphBuilder; + IMediaControl* _mediaControl; + rtc::scoped_refptr<CaptureSinkFilter> sink_filter_; + IPin* _inputSendPin; + IPin* _outputCapturePin; + + // Microsoft DV interface (external DV cameras) + IBaseFilter* _dvFilter; + IPin* _inputDvPin; + IPin* _outputDvPin; +}; +} // namespace videocapturemodule +} // namespace webrtc +#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_DS_H_ diff --git a/third_party/libwebrtc/modules/video_capture/windows/video_capture_factory_windows.cc b/third_party/libwebrtc/modules/video_capture/windows/video_capture_factory_windows.cc new file mode 100644 index 0000000000..481326c1d2 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/windows/video_capture_factory_windows.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "api/scoped_refptr.h" +#include "modules/video_capture/windows/video_capture_ds.h" + +namespace webrtc { +namespace videocapturemodule { + +// static +VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() { + // TODO(tommi): Use the Media Foundation version on Vista and up. + return DeviceInfoDS::Create(); +} + +rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create( + const char* device_id) { + if (device_id == nullptr) + return nullptr; + + // TODO(tommi): Use Media Foundation implementation for Vista and up. + auto capture = rtc::make_ref_counted<VideoCaptureDS>(); + if (capture->Init(device_id) != 0) { + return nullptr; + } + + return capture; +} + +} // namespace videocapturemodule +} // namespace webrtc |