Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Side by Side Diff: media/capture/video/win/video_capture_device_factory_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698