Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / win / video_capture_device_factory_win.cc
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.
4
5 #include "media/video/capture/win/video_capture_device_factory_win.h"
6
7 #include <mfapi.h>
8 #include <mferror.h>
9
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"
21
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;
27
28 namespace media {
29
30 // Lazy Instance to initialize the MediaFoundation Library.
31 class MFInitializerSingleton {
32  public:
33   MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); }
34   ~MFInitializerSingleton() { MFShutdown(); }
35 };
36
37 static base::LazyInstance<MFInitializerSingleton> g_mf_initialize =
38     LAZY_INSTANCE_INITIALIZER;
39
40 static void EnsureMediaFoundationInit() {
41   g_mf_initialize.Get();
42 }
43
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",
49   };
50
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))
55       return false;
56   }
57   return true;
58 }
59
60 static bool PrepareVideoCaptureAttributesMediaFoundation(
61     IMFAttributes** attributes,
62     int count) {
63   EnsureMediaFoundationInit();
64
65   if (FAILED(MFCreateAttributes(attributes, count)))
66     return false;
67
68   return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
69       MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
70 }
71
72 static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link,
73                                                     IMFMediaSource** source) {
74   ScopedComPtr<IMFAttributes> attributes;
75   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 2))
76     return false;
77
78   attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
79                         base::SysUTF8ToWide(sym_link).c_str());
80
81   return SUCCEEDED(MFCreateDeviceSource(attributes, source));
82 }
83
84 static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices,
85                                                  UINT32* count) {
86   ScopedComPtr<IMFAttributes> attributes;
87   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1))
88     return false;
89
90   return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
91 }
92
93 static void GetDeviceNamesDirectShow(
94     const CLSID& class_id,
95     const Name::CaptureApiType capture_api_type,
96     Names* device_names) {
97   DCHECK(device_names);
98   DVLOG(1) << " GetDeviceNamesDirectShow";
99
100   ScopedComPtr<ICreateDevEnum> dev_enum;
101   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
102                                        CLSCTX_INPROC);
103   if (FAILED(hr))
104     return;
105
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.
110   if (hr != S_OK)
111     return;
112
113   // Enumerate all video capture devices.
114   for (ScopedComPtr<IMoniker> moniker;
115        enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK;
116        moniker.Release()) {
117     ScopedComPtr<IPropertyBag> prop_bag;
118     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
119     if (FAILED(hr))
120       continue;
121
122     // Find the description or friendly name.
123     ScopedVariant name;
124     hr = prop_bag->Read(L"Description", name.Receive(), 0);
125     if (FAILED(hr))
126       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
127
128     if (FAILED(hr) || name.type() != VT_BSTR)
129       continue;
130
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
136     // GTalk installed.
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)) {
142       continue;
143     }
144
145     const std::string device_name(base::SysWideToUTF8(str_ptr));
146     name.Reset();
147     hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
148     std::string id;
149     if (FAILED(hr) || name.type() != VT_BSTR) {
150       id = device_name;
151     } else {
152       DCHECK_EQ(name.type(), VT_BSTR);
153       id = base::SysWideToUTF8(V_BSTR(&name));
154     }
155     device_names->push_back(Name(device_name, id, capture_api_type));
156   }
157 }
158
159 static void GetDeviceNamesMediaFoundation(Names* device_names) {
160   DVLOG(1) << " GetDeviceNamesMediaFoundation";
161   ScopedCoMem<IMFActivate*> devices;
162   UINT32 count;
163   if (!EnumerateVideoDevicesMediaFoundation(&devices, &count))
164     return;
165
166   for (UINT32 i = 0; i < count; ++i) {
167     ScopedCoMem<wchar_t> name;
168     UINT32 name_size;
169     HRESULT hr = devices[i]->GetAllocatedString(
170         MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size);
171     if (SUCCEEDED(hr)) {
172       ScopedCoMem<wchar_t> id;
173       UINT32 id_size;
174       hr = devices[i]->GetAllocatedString(
175           MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
176           &id_size);
177       if (SUCCEEDED(hr)) {
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));
182       }
183     }
184     DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: "
185                                << logging::SystemErrorCodeToString(hr);
186     devices[i]->Release();
187   }
188 }
189
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,
195                                        CLSCTX_INPROC);
196   if (FAILED(hr))
197     return;
198
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.
204   if (hr != S_OK)
205     return;
206
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);
217     return;
218   }
219
220   base::win::ScopedComPtr<IPin> output_capture_pin(
221       VideoCaptureDeviceWin::GetPin(capture_filter,
222                                     PINDIR_OUTPUT,
223                                     PIN_CATEGORY_CAPTURE,
224                                     GUID_NULL));
225   if (!output_capture_pin) {
226     DLOG(ERROR) << "Failed to get capture output pin";
227     return;
228   }
229
230   ScopedComPtr<IAMStreamConfig> stream_config;
231   hr = output_capture_pin.QueryInterface(stream_config.Receive());
232   if (FAILED(hr)) {
233     DLOG(ERROR) << "Failed to get IAMStreamConfig interface from "
234                    "capture device: " << logging::SystemErrorCodeToString(hr);
235     return;
236   }
237
238   int count = 0, size = 0;
239   hr = stream_config->GetNumberOfCapabilities(&count, &size);
240   if (FAILED(hr)) {
241     DLOG(ERROR) << "GetNumberOfCapabilities failed: "
242                 << logging::SystemErrorCodeToString(hr);
243     return;
244   }
245
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.
252     if (hr != S_OK) {
253       DLOG(ERROR) << "GetStreamCaps failed: "
254                   << logging::SystemErrorCodeToString(hr);
255       return;
256     }
257
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)
265         continue;
266       VIDEOINFOHEADER* h =
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) :
273           0.0f;
274       formats->push_back(format);
275       DVLOG(1) << device.name() << " " << format.ToString();
276     }
277   }
278 }
279
280 static void GetDeviceSupportedFormatsMediaFoundation(
281     const Name& device,
282     VideoCaptureFormats* formats) {
283   DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name();
284   ScopedComPtr<IMFMediaSource> source;
285   if (!CreateVideoCaptureDeviceMediaFoundation(device.id().c_str(),
286                                                source.Receive())) {
287     return;
288   }
289
290   base::win::ScopedComPtr<IMFSourceReader> reader;
291   HRESULT hr =
292       MFCreateSourceReaderFromMediaSource(source, NULL, reader.Receive());
293   if (FAILED(hr)) {
294     DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: "
295                 << logging::SystemErrorCodeToString(hr);
296     return;
297   }
298
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);
305     if (FAILED(hr)) {
306       DLOG(ERROR) << "MFGetAttributeSize failed: "
307                   << logging::SystemErrorCodeToString(hr);
308       return;
309     }
310     VideoCaptureFormat capture_format;
311     capture_format.frame_size.SetSize(width, height);
312
313     UINT32 numerator, denominator;
314     hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
315     if (FAILED(hr)) {
316       DLOG(ERROR) << "MFGetAttributeSize failed: "
317                   << logging::SystemErrorCodeToString(hr);
318       return;
319     }
320     capture_format.frame_rate = denominator
321         ? static_cast<float>(numerator) / denominator : 0.0f;
322
323     GUID type_guid;
324     hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
325     if (FAILED(hr)) {
326       DLOG(ERROR) << "GetGUID failed: "
327                   << logging::SystemErrorCodeToString(hr);
328       return;
329     }
330     VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
331                                             &capture_format.pixel_format);
332     type.Release();
333     formats->push_back(capture_format);
334     ++stream_index;
335
336     DVLOG(1) << device.name() << " " << capture_format.ToString();
337   }
338 }
339
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.
345 // static
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)
350     return false;
351
352   static bool g_dlls_available = LoadMediaFoundationDlls();
353   return g_dlls_available;
354 }
355
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));
366 }
367
368
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(),
379                                                  source.Receive())) {
380       return scoped_ptr<VideoCaptureDevice>();
381     }
382     if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
383       device.reset();
384   } else {
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())
390       device.reset();
391   }
392   return device.Pass();
393 }
394
395 void VideoCaptureDeviceFactoryWin::GetDeviceNames(Names* device_names) {
396   DCHECK(thread_checker_.CalledOnValidThread());
397   if (use_media_foundation_) {
398     GetDeviceNamesMediaFoundation(device_names);
399   } else {
400     GetDeviceNamesDirectShow(CLSID_VideoInputDeviceCategory,
401                              Name::DIRECT_SHOW,
402                              device_names);
403
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);
419           break;
420         }
421       }
422     }
423   }
424 }
425
426 void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
427     const Name& device,
428     VideoCaptureFormats* formats) {
429   DCHECK(thread_checker_.CalledOnValidThread());
430   if (use_media_foundation_)
431     GetDeviceSupportedFormatsMediaFoundation(device, formats);
432   else
433     GetDeviceSupportedFormatsDirectShow(device, formats);
434 }
435
436 }  // namespace media