• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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