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 <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/callback.h"
13 #include "base/command_line.h"
14 #include "base/debug/trace_event.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
18 #include "content/public/common/content_switches.h"
19 #include "media/base/bitstream_buffer.h"
20
21 #define NOTIFY_ERROR(x) \
22 do { \
23 SetEncoderState(kError); \
24 DLOG(ERROR) << "calling NotifyError(): " << x; \
25 NotifyError(x); \
26 } while (0)
27
28 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value) \
29 do { \
30 if (device_->Ioctl(type, arg) != 0) { \
31 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
32 NOTIFY_ERROR(kPlatformFailureError); \
33 return value; \
34 } \
35 } while (0)
36
37 #define IOCTL_OR_ERROR_RETURN(type, arg) \
38 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0))
39
40 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \
41 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false)
42
43 #define IOCTL_OR_LOG_ERROR(type, arg) \
44 do { \
45 if (device_->Ioctl(type, arg) != 0) \
46 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
47 } while (0)
48
49 namespace content {
50
51 struct V4L2VideoEncodeAccelerator::BitstreamBufferRef {
BitstreamBufferRefcontent::V4L2VideoEncodeAccelerator::BitstreamBufferRef52 BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
53 : id(id), shm(shm.Pass()), size(size) {}
54 const int32 id;
55 const scoped_ptr<base::SharedMemory> shm;
56 const size_t size;
57 };
58
InputRecord()59 V4L2VideoEncodeAccelerator::InputRecord::InputRecord() : at_device(false) {
60 }
61
OutputRecord()62 V4L2VideoEncodeAccelerator::OutputRecord::OutputRecord()
63 : at_device(false), address(NULL), length(0) {
64 }
65
V4L2VideoEncodeAccelerator(scoped_ptr<V4L2Device> device)66 V4L2VideoEncodeAccelerator::V4L2VideoEncodeAccelerator(
67 scoped_ptr<V4L2Device> device)
68 : child_message_loop_proxy_(base::MessageLoopProxy::current()),
69 weak_this_ptr_factory_(this),
70 weak_this_(weak_this_ptr_factory_.GetWeakPtr()),
71 output_buffer_byte_size_(0),
72 device_input_format_(media::VideoFrame::UNKNOWN),
73 input_planes_count_(0),
74 output_format_fourcc_(0),
75 encoder_thread_("V4L2EncoderThread"),
76 encoder_state_(kUninitialized),
77 stream_header_size_(0),
78 device_(device.Pass()),
79 input_streamon_(false),
80 input_buffer_queued_count_(0),
81 input_memory_type_(V4L2_MEMORY_USERPTR),
82 output_streamon_(false),
83 output_buffer_queued_count_(0),
84 device_poll_thread_("V4L2EncoderDevicePollThread") {
85 }
86
~V4L2VideoEncodeAccelerator()87 V4L2VideoEncodeAccelerator::~V4L2VideoEncodeAccelerator() {
88 DCHECK(!encoder_thread_.IsRunning());
89 DCHECK(!device_poll_thread_.IsRunning());
90 DVLOG(4) << __func__;
91
92 DestroyInputBuffers();
93 DestroyOutputBuffers();
94 }
95
Initialize(media::VideoFrame::Format input_format,const gfx::Size & input_visible_size,media::VideoCodecProfile output_profile,uint32 initial_bitrate,Client * client)96 bool V4L2VideoEncodeAccelerator::Initialize(
97 media::VideoFrame::Format input_format,
98 const gfx::Size& input_visible_size,
99 media::VideoCodecProfile output_profile,
100 uint32 initial_bitrate,
101 Client* client) {
102 DVLOG(3) << __func__ << ": input_format="
103 << media::VideoFrame::FormatToString(input_format)
104 << ", input_visible_size=" << input_visible_size.ToString()
105 << ", output_profile=" << output_profile
106 << ", initial_bitrate=" << initial_bitrate;
107
108 visible_size_ = input_visible_size;
109
110 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
111 client_ = client_ptr_factory_->GetWeakPtr();
112
113 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
114 DCHECK_EQ(encoder_state_, kUninitialized);
115
116 struct v4l2_capability caps;
117 memset(&caps, 0, sizeof(caps));
118 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
119 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
120 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
121 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
122 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
123 "caps check failed: 0x" << std::hex << caps.capabilities;
124 return false;
125 }
126
127 if (!SetFormats(input_format, output_profile)) {
128 DLOG(ERROR) << "Failed setting up formats";
129 return false;
130 }
131
132 if (input_format != device_input_format_) {
133 DVLOG(1) << "Input format not supported by the HW, will convert to "
134 << media::VideoFrame::FormatToString(device_input_format_);
135
136 scoped_ptr<V4L2Device> device =
137 V4L2Device::Create(V4L2Device::kImageProcessor);
138 image_processor_.reset(new V4L2ImageProcessor(device.Pass()));
139
140 // Convert from input_format to device_input_format_, keeping the size
141 // at visible_size_ and requiring the output buffers to be of at least
142 // input_allocated_size_.
143 if (!image_processor_->Initialize(
144 input_format,
145 device_input_format_,
146 visible_size_,
147 visible_size_,
148 input_allocated_size_,
149 base::Bind(&V4L2VideoEncodeAccelerator::ImageProcessorError,
150 weak_this_))) {
151 DLOG(ERROR) << "Failed initializing image processor";
152 return false;
153 }
154 }
155
156 if (!InitControls())
157 return false;
158
159 if (!CreateOutputBuffers())
160 return false;
161
162 if (!encoder_thread_.Start()) {
163 DLOG(ERROR) << "Initialize(): encoder thread failed to start";
164 return false;
165 }
166
167 RequestEncodingParametersChange(initial_bitrate, kInitialFramerate);
168
169 SetEncoderState(kInitialized);
170
171 child_message_loop_proxy_->PostTask(
172 FROM_HERE,
173 base::Bind(&Client::RequireBitstreamBuffers,
174 client_,
175 kInputBufferCount,
176 image_processor_.get() ?
177 image_processor_->input_allocated_size() :
178 input_allocated_size_,
179 output_buffer_byte_size_));
180 return true;
181 }
182
ImageProcessorError()183 void V4L2VideoEncodeAccelerator::ImageProcessorError() {
184 DVLOG(1) << "Image processor error";
185 NOTIFY_ERROR(kPlatformFailureError);
186 }
187
Encode(const scoped_refptr<media::VideoFrame> & frame,bool force_keyframe)188 void V4L2VideoEncodeAccelerator::Encode(
189 const scoped_refptr<media::VideoFrame>& frame,
190 bool force_keyframe) {
191 DVLOG(3) << "Encode(): force_keyframe=" << force_keyframe;
192 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
193
194 if (image_processor_) {
195 image_processor_->Process(
196 frame,
197 base::Bind(&V4L2VideoEncodeAccelerator::FrameProcessed,
198 weak_this_,
199 force_keyframe));
200 } else {
201 encoder_thread_.message_loop()->PostTask(
202 FROM_HERE,
203 base::Bind(&V4L2VideoEncodeAccelerator::EncodeTask,
204 base::Unretained(this),
205 frame,
206 force_keyframe));
207 }
208 }
209
UseOutputBitstreamBuffer(const media::BitstreamBuffer & buffer)210 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer(
211 const media::BitstreamBuffer& buffer) {
212 DVLOG(3) << "UseOutputBitstreamBuffer(): id=" << buffer.id();
213 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
214
215 if (buffer.size() < output_buffer_byte_size_) {
216 NOTIFY_ERROR(kInvalidArgumentError);
217 return;
218 }
219
220 scoped_ptr<base::SharedMemory> shm(
221 new base::SharedMemory(buffer.handle(), false));
222 if (!shm->Map(buffer.size())) {
223 NOTIFY_ERROR(kPlatformFailureError);
224 return;
225 }
226
227 scoped_ptr<BitstreamBufferRef> buffer_ref(
228 new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
229 encoder_thread_.message_loop()->PostTask(
230 FROM_HERE,
231 base::Bind(&V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask,
232 base::Unretained(this),
233 base::Passed(&buffer_ref)));
234 }
235
RequestEncodingParametersChange(uint32 bitrate,uint32 framerate)236 void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange(
237 uint32 bitrate,
238 uint32 framerate) {
239 DVLOG(3) << "RequestEncodingParametersChange(): bitrate=" << bitrate
240 << ", framerate=" << framerate;
241 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
242
243 encoder_thread_.message_loop()->PostTask(
244 FROM_HERE,
245 base::Bind(
246 &V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask,
247 base::Unretained(this),
248 bitrate,
249 framerate));
250 }
251
Destroy()252 void V4L2VideoEncodeAccelerator::Destroy() {
253 DVLOG(3) << "Destroy()";
254 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
255
256 // We're destroying; cancel all callbacks.
257 client_ptr_factory_.reset();
258
259 if (image_processor_.get())
260 image_processor_.release()->Destroy();
261
262 // If the encoder thread is running, destroy using posted task.
263 if (encoder_thread_.IsRunning()) {
264 encoder_thread_.message_loop()->PostTask(
265 FROM_HERE,
266 base::Bind(&V4L2VideoEncodeAccelerator::DestroyTask,
267 base::Unretained(this)));
268 // DestroyTask() will put the encoder into kError state and cause all tasks
269 // to no-op.
270 encoder_thread_.Stop();
271 } else {
272 // Otherwise, call the destroy task directly.
273 DestroyTask();
274 }
275
276 // Set to kError state just in case.
277 SetEncoderState(kError);
278
279 delete this;
280 }
281
282 // static
283 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GetSupportedProfiles()284 V4L2VideoEncodeAccelerator::GetSupportedProfiles() {
285 std::vector<SupportedProfile> profiles;
286 SupportedProfile profile;
287
288 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
289 if (cmd_line->HasSwitch(switches::kEnableWebRtcHWVp8Encoding)) {
290 profile.profile = media::VP8PROFILE_MAIN;
291 profile.max_resolution.SetSize(1920, 1088);
292 profile.max_framerate.numerator = 30;
293 profile.max_framerate.denominator = 1;
294 profiles.push_back(profile);
295 } else {
296 profile.profile = media::H264PROFILE_MAIN;
297 profile.max_resolution.SetSize(1920, 1088);
298 profile.max_framerate.numerator = 30;
299 profile.max_framerate.denominator = 1;
300 profiles.push_back(profile);
301 }
302
303 return profiles;
304 }
305
FrameProcessed(bool force_keyframe,const scoped_refptr<media::VideoFrame> & frame)306 void V4L2VideoEncodeAccelerator::FrameProcessed(
307 bool force_keyframe,
308 const scoped_refptr<media::VideoFrame>& frame) {
309 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
310 DVLOG(3) << "FrameProcessed(): force_keyframe=" << force_keyframe;
311
312 encoder_thread_.message_loop()->PostTask(
313 FROM_HERE,
314 base::Bind(&V4L2VideoEncodeAccelerator::EncodeTask,
315 base::Unretained(this),
316 frame,
317 force_keyframe));
318 }
319
EncodeTask(const scoped_refptr<media::VideoFrame> & frame,bool force_keyframe)320 void V4L2VideoEncodeAccelerator::EncodeTask(
321 const scoped_refptr<media::VideoFrame>& frame,
322 bool force_keyframe) {
323 DVLOG(3) << "EncodeTask(): force_keyframe=" << force_keyframe;
324 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
325 DCHECK_NE(encoder_state_, kUninitialized);
326
327 if (encoder_state_ == kError) {
328 DVLOG(2) << "EncodeTask(): early out: kError state";
329 return;
330 }
331
332 encoder_input_queue_.push_back(frame);
333 Enqueue();
334
335 if (force_keyframe) {
336 // TODO(posciak): this presently makes for slightly imprecise encoding
337 // parameters updates. To precisely align the parameter updates with the
338 // incoming input frame, we should queue the parameters together with the
339 // frame onto encoder_input_queue_ and apply them when the input is about
340 // to be queued to the codec.
341 struct v4l2_ext_control ctrls[1];
342 struct v4l2_ext_controls control;
343 memset(&ctrls, 0, sizeof(ctrls));
344 memset(&control, 0, sizeof(control));
345 ctrls[0].id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE;
346 ctrls[0].value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME;
347 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
348 control.count = 1;
349 control.controls = ctrls;
350 IOCTL_OR_ERROR_RETURN(VIDIOC_S_EXT_CTRLS, &control);
351 }
352 }
353
UseOutputBitstreamBufferTask(scoped_ptr<BitstreamBufferRef> buffer_ref)354 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask(
355 scoped_ptr<BitstreamBufferRef> buffer_ref) {
356 DVLOG(3) << "UseOutputBitstreamBufferTask(): id=" << buffer_ref->id;
357 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
358
359 encoder_output_queue_.push_back(
360 linked_ptr<BitstreamBufferRef>(buffer_ref.release()));
361 Enqueue();
362
363 if (encoder_state_ == kInitialized) {
364 // Finish setting up our OUTPUT queue. See: Initialize().
365 // VIDIOC_REQBUFS on OUTPUT queue.
366 if (!CreateInputBuffers())
367 return;
368 if (!StartDevicePoll())
369 return;
370 encoder_state_ = kEncoding;
371 }
372 }
373
DestroyTask()374 void V4L2VideoEncodeAccelerator::DestroyTask() {
375 DVLOG(3) << "DestroyTask()";
376
377 // DestroyTask() should run regardless of encoder_state_.
378
379 // Stop streaming and the device_poll_thread_.
380 StopDevicePoll();
381
382 // Set our state to kError, and early-out all tasks.
383 encoder_state_ = kError;
384 }
385
ServiceDeviceTask()386 void V4L2VideoEncodeAccelerator::ServiceDeviceTask() {
387 DVLOG(3) << "ServiceDeviceTask()";
388 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
389 DCHECK_NE(encoder_state_, kUninitialized);
390 DCHECK_NE(encoder_state_, kInitialized);
391
392 if (encoder_state_ == kError) {
393 DVLOG(2) << "ServiceDeviceTask(): early out: kError state";
394 return;
395 }
396
397 Dequeue();
398 Enqueue();
399
400 // Clear the interrupt fd.
401 if (!device_->ClearDevicePollInterrupt())
402 return;
403
404 // Device can be polled as soon as either input or output buffers are queued.
405 bool poll_device =
406 (input_buffer_queued_count_ + output_buffer_queued_count_ > 0);
407
408 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
409 // so either:
410 // * device_poll_thread_ is running normally
411 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
412 // in which case we're in kError state, and we should have early-outed
413 // already.
414 DCHECK(device_poll_thread_.message_loop());
415 // Queue the DevicePollTask() now.
416 device_poll_thread_.message_loop()->PostTask(
417 FROM_HERE,
418 base::Bind(&V4L2VideoEncodeAccelerator::DevicePollTask,
419 base::Unretained(this),
420 poll_device));
421
422 DVLOG(2) << __func__ << ": buffer counts: ENC["
423 << encoder_input_queue_.size() << "] => DEVICE["
424 << free_input_buffers_.size() << "+"
425 << input_buffer_queued_count_ << "/"
426 << input_buffer_map_.size() << "->"
427 << free_output_buffers_.size() << "+"
428 << output_buffer_queued_count_ << "/"
429 << output_buffer_map_.size() << "] => OUT["
430 << encoder_output_queue_.size() << "]";
431 }
432
Enqueue()433 void V4L2VideoEncodeAccelerator::Enqueue() {
434 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
435
436 DVLOG(3) << "Enqueue() "
437 << "free_input_buffers: " << free_input_buffers_.size()
438 << "input_queue: " << encoder_input_queue_.size();
439
440 // Enqueue all the inputs we can.
441 const int old_inputs_queued = input_buffer_queued_count_;
442 // while (!ready_input_buffers_.empty()) {
443 while (!encoder_input_queue_.empty() && !free_input_buffers_.empty()) {
444 if (!EnqueueInputRecord())
445 return;
446 }
447 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) {
448 // We just started up a previously empty queue.
449 // Queue state changed; signal interrupt.
450 if (!device_->SetDevicePollInterrupt())
451 return;
452 // Start VIDIOC_STREAMON if we haven't yet.
453 if (!input_streamon_) {
454 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
455 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
456 input_streamon_ = true;
457 }
458 }
459
460 // Enqueue all the outputs we can.
461 const int old_outputs_queued = output_buffer_queued_count_;
462 while (!free_output_buffers_.empty() && !encoder_output_queue_.empty()) {
463 if (!EnqueueOutputRecord())
464 return;
465 }
466 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) {
467 // We just started up a previously empty queue.
468 // Queue state changed; signal interrupt.
469 if (!device_->SetDevicePollInterrupt())
470 return;
471 // Start VIDIOC_STREAMON if we haven't yet.
472 if (!output_streamon_) {
473 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
474 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type);
475 output_streamon_ = true;
476 }
477 }
478 }
479
Dequeue()480 void V4L2VideoEncodeAccelerator::Dequeue() {
481 DVLOG(3) << "Dequeue()";
482 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
483
484 // Dequeue completed input (VIDEO_OUTPUT) buffers, and recycle to the free
485 // list.
486 struct v4l2_buffer dqbuf;
487 struct v4l2_plane planes[VIDEO_MAX_PLANES];
488 while (input_buffer_queued_count_ > 0) {
489 DVLOG(4) << "inputs queued: " << input_buffer_queued_count_;
490 DCHECK(input_streamon_);
491 memset(&dqbuf, 0, sizeof(dqbuf));
492 memset(&planes, 0, sizeof(planes));
493 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
494 dqbuf.memory = V4L2_MEMORY_MMAP;
495 dqbuf.m.planes = planes;
496 dqbuf.length = input_planes_count_;
497 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
498 if (errno == EAGAIN) {
499 // EAGAIN if we're just out of buffers to dequeue.
500 break;
501 }
502 DPLOG(ERROR) << "Dequeue(): ioctl() failed: VIDIOC_DQBUF";
503 NOTIFY_ERROR(kPlatformFailureError);
504 return;
505 }
506 InputRecord& input_record = input_buffer_map_[dqbuf.index];
507 DCHECK(input_record.at_device);
508 input_record.at_device = false;
509
510 input_record.frame = NULL;
511 free_input_buffers_.push_back(dqbuf.index);
512 input_buffer_queued_count_--;
513 }
514
515 // Dequeue completed output (VIDEO_CAPTURE) buffers, and recycle to the
516 // free list. Notify the client that an output buffer is complete.
517 while (output_buffer_queued_count_ > 0) {
518 DCHECK(output_streamon_);
519 memset(&dqbuf, 0, sizeof(dqbuf));
520 memset(planes, 0, sizeof(planes));
521 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
522 dqbuf.memory = V4L2_MEMORY_MMAP;
523 dqbuf.m.planes = planes;
524 dqbuf.length = 1;
525 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) {
526 if (errno == EAGAIN) {
527 // EAGAIN if we're just out of buffers to dequeue.
528 break;
529 }
530 DPLOG(ERROR) << "Dequeue(): ioctl() failed: VIDIOC_DQBUF";
531 NOTIFY_ERROR(kPlatformFailureError);
532 return;
533 }
534 const bool key_frame = ((dqbuf.flags & V4L2_BUF_FLAG_KEYFRAME) != 0);
535 OutputRecord& output_record = output_buffer_map_[dqbuf.index];
536 DCHECK(output_record.at_device);
537 DCHECK(output_record.buffer_ref.get());
538
539 void* output_data = output_record.address;
540 size_t output_size = dqbuf.m.planes[0].bytesused;
541 // This shouldn't happen, but just in case. We should be able to recover
542 // after next keyframe after showing some corruption.
543 DCHECK_LE(output_size, output_buffer_byte_size_);
544 if (output_size > output_buffer_byte_size_)
545 output_size = output_buffer_byte_size_;
546 uint8* target_data =
547 reinterpret_cast<uint8*>(output_record.buffer_ref->shm->memory());
548 if (output_format_fourcc_ == V4L2_PIX_FMT_H264) {
549 if (stream_header_size_ == 0) {
550 // Assume that the first buffer dequeued is the stream header.
551 stream_header_size_ = output_size;
552 stream_header_.reset(new uint8[stream_header_size_]);
553 memcpy(stream_header_.get(), output_data, stream_header_size_);
554 }
555 if (key_frame &&
556 output_buffer_byte_size_ - stream_header_size_ >= output_size) {
557 // Insert stream header before every keyframe.
558 memcpy(target_data, stream_header_.get(), stream_header_size_);
559 memcpy(target_data + stream_header_size_, output_data, output_size);
560 output_size += stream_header_size_;
561 } else {
562 memcpy(target_data, output_data, output_size);
563 }
564 } else {
565 memcpy(target_data, output_data, output_size);
566 }
567
568 DVLOG(3) << "Dequeue(): returning "
569 "bitstream_buffer_id=" << output_record.buffer_ref->id
570 << ", size=" << output_size << ", key_frame=" << key_frame;
571 child_message_loop_proxy_->PostTask(
572 FROM_HERE,
573 base::Bind(&Client::BitstreamBufferReady,
574 client_,
575 output_record.buffer_ref->id,
576 output_size,
577 key_frame));
578 output_record.at_device = false;
579 output_record.buffer_ref.reset();
580 free_output_buffers_.push_back(dqbuf.index);
581 output_buffer_queued_count_--;
582 }
583 }
584
EnqueueInputRecord()585 bool V4L2VideoEncodeAccelerator::EnqueueInputRecord() {
586 DVLOG(3) << "EnqueueInputRecord()";
587 DCHECK(!free_input_buffers_.empty());
588 DCHECK(!encoder_input_queue_.empty());
589
590 // Enqueue an input (VIDEO_OUTPUT) buffer.
591 scoped_refptr<media::VideoFrame> frame = encoder_input_queue_.front();
592 const int index = free_input_buffers_.back();
593 InputRecord& input_record = input_buffer_map_[index];
594 DCHECK(!input_record.at_device);
595 struct v4l2_buffer qbuf;
596 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES];
597 memset(&qbuf, 0, sizeof(qbuf));
598 memset(qbuf_planes, 0, sizeof(qbuf_planes));
599 qbuf.index = index;
600 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
601 qbuf.m.planes = qbuf_planes;
602
603 DCHECK_EQ(device_input_format_, frame->format());
604 for (size_t i = 0; i < input_planes_count_; ++i) {
605 qbuf.m.planes[i].bytesused =
606 base::checked_cast<__u32>(media::VideoFrame::PlaneAllocationSize(
607 frame->format(), i, input_allocated_size_));
608
609 switch (input_memory_type_) {
610 case V4L2_MEMORY_USERPTR:
611 qbuf.m.planes[i].m.userptr =
612 reinterpret_cast<unsigned long>(frame->data(i));
613 DCHECK(qbuf.m.planes[i].m.userptr);
614 break;
615
616 case V4L2_MEMORY_DMABUF:
617 qbuf.m.planes[i].m.fd = frame->dmabuf_fd(i);
618 DCHECK_NE(qbuf.m.planes[i].m.fd, -1);
619 break;
620
621 default:
622 NOTREACHED();
623 return false;
624 }
625 }
626
627 qbuf.memory = input_memory_type_;
628 qbuf.length = input_planes_count_;
629
630 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
631 input_record.at_device = true;
632 input_record.frame = frame;
633 encoder_input_queue_.pop_front();
634 free_input_buffers_.pop_back();
635 input_buffer_queued_count_++;
636 return true;
637 }
638
EnqueueOutputRecord()639 bool V4L2VideoEncodeAccelerator::EnqueueOutputRecord() {
640 DVLOG(3) << "EnqueueOutputRecord()";
641 DCHECK(!free_output_buffers_.empty());
642 DCHECK(!encoder_output_queue_.empty());
643
644 // Enqueue an output (VIDEO_CAPTURE) buffer.
645 linked_ptr<BitstreamBufferRef> output_buffer = encoder_output_queue_.back();
646 const int index = free_output_buffers_.back();
647 OutputRecord& output_record = output_buffer_map_[index];
648 DCHECK(!output_record.at_device);
649 DCHECK(!output_record.buffer_ref.get());
650 struct v4l2_buffer qbuf;
651 struct v4l2_plane qbuf_planes[1];
652 memset(&qbuf, 0, sizeof(qbuf));
653 memset(qbuf_planes, 0, sizeof(qbuf_planes));
654 qbuf.index = index;
655 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
656 qbuf.memory = V4L2_MEMORY_MMAP;
657 qbuf.m.planes = qbuf_planes;
658 qbuf.length = 1;
659 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
660 output_record.at_device = true;
661 output_record.buffer_ref = output_buffer;
662 encoder_output_queue_.pop_back();
663 free_output_buffers_.pop_back();
664 output_buffer_queued_count_++;
665 return true;
666 }
667
StartDevicePoll()668 bool V4L2VideoEncodeAccelerator::StartDevicePoll() {
669 DVLOG(3) << "StartDevicePoll()";
670 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
671 DCHECK(!device_poll_thread_.IsRunning());
672
673 // Start up the device poll thread and schedule its first DevicePollTask().
674 if (!device_poll_thread_.Start()) {
675 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
676 NOTIFY_ERROR(kPlatformFailureError);
677 return false;
678 }
679 // Enqueue a poll task with no devices to poll on -- it will wait only on the
680 // interrupt fd.
681 device_poll_thread_.message_loop()->PostTask(
682 FROM_HERE,
683 base::Bind(&V4L2VideoEncodeAccelerator::DevicePollTask,
684 base::Unretained(this),
685 false));
686
687 return true;
688 }
689
StopDevicePoll()690 bool V4L2VideoEncodeAccelerator::StopDevicePoll() {
691 DVLOG(3) << "StopDevicePoll()";
692
693 // Signal the DevicePollTask() to stop, and stop the device poll thread.
694 if (!device_->SetDevicePollInterrupt())
695 return false;
696 device_poll_thread_.Stop();
697 // Clear the interrupt now, to be sure.
698 if (!device_->ClearDevicePollInterrupt())
699 return false;
700
701 if (input_streamon_) {
702 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
703 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
704 }
705 input_streamon_ = false;
706
707 if (output_streamon_) {
708 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
709 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
710 }
711 output_streamon_ = false;
712
713 // Reset all our accounting info.
714 encoder_input_queue_.clear();
715 free_input_buffers_.clear();
716 for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
717 InputRecord& input_record = input_buffer_map_[i];
718 input_record.at_device = false;
719 input_record.frame = NULL;
720 free_input_buffers_.push_back(i);
721 }
722 input_buffer_queued_count_ = 0;
723
724 free_output_buffers_.clear();
725 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
726 OutputRecord& output_record = output_buffer_map_[i];
727 output_record.at_device = false;
728 output_record.buffer_ref.reset();
729 free_output_buffers_.push_back(i);
730 }
731 output_buffer_queued_count_ = 0;
732
733 encoder_output_queue_.clear();
734
735 DVLOG(3) << "StopDevicePoll(): device poll stopped";
736 return true;
737 }
738
DevicePollTask(bool poll_device)739 void V4L2VideoEncodeAccelerator::DevicePollTask(bool poll_device) {
740 DVLOG(3) << "DevicePollTask()";
741 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current());
742
743 bool event_pending;
744 if (!device_->Poll(poll_device, &event_pending)) {
745 NOTIFY_ERROR(kPlatformFailureError);
746 return;
747 }
748
749 // All processing should happen on ServiceDeviceTask(), since we shouldn't
750 // touch encoder state from this thread.
751 encoder_thread_.message_loop()->PostTask(
752 FROM_HERE,
753 base::Bind(&V4L2VideoEncodeAccelerator::ServiceDeviceTask,
754 base::Unretained(this)));
755 }
756
NotifyError(Error error)757 void V4L2VideoEncodeAccelerator::NotifyError(Error error) {
758 DVLOG(1) << "NotifyError(): error=" << error;
759
760 if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
761 child_message_loop_proxy_->PostTask(
762 FROM_HERE,
763 base::Bind(
764 &V4L2VideoEncodeAccelerator::NotifyError, weak_this_, error));
765 return;
766 }
767
768 if (client_) {
769 client_->NotifyError(error);
770 client_ptr_factory_.reset();
771 }
772 }
773
SetEncoderState(State state)774 void V4L2VideoEncodeAccelerator::SetEncoderState(State state) {
775 DVLOG(3) << "SetEncoderState(): state=" << state;
776
777 // We can touch encoder_state_ only if this is the encoder thread or the
778 // encoder thread isn't running.
779 if (encoder_thread_.message_loop() != NULL &&
780 encoder_thread_.message_loop() != base::MessageLoop::current()) {
781 encoder_thread_.message_loop()->PostTask(
782 FROM_HERE,
783 base::Bind(&V4L2VideoEncodeAccelerator::SetEncoderState,
784 base::Unretained(this),
785 state));
786 } else {
787 encoder_state_ = state;
788 }
789 }
790
RequestEncodingParametersChangeTask(uint32 bitrate,uint32 framerate)791 void V4L2VideoEncodeAccelerator::RequestEncodingParametersChangeTask(
792 uint32 bitrate,
793 uint32 framerate) {
794 DVLOG(3) << "RequestEncodingParametersChangeTask(): bitrate=" << bitrate
795 << ", framerate=" << framerate;
796 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
797
798 if (bitrate < 1)
799 bitrate = 1;
800 if (framerate < 1)
801 framerate = 1;
802
803 struct v4l2_ext_control ctrls[1];
804 struct v4l2_ext_controls control;
805 memset(&ctrls, 0, sizeof(ctrls));
806 memset(&control, 0, sizeof(control));
807 ctrls[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
808 ctrls[0].value = bitrate;
809 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
810 control.count = arraysize(ctrls);
811 control.controls = ctrls;
812 IOCTL_OR_ERROR_RETURN(VIDIOC_S_EXT_CTRLS, &control);
813
814 struct v4l2_streamparm parms;
815 memset(&parms, 0, sizeof(parms));
816 parms.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
817 // Note that we are provided "frames per second" but V4L2 expects "time per
818 // frame"; hence we provide the reciprocal of the framerate here.
819 parms.parm.output.timeperframe.numerator = 1;
820 parms.parm.output.timeperframe.denominator = framerate;
821 IOCTL_OR_ERROR_RETURN(VIDIOC_S_PARM, &parms);
822 }
823
SetOutputFormat(media::VideoCodecProfile output_profile)824 bool V4L2VideoEncodeAccelerator::SetOutputFormat(
825 media::VideoCodecProfile output_profile) {
826 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
827 DCHECK(!input_streamon_);
828 DCHECK(!output_streamon_);
829
830 output_format_fourcc_ =
831 V4L2Device::VideoCodecProfileToV4L2PixFmt(output_profile);
832 if (!output_format_fourcc_) {
833 DLOG(ERROR) << "Initialize(): invalid output_profile=" << output_profile;
834 return false;
835 }
836
837 output_buffer_byte_size_ = kOutputBufferSize;
838
839 struct v4l2_format format;
840 memset(&format, 0, sizeof(format));
841 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
842 format.fmt.pix_mp.width = visible_size_.width();
843 format.fmt.pix_mp.height = visible_size_.height();
844 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
845 format.fmt.pix_mp.plane_fmt[0].sizeimage =
846 base::checked_cast<__u32>(output_buffer_byte_size_);
847 format.fmt.pix_mp.num_planes = 1;
848 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
849
850 // Device might have adjusted the required output size.
851 size_t adjusted_output_buffer_size =
852 base::checked_cast<size_t>(format.fmt.pix_mp.plane_fmt[0].sizeimage);
853 DCHECK_GE(adjusted_output_buffer_size, output_buffer_byte_size_);
854 output_buffer_byte_size_ = adjusted_output_buffer_size;
855
856 return true;
857 }
858
NegotiateInputFormat(media::VideoFrame::Format input_format)859 bool V4L2VideoEncodeAccelerator::NegotiateInputFormat(
860 media::VideoFrame::Format input_format) {
861 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
862 DCHECK(!input_streamon_);
863 DCHECK(!output_streamon_);
864
865 device_input_format_ = media::VideoFrame::UNKNOWN;
866 input_planes_count_ = 0;
867
868 uint32 input_format_fourcc =
869 V4L2Device::VideoFrameFormatToV4L2PixFmt(input_format);
870 if (!input_format_fourcc) {
871 DVLOG(1) << "Unsupported input format";
872 return false;
873 }
874
875 size_t input_planes_count = media::VideoFrame::NumPlanes(input_format);
876 DCHECK_LE(input_planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
877
878 // First see if we the device can use the provided input_format directly.
879 struct v4l2_format format;
880 memset(&format, 0, sizeof(format));
881 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
882 format.fmt.pix_mp.width = visible_size_.width();
883 format.fmt.pix_mp.height = visible_size_.height();
884 format.fmt.pix_mp.pixelformat = input_format_fourcc;
885 format.fmt.pix_mp.num_planes = input_planes_count;
886 if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
887 // Error or format unsupported by device, try to negotiate a fallback.
888 input_format_fourcc = device_->PreferredInputFormat();
889 input_format =
890 V4L2Device::V4L2PixFmtToVideoFrameFormat(input_format_fourcc);
891 if (input_format == media::VideoFrame::UNKNOWN)
892 return false;
893
894 input_planes_count = media::VideoFrame::NumPlanes(input_format);
895 DCHECK_LE(input_planes_count, static_cast<size_t>(VIDEO_MAX_PLANES));
896
897 // Device might have adjusted parameters, reset them along with the format.
898 memset(&format, 0, sizeof(format));
899 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
900 format.fmt.pix_mp.width = visible_size_.width();
901 format.fmt.pix_mp.height = visible_size_.height();
902 format.fmt.pix_mp.pixelformat = input_format_fourcc;
903 format.fmt.pix_mp.num_planes = input_planes_count;
904 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format);
905 DCHECK_EQ(format.fmt.pix_mp.num_planes, input_planes_count);
906 }
907
908 // Take device-adjusted sizes for allocated size.
909 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format);
910 DCHECK(gfx::Rect(input_allocated_size_).Contains(gfx::Rect(visible_size_)));
911
912 device_input_format_ = input_format;
913 input_planes_count_ = input_planes_count;
914 return true;
915 }
916
SetFormats(media::VideoFrame::Format input_format,media::VideoCodecProfile output_profile)917 bool V4L2VideoEncodeAccelerator::SetFormats(
918 media::VideoFrame::Format input_format,
919 media::VideoCodecProfile output_profile) {
920 DVLOG(3) << "SetFormats()";
921 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
922 DCHECK(!input_streamon_);
923 DCHECK(!output_streamon_);
924
925 if (!SetOutputFormat(output_profile))
926 return false;
927
928 if (!NegotiateInputFormat(input_format))
929 return false;
930
931 struct v4l2_crop crop;
932 memset(&crop, 0, sizeof(crop));
933 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
934 crop.c.left = 0;
935 crop.c.top = 0;
936 crop.c.width = visible_size_.width();
937 crop.c.height = visible_size_.height();
938 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop);
939
940 return true;
941 }
942
InitControls()943 bool V4L2VideoEncodeAccelerator::InitControls() {
944 struct v4l2_ext_control ctrls[9];
945 struct v4l2_ext_controls control;
946 memset(&ctrls, 0, sizeof(ctrls));
947 memset(&control, 0, sizeof(control));
948 // No B-frames, for lowest decoding latency.
949 ctrls[0].id = V4L2_CID_MPEG_VIDEO_B_FRAMES;
950 ctrls[0].value = 0;
951 // Enable frame-level bitrate control.
952 ctrls[1].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
953 ctrls[1].value = 1;
954 // Enable "tight" bitrate mode. For this to work properly, frame- and mb-level
955 // bitrate controls have to be enabled as well.
956 ctrls[2].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF;
957 ctrls[2].value = 1;
958 // Force bitrate control to average over a GOP (for tight bitrate
959 // tolerance).
960 ctrls[3].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT;
961 ctrls[3].value = 1;
962 // Quantization parameter maximum value (for variable bitrate control).
963 ctrls[4].id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP;
964 ctrls[4].value = 51;
965 // Separate stream header so we can cache it and insert into the stream.
966 ctrls[5].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
967 ctrls[5].value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE;
968 // Enable macroblock-level bitrate control.
969 ctrls[6].id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE;
970 ctrls[6].value = 1;
971 // Use H.264 level 4.0 to match the supported max resolution.
972 ctrls[7].id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
973 ctrls[7].value = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
974 // Disable periodic key frames.
975 ctrls[8].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
976 ctrls[8].value = 0;
977 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
978 control.count = arraysize(ctrls);
979 control.controls = ctrls;
980 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_EXT_CTRLS, &control);
981
982 return true;
983 }
984
CreateInputBuffers()985 bool V4L2VideoEncodeAccelerator::CreateInputBuffers() {
986 DVLOG(3) << "CreateInputBuffers()";
987 // This function runs on encoder_thread_ after output buffers have been
988 // provided by the client.
989 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
990 DCHECK(!input_streamon_);
991
992 struct v4l2_requestbuffers reqbufs;
993 memset(&reqbufs, 0, sizeof(reqbufs));
994 // Driver will modify to the appropriate number of buffers.
995 reqbufs.count = 1;
996 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
997 // TODO(posciak): Once we start doing zero-copy, we should decide based on
998 // the current pipeline setup which memory type to use. This should probably
999 // be decided based on an argument to Initialize().
1000 if (image_processor_.get())
1001 input_memory_type_ = V4L2_MEMORY_DMABUF;
1002 else
1003 input_memory_type_ = V4L2_MEMORY_USERPTR;
1004
1005 reqbufs.memory = input_memory_type_;
1006 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
1007
1008 DCHECK(input_buffer_map_.empty());
1009 input_buffer_map_.resize(reqbufs.count);
1010 for (size_t i = 0; i < input_buffer_map_.size(); ++i)
1011 free_input_buffers_.push_back(i);
1012
1013 return true;
1014 }
1015
CreateOutputBuffers()1016 bool V4L2VideoEncodeAccelerator::CreateOutputBuffers() {
1017 DVLOG(3) << "CreateOutputBuffers()";
1018 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1019 DCHECK(!output_streamon_);
1020
1021 struct v4l2_requestbuffers reqbufs;
1022 memset(&reqbufs, 0, sizeof(reqbufs));
1023 reqbufs.count = kOutputBufferCount;
1024 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1025 reqbufs.memory = V4L2_MEMORY_MMAP;
1026 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs);
1027
1028 DCHECK(output_buffer_map_.empty());
1029 output_buffer_map_.resize(reqbufs.count);
1030 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
1031 struct v4l2_plane planes[1];
1032 struct v4l2_buffer buffer;
1033 memset(&buffer, 0, sizeof(buffer));
1034 memset(planes, 0, sizeof(planes));
1035 buffer.index = i;
1036 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1037 buffer.memory = V4L2_MEMORY_MMAP;
1038 buffer.m.planes = planes;
1039 buffer.length = arraysize(planes);
1040 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYBUF, &buffer);
1041 void* address = device_->Mmap(NULL,
1042 buffer.m.planes[0].length,
1043 PROT_READ | PROT_WRITE,
1044 MAP_SHARED,
1045 buffer.m.planes[0].m.mem_offset);
1046 if (address == MAP_FAILED) {
1047 DPLOG(ERROR) << "CreateOutputBuffers(): mmap() failed";
1048 return false;
1049 }
1050 output_buffer_map_[i].address = address;
1051 output_buffer_map_[i].length = buffer.m.planes[0].length;
1052 free_output_buffers_.push_back(i);
1053 }
1054
1055 return true;
1056 }
1057
DestroyInputBuffers()1058 void V4L2VideoEncodeAccelerator::DestroyInputBuffers() {
1059 DVLOG(3) << "DestroyInputBuffers()";
1060 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1061 DCHECK(!input_streamon_);
1062
1063 struct v4l2_requestbuffers reqbufs;
1064 memset(&reqbufs, 0, sizeof(reqbufs));
1065 reqbufs.count = 0;
1066 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1067 reqbufs.memory = input_memory_type_;
1068 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
1069
1070 input_buffer_map_.clear();
1071 free_input_buffers_.clear();
1072 }
1073
DestroyOutputBuffers()1074 void V4L2VideoEncodeAccelerator::DestroyOutputBuffers() {
1075 DVLOG(3) << "DestroyOutputBuffers()";
1076 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1077 DCHECK(!output_streamon_);
1078
1079 for (size_t i = 0; i < output_buffer_map_.size(); ++i) {
1080 if (output_buffer_map_[i].address != NULL)
1081 device_->Munmap(output_buffer_map_[i].address,
1082 output_buffer_map_[i].length);
1083 }
1084
1085 struct v4l2_requestbuffers reqbufs;
1086 memset(&reqbufs, 0, sizeof(reqbufs));
1087 reqbufs.count = 0;
1088 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1089 reqbufs.memory = V4L2_MEMORY_MMAP;
1090 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs);
1091
1092 output_buffer_map_.clear();
1093 free_output_buffers_.clear();
1094 }
1095
1096 } // namespace content
1097