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
5#include <errno.h>
6#include <fcntl.h>
7#include <poll.h>
8#include <string.h>
9#include <sys/eventfd.h>
10#include <sys/ioctl.h>
11#include <sys/mman.h>
12
13#include "base/numerics/safe_conversions.h"
14#include "base/posix/eintr_wrapper.h"
15#include "base/strings/stringprintf.h"
16#include "v4l2_device.h"
17
18namespace media {
19
20V4L2Device::V4L2Device() {}
21
22V4L2Device::~V4L2Device() {
23  CloseDevice();
24}
25
26// static
27VideoPixelFormat V4L2Device::V4L2PixFmtToVideoPixelFormat(uint32_t pix_fmt) {
28  switch (pix_fmt) {
29    case V4L2_PIX_FMT_NV12:
30    case V4L2_PIX_FMT_NV12M:
31      return PIXEL_FORMAT_NV12;
32
33    case V4L2_PIX_FMT_MT21:
34      return PIXEL_FORMAT_MT21;
35
36    case V4L2_PIX_FMT_YUV420:
37    case V4L2_PIX_FMT_YUV420M:
38      return PIXEL_FORMAT_I420;
39
40    case V4L2_PIX_FMT_YVU420:
41      return PIXEL_FORMAT_YV12;
42
43    case V4L2_PIX_FMT_YUV422M:
44      return PIXEL_FORMAT_I422;
45
46    case V4L2_PIX_FMT_RGB32:
47      return PIXEL_FORMAT_ARGB;
48
49    default:
50      DVLOG(1) << "Add more cases as needed";
51      return PIXEL_FORMAT_UNKNOWN;
52  }
53}
54
55// static
56uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format) {
57  switch (format) {
58    case PIXEL_FORMAT_NV12:
59      return V4L2_PIX_FMT_NV12M;
60
61    case PIXEL_FORMAT_MT21:
62      return V4L2_PIX_FMT_MT21;
63
64    case PIXEL_FORMAT_I420:
65      return V4L2_PIX_FMT_YUV420M;
66
67    case PIXEL_FORMAT_YV12:
68      return V4L2_PIX_FMT_YVU420;
69
70    default:
71      LOG(FATAL) << "Add more cases as needed";
72      return 0;
73  }
74}
75
76// static
77uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile,
78                                                   bool slice_based) {
79  if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) {
80    if (slice_based)
81      return V4L2_PIX_FMT_H264_SLICE;
82    else
83      return V4L2_PIX_FMT_H264;
84  } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) {
85    if (slice_based)
86      return V4L2_PIX_FMT_VP8_FRAME;
87    else
88      return V4L2_PIX_FMT_VP8;
89  } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) {
90    if (slice_based)
91      return V4L2_PIX_FMT_VP9_FRAME;
92    else
93      return V4L2_PIX_FMT_VP9;
94  } else {
95    LOG(FATAL) << "Add more cases as needed";
96    return 0;
97  }
98}
99
100// static
101std::vector<VideoCodecProfile> V4L2Device::V4L2PixFmtToVideoCodecProfiles(
102    uint32_t pix_fmt,
103    bool is_encoder) {
104  VideoCodecProfile min_profile, max_profile;
105  std::vector<VideoCodecProfile> profiles;
106
107  switch (pix_fmt) {
108    case V4L2_PIX_FMT_H264:
109    case V4L2_PIX_FMT_H264_SLICE:
110      if (is_encoder) {
111        // TODO(posciak): need to query the device for supported H.264 profiles,
112        // for now choose Main as a sensible default.
113        min_profile = H264PROFILE_MAIN;
114        max_profile = H264PROFILE_MAIN;
115      } else {
116        min_profile = H264PROFILE_MIN;
117        max_profile = H264PROFILE_MAX;
118      }
119      break;
120
121    case V4L2_PIX_FMT_VP8:
122    case V4L2_PIX_FMT_VP8_FRAME:
123      min_profile = VP8PROFILE_MIN;
124      max_profile = VP8PROFILE_MAX;
125      break;
126
127    case V4L2_PIX_FMT_VP9:
128    case V4L2_PIX_FMT_VP9_FRAME:
129      min_profile = VP9PROFILE_MIN;
130      max_profile = VP9PROFILE_MAX;
131      break;
132
133    default:
134      DVLOG(1) << "Unhandled pixelformat " << std::hex << "0x" << pix_fmt;
135      return profiles;
136  }
137
138  for (int profile = min_profile; profile <= max_profile; ++profile)
139    profiles.push_back(static_cast<VideoCodecProfile>(profile));
140
141  return profiles;
142}
143
144int V4L2Device::Ioctl(int request, void* arg) {
145  DCHECK(device_fd_.is_valid());
146  return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg));
147}
148
149bool V4L2Device::Poll(bool poll_device, bool* event_pending) {
150  struct pollfd pollfds[2];
151  nfds_t nfds;
152  int pollfd = -1;
153
154  pollfds[0].fd = device_poll_interrupt_fd_.get();
155  pollfds[0].events = POLLIN | POLLERR;
156  nfds = 1;
157
158  if (poll_device) {
159    DVLOG(3) << "Poll(): adding device fd to poll() set";
160    pollfds[nfds].fd = device_fd_.get();
161    pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI;
162    pollfd = nfds;
163    nfds++;
164  }
165
166  if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) {
167    DPLOG(ERROR) << "poll() failed";
168    return false;
169  }
170  *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI);
171  return true;
172}
173
174void* V4L2Device::Mmap(void* addr,
175                       unsigned int len,
176                       int prot,
177                       int flags,
178                       unsigned int offset) {
179  DCHECK(device_fd_.is_valid());
180  return mmap(addr, len, prot, flags, device_fd_.get(), offset);
181}
182
183void V4L2Device::Munmap(void* addr, unsigned int len) {
184  munmap(addr, len);
185}
186
187bool V4L2Device::SetDevicePollInterrupt() {
188  DVLOG(3) << "SetDevicePollInterrupt()";
189
190  const uint64_t buf = 1;
191  if (HANDLE_EINTR(write(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) ==
192      -1) {
193    DPLOG(ERROR) << "SetDevicePollInterrupt(): write() failed";
194    return false;
195  }
196  return true;
197}
198
199bool V4L2Device::ClearDevicePollInterrupt() {
200  DVLOG(3) << "ClearDevicePollInterrupt()";
201
202  uint64_t buf;
203  if (HANDLE_EINTR(read(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) ==
204      -1) {
205    if (errno == EAGAIN) {
206      // No interrupt flag set, and we're reading nonblocking.  Not an error.
207      return true;
208    } else {
209      DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed";
210      return false;
211    }
212  }
213  return true;
214}
215
216bool V4L2Device::Open(Type type, uint32_t v4l2_pixfmt) {
217  std::string path = GetDevicePathFor(type, v4l2_pixfmt);
218
219  if (path.empty()) {
220    DVLOG(1) << "No devices supporting " << std::hex << "0x" << v4l2_pixfmt
221             << " for type: " << static_cast<int>(type);
222    return false;
223  }
224
225  if (!OpenDevicePath(path, type)) {
226    LOG(ERROR) << "Failed opening " << path;
227    return false;
228  }
229
230  device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
231  if (!device_poll_interrupt_fd_.is_valid()) {
232    LOG(ERROR) << "Failed creating a poll interrupt fd";
233    return false;
234  }
235
236  return true;
237}
238
239std::vector<base::ScopedFD> V4L2Device::GetDmabufsForV4L2Buffer(
240    int index,
241    size_t num_planes,
242    enum v4l2_buf_type buf_type) {
243  DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type));
244
245  std::vector<base::ScopedFD> dmabuf_fds;
246  for (size_t i = 0; i < num_planes; ++i) {
247    struct v4l2_exportbuffer expbuf;
248    memset(&expbuf, 0, sizeof(expbuf));
249    expbuf.type = buf_type;
250    expbuf.index = index;
251    expbuf.plane = i;
252    expbuf.flags = O_CLOEXEC;
253    if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) {
254      dmabuf_fds.clear();
255      break;
256    }
257
258    dmabuf_fds.push_back(base::ScopedFD(expbuf.fd));
259  }
260
261  return dmabuf_fds;
262}
263
264VideoDecodeAccelerator::SupportedProfiles
265V4L2Device::GetSupportedDecodeProfiles(const size_t num_formats,
266                                       const uint32_t pixelformats[]) {
267  VideoDecodeAccelerator::SupportedProfiles supported_profiles;
268
269  Type type = Type::kDecoder;
270  const auto& devices = GetDevicesForType(type);
271  for (const auto& device : devices) {
272    if (!OpenDevicePath(device.first, type)) {
273      LOG(ERROR) << "Failed opening " << device.first;
274      continue;
275    }
276
277    const auto& profiles =
278        EnumerateSupportedDecodeProfiles(num_formats, pixelformats);
279    supported_profiles.insert(supported_profiles.end(), profiles.begin(),
280                              profiles.end());
281    CloseDevice();
282  }
283
284  return supported_profiles;
285}
286
287void V4L2Device::GetSupportedResolution(uint32_t pixelformat,
288                                        Size* min_resolution,
289                                        Size* max_resolution) {
290  max_resolution->SetSize(0, 0);
291  min_resolution->SetSize(0, 0);
292  v4l2_frmsizeenum frame_size;
293  memset(&frame_size, 0, sizeof(frame_size));
294  frame_size.pixel_format = pixelformat;
295  for (; Ioctl(VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0; ++frame_size.index) {
296    if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
297      if (frame_size.discrete.width >=
298              base::checked_cast<uint32_t>(max_resolution->width()) &&
299          frame_size.discrete.height >=
300              base::checked_cast<uint32_t>(max_resolution->height())) {
301        max_resolution->SetSize(frame_size.discrete.width,
302                                frame_size.discrete.height);
303      }
304      if (min_resolution->IsEmpty() ||
305          (frame_size.discrete.width <=
306               base::checked_cast<uint32_t>(min_resolution->width()) &&
307           frame_size.discrete.height <=
308               base::checked_cast<uint32_t>(min_resolution->height()))) {
309        min_resolution->SetSize(frame_size.discrete.width,
310                                frame_size.discrete.height);
311      }
312    } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
313               frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
314      max_resolution->SetSize(frame_size.stepwise.max_width,
315                              frame_size.stepwise.max_height);
316      min_resolution->SetSize(frame_size.stepwise.min_width,
317                              frame_size.stepwise.min_height);
318      break;
319    }
320  }
321  if (max_resolution->IsEmpty()) {
322    max_resolution->SetSize(1920, 1088);
323    LOG(ERROR) << "GetSupportedResolution failed to get maximum resolution for "
324               << "fourcc " << std::hex << pixelformat
325               << ", fall back to " << max_resolution->ToString();
326  }
327  if (min_resolution->IsEmpty()) {
328    min_resolution->SetSize(16, 16);
329    LOG(ERROR) << "GetSupportedResolution failed to get minimum resolution for "
330               << "fourcc " << std::hex << pixelformat
331               << ", fall back to " << min_resolution->ToString();
332  }
333}
334
335std::vector<uint32_t> V4L2Device::EnumerateSupportedPixelformats(
336    v4l2_buf_type buf_type) {
337  std::vector<uint32_t> pixelformats;
338
339  v4l2_fmtdesc fmtdesc;
340  memset(&fmtdesc, 0, sizeof(fmtdesc));
341  fmtdesc.type = buf_type;
342
343  for (; Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) {
344    DVLOG(1) << "Found " << fmtdesc.description << std::hex << " (0x"
345             << fmtdesc.pixelformat << ")";
346    pixelformats.push_back(fmtdesc.pixelformat);
347  }
348
349  return pixelformats;
350}
351
352VideoDecodeAccelerator::SupportedProfiles
353V4L2Device::EnumerateSupportedDecodeProfiles(const size_t num_formats,
354                                             const uint32_t pixelformats[]) {
355  VideoDecodeAccelerator::SupportedProfiles profiles;
356
357  const auto& supported_pixelformats =
358      EnumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
359
360  for (uint32_t pixelformat : supported_pixelformats) {
361    if (std::find(pixelformats, pixelformats + num_formats, pixelformat) ==
362        pixelformats + num_formats)
363      continue;
364
365    VideoDecodeAccelerator::SupportedProfile profile;
366    GetSupportedResolution(pixelformat, &profile.min_resolution,
367                           &profile.max_resolution);
368
369    const auto video_codec_profiles =
370        V4L2PixFmtToVideoCodecProfiles(pixelformat, false);
371
372    for (const auto& video_codec_profile : video_codec_profiles) {
373      profile.profile = video_codec_profile;
374      profiles.push_back(profile);
375
376      DVLOG(1) << "Found decoder profile " << GetProfileName(profile.profile)
377               << ", resolutions: " << profile.min_resolution.ToString() << " "
378               << profile.max_resolution.ToString();
379    }
380  }
381
382  return profiles;
383}
384
385bool V4L2Device::OpenDevicePath(const std::string& path, Type type) {
386  DCHECK(!device_fd_.is_valid());
387
388  device_fd_.reset(
389      HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)));
390  if (!device_fd_.is_valid())
391    return false;
392
393  return true;
394}
395
396void V4L2Device::CloseDevice() {
397  device_fd_.reset();
398}
399
400void V4L2Device::EnumerateDevicesForType(Type type) {
401  static const std::string kDecoderDevicePattern = "/dev/video-dec";
402  std::string device_pattern;
403  v4l2_buf_type buf_type;
404  switch (type) {
405    case Type::kDecoder:
406      device_pattern = kDecoderDevicePattern;
407      buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
408      break;
409    default:
410      LOG(ERROR) << "Only decoder type is supported!!";
411      return;
412  }
413
414  std::vector<std::string> candidate_paths;
415
416  // TODO(posciak): Remove this legacy unnumbered device once
417  // all platforms are updated to use numbered devices.
418  candidate_paths.push_back(device_pattern);
419
420  // We are sandboxed, so we can't query directory contents to check which
421  // devices are actually available. Try to open the first 10; if not present,
422  // we will just fail to open immediately.
423  for (int i = 0; i < 10; ++i) {
424    candidate_paths.push_back(
425        base::StringPrintf("%s%d", device_pattern.c_str(), i));
426  }
427
428  Devices devices;
429  for (const auto& path : candidate_paths) {
430    if (!OpenDevicePath(path, type))
431      continue;
432
433    const auto& supported_pixelformats =
434        EnumerateSupportedPixelformats(buf_type);
435    if (!supported_pixelformats.empty()) {
436      DVLOG(1) << "Found device: " << path;
437      devices.push_back(std::make_pair(path, supported_pixelformats));
438    }
439
440    CloseDevice();
441  }
442
443  DCHECK_EQ(devices_by_type_.count(type), 0u);
444  devices_by_type_[type] = devices;
445}
446
447const V4L2Device::Devices& V4L2Device::GetDevicesForType(Type type) {
448  if (devices_by_type_.count(type) == 0)
449    EnumerateDevicesForType(type);
450
451  DCHECK_NE(devices_by_type_.count(type), 0u);
452  return devices_by_type_[type];
453}
454
455std::string V4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) {
456  const Devices& devices = GetDevicesForType(type);
457
458  for (const auto& device : devices) {
459    if (std::find(device.second.begin(), device.second.end(), pixfmt) !=
460        device.second.end())
461      return device.first;
462  }
463
464  return std::string();
465}
466
467}  //  namespace media
468