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

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

Powered by Google App Engine
This is Rietveld 408576698