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 { | 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 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(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 Loading... |
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 |
OLD | NEW |