| 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);
|
|
|