Chromium Code Reviews| Index: media/capture/video/linux/v4l2_capture_delegate.cc |
| diff --git a/media/capture/video/linux/v4l2_capture_delegate.cc b/media/capture/video/linux/v4l2_capture_delegate.cc |
| index 612672b48aa132201a7a7789b087078e4689661e..2804999cbee0761646b27796422788ce71bf37ec 100644 |
| --- a/media/capture/video/linux/v4l2_capture_delegate.cc |
| +++ b/media/capture/video/linux/v4l2_capture_delegate.cc |
| @@ -16,8 +16,6 @@ |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "media/base/bind_to_current_loop.h" |
| -#include "media/capture/video/linux/v4l2_capture_delegate_multi_plane.h" |
| -#include "media/capture/video/linux/v4l2_capture_delegate_single_plane.h" |
| #include "media/capture/video/linux/video_capture_device_linux.h" |
| namespace media { |
| @@ -48,44 +46,76 @@ static struct { |
| {V4L2_PIX_FMT_YUYV, PIXEL_FORMAT_YUY2, 1}, |
| {V4L2_PIX_FMT_UYVY, PIXEL_FORMAT_UYVY, 1}, |
| {V4L2_PIX_FMT_RGB24, PIXEL_FORMAT_RGB24, 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. |
| + // decode. However, is needed for large resolutions due to USB bandwidth |
|
perkj_chrome
2016/02/12 11:01:40
nit s it is needed....
mcasas
2016/02/12 21:32:43
Done.
|
| + // 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. |
| + // least preferred format. |
| {V4L2_PIX_FMT_JPEG, PIXEL_FORMAT_MJPEG, 1}, |
| }; |
| -// 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>(); |
| - } |
| +// Fill in |format| with the given parameters. |
| +static void FillV4L2Format(v4l2_format* format, |
| + uint32_t width, |
| + uint32_t height, |
| + uint32_t pixelformat_fourcc) { |
| + memset(format, 0, sizeof(*format)); |
| + format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| + format->fmt.pix.width = width; |
| + format->fmt.pix.height = height; |
| + format->fmt.pix.pixelformat = pixelformat_fourcc; |
| +} |
| + |
| +// Fills all parts of |buffer|. |
| +static void FillV4L2Buffer(v4l2_buffer* buffer, int index) { |
| + memset(buffer, 0, sizeof(*buffer)); |
| + buffer->memory = V4L2_MEMORY_MMAP; |
| + buffer->index = index; |
| + buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| +} |
| + |
| +static void FillV4L2RequestBuffer(v4l2_requestbuffers* request_buffer, |
| + int count) { |
| + memset(request_buffer, 0, sizeof(*request_buffer)); |
| + request_buffer->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| + request_buffer->memory = V4L2_MEMORY_MMAP; |
| + request_buffer->count = count; |
| +} |
| + |
| +// Returns the input |fourcc| as a std::string four char representation. |
| +static std::string FourccToString(uint32_t fourcc) { |
| + return base::StringPrintf("%c%c%c%c", fourcc & 0xFF, (fourcc >> 8) & 0xFF, |
| + (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF); |
| } |
| +// Class keeping track of a SPLANE V4L2 buffer, mmap()ed on construction and |
| +// munmap()ed on destruction. |
| +class V4L2CaptureDelegate::BufferTracker |
| + : public base::RefCounted<BufferTracker> { |
| + public: |
| + BufferTracker(); |
| + // Abstract method to mmap() given |fd| according to |buffer|. |
| + bool Init(int fd, const v4l2_buffer& buffer); |
| + |
| + const uint8_t* start() const { return start_; } |
| + size_t payload_size() const { return payload_size_; } |
| + void set_payload_size(size_t payload_size) { |
| + DCHECK_LE(payload_size, length_); |
| + payload_size_ = payload_size; |
| + } |
| + |
| + private: |
| + friend class base::RefCounted<BufferTracker>; |
| + virtual ~BufferTracker(); |
| + |
| + uint8_t* start_; |
| + size_t length_; |
| + size_t payload_size_; |
| +}; |
| + |
| // static |
| size_t V4L2CaptureDelegate::GetNumPlanesForFourCc(uint32_t fourcc) { |
| for (const auto& fourcc_and_pixel_format : kSupportedFormatsAndPlanarity) { |
| @@ -124,51 +154,16 @@ std::list<uint32_t> V4L2CaptureDelegate::GetListOfUsableFourCcs( |
| return supported_formats; |
| } |
| -// 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); |
| -} |
| - |
| -V4L2CaptureDelegate::BufferTracker::BufferTracker() { |
| -} |
| - |
| -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 V4L2CaptureDelegate::BufferTracker::AddMmapedPlane(uint8_t* const start, |
| - size_t length) { |
| - Plane plane; |
| - plane.start = start; |
| - plane.length = length; |
| - plane.payload_size = 0; |
| - planes_.push_back(plane); |
| -} |
| - |
| 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), |
| + : v4l2_task_runner_(v4l2_task_runner), |
| device_name_(device_name), |
| power_line_frequency_(power_line_frequency), |
| is_capturing_(false), |
| timeout_count_(0), |
| - rotation_(0) { |
| -} |
| - |
| -V4L2CaptureDelegate::~V4L2CaptureDelegate() { |
| -} |
| + rotation_(0) {} |
| void V4L2CaptureDelegate::AllocateAndStart( |
| int width, |
| @@ -188,23 +183,21 @@ void 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_CAPTURE_MPLANE) && |
| - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && |
| - !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { |
| + ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && |
| + !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)))) { |
| device_fd_.reset(); |
| SetErrorState(FROM_HERE, "This is not a V4L2 video capture device"); |
| return; |
| } |
| - // Get supported video formats in preferred order. |
| - // For large resolutions, favour mjpeg over raw formats. |
| + // Get supported video formats in preferred order. For large resolutions, |
| + // favour mjpeg over raw formats. |
| 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 = capture_type_; |
| + 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); |
| @@ -215,12 +208,7 @@ void V4L2CaptureDelegate::AllocateAndStart( |
| } |
| DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); |
| - |
| - video_fmt_.type = capture_type_; |
| - if (!FillV4L2Format(&video_fmt_, width, height, *best)) { |
| - SetErrorState(FROM_HERE, "Failed filling in V4L2 Format"); |
| - return; |
| - } |
| + FillV4L2Format(&video_fmt_, width, height, *best); |
| if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { |
| SetErrorState(FROM_HERE, "Failed to set video capture format"); |
| @@ -235,7 +223,7 @@ void V4L2CaptureDelegate::AllocateAndStart( |
| // Set capture framerate in the form of capture interval. |
| v4l2_streamparm streamparm = {}; |
| - streamparm.type = capture_type_; |
| + 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. |
| @@ -279,10 +267,8 @@ void V4L2CaptureDelegate::AllocateAndStart( |
| capture_format_.frame_rate = frame_rate; |
| capture_format_.pixel_format = pixel_format; |
| - v4l2_requestbuffers r_buffer = {}; |
| - r_buffer.type = capture_type_; |
| - r_buffer.memory = V4L2_MEMORY_MMAP; |
| - r_buffer.count = kNumVideoBuffers; |
| + v4l2_requestbuffers r_buffer; |
| + FillV4L2RequestBuffer(&r_buffer, kNumVideoBuffers); |
| if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
| SetErrorState(FROM_HERE, "Error requesting MMAP buffers from V4L2"); |
| return; |
| @@ -294,7 +280,8 @@ void V4L2CaptureDelegate::AllocateAndStart( |
| } |
| } |
| - if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < |
| + v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| + if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type)) < |
| 0) { |
| SetErrorState(FROM_HERE, "VIDIOC_STREAMON failed"); |
| return; |
| @@ -303,14 +290,15 @@ void V4L2CaptureDelegate::AllocateAndStart( |
| is_capturing_ = true; |
| // Post task to start fetching frames from v4l2. |
| v4l2_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| + FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, AsWeakPtr())); |
| } |
| void V4L2CaptureDelegate::StopAndDeAllocate() { |
| DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
| // 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_)) < |
| + v4l2_buf_type capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| + if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type)) < |
| 0) { |
| SetErrorState(FROM_HERE, "VIDIOC_STREAMOFF failed"); |
| return; |
| @@ -318,10 +306,8 @@ void V4L2CaptureDelegate::StopAndDeAllocate() { |
| buffer_tracker_pool_.clear(); |
| - v4l2_requestbuffers r_buffer = {}; |
| - r_buffer.type = capture_type_; |
| - r_buffer.memory = V4L2_MEMORY_MMAP; |
| - r_buffer.count = 0; |
| + v4l2_requestbuffers r_buffer; |
| + FillV4L2RequestBuffer(&r_buffer, 0); |
| if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
| SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); |
| @@ -338,6 +324,8 @@ void V4L2CaptureDelegate::SetRotation(int rotation) { |
| rotation_ = rotation; |
| } |
| +V4L2CaptureDelegate::~V4L2CaptureDelegate() {} |
| + |
| bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { |
| v4l2_buffer buffer; |
| FillV4L2Buffer(&buffer, index); |
| @@ -347,7 +335,7 @@ bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { |
| return false; |
| } |
| - const scoped_refptr<BufferTracker>& buffer_tracker = CreateBufferTracker(); |
| + const scoped_refptr<BufferTracker> buffer_tracker(new BufferTracker()); |
| if (!buffer_tracker->Init(device_fd_.get(), buffer)) { |
| DLOG(ERROR) << "Error creating BufferTracker"; |
| return false; |
| @@ -362,13 +350,6 @@ bool V4L2CaptureDelegate::MapAndQueueBuffer(int index) { |
| 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_) |
| @@ -406,8 +387,12 @@ void V4L2CaptureDelegate::DoCapture() { |
| return; |
| } |
| - SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); |
| - SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); |
| + buffer_tracker_pool_[buffer.index]->set_payload_size(buffer.bytesused); |
| + const scoped_refptr<BufferTracker>& buffer_tracker = |
| + buffer_tracker_pool_[buffer.index]; |
| + client_->OnIncomingCapturedData( |
| + buffer_tracker->start(), buffer_tracker->payload_size(), |
| + capture_format_, rotation_, base::TimeTicks::Now()); |
| if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
| SetErrorState(FROM_HERE, "Failed to enqueue capture buffer"); |
| @@ -416,7 +401,7 @@ void V4L2CaptureDelegate::DoCapture() { |
| } |
| v4l2_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
| + FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, AsWeakPtr())); |
|
perkj_chrome
2016/02/12 11:01:40
If I remember the comments correct. WeaPtrs are no
mcasas
2016/02/12 21:32:43
Added a TODO somewhere and leaving this for
anothe
|
| } |
| void V4L2CaptureDelegate::SetErrorState( |
| @@ -427,4 +412,29 @@ void V4L2CaptureDelegate::SetErrorState( |
| client_->OnError(from_here, reason); |
| } |
| +V4L2CaptureDelegate::BufferTracker::BufferTracker() {} |
| + |
| +V4L2CaptureDelegate::BufferTracker::~BufferTracker() { |
| + if (start_ == nullptr) |
| + return; |
| + const int result = munmap(start_, length_); |
| + PLOG_IF(ERROR, result < 0) << "Error munmap()ing V4L2 buffer"; |
| +} |
| + |
| +bool V4L2CaptureDelegate::BufferTracker::Init(int fd, |
| + const v4l2_buffer& buffer) { |
| + // Some devices require mmap() to be called with both READ and WRITE. |
| + // See http://crbug.com/178582. |
| + void* const start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, |
| + MAP_SHARED, fd, buffer.m.offset); |
| + if (start == MAP_FAILED) { |
| + DLOG(ERROR) << "Error mmap()ing a V4L2 buffer into userspace"; |
| + return false; |
| + } |
| + start_ = static_cast<uint8_t*>(start); |
| + length_ = buffer.length; |
| + payload_size_ = 0; |
| + return true; |
| +} |
| + |
| } // namespace media |