Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1363)

Side by Side Diff: content/common/gpu/media/v4l2_jpeg_decode_accelerator.cc

Issue 1125263005: MJPEG acceleration for V4L2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix nit Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include <linux/videodev2.h>
6 #include <sys/mman.h>
7
8 #include "base/bind.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h"
11
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)
20
21 #define IOCTL_OR_ERROR_RETURN(type, arg) \
22 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type)
23
24 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
25 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type)
26
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)
34
35 namespace content {
36
37 V4L2JpegDecodeAccelerator::BufferRecord::BufferRecord()
38 : address(nullptr), length(0), at_device(false) {
39 }
40
41 V4L2JpegDecodeAccelerator::BufferRecord::~BufferRecord() {
42 }
43
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) {
48 }
49
50 V4L2JpegDecodeAccelerator::JobRecord::~JobRecord() {
51 }
52
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();
68 }
69
70 V4L2JpegDecodeAccelerator::~V4L2JpegDecodeAccelerator() {
71 DCHECK(child_task_runner_->BelongsToCurrentThread());
72
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();
78 }
79 weak_factory_.InvalidateWeakPtrs();
80 DCHECK(!device_poll_thread_.IsRunning());
81 }
82
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();
89
90 // Stop streaming and the device_poll_thread_.
91 StopDevicePoll();
92
93 ResetQueues();
94 DestroyInputBuffers();
95 DestroyOutputBuffers();
96 }
97
98 void V4L2JpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) {
99 DCHECK(child_task_runner_->BelongsToCurrentThread());
100 client_->VideoFrameReady(bitstream_buffer_id);
101 }
102
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);
109 }
110
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));
118 }
119
120 bool V4L2JpegDecodeAccelerator::Initialize(Client* client) {
121 DCHECK(child_task_runner_->BelongsToCurrentThread());
122
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;
130 }
131 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
132 LOG(ERROR) << "Initialize(): VIDIOC_QUERYCAP, caps check failed: 0x"
133 << std::hex << caps.capabilities;
134 return false;
135 }
136
137 if (!decoder_thread_.Start()) {
138 LOG(ERROR) << "Initialize(): decoder thread failed to start";
139 return false;
140 }
141 client_ = client;
142 decoder_task_runner_ = decoder_thread_.task_runner();
143
144 decoder_task_runner_->PostTask(
145 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::StartDevicePoll,
146 base::Unretained(this)));
147
148 DVLOG(1) << "V4L2JpegDecodeAccelerator initialized.";
149 return true;
150 }
151
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());
158
159 if (video_frame->format() != media::VideoFrame::I420) {
160 PostNotifyError(bitstream_buffer.id(), UNSUPPORTED_JPEG);
161 return;
162 }
163
164 scoped_ptr<JobRecord> job_record(
165 new JobRecord(bitstream_buffer, video_frame));
166
167 decoder_task_runner_->PostTask(
168 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DecodeTask,
169 base::Unretained(this), base::Passed(&job_record)));
170 }
171
172 void V4L2JpegDecodeAccelerator::DecodeTask(scoped_ptr<JobRecord> job_record) {
173 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
174 job_record->shm.reset(
175 new base::SharedMemory(job_record->bitstream_buffer.handle(), true));
176 if (!job_record->shm->Map(job_record->bitstream_buffer.size())) {
177 PLOG(ERROR) << "DecodeTask(): could not map bitstream_buffer";
178 PostNotifyError(job_record->bitstream_buffer.id(), UNREADABLE_INPUT);
179 return;
180 }
181 input_jobs_.push(make_linked_ptr(job_record.release()));
182
183 ServiceDeviceTask();
184 }
185
186 size_t V4L2JpegDecodeAccelerator::InputBufferQueuedCount() {
187 return input_buffer_map_.size() - free_input_buffers_.size();
188 }
189
190 size_t V4L2JpegDecodeAccelerator::OutputBufferQueuedCount() {
191 return output_buffer_map_.size() - free_output_buffers_.size();
192 }
193
194 bool V4L2JpegDecodeAccelerator::ShouldRecreateInputBuffers() {
195 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
196 if (input_jobs_.empty())
197 return false;
198
199 linked_ptr<JobRecord> job_record = input_jobs_.front();
200 // Check input buffer size is enough
201 return (input_buffer_map_.empty() ||
202 job_record->bitstream_buffer.size() > input_buffer_map_.front().length);
203 }
204
205 bool V4L2JpegDecodeAccelerator::ShouldRecreateOutputBuffers() {
206 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
207 if (input_jobs_.empty())
208 return false;
209
210 linked_ptr<JobRecord> job_record = input_jobs_.front();
211 // Check image resolution is the same as previous.
212 if (job_record->out_frame->coded_size() != image_coded_size_) {
213 size_t frame_size = media::VideoFrame::AllocationSize(
214 job_record->out_frame->format(), job_record->out_frame->coded_size());
215 if (output_buffer_map_.empty() ||
216 frame_size > output_buffer_map_.front().length) {
217 return true;
218 }
219 }
220 return false;
221 }
222
223 bool V4L2JpegDecodeAccelerator::CreateBuffersIfNecessary() {
224 DVLOG(3) << __func__;
225 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
226
227 recreate_input_buffers_pending_ = ShouldRecreateInputBuffers();
228 recreate_output_buffers_pending_ = ShouldRecreateOutputBuffers();
229 if (!recreate_input_buffers_pending_ && !recreate_output_buffers_pending_)
230 return true;
231
232 // If running queue is not empty, we should wait until pending frames finish.
233 if (!running_jobs_.empty())
234 return true;
235
236 if (input_streamon_ || output_streamon_) {
237 ResetQueues();
238 if (recreate_input_buffers_pending_)
239 DestroyInputBuffers();
240
241 if (recreate_output_buffers_pending_)
242 DestroyOutputBuffers();
243 }
244
245 if (recreate_input_buffers_pending_ && !CreateInputBuffers()) {
246 LOG(ERROR) << "Create input buffers failed.";
247 return false;
248 }
249 if (recreate_output_buffers_pending_ && !CreateOutputBuffers()) {
250 LOG(ERROR) << "Create output buffers failed.";
251 return false;
252 }
253
254 return true;
255 }
256
257 bool V4L2JpegDecodeAccelerator::CreateInputBuffers() {
258 DVLOG(3) << __func__;
259 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
260 DCHECK(!input_streamon_);
261 DCHECK(!input_jobs_.empty());
262 linked_ptr<JobRecord> job_record = input_jobs_.front();
263 // Reserve twice size to avoid recreating input buffer frequently.
264 size_t reserve_size = job_record->bitstream_buffer.size() * 2;
265
266 struct v4l2_format format;
267 memset(&format, 0, sizeof(format));
268 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
269 format.fmt.pix.width = job_record->out_frame->coded_size().width();
270 format.fmt.pix.height = job_record->out_frame->coded_size().height();
271 format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
272 format.fmt.pix.sizeimage = reserve_size;
273 format.fmt.pix.field = V4L2_FIELD_ANY;
274 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
275
276 struct v4l2_requestbuffers reqbufs;
277 memset(&reqbufs, 0, sizeof(reqbufs));
278 reqbufs.count = kBufferCount;
279 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
280 reqbufs.memory = V4L2_MEMORY_MMAP;
281 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
282
283 DCHECK(input_buffer_map_.empty());
284 input_buffer_map_.resize(reqbufs.count);
285
286 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
287 free_input_buffers_.push_back(i);
288
289 struct v4l2_buffer buffer;
290 memset(&buffer, 0, sizeof(buffer));
291 buffer.index = i;
292 buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
293 buffer.memory = V4L2_MEMORY_MMAP;
294 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
295 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
296 MAP_SHARED, buffer.m.offset);
297 if (address == MAP_FAILED) {
298 PLOG(ERROR) << "CreateInputBuffers(): mmap() failed";
299 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
300 return false;
301 }
302 input_buffer_map_[i].address = address;
303 input_buffer_map_[i].length = buffer.length;
304 }
305 recreate_input_buffers_pending_ = false;
306
307 return true;
308 }
309
310 bool V4L2JpegDecodeAccelerator::CreateOutputBuffers() {
311 DVLOG(3) << __func__;
312 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
313 DCHECK(!output_streamon_);
314 DCHECK(!input_jobs_.empty());
315 linked_ptr<JobRecord> job_record = input_jobs_.front();
316
317 size_t frame_size = media::VideoFrame::AllocationSize(
318 media::VideoFrame::I420, job_record->out_frame->coded_size());
319 struct v4l2_format format;
320 memset(&format, 0, sizeof(format));
321 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
322 format.fmt.pix.width = job_record->out_frame->coded_size().width();
323 format.fmt.pix.height = job_record->out_frame->coded_size().height();
324 format.fmt.pix.sizeimage = frame_size;
325 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
326 format.fmt.pix.field = V4L2_FIELD_ANY;
327 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
328
329 struct v4l2_requestbuffers reqbufs;
330 memset(&reqbufs, 0, sizeof(reqbufs));
331 reqbufs.count = kBufferCount;
332 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
333 reqbufs.memory = V4L2_MEMORY_MMAP;
334 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
335
336 DCHECK(output_buffer_map_.empty());
337 output_buffer_map_.resize(reqbufs.count);
338
339 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
340 free_output_buffers_.push_back(i);
341
342 struct v4l2_buffer buffer;
343 memset(&buffer, 0, sizeof(buffer));
344 buffer.index = i;
345 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
346 buffer.memory = V4L2_MEMORY_MMAP;
347 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
348 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
349 MAP_SHARED, buffer.m.offset);
350 if (address == MAP_FAILED) {
351 PLOG(ERROR) << "CreateOutputBuffers(): mmap() failed";
352 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
353 return false;
354 }
355 output_buffer_map_[i].address = address;
356 output_buffer_map_[i].length = buffer.length;
357 }
358 image_coded_size_ = job_record->out_frame->coded_size();
359 recreate_output_buffers_pending_ = false;
360
361 return true;
362 }
363
364 void V4L2JpegDecodeAccelerator::DestroyInputBuffers() {
365 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
366 DCHECK(!input_streamon_);
367
368 for (size_t buf = 0; buf < input_buffer_map_.size(); ++buf) {
369 BufferRecord& input_record = input_buffer_map_[buf];
370 device_->Munmap(input_record.address, input_record.length);
371 }
372
373 struct v4l2_requestbuffers reqbufs;
374 memset(&reqbufs, 0, sizeof(reqbufs));
375 reqbufs.count = 0;
376 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
377 reqbufs.memory = V4L2_MEMORY_MMAP;
378 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
379
380 input_buffer_map_.clear();
381 free_input_buffers_.clear();
382 }
383
384 void V4L2JpegDecodeAccelerator::DestroyOutputBuffers() {
385 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
386 DCHECK(!output_streamon_);
387
388 for (size_t buf = 0; buf < output_buffer_map_.size(); ++buf) {
389 BufferRecord& output_record = output_buffer_map_[buf];
390 device_->Munmap(output_record.address, output_record.length);
391 }
392
393 struct v4l2_requestbuffers reqbufs;
394 memset(&reqbufs, 0, sizeof(reqbufs));
395 reqbufs.count = 0;
396 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
397 reqbufs.memory = V4L2_MEMORY_MMAP;
398 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
399
400 output_buffer_map_.clear();
401 free_output_buffers_.clear();
402 }
403
404 void V4L2JpegDecodeAccelerator::DevicePollTask() {
405 DCHECK(device_poll_task_runner_->BelongsToCurrentThread());
406
407 bool event_pending;
408 if (!device_->Poll(true, &event_pending)) {
409 PLOG(ERROR) << "DevicePollTask(): Poll device error.";
410 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
411 return;
412 }
413
414 // All processing should happen on ServiceDeviceTask(), since we shouldn't
415 // touch decoder state from this thread.
416 decoder_task_runner_->PostTask(
417 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::ServiceDeviceTask,
418 base::Unretained(this)));
419 }
420
421 void V4L2JpegDecodeAccelerator::ServiceDeviceTask() {
422 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
423 // If DestroyTask() shuts |device_poll_thread_| down, we should early-out.
424 if (!device_poll_thread_.IsRunning())
425 return;
426
427 if (!running_jobs_.empty())
428 Dequeue();
429 if (!CreateBuffersIfNecessary())
430 return;
431
432 EnqueueInput();
433 EnqueueOutput();
434
435 if (!running_jobs_.empty()) {
436 device_poll_task_runner_->PostTask(
437 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DevicePollTask,
438 base::Unretained(this)));
439 }
440
441 DVLOG(2) << __func__ << ": buffer counts: INPUT["
442 << input_jobs_.size() << "] => DEVICE["
443 << free_input_buffers_.size() << "/"
444 << input_buffer_map_.size() << "->"
445 << free_output_buffers_.size() << "/"
446 << output_buffer_map_.size() << "]";
447 }
448
449 void V4L2JpegDecodeAccelerator::EnqueueInput() {
450 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
451 while (!input_jobs_.empty() && !free_input_buffers_.empty()) {
452 // Do not enqueue input record when input/output buffers are required to
453 // re-create until all pending frames are handled by device.
454 if (recreate_input_buffers_pending_ || recreate_output_buffers_pending_)
455 break;
456 if (!EnqueueInputRecord())
457 return;
458 recreate_input_buffers_pending_ = ShouldRecreateInputBuffers();
459 recreate_output_buffers_pending_ = ShouldRecreateOutputBuffers();
460 }
461 // Check here because we cannot STREAMON before QBUF in earlier kernel.
462 if (!input_streamon_ && InputBufferQueuedCount()) {
463 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
464 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
465 input_streamon_ = true;
466 }
467 }
468
469 void V4L2JpegDecodeAccelerator::EnqueueOutput() {
470 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
471 // Output record can be enqueued because the output coded sizes of the frames
472 // currently in the pipeline are all the same.
473 while (running_jobs_.size() > OutputBufferQueuedCount() &&
474 !free_output_buffers_.empty()) {
475 if (!EnqueueOutputRecord())
476 return;
477 }
478 // Check here because we cannot STREAMON before QBUF in earlier kernel.
479 if (!output_streamon_ && OutputBufferQueuedCount()) {
480 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
481 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
482 output_streamon_ = true;
483 }
484 }
485
486 void V4L2JpegDecodeAccelerator::Dequeue() {
487 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
488
489 // Dequeue completed input (VIDEO_OUTPUT) buffers,
490 // and recycle to the free list.
491 struct v4l2_buffer dqbuf;
492 while (InputBufferQueuedCount() > 0) {
493 DCHECK(input_streamon_);
494 memset(&dqbuf, 0, sizeof(dqbuf));
495 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
496 dqbuf.memory = V4L2_MEMORY_MMAP;
497 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
498 if (errno == EAGAIN) {
499 // EAGAIN if we're just out of buffers to dequeue.
500 break;
501 }
502 PLOG(ERROR) << "ioctl() failed: input buffer VIDIOC_DQBUF failed.";
503 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
504 return;
505 }
506 BufferRecord& input_record = input_buffer_map_[dqbuf.index];
507 DCHECK(input_record.at_device);
508 input_record.at_device = false;
509 free_input_buffers_.push_back(dqbuf.index);
510
511 if (dqbuf.flags & V4L2_BUF_FLAG_ERROR) {
512 DVLOG(1) << "Dequeue input buffer error.";
513 PostNotifyError(kInvalidBitstreamBufferId, UNSUPPORTED_JPEG);
514 running_jobs_.pop();
515 }
516 }
517
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 // If dequeued input buffer has an error, the error frame has removed from
521 // |running_jobs_|. We check the size of |running_jobs_| instead of
522 // OutputBufferQueueCount() to avoid calling DQBUF unnecessarily.
523 while (!running_jobs_.empty()) {
524 DCHECK(OutputBufferQueuedCount() > 0);
525 DCHECK(output_streamon_);
526 memset(&dqbuf, 0, sizeof(dqbuf));
527 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
528 dqbuf.memory = V4L2_MEMORY_MMAP;
529 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
530 if (errno == EAGAIN) {
531 // EAGAIN if we're just out of buffers to dequeue.
532 break;
533 }
534 PLOG(ERROR) << "ioctl() failed: output buffer VIDIOC_DQBUF failed.";
535 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
536 return;
537 }
538 BufferRecord& output_record = output_buffer_map_[dqbuf.index];
539 DCHECK(output_record.at_device);
540 output_record.at_device = false;
541 free_output_buffers_.push_back(dqbuf.index);
542
543 // Jobs are always processed in FIFO order.
544 linked_ptr<JobRecord> job_record = running_jobs_.front();
545 running_jobs_.pop();
546
547 if (dqbuf.flags & V4L2_BUF_FLAG_ERROR) {
548 DVLOG(1) << "Dequeue output buffer error.";
549 PostNotifyError(kInvalidBitstreamBufferId, UNSUPPORTED_JPEG);
550 } else {
551 memcpy(job_record->out_frame->data(media::VideoFrame::kYPlane),
552 output_record.address, output_record.length);
553
554 DVLOG(3) << "Decoding finished, returning bitstream buffer, id="
555 << job_record->bitstream_buffer.id();
556
557 child_task_runner_->PostTask(
558 FROM_HERE,
559 base::Bind(&V4L2JpegDecodeAccelerator::VideoFrameReady, weak_ptr_,
560 job_record->bitstream_buffer.id()));
561 }
562 }
563 }
564
565 bool V4L2JpegDecodeAccelerator::EnqueueInputRecord() {
566 DCHECK(!input_jobs_.empty());
567 DCHECK(!free_input_buffers_.empty());
568
569 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
570 linked_ptr<JobRecord> job_record = input_jobs_.front();
571 input_jobs_.pop();
572 const int index = free_input_buffers_.back();
573 BufferRecord& input_record = input_buffer_map_[index];
574 DCHECK(!input_record.at_device);
575
576 struct v4l2_buffer qbuf;
577 memset(&qbuf, 0, sizeof(qbuf));
578 memcpy(input_record.address, job_record->shm->memory(),
579 job_record->bitstream_buffer.size());
580 qbuf.index = index;
581 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
582 qbuf.memory = V4L2_MEMORY_MMAP;
583 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
584 input_record.at_device = true;
585 running_jobs_.push(job_record);
586 free_input_buffers_.pop_back();
587
588 DVLOG(3) << __func__ << ": enqueued frame id="
589 << job_record->bitstream_buffer.id() << " to device.";
590 return true;
591 }
592
593 bool V4L2JpegDecodeAccelerator::EnqueueOutputRecord() {
594 DCHECK(!free_output_buffers_.empty());
595
596 // Enqueue an output (VIDEO_CAPTURE) buffer.
597 const int index = free_output_buffers_.back();
598 BufferRecord& output_record = output_buffer_map_[index];
599 DCHECK(!output_record.at_device);
600 struct v4l2_buffer qbuf;
601 memset(&qbuf, 0, sizeof(qbuf));
602 qbuf.index = index;
603 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
604 qbuf.memory = V4L2_MEMORY_MMAP;
605 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
606 output_record.at_device = true;
607 free_output_buffers_.pop_back();
608 return true;
609 }
610
611 void V4L2JpegDecodeAccelerator::ResetQueues() {
612 if (input_streamon_) {
613 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
614 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMOFF, &type);
615 input_streamon_ = false;
616 }
617
618 if (output_streamon_) {
619 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
620 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMOFF, &type);
621 output_streamon_ = false;
622 }
623
624 free_input_buffers_.clear();
625 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
626 BufferRecord& input_record = input_buffer_map_[i];
627 input_record.at_device = false;
628 free_input_buffers_.push_back(i);
629 }
630
631 free_output_buffers_.clear();
632 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
633 BufferRecord& output_record = output_buffer_map_[i];
634 output_record.at_device = false;
635 free_output_buffers_.push_back(i);
636 }
637 }
638
639 void V4L2JpegDecodeAccelerator::StartDevicePoll() {
640 DVLOG(3) << __func__ << ": starting device poll";
641 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
642 DCHECK(!device_poll_thread_.IsRunning());
643
644 if (!device_poll_thread_.Start()) {
645 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
646 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
647 return;
648 }
649 device_poll_task_runner_ = device_poll_thread_.task_runner();
650 }
651
652 bool V4L2JpegDecodeAccelerator::StopDevicePoll() {
653 DVLOG(3) << __func__ << ": stopping device poll";
654 // Signal the DevicePollTask() to stop, and stop the device poll thread.
655 if (!device_->SetDevicePollInterrupt()) {
656 LOG(ERROR) << "StopDevicePoll(): SetDevicePollInterrupt failed.";
657 PostNotifyError(kInvalidBitstreamBufferId, PLATFORM_FAILURE);
658 return false;
659 }
660
661 device_poll_thread_.Stop();
662
663 // Clear the interrupt now, to be sure.
664 if (!device_->ClearDevicePollInterrupt())
665 return false;
666
667 return true;
668 }
669
670 } // namespace content
OLDNEW
« no previous file with comments | « content/common/gpu/media/v4l2_jpeg_decode_accelerator.h ('k') | content/common/sandbox_linux/bpf_gpu_policy_linux.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698