linux_aura: Disable the plugin install infobar.
[chromium-blink-merge.git] / media / video / capture / win / video_capture_device_factory_win.cc
blob0ce8a83c82d8936fb21ac5ec64eb18e71d70528d
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"
7 #include <mfapi.h>
8 #include <mferror.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;
26 namespace media {
28 // Lazy Instance to initialize the MediaFoundation Library.
29 class MFInitializerSingleton {
30 public:
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))
53 return false;
55 return true;
58 static bool PrepareVideoCaptureAttributesMediaFoundation(
59 IMFAttributes** attributes,
60 int count) {
61 EnsureMediaFoundationInit();
63 if (FAILED(MFCreateAttributes(attributes, count)))
64 return false;
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))
74 return false;
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,
83 UINT32* count) {
84 ScopedComPtr<IMFAttributes> attributes;
85 if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1))
86 return false;
88 return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
91 static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) {
92 DCHECK(device_names);
93 DVLOG(1) << " GetDeviceNamesDirectShow";
95 ScopedComPtr<ICreateDevEnum> dev_enum;
96 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
97 CLSCTX_INPROC);
98 if (FAILED(hr))
99 return;
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.
106 if (hr != S_OK)
107 return;
109 device_names->clear();
111 // Name of a fake DirectShow filter that exist on computers with
112 // GTalk installed.
113 static const char kGoogleCameraAdapter[] = "google camera adapter";
115 // Enumerate all video capture devices.
116 ScopedComPtr<IMoniker> moniker;
117 int index = 0;
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());
121 if (FAILED(hr)) {
122 moniker.Release();
123 continue;
126 // Find the description or friendly name.
127 ScopedVariant name;
128 hr = prop_bag->Read(L"Description", name.Receive(), 0);
129 if (FAILED(hr))
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)))) {
143 std::string id;
144 std::string device_name(base::SysWideToUTF8(str_ptr));
145 name.Reset();
146 hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
147 if (FAILED(hr) || name.type() != VT_BSTR) {
148 id = device_name;
149 } else {
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));
158 moniker.Release();
162 static void GetDeviceNamesMediaFoundation(
163 VideoCaptureDevice::Names* device_names) {
164 DVLOG(1) << " GetDeviceNamesMediaFoundation";
165 ScopedCoMem<IMFActivate*> devices;
166 UINT32 count;
167 if (!EnumerateVideoDevicesMediaFoundation(&devices, &count))
168 return;
170 HRESULT hr;
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,
178 &id_size))) {
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);
184 } else {
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,
197 CLSCTX_INPROC);
198 if (FAILED(hr))
199 return;
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.
206 if (hr != S_OK)
207 return;
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;
212 int index = 0;
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());
217 if (FAILED(hr)) {
218 moniker.Release();
219 continue;
222 device_id.Reset();
223 hr = prop_bag->Read(L"DevicePath", device_id.Receive(), 0);
224 if (FAILED(hr)) {
225 DVLOG(1) << "Couldn't read a device's DevicePath.";
226 return;
228 if (device.id() == base::SysWideToUTF8(V_BSTR(&device_id)))
229 break;
230 moniker.Release();
233 if (moniker.get()) {
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.";
239 return;
242 base::win::ScopedComPtr<IPin> output_capture_pin(
243 VideoCaptureDeviceWin::GetPin(capture_filter,
244 PINDIR_OUTPUT,
245 PIN_CATEGORY_CAPTURE));
246 if (!output_capture_pin) {
247 DVLOG(2) << "Failed to get capture output pin";
248 return;
251 ScopedComPtr<IAMStreamConfig> stream_config;
252 hr = output_capture_pin.QueryInterface(stream_config.Receive());
253 if (FAILED(hr)) {
254 DVLOG(2) << "Failed to get IAMStreamConfig interface from "
255 "capture device";
256 return;
259 int count = 0, size = 0;
260 hr = stream_config->GetNumberOfCapabilities(&count, &size);
261 if (FAILED(hr)) {
262 DVLOG(2) << "Failed to GetNumberOfCapabilities";
263 return;
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.
272 if (hr != S_OK) {
273 DVLOG(2) << "Failed to GetStreamCaps";
274 return;
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)
284 continue;
285 VIDEOINFOHEADER* h =
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(),
308 source.Receive())) {
309 return;
312 HRESULT hr;
313 base::win::ScopedComPtr<IMFSourceReader> reader;
314 if (FAILED(hr = MFCreateSourceReaderFromMediaSource(source, NULL,
315 reader.Receive()))) {
316 DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource: " << std::hex << hr;
317 return;
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);
326 if (FAILED(hr)) {
327 DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
328 return;
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);
335 if (FAILED(hr)) {
336 DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
337 return;
339 capture_format.frame_rate = denominator ? numerator / denominator : 0;
341 GUID type_guid;
342 hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
343 if (FAILED(hr)) {
344 DLOG(ERROR) << "GetGUID: " << std::hex << hr;
345 return;
347 VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
348 &capture_format.pixel_format);
349 type.Release();
350 formats->push_back(capture_format);
351 ++stream_index;
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.
365 // static
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)
370 return false;
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(),
401 source.Receive())) {
402 return scoped_ptr<VideoCaptureDevice>();
404 if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
405 device.reset();
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())
411 device.reset();
412 } else {
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);
423 else
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);
433 else
434 GetDeviceSupportedFormatsDirectShow(device, formats);
437 } // namespace media