Commit cf6c86f8 authored by mcasas@chromium.org's avatar mcasas@chromium.org

VideoCaptureDeviceWin: Extract class-static method into a Factory (both MF/DS)

This CL extends the separation Factory/device
initiated in the other CLs part of the bug. Aside
from code cleanup, it allows for better testing
(via dependency injection) of the factory parts
(this is specially important for blacklisted devices,
of which there are none in Win ATM). It can also
be used to modify the GetDeviceNames() method to
an asynchronous API.

Both VideoCaptureDeviceWin and VideoCaptureDeviceMFwin 
(using DirectShow and MediaFoundation APIs resp.),
have a number of static methods Create(),
GetDeviceNames() and GetDeviceSupportedFormats().
Plus, there are overarching versions of those
at the VideoCaptureDevice level, that do library
initialisation and appropriate forwarding to one
or the other implementation. This CL extracts all
of those class-static methods and moves them to
a single VideoCaptureDeviceFactoryWin.

ScopedMediaType, previously file-level data type
moves to the public def of VideoCaptureDeviceWin
since is used in the Factory.

Every file-static function and variable that could,
has been moved to the Factory file. Previous 
file-level static functions that are used in the
Factory and the VCD have been changed to VCD
static methods and used in both ends.

TBR= dalecurtis@chromium.org (media/media.gyp)

BUG=323913

