Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / common / gpu / media / v4l2_jpeg_decode_accelerator.cc
blob149f186ae543401598982a0bec8c6162a1ff6347
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>
6 #include <sys/mman.h>
8 #include "base/bind.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) \
13 do { \
14 if (device_->Ioctl(type, arg) != 0) { \
15 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_name; \
16 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE); \
17 return value; \
18 } \
19 } while (0)
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) \
28 do { \
29 if (device_->Ioctl(type, arg) != 0) { \
30 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
31 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE); \
32 } \
33 } while (0)
35 namespace content {
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),
60 client_(nullptr),
61 device_(device),
62 decoder_thread_("V4L2JpegDecodeThread"),
63 device_poll_thread_("V4L2JpegDecodeDevicePollThread"),
64 input_streamon_(false),
65 output_streamon_(false),
66 weak_factory_(this) {
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())
86 input_jobs_.pop();
87 while (!running_jobs_.empty())
88 running_jobs_.pop();
90 // Stop streaming and the device_poll_thread_.
91 StopDevicePoll();
93 ResetQueues();
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,
104 Error error) {
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,
113 Error error) {
114 child_task_runner_->PostTask(
115 FROM_HERE,
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";
129 return false;
131 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
132 LOG(ERROR) << "Initialize(): VIDIOC_QUERYCAP, caps check failed: 0x"
133 << std::hex << caps.capabilities;
134 return false;
137 if (!decoder_thread_.Start()) {
138 LOG(ERROR) << "Initialize(): decoder thread failed to start";
139 return false;
141 client_ = client;
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.";
149 return true;
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);
161 return;
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)
179 return true;
181 return false;
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);
191 return;
193 input_jobs_.push(make_linked_ptr(job_record.release()));
195 ServiceDeviceTask();
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())
209 return false;
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())
220 return false;
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) {
229 return true;
232 return false;
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_)
242 return true;
244 // If running queue is not empty, we should wait until pending frames finish.
245 if (!running_jobs_.empty())
246 return true;
248 if (input_streamon_ || output_streamon_) {
249 ResetQueues();
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.";
259 return false;
261 if (recreate_output_buffers_pending_ && !CreateOutputBuffers()) {
262 LOG(ERROR) << "Create output buffers failed.";
263 return false;
266 return true;
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));
303 buffer.index = i;
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);
312 return false;
314 input_buffer_map_[i].address = address;
315 input_buffer_map_[i].length = buffer.length;
317 recreate_input_buffers_pending_ = false;
319 return true;
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));
356 buffer.index = i;
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);
365 return false;
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;
373 return true;
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));
387 reqbufs.count = 0;
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));
407 reqbufs.count = 0;
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());
419 bool event_pending;
420 if (!device_->Poll(true, &event_pending)) {
421 PLOG(ERROR) << "DevicePollTask(): Poll device error.";
422 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
423 return;
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())
437 return;
439 if (!running_jobs_.empty())
440 Dequeue();
441 if (!CreateBuffersIfNecessary())
442 return;
444 EnqueueInput();
445 EnqueueOutput();
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_)
467 break;
468 if (!EnqueueInputRecord())
469 return;
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())
488 return;
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.
512 break;
514 PLOG(ERROR) << "ioctl() failed: input buffer VIDIOC_DQBUF failed.";
515 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
516 return;
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);
526 running_jobs_.pop();
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.
544 break;
546 PLOG(ERROR) << "ioctl() failed: output buffer VIDIOC_DQBUF failed.";
547 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
548 return;
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();
557 running_jobs_.pop();
559 if (dqbuf.flags & V4L2_BUF_FLAG_ERROR) {
560 DVLOG(1) << "Dequeue output buffer error.";
561 PostNotifyError(kInvalidBitstreamBufferId, UNSUPPORTED_JPEG);
562 } else {
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(
570 FROM_HERE,
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();
583 input_jobs_.pop();
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());
592 qbuf.index = index;
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.";
602 return true;
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));
614 qbuf.index = index;
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();
620 return true;
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);
659 return;
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);
670 return false;
673 device_poll_thread_.Stop();
675 // Clear the interrupt now, to be sure.
676 if (!device_->ClearDevicePollInterrupt())
677 return false;
679 return true;
682 } // namespace content