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

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: Add unittest and address review comments 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 <fcntl.h>
6 #include <linux/videodev2.h>
7 #include <poll.h>
8 #include <sys/eventfd.h>
9 #include <sys/ioctl.h>
10 #include <sys/mman.h>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h"
17 #include "media/base/bind_to_current_loop.h"
18 #include "media/base/video_frame.h"
19
20 #undef DVLOG
21 #define DVLOG VLOG
kcwu 2015/06/08 10:04:29 don't forget to remove this
henryhsu 2015/06/09 10:20:05 Done.
22
23 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \
24 do { \
25 if (device_->Ioctl(type, arg) != 0) { \
26 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
27 return value; \
28 } \
29 } while (0)
30
31 #define IOCTL_OR_ERROR_RETURN(type, arg) \
32 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0))
33
34 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
35 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false)
36
37 #define IOCTL_OR_LOG_ERROR(type, arg) \
38 do { \
39 if (device_->Ioctl(type, arg) != 0) \
40 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
41 } while (0)
42
43 #define TIME_ENTER() \
44 {}
45
46 #define TIME_LEAVE() \
47 {}
48
49
50 namespace content {
51
52 V4L2JpegDecodeAccelerator::InputRecord::InputRecord() : at_device(false) {
53 }
54
55 V4L2JpegDecodeAccelerator::InputRecord::~InputRecord() {
56 }
57
58 V4L2JpegDecodeAccelerator::OutputRecord::OutputRecord()
59 : address(nullptr), length(0), at_device(false) {
60 }
61
62 V4L2JpegDecodeAccelerator::OutputRecord::~OutputRecord() {
63 }
64
65 V4L2JpegDecodeAccelerator::JobRecord::JobRecord(
66 media::BitstreamBuffer bitstream_buffer,
67 scoped_refptr<media::VideoFrame> video_frame)
68 : bitstream_buffer(bitstream_buffer), frame(video_frame) {
69 }
70
71 V4L2JpegDecodeAccelerator::JobRecord::~JobRecord() {
72 }
73
74 V4L2JpegDecodeAccelerator::V4L2JpegDecodeAccelerator(
75 const scoped_refptr<V4L2Device>& device,
76 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
77 : reset_buffer_flag_(false),
78 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
79 io_task_runner_(io_task_runner),
80 device_(device),
81 decoder_thread_("V4L2JpegDecodeThread"),
82 device_poll_thread_("V4L2JpegDecodeDevicePollThread"),
83 input_streamon_(false),
84 input_buffer_queued_count_(0),
85 output_streamon_(false),
86 output_buffer_queued_count_(0),
87 device_weak_factory_(this) {
88 device_weak_ = device_weak_factory_.GetWeakPtr();
89 }
90
91 V4L2JpegDecodeAccelerator::~V4L2JpegDecodeAccelerator() {
92 DCHECK(child_task_runner_->BelongsToCurrentThread());
93
94 // If the device thread is running, destroy using posted task.
95 if (decoder_thread_.IsRunning()) {
96 decoder_task_runner_->PostTask(
97 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DestroyTask,
98 base::Unretained(this)));
99 // Wait for tasks to finish/early-exit.
100 decoder_thread_.Stop();
101 }
102 DCHECK(!decoder_thread_.IsRunning());
103 DCHECK(!device_poll_thread_.IsRunning());
104 DCHECK(!device_weak_factory_.HasWeakPtrs());
105 }
106
107 void V4L2JpegDecodeAccelerator::DestroyTask() {
108 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
109
110 device_weak_factory_.InvalidateWeakPtrs();
111 // Stop streaming and the device_poll_thread_.
112 StopDevicePoll(false);
113
114 DestroyInputBuffers();
115 DestroyOutputBuffers();
116 }
117
118 void V4L2JpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id,
119 Error error) {
120 DCHECK(child_task_runner_->BelongsToCurrentThread());
121 LOG(ERROR) << "Notifying of error " << error << " for buffer id "
122 << bitstream_buffer_id;
123 DCHECK(client_);
124 client_->NotifyError(bitstream_buffer_id, error);
125 }
126
127 void V4L2JpegDecodeAccelerator::NotifyErrorFromDecoderThread(
128 int32_t bitstream_buffer_id,
129 Error error) {
130 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
131 child_task_runner_->PostTask(
132 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::NotifyError,
133 device_weak_, bitstream_buffer_id, error));
134 }
135
136 bool V4L2JpegDecodeAccelerator::Initialize(Client* client) {
137 TIME_ENTER();
138 DCHECK(child_task_runner_->BelongsToCurrentThread());
139
140 client_ = client;
141
142 // Capabilities check.
143 struct v4l2_capability caps;
144 const __u32 kCapsRequired = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
145 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
146 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
147 LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP"
148 ", caps check failed: 0x" << std::hex << caps.capabilities;
149 return false;
150 }
151
152 if (!decoder_thread_.Start()) {
153 LOG(ERROR) << "Initialize(): encoder thread failed to start";
154 return false;
155 }
156 decoder_task_runner_ = decoder_thread_.task_runner();
157
158 decoder_task_runner_->PostTask(
159 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::StartDevicePoll,
160 base::Unretained(this)));
161
162 DVLOG(1) << "V4L2JpegDecodeAccelerator initialized.";
163
164 TIME_LEAVE();
165 return true;
166 }
167
168 void V4L2JpegDecodeAccelerator::Decode(
169 const media::BitstreamBuffer& bitstream_buffer,
170 const scoped_refptr<media::VideoFrame>& video_frame) {
171 TIME_ENTER();
172 DVLOG(1) << "Decode(): input_id=" << bitstream_buffer.id()
173 << ", size=" << bitstream_buffer.size();
174 DCHECK(io_task_runner_->BelongsToCurrentThread());
175 DCHECK_EQ(video_frame->format(), media::VideoFrame::I420);
176
177 scoped_ptr<JobRecord> job_record(
178 new JobRecord(bitstream_buffer, video_frame));
179
180 decoder_task_runner_->PostTask(
181 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DecodeTask,
182 base::Unretained(this), base::Passed(&job_record)));
183 TIME_LEAVE();
184 }
185
186 void V4L2JpegDecodeAccelerator::DecodeTask(scoped_ptr<JobRecord> job_record) {
187 TIME_ENTER();
188 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
189 input_queue_.push(make_linked_ptr(job_record.release()));
190 if (!CheckBufferAttributes())
191 return;
192 if (!reset_buffer_flag_)
193 Enqueue();
194 TIME_LEAVE();
195 }
196
197 bool V4L2JpegDecodeAccelerator::CheckBufferAttributes() {
198 TIME_ENTER();
199 DVLOG(3) << __func__;
200 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
201 DCHECK(!input_queue_.empty());
202 linked_ptr<JobRecord> job_record = input_queue_.front();
203 uint32_t reset_input_buffer = 0, reset_output_buffer = 0;
204
205 // Check input buffer size is enough
206 if (input_buffer_map_.empty() ||
207 job_record->bitstream_buffer.size() > input_buffer_map_.front().length) {
208 reset_input_buffer = kResetInputBuffer;
209 }
210
211 // Check image resolution and format are the same as previous.
212 if (job_record->frame->format() != output_format_ ||
213 job_record->frame->coded_size() != image_coded_size_) {
214 size_t frame_size = media::VideoFrame::AllocationSize(
215 job_record->frame->format(), job_record->frame->coded_size());
216 if (output_buffer_map_.empty() ||
217 frame_size > output_buffer_map_.front().length) {
218 reset_output_buffer = kResetOutputBuffer;
219 }
220 }
221
222 if (reset_input_buffer || reset_output_buffer) {
223 if (input_streamon_ || output_streamon_) {
224 reset_buffer_flag_ = reset_input_buffer | reset_output_buffer;
225 ResetBuffers();
226 } else {
227 image_coded_size_ = job_record->frame->coded_size();
228 output_format_ = job_record->frame->format();
229 if (!CreateInputBuffers() || !CreateOutputBuffers()) {
230 LOG(ERROR) << "Create Input/Output buffer failed.";
231 return false;
232 }
233 }
234 }
235 TIME_LEAVE();
236 return true;
237 }
238
239 void V4L2JpegDecodeAccelerator::ResetBuffers() {
240 DVLOG(3) << __func__;
241 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
242 if (input_buffer_queued_count_ || output_buffer_queued_count_)
243 return;
244
245 if (!StopDevicePoll(true)) {
246 LOG(ERROR) << "Stop device poll thread failed when renew buffers.";
247 }
248
249 DCHECK(!input_queue_.empty());
250 linked_ptr<JobRecord> job_record = input_queue_.front();
251
252 if (reset_buffer_flag_ & kResetInputBuffer) {
253 DestroyInputBuffers();
254 CreateInputBuffers();
255 }
256
257 if (reset_buffer_flag_ & kResetOutputBuffer) {
258 DestroyOutputBuffers();
259
260 image_coded_size_ = job_record->frame->coded_size();
261 output_format_ = job_record->frame->format();
262 CreateOutputBuffers();
263 }
264
265 reset_buffer_flag_ = 0;
266 Enqueue();
267 StartDevicePoll();
268 }
269
270 bool V4L2JpegDecodeAccelerator::CreateInputBuffers() {
271 TIME_ENTER();
272 DVLOG(3) << __func__;
273 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
274 DCHECK(!input_streamon_);
275
276 DCHECK(!input_queue_.empty());
277 linked_ptr<JobRecord> job_record = input_queue_.front();
278 size_t reserve_size = job_record->bitstream_buffer.size() * 2;
279
280 struct v4l2_format format;
281 memset(&format, 0, sizeof(format));
282 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
283 format.fmt.pix.width = job_record->frame->coded_size().width();
284 format.fmt.pix.height = job_record->frame->coded_size().height();
285 format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
286 format.fmt.pix.sizeimage = reserve_size;
287 format.fmt.pix.field = V4L2_FIELD_ANY;
288 format.fmt.pix.bytesperline = 0;
289 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
290
291 struct v4l2_requestbuffers reqbufs;
292 memset(&reqbufs, 0, sizeof(reqbufs));
293 reqbufs.count = kInputBufferCount;
294 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
295 reqbufs.memory = V4L2_MEMORY_MMAP;
296 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
297
298 DCHECK(input_buffer_map_.empty());
299 input_buffer_map_.resize(reqbufs.count);
300
301 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
302 free_input_buffers_.push_back(i);
303
304 struct v4l2_buffer buffer;
305 memset(&buffer, 0, sizeof(buffer));
306 buffer.index = i;
307 buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
308 buffer.memory = V4L2_MEMORY_MMAP;
309 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
310 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
311 MAP_SHARED, buffer.m.offset);
312 if (address == MAP_FAILED) {
313 PLOG(ERROR) << "CreateInputBuffers(): mmap() failed";
314 return false;
315 }
316 input_buffer_map_[i].address = address;
317 input_buffer_map_[i].length = buffer.length;
318 }
319
320 TIME_LEAVE();
321 return true;
322 }
323
324 bool V4L2JpegDecodeAccelerator::CreateOutputBuffers() {
325 TIME_ENTER();
326 DVLOG(3) << __func__;
327 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
328 DCHECK(!output_streamon_);
329
330 DCHECK(!input_queue_.empty());
331 linked_ptr<JobRecord> job_record = input_queue_.front();
332
333 size_t frame_size = media::VideoFrame::AllocationSize(
334 output_format_, job_record->frame->coded_size());
335 uint32_t output_format_fourcc_ = V4L2_PIX_FMT_YUV420;
336 struct v4l2_format format;
337 memset(&format, 0, sizeof(format));
338 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
339 format.fmt.pix.width = job_record->frame->coded_size().width();
340 format.fmt.pix.height = job_record->frame->coded_size().height();
341 format.fmt.pix.sizeimage = frame_size;
342 format.fmt.pix.pixelformat = output_format_fourcc_;
343 format.fmt.pix.field = V4L2_FIELD_ANY;
344 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
345
346 struct v4l2_requestbuffers reqbufs;
347 memset(&reqbufs, 0, sizeof(reqbufs));
348 reqbufs.count = kOutputBufferCount;
349 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
350 reqbufs.memory = V4L2_MEMORY_MMAP;
351 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
352
353 DCHECK(output_buffer_map_.empty());
354 output_buffer_map_.resize(reqbufs.count);
355
356 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
357 free_output_buffers_.push_back(i);
358
359 struct v4l2_buffer buffer;
360 memset(&buffer, 0, sizeof(buffer));
361 buffer.index = i;
362 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
363 buffer.memory = V4L2_MEMORY_MMAP;
364 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
365 void* address = device_->Mmap(NULL, buffer.length, PROT_READ | PROT_WRITE,
366 MAP_SHARED, buffer.m.offset);
367 if (address == MAP_FAILED) {
368 PLOG(ERROR) << "CreateOutputBuffers(): mmap() failed";
369 return false;
370 }
371 output_buffer_map_[i].address = address;
372 output_buffer_map_[i].length = buffer.length;
373 }
374
375 TIME_LEAVE();
376 return true;
377 }
378
379 void V4L2JpegDecodeAccelerator::DestroyInputBuffers() {
380 TIME_ENTER();
381 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
382 DCHECK(!input_streamon_);
383
384 for (size_t buf = 0; buf < input_buffer_map_.size(); ++buf) {
385 InputRecord& input_record = input_buffer_map_[buf];
386 device_->Munmap(input_record.address, input_record.length);
387 }
388
389 struct v4l2_requestbuffers reqbufs;
390 memset(&reqbufs, 0, sizeof(reqbufs));
391 reqbufs.count = 0;
392 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
393 reqbufs.memory = V4L2_MEMORY_MMAP;
394 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
395
396 input_buffer_map_.clear();
397 free_input_buffers_.clear();
398 TIME_LEAVE();
399 }
400
401 void V4L2JpegDecodeAccelerator::DestroyOutputBuffers() {
402 TIME_ENTER();
403 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
404 DCHECK(!output_streamon_);
405
406 for (size_t buf = 0; buf < output_buffer_map_.size(); ++buf) {
407 OutputRecord& output_record = output_buffer_map_[buf];
408 device_->Munmap(output_record.address, output_record.length);
409 }
410
411 struct v4l2_requestbuffers reqbufs;
412 memset(&reqbufs, 0, sizeof(reqbufs));
413 reqbufs.count = 0;
414 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
415 reqbufs.memory = V4L2_MEMORY_MMAP;
416 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
417
418 output_buffer_map_.clear();
419 free_output_buffers_.clear();
420 TIME_LEAVE();
421 }
422
423 void V4L2JpegDecodeAccelerator::DevicePollTask(bool poll_device) {
kcwu 2015/06/08 10:04:29 poll_device unused
henryhsu 2015/06/09 10:20:05 Done.
424 DCHECK(device_poll_task_runner_->BelongsToCurrentThread());
425
426 bool event_pending;
427 TIME_ENTER();
428 if (!device_->Poll(true, &event_pending)) {
429 NotifyError(-1, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
430 return;
431 }
432 TIME_LEAVE();
433
434 // All processing should happen on ServiceDeviceTask(), since we shouldn't
435 // touch encoder state from this thread.
436 decoder_task_runner_->PostTask(
437 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::ServiceDeviceTask,
438 base::Unretained(this)));
439 }
440
441 void V4L2JpegDecodeAccelerator::ServiceDeviceTask() {
442 TIME_ENTER();
443 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
444 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
445 // so either:
446 // * device_poll_thread_ is running normally
447 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
448 // in which case we should early-out.
449 if (!device_poll_thread_.IsRunning())
450 return;
451
452 Dequeue();
453 Enqueue();
454
455 if (!device_->ClearDevicePollInterrupt()) {
456 return;
457 }
458
459 bool poll_device =
kcwu 2015/06/08 10:04:29 actually unused
henryhsu 2015/06/09 10:20:05 Done.
460 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0);
461
462 device_poll_task_runner_->PostTask(
463 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DevicePollTask,
464 base::Unretained(this), poll_device));
465
466 DVLOG(2) << __func__ << ": buffer counts: INPUT["
467 << input_queue_.size() << "] => DEVICE["
468 << free_input_buffers_.size() << "+"
469 << input_buffer_queued_count_ << "/"
470 << input_buffer_map_.size() << "->"
471 << free_output_buffers_.size() << "+"
472 << output_buffer_queued_count_ << "/"
473 << output_buffer_map_.size() << "] => CLIENT["
474 << output_buffer_map_.size() - output_buffer_queued_count_ -
475 free_output_buffers_.size() << "]";
476 TIME_LEAVE();
477 }
478
479 void V4L2JpegDecodeAccelerator::Enqueue() {
480 TIME_ENTER();
481 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
482
483 const int old_inputs_queued = input_buffer_queued_count_;
484 while (!input_queue_.empty() && !free_input_buffers_.empty()) {
485 if (!EnqueueInputRecord())
486 return;
487 }
488 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
489 // Start VIDIOC_STREAMON if we haven't yet.
490 if (!input_streamon_) {
491 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
492 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
493 input_streamon_ = true;
494 }
495 }
496
497 const int old_outputs_queued = output_buffer_queued_count_;
498 while (output_buffer_queued_count_ < input_buffer_queued_count_ &&
499 !free_output_buffers_.empty()) {
500 if (!EnqueueOutputRecord())
501 return;
502 }
503 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) {
504 // Start VIDIOC_STREAMON if we haven't yet.
505 if (!output_streamon_) {
506 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
507 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
508 output_streamon_ = true;
509 }
510 }
511 TIME_LEAVE();
512 }
513
514 void V4L2JpegDecodeAccelerator::Dequeue() {
515 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
516
517 // Dequeue completed input (VIDEO_OUTPUT) buffers,
518 // and recycle to the free list.
519 struct v4l2_buffer dqbuf;
520 while (input_buffer_queued_count_ > 0) {
521 DCHECK(input_streamon_);
522 memset(&dqbuf, 0, sizeof(dqbuf));
523 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
524 dqbuf.memory = V4L2_MEMORY_MMAP;
525 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
526 if (errno == EAGAIN) {
527 // EAGAIN if we're just out of buffers to dequeue.
528 break;
529 }
530 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
531 NotifyError(dqbuf.index, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
532 return;
533 }
534 InputRecord& input_record = input_buffer_map_[dqbuf.index];
535 DCHECK(input_record.at_device);
536 input_record.at_device = false;
537 free_input_buffers_.push_back(dqbuf.index);
538 input_buffer_queued_count_--;
539 }
540
541 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
542 // Return the finished buffer to the client via the job ready callback.
543 while (output_buffer_queued_count_ > 0) {
544 DCHECK(output_streamon_);
545 memset(&dqbuf, 0, sizeof(dqbuf));
546 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
547 dqbuf.memory = V4L2_MEMORY_MMAP;
548 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
549 if (errno == EAGAIN) {
550 // EAGAIN if we're just out of buffers to dequeue.
551 break;
552 }
553 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
554 NotifyError(dqbuf.index, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
555 return;
556 }
557 OutputRecord& output_record = output_buffer_map_[dqbuf.index];
558 DCHECK(output_record.at_device);
559 output_record.at_device = false;
560 free_output_buffers_.push_back(dqbuf.index);
561 output_buffer_queued_count_--;
562
563 // Jobs are always processed in FIFO order.
564 DCHECK(!running_jobs_.empty());
565 linked_ptr<JobRecord> job_record = running_jobs_.front();
566 running_jobs_.pop();
567
568 TIME_ENTER();
569 memcpy(job_record->frame->data(media::VideoFrame::kYPlane),
570 output_record.address, output_record.length);
571
572 DVLOG(3) << "Processing finished, returning frame, ts="
573 << job_record->frame->timestamp().InMilliseconds();
574
575 TIME_LEAVE();
576 DCHECK(client_);
577 client_->VideoFrameReady(job_record->bitstream_buffer.id());
578 if (reset_buffer_flag_)
579 ResetBuffers();
580 }
581 }
582
583 bool V4L2JpegDecodeAccelerator::EnqueueInputRecord() {
584 TIME_ENTER();
585 DCHECK(!input_queue_.empty());
586 DCHECK(!free_input_buffers_.empty());
587
588 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
589 linked_ptr<JobRecord> job_record = input_queue_.front();
590 input_queue_.pop();
591 const int index = free_input_buffers_.back();
592 InputRecord& input_record = input_buffer_map_[index];
593 DCHECK(!input_record.at_device);
594
595 scoped_ptr<base::SharedMemory> shm(
596 new base::SharedMemory(job_record->bitstream_buffer.handle(), true));
597 if (!shm->Map(job_record->bitstream_buffer.size())) {
598 LOG(ERROR) << "Decode(): could not map bitstream_buffer";
599 NotifyError(job_record->bitstream_buffer.id(), UNREADABLE_INPUT);
600 return false;
601 }
602 struct v4l2_buffer qbuf;
603 memset(&qbuf, 0, sizeof(qbuf));
604 memcpy(input_record.address, shm->memory(),
605 job_record->bitstream_buffer.size());
606 qbuf.index = index;
607 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
608 qbuf.memory = V4L2_MEMORY_MMAP;
609 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
610 input_record.at_device = true;
611 running_jobs_.push(job_record);
612 free_input_buffers_.pop_back();
613 input_buffer_queued_count_++;
614
615 DVLOG(3) << __func__ << ": enqueued frame ts="
616 << job_record->frame->timestamp().InMilliseconds() << " to device.";
617
618 TIME_LEAVE();
619 return true;
620 }
621
622 bool V4L2JpegDecodeAccelerator::EnqueueOutputRecord() {
623 TIME_ENTER();
624 DCHECK(!free_output_buffers_.empty());
625
626 // Enqueue an output (VIDEO_CAPTURE) buffer.
627 const int index = free_output_buffers_.back();
628 OutputRecord& output_record = output_buffer_map_[index];
629 DCHECK(!output_record.at_device);
630 struct v4l2_buffer qbuf;
631 memset(&qbuf, 0, sizeof(qbuf));
632 qbuf.index = index;
633 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
634 qbuf.memory = V4L2_MEMORY_MMAP;
635 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
636 output_record.at_device = true;
637 free_output_buffers_.pop_back();
638 output_buffer_queued_count_++;
639 TIME_LEAVE();
640 return true;
641 }
642
643 void V4L2JpegDecodeAccelerator::StartDevicePoll() {
644 TIME_ENTER();
645 DVLOG(3) << __func__ << ": starting device poll";
646 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
647 DCHECK(!device_poll_thread_.IsRunning());
648
649 // Start up the device poll thread and schedule its first DevicePollTask().
650 if (!device_poll_thread_.Start()) {
651 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
652 NotifyError(-1, media::JpegDecodeAccelerator::PLATFORM_FAILURE);
653 return;
654 }
655 device_poll_task_runner_ = device_poll_thread_.task_runner();
656
657 // Enqueue a poll task with no devices to poll on - will wait only for the
kcwu 2015/06/08 10:04:29 revise the comment
henryhsu 2015/06/09 10:20:05 Done.
658 // poll interrupt
659 device_poll_task_runner_->PostTask(
660 FROM_HERE, base::Bind(&V4L2JpegDecodeAccelerator::DevicePollTask,
661 base::Unretained(this), false));
662
663 TIME_LEAVE();
664 }
665
666 bool V4L2JpegDecodeAccelerator::StopDevicePoll(bool keep_input_queue) {
667 TIME_ENTER();
668 DVLOG(3) << __func__ << ": stopping device poll";
669 if (decoder_thread_.IsRunning())
670 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
671
672 // Signal the DevicePollTask() to stop, and stop the device poll thread.
673 if (!device_->SetDevicePollInterrupt())
674 return false;
675
676 device_poll_thread_.Stop();
677
678 // Clear the interrupt now, to be sure.
679 if (!device_->ClearDevicePollInterrupt())
680 return false;
681
682 if (input_streamon_) {
683 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
684 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
685 }
686 input_streamon_ = false;
687
688 if (output_streamon_) {
689 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
690 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
691 }
692 output_streamon_ = false;
693
694 // Reset all our accounting info.
695 if (!keep_input_queue) {
696 while (!input_queue_.empty())
697 input_queue_.pop();
698 }
699
700 while (!running_jobs_.empty())
701 running_jobs_.pop();
702
703 free_input_buffers_.clear();
704 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
705 InputRecord& input_record = input_buffer_map_[i];
706 input_record.at_device = false;
707 free_input_buffers_.push_back(i);
708 }
709 input_buffer_queued_count_ = 0;
710
711 free_output_buffers_.clear();
712 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
713 OutputRecord& output_record = output_buffer_map_[i];
714 output_record.at_device = false;
715 free_output_buffers_.push_back(i);
716 }
717 output_buffer_queued_count_ = 0;
718
719 TIME_LEAVE();
720 return true;
721 }
722
723 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698