summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/video/capture/linux/video_capture_device_linux.cc364
-rw-r--r--media/video/capture/linux/video_capture_device_linux.h41
2 files changed, 209 insertions, 196 deletions
diff --git a/media/video/capture/linux/video_capture_device_linux.cc b/media/video/capture/linux/video_capture_device_linux.cc
index ec5ac6f..60d8f77 100644
--- a/media/video/capture/linux/video_capture_device_linux.cc
+++ b/media/video/capture/linux/video_capture_device_linux.cc
@@ -29,7 +29,7 @@ namespace media {
#define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff))
// Max number of video buffers VideoCaptureDeviceLinux can allocate.
-enum { kMaxVideoBuffers = 2 };
+const uint32 kMaxVideoBuffers = 2;
// Timeout in milliseconds v4l2_thread_ blocks waiting for a frame from the hw.
enum { kCaptureTimeoutMs = 200 };
// The number of continuous timeouts tolerated before treated as error.
@@ -40,6 +40,55 @@ enum { kMjpegHeight = 480 };
// Typical framerate, in fps
enum { kTypicalFramerate = 30 };
+class VideoCaptureDeviceLinux::V4L2CaptureDelegate
+ : public base::RefCountedThreadSafe<V4L2CaptureDelegate>{
+ public:
+ V4L2CaptureDelegate(
+ const Name& device_name,
+ const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner,
+ int power_line_frequency);
+
+ void AllocateAndStart(int width,
+ int height,
+ float frame_rate,
+ scoped_ptr<Client> client);
+ void StopAndDeAllocate();
+ void SetRotation(int rotation);
+ bool DeAllocateVideoBuffers();
+
+ private:
+ // Buffers used to receive captured frames from v4l2.
+ struct Buffer {
+ Buffer() : start(0), length(0) {}
+ void* start;
+ size_t length;
+ };
+
+ friend class base::RefCountedThreadSafe<V4L2CaptureDelegate>;
+ ~V4L2CaptureDelegate();
+
+ void DoCapture();
+ bool AllocateVideoBuffers();
+ void SetErrorState(const std::string& reason);
+
+ const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner_;
+
+ bool is_capturing_;
+ scoped_ptr<VideoCaptureDevice::Client> client_;
+ const Name device_name_;
+ base::ScopedFD device_fd_; // File descriptor for the opened camera device.
+ Buffer* buffer_pool_;
+ int buffer_pool_size_; // Number of allocated buffers.
+ int timeout_count_;
+ VideoCaptureFormat capture_format_;
+ const int power_line_frequency_;
+
+ // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270.
+ int rotation_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(V4L2CaptureDelegate);
+};
+
// V4L2 color formats VideoCaptureDeviceLinux support.
static const int32 kV4l2RawFmts[] = {
V4L2_PIX_FMT_YUV420,
@@ -75,29 +124,26 @@ static bool ReadIdFile(const std::string path, std::string* id) {
// static
VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat(
uint32 v4l2_fourcc) {
- VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN;
- switch (v4l2_fourcc) {
- case V4L2_PIX_FMT_YUV420:
- result = PIXEL_FORMAT_I420;
- break;
- case V4L2_PIX_FMT_YUYV:
- result = PIXEL_FORMAT_YUY2;
- break;
- case V4L2_PIX_FMT_UYVY:
- result = PIXEL_FORMAT_UYVY;
- break;
- case V4L2_PIX_FMT_MJPEG:
- case V4L2_PIX_FMT_JPEG:
- result = PIXEL_FORMAT_MJPEG;
- break;
- default:
- DVLOG(1) << "Unsupported pixel format: "
- << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 0)
- << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 1)
- << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 2)
- << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 3);
+ const struct {
+ uint32 fourcc;
+ VideoPixelFormat pixel_format;
+ } kFourCcAndChromiumPixelFormat[] = {
+ {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420},
+ {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2},
+ {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY},
+ {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG},
+ {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG},
+ };
+ for (const auto& fourcc_and_pixel_format : kFourCcAndChromiumPixelFormat) {
+ if (fourcc_and_pixel_format.fourcc == v4l2_fourcc)
+ return fourcc_and_pixel_format.pixel_format;
}
- return result;
+ DVLOG(1) << "Unsupported pixel format: "
+ << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 0)
+ << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 1)
+ << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 2)
+ << GET_V4L2_FOURCC_CHAR(v4l2_fourcc, 3);
+ return PIXEL_FORMAT_UNKNOWN;
}
// static
@@ -140,18 +186,13 @@ const std::string VideoCaptureDevice::Name::GetModel() const {
}
VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name)
- : is_capturing_(false),
- device_name_(device_name),
- v4l2_thread_("V4L2Thread"),
- buffer_pool_(NULL),
- buffer_pool_size_(0),
- timeout_count_(0),
- rotation_(0) {
+ : v4l2_thread_("V4L2CaptureThread"),
+ device_name_(device_name) {
}
VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
// Check if the thread is running.
- // This means that the device have not been DeAllocated properly.
+ // This means that the device has not been StopAndDeAllocate()d properly.
DCHECK(!v4l2_thread_.IsRunning());
v4l2_thread_.Stop();
}
@@ -159,75 +200,90 @@ VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
void VideoCaptureDeviceLinux::AllocateAndStart(
const VideoCaptureParams& params,
scoped_ptr<VideoCaptureDevice::Client> client) {
- if (v4l2_thread_.IsRunning()) {
+ DCHECK(!capture_impl_);
+ if (v4l2_thread_.IsRunning())
return; // Wrong state.
- }
v4l2_thread_.Start();
+ capture_impl_ = new V4L2CaptureDelegate(device_name_,
+ v4l2_thread_.message_loop_proxy(),
+ GetPowerLineFrequencyForLocation());
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnAllocateAndStart,
- base::Unretained(this),
- params.requested_format.frame_size.width(),
- params.requested_format.frame_size.height(),
- params.requested_format.frame_rate,
- base::Passed(&client)));
+ base::Bind(
+ &VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart,
+ capture_impl_,
+ params.requested_format.frame_size.width(),
+ params.requested_format.frame_size.height(),
+ params.requested_format.frame_rate,
+ base::Passed(&client)));
}
void VideoCaptureDeviceLinux::StopAndDeAllocate() {
- if (!v4l2_thread_.IsRunning()) {
+ if (!v4l2_thread_.IsRunning())
return; // Wrong state.
- }
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnStopAndDeAllocate,
- base::Unretained(this)));
+ base::Bind(
+ &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate,
+ capture_impl_));
v4l2_thread_.Stop();
- // Make sure no buffers are still allocated.
- // This can happen (theoretically) if an error occurs when trying to stop
- // the camera.
- DeAllocateVideoBuffers();
+ // TODO(mcasas): VCDLinux called DeAllocateVideoBuffers() a second time after
+ // stopping |v4l2_thread_| to make sure buffers were completely deallocated.
+ // Investigate if that's needed, otherwise remove the following line and make
+ // V4L2CaptureDelegate::DeAllocateVideoBuffers() private.
+ capture_impl_->DeAllocateVideoBuffers();
+ capture_impl_ = NULL;
}
void VideoCaptureDeviceLinux::SetRotation(int rotation) {
if (v4l2_thread_.IsRunning()) {
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::SetRotationOnV4L2Thread,
- base::Unretained(this), rotation));
- } else {
- // If the |v4l2_thread_| is not running, there's no race condition and
- // |rotation_| can be set directly.
- rotation_ = rotation;
+ base::Bind(
+ &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation,
+ capture_impl_,
+ rotation));
}
}
-void VideoCaptureDeviceLinux::SetRotationOnV4L2Thread(int rotation) {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
- DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
- rotation_ = rotation;
+VideoCaptureDeviceLinux::V4L2CaptureDelegate::V4L2CaptureDelegate(
+ const Name& device_name,
+ const scoped_refptr<base::SingleThreadTaskRunner> v4l2_task_runner,
+ int power_line_frequency)
+ : v4l2_task_runner_(v4l2_task_runner),
+ is_capturing_(false),
+ device_name_(device_name),
+ buffer_pool_(NULL),
+ buffer_pool_size_(0),
+ timeout_count_(0),
+ power_line_frequency_(power_line_frequency),
+ rotation_(0) {
}
-void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
- int height,
- float frame_rate,
- scoped_ptr<Client> client) {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
+VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() {
+ DCHECK(!client_);
+}
+void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
+ int width,
+ int height,
+ float frame_rate,
+ scoped_ptr<Client> client) {
+ DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
+ DCHECK(client);
client_ = client.Pass();
// Need to open camera with O_RDWR after Linux kernel 3.3.
device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR)));
if (!device_fd_.is_valid()) {
- SetErrorState("Failed to open V4L2 device driver.");
+ SetErrorState("Failed to open V4L2 device driver file.");
return;
}
- // Test if this is a V4L2 capture device.
- v4l2_capability cap;
+ v4l2_capability cap = {};
if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) &&
- (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) &&
- !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT))) {
- // This is not a V4L2 video capture device.
+ (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE &&
+ !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)))) {
device_fd_.reset();
SetErrorState("This is not a V4L2 video capture device");
return;
@@ -239,7 +295,7 @@ void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight);
std::list<int>::const_iterator best = desired_v4l2_formats.end();
- v4l2_fmtdesc fmtdesc = {0};
+ v4l2_fmtdesc fmtdesc = {};
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
++fmtdesc.index) {
@@ -250,24 +306,19 @@ void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
return;
}
- // Set format and frame size now.
- v4l2_format video_fmt;
- memset(&video_fmt, 0, sizeof(v4l2_format));
+ v4l2_format video_fmt = {};
video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video_fmt.fmt.pix.sizeimage = 0;
video_fmt.fmt.pix.width = width;
video_fmt.fmt.pix.height = height;
video_fmt.fmt.pix.pixelformat = *best;
-
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt)) < 0) {
- SetErrorState(
- base::StringPrintf("Failed to set camera format: %s", strerror(errno)));
+ SetErrorState("Failed to set video capture format");
return;
}
// Set capture framerate in the form of capture interval.
- v4l2_streamparm streamparm;
- memset(&streamparm, 0, sizeof(v4l2_streamparm));
+ v4l2_streamparm streamparm = {};
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// The following line checks that the driver knows about framerate get/set.
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
@@ -295,18 +346,16 @@ void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
// Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
// operation (|errno| == EINVAL in this case) or plain failure.
- const int power_line_frequency = GetPowerLineFrequencyForLocation();
- if ((power_line_frequency == kPowerLine50Hz) ||
- (power_line_frequency == kPowerLine60Hz)) {
+ if ((power_line_frequency_ == kPowerLine50Hz) ||
+ (power_line_frequency_ == kPowerLine60Hz)) {
struct v4l2_control control = {};
control.id = V4L2_CID_POWER_LINE_FREQUENCY;
- control.value = (power_line_frequency == kPowerLine50Hz) ?
- V4L2_CID_POWER_LINE_FREQUENCY_50HZ :
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+ control.value = (power_line_frequency_ == kPowerLine50Hz)
+ ? V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+ : V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
}
- // Store our current width and height.
capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
video_fmt.fmt.pix.height);
capture_format_.frame_rate = frame_rate;
@@ -314,36 +363,36 @@ void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat);
if (!AllocateVideoBuffers()) {
- SetErrorState("Allocate buffers failed");
+ SetErrorState("Allocate buffer failed (Cannot recover from this error)");
return;
}
- // Start UVC camera.
- v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) == -1) {
+ const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) < 0) {
SetErrorState("VIDIOC_STREAMON failed");
return;
}
is_capturing_ = true;
// Post task to start fetching frames from v4l2.
- v4l2_thread_.message_loop()->PostTask(
+ v4l2_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask,
- base::Unretained(this)));
+ base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
+ this));
}
-void VideoCaptureDeviceLinux::OnStopAndDeAllocate() {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
+void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() {
+ DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
- v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &type)) < 0) {
SetErrorState("VIDIOC_STREAMOFF failed");
return;
}
// We don't dare to deallocate the buffers if we can't stop the capture
// device.
- DeAllocateVideoBuffers();
+ if (!DeAllocateVideoBuffers())
+ SetErrorState("Failed to reset buffers");
// We need to close and open the device if we want to change the settings.
// Otherwise VIDIOC_S_FMT will return error. Sad but true.
@@ -352,140 +401,131 @@ void VideoCaptureDeviceLinux::OnStopAndDeAllocate() {
client_.reset();
}
-void VideoCaptureDeviceLinux::OnCaptureTask() {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
+void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation(int rotation) {
+ DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
+ DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
+ rotation_ = rotation;
+}
+
+void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() {
+ DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
if (!is_capturing_)
return;
- pollfd device_pfd;
+ pollfd device_pfd = {};
device_pfd.fd = device_fd_.get();
device_pfd.events = POLLIN;
-
const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs));
if (result < 0) {
SetErrorState("Poll failed");
return;
}
-
- // Check if poll did timeout.
+ // Check if poll() timed out; track the amount of times it did in a row and
+ // throw an error if it times out too many times.
if (result == 0) {
timeout_count_++;
if (timeout_count_ >= kContinuousTimeoutLimit) {
- SetErrorState(base::StringPrintf(
- "Continuous timeout %d times", timeout_count_));
+ SetErrorState("Multiple continuous timeouts while read-polling.");
timeout_count_ = 0;
return;
}
+ } else {
+ timeout_count_ = 0;
}
- timeout_count_ = 0;
-
// Check if the driver has filled a buffer.
if (device_pfd.revents & POLLIN) {
- v4l2_buffer buffer;
- memset(&buffer, 0, sizeof(buffer));
- buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buffer.memory = V4L2_MEMORY_MMAP;
- // Dequeue a buffer.
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) == 0) {
- client_->OnIncomingCapturedData(
- static_cast<uint8*>(buffer_pool_[buffer.index].start),
- buffer.bytesused,
- capture_format_,
- rotation_,
- base::TimeTicks::Now());
-
- // Enqueue the buffer again.
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) == -1) {
- SetErrorState(base::StringPrintf(
- "Failed to enqueue capture buffer errno %d", errno));
- }
- } else {
- SetErrorState(base::StringPrintf(
- "Failed to dequeue capture buffer errno %d", errno));
+ v4l2_buffer buffer = {};
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) {
+ SetErrorState("Failed to dequeue capture buffer");
return;
}
+ client_->OnIncomingCapturedData(
+ static_cast<uint8*>(buffer_pool_[buffer.index].start),
+ buffer.bytesused,
+ capture_format_,
+ rotation_,
+ base::TimeTicks::Now());
+
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0)
+ SetErrorState("Failed to enqueue capture buffer");
}
- v4l2_thread_.message_loop()->PostTask(
+ v4l2_task_runner_->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnCaptureTask,
- base::Unretained(this)));
+ base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
+ this));
}
-bool VideoCaptureDeviceLinux::AllocateVideoBuffers() {
- v4l2_requestbuffers r_buffer;
- memset(&r_buffer, 0, sizeof(r_buffer));
-
+bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateVideoBuffers() {
+ v4l2_requestbuffers r_buffer = {};
r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
r_buffer.memory = V4L2_MEMORY_MMAP;
r_buffer.count = kMaxVideoBuffers;
-
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
+ DLOG(ERROR) << "Error requesting MMAP buffers from V4L2";
return false;
}
-
- if (r_buffer.count > kMaxVideoBuffers) {
- r_buffer.count = kMaxVideoBuffers;
- }
-
+ DCHECK_EQ(r_buffer.count, kMaxVideoBuffers);
+ r_buffer.count = std::min(r_buffer.count, kMaxVideoBuffers);
buffer_pool_size_ = r_buffer.count;
-
- // Map the buffers.
- buffer_pool_ = new Buffer[r_buffer.count];
- for (unsigned int i = 0; i < r_buffer.count; i++) {
- v4l2_buffer buffer;
- memset(&buffer, 0, sizeof(buffer));
+ buffer_pool_ = new Buffer[buffer_pool_size_];
+ for (unsigned int i = 0; i < r_buffer.count; ++i) {
+ v4l2_buffer buffer = {};
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;
buffer.index = i;
-
+ buffer.length = 1;
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
+ DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
return false;
}
// Some devices require mmap() to be called with both READ and WRITE.
- // See crbug.com/178582.
+ // See http://crbug.com/178582.
buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
- MAP_SHARED, device_fd_.get(), buffer.m.offset);
+ MAP_SHARED, device_fd_.get(), buffer.m.offset);
if (buffer_pool_[i].start == MAP_FAILED) {
+ DLOG(ERROR) << "Error mmmap()ing a V4L2 buffer into userspace";
return false;
}
+
buffer_pool_[i].length = buffer.length;
// Enqueue the buffer in the drivers incoming queue.
if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) {
+ DLOG(ERROR)
+ << "Error enqueuing a V4L2 buffer back to the drivers incoming queue";
return false;
}
}
return true;
}
-void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() {
+bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() {
if (!buffer_pool_)
- return;
+ return true;
- // Unmaps buffers.
- for (int i = 0; i < buffer_pool_size_; i++) {
+ for (int i = 0; i < buffer_pool_size_; ++i)
munmap(buffer_pool_[i].start, buffer_pool_[i].length);
- }
- v4l2_requestbuffers r_buffer;
- memset(&r_buffer, 0, sizeof(r_buffer));
+
+ v4l2_requestbuffers r_buffer = {};
r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
r_buffer.memory = V4L2_MEMORY_MMAP;
r_buffer.count = 0;
-
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
- SetErrorState("Failed to reset buf.");
- }
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0)
+ return false;
delete [] buffer_pool_;
buffer_pool_ = NULL;
buffer_pool_size_ = 0;
+ return true;
}
-void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) {
- DCHECK(!v4l2_thread_.IsRunning() ||
- v4l2_thread_.message_loop() == base::MessageLoop::current());
+void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState(
+ const std::string& reason) {
+ DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
is_capturing_ = false;
client_->OnError(reason);
}
diff --git a/media/video/capture/linux/video_capture_device_linux.h b/media/video/capture/linux/video_capture_device_linux.h
index 50825e6..a52db26 100644
--- a/media/video/capture/linux/video_capture_device_linux.h
+++ b/media/video/capture/linux/video_capture_device_linux.h
@@ -20,6 +20,7 @@
namespace media {
+// Linux V4L2 implementation of VideoCaptureDevice.
class VideoCaptureDeviceLinux : public VideoCaptureDevice {
public:
static VideoPixelFormat V4l2FourCcToChromiumPixelFormat(uint32 v4l2_fourcc);
@@ -31,49 +32,21 @@ class VideoCaptureDeviceLinux : public VideoCaptureDevice {
// VideoCaptureDevice implementation.
void AllocateAndStart(const VideoCaptureParams& params,
scoped_ptr<Client> client) override;
-
void StopAndDeAllocate() override;
protected:
void SetRotation(int rotation);
- // Once |v4l2_thread_| is started, only called on that thread.
- void SetRotationOnV4L2Thread(int rotation);
-
private:
- // Buffers used to receive video frames from with v4l2.
- struct Buffer {
- Buffer() : start(0), length(0) {}
- void* start;
- size_t length;
- };
-
- // Called on the v4l2_thread_.
- void OnAllocateAndStart(int width,
- int height,
- float frame_rate,
- scoped_ptr<Client> client);
- void OnStopAndDeAllocate();
- void OnCaptureTask();
-
- bool AllocateVideoBuffers();
- void DeAllocateVideoBuffers();
- void SetErrorState(const std::string& reason);
+ // Internal delegate doing the actual capture setting, buffer allocation and
+ // circulacion with the V4L2 API. Created and deleted in the thread where
+ // VideoCaptureDeviceLinux lives but otherwise operating on |v4l2_thread_|.
+ class V4L2CaptureDelegate;
+ scoped_refptr<V4L2CaptureDelegate> capture_impl_;
- bool is_capturing_;
- scoped_ptr<VideoCaptureDevice::Client> client_;
- Name device_name_;
- base::ScopedFD device_fd_; // File descriptor for the opened camera device.
base::Thread v4l2_thread_; // Thread used for reading data from the device.
- Buffer* buffer_pool_;
- int buffer_pool_size_; // Number of allocated buffers.
- int timeout_count_;
- VideoCaptureFormat capture_format_;
- // Clockwise rotation in degrees. This value should be 0, 90, 180, or 270.
- // This is only used on |v4l2_thread_| when it is running, or the constructor
- // thread otherwise.
- int rotation_;
+ const Name device_name_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceLinux);
};