1 // Copyright 2015 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.
5 #include <linux/videodev2.h>
9 #include "base/thread_task_runner_handle.h"
10 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h"
12 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_name) \
14 if (device_->Ioctl(type, arg) != 0) { \
15 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_name; \
16 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE); \
21 #define IOCTL_OR_ERROR_RETURN(type, arg) \
22 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type)
24 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
25 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type)
27 #define IOCTL_OR_LOG_ERROR(type, arg) \
29 if (device_->Ioctl(type, arg) != 0) { \
30 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
31 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE); \
37 V4L2JpegDecodeAccelerator::BufferRecord::BufferRecord()
38 : address(nullptr), length(0), at_device(false) {
41 V4L2JpegDecodeAccelerator::BufferRecord::~BufferRecord() {
44 V4L2JpegDecodeAccelerator::JobRecord::JobRecord(
45 media::BitstreamBuffer bitstream_buffer
,
46 scoped_refptr
<media::VideoFrame
> video_frame
)
47 : bitstream_buffer(bitstream_buffer
), out_frame(video_frame
) {
50 V4L2JpegDecodeAccelerator::JobRecord::~JobRecord() {
53 V4L2JpegDecodeAccelerator::V4L2JpegDecodeAccelerator(
54 const scoped_refptr
<V4L2Device
>& device
,
55 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
)
56 : recreate_input_buffers_pending_(false),
57 recreate_output_buffers_pending_(false),
58 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
59 io_task_runner_(io_task_runner
),
62 decoder_thread_("V4L2JpegDecodeThread"),
63 device_poll_thread_("V4L2JpegDecodeDevicePollThread"),
64 input_streamon_(false),
65 output_streamon_(false),
67 weak_ptr_
= weak_factory_
.GetWeakPtr();
70 V4L2JpegDecodeAccelerator::~V4L2JpegDecodeAccelerator() {
71 DCHECK(child_task_runner_
->BelongsToCurrentThread());
73 if (decoder_thread_
.IsRunning()) {
74 decoder_task_runner_
->PostTask(
75 FROM_HERE
, base::Bind(&V4L2JpegDecodeAccelerator::DestroyTask
,
76 base::Unretained(this)));
77 decoder_thread_
.Stop();
79 weak_factory_
.InvalidateWeakPtrs();
80 DCHECK(!device_poll_thread_
.IsRunning());
83 void V4L2JpegDecodeAccelerator::DestroyTask() {
84 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
85 while (!input_jobs_
.empty())
87 while (!running_jobs_
.empty())
90 // Stop streaming and the device_poll_thread_.
94 DestroyInputBuffers();
95 DestroyOutputBuffers();
98 void V4L2JpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id
) {
99 DCHECK(child_task_runner_
->BelongsToCurrentThread());
100 client_
->VideoFrameReady(bitstream_buffer_id
);
103 void V4L2JpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id
,
105 DCHECK(child_task_runner_
->BelongsToCurrentThread());
106 LOG(ERROR
) << "Notifying of error " << error
<< " for buffer id "
107 << bitstream_buffer_id
;
108 client_
->NotifyError(bitstream_buffer_id
, error
);
111 void V4L2JpegDecodeAccelerator::PostNotifyError(
112 int32_t bitstream_buffer_id
,
114 child_task_runner_
->PostTask(
116 base::Bind(&V4L2JpegDecodeAccelerator::NotifyError
, weak_ptr_
,
117 bitstream_buffer_id
, error
));
120 bool V4L2JpegDecodeAccelerator::Initialize(Client
* client
) {
121 DCHECK(child_task_runner_
->BelongsToCurrentThread());
123 // Capabilities check.
124 struct v4l2_capability caps
;
125 const __u32 kCapsRequired
= V4L2_CAP_STREAMING
| V4L2_CAP_VIDEO_M2M
;
126 memset(&caps
, 0, sizeof(caps
));
127 if (device_
->Ioctl(VIDIOC_QUERYCAP
, &caps
) != 0) {
128 PLOG(ERROR
) << __func__
<< "(): ioctl() failed: VIDIOC_QUERYCAP";
131 if ((caps
.capabilities
& kCapsRequired
) != kCapsRequired
) {
132 LOG(ERROR
) << "Initialize(): VIDIOC_QUERYCAP, caps check failed: 0x"
133 << std::hex
<< caps
.capabilities
;
137 if (!decoder_thread_
.Start()) {
138 LOG(ERROR
) << "Initialize(): decoder thread failed to start";
142 decoder_task_runner_
= decoder_thread_
.task_runner();
144 decoder_task_runner_
->PostTask(
145 FROM_HERE
, base::Bind(&V4L2JpegDecodeAccelerator::StartDevicePoll
,
146 base::Unretained(this)));
148 DVLOG(1) << "V4L2JpegDecodeAccelerator initialized.";
152 void V4L2JpegDecodeAccelerator::Decode(
153 const media::BitstreamBuffer
& bitstream_buffer
,
154 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
155 DVLOG(1) << "Decode(): input_id=" << bitstream_buffer
.id()
156 << ", size=" << bitstream_buffer
.size();
157 DCHECK(io_task_runner_
->BelongsToCurrentThread());
159 if (video_frame
->format() != media::PIXEL_FORMAT_I420
) {
160 PostNotifyError(bitstream_buffer
.id(), UNSUPPORTED_JPEG
);
164 scoped_ptr
<JobRecord
> job_record(
165 new JobRecord(bitstream_buffer
, video_frame
));
167 decoder_task_runner_
->PostTask(
168 FROM_HERE
, base::Bind(&V4L2JpegDecodeAccelerator::DecodeTask
,
169 base::Unretained(this), base::Passed(&job_record
)));
172 bool V4L2JpegDecodeAccelerator::IsSupported() {
173 v4l2_fmtdesc fmtdesc
;
174 memset(&fmtdesc
, 0, sizeof(fmtdesc
));
175 fmtdesc
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
177 for (; device_
->Ioctl(VIDIOC_ENUM_FMT
, &fmtdesc
) == 0; ++fmtdesc
.index
) {
178 if (fmtdesc
.pixelformat
== V4L2_PIX_FMT_JPEG
)
184 void V4L2JpegDecodeAccelerator::DecodeTask(scoped_ptr
<JobRecord
> job_record
) {
185 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
186 job_record
->shm
.reset(
187 new base::SharedMemory(job_record
->bitstream_buffer
.handle(), true));
188 if (!job_record
->shm
->Map(job_record
->bitstream_buffer
.size())) {
189 PLOG(ERROR
) << "DecodeTask(): could not map bitstream_buffer";
190 PostNotifyError(job_record
->bitstream_buffer
.id(), UNREADABLE_INPUT
);
193 input_jobs_
.push(make_linked_ptr(job_record
.release()));
198 size_t V4L2JpegDecodeAccelerator::InputBufferQueuedCount() {
199 return input_buffer_map_
.size() - free_input_buffers_
.size();
202 size_t V4L2JpegDecodeAccelerator::OutputBufferQueuedCount() {
203 return output_buffer_map_
.size() - free_output_buffers_
.size();
206 bool V4L2JpegDecodeAccelerator::ShouldRecreateInputBuffers() {
207 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
208 if (input_jobs_
.empty())
211 linked_ptr
<JobRecord
> job_record
= input_jobs_
.front();
212 // Check input buffer size is enough
213 return (input_buffer_map_
.empty() ||
214 job_record
->bitstream_buffer
.size() > input_buffer_map_
.front().length
);
217 bool V4L2JpegDecodeAccelerator::ShouldRecreateOutputBuffers() {
218 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
219 if (input_jobs_
.empty())
222 linked_ptr
<JobRecord
> job_record
= input_jobs_
.front();
223 // Check image resolution is the same as previous.
224 if (job_record
->out_frame
->coded_size() != image_coded_size_
) {
225 size_t frame_size
= media::VideoFrame::AllocationSize(
226 job_record
->out_frame
->format(), job_record
->out_frame
->coded_size());
227 if (output_buffer_map_
.empty() ||
228 frame_size
> output_buffer_map_
.front().length
) {
235 bool V4L2JpegDecodeAccelerator::CreateBuffersIfNecessary() {
236 DVLOG(3) << __func__
;
237 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
239 recreate_input_buffers_pending_
= ShouldRecreateInputBuffers();
240 recreate_output_buffers_pending_
= ShouldRecreateOutputBuffers();
241 if (!recreate_input_buffers_pending_
&& !recreate_output_buffers_pending_
)
244 // If running queue is not empty, we should wait until pending frames finish.
245 if (!running_jobs_
.empty())
248 if (input_streamon_
|| output_streamon_
) {
250 if (recreate_input_buffers_pending_
)
251 DestroyInputBuffers();
253 if (recreate_output_buffers_pending_
)
254 DestroyOutputBuffers();
257 if (recreate_input_buffers_pending_
&& !CreateInputBuffers()) {
258 LOG(ERROR
) << "Create input buffers failed.";
261 if (recreate_output_buffers_pending_
&& !CreateOutputBuffers()) {
262 LOG(ERROR
) << "Create output buffers failed.";
269 bool V4L2JpegDecodeAccelerator::CreateInputBuffers() {
270 DVLOG(3) << __func__
;
271 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
272 DCHECK(!input_streamon_
);
273 DCHECK(!input_jobs_
.empty());
274 linked_ptr
<JobRecord
> job_record
= input_jobs_
.front();
275 // Reserve twice size to avoid recreating input buffer frequently.
276 size_t reserve_size
= job_record
->bitstream_buffer
.size() * 2;
278 struct v4l2_format format
;
279 memset(&format
, 0, sizeof(format
));
280 format
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
281 format
.fmt
.pix
.width
= job_record
->out_frame
->coded_size().width();
282 format
.fmt
.pix
.height
= job_record
->out_frame
->coded_size().height();
283 format
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_JPEG
;
284 format
.fmt
.pix
.sizeimage
= reserve_size
;
285 format
.fmt
.pix
.field
= V4L2_FIELD_ANY
;
286 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT
, &format
);
288 struct v4l2_requestbuffers reqbufs
;
289 memset(&reqbufs
, 0, sizeof(reqbufs
));
290 reqbufs
.count
= kBufferCount
;
291 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
292 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
293 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS
, &reqbufs
);
295 DCHECK(input_buffer_map_
.empty());
296 input_buffer_map_
.resize(reqbufs
.count
);
298 for (size_t i
= 0; i
< input_buffer_map_
.size(); ++i
) {
299 free_input_buffers_
.push_back(i
);
301 struct v4l2_buffer buffer
;
302 memset(&buffer
, 0, sizeof(buffer
));
304 buffer
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
305 buffer
.memory
= V4L2_MEMORY_MMAP
;
306 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF
, &buffer
);
307 void* address
= device_
->Mmap(NULL
, buffer
.length
, PROT_READ
| PROT_WRITE
,
308 MAP_SHARED
, buffer
.m
.offset
);
309 if (address
== MAP_FAILED
) {
310 PLOG(ERROR
) << "CreateInputBuffers(): mmap() failed";
311 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
314 input_buffer_map_
[i
].address
= address
;
315 input_buffer_map_
[i
].length
= buffer
.length
;
317 recreate_input_buffers_pending_
= false;
322 bool V4L2JpegDecodeAccelerator::CreateOutputBuffers() {
323 DVLOG(3) << __func__
;
324 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
325 DCHECK(!output_streamon_
);
326 DCHECK(!input_jobs_
.empty());
327 linked_ptr
<JobRecord
> job_record
= input_jobs_
.front();
329 size_t frame_size
= media::VideoFrame::AllocationSize(
330 media::PIXEL_FORMAT_I420
, job_record
->out_frame
->coded_size());
331 struct v4l2_format format
;
332 memset(&format
, 0, sizeof(format
));
333 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
334 format
.fmt
.pix
.width
= job_record
->out_frame
->coded_size().width();
335 format
.fmt
.pix
.height
= job_record
->out_frame
->coded_size().height();
336 format
.fmt
.pix
.sizeimage
= frame_size
;
337 format
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_YUV420
;
338 format
.fmt
.pix
.field
= V4L2_FIELD_ANY
;
339 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT
, &format
);
341 struct v4l2_requestbuffers reqbufs
;
342 memset(&reqbufs
, 0, sizeof(reqbufs
));
343 reqbufs
.count
= kBufferCount
;
344 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
345 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
346 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS
, &reqbufs
);
348 DCHECK(output_buffer_map_
.empty());
349 output_buffer_map_
.resize(reqbufs
.count
);
351 for (size_t i
= 0; i
< output_buffer_map_
.size(); ++i
) {
352 free_output_buffers_
.push_back(i
);
354 struct v4l2_buffer buffer
;
355 memset(&buffer
, 0, sizeof(buffer
));
357 buffer
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
358 buffer
.memory
= V4L2_MEMORY_MMAP
;
359 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF
, &buffer
);
360 void* address
= device_
->Mmap(NULL
, buffer
.length
, PROT_READ
| PROT_WRITE
,
361 MAP_SHARED
, buffer
.m
.offset
);
362 if (address
== MAP_FAILED
) {
363 PLOG(ERROR
) << "CreateOutputBuffers(): mmap() failed";
364 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
367 output_buffer_map_
[i
].address
= address
;
368 output_buffer_map_
[i
].length
= buffer
.length
;
370 image_coded_size_
= job_record
->out_frame
->coded_size();
371 recreate_output_buffers_pending_
= false;
376 void V4L2JpegDecodeAccelerator::DestroyInputBuffers() {
377 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
378 DCHECK(!input_streamon_
);
380 for (size_t buf
= 0; buf
< input_buffer_map_
.size(); ++buf
) {
381 BufferRecord
& input_record
= input_buffer_map_
[buf
];
382 device_
->Munmap(input_record
.address
, input_record
.length
);
385 struct v4l2_requestbuffers reqbufs
;
386 memset(&reqbufs
, 0, sizeof(reqbufs
));
388 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
389 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
390 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS
, &reqbufs
);
392 input_buffer_map_
.clear();
393 free_input_buffers_
.clear();
396 void V4L2JpegDecodeAccelerator::DestroyOutputBuffers() {
397 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
398 DCHECK(!output_streamon_
);
400 for (size_t buf
= 0; buf
< output_buffer_map_
.size(); ++buf
) {
401 BufferRecord
& output_record
= output_buffer_map_
[buf
];
402 device_
->Munmap(output_record
.address
, output_record
.length
);
405 struct v4l2_requestbuffers reqbufs
;
406 memset(&reqbufs
, 0, sizeof(reqbufs
));
408 reqbufs
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
409 reqbufs
.memory
= V4L2_MEMORY_MMAP
;
410 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS
, &reqbufs
);
412 output_buffer_map_
.clear();
413 free_output_buffers_
.clear();
416 void V4L2JpegDecodeAccelerator::DevicePollTask() {
417 DCHECK(device_poll_task_runner_
->BelongsToCurrentThread());
420 if (!device_
->Poll(true, &event_pending
)) {
421 PLOG(ERROR
) << "DevicePollTask(): Poll device error.";
422 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
426 // All processing should happen on ServiceDeviceTask(), since we shouldn't
427 // touch decoder state from this thread.
428 decoder_task_runner_
->PostTask(
429 FROM_HERE
, base::Bind(&V4L2JpegDecodeAccelerator::ServiceDeviceTask
,
430 base::Unretained(this)));
433 void V4L2JpegDecodeAccelerator::ServiceDeviceTask() {
434 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
435 // If DestroyTask() shuts |device_poll_thread_| down, we should early-out.
436 if (!device_poll_thread_
.IsRunning())
439 if (!running_jobs_
.empty())
441 if (!CreateBuffersIfNecessary())
447 if (!running_jobs_
.empty()) {
448 device_poll_task_runner_
->PostTask(
449 FROM_HERE
, base::Bind(&V4L2JpegDecodeAccelerator::DevicePollTask
,
450 base::Unretained(this)));
453 DVLOG(2) << __func__
<< ": buffer counts: INPUT["
454 << input_jobs_
.size() << "] => DEVICE["
455 << free_input_buffers_
.size() << "/"
456 << input_buffer_map_
.size() << "->"
457 << free_output_buffers_
.size() << "/"
458 << output_buffer_map_
.size() << "]";
461 void V4L2JpegDecodeAccelerator::EnqueueInput() {
462 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
463 while (!input_jobs_
.empty() && !free_input_buffers_
.empty()) {
464 // Do not enqueue input record when input/output buffers are required to
465 // re-create until all pending frames are handled by device.
466 if (recreate_input_buffers_pending_
|| recreate_output_buffers_pending_
)
468 if (!EnqueueInputRecord())
470 recreate_input_buffers_pending_
= ShouldRecreateInputBuffers();
471 recreate_output_buffers_pending_
= ShouldRecreateOutputBuffers();
473 // Check here because we cannot STREAMON before QBUF in earlier kernel.
474 if (!input_streamon_
&& InputBufferQueuedCount()) {
475 __u32 type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
476 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON
, &type
);
477 input_streamon_
= true;
481 void V4L2JpegDecodeAccelerator::EnqueueOutput() {
482 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
483 // Output record can be enqueued because the output coded sizes of the frames
484 // currently in the pipeline are all the same.
485 while (running_jobs_
.size() > OutputBufferQueuedCount() &&
486 !free_output_buffers_
.empty()) {
487 if (!EnqueueOutputRecord())
490 // Check here because we cannot STREAMON before QBUF in earlier kernel.
491 if (!output_streamon_
&& OutputBufferQueuedCount()) {
492 __u32 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
493 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON
, &type
);
494 output_streamon_
= true;
498 void V4L2JpegDecodeAccelerator::Dequeue() {
499 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
501 // Dequeue completed input (VIDEO_OUTPUT) buffers,
502 // and recycle to the free list.
503 struct v4l2_buffer dqbuf
;
504 while (InputBufferQueuedCount() > 0) {
505 DCHECK(input_streamon_
);
506 memset(&dqbuf
, 0, sizeof(dqbuf
));
507 dqbuf
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
508 dqbuf
.memory
= V4L2_MEMORY_MMAP
;
509 if (device_
->Ioctl(VIDIOC_DQBUF
, &dqbuf
) != 0) {
510 if (errno
== EAGAIN
) {
511 // EAGAIN if we're just out of buffers to dequeue.
514 PLOG(ERROR
) << "ioctl() failed: input buffer VIDIOC_DQBUF failed.";
515 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
518 BufferRecord
& input_record
= input_buffer_map_
[dqbuf
.index
];
519 DCHECK(input_record
.at_device
);
520 input_record
.at_device
= false;
521 free_input_buffers_
.push_back(dqbuf
.index
);
523 if (dqbuf
.flags
& V4L2_BUF_FLAG_ERROR
) {
524 DVLOG(1) << "Dequeue input buffer error.";
525 PostNotifyError(kInvalidBitstreamBufferId
, UNSUPPORTED_JPEG
);
530 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
531 // Return the finished buffer to the client via the job ready callback.
532 // If dequeued input buffer has an error, the error frame has removed from
533 // |running_jobs_|. We check the size of |running_jobs_| instead of
534 // OutputBufferQueueCount() to avoid calling DQBUF unnecessarily.
535 while (!running_jobs_
.empty()) {
536 DCHECK(OutputBufferQueuedCount() > 0);
537 DCHECK(output_streamon_
);
538 memset(&dqbuf
, 0, sizeof(dqbuf
));
539 dqbuf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
540 dqbuf
.memory
= V4L2_MEMORY_MMAP
;
541 if (device_
->Ioctl(VIDIOC_DQBUF
, &dqbuf
) != 0) {
542 if (errno
== EAGAIN
) {
543 // EAGAIN if we're just out of buffers to dequeue.
546 PLOG(ERROR
) << "ioctl() failed: output buffer VIDIOC_DQBUF failed.";
547 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
550 BufferRecord
& output_record
= output_buffer_map_
[dqbuf
.index
];
551 DCHECK(output_record
.at_device
);
552 output_record
.at_device
= false;
553 free_output_buffers_
.push_back(dqbuf
.index
);
555 // Jobs are always processed in FIFO order.
556 linked_ptr
<JobRecord
> job_record
= running_jobs_
.front();
559 if (dqbuf
.flags
& V4L2_BUF_FLAG_ERROR
) {
560 DVLOG(1) << "Dequeue output buffer error.";
561 PostNotifyError(kInvalidBitstreamBufferId
, UNSUPPORTED_JPEG
);
563 memcpy(job_record
->out_frame
->data(media::VideoFrame::kYPlane
),
564 output_record
.address
, output_record
.length
);
566 DVLOG(3) << "Decoding finished, returning bitstream buffer, id="
567 << job_record
->bitstream_buffer
.id();
569 child_task_runner_
->PostTask(
571 base::Bind(&V4L2JpegDecodeAccelerator::VideoFrameReady
, weak_ptr_
,
572 job_record
->bitstream_buffer
.id()));
577 bool V4L2JpegDecodeAccelerator::EnqueueInputRecord() {
578 DCHECK(!input_jobs_
.empty());
579 DCHECK(!free_input_buffers_
.empty());
581 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
582 linked_ptr
<JobRecord
> job_record
= input_jobs_
.front();
584 const int index
= free_input_buffers_
.back();
585 BufferRecord
& input_record
= input_buffer_map_
[index
];
586 DCHECK(!input_record
.at_device
);
588 struct v4l2_buffer qbuf
;
589 memset(&qbuf
, 0, sizeof(qbuf
));
590 memcpy(input_record
.address
, job_record
->shm
->memory(),
591 job_record
->bitstream_buffer
.size());
593 qbuf
.type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
594 qbuf
.memory
= V4L2_MEMORY_MMAP
;
595 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF
, &qbuf
);
596 input_record
.at_device
= true;
597 running_jobs_
.push(job_record
);
598 free_input_buffers_
.pop_back();
600 DVLOG(3) << __func__
<< ": enqueued frame id="
601 << job_record
->bitstream_buffer
.id() << " to device.";
605 bool V4L2JpegDecodeAccelerator::EnqueueOutputRecord() {
606 DCHECK(!free_output_buffers_
.empty());
608 // Enqueue an output (VIDEO_CAPTURE) buffer.
609 const int index
= free_output_buffers_
.back();
610 BufferRecord
& output_record
= output_buffer_map_
[index
];
611 DCHECK(!output_record
.at_device
);
612 struct v4l2_buffer qbuf
;
613 memset(&qbuf
, 0, sizeof(qbuf
));
615 qbuf
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
616 qbuf
.memory
= V4L2_MEMORY_MMAP
;
617 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF
, &qbuf
);
618 output_record
.at_device
= true;
619 free_output_buffers_
.pop_back();
623 void V4L2JpegDecodeAccelerator::ResetQueues() {
624 if (input_streamon_
) {
625 __u32 type
= V4L2_BUF_TYPE_VIDEO_OUTPUT
;
626 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMOFF
, &type
);
627 input_streamon_
= false;
630 if (output_streamon_
) {
631 __u32 type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
632 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMOFF
, &type
);
633 output_streamon_
= false;
636 free_input_buffers_
.clear();
637 for (size_t i
= 0; i
< input_buffer_map_
.size(); ++i
) {
638 BufferRecord
& input_record
= input_buffer_map_
[i
];
639 input_record
.at_device
= false;
640 free_input_buffers_
.push_back(i
);
643 free_output_buffers_
.clear();
644 for (size_t i
= 0; i
< output_buffer_map_
.size(); ++i
) {
645 BufferRecord
& output_record
= output_buffer_map_
[i
];
646 output_record
.at_device
= false;
647 free_output_buffers_
.push_back(i
);
651 void V4L2JpegDecodeAccelerator::StartDevicePoll() {
652 DVLOG(3) << __func__
<< ": starting device poll";
653 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
654 DCHECK(!device_poll_thread_
.IsRunning());
656 if (!device_poll_thread_
.Start()) {
657 LOG(ERROR
) << "StartDevicePoll(): Device thread failed to start";
658 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
661 device_poll_task_runner_
= device_poll_thread_
.task_runner();
664 bool V4L2JpegDecodeAccelerator::StopDevicePoll() {
665 DVLOG(3) << __func__
<< ": stopping device poll";
666 // Signal the DevicePollTask() to stop, and stop the device poll thread.
667 if (!device_
->SetDevicePollInterrupt()) {
668 LOG(ERROR
) << "StopDevicePoll(): SetDevicePollInterrupt failed.";
669 PostNotifyError(kInvalidBitstreamBufferId
, PLATFORM_FAILURE
);
673 device_poll_thread_
.Stop();
675 // Clear the interrupt now, to be sure.
676 if (!device_
->ClearDevicePollInterrupt())
682 } // namespace content