blob: 15bda062bd34614262b0a969add9aa59ab4f4c1e [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <mfidl.h>
#include <ks.h>
#include <ksmedia.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfobjects.h>
#include <stddef.h>
#include <vidcap.h>
#include <wrl.h>
#include <wrl/client.h>
#include "base/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/win/windows_version.h"
#include "media/base/media_switches.h"
#include "media/capture/video/win/video_capture_device_factory_win.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
// MediaFoundation devices
const wchar_t* kMFDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
const wchar_t* kMFDeviceName0 = L"Device 0";
const wchar_t* kMFDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00";
const wchar_t* kMFDeviceName1 = L"Device 1";
const wchar_t* kMFDeviceId2 = L"\\\\?\\usb#vid_0002&pid_0002&mi_00";
const wchar_t* kMFDeviceName2 = L"Device 2";
const wchar_t* kMFDeviceId5 = L"\\\\?\\usb#vid_0005&pid_0005&mi_00";
const wchar_t* kMFDeviceName5 = L"Dazzle";
const wchar_t* kMFDeviceId6 = L"\\\\?\\usb#vid_eb1a&pid_2860&mi_00";
const wchar_t* kMFDeviceName6 = L"Empia Device";
// DirectShow devices
const wchar_t* kDirectShowDeviceId0 = L"\\\\?\\usb#vid_0000&pid_0000&mi_00";
const wchar_t* kDirectShowDeviceName0 = L"Device 0";
const wchar_t* kDirectShowDeviceId1 = L"\\\\?\\usb#vid_0001&pid_0001&mi_00#1";
const wchar_t* kDirectShowDeviceName1 = L"Device 1";
const wchar_t* kDirectShowDeviceId3 = L"Virtual Camera 3";
const wchar_t* kDirectShowDeviceName3 = L"Virtual Camera";
const wchar_t* kDirectShowDeviceId4 = L"Virtual Camera 4";
const wchar_t* kDirectShowDeviceName4 = L"Virtual Camera";
const wchar_t* kDirectShowDeviceId5 = L"\\\\?\\usb#vid_0005&pid_0005&mi_00#5";
const wchar_t* kDirectShowDeviceName5 = L"Dazzle";
const wchar_t* kDirectShowDeviceId6 = L"\\\\?\\usb#vid_eb1a&pid_2860&mi_00";
const wchar_t* kDirectShowDeviceName6 = L"Empia Device";
constexpr int kWidth = 1280;
constexpr int kHeight = 720;
constexpr int kFps = 30;
using iterator = std::vector<VideoCaptureDeviceInfo>::const_iterator;
iterator FindDeviceInRange(iterator begin,
iterator end,
const std::string& device_id) {
return std::find_if(begin, end,
[device_id](const VideoCaptureDeviceInfo& device_info) {
return device_id == device_info.descriptor.device_id;
});
}
template <class Interface>
Interface* AddReference(Interface* object) {
DCHECK(object);
object->AddRef();
return object;
}
template <class Interface>
class StubInterface
: public base::RefCountedThreadSafe<StubInterface<Interface>>,
public Interface {
public:
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** object) override {
if (riid == __uuidof(this) || riid == __uuidof(IUnknown)) {
*object = AddReference(this);
return S_OK;
}
return E_NOINTERFACE;
}
IFACEMETHODIMP_(ULONG) AddRef() override {
base::RefCountedThreadSafe<StubInterface>::AddRef();
return 1U;
}
IFACEMETHODIMP_(ULONG) Release() override {
base::RefCountedThreadSafe<StubInterface>::Release();
return 1U;
}
protected:
friend class base::RefCountedThreadSafe<StubInterface<Interface>>;
virtual ~StubInterface() = default;
};
template <class Interface>
class StubDeviceInterface : public StubInterface<Interface> {
public:
StubDeviceInterface(std::string device_id)
: device_id_(std::move(device_id)) {}
const std::string& device_id() const { return device_id_; }
protected:
~StubDeviceInterface() override = default;
private:
std::string device_id_;
};
// Stub IAMCameraControl with pan, tilt and zoom ranges for all devices except
// from Device 1.
class StubAMCameraControl final : public StubDeviceInterface<IAMCameraControl> {
public:
using StubDeviceInterface::StubDeviceInterface;
// IAMCameraControl
IFACEMETHODIMP Get(long property, long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetRange(long property,
long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
switch (property) {
case CameraControl_Pan:
case CameraControl_Tilt:
case CameraControl_Zoom:
if (device_id() != base::SysWideToUTF8(kMFDeviceId1)) {
*min = 100;
*max = 400;
*step = 1;
*default_value = 100;
*caps_flags = CameraControl_Flags_Manual;
return S_OK;
}
break;
}
return E_NOTIMPL;
}
IFACEMETHODIMP Set(long property, long value, long flags) override {
return E_NOTIMPL;
}
private:
~StubAMCameraControl() override = default;
};
class StubAMVideoProcAmp final : public StubDeviceInterface<IAMVideoProcAmp> {
public:
using StubDeviceInterface::StubDeviceInterface;
// IAMVideoProcAmp
IFACEMETHODIMP Get(long property, long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetRange(long property,
long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Set(long property, long value, long flags) override {
return E_NOTIMPL;
}
private:
~StubAMVideoProcAmp() override = default;
};
class StubMFActivate final : public StubInterface<IMFActivate> {
public:
StubMFActivate(const std::wstring& symbolic_link,
const std::wstring& name,
bool kscategory_video_camera,
bool kscategory_sensor_camera)
: symbolic_link_(symbolic_link),
name_(name),
kscategory_video_camera_(kscategory_video_camera),
kscategory_sensor_camera_(kscategory_sensor_camera) {}
bool MatchesQuery(IMFAttributes* query, HRESULT* status) {
UINT32 count;
*status = query->GetCount(&count);
if (FAILED(*status))
return false;
GUID value;
*status = query->GetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &value);
if (FAILED(*status))
return false;
if (value != MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)
return false;
*status = query->GetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
&value);
if (SUCCEEDED(*status)) {
if ((value == KSCATEGORY_SENSOR_CAMERA && kscategory_sensor_camera_) ||
(value == KSCATEGORY_VIDEO_CAMERA && kscategory_video_camera_))
return true;
} else if (*status == MF_E_ATTRIBUTENOTFOUND) {
// When no category attribute is specified, it should behave the same as
// if KSCATEGORY_VIDEO_CAMERA is specified.
*status = S_OK;
if (kscategory_video_camera_)
return true;
}
return false;
}
// IMFAttributes
IFACEMETHODIMP GetItem(REFGUID key, PROPVARIANT* value) override {
return E_FAIL;
}
IFACEMETHODIMP GetItemType(REFGUID guidKey,
MF_ATTRIBUTE_TYPE* pType) override {
return E_NOTIMPL;
}
IFACEMETHODIMP CompareItem(REFGUID guidKey,
REFPROPVARIANT Value,
BOOL* pbResult) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Compare(IMFAttributes* pTheirs,
MF_ATTRIBUTES_MATCH_TYPE MatchType,
BOOL* pbResult) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetUINT32(REFGUID key, UINT32* value) override {
if (key == MF_MT_INTERLACE_MODE) {
*value = MFVideoInterlace_Progressive;
return S_OK;
}
return E_NOTIMPL;
}
IFACEMETHODIMP GetUINT64(REFGUID key, UINT64* value) override {
return E_FAIL;
}
IFACEMETHODIMP GetDouble(REFGUID guidKey, double* pfValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetGUID(REFGUID key, GUID* value) override { return E_FAIL; }
IFACEMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetString(REFGUID guidKey,
LPWSTR pwszValue,
UINT32 cchBufSize,
UINT32* pcchLength) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetAllocatedString(REFGUID guidKey,
LPWSTR* ppwszValue,
UINT32* pcchLength) override {
std::wstring value;
if (guidKey == MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK) {
value = symbolic_link_;
} else if (guidKey == MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME) {
value = name_;
} else {
return E_NOTIMPL;
}
*ppwszValue = static_cast<wchar_t*>(
CoTaskMemAlloc((value.size() + 1) * sizeof(wchar_t)));
wcscpy(*ppwszValue, value.c_str());
*pcchLength = value.length();
return S_OK;
}
IFACEMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetBlob(REFGUID guidKey,
UINT8* pBuf,
UINT32 cbBufSize,
UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetAllocatedBlob(REFGUID guidKey,
UINT8** ppBuf,
UINT32* pcbSize) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetUnknown(REFGUID guidKey,
REFIID riid,
LPVOID* ppv) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) override {
return E_NOTIMPL;
}
IFACEMETHODIMP DeleteItem(REFGUID guidKey) override { return E_NOTIMPL; }
IFACEMETHODIMP DeleteAllItems(void) override { return E_NOTIMPL; }
IFACEMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetDouble(REFGUID guidKey, double fValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetBlob(REFGUID guidKey,
const UINT8* pBuf,
UINT32 cbBufSize) override {
return E_NOTIMPL;
}
IFACEMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown) override {
return E_NOTIMPL;
}
IFACEMETHODIMP LockStore(void) override { return E_NOTIMPL; }
IFACEMETHODIMP UnlockStore(void) override { return E_NOTIMPL; }
IFACEMETHODIMP GetCount(UINT32* pcItems) override { return E_NOTIMPL; }
IFACEMETHODIMP GetItemByIndex(UINT32 unIndex,
GUID* pguidKey,
PROPVARIANT* pValue) override {
return E_NOTIMPL;
}
IFACEMETHODIMP CopyAllItems(IMFAttributes* pDest) override {
return E_NOTIMPL;
}
// IMFActivate
IFACEMETHODIMP ActivateObject(REFIID riid, void** ppv) override {
return E_NOTIMPL;
}
IFACEMETHODIMP DetachObject(void) override { return E_NOTIMPL; }
IFACEMETHODIMP ShutdownObject(void) override { return E_NOTIMPL; }
private:
~StubMFActivate() override = default;
const std::wstring symbolic_link_;
const std::wstring name_;
const bool kscategory_video_camera_;
const bool kscategory_sensor_camera_;
};
// Stub IMFMediaSourceEx with IAMCameraControl and IAMVideoProcAmp interfaces
// for all devices except from Device 0.
class StubMFMediaSource final : public StubDeviceInterface<IMFMediaSourceEx> {
public:
StubMFMediaSource(std::string device_id,
std::vector<VideoPixelFormat> native_formats)
: StubDeviceInterface(device_id),
native_formats_(std::move(native_formats)) {
// If no native formats were specified, default to I420
if (native_formats_.size() == 0) {
native_formats_.push_back(PIXEL_FORMAT_I420);
}
}
using StubDeviceInterface::StubDeviceInterface;
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** object) override {
if (device_id() != base::SysWideToUTF8(kMFDeviceId0)) {
if (riid == __uuidof(IAMCameraControl)) {
*object = AddReference(new StubAMCameraControl(device_id()));
return S_OK;
}
if (riid == __uuidof(IAMVideoProcAmp)) {
*object = AddReference(new StubAMVideoProcAmp(device_id()));
return S_OK;
}
}
if (riid == __uuidof(IMFMediaSource)) {
*object = AddReference(static_cast<IMFMediaSource*>(this));
return S_OK;
}
if (riid == _uuidof(IMFMediaEventGenerator)) {
*object = AddReference(static_cast<IMFMediaEventGenerator*>(this));
return S_OK;
}
return StubDeviceInterface::QueryInterface(riid, object);
}
// IMFMediaEventGenerator
IFACEMETHODIMP BeginGetEvent(IMFAsyncCallback* callback,
IUnknown* state) override {
return S_OK;
}
IFACEMETHODIMP EndGetEvent(IMFAsyncResult* result,
IMFMediaEvent** event) override {
return S_OK;
}
IFACEMETHODIMP GetEvent(DWORD flags, IMFMediaEvent** event) override {
return S_OK;
}
IFACEMETHODIMP QueueEvent(MediaEventType met,
REFGUID extended_type,
HRESULT status,
const PROPVARIANT* value) override {
return S_OK;
}
// IMFMediaSource
IFACEMETHODIMP CreatePresentationDescriptor(
IMFPresentationDescriptor** presentation_descriptor) override {
HRESULT hr = S_OK;
std::vector<Microsoft::WRL::ComPtr<IMFMediaType>> media_types;
std::vector<IMFMediaType*> media_type_list;
for (const VideoPixelFormat& pixel_format : native_formats_) {
Microsoft::WRL::ComPtr<IMFMediaType> media_type;
hr = MFCreateMediaType(&media_type);
if (FAILED(hr)) {
return hr;
}
hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
if (FAILED(hr)) {
return hr;
}
GUID subType = GUID_NULL;
switch (pixel_format) {
case PIXEL_FORMAT_I420:
subType = MFVideoFormat_I420;
break;
case PIXEL_FORMAT_NV12:
subType = MFVideoFormat_NV12;
break;
default:
break;
}
hr = media_type->SetGUID(MF_MT_SUBTYPE, subType);
if (FAILED(hr)) {
return hr;
}
hr = MFSetAttributeSize(media_type.Get(), MF_MT_FRAME_SIZE, kWidth,
kHeight);
if (FAILED(hr)) {
return hr;
}
hr = MFSetAttributeRatio(media_type.Get(), MF_MT_FRAME_RATE, kFps, 1);
if (FAILED(hr)) {
return hr;
}
media_types.push_back(media_type);
media_type_list.push_back(media_type.Get());
}
if (media_type_list.empty()) {
ADD_FAILURE() << "media_type_list empty";
return MF_E_UNEXPECTED;
}
Microsoft::WRL::ComPtr<IMFStreamDescriptor> stream_descriptor;
hr = MFCreateStreamDescriptor(0, media_type_list.size(),
&media_type_list[0], &stream_descriptor);
if (FAILED(hr)) {
return hr;
}
IMFStreamDescriptor* stream_descriptors = stream_descriptor.Get();
return MFCreatePresentationDescriptor(1, &stream_descriptors,
presentation_descriptor);
}
IFACEMETHODIMP GetCharacteristics(DWORD* characteristics) override {
return S_OK;
}
IFACEMETHODIMP Pause() override { return S_OK; }
IFACEMETHODIMP Shutdown() override { return S_OK; }
IFACEMETHODIMP Start(IMFPresentationDescriptor* presentation_descriptor,
const GUID* time_format,
const PROPVARIANT* start_position) override {
return S_OK;
}
IFACEMETHODIMP Stop() override { return S_OK; }
// IMFMediaSourceEx
IFACEMETHODIMP GetSourceAttributes(IMFAttributes** attributes) {
return E_NOTIMPL;
}
IFACEMETHODIMP GetStreamAttributes(DWORD stream_id,
IMFAttributes** attributes) {
return E_NOTIMPL;
}
IFACEMETHODIMP SetD3DManager(IUnknown* manager) { return S_OK; }
private:
~StubMFMediaSource() override = default;
std::vector<VideoPixelFormat> native_formats_;
};
// Stub ICameraControl with pan, tilt and zoom range for all devices except
// from Device 5.
class StubCameraControl final : public StubDeviceInterface<ICameraControl> {
public:
using StubDeviceInterface::StubDeviceInterface;
// ICameraControl
IFACEMETHODIMP get_Exposure(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_ExposureRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_FocalLengths(long* ocular_focal_length,
long* objective_focal_length_min,
long* objective_focal_length_max) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Focus(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_FocusRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Iris(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_IrisRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Pan(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_PanRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_PanTilt(long* pan_value,
long* tilt_value,
long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_PanTiltRelative(long* pan_value,
long* tilt_value,
long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_PrivacyMode(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Roll(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_RollRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_ScanMode(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Tilt(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_TiltRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Zoom(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_ZoomRelative(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Exposure(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_ExposureRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Focus(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_FocusRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Iris(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_IrisRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Pan(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
if (device_id() != base::SysWideToUTF8(kDirectShowDeviceId5)) {
*min = 100;
*max = 400;
*step = 1;
*default_value = 100;
*caps_flags = CameraControl_Flags_Manual;
return S_OK;
}
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_PanRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Roll(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_RollRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Tilt(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
if (device_id() != base::SysWideToUTF8(kDirectShowDeviceId5)) {
*min = 100;
*max = 400;
*step = 1;
*default_value = 100;
*caps_flags = CameraControl_Flags_Manual;
return S_OK;
}
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_TiltRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Zoom(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
if (device_id() != base::SysWideToUTF8(kDirectShowDeviceId5)) {
*min = 100;
*max = 400;
*step = 1;
*default_value = 100;
*caps_flags = CameraControl_Flags_Manual;
return S_OK;
}
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_ZoomRelative(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Exposure(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_ExposureRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Focus(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_FocusRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Iris(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_IrisRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Pan(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_PanRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_PanTilt(long pan_value,
long tilt_value,
long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_PanTiltRelative(long pan_value,
long tilt_value,
long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_PrivacyMode(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Roll(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_RollRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_ScanMode(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Tilt(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_TiltRelative(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Zoom(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_ZoomRelative(long value, long flags) override {
return E_NOTIMPL;
}
private:
~StubCameraControl() override = default;
};
class StubVideoProcAmp final : public StubDeviceInterface<IVideoProcAmp> {
public:
using StubDeviceInterface::StubDeviceInterface;
// IVideoProcAmp
IFACEMETHODIMP get_BacklightCompensation(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Brightness(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_ColorEnable(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Contrast(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_DigitalMultiplier(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Gain(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Gamma(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Hue(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_PowerlineFrequency(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Saturation(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_Sharpness(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_WhiteBalance(long* value, long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_WhiteBalanceComponent(long* value1,
long* value2,
long* flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_BacklightCompensation(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Brightness(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_ColorEnable(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Contrast(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_DigitalMultiplier(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Gain(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Gamma(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Hue(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_PowerlineFrequency(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Saturation(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_Sharpness(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_WhiteBalance(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP getRange_WhiteBalanceComponent(long* min,
long* max,
long* step,
long* default_value,
long* caps_flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_BacklightCompensation(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Brightness(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_ColorEnable(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Contrast(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_DigitalMultiplier(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Gain(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_Gamma(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Hue(long value, long flags) override { return E_NOTIMPL; }
IFACEMETHODIMP put_PowerlineFrequency(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Saturation(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_Sharpness(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_WhiteBalance(long value, long flags) override {
return E_NOTIMPL;
}
IFACEMETHODIMP put_WhiteBalanceComponent(long value1,
long value2,
long flags) override {
return E_NOTIMPL;
}
private:
~StubVideoProcAmp() override = default;
};
// Stub IKsTopologyInfo with 2 nodes.
// For all devices except from Device 3, the first node is
// a KSNODETYPE_VIDEO_CAMERA_TERMINAL (ICameraControl) node.
// For all devices except from Device 4, the second node is
// a KSNODETYPE_VIDEO_PROCESSING (IVideoProcAmp) node.
class StubKsTopologyInfo final : public StubDeviceInterface<IKsTopologyInfo> {
public:
enum { kNumNodes = 2 };
using StubDeviceInterface::StubDeviceInterface;
// IKsTopologyInfo
IFACEMETHODIMP CreateNodeInstance(DWORD node_id,
REFIID iid,
void** object) override {
GUID node_type;
HRESULT hr = get_NodeType(node_id, &node_type);
if (FAILED(hr))
return hr;
if (node_type == KSNODETYPE_VIDEO_CAMERA_TERMINAL) {
EXPECT_EQ(iid, __uuidof(ICameraControl));
*object = AddReference(new StubCameraControl(device_id()));
return S_OK;
}
if (node_type == KSNODETYPE_VIDEO_PROCESSING) {
EXPECT_EQ(iid, __uuidof(IVideoProcAmp));
*object = AddReference(new StubVideoProcAmp(device_id()));
return S_OK;
}
NOTREACHED();
return E_NOTIMPL;
}
IFACEMETHODIMP get_Category(DWORD index, GUID* category) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_ConnectionInfo(
DWORD index,
KSTOPOLOGY_CONNECTION* connection_info) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_NodeName(DWORD node_id,
WCHAR* node_name,
DWORD buf_size,
DWORD* name_len) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_NodeType(DWORD node_id, GUID* node_type) override {
EXPECT_LT(node_id, kNumNodes);
switch (node_id) {
case 0:
*node_type = device_id() != base::SysWideToUTF8(kDirectShowDeviceId3)
? KSNODETYPE_VIDEO_CAMERA_TERMINAL
: KSNODETYPE_DEV_SPECIFIC;
return S_OK;
case 1:
*node_type = device_id() != base::SysWideToUTF8(kDirectShowDeviceId4)
? KSNODETYPE_VIDEO_PROCESSING
: KSNODETYPE_DEV_SPECIFIC;
return S_OK;
}
NOTREACHED();
return E_NOTIMPL;
}
IFACEMETHODIMP get_NumCategories(DWORD* num_categories) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_NumConnections(DWORD* num_connections) override {
return E_NOTIMPL;
}
IFACEMETHODIMP get_NumNodes(DWORD* num_nodes) override {
*num_nodes = kNumNodes;
return S_OK;
}
private:
~StubKsTopologyInfo() override = default;
};
// Stub IBaseFilter with IKsTopologyInfo interface.
class StubBaseFilter final : public StubDeviceInterface<IBaseFilter> {
public:
using StubDeviceInterface::StubDeviceInterface;
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** object) override {
if (riid == __uuidof(IKsTopologyInfo)) {
*object = AddReference(new StubKsTopologyInfo(device_id()));
return S_OK;
}
return StubDeviceInterface::QueryInterface(riid, object);
}
// IPersist
IFACEMETHODIMP GetClassID(CLSID* class_id) override { return E_NOTIMPL; }
// IMediaFilter
IFACEMETHODIMP GetState(DWORD milli_secs_timeout,
FILTER_STATE* State) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetSyncSource(IReferenceClock** clock) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Pause() override { return E_NOTIMPL; }
IFACEMETHODIMP Run(REFERENCE_TIME start) override { return E_NOTIMPL; }
IFACEMETHODIMP SetSyncSource(IReferenceClock* clock) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Stop() override { return E_NOTIMPL; }
// IBaseFilter
IFACEMETHODIMP EnumPins(IEnumPins** enum_pins) override { return E_NOTIMPL; }
IFACEMETHODIMP FindPin(LPCWSTR id, IPin** pin) override { return E_NOTIMPL; }
IFACEMETHODIMP JoinFilterGraph(IFilterGraph* graph, LPCWSTR name) override {
return E_NOTIMPL;
}
IFACEMETHODIMP QueryFilterInfo(FILTER_INFO* info) override {
return E_NOTIMPL;
}
IFACEMETHODIMP QueryVendorInfo(LPWSTR* vendor_info) override {
return E_NOTIMPL;
}
private:
~StubBaseFilter() override = default;
};
class StubPropertyBag final : public StubInterface<IPropertyBag> {
public:
StubPropertyBag(const wchar_t* device_path, const wchar_t* description)
: device_path_(device_path), description_(description) {}
IFACEMETHODIMP Read(LPCOLESTR pszPropName,
VARIANT* pVar,
IErrorLog* pErrorLog) override {
if (pszPropName == std::wstring(L"Description")) {
pVar->vt = VT_BSTR;
pVar->bstrVal = SysAllocString(description_);
return S_OK;
}
if (pszPropName == std::wstring(L"DevicePath")) {
pVar->vt = VT_BSTR;
pVar->bstrVal = SysAllocString(device_path_);
return S_OK;
}
return E_NOTIMPL;
}
IFACEMETHODIMP Write(LPCOLESTR pszPropName, VARIANT* pVar) override {
return E_NOTIMPL;
}
private:
~StubPropertyBag() override = default;
const wchar_t* device_path_;
const wchar_t* description_;
};
class StubMoniker final : public StubInterface<IMoniker> {
public:
StubMoniker(const wchar_t* device_path, const wchar_t* description)
: device_path_(device_path), description_(description) {}
IFACEMETHODIMP GetClassID(CLSID* pClassID) override { return E_NOTIMPL; }
IFACEMETHODIMP IsDirty(void) override { return E_NOTIMPL; }
IFACEMETHODIMP Load(IStream* pStm) override { return E_NOTIMPL; }
IFACEMETHODIMP Save(IStream* pStm, BOOL fClearDirty) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetSizeMax(ULARGE_INTEGER* pcbSize) override {
return E_NOTIMPL;
}
IFACEMETHODIMP BindToObject(IBindCtx* pbc,
IMoniker* pmkToLeft,
REFIID riidResult,
void** ppvResult) override {
if (riidResult == __uuidof(IBaseFilter)) {
*ppvResult =
AddReference(new StubBaseFilter(base::SysWideToUTF8(device_path_)));
return S_OK;
}
return MK_E_NOOBJECT;
}
IFACEMETHODIMP BindToStorage(IBindCtx* pbc,
IMoniker* pmkToLeft,
REFIID riid,
void** ppvObj) override {
*ppvObj = AddReference(new StubPropertyBag(device_path_, description_));
return S_OK;
}
IFACEMETHODIMP Reduce(IBindCtx* pbc,
DWORD dwReduceHowFar,
IMoniker** ppmkToLeft,
IMoniker** ppmkReduced) override {
return E_NOTIMPL;
}
IFACEMETHODIMP ComposeWith(IMoniker* pmkRight,
BOOL fOnlyIfNotGeneric,
IMoniker** ppmkComposite) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Enum(BOOL fForward, IEnumMoniker** ppenumMoniker) override {
return E_NOTIMPL;
}
IFACEMETHODIMP IsEqual(IMoniker* pmkOtherMoniker) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Hash(DWORD* pdwHash) override { return E_NOTIMPL; }
IFACEMETHODIMP IsRunning(IBindCtx* pbc,
IMoniker* pmkToLeft,
IMoniker* pmkNewlyRunning) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetTimeOfLastChange(IBindCtx* pbc,
IMoniker* pmkToLeft,
FILETIME* pFileTime) override {
return E_NOTIMPL;
}
IFACEMETHODIMP Inverse(IMoniker** ppmk) override { return E_NOTIMPL; }
IFACEMETHODIMP CommonPrefixWith(IMoniker* pmkOther,
IMoniker** ppmkPrefix) override {
return E_NOTIMPL;
}
IFACEMETHODIMP RelativePathTo(IMoniker* pmkOther,
IMoniker** ppmkRelPath) override {
return E_NOTIMPL;
}
IFACEMETHODIMP GetDisplayName(IBindCtx* pbc,
IMoniker* pmkToLeft,
LPOLESTR* ppszDisplayName) override {
return E_NOTIMPL;
}
IFACEMETHODIMP ParseDisplayName(IBindCtx* pbc,
IMoniker* pmkToLeft,
LPOLESTR pszDisplayName,
ULONG* pchEaten,
IMoniker** ppmkOut) override {
return E_NOTIMPL;
}
IFACEMETHODIMP IsSystemMoniker(DWORD* pdwMksys) override { return E_NOTIMPL; }
private:
~StubMoniker() override = default;
const wchar_t* device_path_;
const wchar_t* description_;
};
class StubEnumMoniker : public StubInterface<IEnumMoniker> {
public:
StubEnumMoniker(std::vector<scoped_refptr<StubMoniker>> monikers)
: monikers_(std::move(monikers)) {}
IFACEMETHODIMP Next(ULONG celt,
IMoniker** rgelt,
ULONG* celt_fetched) override {
if (cursor_position_ >= monikers_.size())
return S_FALSE;
const ULONG original_cursor_position = cursor_position_;
while (celt-- > 0 && cursor_position_ < monikers_.size())
*rgelt++ = AddReference(monikers_[cursor_position_++].get());
if (celt_fetched)
*celt_fetched = cursor_position_ - original_cursor_position;
return S_OK;
}
IFACEMETHODIMP Skip(ULONG celt) override { return E_NOTIMPL; }
IFACEMETHODIMP Reset(void) override { return E_NOTIMPL; }
IFACEMETHODIMP Clone(IEnumMoniker** enum_moniker) override {
return E_NOTIMPL;
}
private:
~StubEnumMoniker() override = default;
std::vector<scoped_refptr<StubMoniker>> monikers_;
ULONG cursor_position_ = 0;
};
class FakeVideoCaptureDeviceFactoryWin : public VideoCaptureDeviceFactoryWin {
public:
void set_disable_get_supported_formats_mf_mocking(
bool disable_get_supported_formats_mf_mocking) {
disable_get_supported_formats_mf_mocking_ =
disable_get_supported_formats_mf_mocking;
}
void AddNativeFormatForMfDevice(std::wstring device_id,
VideoPixelFormat format) {
device_source_native_formats_[device_id].push_back(format);
}
protected:
bool CreateDeviceEnumMonikerDirectShow(IEnumMoniker** enum_moniker) override {
*enum_moniker = AddReference(new StubEnumMoniker(
{base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId0,
kDirectShowDeviceName0),
base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId1,
kDirectShowDeviceName1),
base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId3,
kDirectShowDeviceName3),
base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId4,
kDirectShowDeviceName4),
base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId5,
kDirectShowDeviceName5),
base::MakeRefCounted<StubMoniker>(kDirectShowDeviceId6,
kDirectShowDeviceName6)}));
return true;
}
bool CreateDeviceSourceMediaFoundation(
Microsoft::WRL::ComPtr<IMFAttributes> attributes,
IMFMediaSource** source) override {
UINT32 length;
if (FAILED(attributes->GetStringLength(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&length))) {
return false;
}
std::wstring symbolic_link(length, wchar_t());
if (FAILED(attributes->GetString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&symbolic_link[0], length + 1, &length))) {
return false;
}
const bool has_dxgi_device_manager =
static_cast<bool>(dxgi_device_manager_for_testing());
if (use_d3d11_with_media_foundation_for_testing() !=
has_dxgi_device_manager) {
return false;
}
*source = AddReference(
new StubMFMediaSource(base::SysWideToUTF8(symbolic_link),
device_source_native_formats_[symbolic_link]));
return true;
}
bool EnumerateDeviceSourcesMediaFoundation(
Microsoft::WRL::ComPtr<IMFAttributes> attributes,
IMFActivate*** devices,
UINT32* count) override {
std::vector<scoped_refptr<StubMFActivate>> stub_devices = {
base::MakeRefCounted<StubMFActivate>(kMFDeviceId0, kMFDeviceName0, true,
false),
base::MakeRefCounted<StubMFActivate>(kMFDeviceId1, kMFDeviceName1, true,
true),
base::MakeRefCounted<StubMFActivate>(kMFDeviceId2, kMFDeviceName2,
false, true),
base::MakeRefCounted<StubMFActivate>(kMFDeviceId5, kMFDeviceName5, true,
false),
base::MakeRefCounted<StubMFActivate>(kMFDeviceId6, kMFDeviceName6, true,
false)};
// Iterate once to get the match count and check for errors.
*count = 0U;
HRESULT hr;
for (auto& device : stub_devices) {
if (device->MatchesQuery(attributes.Get(), &hr))
(*count)++;
if (FAILED(hr))
return false;
}
// Second iteration packs the returned devices and increments their
// reference count.
*devices = static_cast<IMFActivate**>(
CoTaskMemAlloc(sizeof(IMFActivate*) * (*count)));
int offset = 0;
for (auto& device : stub_devices) {
if (!device->MatchesQuery(attributes.Get(), &hr))
continue;
*(*devices + offset++) = AddReference(device.get());
}
return true;
}
VideoCaptureFormats GetSupportedFormatsDirectShow(
Microsoft::WRL::ComPtr<IBaseFilter> capture_filter,
const std::string& display_name) override {
VideoCaptureFormats supported_formats;
if (display_name == base::SysWideToUTF8(kDirectShowDeviceName5)) {
VideoCaptureFormat arbitrary_format;
supported_formats.emplace_back(arbitrary_format);
}
return supported_formats;
}
VideoCaptureFormats GetSupportedFormatsMediaFoundation(
Microsoft::WRL::ComPtr<IMFMediaSource> source,
const std::string& display_name) override {
if (disable_get_supported_formats_mf_mocking_) {
return VideoCaptureDeviceFactoryWin::GetSupportedFormatsMediaFoundation(
source, display_name);
}
VideoCaptureFormats supported_formats;
if (display_name == base::SysWideToUTF8(kMFDeviceName6)) {
VideoCaptureFormat arbitrary_format;
supported_formats.emplace_back(arbitrary_format);
}
return supported_formats;
}
bool disable_get_supported_formats_mf_mocking_ = false;
std::map<std::wstring, std::vector<VideoPixelFormat>>
device_source_native_formats_;
};
} // namespace
class VideoCaptureDeviceFactoryWinTest : public ::testing::Test {
protected:
VideoCaptureDeviceFactoryWinTest()
: media_foundation_supported_(
VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()) {}
bool ShouldSkipMFTest() {
if (media_foundation_supported_)
return false;
DVLOG(1) << "Media foundation is not supported by the current platform. "
"Skipping test.";
return true;
}
bool ShouldSkipD3D11Test() {
// D3D11 is only supported with Media Foundation on Windows 8 or later
if (base::win::GetVersion() >= base::win::Version::WIN8)
return false;
DVLOG(1) << "D3D11 with Media foundation is not supported by the current "
"platform. "
"Skipping test.";
return true;
}
base::test::TaskEnvironment task_environment_;
FakeVideoCaptureDeviceFactoryWin factory_;
const bool media_foundation_supported_;
};
class VideoCaptureDeviceFactoryMFWinTest
: public VideoCaptureDeviceFactoryWinTest,
public testing::WithParamInterface<bool> {
void SetUp() override {
VideoCaptureDeviceFactoryWinTest::SetUp();
factory_.set_use_media_foundation_for_testing(true);
}
};
TEST_P(VideoCaptureDeviceFactoryMFWinTest, GetDevicesInfo) {
if (ShouldSkipMFTest())
return;
const bool use_d3d11 = GetParam();
if (use_d3d11 && ShouldSkipD3D11Test())
return;
factory_.set_use_d3d11_with_media_foundation_for_testing(use_d3d11);
std::vector<VideoCaptureDeviceInfo> devices_info;
base::RunLoop run_loop;
factory_.GetDevicesInfo(base::BindLambdaForTesting(
[&devices_info, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
devices_info = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(devices_info.size(), 6U);
for (auto it = devices_info.begin(); it != devices_info.end(); it++) {
// Verify that there are no duplicates.
EXPECT_EQ(
FindDeviceInRange(devices_info.begin(), it, it->descriptor.device_id),
it);
}
iterator it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId0));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName0));
// No IAMCameraControl and no IAMVideoProcAmp interfaces.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId1));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName1));
// No pan/tilt/zoom in IAMCameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId3));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName3));
// No ICameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId4));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName4));
// No IVideoProcAmp interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
// Devices that are listed in MediaFoundation but only report supported
// formats in DirectShow are expected to get enumerated with
// VideoCaptureApi::WIN_DIRECT_SHOW
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId5));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName5));
// No pan, tilt, or zoom ranges in ICameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
// Devices that are listed in both MediaFoundation and DirectShow but are
// blocked for use with MediaFoundation are expected to get enumerated with
// VideoCaptureApi::WIN_DIRECT_SHOW.
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId6));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName6));
EXPECT_TRUE(it->descriptor.control_support().pan);
EXPECT_TRUE(it->descriptor.control_support().tilt);
EXPECT_TRUE(it->descriptor.control_support().zoom);
}
TEST_P(VideoCaptureDeviceFactoryMFWinTest, GetDevicesInfo_IncludeIRCameras) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kIncludeIRCamerasInDeviceEnumeration);
if (ShouldSkipMFTest())
return;
const bool use_d3d11 = GetParam();
if (use_d3d11 && ShouldSkipD3D11Test())
return;
factory_.set_use_d3d11_with_media_foundation_for_testing(use_d3d11);
std::vector<VideoCaptureDeviceInfo> devices_info;
base::RunLoop run_loop;
factory_.GetDevicesInfo(base::BindLambdaForTesting(
[&devices_info, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
devices_info = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(devices_info.size(), 7U);
for (auto it = devices_info.begin(); it != devices_info.end(); it++) {
// Verify that there are no duplicates.
EXPECT_EQ(
FindDeviceInRange(devices_info.begin(), it, it->descriptor.device_id),
it);
}
iterator it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId0));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName0));
// No IAMCameraControl and no IAMVideoProcAmp interfaces.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId1));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_MEDIA_FOUNDATION);
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName1));
// No pan/tilt/zoom in IAMCameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId2));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api,
VideoCaptureApi::WIN_MEDIA_FOUNDATION_SENSOR);
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName2));
EXPECT_TRUE(it->descriptor.control_support().pan);
EXPECT_TRUE(it->descriptor.control_support().tilt);
EXPECT_TRUE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId3));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName3));
// No ICameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId4));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName4));
// No IVideoProcAmp interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
// Devices that are listed in MediaFoundation but only report supported
// formats in DirectShow are expected to get enumerated with
// VideoCaptureApi::WIN_DIRECT_SHOW
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId5));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName5));
// No pan, tilt, or zoom ranges in ICameraControl interface.
EXPECT_FALSE(it->descriptor.control_support().pan);
EXPECT_FALSE(it->descriptor.control_support().tilt);
EXPECT_FALSE(it->descriptor.control_support().zoom);
// Devices that are listed in both MediaFoundation and DirectShow but are
// blocked for use with MediaFoundation are expected to get enumerated with
// VideoCaptureApi::WIN_DIRECT_SHOW.
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kDirectShowDeviceId6));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.capture_api, VideoCaptureApi::WIN_DIRECT_SHOW);
EXPECT_EQ(it->descriptor.display_name(),
base::SysWideToUTF8(kDirectShowDeviceName6));
EXPECT_TRUE(it->descriptor.control_support().pan);
EXPECT_TRUE(it->descriptor.control_support().tilt);
EXPECT_TRUE(it->descriptor.control_support().zoom);
}
TEST_P(VideoCaptureDeviceFactoryMFWinTest,
DeviceSupportedFormatNV12Passthrough) {
if (ShouldSkipMFTest())
return;
if (ShouldSkipD3D11Test())
return;
// Test whether the VideoCaptureDeviceFactory passes through NV12 as the
// output pixel format when D3D11 support is enabled
const bool use_d3d11 = GetParam();
factory_.set_use_d3d11_with_media_foundation_for_testing(use_d3d11);
factory_.set_disable_get_supported_formats_mf_mocking(true);
// Specify native NV12 format for first device and I420 for others
factory_.AddNativeFormatForMfDevice(kMFDeviceId0, PIXEL_FORMAT_NV12);
factory_.AddNativeFormatForMfDevice(kMFDeviceId1, PIXEL_FORMAT_I420);
const VideoPixelFormat expected_pixel_format_for_nv12 =
use_d3d11 ? PIXEL_FORMAT_NV12 : PIXEL_FORMAT_I420;
std::vector<VideoCaptureDeviceInfo> devices_info;
base::RunLoop run_loop;
factory_.GetDevicesInfo(base::BindLambdaForTesting(
[&devices_info, &run_loop](std::vector<VideoCaptureDeviceInfo> result) {
devices_info = std::move(result);
run_loop.Quit();
}));
run_loop.Run();
// Verify that the pixel formats advertised in supported_formats for each
// device match the expected format (NV12 when D3D11 support is enabled and
// the native source type for the device is NV12 or I420 in all other cases)
iterator it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId0));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName0));
EXPECT_FALSE(it->supported_formats.empty());
for (size_t i = 0; i < it->supported_formats.size(); i++) {
SCOPED_TRACE(i);
EXPECT_EQ(it->supported_formats[i].pixel_format,
expected_pixel_format_for_nv12);
}
it = FindDeviceInRange(devices_info.begin(), devices_info.end(),
base::SysWideToUTF8(kMFDeviceId1));
ASSERT_NE(it, devices_info.end());
EXPECT_EQ(it->descriptor.display_name(), base::SysWideToUTF8(kMFDeviceName1));
EXPECT_FALSE(it->supported_formats.empty());
for (size_t i = 0; i < it->supported_formats.size(); i++) {
SCOPED_TRACE(i);
EXPECT_EQ(it->supported_formats[i].pixel_format,
expected_pixel_format_for_nv12);
}
}
INSTANTIATE_TEST_SUITE_P(VideoCaptureDeviceFactoryMFWinTests,
VideoCaptureDeviceFactoryMFWinTest,
testing::Bool());
} // namespace media