Update To 11.40.268.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/metrics/histogram.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/win/metro.h"
16 #include "base/win/scoped_co_mem.h"
17 #include "base/win/scoped_variant.h"
18 #include "base/win/windows_version.h"
19 #include "media/base/media_switches.h"
20 #include "media/video/capture/win/video_capture_device_mf_win.h"
21 #include "media/video/capture/win/video_capture_device_win.h"
22
23 using base::win::ScopedCoMem;
24 using base::win::ScopedComPtr;
25 using base::win::ScopedVariant;
26 using Name = media::VideoCaptureDevice::Name;
27 using Names = media::VideoCaptureDevice::Names;
28
29 namespace media {
30
31 // We would like to avoid enumerating and/or using certain devices due to they
32 // provoking crashes or any other reason. This enum is defined for the purposes
33 // of UMA collection. Existing entries cannot be removed.
34 enum BlacklistedCameraNames {
35   BLACKLISTED_CAMERA_GOOGLE_CAMERA_ADAPTER,
36   BLACKLISTED_CAMERA_IP_CAMERA,
37   BLACKLISTED_CAMERA_CYBERLINK_WEBCAM_SPLITTER,
38    // This one must be last, and equal to the previous enumerated value.
39   BLACKLISTED_CAMERA_MAX = BLACKLISTED_CAMERA_CYBERLINK_WEBCAM_SPLITTER
40 };
41
42 // Lazy Instance to initialize the MediaFoundation Library.
43 class MFInitializerSingleton {
44  public:
45   MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); }
46   ~MFInitializerSingleton() { MFShutdown(); }
47 };
48
49 static base::LazyInstance<MFInitializerSingleton> g_mf_initialize =
50     LAZY_INSTANCE_INITIALIZER;
51
52 // Blacklisted devices are identified by a characteristic prefix of the name.
53 // This prefix is used case-insensitively. This list must be kept in sync with
54 // |BlacklistedCameraNames|.
55 static const char* const kBlacklistedCameraNames[] = {
56   // Name of a fake DirectShow filter on computers with GTalk installed.
57   "Google Camera Adapter",
58   // The following two software WebCams cause crashes.
59   "IP Camera [JPEG/MJPEG]",
60   "CyberLink Webcam Splitter",
61 };
62
63 static void EnsureMediaFoundationInit() {
64   g_mf_initialize.Get();
65 }
66
67 static bool LoadMediaFoundationDlls() {
68   static const wchar_t* const kMfDLLs[] = {
69     L"%WINDIR%\\system32\\mf.dll",
70     L"%WINDIR%\\system32\\mfplat.dll",
71     L"%WINDIR%\\system32\\mfreadwrite.dll",
72   };
73
74   for (int i = 0; i < arraysize(kMfDLLs); ++i) {
75     wchar_t path[MAX_PATH] = {0};
76     ExpandEnvironmentStringsW(kMfDLLs[i], path, arraysize(path));
77     if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
78       return false;
79   }
80   return true;
81 }
82
83 static bool PrepareVideoCaptureAttributesMediaFoundation(
84     IMFAttributes** attributes,
85     int count) {
86   EnsureMediaFoundationInit();
87
88   if (FAILED(MFCreateAttributes(attributes, count)))
89     return false;
90
91   return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
92       MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
93 }
94
95 static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link,
96                                                     IMFMediaSource** source) {
97   ScopedComPtr<IMFAttributes> attributes;
98   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 2))
99     return false;
100
101   attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
102                         base::SysUTF8ToWide(sym_link).c_str());
103
104   return SUCCEEDED(MFCreateDeviceSource(attributes, source));
105 }
106
107 static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices,
108                                                  UINT32* count) {
109   ScopedComPtr<IMFAttributes> attributes;
110   if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1))
111     return false;
112
113   return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count));
114 }
115
116 static bool IsDeviceBlackListed(const std::string& name) {
117   DCHECK_EQ(BLACKLISTED_CAMERA_MAX + 1,
118             static_cast<int>(arraysize(kBlacklistedCameraNames)));
119   for (size_t i = 0; i < arraysize(kBlacklistedCameraNames); ++i) {
120     if (StartsWithASCII(name, kBlacklistedCameraNames[i], false)) {
121       DVLOG(1) << "Enumerated blacklisted device: " << name;
122       UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.BlacklistedDevice",
123           i, BLACKLISTED_CAMERA_MAX + 1);
124       return true;
125     }
126   }
127   return false;
128 }
129
130 static void GetDeviceNamesDirectShow(
131     const CLSID& class_id,
132     const Name::CaptureApiType capture_api_type,
133     Names* device_names) {
134   DCHECK(device_names);
135   DVLOG(1) << " GetDeviceNamesDirectShow";
136
137   ScopedComPtr<ICreateDevEnum> dev_enum;
138   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
139                                        CLSCTX_INPROC);
140   if (FAILED(hr))
141     return;
142
143   ScopedComPtr<IEnumMoniker> enum_moniker;
144   hr = dev_enum->CreateClassEnumerator(class_id, enum_moniker.Receive(), 0);
145   // CreateClassEnumerator returns S_FALSE on some Windows OS
146   // when no camera exist. Therefore the FAILED macro can't be used.
147   if (hr != S_OK)
148     return;
149
150   // Enumerate all video capture devices.
151   for (ScopedComPtr<IMoniker> moniker;
152        enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK;
153        moniker.Release()) {
154     ScopedComPtr<IPropertyBag> prop_bag;
155     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
156     if (FAILED(hr))
157       continue;
158
159     // Find the description or friendly name.
160     ScopedVariant name;
161     hr = prop_bag->Read(L"Description", name.Receive(), 0);
162     if (FAILED(hr))
163       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
164
165     if (FAILED(hr) || name.type() != VT_BSTR)
166       continue;
167
168     const std::string device_name(base::SysWideToUTF8(V_BSTR(&name)));
169     if (IsDeviceBlackListed(device_name))
170       continue;
171
172     name.Reset();
173     hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
174     std::string id;
175     if (FAILED(hr) || name.type() != VT_BSTR) {
176       id = device_name;
177     } else {
178       DCHECK_EQ(name.type(), VT_BSTR);
179       id = base::SysWideToUTF8(V_BSTR(&name));
180     }
181     device_names->push_back(Name(device_name, id, capture_api_type));
182   }
183 }
184
185 static void GetDeviceNamesMediaFoundation(Names* device_names) {
186   DVLOG(1) << " GetDeviceNamesMediaFoundation";
187   ScopedCoMem<IMFActivate*> devices;
188   UINT32 count;
189   if (!EnumerateVideoDevicesMediaFoundation(&devices, &count))
190     return;
191
192   for (UINT32 i = 0; i < count; ++i) {
193     ScopedCoMem<wchar_t> name;
194     UINT32 name_size;
195     HRESULT hr = devices[i]->GetAllocatedString(
196         MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size);
197     if (SUCCEEDED(hr)) {
198       ScopedCoMem<wchar_t> id;
199       UINT32 id_size;
200       hr = devices[i]->GetAllocatedString(
201           MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id,
202           &id_size);
203       if (SUCCEEDED(hr)) {
204         device_names->push_back(Name(
205             base::SysWideToUTF8(std::wstring(name, name_size)),
206             base::SysWideToUTF8(std::wstring(id, id_size)),
207             Name::MEDIA_FOUNDATION));
208       }
209     }
210     DLOG_IF(ERROR, FAILED(hr)) << "GetAllocatedString failed: "
211                                << logging::SystemErrorCodeToString(hr);
212     devices[i]->Release();
213   }
214 }
215
216 static void GetDeviceSupportedFormatsDirectShow(const Name& device,
217                                                 VideoCaptureFormats* formats) {
218   DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name();
219   ScopedComPtr<ICreateDevEnum> dev_enum;
220   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
221                                        CLSCTX_INPROC);
222   if (FAILED(hr))
223     return;
224
225   ScopedComPtr<IEnumMoniker> enum_moniker;
226   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
227                                        enum_moniker.Receive(), 0);
228   // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
229   // exists. Therefore the FAILED macro can't be used.
230   if (hr != S_OK)
231     return;
232
233   // Walk the capture devices. No need to check for device presence again since
234   // that is anyway needed in GetDeviceFilter(). "google camera adapter" and old
235   // VFW devices are already skipped previously in GetDeviceNames() enumeration.
236   base::win::ScopedComPtr<IBaseFilter> capture_filter;
237   hr = VideoCaptureDeviceWin::GetDeviceFilter(device.capabilities_id(),
238                                               CLSID_VideoInputDeviceCategory,
239                                               capture_filter.Receive());
240   if (!capture_filter) {
241     DLOG(ERROR) << "Failed to create capture filter: "
242                 << logging::SystemErrorCodeToString(hr);
243     return;
244   }
245
246   base::win::ScopedComPtr<IPin> output_capture_pin(
247       VideoCaptureDeviceWin::GetPin(capture_filter,
248                                     PINDIR_OUTPUT,
249                                     PIN_CATEGORY_CAPTURE,
250                                     GUID_NULL));
251   if (!output_capture_pin) {
252     DLOG(ERROR) << "Failed to get capture output pin";
253     return;
254   }
255
256   ScopedComPtr<IAMStreamConfig> stream_config;
257   hr = output_capture_pin.QueryInterface(stream_config.Receive());
258   if (FAILED(hr)) {
259     DLOG(ERROR) << "Failed to get IAMStreamConfig interface from "
260                    "capture device: " << logging::SystemErrorCodeToString(hr);
261     return;
262   }
263
264   int count = 0, size = 0;
265   hr = stream_config->GetNumberOfCapabilities(&count, &size);
266   if (FAILED(hr)) {
267     DLOG(ERROR) << "GetNumberOfCapabilities failed: "
268                 << logging::SystemErrorCodeToString(hr);
269     return;
270   }
271
272   scoped_ptr<BYTE[]> caps(new BYTE[size]);
273   for (int i = 0; i < count; ++i) {
274     VideoCaptureDeviceWin::ScopedMediaType media_type;
275     hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
276     // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
277     // macros here since they'll trigger incorrectly.
278     if (hr != S_OK || !media_type.get()) {
279       DLOG(ERROR) << "GetStreamCaps failed: "
280                   << logging::SystemErrorCodeToString(hr);
281       return;
282     }
283
284     if (media_type->majortype == MEDIATYPE_Video &&
285         media_type->formattype == FORMAT_VideoInfo) {
286       VideoCaptureFormat format;
287       format.pixel_format =
288           VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat(
289               media_type->subtype);
290       if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
291         continue;
292       VIDEOINFOHEADER* h =
293           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
294       format.frame_size.SetSize(h->bmiHeader.biWidth,
295                                 h->bmiHeader.biHeight);
296       // Trust the frame rate from the VIDEOINFOHEADER.
297       format.frame_rate = (h->AvgTimePerFrame > 0) ?
298           kSecondsToReferenceTime / static_cast<float>(h->AvgTimePerFrame) :
299           0.0f;
300       formats->push_back(format);
301       DVLOG(1) << device.name() << " " << format.ToString();
302     }
303   }
304 }
305
306 static void GetDeviceSupportedFormatsMediaFoundation(
307     const Name& device,
308     VideoCaptureFormats* formats) {
309   DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name();
310   ScopedComPtr<IMFMediaSource> source;
311   if (!CreateVideoCaptureDeviceMediaFoundation(device.id().c_str(),
312                                                source.Receive())) {
313     return;
314   }
315
316   base::win::ScopedComPtr<IMFSourceReader> reader;
317   HRESULT hr =
318       MFCreateSourceReaderFromMediaSource(source, NULL, reader.Receive());
319   if (FAILED(hr)) {
320     DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource failed: "
321                 << logging::SystemErrorCodeToString(hr);
322     return;
323   }
324
325   DWORD stream_index = 0;
326   ScopedComPtr<IMFMediaType> type;
327   while (SUCCEEDED(reader->GetNativeMediaType(
328              kFirstVideoStream, stream_index, type.Receive()))) {
329     UINT32 width, height;
330     hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height);
331     if (FAILED(hr)) {
332       DLOG(ERROR) << "MFGetAttributeSize failed: "
333                   << logging::SystemErrorCodeToString(hr);
334       return;
335     }
336     VideoCaptureFormat capture_format;
337     capture_format.frame_size.SetSize(width, height);
338
339     UINT32 numerator, denominator;
340     hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator);
341     if (FAILED(hr)) {
342       DLOG(ERROR) << "MFGetAttributeSize failed: "
343                   << logging::SystemErrorCodeToString(hr);
344       return;
345     }
346     capture_format.frame_rate = denominator
347         ? static_cast<float>(numerator) / denominator : 0.0f;
348
349     GUID type_guid;
350     hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid);
351     if (FAILED(hr)) {
352       DLOG(ERROR) << "GetGUID failed: "
353                   << logging::SystemErrorCodeToString(hr);
354       return;
355     }
356     VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
357                                             &capture_format.pixel_format);
358     type.Release();
359     formats->push_back(capture_format);
360     ++stream_index;
361
362     DVLOG(1) << device.name() << " " << capture_format.ToString();
363   }
364 }
365
366 // Returns true iff the current platform supports the Media Foundation API
367 // and that the DLLs are available.  On Vista this API is an optional download
368 // but the API is advertised as a part of Windows 7 and onwards.  However,
369 // we've seen that the required DLLs are not available in some Win7
370 // distributions such as Windows 7 N and Windows 7 KN.
371 // static
372 bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() {
373   // Even though the DLLs might be available on Vista, we get crashes
374   // when running our tests on the build bots.
375   if (base::win::GetVersion() < base::win::VERSION_WIN7)
376     return false;
377
378   static bool g_dlls_available = LoadMediaFoundationDlls();
379   return g_dlls_available;
380 }
381
382 VideoCaptureDeviceFactoryWin::VideoCaptureDeviceFactoryWin() {
383   // Use Media Foundation for Metro processes (after and including Win8) and
384   // DirectShow for any other versions, unless forced via flag. Media Foundation
385   // can also be forced if appropriate flag is set and we are in Windows 7 or
386   // 8 in non-Metro mode.
387   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
388   use_media_foundation_ = (base::win::IsMetroProcess() &&
389       !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) ||
390      (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
391       cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture));
392 }
393
394
395 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create(
396     const Name& device_name) {
397   DCHECK(thread_checker_.CalledOnValidThread());
398   scoped_ptr<VideoCaptureDevice> device;
399   if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
400     DCHECK(PlatformSupportsMediaFoundation());
401     device.reset(new VideoCaptureDeviceMFWin(device_name));
402     DVLOG(1) << " MediaFoundation Device: " << device_name.name();
403     ScopedComPtr<IMFMediaSource> source;
404     if (!CreateVideoCaptureDeviceMediaFoundation(device_name.id().c_str(),
405                                                  source.Receive())) {
406       return scoped_ptr<VideoCaptureDevice>();
407     }
408     if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source))
409       device.reset();
410   } else {
411     DCHECK(device_name.capture_api_type() == Name::DIRECT_SHOW ||
412            device_name.capture_api_type() == Name::DIRECT_SHOW_WDM_CROSSBAR);
413     device.reset(new VideoCaptureDeviceWin(device_name));
414     DVLOG(1) << " DirectShow Device: " << device_name.name();
415     if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init())
416       device.reset();
417   }
418   return device.Pass();
419 }
420
421 void VideoCaptureDeviceFactoryWin::GetDeviceNames(Names* device_names) {
422   DCHECK(thread_checker_.CalledOnValidThread());
423   if (use_media_foundation_) {
424     GetDeviceNamesMediaFoundation(device_names);
425   } else {
426     GetDeviceNamesDirectShow(CLSID_VideoInputDeviceCategory,
427                              Name::DIRECT_SHOW,
428                              device_names);
429
430     Names crossbar_device_names;
431     GetDeviceNamesDirectShow(AM_KSCATEGORY_CROSSBAR,
432                              Name::DIRECT_SHOW_WDM_CROSSBAR,
433                              &crossbar_device_names);
434     // Search in the listed |device_names| to find a device with matching USB ID
435     // to each device in |crossbar_device_names|.
436     for (Names::iterator crossbar_device_it = crossbar_device_names.begin();
437          crossbar_device_it != crossbar_device_names.end();
438          ++crossbar_device_it) {
439       const std::string& crossbar_device_model = crossbar_device_it->GetModel();
440       for (Names::const_iterator device_it = device_names->begin();
441            device_it != device_names->end(); ++device_it) {
442         if (crossbar_device_model == device_it->GetModel()) {
443           crossbar_device_it->set_capabilities_id(device_it->id());
444           device_names->push_back(*crossbar_device_it);
445           break;
446         }
447       }
448     }
449   }
450 }
451
452 void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats(
453     const Name& device,
454     VideoCaptureFormats* formats) {
455   DCHECK(thread_checker_.CalledOnValidThread());
456   if (use_media_foundation_)
457     GetDeviceSupportedFormatsMediaFoundation(device, formats);
458   else
459     GetDeviceSupportedFormatsDirectShow(device, formats);
460 }
461
462 }  // namespace media