tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

device_info_v4l2.cc (15759B)


      1 /*
      2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/video_capture/linux/device_info_v4l2.h"
     12 
     13 #include <fcntl.h>
     14 #if defined(__NetBSD__) || defined(__OpenBSD__) // WEBRTC_BSD
     15 #include <sys/videoio.h>
     16 #elif defined(__sun)
     17 #include <sys/videodev2.h>
     18 #else
     19 #include <linux/videodev2.h>
     20 #endif
     21 #include <poll.h>
     22 #include <sys/ioctl.h>
     23 #include <unistd.h>
     24 
     25 #include <cerrno>
     26 #include <cstdint>
     27 #include <cstdio>
     28 #include <cstdlib>
     29 #include <cstring>
     30 
     31 #include "common_video/libyuv/include/webrtc_libyuv.h"
     32 #include "modules/video_capture/device_info_impl.h"
     33 #include "modules/video_capture/video_capture_defines.h"
     34 #include "rtc_base/checks.h"
     35 #include "rtc_base/logging.h"
     36 
     37 // These defines are here to support building on kernel 3.16 which some
     38 // downstream projects, e.g. Firefox, use.
     39 // TODO(apehrson): Remove them and their undefs when no longer needed.
     40 #ifndef V4L2_PIX_FMT_ABGR32
     41 #define ABGR32_OVERRIDE 1
     42 #define V4L2_PIX_FMT_ABGR32 v4l2_fourcc('A', 'R', '2', '4')
     43 #endif
     44 
     45 #ifndef V4L2_PIX_FMT_ARGB32
     46 #define ARGB32_OVERRIDE 1
     47 #define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4')
     48 #endif
     49 
     50 #ifndef V4L2_PIX_FMT_RGBA32
     51 #define RGBA32_OVERRIDE 1
     52 #define V4L2_PIX_FMT_RGBA32 v4l2_fourcc('A', 'B', '2', '4')
     53 #endif
     54 
     55 #ifdef WEBRTC_LINUX
     56 #define EVENT_SIZE  ( sizeof (struct inotify_event) )
     57 #define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
     58 #endif
     59 
     60 namespace webrtc {
     61 namespace videocapturemodule {
     62 #ifdef WEBRTC_LINUX
     63 void DeviceInfoV4l2::HandleEvent(inotify_event* event, int fd)
     64 {
     65    if (event->mask & IN_CREATE) {
     66        if (fd == _fd_v4l) {
     67            DeviceChange();
     68        } else if ((event->mask & IN_ISDIR) && (fd == _fd_dev)) {
     69            if (_wd_v4l < 0) {
     70                // Sometimes inotify_add_watch failed if we call it immediately after receiving this event
     71                // Adding 5ms delay to let file system settle down
     72                usleep(5*1000);
     73                _wd_v4l = inotify_add_watch(_fd_v4l, "/dev/v4l/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
     74                if (_wd_v4l >= 0) {
     75                    DeviceChange();
     76                }
     77            }
     78        }
     79    } else if (event->mask & IN_DELETE) {
     80        if (fd == _fd_v4l) {
     81            DeviceChange();
     82        }
     83    } else if (event->mask & IN_DELETE_SELF) {
     84        if (fd == _fd_v4l) {
     85            inotify_rm_watch(_fd_v4l, _wd_v4l);
     86            _wd_v4l = -1;
     87        } else {
     88            assert(false);
     89        }
     90    }
     91 }
     92 
     93 int DeviceInfoV4l2::EventCheck(int fd)
     94 {
     95    struct pollfd fds = {
     96      .fd = fd,
     97      .events = POLLIN,
     98      .revents = 0
     99    };
    100 
    101    return poll(&fds, 1, 100);
    102 }
    103 
    104 int DeviceInfoV4l2::HandleEvents(int fd)
    105 {
    106    char buffer[BUF_LEN];
    107 
    108    ssize_t r = read(fd, buffer, BUF_LEN);
    109 
    110    if (r <= 0) {
    111        return r;
    112    }
    113 
    114    ssize_t buffer_i = 0;
    115    inotify_event* pevent;
    116    size_t eventSize;
    117    int count = 0;
    118 
    119    while (buffer_i < r)
    120    {
    121        pevent = (inotify_event *) (&buffer[buffer_i]);
    122        eventSize = sizeof(inotify_event) + pevent->len;
    123        char event[sizeof(inotify_event) + FILENAME_MAX + 1] // null-terminated
    124            __attribute__ ((aligned(__alignof__(struct inotify_event))));
    125 
    126        memcpy(event, pevent, eventSize);
    127 
    128        HandleEvent((inotify_event*)(event), fd);
    129 
    130        buffer_i += eventSize;
    131        count++;
    132    }
    133 
    134    return count;
    135 }
    136 
    137 int DeviceInfoV4l2::ProcessInotifyEvents()
    138 {
    139    while (!_isShutdown) {
    140        if (EventCheck(_fd_dev) > 0) {
    141            if (HandleEvents(_fd_dev) < 0) {
    142                break;
    143            }
    144        }
    145        if (EventCheck(_fd_v4l) > 0) {
    146            if (HandleEvents(_fd_v4l) < 0) {
    147                break;
    148            }
    149        }
    150    }
    151    return 0;
    152 }
    153 
    154 void DeviceInfoV4l2::InotifyProcess()
    155 {
    156    _fd_v4l = inotify_init();
    157    _fd_dev = inotify_init();
    158    if (_fd_v4l >= 0 && _fd_dev >= 0) {
    159        _wd_v4l = inotify_add_watch(_fd_v4l, "/dev/v4l/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
    160        _wd_dev = inotify_add_watch(_fd_dev, "/dev/", IN_CREATE);
    161        ProcessInotifyEvents();
    162 
    163        if (_wd_v4l >= 0) {
    164          inotify_rm_watch(_fd_v4l, _wd_v4l);
    165        }
    166 
    167        if (_wd_dev >= 0) {
    168          inotify_rm_watch(_fd_dev, _wd_dev);
    169        }
    170 
    171        close(_fd_v4l);
    172        close(_fd_dev);
    173    }
    174 }
    175 #endif
    176 
    177 DeviceInfoV4l2::DeviceInfoV4l2() : DeviceInfoImpl()
    178 #ifdef WEBRTC_LINUX
    179    , _isShutdown(false)
    180 #endif
    181 {
    182 #ifdef WEBRTC_LINUX
    183  _inotifyEventThread = PlatformThread::SpawnJoinable(
    184      [this] {
    185        InotifyProcess();
    186      }, "InotifyEventThread");
    187 #endif
    188 }
    189 
    190 int32_t DeviceInfoV4l2::Init() {
    191  return 0;
    192 }
    193 
    194 DeviceInfoV4l2::~DeviceInfoV4l2() {
    195 #ifdef WEBRTC_LINUX
    196    _isShutdown = true;
    197 
    198    if (!_inotifyEventThread.empty()) {
    199      _inotifyEventThread.Finalize();
    200    }
    201 #endif
    202 }
    203 
    204 uint32_t DeviceInfoV4l2::NumberOfDevices() {
    205  uint32_t count = 0;
    206  char device[20];
    207  int fd = -1;
    208  struct v4l2_capability cap;
    209 
    210  /* detect /dev/video [0-63]VideoCaptureModule entries */
    211  for (int n = 0; n < 64; n++) {
    212    snprintf(device, sizeof(device), "/dev/video%d", n);
    213    if ((fd = open(device, O_RDONLY)) != -1) {
    214      // query device capabilities and make sure this is a video capture device
    215      if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !IsVideoCaptureDevice(&cap)) {
    216        close(fd);
    217        continue;
    218      }
    219 
    220      close(fd);
    221      count++;
    222    }
    223  }
    224 
    225  return count;
    226 }
    227 
    228 int32_t DeviceInfoV4l2::GetDeviceName(uint32_t deviceNumber,
    229                                      char* deviceNameUTF8,
    230                                      uint32_t deviceNameLength,
    231                                      char* deviceUniqueIdUTF8,
    232                                      uint32_t deviceUniqueIdUTF8Length,
    233                                      char* /*productUniqueIdUTF8*/,
    234                                      uint32_t /*productUniqueIdUTF8Length*/,
    235                                      pid_t* /*pid*/,
    236                                      bool* /*deviceIsPlaceholder*/) {
    237  // Travel through /dev/video [0-63]
    238  uint32_t count = 0;
    239  char device[20];
    240  int fd = -1;
    241  bool found = false;
    242  struct v4l2_capability cap;
    243  for (int n = 0; n < 64; n++) {
    244    snprintf(device, sizeof(device), "/dev/video%d", n);
    245    if ((fd = open(device, O_RDONLY)) != -1) {
    246      // query device capabilities and make sure this is a video capture device
    247      if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || !IsVideoCaptureDevice(&cap)) {
    248        close(fd);
    249        continue;
    250      }
    251      if (count == deviceNumber) {
    252        // Found the device
    253        found = true;
    254        break;
    255      } else {
    256        close(fd);
    257        count++;
    258      }
    259    }
    260  }
    261 
    262  if (!found)
    263    return -1;
    264 
    265  // query device capabilities
    266  if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
    267    RTC_LOG(LS_INFO) << "error in querying the device capability for device "
    268                     << device << ". errno = " << errno;
    269    close(fd);
    270    return -1;
    271  }
    272 
    273  close(fd);
    274 
    275  char cameraName[64];
    276  memset(deviceNameUTF8, 0, deviceNameLength);
    277  memcpy(cameraName, cap.card, sizeof(cap.card));
    278 
    279  if (deviceNameLength > strlen(cameraName)) {
    280    memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
    281  } else {
    282    RTC_LOG(LS_INFO) << "buffer passed is too small";
    283    return -1;
    284  }
    285 
    286  if (cap.bus_info[0] != 0) {  // may not available in all drivers
    287    // copy device id
    288    size_t len = strlen(reinterpret_cast<const char*>(cap.bus_info));
    289    if (deviceUniqueIdUTF8Length > len) {
    290      memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
    291      memcpy(deviceUniqueIdUTF8, cap.bus_info, len);
    292    } else {
    293      RTC_LOG(LS_INFO) << "buffer passed is too small";
    294      return -1;
    295    }
    296  }
    297 
    298  return 0;
    299 }
    300 
    301 int32_t DeviceInfoV4l2::CreateCapabilityMap(const char* deviceUniqueIdUTF8) {
    302  int fd;
    303  char device[32];
    304  bool found = false;
    305 
    306  const int32_t deviceUniqueIdUTF8Length = strlen(deviceUniqueIdUTF8);
    307  if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {
    308    RTC_LOG(LS_INFO) << "Device name too long";
    309    return -1;
    310  }
    311  RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device "
    312                   << deviceUniqueIdUTF8;
    313 
    314  /* detect /dev/video [0-63] entries */
    315  for (int n = 0; n < 64; ++n) {
    316    snprintf(device, sizeof(device), "/dev/video%d", n);
    317    fd = open(device, O_RDONLY);
    318    if (fd == -1)
    319      continue;
    320 
    321    // query device capabilities
    322    struct v4l2_capability cap;
    323    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
    324      // skip devices without video capture capability
    325      if (!IsVideoCaptureDevice(&cap)) {
    326        close(fd);
    327        continue;
    328      }
    329 
    330      if (cap.bus_info[0] != 0) {
    331        if (strncmp(reinterpret_cast<const char*>(cap.bus_info),
    332                    deviceUniqueIdUTF8,
    333                    strlen(deviceUniqueIdUTF8)) == 0) {  // match with device id
    334          found = true;
    335          break;  // fd matches with device unique id supplied
    336        }
    337      } else {  // match for device name
    338        if (IsDeviceNameMatches(reinterpret_cast<const char*>(cap.card),
    339                                deviceUniqueIdUTF8)) {
    340          found = true;
    341          break;
    342        }
    343      }
    344    }
    345    close(fd);  // close since this is not the matching device
    346  }
    347 
    348  if (!found) {
    349    RTC_LOG(LS_INFO) << "no matching device found";
    350    return -1;
    351  }
    352 
    353  // now fd will point to the matching device
    354  // reset old capability list.
    355  _captureCapabilities.clear();
    356 
    357  int size = FillCapabilities(fd);
    358  close(fd);
    359 
    360  // Store the new used device name
    361  _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
    362  _lastUsedDeviceName = reinterpret_cast<char*>(
    363      realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1));
    364  memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8,
    365         _lastUsedDeviceNameLength + 1);
    366 
    367  RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
    368 
    369  return size;
    370 }
    371 
    372 int32_t DeviceInfoV4l2::DisplayCaptureSettingsDialogBox(
    373    const char* /*deviceUniqueIdUTF8*/,
    374    const char* /*dialogTitleUTF8*/,
    375    void* /*parentWindow*/,
    376    uint32_t /*positionX*/,
    377    uint32_t /*positionY*/) {
    378  return -1;
    379 }
    380 
    381 bool DeviceInfoV4l2::IsDeviceNameMatches(const char* name,
    382                                         const char* deviceUniqueIdUTF8) {
    383  if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
    384    return true;
    385  return false;
    386 }
    387 
    388 bool DeviceInfoV4l2::IsVideoCaptureDevice(struct v4l2_capability* cap)
    389 {
    390  if (cap->capabilities & V4L2_CAP_DEVICE_CAPS) {
    391    return cap->device_caps & V4L2_CAP_VIDEO_CAPTURE;
    392  } else {
    393    return cap->capabilities & V4L2_CAP_VIDEO_CAPTURE;
    394  }
    395 }
    396 
    397 int32_t DeviceInfoV4l2::FillCapabilities(int fd) {
    398  // set image format
    399  struct v4l2_format video_fmt;
    400  memset(&video_fmt, 0, sizeof(struct v4l2_format));
    401 
    402  video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    403  video_fmt.fmt.pix.sizeimage = 0;
    404 
    405  unsigned int videoFormats[] = {
    406      V4L2_PIX_FMT_MJPEG,  V4L2_PIX_FMT_JPEG,   V4L2_PIX_FMT_YUV420,
    407      V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUYV,   V4L2_PIX_FMT_UYVY,
    408      V4L2_PIX_FMT_NV12,   V4L2_PIX_FMT_BGR24,  V4L2_PIX_FMT_RGB24,
    409      V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_ABGR32, V4L2_PIX_FMT_ARGB32,
    410      V4L2_PIX_FMT_RGBA32, V4L2_PIX_FMT_BGR32,  V4L2_PIX_FMT_RGB32,
    411  };
    412  constexpr int totalFmts = sizeof(videoFormats) / sizeof(unsigned int);
    413 
    414  int sizes = 13;
    415  unsigned int size[][2] = {{128, 96},   {160, 120},  {176, 144},  {320, 240},
    416                            {352, 288},  {640, 480},  {704, 576},  {800, 600},
    417                            {960, 720},  {1280, 720}, {1024, 768}, {1440, 1080},
    418                            {1920, 1080}};
    419 
    420  for (int fmts = 0; fmts < totalFmts; fmts++) {
    421    for (int i = 0; i < sizes; i++) {
    422      video_fmt.fmt.pix.pixelformat = videoFormats[fmts];
    423      video_fmt.fmt.pix.width = size[i][0];
    424      video_fmt.fmt.pix.height = size[i][1];
    425 
    426      if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) {
    427        if ((video_fmt.fmt.pix.width == size[i][0]) &&
    428            (video_fmt.fmt.pix.height == size[i][1])) {
    429          VideoCaptureCapability cap;
    430          cap.width = video_fmt.fmt.pix.width;
    431          cap.height = video_fmt.fmt.pix.height;
    432          if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) {
    433            cap.videoType = VideoType::kYUY2;
    434          } else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420) {
    435            cap.videoType = VideoType::kI420;
    436          } else if (videoFormats[fmts] == V4L2_PIX_FMT_YVU420) {
    437            cap.videoType = VideoType::kYV12;
    438          } else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG ||
    439                     videoFormats[fmts] == V4L2_PIX_FMT_JPEG) {
    440            cap.videoType = VideoType::kMJPEG;
    441          } else if (videoFormats[fmts] == V4L2_PIX_FMT_UYVY) {
    442            cap.videoType = VideoType::kUYVY;
    443          } else if (videoFormats[fmts] == V4L2_PIX_FMT_NV12) {
    444            cap.videoType = VideoType::kNV12;
    445          } else if (videoFormats[fmts] == V4L2_PIX_FMT_BGR24) {
    446            // NB that for RGB formats, `VideoType` follows naming conventions
    447            // of libyuv[1], where e.g. the format for FOURCC "ARGB" stores
    448            // pixels in BGRA order in memory. V4L2[2] on the other hand names
    449            // its formats based on the order of the RGB components as stored in
    450            // memory. Applies to all RGB formats below.
    451            // [1]https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/docs/formats.md#the-argb-fourcc
    452            // [2]https://www.kernel.org/doc/html/v6.2/userspace-api/media/v4l/pixfmt-rgb.html#bits-per-component
    453            cap.videoType = VideoType::kRGB24;
    454          } else if (videoFormats[fmts] == V4L2_PIX_FMT_RGB24) {
    455            cap.videoType = VideoType::kBGR24;
    456          } else if (videoFormats[fmts] == V4L2_PIX_FMT_RGB565) {
    457            cap.videoType = VideoType::kRGB565;
    458          } else if (videoFormats[fmts] == V4L2_PIX_FMT_ABGR32) {
    459            cap.videoType = VideoType::kARGB;
    460          } else if (videoFormats[fmts] == V4L2_PIX_FMT_ARGB32) {
    461            cap.videoType = VideoType::kBGRA;
    462          } else if (videoFormats[fmts] == V4L2_PIX_FMT_BGR32) {
    463            cap.videoType = VideoType::kARGB;
    464          } else if (videoFormats[fmts] == V4L2_PIX_FMT_RGB32) {
    465            cap.videoType = VideoType::kBGRA;
    466          } else if (videoFormats[fmts] == V4L2_PIX_FMT_RGBA32) {
    467            cap.videoType = VideoType::kABGR;
    468          } else {
    469            RTC_DCHECK_NOTREACHED();
    470          }
    471 
    472          // get fps of current camera mode
    473          // V4l2 does not have a stable method of knowing so we just guess.
    474          if (cap.width >= 800 && cap.videoType != VideoType::kMJPEG) {
    475            cap.maxFPS = 15;
    476          } else {
    477            cap.maxFPS = 30;
    478          }
    479 
    480          _captureCapabilities.push_back(cap);
    481          RTC_LOG(LS_VERBOSE) << "Camera capability, width:" << cap.width
    482                              << " height:" << cap.height
    483                              << " type:" << static_cast<int32_t>(cap.videoType)
    484                              << " fps:" << cap.maxFPS;
    485        }
    486      }
    487    }
    488  }
    489 
    490  RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
    491  return _captureCapabilities.size();
    492 }
    493 
    494 }  // namespace videocapturemodule
    495 }  // namespace webrtc
    496 
    497 #ifdef ABGR32_OVERRIDE
    498 #undef ABGR32_OVERRIDE
    499 #undef V4L2_PIX_FMT_ABGR32
    500 #endif
    501 
    502 #ifdef ARGB32_OVERRIDE
    503 #undef ARGB32_OVERRIDE
    504 #undef V4L2_PIX_FMT_ARGB32
    505 #endif
    506 
    507 #ifdef RGBA32_OVERRIDE
    508 #undef RGBA32_OVERRIDE
    509 #undef V4L2_PIX_FMT_RGBA32
    510 #endif