OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 | 5 |
6 #include "media/gpu/generic_v4l2_device.h" | 6 #include "media/gpu/generic_v4l2_device.h" |
7 | 7 |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <fcntl.h> | 9 #include <fcntl.h> |
10 #include <libdrm/drm_fourcc.h> | 10 #include <libdrm/drm_fourcc.h> |
11 #include <linux/videodev2.h> | 11 #include <linux/videodev2.h> |
12 #include <poll.h> | 12 #include <poll.h> |
13 #include <string.h> | 13 #include <string.h> |
14 #include <sys/eventfd.h> | 14 #include <sys/eventfd.h> |
15 #include <sys/ioctl.h> | 15 #include <sys/ioctl.h> |
16 #include <sys/mman.h> | 16 #include <sys/mman.h> |
17 | 17 |
18 #include <algorithm> | |
18 #include <memory> | 19 #include <memory> |
19 | 20 |
20 #include "base/files/scoped_file.h" | 21 #include "base/files/scoped_file.h" |
21 #include "base/macros.h" | 22 #include "base/macros.h" |
22 #include "base/posix/eintr_wrapper.h" | 23 #include "base/posix/eintr_wrapper.h" |
24 #include "base/strings/stringprintf.h" | |
23 #include "base/trace_event/trace_event.h" | 25 #include "base/trace_event/trace_event.h" |
24 #include "build/build_config.h" | 26 #include "build/build_config.h" |
25 #include "media/gpu/generic_v4l2_device.h" | 27 #include "media/gpu/generic_v4l2_device.h" |
26 #include "ui/gl/egl_util.h" | 28 #include "ui/gl/egl_util.h" |
27 #include "ui/gl/gl_bindings.h" | 29 #include "ui/gl/gl_bindings.h" |
28 | 30 |
29 #if defined(USE_LIBV4L2) | 31 #if defined(USE_LIBV4L2) |
30 // Auto-generated for dlopen libv4l2 libraries | 32 // Auto-generated for dlopen libv4l2 libraries |
31 #include "media/gpu/v4l2_stubs.h" | 33 #include "media/gpu/v4l2_stubs.h" |
32 #include "third_party/v4l-utils/lib/include/libv4l2.h" | 34 #include "third_party/v4l-utils/lib/include/libv4l2.h" |
33 | 35 |
34 using media_gpu::kModuleV4l2; | 36 using media_gpu::kModuleV4l2; |
35 using media_gpu::InitializeStubs; | 37 using media_gpu::InitializeStubs; |
36 using media_gpu::StubPathMap; | 38 using media_gpu::StubPathMap; |
37 | 39 |
38 static const base::FilePath::CharType kV4l2Lib[] = | 40 static const base::FilePath::CharType kV4l2Lib[] = |
39 FILE_PATH_LITERAL("/usr/lib/libv4l2.so"); | 41 FILE_PATH_LITERAL("/usr/lib/libv4l2.so"); |
40 #endif | 42 #endif |
41 | 43 |
42 namespace media { | 44 namespace media { |
43 | 45 |
44 namespace { | |
45 const char kDecoderDevice[] = "/dev/video-dec"; | |
46 const char kEncoderDevice[] = "/dev/video-enc"; | |
47 const char kImageProcessorDevice[] = "/dev/image-proc0"; | |
48 const char kJpegDecoderDevice[] = "/dev/jpeg-dec"; | |
49 } | |
50 | |
51 GenericV4L2Device::GenericV4L2Device(Type type) : V4L2Device(type) { | 46 GenericV4L2Device::GenericV4L2Device(Type type) : V4L2Device(type) { |
52 #if defined(USE_LIBV4L2) | 47 #if defined(USE_LIBV4L2) |
53 use_libv4l2_ = false; | 48 use_libv4l2_ = false; |
54 #endif | 49 #endif |
55 } | 50 } |
56 | 51 |
57 GenericV4L2Device::~GenericV4L2Device() { | 52 GenericV4L2Device::~GenericV4L2Device() { |
58 #if defined(USE_LIBV4L2) | 53 CloseDevice(); |
59 if (use_libv4l2_ && device_fd_.is_valid()) | |
60 v4l2_close(device_fd_.release()); | |
61 #endif | |
62 } | 54 } |
63 | 55 |
64 int GenericV4L2Device::Ioctl(int request, void* arg) { | 56 int GenericV4L2Device::Ioctl(int request, void* arg) { |
57 DCHECK(device_fd_.is_valid()); | |
65 #if defined(USE_LIBV4L2) | 58 #if defined(USE_LIBV4L2) |
66 if (use_libv4l2_) | 59 if (use_libv4l2_) |
67 return HANDLE_EINTR(v4l2_ioctl(device_fd_.get(), request, arg)); | 60 return HANDLE_EINTR(v4l2_ioctl(device_fd_.get(), request, arg)); |
68 #endif | 61 #endif |
69 return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg)); | 62 return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg)); |
70 } | 63 } |
71 | 64 |
72 bool GenericV4L2Device::Poll(bool poll_device, bool* event_pending) { | 65 bool GenericV4L2Device::Poll(bool poll_device, bool* event_pending) { |
73 struct pollfd pollfds[2]; | 66 struct pollfd pollfds[2]; |
74 nfds_t nfds; | 67 nfds_t nfds; |
(...skipping 17 matching lines...) Expand all Loading... | |
92 } | 85 } |
93 *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); | 86 *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); |
94 return true; | 87 return true; |
95 } | 88 } |
96 | 89 |
97 void* GenericV4L2Device::Mmap(void* addr, | 90 void* GenericV4L2Device::Mmap(void* addr, |
98 unsigned int len, | 91 unsigned int len, |
99 int prot, | 92 int prot, |
100 int flags, | 93 int flags, |
101 unsigned int offset) { | 94 unsigned int offset) { |
95 DCHECK(device_fd_.is_valid()); | |
102 return mmap(addr, len, prot, flags, device_fd_.get(), offset); | 96 return mmap(addr, len, prot, flags, device_fd_.get(), offset); |
103 } | 97 } |
104 | 98 |
105 void GenericV4L2Device::Munmap(void* addr, unsigned int len) { | 99 void GenericV4L2Device::Munmap(void* addr, unsigned int len) { |
106 munmap(addr, len); | 100 munmap(addr, len); |
107 } | 101 } |
108 | 102 |
109 bool GenericV4L2Device::SetDevicePollInterrupt() { | 103 bool GenericV4L2Device::SetDevicePollInterrupt() { |
110 DVLOG(3) << "SetDevicePollInterrupt()"; | 104 DVLOG(3) << "SetDevicePollInterrupt()"; |
111 | 105 |
(...skipping 17 matching lines...) Expand all Loading... | |
129 return true; | 123 return true; |
130 } else { | 124 } else { |
131 DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed"; | 125 DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed"; |
132 return false; | 126 return false; |
133 } | 127 } |
134 } | 128 } |
135 return true; | 129 return true; |
136 } | 130 } |
137 | 131 |
138 bool GenericV4L2Device::Initialize() { | 132 bool GenericV4L2Device::Initialize() { |
139 const char* device_path = NULL; | |
140 static bool v4l2_functions_initialized = PostSandboxInitialization(); | 133 static bool v4l2_functions_initialized = PostSandboxInitialization(); |
141 if (!v4l2_functions_initialized) { | 134 if (!v4l2_functions_initialized) { |
142 LOG(ERROR) << "Failed to initialize LIBV4L2 libs"; | 135 LOG(ERROR) << "Failed to initialize LIBV4L2 libs"; |
143 return false; | 136 return false; |
144 } | 137 } |
145 | 138 |
146 switch (type_) { | 139 return true; |
147 case kDecoder: | 140 } |
148 device_path = kDecoderDevice; | 141 |
149 break; | 142 bool GenericV4L2Device::Open(uint32_t v4l2_pixfmt) { |
150 case kEncoder: | 143 std::string path = GetDevicePathFor(type_, v4l2_pixfmt); |
151 device_path = kEncoderDevice; | 144 |
152 break; | 145 if (path.empty()) { |
153 case kImageProcessor: | 146 DVLOG(1) << "No devices supporting " << std::hex << v4l2_pixfmt; |
kcwu
2016/10/06 10:39:59
prepend "0x"
Pawel Osciak
2016/10/07 08:30:23
Done.
| |
154 device_path = kImageProcessorDevice; | 147 return false; |
155 break; | |
156 case kJpegDecoder: | |
157 device_path = kJpegDecoderDevice; | |
158 break; | |
159 } | 148 } |
160 | 149 |
161 DVLOG(2) << "Initialize(): opening device: " << device_path; | 150 if (!OpenDevicePath(path)) { |
162 // Open the video device. | 151 LOG(ERROR) << "Failed opening " << path; |
163 device_fd_.reset( | |
164 HANDLE_EINTR(open(device_path, O_RDWR | O_NONBLOCK | O_CLOEXEC))); | |
165 if (!device_fd_.is_valid()) { | |
166 return false; | 152 return false; |
167 } | 153 } |
168 #if defined(USE_LIBV4L2) | |
169 if (type_ == kEncoder && | |
170 HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) != | |
171 -1) { | |
172 DVLOG(2) << "Using libv4l2 for " << device_path; | |
173 use_libv4l2_ = true; | |
174 } | |
175 #endif | |
176 | 154 |
177 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); | 155 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); |
178 if (!device_poll_interrupt_fd_.is_valid()) { | 156 if (!device_poll_interrupt_fd_.is_valid()) { |
157 LOG(ERROR) << "Failed creating a poll interrupt fd"; | |
179 return false; | 158 return false; |
180 } | 159 } |
160 | |
181 return true; | 161 return true; |
182 } | 162 } |
183 | 163 |
184 std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer( | 164 std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer( |
185 int index, | 165 int index, |
186 size_t num_planes, | 166 size_t num_planes, |
187 enum v4l2_buf_type type) { | 167 enum v4l2_buf_type type) { |
188 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type)); | 168 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type)); |
189 | 169 |
190 std::vector<base::ScopedFD> dmabuf_fds; | 170 std::vector<base::ScopedFD> dmabuf_fds; |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
303 return result; | 283 return result; |
304 } | 284 } |
305 | 285 |
306 GLenum GenericV4L2Device::GetTextureTarget() { | 286 GLenum GenericV4L2Device::GetTextureTarget() { |
307 return GL_TEXTURE_EXTERNAL_OES; | 287 return GL_TEXTURE_EXTERNAL_OES; |
308 } | 288 } |
309 | 289 |
310 uint32_t GenericV4L2Device::PreferredInputFormat() { | 290 uint32_t GenericV4L2Device::PreferredInputFormat() { |
311 // TODO(posciak): We should support "dontcare" returns here once we | 291 // TODO(posciak): We should support "dontcare" returns here once we |
312 // implement proper handling (fallback, negotiation) for this in users. | 292 // implement proper handling (fallback, negotiation) for this in users. |
313 CHECK_EQ(type_, kEncoder); | 293 CHECK_EQ(type_, Type::kEncoder); |
314 return V4L2_PIX_FMT_NV12M; | 294 return V4L2_PIX_FMT_NV12M; |
315 } | 295 } |
316 | 296 |
297 std::vector<uint32_t> GenericV4L2Device::GetSupportedImageProcessorPixelformats( | |
298 v4l2_buf_type buf_type) { | |
299 std::vector<uint32_t> supported_pixelformats; | |
kcwu
2016/10/06 10:39:59
DCHECK_EQ(type_, Type::kImageProcessor);
Pawel Osciak
2016/10/07 08:30:23
Hm, thinking more about it, I think I can reorgani
| |
300 | |
301 const auto& devices = GetDevicesForType(Type::kImageProcessor); | |
302 for (const auto& device : devices) { | |
303 if (!OpenDevicePath(device.first)) { | |
304 LOG(ERROR) << "Failed opening " << device.first; | |
305 continue; | |
306 } | |
307 | |
308 std::vector<uint32_t> pixelformats = | |
309 EnumerateSupportedPixelformats(buf_type); | |
310 | |
311 supported_pixelformats.insert(supported_pixelformats.end(), | |
312 pixelformats.begin(), pixelformats.end()); | |
313 } | |
kcwu
2016/10/06 10:39:59
CloseDevice();
Pawel Osciak
2016/10/07 08:30:23
Done.
| |
314 | |
315 return supported_pixelformats; | |
316 } | |
317 | |
318 VideoDecodeAccelerator::SupportedProfiles | |
319 GenericV4L2Device::GetSupportedDecodeProfiles(const size_t num_formats, | |
320 const uint32_t pixelformats[]) { | |
321 VideoDecodeAccelerator::SupportedProfiles supported_profiles; | |
322 | |
323 const auto& devices = GetDevicesForType(Type::kDecoder); | |
324 for (const auto& device : devices) { | |
325 if (!OpenDevicePath(device.first)) { | |
326 LOG(ERROR) << "Failed opening " << device.first; | |
327 continue; | |
328 } | |
329 | |
330 const auto& profiles = | |
331 EnumerateSupportedDecodeProfiles(num_formats, pixelformats); | |
332 supported_profiles.insert(supported_profiles.end(), profiles.begin(), | |
333 profiles.end()); | |
334 } | |
kcwu
2016/10/06 10:39:59
CloseDevice();
Pawel Osciak
2016/10/07 08:30:23
Done.
| |
335 | |
336 return supported_profiles; | |
337 } | |
338 | |
339 VideoEncodeAccelerator::SupportedProfiles | |
340 GenericV4L2Device::GetSupportedEncodeProfiles() { | |
341 VideoEncodeAccelerator::SupportedProfiles supported_profiles; | |
342 | |
343 const auto& devices = GetDevicesForType(Type::kEncoder); | |
344 for (const auto& device : devices) { | |
345 if (!OpenDevicePath(device.first)) { | |
346 LOG(ERROR) << "Failed opening " << device.first; | |
347 continue; | |
348 } | |
349 | |
350 const auto& profiles = EnumerateSupportedEncodeProfiles(); | |
351 supported_profiles.insert(supported_profiles.end(), profiles.begin(), | |
352 profiles.end()); | |
353 } | |
kcwu
2016/10/06 10:39:59
CloseDevice();
Pawel Osciak
2016/10/07 08:30:23
Done.
| |
354 | |
355 return supported_profiles; | |
356 } | |
357 | |
358 bool GenericV4L2Device::IsJpegDecodingSupported() { | |
359 const auto& devices = GetDevicesForType(Type::kJpegDecoder); | |
360 return !devices.empty(); | |
361 } | |
362 | |
363 bool GenericV4L2Device::OpenDevicePath(const std::string& path) { | |
364 CloseDevice(); | |
kcwu
2016/10/06 10:39:59
I know you auto close before open. But I'm wonderi
Pawel Osciak
2016/10/07 08:30:23
Yes, this is not a good style. Fixing.
| |
365 | |
366 device_fd_.reset( | |
367 HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC))); | |
368 if (!device_fd_.is_valid()) | |
369 return false; | |
370 | |
371 #if defined(USE_LIBV4L2) | |
372 if (type_ == Type::kEncoder && | |
373 HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) != | |
374 -1) { | |
375 DVLOG(2) << "Using libv4l2 for " << path; | |
376 use_libv4l2_ = true; | |
377 } | |
378 #endif | |
379 return true; | |
380 } | |
381 | |
382 void GenericV4L2Device::CloseDevice() { | |
383 #if defined(USE_LIBV4L2) | |
384 if (use_libv4l2_ && device_fd_.is_valid()) | |
385 v4l2_close(device_fd_.release()); | |
386 #endif | |
387 device_fd_.reset(); | |
388 } | |
389 | |
317 // static | 390 // static |
318 bool GenericV4L2Device::PostSandboxInitialization() { | 391 bool GenericV4L2Device::PostSandboxInitialization() { |
319 #if defined(USE_LIBV4L2) | 392 #if defined(USE_LIBV4L2) |
320 StubPathMap paths; | 393 StubPathMap paths; |
321 paths[kModuleV4l2].push_back(kV4l2Lib); | 394 paths[kModuleV4l2].push_back(kV4l2Lib); |
322 | 395 |
323 return InitializeStubs(paths); | 396 return InitializeStubs(paths); |
324 #else | 397 #else |
325 return true; | 398 return true; |
326 #endif | 399 #endif |
327 } | 400 } |
328 | 401 |
402 void GenericV4L2Device::EnumerateDevicesForType(Type type) { | |
403 static const std::string kDecoderDevicePattern = "/dev/video-dec"; | |
404 static const std::string kEncoderDevicePattern = "/dev/video-enc"; | |
405 static const std::string kImageProcessorDevicePattern = "/dev/image-proc"; | |
406 static const std::string kJpegDecoderDevicePattern = "/dev/jpeg-dec"; | |
407 | |
408 std::string device_pattern; | |
409 v4l2_buf_type buf_type; | |
410 switch (type) { | |
411 case Type::kDecoder: | |
412 device_pattern = kDecoderDevicePattern; | |
413 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
414 break; | |
415 case Type::kEncoder: | |
416 device_pattern = kEncoderDevicePattern; | |
417 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
418 break; | |
419 case Type::kImageProcessor: | |
420 device_pattern = kImageProcessorDevicePattern; | |
421 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
422 break; | |
423 case Type::kJpegDecoder: | |
424 device_pattern = kJpegDecoderDevicePattern; | |
425 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
426 break; | |
427 } | |
428 | |
429 std::vector<std::string> candidate_paths; | |
430 | |
431 // TODO(posciak): Remove this legacy unnumbered device once | |
432 // all platforms are updated to use numbered devices. | |
433 candidate_paths.push_back(device_pattern); | |
434 | |
435 for (int i = 0; i < 10; ++i) { | |
kcwu
2016/10/06 10:39:59
Is it possible that number > 9? Add comments if yo
Pawel Osciak
2016/10/07 08:30:23
Unfortunately we can't use an actual FileEnumerato
| |
436 candidate_paths.push_back( | |
437 base::StringPrintf("%s%d", device_pattern.c_str(), i)); | |
438 } | |
439 | |
440 Devices devices; | |
441 for (const auto& path : candidate_paths) { | |
442 if (!OpenDevicePath(path)) | |
443 continue; | |
444 | |
445 const auto& supported_pixelformats = | |
446 EnumerateSupportedPixelformats(buf_type); | |
447 if (supported_pixelformats.empty()) | |
448 continue; | |
kcwu
2016/10/06 10:39:59
CloseDevice?
Pawel Osciak
2016/10/07 08:30:23
Done.
| |
449 | |
450 DVLOG(1) << "Found device: " << path; | |
451 devices.push_back(std::make_pair(path, supported_pixelformats)); | |
452 CloseDevice(); | |
453 } | |
454 | |
455 DCHECK_EQ(devices_by_type_.count(type), 0u); | |
456 devices_by_type_[type] = devices; | |
457 } | |
458 | |
459 const GenericV4L2Device::Devices& GenericV4L2Device::GetDevicesForType( | |
460 Type type) { | |
461 if (devices_by_type_.count(type) == 0) | |
462 EnumerateDevicesForType(type); | |
463 | |
464 DCHECK_NE(devices_by_type_.count(type), 0u); | |
kcwu
2016/10/06 10:39:59
Is this always true?
Pawel Osciak
2016/10/07 08:30:23
Yes. EnumerateDevicesForType() will always insert
| |
465 return devices_by_type_[type]; | |
466 } | |
467 | |
468 std::string GenericV4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) { | |
469 const Devices& devices = GetDevicesForType(type); | |
470 | |
471 for (const auto& device : devices) { | |
472 if (std::find(device.second.begin(), device.second.end(), pixfmt) != | |
473 device.second.end()) | |
474 return device.first; | |
475 } | |
476 | |
477 return std::string(); | |
478 } | |
479 | |
329 } // namespace media | 480 } // namespace media |
OLD | NEW |