Commit ab8f999c authored by Andres Calderon Jaramillo's avatar Andres Calderon Jaramillo Committed by Commit Bot

media: Workaround for Huddly GO deadlock.

This CL stops and restarts the camera capture stream in
V4L2CaptureDelegate the first time a timeout is detected while polling
for captured frames. This is a workaround for a Huddly GO camera issue
where the camera gets into a deadlock state at times at the beginning of
the capture session. The underlying issue seems to be a kernel driver
synchronization problem, but upgrading the kernel might not be practical
in the short term for affected devices.

1) Flash M78 Chrome OS image.
2) Deploy Chrome in debug mode (a bunch of irrelevant DCHECKs must be
   deleted).
3) In non-kiosk mode, visit
   https://webrtc.github.io/samples/src/content/devices/input-output/
4) Switch between the two Huddly GO streams constantly while monitoring
   /var/log/chrome/chrome.
Eventually, the deadlock path is hit (see the DLOG(WARNING) in
v4l2_capture_delegate.cc) but we recover successfully and the camera
continues capturing normally.

Change-Id: Idc90aa8b9dbb069c419c3ca1b90be9b47d4a6c51
Bug: 1010557,b:141978989
Test: on a guado with a Huddly GO:
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1859901
Commit-Queue: Andres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: default avatarRicky Liang <jcliang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708011}
parent c3a5da98
...@@ -351,30 +351,10 @@ void V4L2CaptureDelegate::AllocateAndStart( ...@@ -351,30 +351,10 @@ void V4L2CaptureDelegate::AllocateAndStart(
capture_format_.frame_rate = frame_rate; capture_format_.frame_rate = frame_rate;
capture_format_.pixel_format = pixel_format; capture_format_.pixel_format = pixel_format;
v4l2_requestbuffers r_buffer; if (!StartStream())
FillV4L2RequestBuffer(&r_buffer, kNumVideoBuffers);
if (DoIoctl(VIDIOC_REQBUFS, &r_buffer) < 0) {
SetErrorState(VideoCaptureError::kV4L2ErrorRequestingMmapBuffers, FROM_HERE,
"Error requesting MMAP buffers from V4L2");
return;
}
for (unsigned int i = 0; i < r_buffer.count; ++i) {
if (!MapAndQueueBuffer(i)) {
SetErrorState(VideoCaptureError::kV4L2AllocateBufferFailed, FROM_HERE,
"Allocate buffer failed");
return;
}
}
v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (DoIoctl(VIDIOC_STREAMON, &capture_type) < 0) {
SetErrorState(VideoCaptureError::kV4L2VidiocStreamonFailed, FROM_HERE,
"VIDIOC_STREAMON failed");
return; return;
}
client_->OnStarted(); client_->OnStarted();
is_capturing_ = true;
// Post task to start fetching frames from v4l2. // Post task to start fetching frames from v4l2.
v4l2_task_runner_->PostTask( v4l2_task_runner_->PostTask(
...@@ -383,28 +363,10 @@ void V4L2CaptureDelegate::AllocateAndStart( ...@@ -383,28 +363,10 @@ void V4L2CaptureDelegate::AllocateAndStart(
void V4L2CaptureDelegate::StopAndDeAllocate() { void V4L2CaptureDelegate::StopAndDeAllocate() {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
// The order is important: stop streaming, clear |buffer_pool_|, StopStream();
// thus munmap()ing the v4l2_buffers, and then return them to the OS.
v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (DoIoctl(VIDIOC_STREAMOFF, &capture_type) < 0) {
SetErrorState(VideoCaptureError::kV4L2VidiocStreamoffFailed, FROM_HERE,
"VIDIOC_STREAMOFF failed");
return;
}
buffer_tracker_pool_.clear();
v4l2_requestbuffers r_buffer;
FillV4L2RequestBuffer(&r_buffer, 0);
if (DoIoctl(VIDIOC_REQBUFS, &r_buffer) < 0) {
SetErrorState(VideoCaptureError::kV4L2FailedToVidiocReqbufsWithCount0,
FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0");
}
// At this point we can close the device. // At this point we can close the device.
// This is also needed for correctly changing settings later via VIDIOC_S_FMT. // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
device_fd_.reset(); device_fd_.reset();
is_capturing_ = false;
client_.reset(); client_.reset();
} }
...@@ -831,6 +793,34 @@ bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { ...@@ -831,6 +793,34 @@ bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) {
return true; return true;
} }
bool V4L2CaptureDelegate::StartStream() {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
DCHECK(!is_capturing_);
v4l2_requestbuffers r_buffer;
FillV4L2RequestBuffer(&r_buffer, kNumVideoBuffers);
if (DoIoctl(VIDIOC_REQBUFS, &r_buffer) < 0) {
SetErrorState(VideoCaptureError::kV4L2ErrorRequestingMmapBuffers, FROM_HERE,
"Error requesting MMAP buffers from V4L2");
return false;
}
for (unsigned int i = 0; i < r_buffer.count; ++i) {
if (!MapAndQueueBuffer(i)) {
SetErrorState(VideoCaptureError::kV4L2AllocateBufferFailed, FROM_HERE,
"Allocate buffer failed");
return false;
}
}
v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (DoIoctl(VIDIOC_STREAMON, &capture_type) < 0) {
SetErrorState(VideoCaptureError::kV4L2VidiocStreamonFailed, FROM_HERE,
"VIDIOC_STREAMON failed");
return false;
}
is_capturing_ = true;
return true;
}
void V4L2CaptureDelegate::DoCapture() { void V4L2CaptureDelegate::DoCapture() {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
if (!is_capturing_) if (!is_capturing_)
...@@ -851,7 +841,21 @@ void V4L2CaptureDelegate::DoCapture() { ...@@ -851,7 +841,21 @@ void V4L2CaptureDelegate::DoCapture() {
// throw an error if it times out too many times. // throw an error if it times out too many times.
if (result == 0) { if (result == 0) {
timeout_count_++; timeout_count_++;
if (timeout_count_ >= kContinuousTimeoutLimit) { if (timeout_count_ == 1) {
// TODO(crbug.com/1010557): this is an unfortunate workaround for an issue
// with the Huddly GO camera where the device seems to get into a deadlock
// state. As best as we can tell for now, there is a synchronization issue
// in older kernels, and stopping and starting the stream gets the camera
// out of this bad state. Upgrading the kernel is difficult so this is our
// way out for now.
DLOG(WARNING) << "Restarting camera stream";
if (!StopStream() || !StartStream())
return;
v4l2_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&V4L2CaptureDelegate::DoCapture, GetWeakPtr()));
return;
} else if (timeout_count_ >= kContinuousTimeoutLimit) {
SetErrorState( SetErrorState(
VideoCaptureError::kV4L2MultipleContinuousTimeoutsWhileReadPolling, VideoCaptureError::kV4L2MultipleContinuousTimeoutsWhileReadPolling,
FROM_HERE, "Multiple continuous timeouts while read-polling."); FROM_HERE, "Multiple continuous timeouts while read-polling.");
...@@ -942,6 +946,33 @@ void V4L2CaptureDelegate::DoCapture() { ...@@ -942,6 +946,33 @@ void V4L2CaptureDelegate::DoCapture() {
FROM_HERE, base::BindOnce(&V4L2CaptureDelegate::DoCapture, GetWeakPtr())); FROM_HERE, base::BindOnce(&V4L2CaptureDelegate::DoCapture, GetWeakPtr()));
} }
bool V4L2CaptureDelegate::StopStream() {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
DCHECK(is_capturing_);
is_capturing_ = false;
// The order is important: stop streaming, clear |buffer_pool_|,
// thus munmap()ing the v4l2_buffers, and then return them to the OS.
v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (DoIoctl(VIDIOC_STREAMOFF, &capture_type) < 0) {
SetErrorState(VideoCaptureError::kV4L2VidiocStreamoffFailed, FROM_HERE,
"VIDIOC_STREAMOFF failed");
return false;
}
buffer_tracker_pool_.clear();
v4l2_requestbuffers r_buffer;
FillV4L2RequestBuffer(&r_buffer, 0);
if (DoIoctl(VIDIOC_REQBUFS, &r_buffer) < 0) {
SetErrorState(VideoCaptureError::kV4L2FailedToVidiocReqbufsWithCount0,
FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0");
return false;
}
return true;
}
void V4L2CaptureDelegate::SetErrorState(VideoCaptureError error, void V4L2CaptureDelegate::SetErrorState(VideoCaptureError error,
const base::Location& from_here, const base::Location& from_here,
const std::string& reason) { const std::string& reason) {
......
...@@ -100,7 +100,9 @@ class CAPTURE_EXPORT V4L2CaptureDelegate final { ...@@ -100,7 +100,9 @@ class CAPTURE_EXPORT V4L2CaptureDelegate final {
// enqueues it (VIDIOC_QBUF) back into V4L2. // enqueues it (VIDIOC_QBUF) back into V4L2.
bool MapAndQueueBuffer(int index); bool MapAndQueueBuffer(int index);
bool StartStream();
void DoCapture(); void DoCapture();
bool StopStream();
void SetErrorState(VideoCaptureError error, void SetErrorState(VideoCaptureError error,
const base::Location& from_here, const base::Location& from_here,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment