diff options
Diffstat (limited to 'media/video/capture/linux')
-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); }; |