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;
25 using Name = media::VideoCaptureDevice::Name;
26 using Names = media::VideoCaptureDevice::Names;
30 // Lazy Instance to initialize the MediaFoundation Library.
31 class MFInitializerSingleton {
33 MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); }
34 ~MFInitializerSingleton() { MFShutdown(); }
37 static base::LazyInstance<MFInitializerSingleton> g_mf_initialize =
38 LAZY_INSTANCE_INITIALIZER;
40 static void EnsureMediaFoundationInit() {
41 g_mf_initialize.Get();
44 static bool LoadMediaFoundationDlls() {
45 static const wchar_t* const kMfDLLs[] = {
46 L"%WINDIR%\\system32\\mf.dll",
47 L"%WINDIR%\\system32\\mfplat.dll",
48 L"%WINDIR%\\system32\\mfreadwrite.dll",
51 for (int i = 0; i < arraysize(kMfDLLs); ++i) {
52 wchar_t path[MAX_PATH] = {0};
53 ExpandEnvironmentStringsW(kMfDLLs[i], path, arraysize(path));
54 if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
60 static bool PrepareVideoCaptureAttributesMediaFoundation(
61 IMFAttributes** attributes,
63 EnsureMediaFoundationInit();
65 if (FAILED(MFCreateAttributes(attributes, count)))
68 return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
69 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
72 static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link,
73 IMFMediaSource** source) {
74 ScopedComPtr<IMFAttributes> attributes;
75 if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 2))
78 attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
79 base::SysUTF8ToWide(sym_link).c_str());
81 return SUCCEEDED(MFCreateDeviceSource(attributes, source));
84 static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices,
86 ScopedComPtr<IMFAttributes> attributes;
87 if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1))
90 return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
93 static void GetDeviceNamesDirectShow(
94 const CLSID& class_id,
95 const Name::CaptureApiType capture_api_type,
96 Names* device_names) {
98 DVLOG(1) << " GetDeviceNamesDirectShow";
100 ScopedComPtr<ICreateDevEnum> dev_enum;
101 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
106 ScopedComPtr<IEnumMoniker> enum_moniker;
107 hr = dev_enum->CreateClassEnumerator(class_id, enum_moniker.Receive(), 0);
108 // CreateClassEnumerator returns S_FALSE on some Windows OS
109 // when no camera exist. Therefore the FAILED macro can't be used.
113 // Enumerate all video capture devices.
114 for (ScopedComPtr<IMoniker> moniker;
115 enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK;
117 ScopedComPtr<IPropertyBag> prop_bag;
118 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
122 // Find the description or friendly name.
124 hr = prop_bag->Read(L"Description", name.Receive(), 0);
126 hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
128 if (FAILED(hr) || name.type() != VT_BSTR)
131 // Ignore all VFW drivers and the special Google Camera Adapter.
132 // Google Camera Adapter is not a real DirectShow camera device.
133 // VFW are very old Video for Windows drivers that can not be used.
134 const wchar_t* str_ptr = V_BSTR(&name);
135 // Name of a fake DirectShow filter that exist on computers with
137 static const char kGoogleCameraAdapter[] = "google camera adapter";
138 if (wcsstr(str_ptr, L"(VFW)") != NULL ||
139 LowerCaseEqualsASCII(str_ptr,
140 str_ptr + arraysize(kGoogleCameraAdapter) - 1,
141 kGoogleCameraAdapter)) {
145 const std::string device_name(base::SysWideToUTF8(str_ptr));
147 hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
149 if (FAILED(hr) || name.type() != VT_BSTR) {
152 DCHECK_EQ(name.type(), VT_BSTR);
153 id = base::SysWideToUTF8(V_BSTR(&name));
155 device_names->push_back(Name(device_name, id, capture_api_type));
159 static void GetDeviceNamesMediaFoundation(Names* device_names) {
160 DVLOG(1) << " GetDeviceNamesMediaFoundation";
161 ScopedCoMem<IMFActivate*> devices;
163 if (!EnumerateVideoDevicesMediaFoundation(&devices, &count))
166 for (UINT32 i = 0; i < count; ++i) {
167 ScopedCoMem<wchar_t> name;
169 HRESULT hr = devices[i]->GetAllocatedString(
170 MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size);
172 ScopedCoMem<wchar_t> id;
174 hr = devices[i]->GetAllocatedString(
175 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
178 device_names->push_back(Name(
179 base::SysWideToUTF8(std::wstring(name, name_size)),
180 base::SysWideToUTF8(std::wstring(id, id_size)),
181 Name::MEDIA_FOUNDATION));
184 DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: "
185 << logging::SystemErrorCodeToString(hr);
186 devices[i]->Release();
190 static void GetDeviceSupportedFormatsDirectShow(const Name& device,
191 VideoCaptureFormats* formats) {
192 DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name();
193 ScopedComPtr<ICreateDevEnum> dev_enum;
194 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
199 ScopedComPtr<IEnumMoniker> enum_moniker;
200 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
201 enum_moniker.Receive(), 0);
202 // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
203 // exists. Therefore the FAILED macro can't be used.
207 // Walk the capture devices. No need to check for device presence again since
208 // that is anyway needed in GetDeviceFilter(). "google camera adapter" and old
209 // VFW devices are already skipped previously in GetDeviceNames() enumeration.
210 base::win::ScopedComPtr<IBaseFilter> capture_filter;
211 hr = VideoCaptureDeviceWin::GetDeviceFilter(device.capabilities_id(),
212 CLSID_VideoInputDeviceCategory,
213 capture_filter.Receive());
214 if (!capture_filter) {
215 DLOG(ERROR) << "Failed to create capture filter: "
216 << logging::SystemErrorCodeToString(hr);
220 base::win::ScopedComPtr<IPin> output_capture_pin(
221 VideoCaptureDeviceWin::GetPin(capture_filter,
223 PIN_CATEGORY_CAPTURE,
225 if (!output_capture_pin) {
226 DLOG(ERROR) << "Failed to get capture output pin";
230 ScopedComPtr<IAMStreamConfig> stream_config;
231 hr = output_capture_pin.QueryInterface(stream_config.Receive());
233 DLOG(ERROR) << "Failed to get IAMStreamConfig interface from "
234 "capture device: " << logging::SystemErrorCodeToString(hr);
238 int count = 0, size = 0;
239 hr = stream_config->GetNumberOfCapabilities(&count, &size);
241 DLOG(ERROR) << "GetNumberOfCapabilities failed: "
242 << logging::SystemErrorCodeToString(hr);
246 scoped_ptr<BYTE[]> caps(new BYTE[size]);
247 for (int i = 0; i < count; ++i) {
248 VideoCaptureDeviceWin::ScopedMediaType media_type;
249 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
250 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
251 // macros here since they'll trigger incorrectly.
253 DLOG(ERROR) << "GetStreamCaps failed: "
254 << logging::SystemErrorCodeToString(hr);
258 if (media_type->majortype == MEDIATYPE_Video &&
259 media_type->formattype == FORMAT_VideoInfo) {
260 VideoCaptureFormat format;
261 format.pixel_format =
262 VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
263 media_type->subtype);
264 if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
267 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
268 format.frame_size.SetSize(h->bmiHeader.biWidth,
269 h->bmiHeader.biHeight);
270 // Trust the frame rate from the VIDEOINFOHEADER.
271 format.frame_rate = (h->AvgTimePerFrame > 0) ?
272 kSecondsToReferenceTime / static_cast<float>(h->AvgTimePerFrame) :
274 formats->push_back(format);
275 DVLOG(1) << device.name() << " " << format.ToString();
280 static void GetDeviceSupportedFormatsMediaFoundation(
282 VideoCaptureFormats* formats) {
283 DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name();
284 ScopedComPtr<IMFMediaSource> source;
285 if (!CreateVideoCaptureDeviceMediaFoundation(device.id().c_str(),
290 base::win::ScopedComPtr<IMFSourceReader> reader;
292 MFCreateSourceReaderFromMediaSource(source, NULL, reader.Receive());
294 DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: "
295 << logging::SystemErrorCodeToString(hr);
299 DWORD stream_index = 0;
300 ScopedComPtr<IMFMediaType> type;
301 while (SUCCEEDED(reader->GetNativeMediaType(
302 kFirstVideoStream, stream_index, type.Receive()))) {
303 UINT32 width, height;
304 hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
306 DLOG(ERROR) << "MFGetAttributeSize failed: "
307 << logging::SystemErrorCodeToString(hr);
310 VideoCaptureFormat capture_format;
311 capture_format.frame_size.SetSize(width, height);
313 UINT32 numerator, denominator;
314 hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
316 DLOG(ERROR) << "MFGetAttributeSize failed: "
317 << logging::SystemErrorCodeToString(hr);
320 capture_format.frame_rate = denominator
321 ? static_cast<float>(numerator) / denominator : 0.0f;
324 hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
326 DLOG(ERROR) << "GetGUID failed: "
327 << logging::SystemErrorCodeToString(hr);
330 VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
331 &capture_format.pixel_format);
333 formats->push_back(capture_format);
336 DVLOG(1) << device.name() << " " << capture_format.ToString();
340 // Returns true iff the current platform supports the Media Foundation API
341 // and that the DLLs are available. On Vista this API is an optional download
342 // but the API is advertised as a part of Windows 7 and onwards. However,
343 // we've seen that the required DLLs are not available in some Win7
344 // distributions such as Windows 7 N and Windows 7 KN.
346 bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() {
347 // Even though the DLLs might be available on Vista, we get crashes
348 // when running our tests on the build bots.
349 if (base::win::GetVersion() < base::win::VERSION_WIN7)
352 static bool g_dlls_available = LoadMediaFoundationDlls();
353 return g_dlls_available;
356 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() {
357 // Use Media Foundation for Metro processes (after and including Win8) and
358 // DirectShow for any other versions, unless forced via flag. Media Foundation
359 // can also be forced if appropriate flag is set and we are in Windows 7 or
360 // 8 in non-Metro mode.
361 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
362 use_media_foundation_ = (base::win::IsMetroProcess() &&
363 !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) ||
364 (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
365 cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture));
369 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
370 const Name& device_name) {
371 DCHECK(thread_checker_.CalledOnValidThread());
372 scoped_ptr<VideoCaptureDevice> device;
373 if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
374 DCHECK(PlatformSupportsMediaFoundation());
375 device.reset(new VideoCaptureDeviceMFWin(device_name));
376 DVLOG(1) << " MediaFoundation Device: " << device_name.name();
377 ScopedComPtr<IMFMediaSource> source;
378 if (!CreateVideoCaptureDeviceMediaFoundation(device_name.id().c_str(),
380 return scoped_ptr<VideoCaptureDevice>();
382 if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
385 DCHECK(device_name.capture_api_type() == Name::DIRECT_SHOW ||
386 device_name.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR);
387 device.reset(new VideoCaptureDeviceWin(device_name));
388 DVLOG(1) << " DirectShow Device: " << device_name.name();
389 if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init())
392 return device.Pass();
395 void VideoCaptureDeviceFactoryWin::GetDeviceNames(Names* device_names) {
396 DCHECK(thread_checker_.CalledOnValidThread());
397 if (use_media_foundation_) {
398 GetDeviceNamesMediaFoundation(device_names);
400 GetDeviceNamesDirectShow(CLSID_VideoInputDeviceCategory,
404 Names crossbar_device_names;
405 GetDeviceNamesDirectShow(AM_KSCATEGORY_CROSSBAR,
406 Name::DIRECT_SHOW_WDM_CROSSBAR,
407 &crossbar_device_names);
408 // Search in the listed |device_names| to find a device with matching USB ID
409 // to each device in |crossbar_device_names|.
410 for (Names::iterator crossbar_device_it = crossbar_device_names.begin();
411 crossbar_device_it != crossbar_device_names.end();
412 ++crossbar_device_it) {
413 const std::string& crossbar_device_model = crossbar_device_it->GetModel();
414 for (Names::const_iterator device_it = device_names->begin();
415 device_it != device_names->end(); ++device_it) {
416 if (crossbar_device_model == device_it->GetModel()) {
417 crossbar_device_it->set_capabilities_id(device_it->id());
418 device_names->push_back(*crossbar_device_it);
426 void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
428 VideoCaptureFormats* formats) {
429 DCHECK(thread_checker_.CalledOnValidThread());
430 if (use_media_foundation_)
431 GetDeviceSupportedFormatsMediaFoundation(device, formats);
433 GetDeviceSupportedFormatsDirectShow(device, formats);