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

Side by Side Diff: media/gpu/generic_v4l2_device.cc

Issue 2398883002: Add support for multiple V4L2 video devices of the same type. (Closed)
Patch Set: Fixes for image processor. Created 4 years, 2 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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698