blob: cada827a2472071cc8b33cc32eefce66221a59ea [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 <mfapi.h>
#include <mferror.h>
#include <stddef.h>
#include <wincodec.h>
#include "media/capture/video/win/video_capture_device_factory_win.h"
#include "media/capture/video/win/video_capture_device_mf_win.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::AtLeast;
using ::testing::Mock;
using Microsoft::WRL::ComPtr;
namespace media {
namespace {
class MockClient : public VideoCaptureDevice::Client {
public:
void OnIncomingCapturedData(const uint8_t* data,
int length,
const VideoCaptureFormat& frame_format,
int clockwise_rotation,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
int frame_feedback_id = 0) override {}
MOCK_METHOD4(
ReserveOutputBuffer,
Buffer(const gfx::Size&, VideoPixelFormat, VideoPixelStorage, int));
void OnIncomingCapturedBuffer(Buffer buffer,
const VideoCaptureFormat& format,
base::TimeTicks reference_,
base::TimeDelta timestamp) override {}
void OnIncomingCapturedBufferExt(
Buffer buffer,
const VideoCaptureFormat& format,
base::TimeTicks reference_time,
base::TimeDelta timestamp,
gfx::Rect visible_rect,
const VideoFrameMetadata& additional_metadata) override {}
MOCK_METHOD4(
ResurrectLastOutputBuffer,
Buffer(const gfx::Size&, VideoPixelFormat, VideoPixelStorage, int));
MOCK_METHOD2(OnError, void(const base::Location&, const std::string&));
double GetBufferPoolUtilization() const override { return 0.0; }
MOCK_METHOD0(OnStarted, void());
};
class MockImageCaptureClient
: public base::RefCountedThreadSafe<MockImageCaptureClient> {
public:
// GMock doesn't support move-only arguments, so we use this forward method.
void DoOnGetPhotoState(mojom::PhotoStatePtr received_state) {
state = std::move(received_state);
}
MOCK_METHOD1(OnCorrectSetPhotoOptions, void(bool));
// GMock doesn't support move-only arguments, so we use this forward method.
void DoOnPhotoTaken(mojom::BlobPtr blob) {
EXPECT_TRUE(blob);
OnCorrectPhotoTaken();
}
MOCK_METHOD0(OnCorrectPhotoTaken, void(void));
mojom::PhotoStatePtr state;
private:
friend class base::RefCountedThreadSafe<MockImageCaptureClient>;
virtual ~MockImageCaptureClient() = default;
};
class MockMFMediaSource : public base::RefCountedThreadSafe<MockMFMediaSource>,
public IMFMediaSource {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockMFMediaSource>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockMFMediaSource>::Release();
return 1U;
}
STDMETHOD(GetEvent)(DWORD dwFlags, IMFMediaEvent** ppEvent) override {
return E_NOTIMPL;
}
STDMETHOD(BeginGetEvent)
(IMFAsyncCallback* pCallback, IUnknown* punkState) override {
return E_NOTIMPL;
}
STDMETHOD(EndGetEvent)
(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent) override {
return E_NOTIMPL;
}
STDMETHOD(QueueEvent)
(MediaEventType met,
REFGUID guidExtendedType,
HRESULT hrStatus,
const PROPVARIANT* pvValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetCharacteristics)(DWORD* pdwCharacteristics) override {
return E_NOTIMPL;
}
STDMETHOD(CreatePresentationDescriptor)
(IMFPresentationDescriptor** ppPresentationDescriptor) override {
return E_NOTIMPL;
}
STDMETHOD(Start)
(IMFPresentationDescriptor* pPresentationDescriptor,
const GUID* pguidTimeFormat,
const PROPVARIANT* pvarStartPosition) override {
return E_NOTIMPL;
}
STDMETHOD(Stop)(void) override { return E_NOTIMPL; }
STDMETHOD(Pause)(void) override { return E_NOTIMPL; }
STDMETHOD(Shutdown)(void) override { return E_NOTIMPL; }
private:
friend class base::RefCountedThreadSafe<MockMFMediaSource>;
virtual ~MockMFMediaSource() = default;
};
class MockMFCaptureSource
: public base::RefCountedThreadSafe<MockMFCaptureSource>,
public IMFCaptureSource {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockMFCaptureSource>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockMFCaptureSource>::Release();
return 1U;
}
STDMETHOD(GetCaptureDeviceSource)
(MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType,
IMFMediaSource** ppMediaSource) override {
return E_NOTIMPL;
}
STDMETHOD(GetCaptureDeviceActivate)
(MF_CAPTURE_ENGINE_DEVICE_TYPE mfCaptureEngineDeviceType,
IMFActivate** ppActivate) override {
return E_NOTIMPL;
}
STDMETHOD(GetService)
(REFIID rguidService, REFIID riid, IUnknown** ppUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(AddEffect)(DWORD dwSourceStreamIndex, IUnknown* pUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(RemoveEffect)
(DWORD dwSourceStreamIndex, IUnknown* pUnknown) override { return E_NOTIMPL; }
STDMETHOD(RemoveAllEffects)(DWORD dwSourceStreamIndex) override {
return E_NOTIMPL;
}
STDMETHOD(GetAvailableDeviceMediaType)
(DWORD stream_index,
DWORD media_type_index,
IMFMediaType** media_type) override {
return DoGetAvailableDeviceMediaType(stream_index, media_type_index,
media_type);
}
MOCK_METHOD3(DoGetAvailableDeviceMediaType,
HRESULT(DWORD, DWORD, IMFMediaType**));
STDMETHOD(SetCurrentDeviceMediaType)
(DWORD dwSourceStreamIndex, IMFMediaType* pMediaType) override {
return S_OK;
}
STDMETHOD(GetCurrentDeviceMediaType)
(DWORD stream_index, IMFMediaType** media_type) {
return DoGetCurrentDeviceMediaType(stream_index, media_type);
}
MOCK_METHOD2(DoGetCurrentDeviceMediaType, HRESULT(DWORD, IMFMediaType**));
STDMETHOD(GetDeviceStreamCount)(DWORD* count) {
return DoGetDeviceStreamCount(count);
}
MOCK_METHOD1(DoGetDeviceStreamCount, HRESULT(DWORD*));
STDMETHOD(GetDeviceStreamCategory)
(DWORD stream_index, MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
return DoGetDeviceStreamCategory(stream_index, category);
}
MOCK_METHOD2(DoGetDeviceStreamCategory,
HRESULT(DWORD, MF_CAPTURE_ENGINE_STREAM_CATEGORY*));
STDMETHOD(GetMirrorState)(DWORD dwStreamIndex, BOOL* pfMirrorState) override {
return E_NOTIMPL;
}
STDMETHOD(SetMirrorState)(DWORD dwStreamIndex, BOOL fMirrorState) override {
return E_NOTIMPL;
}
STDMETHOD(GetStreamIndexFromFriendlyName)
(UINT32 uifriendlyName, DWORD* pdwActualStreamIndex) override {
return E_NOTIMPL;
}
private:
friend class base::RefCountedThreadSafe<MockMFCaptureSource>;
virtual ~MockMFCaptureSource() = default;
};
class MockCapturePreviewSink
: public base::RefCountedThreadSafe<MockCapturePreviewSink>,
public IMFCapturePreviewSink {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
if (riid == IID_IUnknown || riid == IID_IMFCapturePreviewSink) {
AddRef();
*object = this;
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockCapturePreviewSink>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockCapturePreviewSink>::Release();
return 1U;
}
STDMETHOD(GetOutputMediaType)
(DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType) override {
return E_NOTIMPL;
}
STDMETHOD(GetService)
(DWORD dwSinkStreamIndex,
REFGUID rguidService,
REFIID riid,
IUnknown** ppUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(AddStream)
(DWORD dwSourceStreamIndex,
IMFMediaType* pMediaType,
IMFAttributes* pAttributes,
DWORD* pdwSinkStreamIndex) override {
return S_OK;
}
STDMETHOD(Prepare)(void) override { return E_NOTIMPL; }
STDMETHOD(RemoveAllStreams)(void) override { return S_OK; }
STDMETHOD(SetRenderHandle)(HANDLE handle) override { return E_NOTIMPL; }
STDMETHOD(SetRenderSurface)(IUnknown* pSurface) override { return E_NOTIMPL; }
STDMETHOD(UpdateVideo)
(const MFVideoNormalizedRect* pSrc,
const RECT* pDst,
const COLORREF* pBorderClr) override {
return E_NOTIMPL;
}
STDMETHOD(SetSampleCallback)
(DWORD dwStreamSinkIndex,
IMFCaptureEngineOnSampleCallback* pCallback) override {
sample_callback = pCallback;
return S_OK;
}
STDMETHOD(GetMirrorState)(BOOL* pfMirrorState) override { return E_NOTIMPL; }
STDMETHOD(SetMirrorState)(BOOL fMirrorState) override { return E_NOTIMPL; }
STDMETHOD(GetRotation)
(DWORD dwStreamIndex, DWORD* pdwRotationValue) override { return E_NOTIMPL; }
STDMETHOD(SetRotation)(DWORD dwStreamIndex, DWORD dwRotationValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetCustomSink)(IMFMediaSink* pMediaSink) override {
return E_NOTIMPL;
}
scoped_refptr<IMFCaptureEngineOnSampleCallback> sample_callback;
private:
friend class base::RefCountedThreadSafe<MockCapturePreviewSink>;
virtual ~MockCapturePreviewSink() = default;
};
class MockCapturePhotoSink
: public base::RefCountedThreadSafe<MockCapturePhotoSink>,
public IMFCapturePhotoSink {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
if (riid == IID_IUnknown || riid == IID_IMFCapturePhotoSink) {
AddRef();
*object = this;
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockCapturePhotoSink>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockCapturePhotoSink>::Release();
return 1U;
}
STDMETHOD(GetOutputMediaType)
(DWORD dwSinkStreamIndex, IMFMediaType** ppMediaType) override {
return E_NOTIMPL;
}
STDMETHOD(GetService)
(DWORD dwSinkStreamIndex,
REFGUID rguidService,
REFIID riid,
IUnknown** ppUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(AddStream)
(DWORD dwSourceStreamIndex,
IMFMediaType* pMediaType,
IMFAttributes* pAttributes,
DWORD* pdwSinkStreamIndex) override {
return S_OK;
}
STDMETHOD(Prepare)(void) override { return E_NOTIMPL; }
STDMETHOD(RemoveAllStreams)(void) override { return S_OK; }
STDMETHOD(SetOutputFileName)(LPCWSTR fileName) override { return E_NOTIMPL; }
STDMETHOD(SetSampleCallback)
(IMFCaptureEngineOnSampleCallback* pCallback) override {
sample_callback = pCallback;
return S_OK;
}
STDMETHOD(SetOutputByteStream)(IMFByteStream* pByteStream) override {
return E_NOTIMPL;
}
scoped_refptr<IMFCaptureEngineOnSampleCallback> sample_callback;
private:
friend class base::RefCountedThreadSafe<MockCapturePhotoSink>;
virtual ~MockCapturePhotoSink() = default;
};
class MockMFCaptureEngine
: public base::RefCountedThreadSafe<MockMFCaptureEngine>,
public IMFCaptureEngine {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) { return S_OK; }
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockMFCaptureEngine>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockMFCaptureEngine>::Release();
return 1U;
}
STDMETHOD(Initialize)
(IMFCaptureEngineOnEventCallback* pEventCallback,
IMFAttributes* pAttributes,
IUnknown* pAudioSource,
IUnknown* pVideoSource) override {
EXPECT_TRUE(pEventCallback);
EXPECT_TRUE(pAttributes);
EXPECT_TRUE(pVideoSource);
event_callback = pEventCallback;
OnCorrectInitialize();
return S_OK;
}
MOCK_METHOD0(OnCorrectInitialize, void(void));
STDMETHOD(StartPreview)(void) override {
OnStartPreview();
return S_OK;
}
MOCK_METHOD0(OnStartPreview, void(void));
STDMETHOD(StopPreview)(void) override {
OnStopPreview();
return S_OK;
}
MOCK_METHOD0(OnStopPreview, void(void));
STDMETHOD(StartRecord)(void) override { return E_NOTIMPL; }
STDMETHOD(StopRecord)(BOOL bFinalize, BOOL bFlushUnprocessedSamples) {
return E_NOTIMPL;
}
STDMETHOD(TakePhoto)(void) override {
OnTakePhoto();
return S_OK;
}
MOCK_METHOD0(OnTakePhoto, void(void));
STDMETHOD(GetSink)(MF_CAPTURE_ENGINE_SINK_TYPE type, IMFCaptureSink** sink) {
return DoGetSink(type, sink);
}
MOCK_METHOD2(DoGetSink,
HRESULT(MF_CAPTURE_ENGINE_SINK_TYPE, IMFCaptureSink**));
STDMETHOD(GetSource)(IMFCaptureSource** source) {
*source = DoGetSource();
return source ? S_OK : E_FAIL;
}
MOCK_METHOD0(DoGetSource, IMFCaptureSource*());
scoped_refptr<IMFCaptureEngineOnEventCallback> event_callback;
private:
friend class base::RefCountedThreadSafe<MockMFCaptureEngine>;
virtual ~MockMFCaptureEngine() = default;
};
class StubMFMediaType : public base::RefCountedThreadSafe<StubMFMediaType>,
public IMFMediaType {
public:
StubMFMediaType(GUID major_type,
GUID sub_type,
int frame_width,
int frame_height,
int frame_rate)
: major_type_(major_type),
sub_type_(sub_type),
frame_width_(frame_width),
frame_height_(frame_height),
frame_rate_(frame_rate) {}
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<StubMFMediaType>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<StubMFMediaType>::Release();
return 1U;
}
STDMETHOD(GetItem)(REFGUID key, PROPVARIANT* value) override {
if (key == MF_MT_FRAME_SIZE) {
value->vt = VT_UI8;
value->uhVal.QuadPart = Pack2UINT32AsUINT64(frame_width_, frame_height_);
return S_OK;
}
if (key == MF_MT_FRAME_RATE) {
value->vt = VT_UI8;
value->uhVal.QuadPart = Pack2UINT32AsUINT64(frame_rate_, 1);
return S_OK;
}
if (key == MF_MT_PIXEL_ASPECT_RATIO) {
value->vt = VT_UI8;
value->uhVal.QuadPart = Pack2UINT32AsUINT64(1, 1);
return S_OK;
}
if (key == MF_MT_INTERLACE_MODE) {
value->vt = VT_UI4;
value->uintVal = MFVideoInterlace_Progressive;
return S_OK;
}
return E_FAIL;
}
STDMETHOD(GetItemType)(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override {
return E_NOTIMPL;
}
STDMETHOD(CompareItem)
(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) override {
return E_NOTIMPL;
}
STDMETHOD(Compare)
(IMFAttributes* pTheirs,
MF_ATTRIBUTES_MATCH_TYPE MatchType,
BOOL* pbResult) override {
return E_NOTIMPL;
}
STDMETHOD(GetUINT32)(REFGUID key, UINT32* value) override {
if (key == MF_MT_INTERLACE_MODE) {
*value = MFVideoInterlace_Progressive;
return S_OK;
}
return E_NOTIMPL;
}
STDMETHOD(GetUINT64)(REFGUID key, UINT64* value) override {
if (key == MF_MT_FRAME_SIZE) {
*value = (long long)frame_width_ << 32 | frame_height_;
return S_OK;
}
if (key == MF_MT_FRAME_RATE) {
*value = (long long)frame_rate_ << 32 | 1;
return S_OK;
}
if (key == MF_MT_PIXEL_ASPECT_RATIO) {
*value = (long long)1 << 32 | 1;
return S_OK;
}
return E_FAIL;
}
STDMETHOD(GetDouble)(REFGUID guidKey, double* pfValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetGUID)(REFGUID key, GUID* value) override {
if (key == MF_MT_MAJOR_TYPE) {
*value = major_type_;
return S_OK;
}
if (key == MF_MT_SUBTYPE) {
*value = sub_type_;
return S_OK;
}
return E_FAIL;
}
STDMETHOD(GetStringLength)(REFGUID guidKey, UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetString)
(REFGUID guidKey,
LPWSTR pwszValue,
UINT32 cchBufSize,
UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetAllocatedString)
(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetBlobSize)(REFGUID guidKey, UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetBlob)
(REFGUID guidKey,
UINT8* pBuf,
UINT32 cbBufSize,
UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetAllocatedBlob)
(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetUnknown)(REFGUID guidKey, REFIID riid, LPVOID* ppv) override {
return E_NOTIMPL;
}
STDMETHOD(SetItem)(REFGUID guidKey, REFPROPVARIANT Value) override {
return E_NOTIMPL;
}
STDMETHOD(DeleteItem)(REFGUID guidKey) override { return E_NOTIMPL; }
STDMETHOD(DeleteAllItems)(void) override { return E_NOTIMPL; }
STDMETHOD(SetUINT32)(REFGUID guidKey, UINT32 unValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetUINT64)(REFGUID guidKey, UINT64 unValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetDouble)(REFGUID guidKey, double fValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetGUID)(REFGUID guidKey, REFGUID guidValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetString)(REFGUID guidKey, LPCWSTR wszValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetBlob)
(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) override {
return E_NOTIMPL;
}
STDMETHOD(SetUnknown)(REFGUID guidKey, IUnknown* pUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(LockStore)(void) override { return E_NOTIMPL; }
STDMETHOD(UnlockStore)(void) override { return E_NOTIMPL; }
STDMETHOD(GetCount)(UINT32* pcItems) override { return E_NOTIMPL; }
STDMETHOD(GetItemByIndex)
(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) override {
return E_NOTIMPL;
}
STDMETHOD(CopyAllItems)(IMFAttributes* pDest) override { return E_NOTIMPL; }
STDMETHOD(GetMajorType)(GUID* pguidMajorType) override { return E_NOTIMPL; }
STDMETHOD(IsCompressedFormat)(BOOL* pfCompressed) override {
return E_NOTIMPL;
}
STDMETHOD(IsEqual)(IMFMediaType* pIMediaType, DWORD* pdwFlags) override {
return E_NOTIMPL;
}
STDMETHOD(GetRepresentation)
(GUID guidRepresentation, LPVOID* ppvRepresentation) override {
return E_NOTIMPL;
}
STDMETHOD(FreeRepresentation)
(GUID guidRepresentation, LPVOID pvRepresentation) override {
return E_NOTIMPL;
}
private:
friend class base::RefCountedThreadSafe<StubMFMediaType>;
virtual ~StubMFMediaType() = default;
const GUID major_type_;
const GUID sub_type_;
const int frame_width_;
const int frame_height_;
const int frame_rate_;
};
class MockMFMediaEvent : public base::RefCountedThreadSafe<MockMFMediaEvent>,
public IMFMediaEvent {
public:
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) override {
return E_NOTIMPL;
}
STDMETHOD_(ULONG, AddRef)() override {
base::RefCountedThreadSafe<MockMFMediaEvent>::AddRef();
return 1U;
}
STDMETHOD_(ULONG, Release)() override {
base::RefCountedThreadSafe<MockMFMediaEvent>::Release();
return 1U;
}
STDMETHOD(GetItem)(REFGUID guidKey, PROPVARIANT* pValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetItemType)(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) override {
return E_NOTIMPL;
}
STDMETHOD(CompareItem)
(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) override {
return E_NOTIMPL;
}
STDMETHOD(Compare)
(IMFAttributes* pTheirs,
MF_ATTRIBUTES_MATCH_TYPE MatchType,
BOOL* pbResult) override {
return E_NOTIMPL;
}
STDMETHOD(GetUINT32)(REFGUID guidKey, UINT32* punValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetUINT64)(REFGUID guidKey, UINT64* punValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetDouble)(REFGUID guidKey, double* pfValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetGUID)(REFGUID guidKey, GUID* pguidValue) override {
return E_NOTIMPL;
}
STDMETHOD(GetStringLength)(REFGUID guidKey, UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetString)
(REFGUID guidKey,
LPWSTR pwszValue,
UINT32 cchBufSize,
UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetAllocatedString)
(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) override {
return E_NOTIMPL;
}
STDMETHOD(GetBlobSize)(REFGUID guidKey, UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetBlob)
(REFGUID guidKey,
UINT8* pBuf,
UINT32 cbBufSize,
UINT32* pcbBlobSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetAllocatedBlob)
(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) override {
return E_NOTIMPL;
}
STDMETHOD(GetUnknown)(REFGUID guidKey, REFIID riid, LPVOID* ppv) override {
return E_NOTIMPL;
}
STDMETHOD(SetItem)(REFGUID guidKey, REFPROPVARIANT Value) override {
return E_NOTIMPL;
}
STDMETHOD(DeleteItem)(REFGUID guidKey) override { return E_NOTIMPL; }
STDMETHOD(DeleteAllItems)(void) override { return E_NOTIMPL; }
STDMETHOD(SetUINT32)(REFGUID guidKey, UINT32 unValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetUINT64)(REFGUID guidKey, UINT64 unValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetDouble)(REFGUID guidKey, double fValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetGUID)(REFGUID guidKey, REFGUID guidValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetString)(REFGUID guidKey, LPCWSTR wszValue) override {
return E_NOTIMPL;
}
STDMETHOD(SetBlob)
(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) override {
return E_NOTIMPL;
}
STDMETHOD(SetUnknown)(REFGUID guidKey, IUnknown* pUnknown) override {
return E_NOTIMPL;
}
STDMETHOD(LockStore)(void) override { return E_NOTIMPL; }
STDMETHOD(UnlockStore)(void) override { return E_NOTIMPL; }
STDMETHOD(GetCount)(UINT32* pcItems) override { return E_NOTIMPL; }
STDMETHOD(GetItemByIndex)
(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) override {
return E_NOTIMPL;
}
STDMETHOD(CopyAllItems)(IMFAttributes* pDest) override { return E_NOTIMPL; }
STDMETHOD(GetType)(MediaEventType* pmet) override { return E_NOTIMPL; }
STDMETHOD(GetExtendedType)(GUID* pguidExtendedType) override {
return E_NOTIMPL;
}
STDMETHOD(GetStatus)(HRESULT* status) override {
*status = DoGetStatus();
return S_OK;
}
MOCK_METHOD0(DoGetStatus, HRESULT());
STDMETHOD(GetValue)(PROPVARIANT* pvValue) override { return E_NOTIMPL; }
private:
friend class base::RefCountedThreadSafe<MockMFMediaEvent>;
virtual ~MockMFMediaEvent() = default;
};
} // namespace
const int kArbitraryValidVideoWidth = 1920;
const int kArbitraryValidVideoHeight = 1080;
const int kArbitraryValidPhotoWidth = 3264;
const int kArbitraryValidPhotoHeight = 2448;
class VideoCaptureDeviceMFWinTest : public ::testing::Test {
protected:
VideoCaptureDeviceMFWinTest()
: media_source_(new MockMFMediaSource()),
engine_(new MockMFCaptureEngine()),
client_(new MockClient()),
image_capture_client_(new MockImageCaptureClient()),
device_(new VideoCaptureDeviceMFWin(media_source_, engine_)),
capture_source_(new MockMFCaptureSource()),
media_foundation_supported_(
VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()) {}
void SetUp() override {
if (!media_foundation_supported_)
return;
device_->set_max_retry_count_for_testing(3);
device_->set_retry_delay_in_ms_for_testing(1);
EXPECT_CALL(*(engine_.Get()), OnCorrectInitialize());
EXPECT_TRUE(device_->Init());
EXPECT_CALL(*(engine_.Get()), DoGetSource())
.WillRepeatedly(Invoke([this]() {
this->capture_source_->AddRef();
return this->capture_source_.get();
}));
}
bool ShouldSkipTest() {
if (media_foundation_supported_)
return false;
DVLOG(1) << "Media foundation is not supported by the current platform. "
"Skipping test.";
return true;
}
void PrepareMFDeviceWithOneVideoStream() {
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
.WillRepeatedly(Invoke([](DWORD* stream_count) {
*stream_count = 1;
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(0, _))
.WillRepeatedly(Invoke([](DWORD stream_index,
MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
*category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(0, _, _))
.WillRepeatedly(Invoke([](DWORD stream_index, DWORD media_type_index,
IMFMediaType** media_type) {
if (media_type_index != 0)
return MF_E_NO_MORE_TYPES;
*media_type = new StubMFMediaType(
MFMediaType_Video, MFVideoFormat_MJPG, kArbitraryValidVideoWidth,
kArbitraryValidVideoHeight, 30);
(*media_type)->AddRef();
return S_OK;
}));
EXPECT_CALL(*(engine_.Get()),
DoGetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _))
.WillRepeatedly(Invoke(
[](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
*sink = new MockCapturePreviewSink();
(*sink)->AddRef();
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetCurrentDeviceMediaType(_, _))
.WillRepeatedly(
Invoke([](DWORD stream_index, IMFMediaType** media_type) {
*media_type = new StubMFMediaType(
MFMediaType_Video, MFVideoFormat_MJPG,
kArbitraryValidVideoWidth, kArbitraryValidVideoHeight, 30);
(*media_type)->AddRef();
return S_OK;
}));
}
void PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream() {
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
.WillRepeatedly(Invoke([](DWORD* stream_count) {
*stream_count = 2;
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(_, _))
.WillRepeatedly(Invoke([](DWORD stream_index,
MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
if (stream_index == 0) {
*category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
return S_OK;
} else if (stream_index == 1) {
*category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_INDEPENDENT;
return S_OK;
}
return E_FAIL;
}));
auto get_device_media_type = [](DWORD stream_index,
IMFMediaType** media_type) {
if (stream_index == 0) {
*media_type = new StubMFMediaType(MFMediaType_Video, MFVideoFormat_MJPG,
kArbitraryValidVideoWidth,
kArbitraryValidVideoHeight, 30);
(*media_type)->AddRef();
return S_OK;
} else if (stream_index == 1) {
*media_type = new StubMFMediaType(
MFMediaType_Image, GUID_ContainerFormatJpeg,
kArbitraryValidPhotoWidth, kArbitraryValidPhotoHeight, 0);
(*media_type)->AddRef();
return S_OK;
}
return E_FAIL;
};
EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(_, _, _))
.WillRepeatedly(Invoke(
[get_device_media_type](DWORD stream_index, DWORD media_type_index,
IMFMediaType** media_type) {
if (media_type_index != 0)
return MF_E_NO_MORE_TYPES;
return get_device_media_type(stream_index, media_type);
}));
EXPECT_CALL(*(engine_.Get()), DoGetSink(_, _))
.WillRepeatedly(Invoke(
[](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
if (sink_type == MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW) {
*sink = new MockCapturePreviewSink();
(*sink)->AddRef();
return S_OK;
} else if (sink_type == MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO) {
*sink = new MockCapturePhotoSink();
(*sink)->AddRef();
return S_OK;
}
return E_FAIL;
}));
EXPECT_CALL(*capture_source_, DoGetCurrentDeviceMediaType(_, _))
.WillRepeatedly(Invoke(get_device_media_type));
}
Microsoft::WRL::ComPtr<MockMFMediaSource> media_source_;
Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine_;
std::unique_ptr<MockClient> client_;
scoped_refptr<MockImageCaptureClient> image_capture_client_;
std::unique_ptr<VideoCaptureDeviceMFWin> device_;
VideoCaptureFormat last_format_;
scoped_refptr<MockMFCaptureSource> capture_source_;
private:
const bool media_foundation_supported_;
};
// Expects StartPreview() to be called on AllocateAndStart()
TEST_F(VideoCaptureDeviceMFWinTest, StartPreviewOnAllocateAndStart) {
if (ShouldSkipTest())
return;
PrepareMFDeviceWithOneVideoStream();
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
EXPECT_CALL(*(engine_.Get()), OnStopPreview());
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
device_->StopAndDeAllocate();
}
// Expects OnError() to be called on an errored IMFMediaEvent
TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnErrorMediaEvent) {
if (ShouldSkipTest())
return;
PrepareMFDeviceWithOneVideoStream();
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
EXPECT_CALL(*client_, OnError(_, _));
scoped_refptr<MockMFMediaEvent> media_event_error = new MockMFMediaEvent();
EXPECT_CALL(*media_event_error, DoGetStatus()).WillRepeatedly(Return(E_FAIL));
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
engine_->event_callback->OnEvent(media_event_error.get());
}
// Allocates device with flaky methods failing with MF_E_INVALIDREQUEST and
// expects the device to retry and start correctly
TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFlakyInvalidRequest) {
if (ShouldSkipTest())
return;
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
.Times(AtLeast(2))
.WillOnce(Return(MF_E_INVALIDREQUEST))
.WillRepeatedly(Invoke([](DWORD* stream_count) {
*stream_count = 1;
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCategory(0, _))
.Times(AtLeast(2))
.WillOnce(Return(MF_E_INVALIDREQUEST))
.WillRepeatedly(Invoke(
[](DWORD stream_index, MF_CAPTURE_ENGINE_STREAM_CATEGORY* category) {
*category = MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW;
return S_OK;
}));
EXPECT_CALL(*capture_source_, DoGetAvailableDeviceMediaType(0, _, _))
.Times(AtLeast(2))
.WillOnce(Return(MF_E_INVALIDREQUEST))
.WillRepeatedly(Invoke([](DWORD stream_index, DWORD media_type_index,
IMFMediaType** media_type) {
if (media_type_index != 0)
return MF_E_NO_MORE_TYPES;
*media_type = new StubMFMediaType(MFMediaType_Video, MFVideoFormat_MJPG,
kArbitraryValidVideoWidth,
kArbitraryValidVideoHeight, 30);
(*media_type)->AddRef();
return S_OK;
}));
EXPECT_CALL(*(engine_.Get()),
DoGetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _))
.WillRepeatedly(Invoke(
[](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, IMFCaptureSink** sink) {
*sink = new MockCapturePreviewSink();
(*sink)->AddRef();
return S_OK;
}));
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
}
// Allocates device with methods always failing with MF_E_INVALIDREQUEST and
// expects the device to give up and call OnError()
TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFailingInvalidRequest) {
if (ShouldSkipTest())
return;
EXPECT_CALL(*capture_source_, DoGetDeviceStreamCount(_))
.WillRepeatedly(Return(MF_E_INVALIDREQUEST));
EXPECT_CALL(*client_, OnError(_, _));
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
}
// Given an |IMFCaptureSource| offering a video stream without photo stream to
// |VideoCaptureDevice|, when asking the photo state from |VideoCaptureDevice|
// then expect the returned state to match the video resolution
TEST_F(VideoCaptureDeviceMFWinTest, GetPhotoStateViaVideoStream) {
if (ShouldSkipTest())
return;
PrepareMFDeviceWithOneVideoStream();
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
VideoCaptureDevice::GetPhotoStateCallback get_photo_state_callback =
base::BindOnce(&MockImageCaptureClient::DoOnGetPhotoState,
image_capture_client_);
device_->GetPhotoState(std::move(get_photo_state_callback));
mojom::PhotoState* state = image_capture_client_->state.get();
ASSERT_EQ(state->width->min, kArbitraryValidVideoWidth);
ASSERT_EQ(state->width->current, kArbitraryValidVideoWidth);
ASSERT_EQ(state->width->max, kArbitraryValidVideoWidth);
ASSERT_EQ(state->height->min, kArbitraryValidVideoHeight);
ASSERT_EQ(state->height->current, kArbitraryValidVideoHeight);
ASSERT_EQ(state->height->max, kArbitraryValidVideoHeight);
}
// Given an |IMFCaptureSource| offering a video stream and a photo stream to
// |VideoCaptureDevice|, when asking the photo state from |VideoCaptureDevice|
// then expect the returned state to match the available photo resolution
TEST_F(VideoCaptureDeviceMFWinTest, GetPhotoStateViaPhotoStream) {
if (ShouldSkipTest())
return;
PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream();
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
VideoCaptureDevice::GetPhotoStateCallback get_photo_state_callback =
base::BindOnce(&MockImageCaptureClient::DoOnGetPhotoState,
image_capture_client_);
device_->GetPhotoState(std::move(get_photo_state_callback));
mojom::PhotoState* state = image_capture_client_->state.get();
ASSERT_EQ(state->width->min, kArbitraryValidPhotoWidth);
ASSERT_EQ(state->width->current, kArbitraryValidPhotoWidth);
ASSERT_EQ(state->width->max, kArbitraryValidPhotoWidth);
ASSERT_EQ(state->height->min, kArbitraryValidPhotoHeight);
ASSERT_EQ(state->height->current, kArbitraryValidPhotoHeight);
ASSERT_EQ(state->height->max, kArbitraryValidPhotoHeight);
}
// Given an |IMFCaptureSource| offering a video stream and a photo stream to
// |VideoCaptureDevice|, when taking photo from |VideoCaptureDevice| then
// expect IMFCaptureEngine::TakePhoto() to be called
TEST_F(VideoCaptureDeviceMFWinTest, TakePhotoViaPhotoStream) {
if (ShouldSkipTest())
return;
PrepareMFDeviceWithOneVideoStreamAndOnePhotoStream();
EXPECT_CALL(*(engine_.Get()), OnStartPreview());
EXPECT_CALL(*client_, OnStarted());
EXPECT_CALL(*(engine_.Get()), OnTakePhoto());
device_->AllocateAndStart(VideoCaptureParams(), std::move(client_));
VideoCaptureDevice::TakePhotoCallback take_photo_callback = base::BindOnce(
&MockImageCaptureClient::DoOnPhotoTaken, image_capture_client_);
device_->TakePhoto(std::move(take_photo_callback));
}
} // namespace media