1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/video/capture/win/video_capture_device_factory_win.h"
10 #include "base/command_line.h"
11 #include "base/lazy_instance.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/win/metro.h"
15 #include "base/win/scoped_co_mem.h"
16 #include "base/win/scoped_variant.h"
17 #include "base/win/windows_version.h"
18 #include "media/base/media_switches.h"
19 #include "media/video/capture/win/video_capture_device_mf_win.h"
20 #include "media/video/capture/win/video_capture_device_win.h"
22 using base::win::ScopedCoMem
;
23 using base::win::ScopedComPtr
;
24 using base::win::ScopedVariant
;
28 // Lazy Instance to initialize the MediaFoundation Library.
29 class MFInitializerSingleton
{
31 MFInitializerSingleton() { MFStartup(MF_VERSION
, MFSTARTUP_LITE
); }
32 ~MFInitializerSingleton() { MFShutdown(); }
35 static base::LazyInstance
<MFInitializerSingleton
> g_mf_initialize
=
36 LAZY_INSTANCE_INITIALIZER
;
38 static void EnsureMediaFoundationInit() {
39 g_mf_initialize
.Get();
42 static bool LoadMediaFoundationDlls() {
43 static const wchar_t* const kMfDLLs
[] = {
44 L
"%WINDIR%\\system32\\mf.dll",
45 L
"%WINDIR%\\system32\\mfplat.dll",
46 L
"%WINDIR%\\system32\\mfreadwrite.dll",
49 for (int i
= 0; i
< arraysize(kMfDLLs
); ++i
) {
50 wchar_t path
[MAX_PATH
] = {0};
51 ExpandEnvironmentStringsW(kMfDLLs
[i
], path
, arraysize(path
));
52 if (!LoadLibraryExW(path
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
))
58 static bool PrepareVideoCaptureAttributesMediaFoundation(
59 IMFAttributes
** attributes
,
61 EnsureMediaFoundationInit();
63 if (FAILED(MFCreateAttributes(attributes
, count
)))
66 return SUCCEEDED((*attributes
)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE
,
67 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
));
70 static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link
,
71 IMFMediaSource
** source
) {
72 ScopedComPtr
<IMFAttributes
> attributes
;
73 if (!PrepareVideoCaptureAttributesMediaFoundation(attributes
.Receive(), 2))
76 attributes
->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK
,
77 base::SysUTF8ToWide(sym_link
).c_str());
79 return SUCCEEDED(MFCreateDeviceSource(attributes
, source
));
82 static bool EnumerateVideoDevicesMediaFoundation(IMFActivate
*** devices
,
84 ScopedComPtr
<IMFAttributes
> attributes
;
85 if (!PrepareVideoCaptureAttributesMediaFoundation(attributes
.Receive(), 1))
88 return SUCCEEDED(MFEnumDeviceSources(attributes
, devices
, count
));
91 static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names
* device_names
) {
93 DVLOG(1) << " GetDeviceNamesDirectShow";
95 ScopedComPtr
<ICreateDevEnum
> dev_enum
;
96 HRESULT hr
= dev_enum
.CreateInstance(CLSID_SystemDeviceEnum
, NULL
,
101 ScopedComPtr
<IEnumMoniker
> enum_moniker
;
102 hr
= dev_enum
->CreateClassEnumerator(CLSID_VideoInputDeviceCategory
,
103 enum_moniker
.Receive(), 0);
104 // CreateClassEnumerator returns S_FALSE on some Windows OS
105 // when no camera exist. Therefore the FAILED macro can't be used.
109 device_names
->clear();
111 // Name of a fake DirectShow filter that exist on computers with
113 static const char kGoogleCameraAdapter
[] = "google camera adapter";
115 // Enumerate all video capture devices.
116 ScopedComPtr
<IMoniker
> moniker
;
118 while (enum_moniker
->Next(1, moniker
.Receive(), NULL
) == S_OK
) {
119 ScopedComPtr
<IPropertyBag
> prop_bag
;
120 hr
= moniker
->BindToStorage(0, 0, IID_IPropertyBag
, prop_bag
.ReceiveVoid());
126 // Find the description or friendly name.
128 hr
= prop_bag
->Read(L
"Description", name
.Receive(), 0);
130 hr
= prop_bag
->Read(L
"FriendlyName", name
.Receive(), 0);
132 if (SUCCEEDED(hr
) && name
.type() == VT_BSTR
) {
133 // Ignore all VFW drivers and the special Google Camera Adapter.
134 // Google Camera Adapter is not a real DirectShow camera device.
135 // VFW are very old Video for Windows drivers that can not be used.
136 const wchar_t* str_ptr
= V_BSTR(&name
);
137 const int name_length
= arraysize(kGoogleCameraAdapter
) - 1;
139 if ((wcsstr(str_ptr
, L
"(VFW)") == NULL
) &&
140 lstrlenW(str_ptr
) < name_length
||
141 (!(LowerCaseEqualsASCII(str_ptr
, str_ptr
+ name_length
,
142 kGoogleCameraAdapter
)))) {
144 std::string
device_name(base::SysWideToUTF8(str_ptr
));
146 hr
= prop_bag
->Read(L
"DevicePath", name
.Receive(), 0);
147 if (FAILED(hr
) || name
.type() != VT_BSTR
) {
150 DCHECK_EQ(name
.type(), VT_BSTR
);
151 id
= base::SysWideToUTF8(V_BSTR(&name
));
154 device_names
->push_back(VideoCaptureDevice::Name(device_name
, id
,
155 VideoCaptureDevice::Name::DIRECT_SHOW
));
162 static void GetDeviceNamesMediaFoundation(
163 VideoCaptureDevice::Names
* device_names
) {
164 DVLOG(1) << " GetDeviceNamesMediaFoundation";
165 ScopedCoMem
<IMFActivate
*> devices
;
167 if (!EnumerateVideoDevicesMediaFoundation(&devices
, &count
))
171 for (UINT32 i
= 0; i
< count
; ++i
) {
172 UINT32 name_size
, id_size
;
173 ScopedCoMem
<wchar_t> name
, id
;
174 if (SUCCEEDED(hr
= devices
[i
]->GetAllocatedString(
175 MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME
, &name
, &name_size
)) &&
176 SUCCEEDED(hr
= devices
[i
]->GetAllocatedString(
177 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK
, &id
,
179 std::wstring
name_w(name
, name_size
), id_w(id
, id_size
);
180 VideoCaptureDevice::Name
device(base::SysWideToUTF8(name_w
),
181 base::SysWideToUTF8(id_w
),
182 VideoCaptureDevice::Name::MEDIA_FOUNDATION
);
183 device_names
->push_back(device
);
185 DLOG(WARNING
) << "GetAllocatedString failed: " << std::hex
<< hr
;
187 devices
[i
]->Release();
191 static void GetDeviceSupportedFormatsDirectShow(
192 const VideoCaptureDevice::Name
& device
,
193 VideoCaptureFormats
* formats
) {
194 DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device
.name();
195 ScopedComPtr
<ICreateDevEnum
> dev_enum
;
196 HRESULT hr
= dev_enum
.CreateInstance(CLSID_SystemDeviceEnum
, NULL
,
201 ScopedComPtr
<IEnumMoniker
> enum_moniker
;
202 hr
= dev_enum
->CreateClassEnumerator(CLSID_VideoInputDeviceCategory
,
203 enum_moniker
.Receive(), 0);
204 // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
205 // exists. Therefore the FAILED macro can't be used.
209 // Walk the capture devices. No need to check for "google camera adapter",
210 // since this is already skipped in the enumeration of GetDeviceNames().
211 ScopedComPtr
<IMoniker
> moniker
;
213 ScopedVariant device_id
;
214 while (enum_moniker
->Next(1, moniker
.Receive(), NULL
) == S_OK
) {
215 ScopedComPtr
<IPropertyBag
> prop_bag
;
216 hr
= moniker
->BindToStorage(0, 0, IID_IPropertyBag
, prop_bag
.ReceiveVoid());
223 hr
= prop_bag
->Read(L
"DevicePath", device_id
.Receive(), 0);
225 DVLOG(1) << "Couldn't read a device's DevicePath.";
228 if (device
.id() == base::SysWideToUTF8(V_BSTR(&device_id
)))
234 base::win::ScopedComPtr
<IBaseFilter
> capture_filter
;
235 hr
= VideoCaptureDeviceWin::GetDeviceFilter(device
,
236 capture_filter
.Receive());
237 if (!capture_filter
) {
238 DVLOG(2) << "Failed to create capture filter.";
242 base::win::ScopedComPtr
<IPin
> output_capture_pin(
243 VideoCaptureDeviceWin::GetPin(capture_filter
,
245 PIN_CATEGORY_CAPTURE
));
246 if (!output_capture_pin
) {
247 DVLOG(2) << "Failed to get capture output pin";
251 ScopedComPtr
<IAMStreamConfig
> stream_config
;
252 hr
= output_capture_pin
.QueryInterface(stream_config
.Receive());
254 DVLOG(2) << "Failed to get IAMStreamConfig interface from "
259 int count
= 0, size
= 0;
260 hr
= stream_config
->GetNumberOfCapabilities(&count
, &size
);
262 DVLOG(2) << "Failed to GetNumberOfCapabilities";
266 scoped_ptr
<BYTE
[]> caps(new BYTE
[size
]);
267 for (int i
= 0; i
< count
; ++i
) {
268 VideoCaptureDeviceWin::ScopedMediaType media_type
;
269 hr
= stream_config
->GetStreamCaps(i
, media_type
.Receive(), caps
.get());
270 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
271 // macros here since they'll trigger incorrectly.
273 DVLOG(2) << "Failed to GetStreamCaps";
277 if (media_type
->majortype
== MEDIATYPE_Video
&&
278 media_type
->formattype
== FORMAT_VideoInfo
) {
279 VideoCaptureFormat format
;
280 format
.pixel_format
=
281 VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
282 media_type
->subtype
);
283 if (format
.pixel_format
== PIXEL_FORMAT_UNKNOWN
)
286 reinterpret_cast<VIDEOINFOHEADER
*>(media_type
->pbFormat
);
287 format
.frame_size
.SetSize(h
->bmiHeader
.biWidth
,
288 h
->bmiHeader
.biHeight
);
289 // Trust the frame rate from the VIDEOINFOHEADER.
290 format
.frame_rate
= (h
->AvgTimePerFrame
> 0) ?
291 static_cast<int>(kSecondsToReferenceTime
/ h
->AvgTimePerFrame
) :
293 formats
->push_back(format
);
294 DVLOG(1) << device
.name() << " resolution: "
295 << format
.frame_size
.ToString() << ", fps: " << format
.frame_rate
296 << ", pixel format: " << format
.pixel_format
;
302 static void GetDeviceSupportedFormatsMediaFoundation(
303 const VideoCaptureDevice::Name
& device
,
304 VideoCaptureFormats
* formats
) {
305 DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device
.name();
306 ScopedComPtr
<IMFMediaSource
> source
;
307 if (!CreateVideoCaptureDeviceMediaFoundation(device
.id().c_str(),
313 base::win::ScopedComPtr
<IMFSourceReader
> reader
;
314 if (FAILED(hr
= MFCreateSourceReaderFromMediaSource(source
, NULL
,
315 reader
.Receive()))) {
316 DLOG(ERROR
) << "MFCreateSourceReaderFromMediaSource: " << std::hex
<< hr
;
320 DWORD stream_index
= 0;
321 ScopedComPtr
<IMFMediaType
> type
;
322 while (SUCCEEDED(hr
= reader
->GetNativeMediaType(
323 MF_SOURCE_READER_FIRST_VIDEO_STREAM
, stream_index
, type
.Receive()))) {
324 UINT32 width
, height
;
325 hr
= MFGetAttributeSize(type
, MF_MT_FRAME_SIZE
, &width
, &height
);
327 DLOG(ERROR
) << "MFGetAttributeSize: " << std::hex
<< hr
;
330 VideoCaptureFormat capture_format
;
331 capture_format
.frame_size
.SetSize(width
, height
);
333 UINT32 numerator
, denominator
;
334 hr
= MFGetAttributeRatio(type
, MF_MT_FRAME_RATE
, &numerator
, &denominator
);
336 DLOG(ERROR
) << "MFGetAttributeSize: " << std::hex
<< hr
;
339 capture_format
.frame_rate
= denominator
? numerator
/ denominator
: 0;
342 hr
= type
->GetGUID(MF_MT_SUBTYPE
, &type_guid
);
344 DLOG(ERROR
) << "GetGUID: " << std::hex
<< hr
;
347 VideoCaptureDeviceMFWin::FormatFromGuid(type_guid
,
348 &capture_format
.pixel_format
);
350 formats
->push_back(capture_format
);
353 DVLOG(1) << device
.name() << " resolution: "
354 << capture_format
.frame_size
.ToString() << ", fps: "
355 << capture_format
.frame_rate
<< ", pixel format: "
356 << capture_format
.pixel_format
;
360 // Returns true iff the current platform supports the Media Foundation API
361 // and that the DLLs are available. On Vista this API is an optional download
362 // but the API is advertised as a part of Windows 7 and onwards. However,
363 // we've seen that the required DLLs are not available in some Win7
364 // distributions such as Windows 7 N and Windows 7 KN.
366 bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() {
367 // Even though the DLLs might be available on Vista, we get crashes
368 // when running our tests on the build bots.
369 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
372 static bool g_dlls_available
= LoadMediaFoundationDlls();
373 return g_dlls_available
;
376 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() {
377 // Use Media Foundation for Metro processes (after and including Win8) and
378 // DirectShow for any other versions, unless forced via flag. Media Foundation
379 // can also be forced if appropriate flag is set and we are in Windows 7 or
380 // 8 in non-Metro mode.
381 const CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
382 use_media_foundation_
= (base::win::IsMetroProcess() &&
383 !cmd_line
->HasSwitch(switches::kForceDirectShowVideoCapture
)) ||
384 (base::win::GetVersion() >= base::win::VERSION_WIN7
&&
385 cmd_line
->HasSwitch(switches::kForceMediaFoundationVideoCapture
));
389 scoped_ptr
<VideoCaptureDevice
> VideoCaptureDeviceFactoryWin::Create(
390 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
,
391 const VideoCaptureDevice::Name
& device_name
) {
392 DCHECK(thread_checker_
.CalledOnValidThread());
393 scoped_ptr
<VideoCaptureDevice
> device
;
394 if (device_name
.capture_api_type() ==
395 VideoCaptureDevice::Name::MEDIA_FOUNDATION
) {
396 DCHECK(PlatformSupportsMediaFoundation());
397 device
.reset(new VideoCaptureDeviceMFWin(device_name
));
398 DVLOG(1) << " MediaFoundation Device: " << device_name
.name();
399 ScopedComPtr
<IMFMediaSource
> source
;
400 if (!CreateVideoCaptureDeviceMediaFoundation(device_name
.id().c_str(),
402 return scoped_ptr
<VideoCaptureDevice
>();
404 if (!static_cast<VideoCaptureDeviceMFWin
*>(device
.get())->Init(source
))
406 } else if (device_name
.capture_api_type() ==
407 VideoCaptureDevice::Name::DIRECT_SHOW
) {
408 device
.reset(new VideoCaptureDeviceWin(device_name
));
409 DVLOG(1) << " DirectShow Device: " << device_name
.name();
410 if (!static_cast<VideoCaptureDeviceWin
*>(device
.get())->Init())
413 NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
415 return device
.Pass();
418 void VideoCaptureDeviceFactoryWin::GetDeviceNames(
419 VideoCaptureDevice::Names
* device_names
) {
420 DCHECK(thread_checker_
.CalledOnValidThread());
421 if (use_media_foundation_
)
422 GetDeviceNamesMediaFoundation(device_names
);
424 GetDeviceNamesDirectShow(device_names
);
427 void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
428 const VideoCaptureDevice::Name
& device
,
429 VideoCaptureFormats
* formats
) {
430 DCHECK(thread_checker_
.CalledOnValidThread());
431 if (use_media_foundation_
)
432 GetDeviceSupportedFormatsMediaFoundation(device
, formats
);
434 GetDeviceSupportedFormatsDirectShow(device
, formats
);