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

Side by Side Diff: content/common/gpu/media/v4l2_jpeg_decode_accelerator.cc

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

Powered by Google App Engine
This is Rietveld 408576698