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 // Note: ported from Chromium commit head: 8c9190713ed9
5
6 #include "generic_v4l2_device.h"
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <linux/videodev2.h>
11 #include <poll.h>
12 #include <string.h>
13 #include <sys/eventfd.h>
14 #include <sys/ioctl.h>
15 #include <sys/mman.h>
16
17 #include <algorithm>
18 #include <memory>
19
20 #include "base/files/scoped_file.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/stl_util.h"
23 #include "base/strings/stringprintf.h"
24
25 #include "macros.h"
26
27 namespace media {
28
GenericV4L2Device()29 GenericV4L2Device::GenericV4L2Device() {}
30
~GenericV4L2Device()31 GenericV4L2Device::~GenericV4L2Device() {
32 CloseDevice();
33 }
34
Ioctl(int request,void * arg)35 int GenericV4L2Device::Ioctl(int request, void* arg) {
36 DCHECK(device_fd_.is_valid());
37 return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg));
38 }
39
Poll(bool poll_device,bool * event_pending)40 bool GenericV4L2Device::Poll(bool poll_device, bool* event_pending) {
41 struct pollfd pollfds[2];
42 nfds_t nfds;
43 int pollfd = -1;
44
45 pollfds[0].fd = device_poll_interrupt_fd_.get();
46 pollfds[0].events = POLLIN | POLLERR;
47 nfds = 1;
48
49 if (poll_device) {
50 DVLOGF(5) << "adding device fd to poll() set";
51 pollfds[nfds].fd = device_fd_.get();
52 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI;
53 pollfd = nfds;
54 nfds++;
55 }
56
57 if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) {
58 VPLOGF(1) << "poll() failed";
59 return false;
60 }
61 *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI);
62 return true;
63 }
64
Mmap(void * addr,unsigned int len,int prot,int flags,unsigned int offset)65 void* GenericV4L2Device::Mmap(void* addr,
66 unsigned int len,
67 int prot,
68 int flags,
69 unsigned int offset) {
70 DCHECK(device_fd_.is_valid());
71 return mmap(addr, len, prot, flags, device_fd_.get(), offset);
72 }
73
Munmap(void * addr,unsigned int len)74 void GenericV4L2Device::Munmap(void* addr, unsigned int len) {
75 munmap(addr, len);
76 }
77
SetDevicePollInterrupt()78 bool GenericV4L2Device::SetDevicePollInterrupt() {
79 DVLOGF(4);
80
81 const uint64_t buf = 1;
82 if (HANDLE_EINTR(write(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) ==
83 -1) {
84 VPLOGF(1) << "write() failed";
85 return false;
86 }
87 return true;
88 }
89
ClearDevicePollInterrupt()90 bool GenericV4L2Device::ClearDevicePollInterrupt() {
91 DVLOGF(5);
92
93 uint64_t buf;
94 if (HANDLE_EINTR(read(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) ==
95 -1) {
96 if (errno == EAGAIN) {
97 // No interrupt flag set, and we're reading nonblocking. Not an error.
98 return true;
99 } else {
100 VPLOGF(1) << "read() failed";
101 return false;
102 }
103 }
104 return true;
105 }
106
Initialize()107 bool GenericV4L2Device::Initialize() {
108 DVLOGF(3);
109 static bool v4l2_functions_initialized = PostSandboxInitialization();
110 if (!v4l2_functions_initialized) {
111 VLOGF(1) << "Failed to initialize LIBV4L2 libs";
112 return false;
113 }
114
115 return true;
116 }
117
Open(Type type,uint32_t v4l2_pixfmt)118 bool GenericV4L2Device::Open(Type type, uint32_t v4l2_pixfmt) {
119 DVLOGF(3);
120 std::string path = GetDevicePathFor(type, v4l2_pixfmt);
121
122 if (path.empty()) {
123 VLOGF(1) << "No devices supporting " << FourccToString(v4l2_pixfmt)
124 << " for type: " << static_cast<int>(type);
125 return false;
126 }
127
128 if (!OpenDevicePath(path, type)) {
129 VLOGF(1) << "Failed opening " << path;
130 return false;
131 }
132
133 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
134 if (!device_poll_interrupt_fd_.is_valid()) {
135 VLOGF(1) << "Failed creating a poll interrupt fd";
136 return false;
137 }
138
139 return true;
140 }
141
GetDmabufsForV4L2Buffer(int index,size_t num_planes,enum v4l2_buf_type buf_type)142 std::vector<base::ScopedFD> GenericV4L2Device::GetDmabufsForV4L2Buffer(
143 int index,
144 size_t num_planes,
145 enum v4l2_buf_type buf_type) {
146 DVLOGF(3);
147 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type));
148
149 std::vector<base::ScopedFD> dmabuf_fds;
150 for (size_t i = 0; i < num_planes; ++i) {
151 struct v4l2_exportbuffer expbuf;
152 memset(&expbuf, 0, sizeof(expbuf));
153 expbuf.type = buf_type;
154 expbuf.index = index;
155 expbuf.plane = i;
156 expbuf.flags = O_CLOEXEC;
157 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) {
158 dmabuf_fds.clear();
159 break;
160 }
161
162 dmabuf_fds.push_back(base::ScopedFD(expbuf.fd));
163 }
164
165 return dmabuf_fds;
166 }
167
PreferredInputFormat(Type type)168 std::vector<uint32_t> GenericV4L2Device::PreferredInputFormat(Type type) {
169 if (type == Type::kEncoder)
170 return {V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_NV12};
171
172 return {};
173 }
174
GetSupportedImageProcessorPixelformats(v4l2_buf_type buf_type)175 std::vector<uint32_t> GenericV4L2Device::GetSupportedImageProcessorPixelformats(
176 v4l2_buf_type buf_type) {
177 std::vector<uint32_t> supported_pixelformats;
178
179 Type type = Type::kImageProcessor;
180 const auto& devices = GetDevicesForType(type);
181 for (const auto& device : devices) {
182 if (!OpenDevicePath(device.first, type)) {
183 VLOGF(1) << "Failed opening " << device.first;
184 continue;
185 }
186
187 std::vector<uint32_t> pixelformats =
188 EnumerateSupportedPixelformats(buf_type);
189
190 supported_pixelformats.insert(supported_pixelformats.end(),
191 pixelformats.begin(), pixelformats.end());
192 CloseDevice();
193 }
194
195 return supported_pixelformats;
196 }
197
198 VideoDecodeAccelerator::SupportedProfiles
GetSupportedDecodeProfiles(const size_t num_formats,const uint32_t pixelformats[])199 GenericV4L2Device::GetSupportedDecodeProfiles(const size_t num_formats,
200 const uint32_t pixelformats[]) {
201 VideoDecodeAccelerator::SupportedProfiles supported_profiles;
202
203 Type type = Type::kDecoder;
204 const auto& devices = GetDevicesForType(type);
205 for (const auto& device : devices) {
206 if (!OpenDevicePath(device.first, type)) {
207 VLOGF(1) << "Failed opening " << device.first;
208 continue;
209 }
210
211 const auto& profiles =
212 EnumerateSupportedDecodeProfiles(num_formats, pixelformats);
213 supported_profiles.insert(supported_profiles.end(), profiles.begin(),
214 profiles.end());
215 CloseDevice();
216 }
217
218 return supported_profiles;
219 }
220
221 VideoEncodeAccelerator::SupportedProfiles
GetSupportedEncodeProfiles()222 GenericV4L2Device::GetSupportedEncodeProfiles() {
223 VideoEncodeAccelerator::SupportedProfiles supported_profiles;
224
225 Type type = Type::kEncoder;
226 const auto& devices = GetDevicesForType(type);
227 for (const auto& device : devices) {
228 if (!OpenDevicePath(device.first, type)) {
229 VLOGF(1) << "Failed opening " << device.first;
230 continue;
231 }
232
233 const auto& profiles = EnumerateSupportedEncodeProfiles();
234 supported_profiles.insert(supported_profiles.end(), profiles.begin(),
235 profiles.end());
236 CloseDevice();
237 }
238
239 return supported_profiles;
240 }
241
IsImageProcessingSupported()242 bool GenericV4L2Device::IsImageProcessingSupported() {
243 const auto& devices = GetDevicesForType(Type::kImageProcessor);
244 return !devices.empty();
245 }
246
IsJpegDecodingSupported()247 bool GenericV4L2Device::IsJpegDecodingSupported() {
248 const auto& devices = GetDevicesForType(Type::kJpegDecoder);
249 return !devices.empty();
250 }
251
IsJpegEncodingSupported()252 bool GenericV4L2Device::IsJpegEncodingSupported() {
253 const auto& devices = GetDevicesForType(Type::kJpegEncoder);
254 return !devices.empty();
255 }
256
OpenDevicePath(const std::string & path,Type type)257 bool GenericV4L2Device::OpenDevicePath(const std::string& path, Type type) {
258 DCHECK(!device_fd_.is_valid());
259
260 device_fd_.reset(
261 HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)));
262 if (!device_fd_.is_valid())
263 return false;
264
265 return true;
266 }
267
CloseDevice()268 void GenericV4L2Device::CloseDevice() {
269 DVLOGF(3);
270 device_fd_.reset();
271 }
272
273 // static
PostSandboxInitialization()274 bool GenericV4L2Device::PostSandboxInitialization() {
275 return true;
276 }
277
EnumerateDevicesForType(Type type)278 void GenericV4L2Device::EnumerateDevicesForType(Type type) {
279 // video input/output devices are registered as /dev/videoX in V4L2.
280 static const std::string kVideoDevicePattern = "/dev/video";
281
282 std::string device_pattern;
283 v4l2_buf_type buf_type;
284 switch (type) {
285 case Type::kDecoder:
286 device_pattern = kVideoDevicePattern;
287 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
288 break;
289 case Type::kEncoder:
290 device_pattern = kVideoDevicePattern;
291 buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
292 break;
293 default:
294 LOG(ERROR) << "Only decoder and encoder types are supported!!";
295 return;
296 }
297
298 std::vector<std::string> candidate_paths;
299
300 // TODO(posciak): Remove this legacy unnumbered device once
301 // all platforms are updated to use numbered devices.
302 candidate_paths.push_back(device_pattern);
303
304 // We are sandboxed, so we can't query directory contents to check which
305 // devices are actually available. Try to open the first 10; if not present,
306 // we will just fail to open immediately.
307 for (int i = 0; i < 10; ++i) {
308 candidate_paths.push_back(
309 base::StringPrintf("%s%d", device_pattern.c_str(), i));
310 }
311
312 Devices devices;
313 for (const auto& path : candidate_paths) {
314 if (!OpenDevicePath(path, type))
315 continue;
316
317 const auto& supported_pixelformats =
318 EnumerateSupportedPixelformats(buf_type);
319 if (!supported_pixelformats.empty()) {
320 DVLOGF(3) << "Found device: " << path;
321 devices.push_back(std::make_pair(path, supported_pixelformats));
322 }
323
324 CloseDevice();
325 }
326
327 DCHECK_EQ(devices_by_type_.count(type), 0u);
328 devices_by_type_[type] = devices;
329 }
330
GetDevicesForType(Type type)331 const GenericV4L2Device::Devices& GenericV4L2Device::GetDevicesForType(
332 Type type) {
333 if (devices_by_type_.count(type) == 0)
334 EnumerateDevicesForType(type);
335
336 DCHECK_NE(devices_by_type_.count(type), 0u);
337 return devices_by_type_[type];
338 }
339
GetDevicePathFor(Type type,uint32_t pixfmt)340 std::string GenericV4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) {
341 const Devices& devices = GetDevicesForType(type);
342
343 for (const auto& device : devices) {
344 if (std::find(device.second.begin(), device.second.end(), pixfmt) !=
345 device.second.end())
346 return device.first;
347 }
348
349 return std::string();
350 }
351
352 } // namespace media
353