Review URL: https://codereview.chromium.org/276383002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271800 0039d316-1c4b-4281-b951-d872f2087c98
parent 698d0288
......@@ -505,6 +505,8 @@
'video/capture/win/sink_filter_win.h',
'video/capture/win/sink_input_pin_win.cc',
'video/capture/win/sink_input_pin_win.h',
'video/capture/win/video_capture_device_factory_win.cc',
'video/capture/win/video_capture_device_factory_win.h',
'video/capture/win/video_capture_device_mf_win.cc',
'video/capture/win/video_capture_device_mf_win.h',
'video/capture/win/video_capture_device_win.cc',
......
......@@ -15,6 +15,8 @@
#include "media/video/capture/linux/video_capture_device_factory_linux.h"
#elif defined(OS_ANDROID)
#include "media/video/capture/android/video_capture_device_factory_android.h"
#elif defined(OS_WIN)
#include "media/video/capture/win/video_capture_device_factory_win.h"
#endif
namespace media {
......@@ -46,6 +48,9 @@ scoped_ptr<VideoCaptureDeviceFactory>
#elif defined(OS_ANDROID)
return scoped_ptr<VideoCaptureDeviceFactory>(new
VideoCaptureDeviceFactoryAndroid());
#elif defined(OS_WIN)
return scoped_ptr<VideoCaptureDeviceFactory>(new
VideoCaptureDeviceFactoryWin());
#else
return scoped_ptr<VideoCaptureDeviceFactory>(new
VideoCaptureDeviceFactory());
......
......@@ -16,7 +16,7 @@
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "media/video/capture/win/video_capture_device_mf_win.h"
#include "media/video/capture/win/video_capture_device_factory_win.h"
#endif
#if defined(OS_ANDROID)
......@@ -165,7 +165,7 @@ class VideoCaptureDeviceTest : public testing::Test {
TEST_F(VideoCaptureDeviceTest, OpenInvalidDevice) {
#if defined(OS_WIN)
VideoCaptureDevice::Name::CaptureApiType api_type =
VideoCaptureDeviceMFWin::PlatformSupported()
VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()
? VideoCaptureDevice::Name::MEDIA_FOUNDATION
: VideoCaptureDevice::Name::DIRECT_SHOW;
VideoCaptureDevice::Name device_name("jibberish", "jibberish", api_type);
......
This diff is collapsed.
// Copyright 2014 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.
// Implementation of a VideoCaptureDeviceFactory class for Windows platforms.
#ifndef MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_FACTORY_WIN_H_
#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_FACTORY_WIN_H_
#include "media/video/capture/video_capture_device_factory.h"
namespace media {
// Extension of VideoCaptureDeviceFactory to create and manipulate Windows
// devices, via either DirectShow or MediaFoundation APIs.
class MEDIA_EXPORT VideoCaptureDeviceFactoryWin :
public VideoCaptureDeviceFactory {
public:
static bool PlatformSupportsMediaFoundation();
VideoCaptureDeviceFactoryWin();
virtual ~VideoCaptureDeviceFactoryWin() {}
virtual scoped_ptr<VideoCaptureDevice> Create(
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
const VideoCaptureDevice::Name& device_name) OVERRIDE;
virtual void GetDeviceNames(VideoCaptureDevice::Names* device_names) OVERRIDE;
virtual void GetDeviceSupportedFormats(
const VideoCaptureDevice::Name& device,
VideoCaptureFormats* supported_formats) OVERRIDE;
private:
bool use_media_foundation_;
DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactoryWin);
};
} // namespace media
#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_FACTORY_WIN_H_
......@@ -7,7 +7,6 @@
#include <mfapi.h>
#include <mferror.h>
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
......@@ -20,7 +19,6 @@ using base::win::ScopedCoMem;
using base::win::ScopedComPtr;
namespace media {
namespace {
// In Windows device identifiers, the USB VID and PID are preceded by the string
// "vid_" or "pid_". The identifiers are each 4 bytes long.
......@@ -28,74 +26,7 @@ const char kVidPrefix[] = "vid_"; // Also contains '\0'.
const char kPidPrefix[] = "pid_"; // Also contains '\0'.
const size_t kVidPidSize = 4;
class MFInitializerSingleton {
public:
MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); }
~MFInitializerSingleton() { MFShutdown(); }
};
static base::LazyInstance<MFInitializerSingleton> g_mf_initialize =
LAZY_INSTANCE_INITIALIZER;
void EnsureMFInit() {
g_mf_initialize.Get();
}
bool PrepareVideoCaptureAttributes(IMFAttributes** attributes, int count) {
EnsureMFInit();
if (FAILED(MFCreateAttributes(attributes, count)))
return false;
return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
}
bool EnumerateVideoDevices(IMFActivate*** devices,
UINT32* count) {
ScopedComPtr<IMFAttributes> attributes;
if (!PrepareVideoCaptureAttributes(attributes.Receive(), 1))
return false;
return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
}
bool CreateVideoCaptureDevice(const char* sym_link, IMFMediaSource** source) {
ScopedComPtr<IMFAttributes> attributes;
if (!PrepareVideoCaptureAttributes(attributes.Receive(), 2))
return false;
attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
base::SysUTF8ToWide(sym_link).c_str());
return SUCCEEDED(MFCreateDeviceSource(attributes, source));
}
bool FormatFromGuid(const GUID& guid, VideoPixelFormat* format) {
struct {
const GUID& guid;
const VideoPixelFormat format;
} static const kFormatMap[] = {
{ MFVideoFormat_I420, PIXEL_FORMAT_I420 },
{ MFVideoFormat_YUY2, PIXEL_FORMAT_YUY2 },
{ MFVideoFormat_UYVY, PIXEL_FORMAT_UYVY },
{ MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24 },
{ MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB },
{ MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG },
{ MFVideoFormat_YV12, PIXEL_FORMAT_YV12 },
};
for (int i = 0; i < arraysize(kFormatMap); ++i) {
if (kFormatMap[i].guid == guid) {
*format = kFormatMap[i].format;
return true;
}
}
return false;
}
bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) {
static bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) {
UINT32 width32, height32;
if (FAILED(MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width32, &height32)))
return false;
......@@ -103,9 +34,9 @@ bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) {
return true;
}
bool GetFrameRate(IMFMediaType* type,
int* frame_rate_numerator,
int* frame_rate_denominator) {
static bool GetFrameRate(IMFMediaType* type,
int* frame_rate_numerator,
int* frame_rate_denominator) {
UINT32 numerator, denominator;
if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator,
&denominator))||
......@@ -117,15 +48,16 @@ bool GetFrameRate(IMFMediaType* type,
return true;
}
bool FillCapabilitiesFromType(IMFMediaType* type,
VideoCaptureCapabilityWin* capability) {
static bool FillCapabilitiesFromType(IMFMediaType* type,
VideoCaptureCapabilityWin* capability) {
GUID type_guid;
if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) ||
!GetFrameSize(type, &capability->supported_format.frame_size) ||
!GetFrameRate(type,
&capability->frame_rate_numerator,
&capability->frame_rate_denominator) ||
!FormatFromGuid(type_guid, &capability->supported_format.pixel_format)) {
!VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
&capability->supported_format.pixel_format)) {
return false;
}
// Keep the integer version of the frame_rate for (potential) returns.
......@@ -154,24 +86,6 @@ HRESULT FillCapabilities(IMFSourceReader* source,
return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr;
}
bool LoadMediaFoundationDlls() {
static const wchar_t* const kMfDLLs[] = {
L"%WINDIR%\\system32\\mf.dll",
L"%WINDIR%\\system32\\mfplat.dll",
L"%WINDIR%\\system32\\mfreadwrite.dll",
};
for (int i = 0; i < arraysize(kMfDLLs); ++i) {
wchar_t path[MAX_PATH] = {0};
ExpandEnvironmentStringsW(kMfDLLs[i], path, arraysize(path));
if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
return false;
}
return true;
}
} // namespace
class MFReaderCallback FINAL
: public base::RefCountedThreadSafe<MFReaderCallback>,
......@@ -250,95 +164,29 @@ class MFReaderCallback FINAL
};
// static
bool VideoCaptureDeviceMFWin::PlatformSupported() {
// Even though the DLLs might be available on Vista, we get crashes
// when running our tests on the build bots.
if (base::win::GetVersion() < base::win::VERSION_WIN7)
return false;
static bool g_dlls_available = LoadMediaFoundationDlls();
return g_dlls_available;
}
// static
void VideoCaptureDeviceMFWin::GetDeviceNames(Names* device_names) {
ScopedCoMem<IMFActivate*> devices;
UINT32 count;
if (!EnumerateVideoDevices(&devices, &count))
return;
bool VideoCaptureDeviceMFWin::FormatFromGuid(const GUID& guid,
VideoPixelFormat* format) {
struct {
const GUID& guid;
const VideoPixelFormat format;
} static const kFormatMap[] = {
{ MFVideoFormat_I420, PIXEL_FORMAT_I420 },
{ MFVideoFormat_YUY2, PIXEL_FORMAT_YUY2 },
{ MFVideoFormat_UYVY, PIXEL_FORMAT_UYVY },
{ MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24 },
{ MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB },
{ MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG },
{ MFVideoFormat_YV12, PIXEL_FORMAT_YV12 },
};
HRESULT hr;
for (UINT32 i = 0; i < count; ++i) {
UINT32 name_size, id_size;
ScopedCoMem<wchar_t> name, id;
if (SUCCEEDED(hr = devices[i]->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size)) &&
SUCCEEDED(hr = devices[i]->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
&id_size))) {
std::wstring name_w(name, name_size), id_w(id, id_size);
Name device(base::SysWideToUTF8(name_w), base::SysWideToUTF8(id_w),
Name::MEDIA_FOUNDATION);
device_names->push_back(device);
} else {
DLOG(WARNING) << "GetAllocatedString failed: " << std::hex << hr;
for (int i = 0; i < arraysize(kFormatMap); ++i) {
if (kFormatMap[i].guid == guid) {
*format = kFormatMap[i].format;
return true;
}
devices[i]->Release();
}
}
// static
void VideoCaptureDeviceMFWin::GetDeviceSupportedFormats(const Name& device,
VideoCaptureFormats* formats) {
ScopedComPtr<IMFMediaSource> source;
if (!CreateVideoCaptureDevice(device.id().c_str(), source.Receive()))
return;
HRESULT hr;
base::win::ScopedComPtr<IMFSourceReader> reader;
if (FAILED(hr = MFCreateSourceReaderFromMediaSource(source, NULL,
reader.Receive()))) {
DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource: " << std::hex << hr;
return;
}
DWORD stream_index = 0;
ScopedComPtr<IMFMediaType> type;
while (SUCCEEDED(hr = reader->GetNativeMediaType(
MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) {
UINT32 width, height;
hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr)) {
DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
return;
}
VideoCaptureFormat capture_format;
capture_format.frame_size.SetSize(width, height);
UINT32 numerator, denominator;
hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
if (FAILED(hr)) {
DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
return;
}
capture_format.frame_rate = denominator ? numerator / denominator : 0;
GUID type_guid;
hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
if (FAILED(hr)) {
DLOG(ERROR) << "GetGUID: " << std::hex << hr;
return;
}
FormatFromGuid(type_guid, &capture_format.pixel_format);
type.Release();
formats->push_back(capture_format);
++stream_index;
DVLOG(1) << device.name() << " resolution: "
<< capture_format.frame_size.ToString() << ", fps: "
<< capture_format.frame_rate << ", pixel format: "
<< capture_format.pixel_format;
}
return false;
}
const std::string VideoCaptureDevice::Name::GetModel() const {
......@@ -370,14 +218,11 @@ VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() {
DCHECK(CalledOnValidThread());
}
bool VideoCaptureDeviceMFWin::Init() {
bool VideoCaptureDeviceMFWin::Init(
const base::win::ScopedComPtr<IMFMediaSource>& source) {
DCHECK(CalledOnValidThread());
DCHECK(!reader_);
ScopedComPtr<IMFMediaSource> source;
if (!CreateVideoCaptureDevice(name_.id().c_str(), source.Receive()))
return false;
ScopedComPtr<IMFAttributes> attributes;
MFCreateAttributes(attributes.Receive(), 1);
DCHECK(attributes);
......
......@@ -30,12 +30,13 @@ class MEDIA_EXPORT VideoCaptureDeviceMFWin
: public base::NonThreadSafe,
public VideoCaptureDevice {
public:
static bool FormatFromGuid(const GUID& guid, VideoPixelFormat* format);
explicit VideoCaptureDeviceMFWin(const Name& device_name);
virtual ~VideoCaptureDeviceMFWin();
// Opens the device driver for this device.
// This function is used by the static VideoCaptureDevice::Create function.
bool Init();
bool Init(const base::win::ScopedComPtr<IMFMediaSource>& source);
// VideoCaptureDevice implementation.
virtual void AllocateAndStart(const VideoCaptureParams& params,
......@@ -43,18 +44,6 @@ class MEDIA_EXPORT VideoCaptureDeviceMFWin
OVERRIDE;
virtual void StopAndDeAllocate() OVERRIDE;
// Returns true iff the current platform supports the Media Foundation API
// and that the DLLs are available. On Vista this API is an optional download
// but the API is advertised as a part of Windows 7 and onwards. However,
// we've seen that the required DLLs are not available in some Win7
// distributions such as Windows 7 N and Windows 7 KN.
static bool PlatformSupported();
static void GetDeviceNames(Names* device_names);
static void GetDeviceSupportedFormats(const Name& device,
VideoCaptureFormats* formats);
// Captured new video data.
void OnIncomingCapturedData(const uint8* data,
int length,
......
......@@ -33,10 +33,38 @@ class VideoCaptureDeviceWin
public VideoCaptureDevice,
public SinkFilterObserver {
public:
// A utility class that wraps the AM_MEDIA_TYPE type and guarantees that
// we free the structure when exiting the scope. DCHECKing is also done to
// avoid memory leaks.
class ScopedMediaType {
public:
ScopedMediaType() : media_type_(NULL) {}
~ScopedMediaType() { Free(); }
AM_MEDIA_TYPE* operator->() { return media_type_; }
AM_MEDIA_TYPE* get() { return media_type_; }
void Free();
AM_MEDIA_TYPE** Receive();
private:
void FreeMediaType(AM_MEDIA_TYPE* mt);
void DeleteMediaType(AM_MEDIA_TYPE* mt);
AM_MEDIA_TYPE* media_type_;
};
static HRESULT GetDeviceFilter(const Name& device_name,
IBaseFilter** filter);
static bool PinMatchesCategory(IPin* pin, REFGUID category);
static base::win::ScopedComPtr<IPin> GetPin(IBaseFilter* filter,
PIN_DIRECTION pin_dir,
REFGUID category);
static VideoPixelFormat TranslateMediaSubtypeToPixelFormat(
const GUID& sub_type);
explicit VideoCaptureDeviceWin(const Name& device_name);
virtual ~VideoCaptureDeviceWin();
// Opens the device driver for this device.
// This function is used by the static VideoCaptureDevice::Create function.
bool Init();
// VideoCaptureDevice implementation.
......@@ -45,10 +73,6 @@ class VideoCaptureDeviceWin
scoped_ptr<VideoCaptureDevice::Client> client) OVERRIDE;
virtual void StopAndDeAllocate() OVERRIDE;
static void GetDeviceNames(Names* device_names);
static void GetDeviceSupportedFormats(const Name& device,
VideoCaptureFormats* formats);
private:
enum InternalState {
kIdle, // The device driver is opened but camera is not in use.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment