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