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

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

Powered by Google App Engine
This is Rietveld 408576698