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

Unified Diff: media/video/capture/linux/v4l2_capture_delegate.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
Index: media/video/capture/linux/v4l2_capture_delegate.cc
diff --git a/media/video/capture/linux/video_capture_device_linux.cc b/media/video/capture/linux/v4l2_capture_delegate.cc
similarity index 34%
copy from media/video/capture/linux/video_capture_device_linux.cc
copy to media/video/capture/linux/v4l2_capture_delegate.cc
index b9fb195f8ae4b0e670b7c482bd3a5245460f35c8..dc46f1e45cebd107a2415def3ed8d79a893e0dcb 100644
--- a/media/video/capture/linux/video_capture_device_linux.cc
+++ b/media/video/capture/linux/v4l2_capture_delegate.cc
@@ -1,277 +1,172 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/video/capture/linux/video_capture_device_linux.h"
+#include "media/video/capture/linux/v4l2_capture_delegate.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/fcntl.h>
#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/base/bind_to_current_loop.h"
+#include "media/video/capture/linux/v4l2_capture_delegate_multi_plane.h"
+#include "media/video/capture/linux/v4l2_capture_delegate_single_plane.h"
+#include "media/video/capture/linux/video_capture_device_linux.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 };
+const int 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 };
+const int kContinuousTimeoutLimit = 10;
+// MJPEG is preferred if the requested width or height is larger than this.
+const int kMjpegWidth = 640;
+const int 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
+const int kTypicalFramerate = 30;
+
+// V4L2 color formats supported by V4L2CaptureDelegate derived classes.
+// This list is ordered by precedence of use -- but see caveats for MJPEG.
+static struct{
+ uint32_t fourcc;
+ VideoPixelFormat pixel_format;
+ size_t num_planes;
+} const kSupportedFormatsAndPlanarity[] = {
+ {V4L2_PIX_FMT_YUV420, PIXEL_FORMAT_I420, 1},
+ {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2, 1},
+ {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY, 1},
+#if !defined(OS_OPENBSD)
+ // TODO(mcasas): add V4L2_PIX_FMT_YVU420M when available in bots.
+ {V4L2_PIX_FMT_YUV420M, PIXEL_FORMAT_I420, 3},
+#endif
+ // MJPEG is usually sitting fairly low since we don't want to have to decode.
+ // However, is needed for large resolutions due to USB bandwidth limitations,
+ // so GetListOfUsableFourCcs() can duplicate it on top, see that method.
+ {V4L2_PIX_FMT_MJPEG, PIXEL_FORMAT_MJPEG, 1},
+ // JPEG works as MJPEG on some gspca webcams from field reports, see
+ // https://code.google.com/p/webrtc/issues/detail?id=529, put it as the least
+ // preferred format.
+ {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1},
};
-// USB VID and PID are both 4 bytes long.
-static const size_t kVidPidSize = 4;
-
-// /sys/class/video4linux/video{N}/device is a symlink to the corresponding
-// USB device info directory.
-static const char kVidPathTemplate[] =
- "/sys/class/video4linux/%s/device/../idVendor";
-static const char kPidPathTemplate[] =
- "/sys/class/video4linux/%s/device/../idProduct";
+// static
+scoped_refptr<V4L2CaptureDelegate>
+V4L2CaptureDelegate::CreateV4L2CaptureDelegate(
+ const VideoCaptureDevice::Name& device_name,
+ const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
+ int power_line_frequency) {
+ switch (device_name.capture_api_type()) {
+ case VideoCaptureDevice::Name::V4L2_SINGLE_PLANE:
+ return make_scoped_refptr(new V4L2CaptureDelegateSinglePlane(
+ device_name, v4l2_task_runner, power_line_frequency));
+ case VideoCaptureDevice::Name::V4L2_MULTI_PLANE:
+#if !defined(OS_OPENBSD)
+ return make_scoped_refptr(new V4L2CaptureDelegateMultiPlane(
+ device_name, v4l2_task_runner, power_line_frequency));
+ default:
+#endif
+ NOTIMPLEMENTED() << "Unknown V4L2 capture API type";
+ return scoped_refptr<V4L2CaptureDelegate>();
+ }
+}
-static bool ReadIdFile(const std::string path, std::string* id) {
- char id_buf[kVidPidSize];
- FILE* file = fopen(path.c_str(), "rb");
- if (!file)
- return false;
- const bool success = fread(id_buf, kVidPidSize, 1, file) == 1;
- fclose(file);
- if (!success)
- return false;
- id->append(id_buf, kVidPidSize);
- return true;
+//static
+size_t V4L2CaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc) {
+ for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
+ if (fourcc_and_pixel_format.fourcc == fourcc)
+ return fourcc_and_pixel_format.num_planes;
+ }
+ DVLOG(1) << "Unknown fourcc " << FourccToString(fourcc);
+ return 0;
}
-// This function translates Video4Linux pixel formats to Chromium pixel formats,
-// should only support those listed in GetListOfUsableFourCCs.
// 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) {
+VideoPixelFormat V4L2CaptureDelegate::V4l2FourCcToChromiumPixelFormat(
+ uint32_t v4l2_fourcc) {
+ for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) {
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);
+ // Not finding a pixel format is OK during device capabilities enumeration.
+ // Let the caller decide if PIXEL_FORMAT_UNKNOWN is an error or not.
+ DVLOG(1) << "Unsupported pixel format: " << FourccToString(v4l2_fourcc);
return PIXEL_FORMAT_UNKNOWN;
}
// static
-std::list<int> 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;
-}
+std::list<uint32_t> V4L2CaptureDelegate::GetListOfUsableFourCcs(
+ bool prefer_mjpeg) {
+ std::list<uint32_t> supported_formats;
+ for (const auto& format : kSupportedFormatsAndPlanarity)
+ supported_formats.push_back(format.fourcc);
-const std::string VideoCaptureDevice::Name::GetModel() const {
- // |unique_id| is of the form "/dev/video2". |file_name| is "video2".
- const std::string dev_dir = "/dev/";
- DCHECK_EQ(0, unique_id_.compare(0, dev_dir.length(), dev_dir));
- const std::string file_name =
- unique_id_.substr(dev_dir.length(), unique_id_.length());
-
- const std::string vidPath =
- base::StringPrintf(kVidPathTemplate, file_name.c_str());
- const std::string pidPath =
- base::StringPrintf(kPidPathTemplate, file_name.c_str());
-
- std::string usb_id;
- if (!ReadIdFile(vidPath, &usb_id))
- return "";
- usb_id.append(":");
- if (!ReadIdFile(pidPath, &usb_id))
- return "";
-
- return usb_id;
-}
+ // Duplicate MJPEG on top of the list depending on |prefer_mjpeg|.
+ if (prefer_mjpeg)
+ supported_formats.push_front(V4L2_PIX_FMT_MJPEG);
-VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name)
- : v4l2_thread_("V4L2CaptureThread"),
- device_name_(device_name) {
+ return supported_formats;
}
-VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
- // Check if the thread is running.
- // This means that the device has not been StopAndDeAllocate()d properly.
- DCHECK(!v4l2_thread_.IsRunning());
- v4l2_thread_.Stop();
+//static
+std::string V4L2CaptureDelegate::FourccToString(uint32_t fourcc) {
+ return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF,
+ (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF);
}
-void VideoCaptureDeviceLinux::AllocateAndStart(
- const VideoCaptureParams& params,
- scoped_ptr<VideoCaptureDevice::Client> client) {
- 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::V4L2CaptureDelegate::AllocateAndStart,
- capture_impl_,
- params.requested_format.frame_size.width(),
- params.requested_format.frame_size.height(),
- params.requested_format.frame_rate,
- base::Passed(&client)));
+V4L2CaptureDelegate::BufferTracker::BufferTracker() {
}
-void VideoCaptureDeviceLinux::StopAndDeAllocate() {
- if (!v4l2_thread_.IsRunning())
- return; // Wrong state.
- v4l2_thread_.message_loop()->PostTask(
- FROM_HERE,
- base::Bind(
- &VideoCaptureDeviceLinux::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;
+V4L2CaptureDelegate::BufferTracker::~BufferTracker() {
+ for (const auto& plane : planes_) {
+ if (plane.start == nullptr)
+ continue;
+ const int result = munmap(plane.start, plane.length);
+ PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer";
+ }
}
-void VideoCaptureDeviceLinux::SetRotation(int rotation) {
- if (v4l2_thread_.IsRunning()) {
- v4l2_thread_.message_loop()->PostTask(
- FROM_HERE,
- base::Bind(
- &VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation,
- capture_impl_,
- rotation));
- }
+void V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start,
+ size_t length) {
+ Plane plane;
+ plane.start = start;
+ plane.length = length;
+ planes_.push_back(plane);
}
-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),
+V4L2CaptureDelegate::V4L2CaptureDelegate(
+ const VideoCaptureDevice::Name& device_name,
+ const scoped_refptr<base::SingleThreadTaskRunner>& v4l2_task_runner,
+ int power_line_frequency)
+ : capture_type_((device_name.capture_api_type() ==
+ VideoCaptureDevice::Name::V4L2_SINGLE_PLANE)
+ ? V4L2_BUF_TYPE_VIDEO_CAPTURE
+ : V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
+ v4l2_task_runner_(v4l2_task_runner),
device_name_(device_name),
- buffer_pool_(NULL),
- buffer_pool_size_(0),
- timeout_count_(0),
power_line_frequency_(power_line_frequency),
+ is_capturing_(false),
+ timeout_count_(0),
rotation_(0) {
}
-VideoCaptureDeviceLinux::V4L2CaptureDelegate::~V4L2CaptureDelegate() {
- DCHECK(!client_);
+V4L2CaptureDelegate::~V4L2CaptureDelegate() {
}
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
+void V4L2CaptureDelegate::AllocateAndStart(
int width,
int height,
float frame_rate,
- scoped_ptr<Client> client) {
+ scoped_ptr<VideoCaptureDevice::Client> client) {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
DCHECK(client);
client_ = client.Pass();
@@ -285,8 +180,10 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
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)))) {
+ ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ||
+ cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) &&
+ !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) &&
+ !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) {
device_fd_.reset();
SetErrorState("This is not a V4L2 video capture device");
return;
@@ -294,12 +191,12 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
// 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();
+ const std::list<uint32_t>& desired_v4l2_formats =
+ GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight);
+ std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end();
v4l2_fmtdesc fmtdesc = {};
- fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmtdesc.type = capture_type_;
for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0;
++fmtdesc.index) {
best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat);
@@ -309,20 +206,28 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
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) {
+ DVLOG(1) << "Chosen pixel format is " << FourccToString(*best);
+
+ video_fmt_.type = capture_type_;
+ if (!FillV4L2Format(&video_fmt_, width, height, *best)) {
+ SetErrorState("Failed filling in V4L2 Format");
+ return;
+ }
+
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) {
SetErrorState("Failed to set video capture format");
return;
}
+ const VideoPixelFormat pixel_format =
+ V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat);
+ if (pixel_format == PIXEL_FORMAT_UNKNOWN) {
+ SetErrorState("Unsupported pixel format");
+ return;
+ }
// Set capture framerate in the form of capture interval.
v4l2_streamparm streamparm = {};
- streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ streamparm.type = capture_type_;
// 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.
@@ -330,9 +235,9 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
// |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);
+ 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) {
@@ -349,29 +254,40 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
// 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)) {
+ if ((power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ||
+ (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) ||
+ (power_line_frequency_ == V4L2_CID_POWER_LINE_FREQUENCY_AUTO)) {
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));
+ control.value = power_line_frequency_;
+ const int retval =
+ HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
+ if (retval != 0)
+ DVLOG(1) << "Error setting power line frequency removal";
}
- capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
- video_fmt.fmt.pix.height);
+ 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);
+ capture_format_.pixel_format = pixel_format;
- if (!AllocateVideoBuffers()) {
- SetErrorState("Allocate buffer failed (Cannot recover from this error)");
+ v4l2_requestbuffers r_buffer = {};
+ r_buffer.type = capture_type_;
+ r_buffer.memory = V4L2_MEMORY_MMAP;
+ r_buffer.count = kNumVideoBuffers;
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) {
+ SetErrorState("Error requesting MMAP buffers from V4L2");
return;
}
+ for (unsigned int i = 0; i < r_buffer.count; ++i) {
+ if (!MapAndQueueBuffer(i)) {
+ SetErrorState("Allocate buffer failed");
+ return;
+ }
+ }
- const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &type)) < 0) {
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_))
+ < 0) {
SetErrorState("VIDIOC_STREAMON failed");
return;
}
@@ -379,38 +295,74 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::AllocateAndStart(
is_capturing_ = true;
// Post task to start fetching frames from v4l2.
v4l2_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture,
- this));
+ FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this));
}
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::StopAndDeAllocate() {
+void 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) {
+ // The order is important: stop streaming, clear |buffer_pool_|,
+ // thus munmap()ing the v4l2_buffers, and then return them to the OS.
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_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.
+ buffer_tracker_pool_.clear();
+
+ v4l2_requestbuffers r_buffer = {};
+ r_buffer.type = capture_type_;
+ 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 VIDIOC_REQBUFS with count = 0");
+
+ // At this point we can close the device.
+ // This is also needed for correctly changing settings later via VIDIOC_S_FMT.
device_fd_.reset();
is_capturing_ = false;
client_.reset();
}
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetRotation(int rotation) {
+void V4L2CaptureDelegate::SetRotation(int rotation) {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
rotation_ = rotation;
}
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() {
+bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) {
+ v4l2_buffer buffer;
+ FillV4L2Buffer(&buffer, index);
+
+ if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYBUF, &buffer)) < 0) {
+ DLOG(ERROR) << "Error querying status of a MMAP V4L2 buffer";
+ return false;
+ }
+
+ const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker();
+ if (!buffer_tracker->Init(device_fd_.get(), buffer)) {
+ DLOG(ERROR) << "Error creating BufferTracker";
+ return false;
+ }
+ buffer_tracker_pool_.push_back(buffer_tracker);
+
+ // 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 into the driver";
+ return false;
+ }
+ return true;
+}
+
+void V4L2CaptureDelegate::FillV4L2Buffer(v4l2_buffer* buffer,
+ int i) const {
+ memset(buffer, 0, sizeof(*buffer));
+ buffer->memory = V4L2_MEMORY_MMAP;
+ buffer->index = i;
+ FinishFillingV4L2Buffer(buffer);
+}
+
+void V4L2CaptureDelegate::DoCapture() {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
if (!is_capturing_)
return;
@@ -436,96 +388,29 @@ void VideoCaptureDeviceLinux::V4L2CaptureDelegate::DoCapture() {
timeout_count_ = 0;
}
- // Check if the driver has filled a buffer.
+ // Deenqueue, send and reenqueue a buffer if the driver has filled one in.
if (device_pfd.revents & POLLIN) {
- v4l2_buffer buffer = {};
- buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2_buffer buffer;
+ FillV4L2Buffer(&buffer, 0);
+
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;
- }
+ SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_);
- 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;
+ SetErrorState("Failed to enqueue capture buffer");
+ return;
}
}
- 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;
+ v4l2_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this));
}
-void VideoCaptureDeviceLinux::V4L2CaptureDelegate::SetErrorState(
- const std::string& reason) {
+void V4L2CaptureDelegate::SetErrorState(const std::string& reason) {
DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
is_capturing_ = false;
client_->OnError(reason);
« no previous file with comments | « media/video/capture/linux/v4l2_capture_delegate.h ('k') | media/video/capture/linux/v4l2_capture_delegate_multi_plane.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698