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

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

Powered by Google App Engine
This is Rietveld 408576698