Home | History | Annotate | Download | only in vda
      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 
     18 namespace media {
     19 
     20 V4L2Device::V4L2Device() {}
     21 
     22 V4L2Device::~V4L2Device() {
     23   CloseDevice();
     24 }
     25 
     26 // static
     27 VideoPixelFormat 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
     56 uint32_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
     77 uint32_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
    101 std::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 
    144 int V4L2Device::Ioctl(int request, void* arg) {
    145   DCHECK(device_fd_.is_valid());
    146   return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg));
    147 }
    148 
    149 bool 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 
    174 void* 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 
    183 void V4L2Device::Munmap(void* addr, unsigned int len) {
    184   munmap(addr, len);
    185 }
    186 
    187 bool 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 
    199 bool 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 
    216 bool 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 
    239 std::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 
    264 VideoDecodeAccelerator::SupportedProfiles
    265 V4L2Device::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 
    287 void 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 
    335 std::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 
    352 VideoDecodeAccelerator::SupportedProfiles
    353 V4L2Device::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 
    385 bool 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 
    396 void V4L2Device::CloseDevice() {
    397   device_fd_.reset();
    398 }
    399 
    400 void 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 
    447 const 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 
    455 std::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