Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(438)

Unified Diff: media/video/capture/linux/video_capture_device_linux.cc

Issue 967793002: Linux Video Capture: Add V4L2VideoCaptureDelegate{Single,Multi}Plane. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: magjed@ comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/video/capture/linux/video_capture_device_linux.h ('k') | media/video/capture/video_capture_device.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/video/capture/linux/video_capture_device_linux.cc
diff --git a/media/video/capture/linux/video_capture_device_linux.cc b/media/video/capture/linux/video_capture_device_linux.cc
index b9fb195f8ae4b0e670b7c482bd3a5245460f35c8..500be2c8fff3285669707ca8be10eecbc7a88e52 100644
--- a/media/video/capture/linux/video_capture_device_linux.cc
+++ b/media/video/capture/linux/video_capture_device_linux.cc
@@ -4,101 +4,21 @@
#include "media/video/capture/linux/video_capture_device_linux.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
#if defined(OS_OPENBSD)
#include <sys/videoio.h>
#else
#include <linux/videodev2.h>
#endif
-#include <sys/ioctl.h>
-#include <sys/mman.h>
#include <list>
#include <string>
#include "base/bind.h"
-#include "base/files/file_enumerator.h"
-#include "base/files/scoped_file.h"
-#include "base/posix/eintr_wrapper.h"
#include "base/strings/stringprintf.h"
+#include "media/video/capture/linux/v4l2_capture_delegate.h"
namespace media {
-#define GET_V4L2_FOURCC_CHAR(a, index) ((char)( ((a) >> (8 * index)) & 0xff))
-
-// Desired number of video buffers to allocate. The actual number of allocated
-// buffers by v4l2 driver can be higher or lower than this number.
-// kNumVideoBuffers should not be too small, or Chrome may not return enough
-// buffers back to driver in time.
-const uint32 kNumVideoBuffers = 4;
-// 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.
-enum { kContinuousTimeoutLimit = 10 };
-// MJPEG is preferred if the width or height is larger than this.
-enum { kMjpegWidth = 640 };
-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,
- V4L2_PIX_FMT_YUYV,
- V4L2_PIX_FMT_UYVY
-};
-
// USB VID and PID are both 4 bytes long.
static const size_t kVidPidSize = 4;
@@ -122,48 +42,18 @@ static bool ReadIdFile(const std::string path, std::string* id) {
return true;
}
-// This function translates Video4Linux pixel formats to Chromium pixel formats,
-// should only support those listed in GetListOfUsableFourCCs.
+// Translates Video4Linux pixel formats to Chromium pixel formats.
// static
VideoPixelFormat VideoCaptureDeviceLinux::V4l2FourCcToChromiumPixelFormat(
uint32 v4l2_fourcc) {
- 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;
- }
- 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;
+ return V4L2CaptureDelegate::V4l2FourCcToChromiumPixelFormat(v4l2_fourcc);
}
+// Gets a list of usable Four CC formats prioritised.
// static
-std::list<int> VideoCaptureDeviceLinux::GetListOfUsableFourCCs(
+std::list<uint32_t> VideoCaptureDeviceLinux::GetListOfUsableFourCCs(
bool favour_mjpeg) {
- std::list<int> fourccs;
- for (size_t i = 0; i < arraysize(kV4l2RawFmts); ++i)
- fourccs.push_back(kV4l2RawFmts[i]);
- if (favour_mjpeg)
- fourccs.push_front(V4L2_PIX_FMT_MJPEG);
- else
- fourccs.push_back(V4L2_PIX_FMT_MJPEG);
-
- // JPEG works as MJPEG on some gspca webcams from field reports.
- // Put it as the least preferred format.
- fourccs.push_back(V4L2_PIX_FMT_JPEG);
- return fourccs;
+ return V4L2CaptureDelegate::GetListOfUsableFourCcs(favour_mjpeg);
}
const std::string VideoCaptureDevice::Name::GetModel() const {
@@ -207,18 +97,21 @@ void VideoCaptureDeviceLinux::AllocateAndStart(
if (v4l2_thread_.IsRunning())
return; // Wrong state.
v4l2_thread_.Start();
- capture_impl_ = new V4L2CaptureDelegate(device_name_,
- v4l2_thread_.message_loop_proxy(),
- GetPowerLineFrequencyForLocation());
+
+ const int line_frequency =
+ TranslatePowerLineFrequencyToV4L2(GetPowerLineFrequencyForLocation());
+ capture_impl_ = V4L2CaptureDelegate::CreateV4L2CaptureDelegate(
+ device_name_, v4l2_thread_.message_loop_proxy(), line_frequency);
+ if (!capture_impl_) {
+ client->OnError("Failed to create VideoCaptureDelegate");
+ return;
+ }
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- 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)));
+ base::Bind(&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() {
@@ -226,309 +119,31 @@ void VideoCaptureDeviceLinux::StopAndDeAllocate() {
return; // Wrong state.
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(
- &VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate,
- capture_impl_));
+ base::Bind(&V4L2CaptureDelegate::StopAndDeAllocate, capture_impl_));
v4l2_thread_.Stop();
- // 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::V4L2CaptureDelegate::SetRotation,
- capture_impl_,
- rotation));
+ FROM_HERE, base::Bind(&V4L2CaptureDelegate::SetRotation,
+ capture_impl_, 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) {
-}
-
-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 file.");
- return;
- }
-
- 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)))) {
- device_fd_.reset();
- SetErrorState("This is not a V4L2 video capture device");
- return;
- }
-
- // Get supported video formats in preferred order.
- // For large resolutions, favour mjpeg over raw formats.
- const std::list<int>& desired_v4l2_formats =
- GetListOfUsableFourCCs(width > kMjpegWidth || height > kMjpegHeight);
- std::list<int>::const_iterator best = desired_v4l2_formats.end();
-
- v4l2_fmtdesc fmtdesc = {};
- fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
- ++fmtdesc.index) {
- best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
- }
- if (best == desired_v4l2_formats.end()) {
- SetErrorState("Failed to find a supported camera format.");
- return;
- }
-
- 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("Failed to set video capture format");
- return;
- }
-
- // Set capture framerate in the form of capture interval.
- 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) {
- // Now check if the device is able to accept a capture framerate set.
- if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
- // |frame_rate| is float, approximate by a fraction.
- streamparm.parm.capture.timeperframe.numerator =
- media::kFrameRatePrecision;
- streamparm.parm.capture.timeperframe.denominator = (frame_rate) ?
- (frame_rate * media::kFrameRatePrecision) :
- (kTypicalFramerate * media::kFrameRatePrecision);
-
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) <
- 0) {
- SetErrorState("Failed to set camera framerate");
- return;
- }
- DVLOG(2) << "Actual camera driverframerate: "
- << streamparm.parm.capture.timeperframe.denominator << "/"
- << streamparm.parm.capture.timeperframe.numerator;
- }
- }
- // TODO(mcasas): what should be done if the camera driver does not allow
- // framerate configuration, or the actual one is different from the desired?
-
- // Set anti-banding/anti-flicker to 50/60Hz. May fail due to not supported
- // operation (|errno| == EINVAL in this case) or plain failure.
- 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;
- HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
- }
-
- capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
- video_fmt.fmt.pix.height);
- capture_format_.frame_rate = frame_rate;
- capture_format_.pixel_format =
- V4l2FourCcToChromiumPixelFormat(video_fmt.fmt.pix.pixelformat);
-
- if (!AllocateVideoBuffers()) {
- SetErrorState("Allocate buffer failed (Cannot recover from this error)");
- return;
- }
-
- 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_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
- this));
-}
-
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() {
- DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
-
- 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.
- 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.
- device_fd_.reset();
- is_capturing_ = false;
- client_.reset();
-}
-
-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 = {};
- 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() 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("Multiple continuous timeouts while read-polling.");
- timeout_count_ = 0;
- return;
- }
- } else {
- timeout_count_ = 0;
- }
-
- // Check if the driver has filled a buffer.
- if (device_pfd.revents & POLLIN) {
- 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_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
- this));
-}
-
-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 = kNumVideoBuffers;
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
- DLOG(ERROR) << "Error requesting MMAP buffers from V4L2";
- return false;
- }
- buffer_pool_size_ = r_buffer.count;
- 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 http://crbug.com/178582.
- buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
- 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;
- }
+// static
+int VideoCaptureDeviceLinux::TranslatePowerLineFrequencyToV4L2(int frequency) {
+ switch (frequency) {
+ case kPowerLine50Hz:
+ return V4L2_CID_POWER_LINE_FREQUENCY_50HZ;
+ case kPowerLine60Hz:
+ return V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+ default:
+ // If we have no idea of the frequency, at least try and set it to AUTO.
+ return V4L2_CID_POWER_LINE_FREQUENCY_AUTO;
}
- return true;
-}
-
-bool VideoCaptureDeviceLinux::V4L2CaptureDelegate::DeAllocateVideoBuffers() {
- if (!buffer_pool_)
- return true;
-
- for (int i = 0; i < buffer_pool_size_; ++i)
- munmap(buffer_pool_[i].start, buffer_pool_[i].length);
-
- 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)
- return false;
-
- delete [] buffer_pool_;
- buffer_pool_ = NULL;
- buffer_pool_size_ = 0;
- return true;
-}
-
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState(
- const std::string& reason) {
- DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
- is_capturing_ = false;
- client_->OnError(reason);
}
} // namespace media
« no previous file with comments | « media/video/capture/linux/video_capture_device_linux.h ('k') | media/video/capture/video_capture_device.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698