diff options
author | mcasas <mcasas@chromium.org> | 2014-12-19 09:25:11 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-19 17:25:54 +0000 |
commit | fe57aa97a267caa856a64a532f113379471de4dc (patch) | |
tree | aaf29801cf1cae145eed32aa735f2efeeb378a61 /media | |
parent | 20fffbee0845f144086cb82ff7b63ff31382d12c (diff) | |
download | chromium_src-fe57aa97a267caa856a64a532f113379471de4dc.zip chromium_src-fe57aa97a267caa856a64a532f113379471de4dc.tar.gz chromium_src-fe57aa97a267caa856a64a532f113379471de4dc.tar.bz2 |
Linux Video Capture: Extracted V4L2CaptureDelegate as a nested class of VideoCaptureDeviceLinux.
Following the rationale in the bug and the lack of
(opposing) comments to it, this CL refactors the
guts of VideoCaptureDeviceLinux.
With this CL VCDLinux does the VCD implementation,
creates the V4L2 thread and the V4L2CaptureDelegate.
The V4L2CaptureDelegate is ref counted, which allows
for real PostTask (now is all base::Unretained()).
The Delegate lives in V4L2Thread, VCDLinux in
Capture Thread.
+------------------------------------------+
| VideoCaptureDeviceLinux |
| +----------------------+|
| <<ref_cnt>> ---->| V4L2CaptureDelegate ||
| | ||
| +----------------------+|
+------------------------------------------+
BUG=441836
Review URL: https://codereview.chromium.org/808483006
Cr-Commit-Position: refs/heads/master@{#309218}
Diffstat (limited to 'media')
-rw-r--r-- | media/video/capture/linux/video_capture_device_linux.cc | 364 | ||||
-rw-r--r-- | media/video/capture/linux/video_capture_device_linux.h | 41 |
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); }; |