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

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: address review comments of patch set 6 Created 5 years, 6 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 return value; \
17 } \
18 } while (0)
19
20 #define IOCTL_OR_ERROR_RETURN(type, arg) \
21 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type)
22
23 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
24 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type)
25
26 #define IOCTL_OR_LOG_ERROR(type, arg) \
27 do { \
28 if (device_->Ioctl(type, arg) != 0) \
29 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
30 } while (0)
31
32 namespace content {
33
34 V4L2JpegDecodeAccelerator::BufferRecord::BufferRecord()
35 : address(nullptr), length(0), at_device(false) {
wuchengli 2015/06/25 08:29:53 |at_device| is only used by DCHECK. It's fine if t
henryhsu 2015/06/26 03:39:32 When we enqueue a buffer to device, we will set at
wuchengli 2015/06/26 23:42:48 |at_device| is only set to true and false in vario
36 }
37
38 V4L2JpegDecodeAccelerator::BufferRecord::~BufferRecord() {
39 }
40
41 V4L2JpegDecodeAccelerator::JobRecord::JobRecord(
42 media::BitstreamBuffer bitstream_buffer,
43 scoped_refptr<media::VideoFrame> video_frame)
44 : bitstream_buffer(bitstream_buffer), out_frame(video_frame) {
45 }
46
47 V4L2JpegDecodeAccelerator::JobRecord::~JobRecord() {
48 }
49
50 V4L2JpegDecodeAccelerator::V4L2JpegDecodeAccelerator(
51 const scoped_refptr<V4L2Device>& device,
52 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
53 : recreate_input_buffers_pending_(false),
54 recreate_output_buffers_pending_(false),
55 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
56 io_task_runner_(io_task_runner),
57 client_(nullptr),
58 device_(device),
59 decoder_thread_("V4L2JpegDecodeThread"),
60 device_poll_thread_("V4L2JpegDecodeDevicePollThread"),
61 input_streamon_(false),
62 output_streamon_(false),
63 weak_factory_(this) {
64 }
65
66 V4L2JpegDecodeAccelerator::~V4L2JpegDecodeAccelerator() {
67 DCHECK(child_task_runner_->BelongsToCurrentThread());
68
69 if (decoder_thread_.IsRunning()) {
70 decoder_task_runner_->PostTask(
71 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DestroyTask,
72 base::Unretained(this)));
73 decoder_thread_.Stop();
74 }
75 weak_factory_.InvalidateWeakPtrs();
76 DCHECK(!device_poll_thread_.IsRunning());
77 }
78
79 void V4L2JpegDecodeAccelerator::DestroyTask() {
80 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
81 while (!input_jobs_.empty()) input_jobs_.pop();
82 while (!running_jobs_.empty()) running_jobs_.pop();
83
84 // Stop streaming and the device_poll_thread_.
85 StopDevicePoll();
86
87 DestroyInputBuffers();
88 DestroyOutputBuffers();
89 }
90
91 void V4L2JpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id,
92 Error error) {
93 DCHECK(child_task_runner_->BelongsToCurrentThread());
94 LOG(ERROR) << "Notifying of error " << error << " for buffer id "
95 << bitstream_buffer_id;
96 client_->NotifyError(bitstream_buffer_id, error);
97 }
98
99 void V4L2JpegDecodeAccelerator::PostNotifyError(
100 int32_t bitstream_buffer_id,
101 Error error) {
102 child_task_runner_->PostTask(
103 FROM_HERE,
104 base::Bind(&V4L2JpegDecodeAccelerator::NotifyError,
105 weak_factory_.GetWeakPtr(), bitstream_buffer_id, error));
106 }
107
108 bool V4L2JpegDecodeAccelerator::Initialize(Client* client) {
109 DCHECK(child_task_runner_->BelongsToCurrentThread());
110
111 // Capabilities check.
112 struct v4l2_capability caps;
113 const __u32 kCapsRequired = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
114 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
115 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
116 LOG(ERROR) << "Initialize(): VIDIOC_QUERYCAP, caps check failed: 0x"
117 << std::hex << caps.capabilities;
118 return false;
119 }
120
121 if (!decoder_thread_.Start()) {
122 LOG(ERROR) << "Initialize(): decoder thread failed to start";
123 return false;
124 }
125 client_ = client;
126 decoder_task_runner_ = decoder_thread_.task_runner();
127
128 decoder_task_runner_->PostTask(
129 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::StartDevicePoll,
130 base::Unretained(this)));
131
132 DVLOG(1) << "V4L2JpegDecodeAccelerator initialized.";
133 return true;
134 }
135
136 void V4L2JpegDecodeAccelerator::Decode(
137 const media::BitstreamBuffer& bitstream_buffer,
138 const scoped_refptr<media::VideoFrame>& video_frame) {
139 DVLOG(1) << "Decode(): input_id=" << bitstream_buffer.id()
140 << ", size=" << bitstream_buffer.size();
141 DCHECK(io_task_runner_->BelongsToCurrentThread());
142 DCHECK_EQ(video_frame->format(), media::VideoFrame::I420);
143
144 scoped_ptr<JobRecord> job_record(
145 new JobRecord(bitstream_buffer, video_frame));
146
147 decoder_task_runner_->PostTask(
148 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DecodeTask,
149 base::Unretained(this), base::Passed(&job_record)));
150 }
151
152 void V4L2JpegDecodeAccelerator::DecodeTask(scoped_ptr<JobRecord> job_record) {
153 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
154 job_record->shm.reset(
155 new base::SharedMemory(job_record->bitstream_buffer.handle(), true));
156 if (!job_record->shm->Map(job_record->bitstream_buffer.size())) {
157 PLOG(ERROR) << "Decode(): could not map bitstream_buffer";
158 PostNotifyError(job_record->bitstream_buffer.id(), UNREADABLE_INPUT);
159 return;
160 }
161 input_jobs_.push(make_linked_ptr(job_record.release()));
162 ServiceDeviceTask();
163 }
164
165 int V4L2JpegDecodeAccelerator::InputBufferQueuedCount() {
166 return input_buffer_map_.size() - free_input_buffers_.size();
167 }
168
169 int V4L2JpegDecodeAccelerator::OutputBufferQueuedCount() {
170 return output_buffer_map_.size() - free_output_buffers_.size();
171 }
172
173 bool V4L2JpegDecodeAccelerator::ShouldRecreateInputBuffers() {
174 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
175 DCHECK(!input_jobs_.empty());
176 linked_ptr<JobRecord> job_record = input_jobs_.front();
177 // Check input buffer size is enough
178 return (input_buffer_map_.empty() ||
179 job_record->bitstream_buffer.size() > input_buffer_map_.front().length);
180 }
181
182 bool V4L2JpegDecodeAccelerator::ShouldRecreateOutputBuffers() {
183 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
184 DCHECK(!input_jobs_.empty());
185 linked_ptr<JobRecord> job_record = input_jobs_.front();
186 // Check image resolution is the same as previous.
187 if (job_record->out_frame->coded_size() != image_coded_size_) {
188 size_t frame_size = media::VideoFrame::AllocationSize(
189 job_record->out_frame->format(), job_record->out_frame->coded_size());
190 if (output_buffer_map_.empty() ||
191 frame_size > output_buffer_map_.front().length) {
192 return true;
193 }
194 }
195 return false;
196 }
197
198 bool V4L2JpegDecodeAccelerator::CreateBuffersIfNecessary() {
199 DVLOG(3) << __func__;
200 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
201 if (input_jobs_.empty())
202 return false;
wuchengli 2015/06/25 08:29:53 This is not an error. return true? I think this ch
henryhsu 2015/06/26 03:39:32 Done.
203
204 recreate_input_buffers_pending_ = ShouldRecreateInputBuffers();
205 recreate_output_buffers_pending_ = ShouldRecreateOutputBuffers();
206 if (!recreate_input_buffers_pending_ && !recreate_output_buffers_pending_)
207 return true;
208
209 // If queues are not empty, we should wait the pending frames finished.
wuchengli 2015/06/25 08:29:53 s/wait the pending frames finished/wait until pend
henryhsu 2015/06/26 03:39:32 Done.
210 if (InputBufferQueuedCount() || OutputBufferQueuedCount())
211 return true;
212
213 if (input_streamon_ || output_streamon_) {
214 if (!StopDevicePoll()) {
215 LOG(ERROR) << "Stop device poll thread failed when recreating buffers.";
216 return false;
217 }
218 if (recreate_input_buffers_pending_)
219 DestroyInputBuffers();
220
221 if (recreate_output_buffers_pending_)
222 DestroyOutputBuffers();
223 }
224
225 if (recreate_input_buffers_pending_ && !CreateInputBuffers()) {
226 LOG(ERROR) << "Create input buffers failed.";
227 return false;
228 }
229 if (recreate_output_buffers_pending_ && !CreateOutputBuffers()) {
230 LOG(ERROR) << "Create output buffers failed.";
231 return false;
232 }
233
234 if (!device_poll_thread_.IsRunning())
235 StartDevicePoll();
236 return true;
237 }
238
239 bool V4L2JpegDecodeAccelerator::CreateInputBuffers() {
240 DVLOG(3) << __func__;
241 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
242 DCHECK(!input_streamon_);
243 DCHECK(!input_jobs_.empty());
244 linked_ptr<JobRecord> job_record = input_jobs_.front();
245 // Reserve twice size to avoid recreating input buffer frequently.
246 size_t reserve_size = job_record->bitstream_buffer.size() * 2;
247
248 struct v4l2_format format;
249 memset(&format, 0, sizeof(format));
250 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
251 format.fmt.pix.width = job_record->out_frame->coded_size().width();
252 format.fmt.pix.height = job_record->out_frame->coded_size().height();
253 format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
254 format.fmt.pix.sizeimage = reserve_size;
255 format.fmt.pix.field = V4L2_FIELD_ANY;
256 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
257
258 struct v4l2_requestbuffers reqbufs;
259 memset(&reqbufs, 0, sizeof(reqbufs));
260 reqbufs.count = kBufferCount;
261 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
262 reqbufs.memory = V4L2_MEMORY_MMAP;
263 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
264
265 DCHECK(input_buffer_map_.empty());
266 input_buffer_map_.resize(reqbufs.count);
267
268 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
269 free_input_buffers_.push_back(i);
270
271 struct v4l2_buffer buffer;
272 memset(&buffer, 0, sizeof(buffer));
273 buffer.index = i;
274 buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
275 buffer.memory = V4L2_MEMORY_MMAP;
276 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
277 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
278 MAP_SHARED, buffer.m.offset);
279 if (address == MAP_FAILED) {
280 PLOG(ERROR) << "CreateInputBuffers(): mmap() failed";
281 PostNotifyError(job_record->bitstream_buffer.id(),
282 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
283 return false;
284 }
285 input_buffer_map_[i].address = address;
286 input_buffer_map_[i].length = buffer.length;
287 }
288 recreate_input_buffers_pending_ = false;
289
290 return true;
291 }
292
293 bool V4L2JpegDecodeAccelerator::CreateOutputBuffers() {
294 DVLOG(3) << __func__;
295 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
296 DCHECK(!output_streamon_);
297 DCHECK(!input_jobs_.empty());
298 linked_ptr<JobRecord> job_record = input_jobs_.front();
299
300 size_t frame_size = media::VideoFrame::AllocationSize(
301 media::VideoFrame::I420, job_record->out_frame->coded_size());
302 struct v4l2_format format;
303 memset(&format, 0, sizeof(format));
304 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
305 format.fmt.pix.width = job_record->out_frame->coded_size().width();
306 format.fmt.pix.height = job_record->out_frame->coded_size().height();
307 format.fmt.pix.sizeimage = frame_size;
308 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
309 format.fmt.pix.field = V4L2_FIELD_ANY;
310 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
311
312 struct v4l2_requestbuffers reqbufs;
313 memset(&reqbufs, 0, sizeof(reqbufs));
314 reqbufs.count = kBufferCount;
315 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
316 reqbufs.memory = V4L2_MEMORY_MMAP;
317 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
318
319 DCHECK(output_buffer_map_.empty());
320 output_buffer_map_.resize(reqbufs.count);
321
322 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
323 free_output_buffers_.push_back(i);
324
325 struct v4l2_buffer buffer;
326 memset(&buffer, 0, sizeof(buffer));
327 buffer.index = i;
328 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
329 buffer.memory = V4L2_MEMORY_MMAP;
330 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
331 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
332 MAP_SHARED, buffer.m.offset);
333 if (address == MAP_FAILED) {
334 PLOG(ERROR) << "CreateOutputBuffers(): mmap() failed";
335 PostNotifyError(job_record->bitstream_buffer.id(),
336 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
337 return false;
338 }
339 output_buffer_map_[i].address = address;
340 output_buffer_map_[i].length = buffer.length;
341 }
342 image_coded_size_ = job_record->out_frame->coded_size();
343 recreate_output_buffers_pending_ = false;
344
345 return true;
346 }
347
348 void V4L2JpegDecodeAccelerator::DestroyInputBuffers() {
349 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
350 DCHECK(!input_streamon_);
351
352 for (size_t buf = 0; buf < input_buffer_map_.size(); ++buf) {
353 BufferRecord& input_record = input_buffer_map_[buf];
354 device_->Munmap(input_record.address, input_record.length);
355 }
356
357 struct v4l2_requestbuffers reqbufs;
358 memset(&reqbufs, 0, sizeof(reqbufs));
359 reqbufs.count = 0;
360 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
361 reqbufs.memory = V4L2_MEMORY_MMAP;
362 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
363
364 input_buffer_map_.clear();
365 free_input_buffers_.clear();
366 }
367
368 void V4L2JpegDecodeAccelerator::DestroyOutputBuffers() {
369 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
370 DCHECK(!output_streamon_);
371
372 for (size_t buf = 0; buf < output_buffer_map_.size(); ++buf) {
373 BufferRecord& output_record = output_buffer_map_[buf];
374 device_->Munmap(output_record.address, output_record.length);
375 }
376
377 struct v4l2_requestbuffers reqbufs;
378 memset(&reqbufs, 0, sizeof(reqbufs));
379 reqbufs.count = 0;
380 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
381 reqbufs.memory = V4L2_MEMORY_MMAP;
382 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
383
384 output_buffer_map_.clear();
385 free_output_buffers_.clear();
386 }
387
388 void V4L2JpegDecodeAccelerator::DevicePollTask() {
389 DCHECK(device_poll_task_runner_->BelongsToCurrentThread());
390
391 bool event_pending;
392 if (!device_->Poll(true, &event_pending)) {
393 PostNotifyError(-1, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
394 return;
395 }
396
397 // All processing should happen on ServiceDeviceTask(), since we shouldn't
398 // touch decoder state from this thread.
399 decoder_task_runner_->PostTask(
400 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::ServiceDeviceTask,
401 base::Unretained(this)));
402 }
403
404 void V4L2JpegDecodeAccelerator::ServiceDeviceTask() {
405 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
406 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
407 // so either:
408 // * device_poll_thread_ is running normally
409 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
410 // in which case we should early-out.
411 if (!device_poll_thread_.IsRunning())
412 return;
413
414 if (InputBufferQueuedCount() > 0 || OutputBufferQueuedCount() > 0)
415 Dequeue();
416 if (!CreateBuffersIfNecessary()) return;
417 if (!recreate_input_buffers_pending_ && !recreate_output_buffers_pending_)
wuchengli 2015/06/25 08:29:53 Add comments to explain why we should not enqueue
henryhsu 2015/06/26 03:39:32 Done.
418 EnqueueInput();
419 EnqueueOutput();
420
421 if (!device_->ClearDevicePollInterrupt()) return;
422
423 if (InputBufferQueuedCount() > 0 || OutputBufferQueuedCount() > 0) {
424 device_poll_task_runner_->PostTask(
425 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DevicePollTask,
426 base::Unretained(this)));
427 }
428
429 DVLOG(2) << __func__ << ": buffer counts: INPUT["
430 << input_jobs_.size() << "] => DEVICE["
431 << free_input_buffers_.size() << "+"
432 << InputBufferQueuedCount() << "/"
433 << input_buffer_map_.size() << "->"
434 << free_output_buffers_.size() << "+"
435 << OutputBufferQueuedCount() << "/"
436 << output_buffer_map_.size() << "]";
437 }
438
439 void V4L2JpegDecodeAccelerator::EnqueueInput() {
440 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
441 while (!input_jobs_.empty() && !free_input_buffers_.empty()) {
442 if (!EnqueueInputRecord())
443 return;
444 }
445 }
446
447 void V4L2JpegDecodeAccelerator::EnqueueOutput() {
448 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
449 while (OutputBufferQueuedCount() < InputBufferQueuedCount() &&
wuchengli 2015/06/25 08:29:53 I'm not sure this is always correct. For example,
henryhsu 2015/06/26 03:39:32 Hmm...seems like we can always enqueue output buff
wuchengli 2015/06/26 23:42:48 No. This means you'll queue more output buffers th
henryhsu 2015/06/29 03:35:02 You right, there is a bug in CreateBuffersIfNecess
450 !free_output_buffers_.empty()) {
451 if (!EnqueueOutputRecord())
452 return;
453 }
454 }
455
456 void V4L2JpegDecodeAccelerator::Dequeue() {
457 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
458
459 // Dequeue completed input (VIDEO_OUTPUT) buffers,
460 // and recycle to the free list.
461 struct v4l2_buffer dqbuf;
462 while (InputBufferQueuedCount() > 0) {
463 DCHECK(input_streamon_);
464 memset(&dqbuf, 0, sizeof(dqbuf));
465 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
466 dqbuf.memory = V4L2_MEMORY_MMAP;
467 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
468 if (errno == EAGAIN) {
469 // EAGAIN if we're just out of buffers to dequeue.
470 break;
471 }
472 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
473 PostNotifyError(dqbuf.index,
474 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
475 return;
476 }
477 BufferRecord& input_record = input_buffer_map_[dqbuf.index];
478 DCHECK(input_record.at_device);
479 input_record.at_device = false;
480 free_input_buffers_.push_back(dqbuf.index);
481 }
482
483 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
484 // Return the finished buffer to the client via the job ready callback.
485 while (OutputBufferQueuedCount() > 0) {
486 DCHECK(output_streamon_);
487 memset(&dqbuf, 0, sizeof(dqbuf));
488 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
489 dqbuf.memory = V4L2_MEMORY_MMAP;
490 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
491 if (errno == EAGAIN) {
492 // EAGAIN if we're just out of buffers to dequeue.
493 break;
494 }
495 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
496 PostNotifyError(dqbuf.index,
497 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
498 return;
499 }
500 BufferRecord& output_record = output_buffer_map_[dqbuf.index];
501 DCHECK(output_record.at_device);
502 output_record.at_device = false;
503 free_output_buffers_.push_back(dqbuf.index);
504
505 // Jobs are always processed in FIFO order.
506 DCHECK(!running_jobs_.empty());
507 linked_ptr<JobRecord> job_record = running_jobs_.front();
508 running_jobs_.pop();
509
510 memcpy(job_record->out_frame->data(media::VideoFrame::kYPlane),
511 output_record.address, output_record.length);
512
513 DVLOG(3) << "Decoding finished, returning frame, id="
514 << job_record->bitstream_buffer.id();
515
516 client_->VideoFrameReady(job_record->bitstream_buffer.id());
517 }
518 }
519
520 bool V4L2JpegDecodeAccelerator::EnqueueInputRecord() {
521 DCHECK(!input_jobs_.empty());
522 DCHECK(!free_input_buffers_.empty());
523
524 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
525 linked_ptr<JobRecord> job_record = input_jobs_.front();
526 input_jobs_.pop();
527 const int index = free_input_buffers_.back();
528 BufferRecord& input_record = input_buffer_map_[index];
529 DCHECK(!input_record.at_device);
530
531 struct v4l2_buffer qbuf;
532 memset(&qbuf, 0, sizeof(qbuf));
533 memcpy(input_record.address, job_record->shm->memory(),
534 job_record->bitstream_buffer.size());
535 qbuf.index = index;
536 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
537 qbuf.memory = V4L2_MEMORY_MMAP;
538 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
539 input_record.at_device = true;
540 running_jobs_.push(job_record);
541 free_input_buffers_.pop_back();
542
543 DVLOG(3) << __func__ << ": enqueued frame id="
544 << job_record->bitstream_buffer.id() << " to device.";
545 return true;
546 }
547
548 bool V4L2JpegDecodeAccelerator::EnqueueOutputRecord() {
549 DCHECK(!free_output_buffers_.empty());
550
551 // Enqueue an output (VIDEO_CAPTURE) buffer.
552 const int index = free_output_buffers_.back();
553 BufferRecord& output_record = output_buffer_map_[index];
554 DCHECK(!output_record.at_device);
555 struct v4l2_buffer qbuf;
556 memset(&qbuf, 0, sizeof(qbuf));
557 qbuf.index = index;
558 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
559 qbuf.memory = V4L2_MEMORY_MMAP;
560 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
561 output_record.at_device = true;
562 free_output_buffers_.pop_back();
563 return true;
564 }
565
566 void V4L2JpegDecodeAccelerator::StartDevicePoll() {
567 DVLOG(3) << __func__ << ": starting device poll";
568 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
569 DCHECK(!device_poll_thread_.IsRunning());
570
571 if (!device_poll_thread_.Start()) {
572 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
573 PostNotifyError(-1, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
574 return;
575 }
576 device_poll_task_runner_ = device_poll_thread_.task_runner();
577
578 if (!input_streamon_) {
579 // Start VIDIOC_STREAMON if we haven't yet.
580 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
581 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
582 input_streamon_ = true;
583 }
584 if (!output_streamon_) {
585 // Start VIDIOC_STREAMON if we haven't yet.
586 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
587 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
588 output_streamon_ = true;
589 }
590 }
591
592 bool V4L2JpegDecodeAccelerator::StopDevicePoll() {
593 DVLOG(3) << __func__ << ": stopping device poll";
594 if (decoder_thread_.IsRunning())
595 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
596
597 // Signal the DevicePollTask() to stop, and stop the device poll thread.
598 if (!device_->SetDevicePollInterrupt()) {
599 LOG(ERROR) << "StopDevicePoll(): SetDevicePollInterrupt failed.";
600 PostNotifyError(-1, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
601 return false;
602 }
603
604 device_poll_thread_.Stop();
605
606 // Clear the interrupt now, to be sure.
607 if (!device_->ClearDevicePollInterrupt())
608 return false;
609
610 if (input_streamon_) {
611 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
612 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
613 input_streamon_ = false;
614 }
615
616 if (output_streamon_) {
617 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
618 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
619 output_streamon_ = false;
620 }
621
622 free_input_buffers_.clear();
623 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
624 BufferRecord& input_record = input_buffer_map_[i];
625 input_record.at_device = false;
626 free_input_buffers_.push_back(i);
627 }
628
629 free_output_buffers_.clear();
630 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
631 BufferRecord& output_record = output_buffer_map_[i];
632 output_record.at_device = false;
633 free_output_buffers_.push_back(i);
634 }
635
636 return true;
637 }
638
639 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698