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;
 }