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 LOG(ERROR) << "calling NotifyError()"; \
26 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \
28 if (device_->Ioctl(type, arg) != 0) { \
29 PLOG(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 PLOG(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 LOG(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 LOG(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 LOG(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 DVLOG(3) << __func__
;
218 DCHECK(child_message_loop_proxy_
->BelongsToCurrentThread());
219 DCHECK(!input_streamon_
);
221 struct v4l2_control control
;
222 memset(&control
, 0, sizeof(control
));
223 control
.id
= V4L2_CID_ROTATE
;
225 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL
, &control
);
227 memset(&control
, 0, sizeof(control
));
228 control
.id
= V4L2_CID_HFLIP
;
230 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL
, &control
);
232 memset(&control
, 0, sizeof(control
));
233 control
.id
= V4L2_CID_VFLIP
;
235 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL
, &control
);
237 memset(&control
, 0, sizeof(control
));
238 control
.id
= V4L2_CID_ALPHA_COMPONENT
;
240 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL
, &control
);
242 struct v4l2_format format
;
243 memset(&format
, 0, sizeof(format
));
244 format
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
245 format
.fmt
.pix_mp
.width
= input_visible_size_
.width();
246 format
.fmt
.pix_mp
.height
= input_visible_size_
.height();
247 format
.fmt
.pix_mp
.pixelformat
= input_format_fourcc_
;
248 format
.fmt
.pix_mp
.num_planes
= input_planes_count_
;
249 for (size_t i
= 0; i
< input_planes_count_
; ++i
) {
250 format
.fmt
.pix_mp
.plane_fmt
[i
].sizeimage
=
251 media::VideoFrame::PlaneAllocationSize(
252 input_format_
, i
, input_allocated_size_
);
253 format
.fmt
.pix_mp
.plane_fmt
[i
].bytesperline
=
254 base::checked_cast
<__u32
>(input_allocated_size_
.width());
256 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT
, &format
);
258 input_allocated_size_
= V4L2Device::CodedSizeFromV4L2Format(format
);
259 DCHECK(gfx::Rect(input_allocated_size_
).Contains(
260 gfx::Rect(input_visible_size_
)));
262 struct v4l2_crop crop
;
263 memset(&crop
, 0, sizeof(crop
));
264 crop
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
267 crop
.c
.width
= base::checked_cast
<__u32
>(input_visible_size_
.width());
268 crop
.c
.height
= base::checked_cast
<__u32
>(input_visible_size_
.height());
269 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP
, &crop
);
271 struct v4l2_requestbuffers reqbufs
;
272 memset(&reqbufs
, 0, sizeof(reqbufs
));
273 reqbufs
.count
= kInputBufferCount
;
274 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
275 reqbufs
.memory
= V4L2_MEMORY_USERPTR
;
276 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS
, &reqbufs
);
278 DCHECK(input_buffer_map_
.empty());
279 input_buffer_map_
.resize(reqbufs
.count
);
281 for (size_t i
= 0; i
< input_buffer_map_
.size(); ++i
)
282 free_input_buffers_
.push_back(i
);
287 bool V4L2ImageProcessor::CreateOutputBuffers() {
288 DVLOG(3) << __func__
;
289 DCHECK(child_message_loop_proxy_
->BelongsToCurrentThread());
290 DCHECK(!output_streamon_
);
292 struct v4l2_format format
;
293 memset(&format
, 0, sizeof(format
));
294 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
295 format
.fmt
.pix_mp
.width
= output_allocated_size_
.width();
296 format
.fmt
.pix_mp
.height
= output_allocated_size_
.height();
297 format
.fmt
.pix_mp
.pixelformat
= output_format_fourcc_
;
298 format
.fmt
.pix_mp
.num_planes
= output_planes_count_
;
299 for (size_t i
= 0; i
< output_planes_count_
; ++i
) {
300 format
.fmt
.pix_mp
.plane_fmt
[i
].sizeimage
=
301 media::VideoFrame::PlaneAllocationSize(
302 output_format_
, i
, output_allocated_size_
);
303 format
.fmt
.pix_mp
.plane_fmt
[i
].bytesperline
=
304 base::checked_cast
<__u32
>(output_allocated_size_
.width());
306 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT
, &format
);
308 gfx::Size adjusted_allocated_size
=
309 V4L2Device::CodedSizeFromV4L2Format(format
);
310 DCHECK(gfx::Rect(adjusted_allocated_size
).Contains(
311 gfx::Rect(output_allocated_size_
)));
312 output_allocated_size_
= adjusted_allocated_size
;
314 struct v4l2_crop crop
;
315 memset(&crop
, 0, sizeof(crop
));
316 crop
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
319 crop
.c
.width
= base::checked_cast
<__u32
>(output_visible_size_
.width());
320 crop
.c
.height
= base::checked_cast
<__u32
>(output_visible_size_
.height());
321 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP
, &crop
);
323 struct v4l2_requestbuffers reqbufs
;
324 memset(&reqbufs
, 0, sizeof(reqbufs
));
325 reqbufs
.count
= kOutputBufferCount
;
326 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
327 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
328 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS
, &reqbufs
);
330 DCHECK(output_buffer_map_
.empty());
331 output_buffer_map_
.resize(reqbufs
.count
);
332 for (size_t i
= 0; i
< output_buffer_map_
.size(); ++i
) {
333 OutputRecord
& output_record
= output_buffer_map_
[i
];
334 output_record
.fds
.resize(output_planes_count_
);
335 for (size_t j
= 0; j
< output_planes_count_
; ++j
) {
336 struct v4l2_exportbuffer expbuf
;
337 memset(&expbuf
, 0, sizeof(expbuf
));
338 expbuf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
341 expbuf
.flags
= O_CLOEXEC
;
342 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_EXPBUF
, &expbuf
);
343 output_record
.fds
[j
] = expbuf
.fd
;
345 free_output_buffers_
.push_back(i
);
351 void V4L2ImageProcessor::DestroyInputBuffers() {
352 DCHECK(child_message_loop_proxy_
->BelongsToCurrentThread());
353 DCHECK(!input_streamon_
);
355 struct v4l2_requestbuffers reqbufs
;
356 memset(&reqbufs
, 0, sizeof(reqbufs
));
358 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
359 reqbufs
.memory
= V4L2_MEMORY_USERPTR
;
360 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS
, &reqbufs
);
362 input_buffer_map_
.clear();
363 free_input_buffers_
.clear();
366 void V4L2ImageProcessor::DestroyOutputBuffers() {
367 DCHECK(child_message_loop_proxy_
->BelongsToCurrentThread());
368 DCHECK(!output_streamon_
);
370 for (size_t buf
= 0; buf
< output_buffer_map_
.size(); ++buf
) {
371 OutputRecord
& output_record
= output_buffer_map_
[buf
];
372 for (size_t plane
= 0; plane
< output_record
.fds
.size(); ++plane
)
373 close(output_record
.fds
[plane
]);
374 output_record
.fds
.clear();
377 struct v4l2_requestbuffers reqbufs
;
378 memset(&reqbufs
, 0, sizeof(reqbufs
));
380 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
381 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
382 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS
, &reqbufs
);
384 output_buffer_map_
.clear();
385 free_output_buffers_
.clear();
388 void V4L2ImageProcessor::DevicePollTask(bool poll_device
) {
389 DCHECK_EQ(device_poll_thread_
.message_loop(), base::MessageLoop::current());
392 if (!device_
->Poll(poll_device
, &event_pending
)) {
397 // All processing should happen on ServiceDeviceTask(), since we shouldn't
398 // touch encoder state from this thread.
399 device_thread_
.message_loop()->PostTask(
401 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask
,
402 base::Unretained(this)));
405 void V4L2ImageProcessor::ServiceDeviceTask() {
406 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
407 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
409 // * device_poll_thread_ is running normally
410 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
411 // in which case we should early-out.
412 if (!device_poll_thread_
.message_loop())
418 if (!device_
->ClearDevicePollInterrupt())
422 (input_buffer_queued_count_
> 0 && output_buffer_queued_count_
> 0);
424 device_poll_thread_
.message_loop()->PostTask(
426 base::Bind(&V4L2ImageProcessor::DevicePollTask
,
427 base::Unretained(this),
430 DVLOG(2) << __func__
<< ": buffer counts: INPUT["
431 << input_queue_
.size() << "] => DEVICE["
432 << free_input_buffers_
.size() << "+"
433 << input_buffer_queued_count_
<< "/"
434 << input_buffer_map_
.size() << "->"
435 << free_output_buffers_
.size() << "+"
436 << output_buffer_queued_count_
<< "/"
437 << output_buffer_map_
.size() << "] => CLIENT["
438 << output_buffer_map_
.size() - output_buffer_queued_count_
-
439 free_output_buffers_
.size() << "]";
442 void V4L2ImageProcessor::Enqueue() {
443 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
445 const int old_inputs_queued
= input_buffer_queued_count_
;
446 while (!input_queue_
.empty() && !free_input_buffers_
.empty()) {
447 if (!EnqueueInputRecord())
450 if (old_inputs_queued
== 0 && input_buffer_queued_count_
!= 0) {
451 // We started up a previously empty queue.
452 // Queue state changed; signal interrupt.
453 if (!device_
->SetDevicePollInterrupt())
455 // VIDIOC_STREAMON if we haven't yet.
456 if (!input_streamon_
) {
457 __u32 type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
458 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON
, &type
);
459 input_streamon_
= true;
463 // TODO(posciak): Fix this to be non-Exynos specific.
464 // Exynos GSC is liable to race conditions if more than one output buffer is
465 // simultaneously enqueued, so enqueue just one.
466 if (output_buffer_queued_count_
== 0 && !free_output_buffers_
.empty()) {
467 const int old_outputs_queued
= output_buffer_queued_count_
;
468 if (!EnqueueOutputRecord())
470 if (old_outputs_queued
== 0 && output_buffer_queued_count_
!= 0) {
471 // We just started up a previously empty queue.
472 // Queue state changed; signal interrupt.
473 if (!device_
->SetDevicePollInterrupt())
475 // Start VIDIOC_STREAMON if we haven't yet.
476 if (!output_streamon_
) {
477 __u32 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
478 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON
, &type
);
479 output_streamon_
= true;
483 DCHECK_LE(output_buffer_queued_count_
, 1);
486 void V4L2ImageProcessor::Dequeue() {
487 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
489 // Dequeue completed input (VIDEO_OUTPUT) buffers,
490 // and recycle to the free list.
491 struct v4l2_buffer dqbuf
;
492 struct v4l2_plane planes
[VIDEO_MAX_PLANES
];
493 while (input_buffer_queued_count_
> 0) {
494 DCHECK(input_streamon_
);
495 memset(&dqbuf
, 0, sizeof(dqbuf
));
496 memset(&planes
, 0, sizeof(planes
));
497 dqbuf
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
498 dqbuf
.memory
= V4L2_MEMORY_USERPTR
;
499 dqbuf
.m
.planes
= planes
;
500 dqbuf
.length
= input_planes_count_
;
501 if (device_
->Ioctl(VIDIOC_DQBUF
, &dqbuf
) != 0) {
502 if (errno
== EAGAIN
) {
503 // EAGAIN if we're just out of buffers to dequeue.
506 PLOG(ERROR
) << "ioctl() failed: VIDIOC_DQBUF";
510 InputRecord
& input_record
= input_buffer_map_
[dqbuf
.index
];
511 DCHECK(input_record
.at_device
);
512 input_record
.at_device
= false;
513 input_record
.frame
= NULL
;
514 free_input_buffers_
.push_back(dqbuf
.index
);
515 input_buffer_queued_count_
--;
518 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
519 // Return the finished buffer to the client via the job ready callback.
520 while (output_buffer_queued_count_
> 0) {
521 DCHECK(output_streamon_
);
522 memset(&dqbuf
, 0, sizeof(dqbuf
));
523 memset(&planes
, 0, sizeof(planes
));
524 dqbuf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
525 dqbuf
.memory
= V4L2_MEMORY_DMABUF
;
526 dqbuf
.m
.planes
= planes
;
527 dqbuf
.length
= output_planes_count_
;
528 if (device_
->Ioctl(VIDIOC_DQBUF
, &dqbuf
) != 0) {
529 if (errno
== EAGAIN
) {
530 // EAGAIN if we're just out of buffers to dequeue.
533 PLOG(ERROR
) << "ioctl() failed: VIDIOC_DQBUF";
537 OutputRecord
& output_record
= output_buffer_map_
[dqbuf
.index
];
538 DCHECK(output_record
.at_device
);
539 output_record
.at_device
= false;
540 output_record
.at_client
= true;
541 output_buffer_queued_count_
--;
543 // Jobs are always processed in FIFO order.
544 DCHECK(!running_jobs_
.empty());
545 linked_ptr
<JobRecord
> job_record
= running_jobs_
.front();
548 scoped_refptr
<media::VideoFrame
> output_frame
=
549 media::VideoFrame::WrapExternalDmabufs(
551 output_allocated_size_
,
552 gfx::Rect(output_visible_size_
),
553 output_visible_size_
,
555 job_record
->frame
->timestamp(),
556 media::BindToCurrentLoop(
557 base::Bind(&V4L2ImageProcessor::ReuseOutputBuffer
,
558 device_weak_factory_
.GetWeakPtr(),
561 DVLOG(3) << "Processing finished, returning frame, ts="
562 << output_frame
->timestamp().InMilliseconds();
564 child_message_loop_proxy_
->PostTask(
565 FROM_HERE
, base::Bind(job_record
->ready_cb
, output_frame
));
569 void V4L2ImageProcessor::ReuseOutputBuffer(int index
) {
570 DVLOG(3) << "Reusing output buffer, index=" << index
;
571 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
573 OutputRecord
& output_record
= output_buffer_map_
[index
];
574 DCHECK(output_record
.at_client
);
575 DCHECK(!output_record
.at_device
);
576 output_record
.at_client
= false;
577 free_output_buffers_
.push_back(index
);
582 bool V4L2ImageProcessor::EnqueueInputRecord() {
583 DCHECK(!input_queue_
.empty());
584 DCHECK(!free_input_buffers_
.empty());
586 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
587 linked_ptr
<JobRecord
> job_record
= input_queue_
.front();
589 const int index
= free_input_buffers_
.back();
590 InputRecord
& input_record
= input_buffer_map_
[index
];
591 DCHECK(!input_record
.at_device
);
592 input_record
.frame
= job_record
->frame
;
593 struct v4l2_buffer qbuf
;
594 struct v4l2_plane qbuf_planes
[VIDEO_MAX_PLANES
];
595 memset(&qbuf
, 0, sizeof(qbuf
));
596 memset(qbuf_planes
, 0, sizeof(qbuf_planes
));
598 qbuf
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
599 qbuf
.memory
= V4L2_MEMORY_USERPTR
;
600 qbuf
.m
.planes
= qbuf_planes
;
601 qbuf
.length
= input_planes_count_
;
602 for (size_t i
= 0; i
< input_planes_count_
; ++i
) {
603 qbuf
.m
.planes
[i
].bytesused
= media::VideoFrame::PlaneAllocationSize(
604 input_record
.frame
->format(), i
, input_allocated_size_
);
605 qbuf
.m
.planes
[i
].length
= qbuf
.m
.planes
[i
].bytesused
;
606 qbuf
.m
.planes
[i
].m
.userptr
=
607 reinterpret_cast<unsigned long>(input_record
.frame
->data(i
));
609 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF
, &qbuf
);
610 input_record
.at_device
= true;
611 running_jobs_
.push(job_record
);
612 free_input_buffers_
.pop_back();
613 input_buffer_queued_count_
++;
615 DVLOG(3) << __func__
<< ": enqueued frame ts="
616 << job_record
->frame
->timestamp().InMilliseconds() << " to device.";
621 bool V4L2ImageProcessor::EnqueueOutputRecord() {
622 DCHECK(!free_output_buffers_
.empty());
624 // Enqueue an output (VIDEO_CAPTURE) buffer.
625 const int index
= free_output_buffers_
.back();
626 OutputRecord
& output_record
= output_buffer_map_
[index
];
627 DCHECK(!output_record
.at_device
);
628 struct v4l2_buffer qbuf
;
629 struct v4l2_plane qbuf_planes
[VIDEO_MAX_PLANES
];
630 memset(&qbuf
, 0, sizeof(qbuf
));
631 memset(qbuf_planes
, 0, sizeof(qbuf_planes
));
633 qbuf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
634 qbuf
.memory
= V4L2_MEMORY_MMAP
;
635 qbuf
.m
.planes
= qbuf_planes
;
636 qbuf
.length
= output_planes_count_
;
637 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF
, &qbuf
);
638 output_record
.at_device
= true;
639 free_output_buffers_
.pop_back();
640 output_buffer_queued_count_
++;
644 bool V4L2ImageProcessor::StartDevicePoll() {
645 DVLOG(3) << __func__
<< ": starting device poll";
646 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
647 DCHECK(!device_poll_thread_
.IsRunning());
649 // Start up the device poll thread and schedule its first DevicePollTask().
650 if (!device_poll_thread_
.Start()) {
651 LOG(ERROR
) << "StartDevicePoll(): Device thread failed to start";
655 // Enqueue a poll task with no devices to poll on - will wait only for the
657 device_poll_thread_
.message_loop()->PostTask(
660 &V4L2ImageProcessor::DevicePollTask
, base::Unretained(this), false));
665 bool V4L2ImageProcessor::StopDevicePoll() {
666 DVLOG(3) << __func__
<< ": stopping device poll";
667 if (device_thread_
.IsRunning())
668 DCHECK_EQ(device_thread_
.message_loop(), base::MessageLoop::current());
670 // Signal the DevicePollTask() to stop, and stop the device poll thread.
671 if (!device_
->SetDevicePollInterrupt())
673 device_poll_thread_
.Stop();
675 // Clear the interrupt now, to be sure.
676 if (!device_
->ClearDevicePollInterrupt())
679 if (input_streamon_
) {
680 __u32 type
= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
;
681 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF
, &type
);
683 input_streamon_
= false;
685 if (output_streamon_
) {
686 __u32 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
;
687 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF
, &type
);
689 output_streamon_
= false;
691 // Reset all our accounting info.
692 while (!input_queue_
.empty())
695 while (!running_jobs_
.empty())
698 free_input_buffers_
.clear();
699 for (size_t i
= 0; i
< input_buffer_map_
.size(); ++i
) {
700 InputRecord
& input_record
= input_buffer_map_
[i
];
701 input_record
.at_device
= false;
702 input_record
.frame
= NULL
;
703 free_input_buffers_
.push_back(i
);
705 input_buffer_queued_count_
= 0;
707 free_output_buffers_
.clear();
708 for (size_t i
= 0; i
< output_buffer_map_
.size(); ++i
) {
709 OutputRecord
& output_record
= output_buffer_map_
[i
];
710 output_record
.at_device
= false;
711 if (!output_record
.at_client
)
712 free_output_buffers_
.push_back(i
);
714 output_buffer_queued_count_
= 0;
719 } // namespace content