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

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

Issue 1882373004: Migrate content/common/gpu/media code to media/gpu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Squash and rebase Created 4 years, 7 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 2014 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 <errno.h>
6 #include <fcntl.h>
7 #include <linux/videodev2.h>
8 #include <poll.h>
9 #include <string.h>
10 #include <sys/eventfd.h>
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13
14 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/callback.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "content/common/gpu/media/v4l2_image_processor.h"
20
21 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_str) \
22 do { \
23 if (device_->Ioctl(type, arg) != 0) { \
24 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_str; \
25 return value; \
26 } \
27 } while (0)
28
29 #define IOCTL_OR_ERROR_RETURN(type, arg) \
30 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type)
31
32 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
33 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type)
34
35 #define IOCTL_OR_LOG_ERROR(type, arg) \
36 do { \
37 if (device_->Ioctl(type, arg) != 0) \
38 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
39 } while (0)
40
41 namespace content {
42
43 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) {
44 }
45
46 V4L2ImageProcessor::InputRecord::~InputRecord() {
47 }
48
49 V4L2ImageProcessor::OutputRecord::OutputRecord() : at_device(false) {}
50
51 V4L2ImageProcessor::OutputRecord::~OutputRecord() {
52 }
53
54 V4L2ImageProcessor::JobRecord::JobRecord() : output_buffer_index(-1) {}
55
56 V4L2ImageProcessor::JobRecord::~JobRecord() {
57 }
58
59 V4L2ImageProcessor::V4L2ImageProcessor(const scoped_refptr<V4L2Device>& device)
60 : input_format_(media::PIXEL_FORMAT_UNKNOWN),
61 output_format_(media::PIXEL_FORMAT_UNKNOWN),
62 input_memory_type_(V4L2_MEMORY_USERPTR),
63 input_format_fourcc_(0),
64 output_format_fourcc_(0),
65 input_planes_count_(0),
66 output_planes_count_(0),
67 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
68 device_(device),
69 device_thread_("V4L2ImageProcessorThread"),
70 device_poll_thread_("V4L2ImageProcessorDevicePollThread"),
71 input_streamon_(false),
72 input_buffer_queued_count_(0),
73 output_streamon_(false),
74 output_buffer_queued_count_(0),
75 num_buffers_(0),
76 weak_this_factory_(this) {
77 weak_this_ = weak_this_factory_.GetWeakPtr();
78 }
79
80 V4L2ImageProcessor::~V4L2ImageProcessor() {
81 DCHECK(child_task_runner_->BelongsToCurrentThread());
82 DCHECK(!device_thread_.IsRunning());
83 DCHECK(!device_poll_thread_.IsRunning());
84
85 DestroyInputBuffers();
86 DestroyOutputBuffers();
87 }
88
89 void V4L2ImageProcessor::NotifyError() {
90 LOG(ERROR) << __func__;
91 DCHECK(!child_task_runner_->BelongsToCurrentThread());
92 child_task_runner_->PostTask(
93 FROM_HERE, base::Bind(&V4L2ImageProcessor::NotifyErrorOnChildThread,
94 weak_this_, error_cb_));
95 }
96
97 void V4L2ImageProcessor::NotifyErrorOnChildThread(
98 const base::Closure& error_cb) {
99 DCHECK(child_task_runner_->BelongsToCurrentThread());
100 error_cb_.Run();
101 }
102
103 bool V4L2ImageProcessor::Initialize(media::VideoPixelFormat input_format,
104 media::VideoPixelFormat output_format,
105 v4l2_memory input_memory_type,
106 gfx::Size input_visible_size,
107 gfx::Size input_allocated_size,
108 gfx::Size output_visible_size,
109 gfx::Size output_allocated_size,
110 int num_buffers,
111 const base::Closure& error_cb) {
112 DCHECK(!error_cb.is_null());
113 DCHECK_GT(num_buffers, 0);
114 DCHECK(input_memory_type == V4L2_MEMORY_USERPTR ||
115 input_memory_type == V4L2_MEMORY_DMABUF);
116 error_cb_ = error_cb;
117
118 input_format_ = input_format;
119 output_format_ = output_format;
120 input_format_fourcc_ = V4L2Device::VideoPixelFormatToV4L2PixFmt(input_format);
121 output_format_fourcc_ =
122 V4L2Device::VideoPixelFormatToV4L2PixFmt(output_format);
123 num_buffers_ = num_buffers;
124
125 if (!input_format_fourcc_ || !output_format_fourcc_) {
126 LOG(ERROR) << "Unrecognized format(s)";
127 return false;
128 }
129
130 input_memory_type_ = input_memory_type;
131 input_visible_size_ = input_visible_size;
132 input_allocated_size_ = input_allocated_size;
133 output_visible_size_ = output_visible_size;
134 output_allocated_size_ = output_allocated_size;
135
136 // Capabilities check.
137 struct v4l2_capability caps;
138 memset(&caps, 0, sizeof(caps));
139 const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
140 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
141 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
142 LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
143 "caps check failed: 0x" << std::hex << caps.capabilities;
144 return false;
145 }
146
147 if (!CreateInputBuffers() || !CreateOutputBuffers())
148 return false;
149
150 if (!device_thread_.Start()) {
151 LOG(ERROR) << "Initialize(): encoder thread failed to start";
152 return false;
153 }
154
155 // StartDevicePoll will NotifyError on failure, so IgnoreResult is fine here.
156 device_thread_.message_loop()->PostTask(
157 FROM_HERE,
158 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll),
159 base::Unretained(this)));
160
161 DVLOG(1) << "V4L2ImageProcessor initialized for "
162 << " input_format:" << media::VideoPixelFormatToString(input_format)
163 << ", output_format:"
164 << media::VideoPixelFormatToString(output_format)
165 << ", input_visible_size: " << input_visible_size.ToString()
166 << ", input_allocated_size: " << input_allocated_size_.ToString()
167 << ", input_planes_count: " << input_planes_count_
168 << ", output_visible_size: " << output_visible_size.ToString()
169 << ", output_allocated_size: " << output_allocated_size_.ToString()
170 << ", output_planes_count: " << output_planes_count_;
171
172 return true;
173 }
174
175 std::vector<base::ScopedFD> V4L2ImageProcessor::GetDmabufsForOutputBuffer(
176 int output_buffer_index) {
177 DCHECK_GE(output_buffer_index, 0);
178 DCHECK_LT(output_buffer_index, num_buffers_);
179 return device_->GetDmabufsForV4L2Buffer(output_buffer_index,
180 output_planes_count_,
181 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
182 }
183
184 std::vector<uint32_t> V4L2ImageProcessor::GetSupportedInputFormats() {
185 std::vector<uint32_t> input_formats;
186 v4l2_fmtdesc fmtdesc;
187 memset(&fmtdesc, 0, sizeof(fmtdesc));
188 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
189 for (; device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) {
190 input_formats.push_back(fmtdesc.pixelformat);
191 }
192 return input_formats;
193 }
194
195 std::vector<uint32_t> V4L2ImageProcessor::GetSupportedOutputFormats() {
196 std::vector<uint32_t> output_formats;
197 v4l2_fmtdesc fmtdesc;
198 memset(&fmtdesc, 0, sizeof(fmtdesc));
199 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
200 for (; device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) {
201 output_formats.push_back(fmtdesc.pixelformat);
202 }
203 return output_formats;
204 }
205
206 bool V4L2ImageProcessor::TryOutputFormat(uint32_t pixelformat,
207 gfx::Size* size,
208 size_t* num_planes) {
209 DVLOG(1) << __func__ << ": size=" << size->ToString();
210 struct v4l2_format format;
211 memset(&format, 0, sizeof(format));
212 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
213 format.fmt.pix_mp.width = size->width();
214 format.fmt.pix_mp.height = size->height();
215 format.fmt.pix_mp.pixelformat = pixelformat;
216 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_TRY_FMT, &format);
217
218 *num_planes = format.fmt.pix_mp.num_planes;
219 *size = V4L2Device::CodedSizeFromV4L2Format(format);
220 DVLOG(1) << __func__ << ": adjusted output coded size=" << size->ToString()
221 << ", num_planes=" << *num_planes;
222 return true;
223 }
224
225 void V4L2ImageProcessor::Process(const scoped_refptr<media::VideoFrame>& frame,
226 int output_buffer_index,
227 const FrameReadyCB& cb) {
228 DVLOG(3) << __func__ << ": ts=" << frame->timestamp().InMilliseconds();
229
230 std::unique_ptr<JobRecord> job_record(new JobRecord());
231 job_record->frame = frame;
232 job_record->output_buffer_index = output_buffer_index;
233 job_record->ready_cb = cb;
234
235 device_thread_.message_loop()->PostTask(
236 FROM_HERE,
237 base::Bind(&V4L2ImageProcessor::ProcessTask,
238 base::Unretained(this),
239 base::Passed(&job_record)));
240 }
241
242 void V4L2ImageProcessor::ProcessTask(std::unique_ptr<JobRecord> job_record) {
243 int index = job_record->output_buffer_index;
244 DVLOG(3) << __func__ << ": Reusing output buffer, index=" << index;
245 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
246
247 EnqueueOutput(index);
248 input_queue_.push(make_linked_ptr(job_record.release()));
249 EnqueueInput();
250 }
251
252 void V4L2ImageProcessor::Destroy() {
253 DVLOG(3) << __func__;
254 DCHECK(child_task_runner_->BelongsToCurrentThread());
255
256 weak_this_factory_.InvalidateWeakPtrs();
257
258 // If the device thread is running, destroy using posted task.
259 if (device_thread_.IsRunning()) {
260 device_thread_.message_loop()->PostTask(
261 FROM_HERE,
262 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this)));
263 // Wait for tasks to finish/early-exit.
264 device_thread_.Stop();
265 } else {
266 // Otherwise DestroyTask() is not needed.
267 DCHECK(!device_poll_thread_.IsRunning());
268 }
269
270 delete this;
271 }
272
273 void V4L2ImageProcessor::DestroyTask() {
274 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
275
276 // Stop streaming and the device_poll_thread_.
277 StopDevicePoll();
278 }
279
280 bool V4L2ImageProcessor::CreateInputBuffers() {
281 DVLOG(3) << __func__;
282 DCHECK(child_task_runner_->BelongsToCurrentThread());
283 DCHECK(!input_streamon_);
284
285 struct v4l2_control control;
286 memset(&control, 0, sizeof(control));
287 control.id = V4L2_CID_ROTATE;
288 control.value = 0;
289 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
290
291 memset(&control, 0, sizeof(control));
292 control.id = V4L2_CID_HFLIP;
293 control.value = 0;
294 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
295
296 memset(&control, 0, sizeof(control));
297 control.id = V4L2_CID_VFLIP;
298 control.value = 0;
299 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
300
301 memset(&control, 0, sizeof(control));
302 control.id = V4L2_CID_ALPHA_COMPONENT;
303 control.value = 255;
304 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control);
305
306 struct v4l2_format format;
307 memset(&format, 0, sizeof(format));
308 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
309 format.fmt.pix_mp.width = input_allocated_size_.width();
310 format.fmt.pix_mp.height = input_allocated_size_.height();
311 format.fmt.pix_mp.pixelformat = input_format_fourcc_;
312 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
313
314 input_planes_count_ = format.fmt.pix_mp.num_planes;
315 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
316 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format);
317 DCHECK(gfx::Rect(input_allocated_size_).Contains(
318 gfx::Rect(input_visible_size_)));
319
320 struct v4l2_crop crop;
321 memset(&crop, 0, sizeof(crop));
322 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
323 crop.c.left = 0;
324 crop.c.top = 0;
325 crop.c.width = base::checked_cast<__u32>(input_visible_size_.width());
326 crop.c.height = base::checked_cast<__u32>(input_visible_size_.height());
327 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
328
329 struct v4l2_requestbuffers reqbufs;
330 memset(&reqbufs, 0, sizeof(reqbufs));
331 reqbufs.count = num_buffers_;
332 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
333 reqbufs.memory = input_memory_type_;
334 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
335 if (static_cast<int>(reqbufs.count) != num_buffers_) {
336 LOG(ERROR) << "Failed to allocate input buffers. reqbufs.count="
337 << reqbufs.count << ", num_buffers=" << num_buffers_;
338 return false;
339 }
340
341 DCHECK(input_buffer_map_.empty());
342 input_buffer_map_.resize(reqbufs.count);
343
344 for (size_t i = 0; i < input_buffer_map_.size(); ++i)
345 free_input_buffers_.push_back(i);
346
347 return true;
348 }
349
350 bool V4L2ImageProcessor::CreateOutputBuffers() {
351 DVLOG(3) << __func__;
352 DCHECK(child_task_runner_->BelongsToCurrentThread());
353 DCHECK(!output_streamon_);
354
355 struct v4l2_format format;
356 memset(&format, 0, sizeof(format));
357 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
358 format.fmt.pix_mp.width = output_allocated_size_.width();
359 format.fmt.pix_mp.height = output_allocated_size_.height();
360 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
361 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
362
363 output_planes_count_ = format.fmt.pix_mp.num_planes;
364 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
365 gfx::Size adjusted_allocated_size =
366 V4L2Device::CodedSizeFromV4L2Format(format);
367 DCHECK(gfx::Rect(adjusted_allocated_size).Contains(
368 gfx::Rect(output_allocated_size_)));
369 output_allocated_size_ = adjusted_allocated_size;
370
371 struct v4l2_crop crop;
372 memset(&crop, 0, sizeof(crop));
373 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
374 crop.c.left = 0;
375 crop.c.top = 0;
376 crop.c.width = base::checked_cast<__u32>(output_visible_size_.width());
377 crop.c.height = base::checked_cast<__u32>(output_visible_size_.height());
378 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
379
380 struct v4l2_requestbuffers reqbufs;
381 memset(&reqbufs, 0, sizeof(reqbufs));
382 reqbufs.count = num_buffers_;
383 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
384 reqbufs.memory = V4L2_MEMORY_MMAP;
385 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
386 if (static_cast<int>(reqbufs.count) != num_buffers_) {
387 LOG(ERROR) << "Failed to allocate output buffers. reqbufs.count="
388 << reqbufs.count << ", num_buffers=" << num_buffers_;
389 return false;
390 }
391
392 DCHECK(output_buffer_map_.empty());
393 output_buffer_map_.resize(reqbufs.count);
394
395 return true;
396 }
397
398 void V4L2ImageProcessor::DestroyInputBuffers() {
399 DCHECK(child_task_runner_->BelongsToCurrentThread());
400 DCHECK(!input_streamon_);
401
402 struct v4l2_requestbuffers reqbufs;
403 memset(&reqbufs, 0, sizeof(reqbufs));
404 reqbufs.count = 0;
405 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
406 reqbufs.memory = input_memory_type_;
407 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
408
409 input_buffer_map_.clear();
410 free_input_buffers_.clear();
411 }
412
413 void V4L2ImageProcessor::DestroyOutputBuffers() {
414 DCHECK(child_task_runner_->BelongsToCurrentThread());
415 DCHECK(!output_streamon_);
416
417 struct v4l2_requestbuffers reqbufs;
418 memset(&reqbufs, 0, sizeof(reqbufs));
419 reqbufs.count = 0;
420 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
421 reqbufs.memory = V4L2_MEMORY_MMAP;
422 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
423
424 output_buffer_map_.clear();
425 }
426
427 void V4L2ImageProcessor::DevicePollTask(bool poll_device) {
428 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current());
429
430 bool event_pending;
431 if (!device_->Poll(poll_device, &event_pending)) {
432 NotifyError();
433 return;
434 }
435
436 // All processing should happen on ServiceDeviceTask(), since we shouldn't
437 // touch encoder state from this thread.
438 device_thread_.message_loop()->PostTask(
439 FROM_HERE,
440 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask,
441 base::Unretained(this)));
442 }
443
444 void V4L2ImageProcessor::ServiceDeviceTask() {
445 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
446 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
447 // so either:
448 // * device_poll_thread_ is running normally
449 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
450 // in which case we should early-out.
451 if (!device_poll_thread_.message_loop())
452 return;
453
454 Dequeue();
455 EnqueueInput();
456
457 if (!device_->ClearDevicePollInterrupt())
458 return;
459
460 bool poll_device =
461 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0);
462
463 device_poll_thread_.message_loop()->PostTask(
464 FROM_HERE,
465 base::Bind(&V4L2ImageProcessor::DevicePollTask,
466 base::Unretained(this),
467 poll_device));
468
469 DVLOG(2) << __func__ << ": buffer counts: INPUT[" << input_queue_.size()
470 << "] => DEVICE[" << free_input_buffers_.size() << "+"
471 << input_buffer_queued_count_ << "/" << input_buffer_map_.size()
472 << "->" << output_buffer_map_.size() - output_buffer_queued_count_
473 << "+" << output_buffer_queued_count_ << "/"
474 << output_buffer_map_.size() << "]";
475 }
476
477 void V4L2ImageProcessor::EnqueueInput() {
478 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
479
480 const int old_inputs_queued = input_buffer_queued_count_;
481 while (!input_queue_.empty() && !free_input_buffers_.empty()) {
482 if (!EnqueueInputRecord())
483 return;
484 }
485 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
486 // We started up a previously empty queue.
487 // Queue state changed; signal interrupt.
488 if (!device_->SetDevicePollInterrupt())
489 return;
490 // VIDIOC_STREAMON if we haven't yet.
491 if (!input_streamon_) {
492 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
493 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
494 input_streamon_ = true;
495 }
496 }
497 }
498
499 void V4L2ImageProcessor::EnqueueOutput(int index) {
500 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
501
502 const int old_outputs_queued = output_buffer_queued_count_;
503 if (!EnqueueOutputRecord(index))
504 return;
505
506 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) {
507 // We just started up a previously empty queue.
508 // Queue state changed; signal interrupt.
509 if (!device_->SetDevicePollInterrupt())
510 return;
511 // Start VIDIOC_STREAMON if we haven't yet.
512 if (!output_streamon_) {
513 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
514 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
515 output_streamon_ = true;
516 }
517 }
518 }
519
520 void V4L2ImageProcessor::Dequeue() {
521 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
522
523 // Dequeue completed input (VIDEO_OUTPUT) buffers,
524 // and recycle to the free list.
525 struct v4l2_buffer dqbuf;
526 struct v4l2_plane planes[VIDEO_MAX_PLANES];
527 while (input_buffer_queued_count_ > 0) {
528 DCHECK(input_streamon_);
529 memset(&dqbuf, 0, sizeof(dqbuf));
530 memset(&planes, 0, sizeof(planes));
531 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
532 dqbuf.memory = input_memory_type_;
533 dqbuf.m.planes = planes;
534 dqbuf.length = input_planes_count_;
535 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
536 if (errno == EAGAIN) {
537 // EAGAIN if we're just out of buffers to dequeue.
538 break;
539 }
540 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
541 NotifyError();
542 return;
543 }
544 InputRecord& input_record = input_buffer_map_[dqbuf.index];
545 DCHECK(input_record.at_device);
546 input_record.at_device = false;
547 input_record.frame = NULL;
548 free_input_buffers_.push_back(dqbuf.index);
549 input_buffer_queued_count_--;
550 }
551
552 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list.
553 // Return the finished buffer to the client via the job ready callback.
554 while (output_buffer_queued_count_ > 0) {
555 DCHECK(output_streamon_);
556 memset(&dqbuf, 0, sizeof(dqbuf));
557 memset(&planes, 0, sizeof(planes));
558 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
559 dqbuf.memory = V4L2_MEMORY_MMAP;
560 dqbuf.m.planes = planes;
561 dqbuf.length = output_planes_count_;
562 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
563 if (errno == EAGAIN) {
564 // EAGAIN if we're just out of buffers to dequeue.
565 break;
566 }
567 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF";
568 NotifyError();
569 return;
570 }
571 OutputRecord& output_record = output_buffer_map_[dqbuf.index];
572 DCHECK(output_record.at_device);
573 output_record.at_device = false;
574 output_buffer_queued_count_--;
575
576 // Jobs are always processed in FIFO order.
577 DCHECK(!running_jobs_.empty());
578 linked_ptr<JobRecord> job_record = running_jobs_.front();
579 running_jobs_.pop();
580
581 DVLOG(3) << "Processing finished, returning frame, index=" << dqbuf.index;
582
583 child_task_runner_->PostTask(
584 FROM_HERE, base::Bind(&V4L2ImageProcessor::FrameReady, weak_this_,
585 job_record->ready_cb, dqbuf.index));
586 }
587 }
588
589 bool V4L2ImageProcessor::EnqueueInputRecord() {
590 DCHECK(!input_queue_.empty());
591 DCHECK(!free_input_buffers_.empty());
592
593 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame.
594 linked_ptr<JobRecord> job_record = input_queue_.front();
595 input_queue_.pop();
596 const int index = free_input_buffers_.back();
597 InputRecord& input_record = input_buffer_map_[index];
598 DCHECK(!input_record.at_device);
599 input_record.frame = job_record->frame;
600 struct v4l2_buffer qbuf;
601 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
602 memset(&qbuf, 0, sizeof(qbuf));
603 memset(qbuf_planes, 0, sizeof(qbuf_planes));
604 qbuf.index = index;
605 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
606 qbuf.memory = input_memory_type_;
607 qbuf.m.planes = qbuf_planes;
608 qbuf.length = input_planes_count_;
609 for (size_t i = 0; i < input_planes_count_; ++i) {
610 qbuf.m.planes[i].bytesused = media::VideoFrame::PlaneSize(
611 input_record.frame->format(), i, input_allocated_size_).GetArea();
612 qbuf.m.planes[i].length = qbuf.m.planes[i].bytesused;
613 if (input_memory_type_ == V4L2_MEMORY_USERPTR) {
614 qbuf.m.planes[i].m.userptr =
615 reinterpret_cast<unsigned long>(input_record.frame->data(i));
616 } else {
617 qbuf.m.planes[i].m.fd = input_record.frame->dmabuf_fd(i);
618 }
619 }
620 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
621 input_record.at_device = true;
622 running_jobs_.push(job_record);
623 free_input_buffers_.pop_back();
624 input_buffer_queued_count_++;
625
626 DVLOG(3) << __func__ << ": enqueued frame ts="
627 << job_record->frame->timestamp().InMilliseconds() << " to device.";
628
629 return true;
630 }
631
632 bool V4L2ImageProcessor::EnqueueOutputRecord(int index) {
633 DCHECK_GE(index, 0);
634 DCHECK_LT(static_cast<size_t>(index), output_buffer_map_.size());
635 // Enqueue an output (VIDEO_CAPTURE) buffer.
636 OutputRecord& output_record = output_buffer_map_[index];
637 DCHECK(!output_record.at_device);
638 struct v4l2_buffer qbuf;
639 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
640 memset(&qbuf, 0, sizeof(qbuf));
641 memset(qbuf_planes, 0, sizeof(qbuf_planes));
642 qbuf.index = index;
643 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
644 qbuf.memory = V4L2_MEMORY_MMAP;
645 qbuf.m.planes = qbuf_planes;
646 qbuf.length = output_planes_count_;
647 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
648 output_record.at_device = true;
649 output_buffer_queued_count_++;
650 return true;
651 }
652
653 bool V4L2ImageProcessor::StartDevicePoll() {
654 DVLOG(3) << __func__ << ": starting device poll";
655 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
656 DCHECK(!device_poll_thread_.IsRunning());
657
658 // Start up the device poll thread and schedule its first DevicePollTask().
659 if (!device_poll_thread_.Start()) {
660 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
661 NotifyError();
662 return false;
663 }
664 // Enqueue a poll task with no devices to poll on - will wait only for the
665 // poll interrupt
666 device_poll_thread_.message_loop()->PostTask(
667 FROM_HERE,
668 base::Bind(
669 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false));
670
671 return true;
672 }
673
674 bool V4L2ImageProcessor::StopDevicePoll() {
675 DVLOG(3) << __func__ << ": stopping device poll";
676 if (device_thread_.IsRunning())
677 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current());
678
679 // Signal the DevicePollTask() to stop, and stop the device poll thread.
680 if (!device_->SetDevicePollInterrupt())
681 return false;
682 device_poll_thread_.Stop();
683
684 // Clear the interrupt now, to be sure.
685 if (!device_->ClearDevicePollInterrupt())
686 return false;
687
688 if (input_streamon_) {
689 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
690 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
691 }
692 input_streamon_ = false;
693
694 if (output_streamon_) {
695 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
696 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
697 }
698 output_streamon_ = false;
699
700 // Reset all our accounting info.
701 while (!input_queue_.empty())
702 input_queue_.pop();
703
704 while (!running_jobs_.empty())
705 running_jobs_.pop();
706
707 free_input_buffers_.clear();
708 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
709 InputRecord& input_record = input_buffer_map_[i];
710 input_record.at_device = false;
711 input_record.frame = NULL;
712 free_input_buffers_.push_back(i);
713 }
714 input_buffer_queued_count_ = 0;
715
716 output_buffer_map_.clear();
717 output_buffer_map_.resize(num_buffers_);
718 output_buffer_queued_count_ = 0;
719
720 return true;
721 }
722
723 void V4L2ImageProcessor::FrameReady(const FrameReadyCB& cb,
724 int output_buffer_index) {
725 DCHECK(child_task_runner_->BelongsToCurrentThread());
726 cb.Run(output_buffer_index);
727 }
728
729 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698