Resolve dcheck due to wrong threading check in VideoCaptureV4L2 `capture_checker_` has to be released before the capture thread is started, since it will also try to grab it. If it starts fast enough, it may trigger the DCHECK in `CaptureProcess()`. Bug: webrtc:504490094 Change-Id: I6cbf91525d15460aabc11d3adaef2eb4b0b7b02b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/465581 Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org> Reviewed-by: Per Kjellander <perkj@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47498}
diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc index 3f836ef..0eef1ff 100644 --- a/modules/video_capture/linux/video_capture_v4l2.cc +++ b/modules/video_capture/linux/video_capture_v4l2.cc
@@ -133,179 +133,183 @@ // only when we are not capturing. The code above can be called many // times while sharing instance of VideoCaptureV4L2 between websites // and therefore it would not follow the requirements of this checker. - RTC_CHECK_RUNS_SERIALIZED(&capture_checker_); + { + RTC_CHECK_RUNS_SERIALIZED(&capture_checker_); - // Set a baseline of configured parameters. It is updated here during - // configuration, then read from the capture thread. - configured_capability_ = capability; + // Set a baseline of configured parameters. It is updated here during + // configuration, then read from the capture thread. + configured_capability_ = capability; - MutexLock lock(&capture_lock_); - // first open /dev/video device - char device[20]; - snprintf(device, sizeof(device), "/dev/video%d", _deviceId); + MutexLock lock(&capture_lock_); + // first open /dev/video device + char device[20]; + snprintf(device, sizeof(device), "/dev/video%d", _deviceId); - if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) { - RTC_LOG(LS_INFO) << "error in opening " << device << " errono = " << errno; - return -1; - } - - // Supported video formats in preferred order. - // If the requested resolution is larger than VGA, we prefer MJPEG. Go for - // I420 otherwise. - unsigned int hdFmts[] = { - V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_ABGR32, V4L2_PIX_FMT_ARGB32, V4L2_PIX_FMT_RGBA32, - V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, - V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_JPEG, - }; - unsigned int sdFmts[] = { - V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_ABGR32, - V4L2_PIX_FMT_ARGB32, V4L2_PIX_FMT_RGBA32, V4L2_PIX_FMT_BGR32, - V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG, - }; - const bool isHd = capability.width > 640 || capability.height > 480; - unsigned int* fmts = isHd ? hdFmts : sdFmts; - static_assert(sizeof(hdFmts) == sizeof(sdFmts)); - constexpr int nFormats = sizeof(hdFmts) / sizeof(unsigned int); - - // Enumerate image formats. - struct v4l2_fmtdesc fmt; - int fmtsIdx = nFormats; - memset(&fmt, 0, sizeof(fmt)); - fmt.index = 0; - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - RTC_LOG(LS_INFO) << "Video Capture enumerats supported image formats:"; - while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) { - RTC_LOG(LS_INFO) << " { pixelformat = " << GetFourccName(fmt.pixelformat) - << ", description = '" << fmt.description << "' }"; - // Match the preferred order. - for (int i = 0; i < nFormats; i++) { - if (fmt.pixelformat == fmts[i] && i < fmtsIdx) - fmtsIdx = i; + if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) { + RTC_LOG(LS_INFO) << "error in opening " << device + << " errono = " << errno; + return -1; } - // Keep enumerating. - fmt.index++; - } - if (fmtsIdx == nFormats) { - RTC_LOG(LS_INFO) << "no supporting video formats found"; - return -1; - } else { - RTC_LOG(LS_INFO) << "We prefer format " << GetFourccName(fmts[fmtsIdx]); - } + // Supported video formats in preferred order. + // If the requested resolution is larger than VGA, we prefer MJPEG. Go for + // I420 otherwise. + unsigned int hdFmts[] = { + V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_ABGR32, V4L2_PIX_FMT_ARGB32, V4L2_PIX_FMT_RGBA32, + V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, + V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_JPEG, + }; + unsigned int sdFmts[] = { + V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_ABGR32, + V4L2_PIX_FMT_ARGB32, V4L2_PIX_FMT_RGBA32, V4L2_PIX_FMT_BGR32, + V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG, + }; + const bool isHd = capability.width > 640 || capability.height > 480; + unsigned int* fmts = isHd ? hdFmts : sdFmts; + static_assert(sizeof(hdFmts) == sizeof(sdFmts)); + constexpr int nFormats = sizeof(hdFmts) / sizeof(unsigned int); - struct v4l2_format video_fmt; - memset(&video_fmt, 0, sizeof(struct v4l2_format)); - video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - video_fmt.fmt.pix.sizeimage = 0; - video_fmt.fmt.pix.width = capability.width; - video_fmt.fmt.pix.height = capability.height; - video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx]; + // Enumerate image formats. + struct v4l2_fmtdesc fmt; + int fmtsIdx = nFormats; + memset(&fmt, 0, sizeof(fmt)); + fmt.index = 0; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + RTC_LOG(LS_INFO) << "Video Capture enumerats supported image formats:"; + while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) { + RTC_LOG(LS_INFO) << " { pixelformat = " << GetFourccName(fmt.pixelformat) + << ", description = '" << fmt.description << "' }"; + // Match the preferred order. + for (int i = 0; i < nFormats; i++) { + if (fmt.pixelformat == fmts[i] && i < fmtsIdx) + fmtsIdx = i; + } + // Keep enumerating. + fmt.index++; + } - if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - configured_capability_.videoType = VideoType::kYUY2; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) - configured_capability_.videoType = VideoType::kI420; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) - configured_capability_.videoType = VideoType::kYV12; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) - configured_capability_.videoType = VideoType::kUYVY; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) - configured_capability_.videoType = VideoType::kNV12; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) - configured_capability_.videoType = VideoType::kRGB24; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) - configured_capability_.videoType = VideoType::kBGR24; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) - configured_capability_.videoType = VideoType::kRGB565; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_ABGR32 || - video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) - configured_capability_.videoType = VideoType::kARGB; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_ARGB32 || - video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) - configured_capability_.videoType = VideoType::kBGRA; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGBA32) - configured_capability_.videoType = VideoType::kABGR; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || - video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - configured_capability_.videoType = VideoType::kMJPEG; - else - RTC_DCHECK_NOTREACHED(); + if (fmtsIdx == nFormats) { + RTC_LOG(LS_INFO) << "no supporting video formats found"; + return -1; + } else { + RTC_LOG(LS_INFO) << "We prefer format " << GetFourccName(fmts[fmtsIdx]); + } - // set format and frame size now - if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) { - RTC_LOG(LS_INFO) << "error in VIDIOC_S_FMT, errno = " << errno; - return -1; - } + struct v4l2_format video_fmt; + memset(&video_fmt, 0, sizeof(struct v4l2_format)); + video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + video_fmt.fmt.pix.sizeimage = 0; + video_fmt.fmt.pix.width = capability.width; + video_fmt.fmt.pix.height = capability.height; + video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx]; - // initialize current width and height - configured_capability_.width = video_fmt.fmt.pix.width; - configured_capability_.height = video_fmt.fmt.pix.height; + if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + configured_capability_.videoType = VideoType::kYUY2; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + configured_capability_.videoType = VideoType::kI420; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) + configured_capability_.videoType = VideoType::kYV12; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) + configured_capability_.videoType = VideoType::kUYVY; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) + configured_capability_.videoType = VideoType::kNV12; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) + configured_capability_.videoType = VideoType::kRGB24; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) + configured_capability_.videoType = VideoType::kBGR24; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) + configured_capability_.videoType = VideoType::kRGB565; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_ABGR32 || + video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) + configured_capability_.videoType = VideoType::kARGB; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_ARGB32 || + video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) + configured_capability_.videoType = VideoType::kBGRA; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGBA32) + configured_capability_.videoType = VideoType::kABGR; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || + video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) + configured_capability_.videoType = VideoType::kMJPEG; + else + RTC_DCHECK_NOTREACHED(); - // Trying to set frame rate, before check driver capability. - bool driver_framerate_support = true; - struct v4l2_streamparm streamparms; - memset(&streamparms, 0, sizeof(streamparms)); - streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) { - RTC_LOG(LS_INFO) << "error in VIDIOC_G_PARM errno = " << errno; - driver_framerate_support = false; - // continue - } else { - // check the capability flag is set to V4L2_CAP_TIMEPERFRAME. - if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { - // driver supports the feature. Set required framerate. - memset(&streamparms, 0, sizeof(streamparms)); - streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - streamparms.parm.capture.timeperframe.numerator = 1; - streamparms.parm.capture.timeperframe.denominator = capability.maxFPS; - if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) { - RTC_LOG(LS_INFO) << "Failed to set the framerate. errno=" << errno; - driver_framerate_support = false; + // set format and frame size now + if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) { + RTC_LOG(LS_INFO) << "error in VIDIOC_S_FMT, errno = " << errno; + return -1; + } + + // initialize current width and height + configured_capability_.width = video_fmt.fmt.pix.width; + configured_capability_.height = video_fmt.fmt.pix.height; + + // Trying to set frame rate, before check driver capability. + bool driver_framerate_support = true; + struct v4l2_streamparm streamparms; + memset(&streamparms, 0, sizeof(streamparms)); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) { + RTC_LOG(LS_INFO) << "error in VIDIOC_G_PARM errno = " << errno; + driver_framerate_support = false; + // continue + } else { + // check the capability flag is set to V4L2_CAP_TIMEPERFRAME. + if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { + // driver supports the feature. Set required framerate. + memset(&streamparms, 0, sizeof(streamparms)); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + streamparms.parm.capture.timeperframe.numerator = 1; + streamparms.parm.capture.timeperframe.denominator = capability.maxFPS; + if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) { + RTC_LOG(LS_INFO) << "Failed to set the framerate. errno=" << errno; + driver_framerate_support = false; + } } } - } - // If driver doesn't support framerate control, need to hardcode. - // Hardcoding the value based on the frame size. - if (!driver_framerate_support) { - if (configured_capability_.width >= 800 && - configured_capability_.videoType != VideoType::kMJPEG) { - configured_capability_.maxFPS = 15; - } else { - configured_capability_.maxFPS = 30; + // If driver doesn't support framerate control, need to hardcode. + // Hardcoding the value based on the frame size. + if (!driver_framerate_support) { + if (configured_capability_.width >= 800 && + configured_capability_.videoType != VideoType::kMJPEG) { + configured_capability_.maxFPS = 15; + } else { + configured_capability_.maxFPS = 30; + } } - } - if (!AllocateVideoBuffers()) { - RTC_LOG(LS_INFO) << "failed to allocate video capture buffers"; - return -1; - } + if (!AllocateVideoBuffers()) { + RTC_LOG(LS_INFO) << "failed to allocate video capture buffers"; + return -1; + } - // Needed to start UVC camera - from the uvcview application - enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) { - RTC_LOG(LS_INFO) << "Failed to turn on stream"; - return -1; - } + // Needed to start UVC camera - from the uvcview application + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) { + RTC_LOG(LS_INFO) << "Failed to turn on stream"; + return -1; + } - _requestedCapability = capability; - _captureStarted = true; - _streaming = true; + _requestedCapability = capability; + _captureStarted = true; + _streaming = true; + quit_ = false; + } + // Outside of `RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);` // start capture thread; - if (_captureThread.empty()) { - quit_ = false; - _captureThread = PlatformThread::SpawnJoinable( - [this] { - while (CaptureProcess()) { - } - }, - "CaptureThread", ThreadAttributes().SetPriority(ThreadPriority::kHigh)); - } + RTC_DCHECK(_captureThread.empty()); + _captureThread = PlatformThread::SpawnJoinable( + [this] { + while (CaptureProcess()) { + } + }, + "CaptureThread", ThreadAttributes().SetPriority(ThreadPriority::kHigh)); + return 0; }