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

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: comments addressed 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 { 46 GenericV4L2Device::GenericV4L2Device() {
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) {
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(Type type, 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 << "0x" << v4l2_pixfmt
154 device_path = kImageProcessorDevice; 147 << " for type: " << static_cast<int>(type);
155 break; 148 return false;
156 case kJpegDecoder:
157 device_path = kJpegDecoderDevice;
158 break;
159 } 149 }
160 150
161 DVLOG(2) << "Initialize(): opening device: " << device_path; 151 if (!OpenDevicePath(path, type)) {
162 // Open the video device. 152 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; 153 return false;
167 } 154 }
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 155
177 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); 156 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
178 if (!device_poll_interrupt_fd_.is_valid()) { 157 if (!device_poll_interrupt_fd_.is_valid()) {
158 LOG(ERROR) << "Failed creating a poll interrupt fd";
179 return false; 159 return false;
180 } 160 }
161
181 return true; 162 return true;
182 } 163 }
183 164
184 std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer( 165 std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer(
185 int index, 166 int index,
186 size_t num_planes, 167 size_t num_planes,
187 enum v4l2_buf_type type) { 168 enum v4l2_buf_type buf_type) {
188 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type)); 169 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type));
189 170
190 std::vector<base::ScopedFD> dmabuf_fds; 171 std::vector<base::ScopedFD> dmabuf_fds;
191 for (size_t i = 0; i < num_planes; ++i) { 172 for (size_t i = 0; i < num_planes; ++i) {
192 struct v4l2_exportbuffer expbuf; 173 struct v4l2_exportbuffer expbuf;
193 memset(&expbuf, 0, sizeof(expbuf)); 174 memset(&expbuf, 0, sizeof(expbuf));
194 expbuf.type = type; 175 expbuf.type = buf_type;
195 expbuf.index = index; 176 expbuf.index = index;
196 expbuf.plane = i; 177 expbuf.plane = i;
197 expbuf.flags = O_CLOEXEC; 178 expbuf.flags = O_CLOEXEC;
198 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { 179 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) {
199 dmabuf_fds.clear(); 180 dmabuf_fds.clear();
200 break; 181 break;
201 } 182 }
202 183
203 dmabuf_fds.push_back(base::ScopedFD(expbuf.fd)); 184 dmabuf_fds.push_back(base::ScopedFD(expbuf.fd));
204 } 185 }
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 if (result != EGL_TRUE) { 281 if (result != EGL_TRUE) {
301 LOG(WARNING) << "Destroy EGLImage failed."; 282 LOG(WARNING) << "Destroy EGLImage failed.";
302 } 283 }
303 return result; 284 return result;
304 } 285 }
305 286
306 GLenum GenericV4L2Device::GetTextureTarget() { 287 GLenum GenericV4L2Device::GetTextureTarget() {
307 return GL_TEXTURE_EXTERNAL_OES; 288 return GL_TEXTURE_EXTERNAL_OES;
308 } 289 }
309 290
310 uint32_t GenericV4L2Device::PreferredInputFormat() { 291 uint32_t GenericV4L2Device::PreferredInputFormat(Type type) {
311 // TODO(posciak): We should support "dontcare" returns here once we 292 if (type == Type::kEncoder)
312 // implement proper handling (fallback, negotiation) for this in users. 293 return V4L2_PIX_FMT_NV12M;
313 CHECK_EQ(type_, kEncoder); 294
314 return V4L2_PIX_FMT_NV12M; 295 return 0;
296 }
297
298 std::vector<uint32_t> GenericV4L2Device::GetSupportedImageProcessorPixelformats(
299 v4l2_buf_type buf_type) {
300 std::vector<uint32_t> supported_pixelformats;
301
302 Type type = Type::kImageProcessor;
303 const auto& devices = GetDevicesForType(type);
304 for (const auto& device : devices) {
305 if (!OpenDevicePath(device.first, type)) {
306 LOG(ERROR) << "Failed opening " << device.first;
307 continue;
308 }
309
310 std::vector<uint32_t> pixelformats =
311 EnumerateSupportedPixelformats(buf_type);
312
313 supported_pixelformats.insert(supported_pixelformats.end(),
314 pixelformats.begin(), pixelformats.end());
315 CloseDevice();
316 }
317
318 return supported_pixelformats;
319 }
320
321 VideoDecodeAccelerator::SupportedProfiles
322 GenericV4L2Device::GetSupportedDecodeProfiles(const size_t num_formats,
323 const uint32_t pixelformats[]) {
324 VideoDecodeAccelerator::SupportedProfiles supported_profiles;
325
326 Type type = Type::kDecoder;
327 const auto& devices = GetDevicesForType(type);
328 for (const auto& device : devices) {
329 if (!OpenDevicePath(device.first, type)) {
330 LOG(ERROR) << "Failed opening " << device.first;
331 continue;
332 }
333
334 const auto& profiles =
335 EnumerateSupportedDecodeProfiles(num_formats, pixelformats);
336 supported_profiles.insert(supported_profiles.end(), profiles.begin(),
337 profiles.end());
338 CloseDevice();
339 }
340
341 return supported_profiles;
342 }
343
344 VideoEncodeAccelerator::SupportedProfiles
345 GenericV4L2Device::GetSupportedEncodeProfiles() {
346 VideoEncodeAccelerator::SupportedProfiles supported_profiles;
347
348 Type type = Type::kEncoder;
349 const auto& devices = GetDevicesForType(type);
350 for (const auto& device : devices) {
351 if (!OpenDevicePath(device.first, type)) {
352 LOG(ERROR) << "Failed opening " << device.first;
353 continue;
354 }
355
356 const auto& profiles = EnumerateSupportedEncodeProfiles();
357 supported_profiles.insert(supported_profiles.end(), profiles.begin(),
358 profiles.end());
359 CloseDevice();
360 }
361
362 return supported_profiles;
363 }
364
365 bool GenericV4L2Device::IsImageProcessingSupported() {
366 const auto& devices = GetDevicesForType(Type::kImageProcessor);
367 return !devices.empty();
368 }
369
370 bool GenericV4L2Device::IsJpegDecodingSupported() {
371 const auto& devices = GetDevicesForType(Type::kJpegDecoder);
372 return !devices.empty();
373 }
374
375 bool GenericV4L2Device::OpenDevicePath(const std::string& path, Type type) {
376 DCHECK(!device_fd_.is_valid());
377
378 device_fd_.reset(
379 HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)));
380 if (!device_fd_.is_valid())
381 return false;
382
383 #if defined(USE_LIBV4L2)
384 if (type == Type::kEncoder &&
385 HANDLE_EINTR(v4l2_fd_open(device_fd_.get(), V4L2_DISABLE_CONVERSION)) !=
386 -1) {
387 DVLOG(2) << "Using libv4l2 for " << path;
388 use_libv4l2_ = true;
389 }
390 #endif
391 return true;
392 }
393
394 void GenericV4L2Device::CloseDevice() {
395 #if defined(USE_LIBV4L2)
396 if (use_libv4l2_ && device_fd_.is_valid())
397 v4l2_close(device_fd_.release());
398 #endif
399 device_fd_.reset();
315 } 400 }
316 401
317 // static 402 // static
318 bool GenericV4L2Device::PostSandboxInitialization() { 403 bool GenericV4L2Device::PostSandboxInitialization() {
319 #if defined(USE_LIBV4L2) 404 #if defined(USE_LIBV4L2)
320 StubPathMap paths; 405 StubPathMap paths;
321 paths[kModuleV4l2].push_back(kV4l2Lib); 406 paths[kModuleV4l2].push_back(kV4l2Lib);
322 407
323 return InitializeStubs(paths); 408 return InitializeStubs(paths);
324 #else 409 #else
325 return true; 410 return true;
326 #endif 411 #endif
327 } 412 }
328 413
414 void GenericV4L2Device::EnumerateDevicesForType(Type type) {
415 static const std::string kDecoderDevicePattern = "/dev/video-dec";
416 static const std::string kEncoderDevicePattern = "/dev/video-enc";
417 static const std::string kImageProcessorDevicePattern = "/dev/image-proc";
418 static const std::string kJpegDecoderDevicePattern = "/dev/jpeg-dec";
419
420 std::string device_pattern;
421 v4l2_buf_type buf_type;
422 switch (type) {
423 case Type::kDecoder:
424 device_pattern = kDecoderDevicePattern;
425 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
426 break;
427 case Type::kEncoder:
428 device_pattern = kEncoderDevicePattern;
429 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
430 break;
431 case Type::kImageProcessor:
432 device_pattern = kImageProcessorDevicePattern;
433 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
434 break;
435 case Type::kJpegDecoder:
436 device_pattern = kJpegDecoderDevicePattern;
437 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
438 break;
439 }
440
441 std::vector<std::string> candidate_paths;
442
443 // TODO(posciak): Remove this legacy unnumbered device once
444 // all platforms are updated to use numbered devices.
445 candidate_paths.push_back(device_pattern);
446
447 // We are sandboxed, so we can't query directory contents to check which
448 // devices are actually available. Try to open the first 10; if not present,
449 // we will just fail to open immediately.
450 for (int i = 0; i < 10; ++i) {
451 candidate_paths.push_back(
452 base::StringPrintf("%s%d", device_pattern.c_str(), i));
453 }
454
455 Devices devices;
456 for (const auto& path : candidate_paths) {
457 if (!OpenDevicePath(path, type))
458 continue;
459
460 const auto& supported_pixelformats =
461 EnumerateSupportedPixelformats(buf_type);
462 if (!supported_pixelformats.empty()) {
463 DVLOG(1) << "Found device: " << path;
464 devices.push_back(std::make_pair(path, supported_pixelformats));
465 }
466
467 CloseDevice();
468 }
469
470 DCHECK_EQ(devices_by_type_.count(type), 0u);
471 devices_by_type_[type] = devices;
472 }
473
474 const GenericV4L2Device::Devices& GenericV4L2Device::GetDevicesForType(
475 Type type) {
476 if (devices_by_type_.count(type) == 0)
477 EnumerateDevicesForType(type);
478
479 DCHECK_NE(devices_by_type_.count(type), 0u);
480 return devices_by_type_[type];
481 }
482
483 std::string GenericV4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) {
484 const Devices& devices = GetDevicesForType(type);
485
486 for (const auto& device : devices) {
487 if (std::find(device.second.begin(), device.second.end(), pixfmt) !=
488 device.second.end())
489 return device.first;
490 }
491
492 return std::string();
493 }
494
329 } // namespace media 495 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698