Upstream version 9.38.198.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
26 namespace media {
27
28 // Lazy Instance to initialize the MediaFoundation Library.
29 class MFInitializerSingleton {
30  public:
31   MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); }
32   ~MFInitializerSingleton() { MFShutdown(); }
33 };
34
35 static base::LazyInstance<MFInitializerSingleton> g_mf_initialize =
36     LAZY_INSTANCE_INITIALIZER;
37
38 static void EnsureMediaFoundationInit() {
39   g_mf_initialize.Get();
40 }
41
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",
47   };
48
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;
54   }
55   return true;
56 }
57
58 static bool PrepareVideoCaptureAttributesMediaFoundation(
59     IMFAttributes** attributes,
60     int count) {
61   EnsureMediaFoundationInit();
62
63   if (FAILED(MFCreateAttributes(attributes, count)))
64     return false;
65
66   return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
67       MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
68 }
69
70 static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link,
71                                                     IMFMediaSource** source) {
72   ScopedComPtr<IMFAttributes> attributes;
73   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 2))
74     return false;
75
76   attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
77                         base::SysUTF8ToWide(sym_link).c_str());
78
79   return SUCCEEDED(MFCreateDeviceSource(attributes, source));
80 }
81
82 static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices,
83                                                  UINT32* count) {
84   ScopedComPtr<IMFAttributes> attributes;
85   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1))
86     return false;
87
88   return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
89 }
90
91 static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) {
92   DCHECK(device_names);
93   DVLOG(1) << " GetDeviceNamesDirectShow";
94
95   ScopedComPtr<ICreateDevEnum> dev_enum;
96   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
97                                        CLSCTX_INPROC);
98   if (FAILED(hr))
99     return;
100
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;
108
109   device_names->clear();
110
111   // Name of a fake DirectShow filter that exist on computers with
112   // GTalk installed.
113   static const char kGoogleCameraAdapter[] = "google camera adapter";
114
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;
124     }
125
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);
131
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;
138
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));
152         }
153
154         device_names->push_back(VideoCaptureDevice::Name(device_name, id,
155             VideoCaptureDevice::Name::DIRECT_SHOW));
156       }
157     }
158     moniker.Release();
159   }
160 }
161
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;
169
170   for (UINT32 i = 0; i < count; ++i) {
171     ScopedCoMem<wchar_t> name;
172     UINT32 name_size;
173     HRESULT hr = devices[i]->GetAllocatedString(
174         MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size);
175     if (SUCCEEDED(hr)) {
176       ScopedCoMem<wchar_t> id;
177       UINT32 id_size;
178       hr = devices[i]->GetAllocatedString(
179           MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
180           &id_size);
181       if (SUCCEEDED(hr)) {
182         device_names->push_back(VideoCaptureDevice::Name(
183             base::SysWideToUTF8(std::wstring(name, name_size)),
184             base::SysWideToUTF8(std::wstring(id, id_size)),
185             VideoCaptureDevice::Name::MEDIA_FOUNDATION));
186       }
187     }
188     if (FAILED(hr))
189       DLOG(WARNING) << "GetAllocatedString failed: " << std::hex << hr;
190     devices[i]->Release();
191   }
192 }
193
194 static void GetDeviceSupportedFormatsDirectShow(
195     const VideoCaptureDevice::Name& device,
196     VideoCaptureFormats* formats) {
197   DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name();
198   ScopedComPtr<ICreateDevEnum> dev_enum;
199   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
200                                        CLSCTX_INPROC);
201   if (FAILED(hr))
202     return;
203
204   ScopedComPtr<IEnumMoniker> enum_moniker;
205   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
206                                        enum_moniker.Receive(), 0);
207   // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
208   // exists. Therefore the FAILED macro can't be used.
209   if (hr != S_OK)
210     return;
211
212   // Walk the capture devices. No need to check for device presence again, that
213   // is caught in GetDeviceFilter(). "google camera adapter" and old VFW devices
214   // are already skipped in the previous GetDeviceNames() enumeration.
215   base::win::ScopedComPtr<IBaseFilter> capture_filter;
216   hr = VideoCaptureDeviceWin::GetDeviceFilter(device,
217                                               capture_filter.Receive());
218   if (!capture_filter) {
219     DVLOG(2) << "Failed to create capture filter.";
220     return;
221   }
222
223   base::win::ScopedComPtr<IPin> output_capture_pin(
224       VideoCaptureDeviceWin::GetPin(capture_filter,
225                                     PINDIR_OUTPUT,
226                                     PIN_CATEGORY_CAPTURE));
227   if (!output_capture_pin) {
228     DVLOG(2) << "Failed to get capture output pin";
229     return;
230   }
231
232   ScopedComPtr<IAMStreamConfig> stream_config;
233   hr = output_capture_pin.QueryInterface(stream_config.Receive());
234   if (FAILED(hr)) {
235     DVLOG(2) << "Failed to get IAMStreamConfig interface from "
236                 "capture device";
237     return;
238   }
239
240   int count = 0, size = 0;
241   hr = stream_config->GetNumberOfCapabilities(&count, &size);
242   if (FAILED(hr)) {
243     DVLOG(2) << "Failed to GetNumberOfCapabilities";
244     return;
245   }
246
247   scoped_ptr<BYTE[]> caps(new BYTE[size]);
248   for (int i = 0; i < count; ++i) {
249     VideoCaptureDeviceWin::ScopedMediaType media_type;
250     hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
251     // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
252     // macros here since they'll trigger incorrectly.
253     if (hr != S_OK) {
254       DVLOG(2) << "Failed to GetStreamCaps";
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() << " resolution: "
276           << format.frame_size.ToString() << ", fps: " << format.frame_rate
277           << ", pixel format: " << format.pixel_format;
278     }
279   }
280 }
281
282 static void GetDeviceSupportedFormatsMediaFoundation(
283     const VideoCaptureDevice::Name& device,
284     VideoCaptureFormats* formats) {
285   DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name();
286   ScopedComPtr<IMFMediaSource> source;
287   if (!CreateVideoCaptureDeviceMediaFoundation(device.id().c_str(),
288                                                source.Receive())) {
289     return;
290   }
291
292   base::win::ScopedComPtr<IMFSourceReader> reader;
293   HRESULT hr =
294       MFCreateSourceReaderFromMediaSource(source, NULL, reader.Receive());
295   if (FAILED(hr)) {
296     DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource: " << std::hex << hr;
297     return;
298   }
299
300   DWORD stream_index = 0;
301   ScopedComPtr<IMFMediaType> type;
302   for (hr = reader->GetNativeMediaType(kFirstVideoStream, stream_index,
303                                        type.Receive());
304        SUCCEEDED(hr);
305        hr = reader->GetNativeMediaType(kFirstVideoStream, stream_index,
306                                        type.Receive())) {
307     UINT32 width, height;
308     hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
309     if (FAILED(hr)) {
310       DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
311       return;
312     }
313     VideoCaptureFormat capture_format;
314     capture_format.frame_size.SetSize(width, height);
315
316     UINT32 numerator, denominator;
317     hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
318     if (FAILED(hr)) {
319       DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr;
320       return;
321     }
322     capture_format.frame_rate = denominator
323         ? static_cast<float>(numerator) / denominator : 0.0f;
324
325     GUID type_guid;
326     hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
327     if (FAILED(hr)) {
328       DLOG(ERROR) << "GetGUID: " << std::hex << hr;
329       return;
330     }
331     VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
332                                             &capture_format.pixel_format);
333     type.Release();
334     formats->push_back(capture_format);
335     ++stream_index;
336
337     DVLOG(1) << device.name() << " resolution: "
338              << capture_format.frame_size.ToString() << ", fps: "
339              << capture_format.frame_rate << ", pixel format: "
340              << capture_format.pixel_format;
341   }
342 }
343
344 // Returns true iff the current platform supports the Media Foundation API
345 // and that the DLLs are available.  On Vista this API is an optional download
346 // but the API is advertised as a part of Windows 7 and onwards.  However,
347 // we've seen that the required DLLs are not available in some Win7
348 // distributions such as Windows 7 N and Windows 7 KN.
349 // static
350 bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() {
351   // Even though the DLLs might be available on Vista, we get crashes
352   // when running our tests on the build bots.
353   if (base::win::GetVersion() < base::win::VERSION_WIN7)
354     return false;
355
356   static bool g_dlls_available = LoadMediaFoundationDlls();
357   return g_dlls_available;
358 }
359
360 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() {
361   // Use Media Foundation for Metro processes (after and including Win8) and
362   // DirectShow for any other versions, unless forced via flag. Media Foundation
363   // can also be forced if appropriate flag is set and we are in Windows 7 or
364   // 8 in non-Metro mode.
365   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
366   use_media_foundation_ = (base::win::IsMetroProcess() &&
367       !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) ||
368      (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
369       cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture));
370 }
371
372
373 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
374     const VideoCaptureDevice::Name& device_name) {
375   DCHECK(thread_checker_.CalledOnValidThread());
376   scoped_ptr<VideoCaptureDevice> device;
377   if (device_name.capture_api_type() ==
378       VideoCaptureDevice::Name::MEDIA_FOUNDATION) {
379     DCHECK(PlatformSupportsMediaFoundation());
380     device.reset(new VideoCaptureDeviceMFWin(device_name));
381     DVLOG(1) << " MediaFoundation Device: " << device_name.name();
382     ScopedComPtr<IMFMediaSource> source;
383     if (!CreateVideoCaptureDeviceMediaFoundation(device_name.id().c_str(),
384                                                  source.Receive())) {
385       return scoped_ptr<VideoCaptureDevice>();
386     }
387     if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
388       device.reset();
389   } else {
390     DCHECK_EQ(device_name.capture_api_type(),
391               VideoCaptureDevice::Name::DIRECT_SHOW);
392     device.reset(new VideoCaptureDeviceWin(device_name));
393     DVLOG(1) << " DirectShow Device: " << device_name.name();
394     if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init())
395       device.reset();
396   }
397   return device.Pass();
398 }
399
400 void VideoCaptureDeviceFactoryWin::GetDeviceNames(
401     VideoCaptureDevice::Names* device_names) {
402   DCHECK(thread_checker_.CalledOnValidThread());
403   if (use_media_foundation_)
404     GetDeviceNamesMediaFoundation(device_names);
405   else
406     GetDeviceNamesDirectShow(device_names);
407 }
408
409 void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
410     const VideoCaptureDevice::Name& device,
411     VideoCaptureFormats* formats) {
412   DCHECK(thread_checker_.CalledOnValidThread());
413   if (use_media_foundation_)
414     GetDeviceSupportedFormatsMediaFoundation(device, formats);
415   else
416     GetDeviceSupportedFormatsDirectShow(device, formats);
417 }
418
419 }  // namespace media