1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include <linux/videodev2.h>
8 #include <sys/eventfd.h>
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "content/common/gpu/media/v4l2_image_processor.h"
18 #include "media/base/bind_to_current_loop.h"
20 #define NOTIFY_ERROR() \
22 DLOG(ERROR) << "calling NotifyError()"; \
26 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \
28 if (device_->Ioctl(type, arg) != 0) { \
29 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
34 #define IOCTL_OR_ERROR_RETURN(type, arg) \
35 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0))
37 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
38 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false)
40 #define IOCTL_OR_LOG_ERROR(type, arg) \
42 if (device_->Ioctl(type, arg) != 0) \
43 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
48 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) {
51 V4L2ImageProcessor::OutputRecord::OutputRecord()
52 : at_device(false), at_client(false) {
55 V4L2ImageProcessor::JobRecord::JobRecord() {
58 V4L2ImageProcessor::V4L2ImageProcessor(scoped_ptr<V4L2Device> device)
59 : input_format_(media::VideoFrame::UNKNOWN),
60 output_format_(media::VideoFrame::UNKNOWN),
61 input_format_fourcc_(0),
62 output_format_fourcc_(0),
63 input_planes_count_(0),
64 output_planes_count_(0),
65 child_message_loop_proxy_(base::MessageLoopProxy::current()),
66 device_(device.Pass()),
67 device_thread_("V4L2ImageProcessorThread"),
68 device_poll_thread_("V4L2ImageProcessorDevicePollThread"),
69 input_streamon_(false),
70 input_buffer_queued_count_(0),
71 output_streamon_(false),
72 output_buffer_queued_count_(0),
73 device_weak_factory_(this) {
76 V4L2ImageProcessor::~V4L2ImageProcessor() {
77 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
78 DCHECK(!device_thread_.IsRunning());
79 DCHECK(!device_poll_thread_.IsRunning());
81 DestroyInputBuffers();
82 DestroyOutputBuffers();
85 void V4L2ImageProcessor::NotifyError() {
86 if (!child_message_loop_proxy_->BelongsToCurrentThread())
87 child_message_loop_proxy_->PostTask(FROM_HERE, error_cb_);
92 bool V4L2ImageProcessor::Initialize(media::VideoFrame::Format input_format,
93 media::VideoFrame::Format output_format,
94 gfx::Size input_visible_size,
95 gfx::Size output_visible_size,
96 gfx::Size output_allocated_size,
97 const base::Closure& error_cb) {
98 DCHECK(!error_cb.is_null());
101 // TODO(posciak): Replace Exynos-specific format/parameter hardcoding in this
102 // class with proper capability enumeration.
103 DCHECK_EQ(input_format, media::VideoFrame::I420);
104 DCHECK_EQ(output_format, media::VideoFrame::NV12);
106 input_format_ = input_format;
107 output_format_ = output_format;
108 input_format_fourcc_ = V4L2Device::VideoFrameFormatToV4L2PixFmt(input_format);
109 output_format_fourcc_ =
110 V4L2Device::VideoFrameFormatToV4L2PixFmt(output_format);
112 if (!input_format_fourcc_ || !output_format_fourcc_) {
113 DLOG(ERROR) << "Unrecognized format(s)";
117 input_visible_size_ = input_visible_size;
118 output_visible_size_ = output_visible_size;
119 output_allocated_size_ = output_allocated_size;
121 input_planes_count_ = media::VideoFrame::NumPlanes(input_format);
122 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
123 output_planes_count_ = media::VideoFrame::NumPlanes(output_format);
124 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
126 // Capabilities check.
127 struct v4l2_capability caps;
128 memset(&caps, 0, sizeof(caps));
129 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
130 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
131 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
132 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
133 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
134 "caps check failed: 0x" << std::hex << caps.capabilities;
138 if (!CreateInputBuffers() || !CreateOutputBuffers())
141 if (!device_thread_.Start()) {
142 DLOG(ERROR) << "Initialize(): encoder thread failed to start";
146 // StartDevicePoll will NOTIFY_ERROR on failure, so IgnoreResult is fine here.
147 device_thread_.message_loop()->PostTask(
149 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll),
150 base::Unretained(this)));
152 DVLOG(1) << "V4L2ImageProcessor initialized for "
154 << media::VideoFrame::FormatToString(input_format)
155 << ", output_format:"
156 << media::VideoFrame::FormatToString(output_format)
157 << ", input_visible_size: " << input_visible_size.ToString()
158 << ", input_allocated_size: " << input_allocated_size_.ToString()
159 << ", output_visible_size: " << output_visible_size.ToString()
160 << ", output_allocated_size: " << output_allocated_size.ToString();
165 void V4L2ImageProcessor::Process(const scoped_refptr<media::VideoFrame>& frame,
166 const FrameReadyCB& cb) {
167 DVLOG(3) << __func__ << ": ts=" << frame->timestamp().InMilliseconds();
169 scoped_ptr<JobRecord> job_record(new JobRecord());
170 job_record->frame = frame;
171 job_record->ready_cb = cb;
173 device_thread_.message_loop()->PostTask(
175 base::Bind(&V4L2ImageProcessor::ProcessTask,
176 base::Unretained(this),
177 base::Passed(&job_record)));
180 void V4L2ImageProcessor::ProcessTask(scoped_ptr<JobRecord> job_record) {
181 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
183 input_queue_.push(make_linked_ptr(job_record.release()));
187 void V4L2ImageProcessor::Destroy() {
188 DVLOG(3) << __func__;
189 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
191 // If the device thread is running, destroy using posted task.
192 if (device_thread_.IsRunning()) {
193 device_thread_.message_loop()->PostTask(
195 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this)));
196 // Wait for tasks to finish/early-exit.
197 device_thread_.Stop();
199 // Otherwise DestroyTask() is not needed.
200 DCHECK(!device_poll_thread_.IsRunning());
201 DCHECK(!device_weak_factory_.HasWeakPtrs());
207 void V4L2ImageProcessor::DestroyTask() {
208 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
210 device_weak_factory_.InvalidateWeakPtrs();
212 // Stop streaming and the device_poll_thread_.
216 bool V4L2ImageProcessor::CreateInputBuffers() {
217 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
218 DCHECK(!input_streamon_);
220 struct v4l2_control control;
221 memset(&control, 0, sizeof(control));
222 control.id = V4L2_CID_ROTATE;
224 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
226 memset(&control, 0, sizeof(control));
227 control.id = V4L2_CID_HFLIP;
229 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
231 memset(&control, 0, sizeof(control));
232 control.id = V4L2_CID_VFLIP;
234 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
236 memset(&control, 0, sizeof(control));
237 control.id = V4L2_CID_ALPHA_COMPONENT;
239 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
241 struct v4l2_format format;
242 memset(&format, 0, sizeof(format));
243 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
244 format.fmt.pix_mp.width = input_visible_size_.width();
245 format.fmt.pix_mp.height = input_visible_size_.height();
246 format.fmt.pix_mp.pixelformat = input_format_fourcc_;
247 format.fmt.pix_mp.num_planes = input_planes_count_;
248 for (size_t i = 0; i < input_planes_count_; ++i) {
249 format.fmt.pix_mp.plane_fmt[i].sizeimage =
250 media::VideoFrame::PlaneAllocationSize(
251 input_format_, i, input_allocated_size_);
252 format.fmt.pix_mp.plane_fmt[i].bytesperline =
253 base::checked_cast<__u32>(input_allocated_size_.width());
255 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
257 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format);
258 DCHECK(gfx::Rect(input_allocated_size_).Contains(
259 gfx::Rect(input_visible_size_)));
261 struct v4l2_crop crop;
262 memset(&crop, 0, sizeof(crop));
263 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
266 crop.c.width = base::checked_cast<__u32>(input_visible_size_.width());
267 crop.c.height = base::checked_cast<__u32>(input_visible_size_.height());
268 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
270 struct v4l2_requestbuffers reqbufs;
271 memset(&reqbufs, 0, sizeof(reqbufs));
272 reqbufs.count = kInputBufferCount;
273 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
274 reqbufs.memory = V4L2_MEMORY_USERPTR;
275 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
277 DCHECK(input_buffer_map_.empty());
278 input_buffer_map_.resize(reqbufs.count);
280 for (size_t i = 0; i < input_buffer_map_.size(); ++i)
281 free_input_buffers_.push_back(i);
286 bool V4L2ImageProcessor::CreateOutputBuffers() {
287 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
288 DCHECK(!output_streamon_);
290 struct v4l2_format format;
291 memset(&format, 0, sizeof(format));
292 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
293 format.fmt.pix_mp.width = output_allocated_size_.width();
294 format.fmt.pix_mp.height = output_allocated_size_.height();
295 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
296 format.fmt.pix_mp.num_planes = output_planes_count_;
297 for (size_t i = 0; i < output_planes_count_; ++i) {
298 format.fmt.pix_mp.plane_fmt[i].sizeimage =
299 media::VideoFrame::PlaneAllocationSize(
300 output_format_, i, output_allocated_size_);
301 format.fmt.pix_mp.plane_fmt[i].bytesperline =
302 base::checked_cast<__u32>(output_allocated_size_.width());
304 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
306 gfx::Size adjusted_allocated_size =
307 V4L2Device::CodedSizeFromV4L2Format(format);
308 DCHECK(gfx::Rect(adjusted_allocated_size).Contains(
309 gfx::Rect(output_allocated_size_)));
310 output_allocated_size_ = adjusted_allocated_size;
312 struct v4l2_crop crop;
313 memset(&crop, 0, sizeof(crop));
314 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
317 crop.c.width = base::checked_cast<__u32>(output_visible_size_.width());
318 crop.c.height = base::checked_cast<__u32>(output_visible_size_.height());
319 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
321 struct v4l2_requestbuffers reqbufs;
322 memset(&reqbufs, 0, sizeof(reqbufs));
323 reqbufs.count = kOutputBufferCount;
324 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
325 reqbufs.memory = V4L2_MEMORY_MMAP;
326 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
328 DCHECK(output_buffer_map_.empty());
329 output_buffer_map_.resize(reqbufs.count);
330 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
331 OutputRecord& output_record = output_buffer_map_[i];
332 output_record.fds.resize(output_planes_count_);
333 for (size_t j = 0; j < output_planes_count_; ++j) {
334 struct v4l2_exportbuffer expbuf;
335 memset(&expbuf, 0, sizeof(expbuf));
336 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
339 expbuf.flags = O_CLOEXEC;
340 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_EXPBUF, &expbuf);
341 output_record.fds[j] = expbuf.fd;
343 free_output_buffers_.push_back(i);
349 void V4L2ImageProcessor::DestroyInputBuffers() {
350 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
351 DCHECK(!input_streamon_);
353 struct v4l2_requestbuffers reqbufs;
354 memset(&reqbufs, 0, sizeof(reqbufs));
356 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
357 reqbufs.memory = V4L2_MEMORY_USERPTR;
358 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
360 input_buffer_map_.clear();
361 free_input_buffers_.clear();
364 void V4L2ImageProcessor::DestroyOutputBuffers() {
365 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
366 DCHECK(!output_streamon_);
368 for (size_t buf = 0; buf < output_buffer_map_.size(); ++buf) {
369 OutputRecord& output_record = output_buffer_map_[buf];
370 for (size_t plane = 0; plane < output_record.fds.size(); ++plane)
371 close(output_record.fds[plane]);
372 output_record.fds.clear();
375 struct v4l2_requestbuffers reqbufs;
376 memset(&reqbufs, 0, sizeof(reqbufs));
378 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
379 reqbufs.memory = V4L2_MEMORY_MMAP;
380 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
382 output_buffer_map_.clear();
383 free_output_buffers_.clear();
386 void V4L2ImageProcessor::DevicePollTask(bool poll_device) {
387 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current());
390 if (!device_->Poll(poll_device, &event_pending)) {
395 // All processing should happen on ServiceDeviceTask(), since we shouldn't
396 // touch encoder state from this thread.
397 device_thread_.message_loop()->PostTask(
399 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask,
400 base::Unretained(this)));
403 void V4L2ImageProcessor::ServiceDeviceTask() {
404 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
405 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
407 // * device_poll_thread_ is running normally
408 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
409 // in which case we should early-out.
410 if (!device_poll_thread_.message_loop())
416 if (!device_->ClearDevicePollInterrupt())
420 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0);
422 device_poll_thread_.message_loop()->PostTask(
424 base::Bind(&V4L2ImageProcessor::DevicePollTask,
425 base::Unretained(this),
428 DVLOG(2) << __func__ << ": buffer counts: INPUT["
429 << input_queue_.size() << "] => DEVICE["
430 << free_input_buffers_.size() << "+"
431 << input_buffer_queued_count_ << "/"
432 << input_buffer_map_.size() << "->"
433 << free_output_buffers_.size() << "+"
434 << output_buffer_queued_count_ << "/"
435 << output_buffer_map_.size() << "] => CLIENT["
436 << output_buffer_map_.size() - output_buffer_queued_count_ -
437 free_output_buffers_.size() << "]";
440 void V4L2ImageProcessor::Enqueue() {
441 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
443 const int old_inputs_queued = input_buffer_queued_count_;
444 while (!input_queue_.empty() && !free_input_buffers_.empty()) {
445 if (!EnqueueInputRecord())
448 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
449 // We started up a previously empty queue.
450 // Queue state changed; signal interrupt.
451 if (!device_->SetDevicePollInterrupt())
453 // VIDIOC_STREAMON if we haven't yet.
454 if (!input_streamon_) {
455 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
456 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
457 input_streamon_ = true;
461 // TODO(posciak): Fix this to be non-Exynos specific.
462 // Exynos GSC is liable to race conditions if more than one output buffer is
463 // simultaneously enqueued, so enqueue just one.
464 if (output_buffer_queued_count_ == 0 && !free_output_buffers_.empty()) {
465 const int old_outputs_queued = output_buffer_queued_count_;
466 if (!EnqueueOutputRecord())
468 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) {
469 // We just started up a previously empty queue.
470 // Queue state changed; signal interrupt.
471 if (!device_->SetDevicePollInterrupt())
473 // Start VIDIOC_STREAMON if we haven't yet.
474 if (!output_streamon_) {
475 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
476 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
477 output_streamon_ = true;
481 DCHECK_LE(output_buffer_queued_count_, 1);
484 void V4L2ImageProcessor::Dequeue() {
485 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
487 // Dequeue completed input (VIDEO_OUTPUT) buffers,
488 // and recycle to the free list.
489 struct v4l2_buffer dqbuf;
490 struct v4l2_plane planes[VIDEO_MAX_PLANES];
491 while (input_buffer_queued_count_ > 0) {
492 DCHECK(input_streamon_);
493 memset(&dqbuf, 0, sizeof(dqbuf));
494 memset(&planes, 0, sizeof(planes));
495 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
496 dqbuf.memory = V4L2_MEMORY_USERPTR;
497 dqbuf.m.planes = planes;
498 dqbuf.length = input_planes_count_;
499 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
500 if (errno == EAGAIN) {
501 // EAGAIN if we're just out of buffers to dequeue.
504 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
508 InputRecord& input_record = input_buffer_map_[dqbuf.index];
509 DCHECK(input_record.at_device);
510 input_record.at_device = false;
511 input_record.frame = NULL;
512 free_input_buffers_.push_back(dqbuf.index);
513 input_buffer_queued_count_--;
516 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
517 // Return the finished buffer to the client via the job ready callback.
518 while (output_buffer_queued_count_ > 0) {
519 DCHECK(output_streamon_);
520 memset(&dqbuf, 0, sizeof(dqbuf));
521 memset(&planes, 0, sizeof(planes));
522 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
523 dqbuf.memory = V4L2_MEMORY_DMABUF;
524 dqbuf.m.planes = planes;
525 dqbuf.length = output_planes_count_;
526 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
527 if (errno == EAGAIN) {
528 // EAGAIN if we're just out of buffers to dequeue.
531 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
535 OutputRecord& output_record = output_buffer_map_[dqbuf.index];
536 DCHECK(output_record.at_device);
537 output_record.at_device = false;
538 output_record.at_client = true;
539 output_buffer_queued_count_--;
541 // Jobs are always processed in FIFO order.
542 DCHECK(!running_jobs_.empty());
543 linked_ptr<JobRecord> job_record = running_jobs_.front();
546 scoped_refptr<media::VideoFrame> output_frame =
547 media::VideoFrame::WrapExternalDmabufs(
549 output_allocated_size_,
550 gfx::Rect(output_visible_size_),
551 output_visible_size_,
553 job_record->frame->timestamp(),
554 media::BindToCurrentLoop(
555 base::Bind(&V4L2ImageProcessor::ReuseOutputBuffer,
556 device_weak_factory_.GetWeakPtr(),
559 DVLOG(3) << "Processing finished, returning frame, ts="
560 << output_frame->timestamp().InMilliseconds();
562 child_message_loop_proxy_->PostTask(
563 FROM_HERE, base::Bind(job_record->ready_cb, output_frame));
567 void V4L2ImageProcessor::ReuseOutputBuffer(int index) {
568 DVLOG(3) << "Reusing output buffer, index=" << index;
569 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
571 OutputRecord& output_record = output_buffer_map_[index];
572 DCHECK(output_record.at_client);
573 DCHECK(!output_record.at_device);
574 output_record.at_client = false;
575 free_output_buffers_.push_back(index);
580 bool V4L2ImageProcessor::EnqueueInputRecord() {
581 DCHECK(!input_queue_.empty());
582 DCHECK(!free_input_buffers_.empty());
584 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
585 linked_ptr<JobRecord> job_record = input_queue_.front();
587 const int index = free_input_buffers_.back();
588 InputRecord& input_record = input_buffer_map_[index];
589 DCHECK(!input_record.at_device);
590 input_record.frame = job_record->frame;
591 struct v4l2_buffer qbuf;
592 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
593 memset(&qbuf, 0, sizeof(qbuf));
594 memset(qbuf_planes, 0, sizeof(qbuf_planes));
596 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
597 qbuf.memory = V4L2_MEMORY_USERPTR;
598 qbuf.m.planes = qbuf_planes;
599 qbuf.length = input_planes_count_;
600 for (size_t i = 0; i < input_planes_count_; ++i) {
601 qbuf.m.planes[i].bytesused = media::VideoFrame::PlaneAllocationSize(
602 input_record.frame->format(), i, input_allocated_size_);
603 qbuf.m.planes[i].length = qbuf.m.planes[i].bytesused;
604 qbuf.m.planes[i].m.userptr =
605 reinterpret_cast<unsigned long>(input_record.frame->data(i));
607 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
608 input_record.at_device = true;
609 running_jobs_.push(job_record);
610 free_input_buffers_.pop_back();
611 input_buffer_queued_count_++;
613 DVLOG(3) << __func__ << ": enqueued frame ts="
614 << job_record->frame->timestamp().InMilliseconds() << " to device.";
619 bool V4L2ImageProcessor::EnqueueOutputRecord() {
620 DCHECK(!free_output_buffers_.empty());
622 // Enqueue an output (VIDEO_CAPTURE) buffer.
623 const int index = free_output_buffers_.back();
624 OutputRecord& output_record = output_buffer_map_[index];
625 DCHECK(!output_record.at_device);
626 struct v4l2_buffer qbuf;
627 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
628 memset(&qbuf, 0, sizeof(qbuf));
629 memset(qbuf_planes, 0, sizeof(qbuf_planes));
631 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
632 qbuf.memory = V4L2_MEMORY_MMAP;
633 qbuf.m.planes = qbuf_planes;
634 qbuf.length = output_planes_count_;
635 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
636 output_record.at_device = true;
637 free_output_buffers_.pop_back();
638 output_buffer_queued_count_++;
642 bool V4L2ImageProcessor::StartDevicePoll() {
643 DVLOG(3) << __func__ << ": starting device poll";
644 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
645 DCHECK(!device_poll_thread_.IsRunning());
647 // Start up the device poll thread and schedule its first DevicePollTask().
648 if (!device_poll_thread_.Start()) {
649 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
653 // Enqueue a poll task with no devices to poll on - will wait only for the
655 device_poll_thread_.message_loop()->PostTask(
658 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false));
663 bool V4L2ImageProcessor::StopDevicePoll() {
664 DVLOG(3) << __func__ << ": stopping device poll";
665 if (device_thread_.IsRunning())
666 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
668 // Signal the DevicePollTask() to stop, and stop the device poll thread.
669 if (!device_->SetDevicePollInterrupt())
671 device_poll_thread_.Stop();
673 // Clear the interrupt now, to be sure.
674 if (!device_->ClearDevicePollInterrupt())
677 if (input_streamon_) {
678 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
679 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
681 input_streamon_ = false;
683 if (output_streamon_) {
684 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
685 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
687 output_streamon_ = false;
689 // Reset all our accounting info.
690 while (!input_queue_.empty())
693 while (!running_jobs_.empty())
696 free_input_buffers_.clear();
697 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
698 InputRecord& input_record = input_buffer_map_[i];
699 input_record.at_device = false;
700 input_record.frame = NULL;
701 free_input_buffers_.push_back(i);
703 input_buffer_queued_count_ = 0;
705 free_output_buffers_.clear();
706 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
707 OutputRecord& output_record = output_buffer_map_[i];
708 output_record.at_device = false;
709 if (!output_record.at_client)
710 free_output_buffers_.push_back(i);
712 output_buffer_queued_count_ = 0;
717 } // namespace content