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

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

Issue 20962003: ExynosVideoEncodeAccelerator (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@screencast_vea
Patch Set: 0d6e96d3 Nits. Created 7 years, 4 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 (c) 2013 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 "content/common/gpu/media/exynos_video_encode_accelerator.h"
6
7 #include <fcntl.h>
8 #include <linux/videodev2.h>
9 #include <poll.h>
10 #include <sys/eventfd.h>
11 #include <sys/ioctl.h>
12
13 #include "base/callback.h"
14 #include "base/debug/trace_event.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "media/base/bitstream_buffer.h"
18
19 #define NOTIFY_ERROR(x) \
20 do { \
21 SetEncoderState(kError); \
22 DLOG(ERROR) << "calling NotifyError(): " << x; \
23 NotifyError(x); \
24 } while (0)
25
26 #define IOCTL_OR_ERROR_RETURN(fd, type, arg) \
27 do { \
28 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \
29 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
30 NOTIFY_ERROR(kPlatformFailureError); \
31 return; \
32 } \
33 } while (0)
34
35 #define IOCTL_OR_ERROR_RETURN_FALSE(fd, type, arg) \
36 do { \
37 if (HANDLE_EINTR(ioctl(fd, type, arg) != 0)) { \
38 DPLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \
39 NOTIFY_ERROR(kPlatformFailureError); \
40 return false; \
41 } \
42 } while (0)
43
44 namespace content {
45
46 namespace {
47
48 const char kExynosGscDevice[] = "/dev/gsc1";
49 const char kExynosMfcDevice[] = "/dev/mfc-enc";
50
51 // File descriptors we need to poll, one-bit flag for each.
52 enum PollFds {
53 kPollGsc = (1 << 0),
54 kPollMfc = (1 << 1),
55 };
56
57 } // anonymous namespace
58
59 struct ExynosVideoEncodeAccelerator::BitstreamBufferRef {
60 BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
61 : id(id), shm(shm.Pass()), size(size) {}
62 const int32 id;
63 const scoped_ptr<base::SharedMemory> shm;
64 const size_t size;
65 };
66
67
68 ExynosVideoEncodeAccelerator::GscInputRecord::GscInputRecord()
69 : at_device(false) {}
70
71 ExynosVideoEncodeAccelerator::GscOutputRecord::GscOutputRecord()
72 : at_device(false), mfc_input(-1) {}
73
74 ExynosVideoEncodeAccelerator::MfcInputRecord::MfcInputRecord()
75 : at_device(false) {
76 fd[0] = fd[1] = -1;
77 }
78
79 ExynosVideoEncodeAccelerator::MfcOutputRecord::MfcOutputRecord()
80 : at_device(false) {}
81
82 ExynosVideoEncodeAccelerator::ExynosVideoEncodeAccelerator(
83 media::VideoEncodeAccelerator::Client* client)
84 : child_message_loop_proxy_(base::MessageLoopProxy::current()),
85 weak_this_ptr_factory_(this),
86 weak_this_(weak_this_ptr_factory_.GetWeakPtr()),
87 client_ptr_factory_(client),
88 client_(client_ptr_factory_.GetWeakPtr()),
89 encoder_thread_("ExynosEncoderThread"),
90 encoder_state_(kUninitialized),
91 output_buffer_byte_size_(0),
92 stream_header_size_(0),
93 input_format_fourcc_(0),
94 output_format_fourcc_(0),
95 gsc_fd_(-1),
96 gsc_input_streamon_(false),
97 gsc_input_buffer_queued_count_(0),
98 gsc_output_streamon_(false),
99 gsc_output_buffer_queued_count_(0),
100 mfc_fd_(-1),
101 mfc_input_streamon_(false),
102 mfc_input_buffer_queued_count_(0),
103 mfc_output_streamon_(false),
104 mfc_output_buffer_queued_count_(0),
105 device_poll_thread_("ExynosEncoderDevicePollThread"),
106 device_poll_interrupt_fd_(-1) {
107 DCHECK(client_);
108 }
109
110 ExynosVideoEncodeAccelerator::~ExynosVideoEncodeAccelerator() {
111 DCHECK(!encoder_thread_.IsRunning());
112 DCHECK(!device_poll_thread_.IsRunning());
113
114 if (device_poll_interrupt_fd_ != -1) {
115 HANDLE_EINTR(close(device_poll_interrupt_fd_));
116 device_poll_interrupt_fd_ = -1;
117 }
118 if (mfc_fd_ != -1) {
119 DestroyMfcInputBuffers();
120 DestroyMfcOutputBuffers();
121 HANDLE_EINTR(close(mfc_fd_));
122 mfc_fd_ = -1;
123 }
124 if (gsc_fd_ != -1) {
125 DestroyGscInputBuffers();
126 DestroyGscOutputBuffers();
127 HANDLE_EINTR(close(gsc_fd_));
128 gsc_fd_ = -1;
129 }
130 }
131
132 void ExynosVideoEncodeAccelerator::Initialize(
133 media::VideoFrame::Format input_format,
134 const gfx::Size& input_visible_size,
135 media::VideoCodecProfile output_profile,
136 uint32 initial_bitrate) {
137 DVLOG(3) << "Initialize(): input_format=" << input_format
138 << ", input_visible_size=" << input_visible_size.ToString()
139 << ", output_profile=" << output_profile
140 << ", initial_bitrate=" << initial_bitrate;
141
142 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
143 DCHECK_EQ(encoder_state_, kUninitialized);
144
145 input_visible_size_ = input_visible_size;
146 input_allocated_size_.SetSize((input_visible_size_.width() + 0xF) & ~0xF,
147 (input_visible_size_.height() + 0xF) & ~0xF);
148 converted_visible_size_.SetSize((input_visible_size_.width() + 0x1) & ~0x1,
149 (input_visible_size_.height() + 0x1) & ~0x1);
150 converted_allocated_size_.SetSize(
151 (converted_visible_size_.width() + 0xF) & ~0xF,
152 (converted_visible_size_.height() + 0xF) & ~0xF);
153 output_visible_size_ = converted_visible_size_;
154
155 switch (input_format) {
156 case media::VideoFrame::RGB32:
157 input_format_fourcc_ = V4L2_PIX_FMT_RGB32;
158 break;
159 case media::VideoFrame::I420:
160 input_format_fourcc_ = V4L2_PIX_FMT_YUV420M;
161 break;
162 default:
163 NOTIFY_ERROR(kInvalidArgumentError);
164 return;
165 }
166
167 if (output_profile >= media::H264PROFILE_MIN &&
168 output_profile <= media::H264PROFILE_MAX) {
169 output_format_fourcc_ = V4L2_PIX_FMT_H264;
170 } else {
171 NOTIFY_ERROR(kInvalidArgumentError);
172 return;
173 }
174
175 // Open the color conversion device.
176 DVLOG(2) << "Initialize(): opening GSC device: " << kExynosGscDevice;
177 gsc_fd_ =
178 HANDLE_EINTR(open(kExynosGscDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC));
179 if (gsc_fd_ == -1) {
180 DPLOG(ERROR) << "Initialize(): could not open GSC device: "
181 << kExynosGscDevice;
182 NOTIFY_ERROR(kPlatformFailureError);
183 return;
184 }
185
186 // Capabilities check.
187 struct v4l2_capability caps;
188 memset(&caps, 0, sizeof(caps));
189 const __u32 kCapsRequired = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
190 V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
191 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_QUERYCAP, &caps);
192 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
193 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
194 "caps check failed: 0x" << std::hex << caps.capabilities;
195 NOTIFY_ERROR(kPlatformFailureError);
196 return;
197 }
198
199 // Open the video encoder device.
200 mfc_fd_ =
201 HANDLE_EINTR(open(kExynosMfcDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC));
202 if (mfc_fd_ == -1) {
203 DPLOG(ERROR) << "Initialize(): could not open MFC device: "
204 << kExynosMfcDevice;
205 NOTIFY_ERROR(kPlatformFailureError);
206 return;
207 }
208
209 memset(&caps, 0, sizeof(caps));
210 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_QUERYCAP, &caps);
211 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
212 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
213 "caps check failed: 0x" << std::hex << caps.capabilities;
214 NOTIFY_ERROR(kPlatformFailureError);
215 return;
216 }
217
218 // Create the interrupt fd.
219 DCHECK_EQ(device_poll_interrupt_fd_, -1);
220 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
221 if (device_poll_interrupt_fd_ == -1) {
222 DPLOG(ERROR) << "Initialize(): eventfd() failed";
223 NOTIFY_ERROR(kPlatformFailureError);
224 return;
225 }
226
227 DVLOG(3)
228 << "Initialize(): input_visible_size_=" << input_visible_size_.ToString()
229 << ", input_allocated_size_=" << input_allocated_size_.ToString()
230 << ", converted_visible_size_=" << converted_visible_size_.ToString()
231 << ", converted_allocated_size_=" << converted_allocated_size_.ToString()
232 << ", output_visible_size_=" << output_visible_size_.ToString();
233
234 if (!CreateGscInputBuffers() || !CreateGscOutputBuffers())
235 return;
236
237 // MFC setup for encoding is rather particular in ordering:
238 //
239 // 1. Format (VIDIOC_S_FMT) set first on OUTPUT and CAPTURE queues.
240 // 2. VIDIOC_REQBUFS, VIDIOC_QBUF, and VIDIOC_STREAMON on CAPTURE queue.
241 // 3. VIDIOC_REQBUFS (and later VIDIOC_QBUF and VIDIOC_STREAMON) on OUTPUT
242 // queue.
243 //
244 // Unfortunately, we cannot do (3) in Initialize() here since we have no
245 // buffers to QBUF in step (2) until the client has provided output buffers
246 // through UseOutputBitstreamBuffer(). So, we just do (1), and the
247 // VIDIOC_REQBUFS part of (2) here. The rest is done the first time we get
248 // a UseOutputBitstreamBuffer() callback.
249
250 if (!SetMfcFormats())
251 return;
252
253 RequestEncodingParametersChangeTask(initial_bitrate, kInitialFramerate);
hshi1 2013/08/13 17:11:20 Where is this "kInitialFramerate" defined?
sheu 2013/08/14 01:00:42 Uhh.. it actually went into exynos_video_*decode*_
254
255 // VIDIOC_REQBUFS on CAPTURE queue.
256 if (!CreateMfcOutputBuffers())
257 return;
258
259
260 if (!encoder_thread_.Start()) {
261 DLOG(ERROR) << "Initialize(): encoder thread failed to start";
262 NOTIFY_ERROR(kPlatformFailureError);
263 return;
264 }
265
266 SetEncoderState(kInitialized);
267
268 child_message_loop_proxy_->PostTask(
269 FROM_HERE, base::Bind(&Client::NotifyInitializeDone, client_));
270
271 child_message_loop_proxy_->PostTask(
272 FROM_HERE,
273 base::Bind(&Client::RequireBitstreamBuffers,
274 client_,
275 gsc_input_buffer_map_.size(),
276 input_allocated_size_,
277 output_buffer_byte_size_));
278 }
279
280 void ExynosVideoEncodeAccelerator::Encode(
281 const scoped_refptr<media::VideoFrame>& frame,
282 bool force_keyframe) {
283 DVLOG(3) << "Encode(): force_keyframe=" << force_keyframe;
284 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
285
286 encoder_thread_.message_loop()->PostTask(
287 FROM_HERE,
288 base::Bind(&ExynosVideoEncodeAccelerator::EncodeTask,
289 base::Unretained(this),
290 frame,
291 force_keyframe));
292 }
293
294 void ExynosVideoEncodeAccelerator::UseOutputBitstreamBuffer(
295 const media::BitstreamBuffer& buffer) {
296 DVLOG(3) << "UseOutputBitstreamBuffer(): id=" << buffer.id();
297 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
298
299 if (buffer.size() < output_buffer_byte_size_) {
300 NOTIFY_ERROR(kInvalidArgumentError);
301 return;
302 }
303
304 scoped_ptr<base::SharedMemory> shm(
305 new base::SharedMemory(buffer.handle(), false));
306 if (!shm->Map(buffer.size())) {
307 NOTIFY_ERROR(kPlatformFailureError);
308 return;
309 }
310
311 scoped_ptr<BitstreamBufferRef> buffer_ref(
312 new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
313 encoder_thread_.message_loop()->PostTask(
314 FROM_HERE,
315 base::Bind(&ExynosVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
316 base::Unretained(this),
317 base::Passed(&buffer_ref)));
318 }
319
320 void ExynosVideoEncodeAccelerator::RequestEncodingParametersChange(
321 uint32 bitrate,
322 uint32 framerate) {
323 DVLOG(3) << "RequestEncodingParametersChange(): bitrate=" << bitrate
324 << ", framerate=" << framerate;
325 if (bitrate < 1 || framerate < 1) {
326 DLOG(ERROR) << "RequestEncodingParametersChange(): "
327 "invalid bitrate=" << bitrate
328 << " or framerate=" << framerate;
329 NOTIFY_ERROR(kInvalidArgumentError);
330 return;
331 }
332 encoder_thread_.message_loop()->PostTask(
333 FROM_HERE,
334 base::Bind(
335 &ExynosVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
336 base::Unretained(this),
337 bitrate,
338 framerate));
339 }
340
341 void ExynosVideoEncodeAccelerator::Destroy() {
342 DVLOG(3) << "Destroy()";
343 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
344
345 // We're destroying; cancel all callbacks.
346 client_ptr_factory_.InvalidateWeakPtrs();
347
348 // If the encoder thread is running, destroy using posted task.
349 if (encoder_thread_.IsRunning()) {
350 encoder_thread_.message_loop()->PostTask(
351 FROM_HERE,
352 base::Bind(&ExynosVideoEncodeAccelerator::DestroyTask,
353 base::Unretained(this)));
354 // DestroyTask() will cause the encoder_thread_ to flush all tasks.
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 s/flush all tasks/turn all tasks into no-ops as st
sheu 2013/08/14 01:00:42 Done.
355 encoder_thread_.Stop();
356 } else {
357 // Otherwise, call the destroy task directly.
358 DestroyTask();
359 }
360
361 // Set to kError state just in case.
362 SetEncoderState(kError);
363
364 delete this;
365 }
366
367 // static
368 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
369 ExynosVideoEncodeAccelerator::GetSupportedProfiles() {
370 std::vector<SupportedProfile> profiles(1);
371 SupportedProfile& profile = profiles[0];
372 profile.profile = media::H264PROFILE_MAIN;
373 profile.max_resolution.SetSize(1920, 1088);
374 profile.max_framerate.numerator = 30;
375 profile.max_framerate.denominator = 1;
376 return profiles;
377 }
378
379 void ExynosVideoEncodeAccelerator::EncodeTask(
380 const scoped_refptr<media::VideoFrame>& frame, bool force_keyframe) {
381 DVLOG(3) << "EncodeTask(): force_keyframe=" << force_keyframe;
382 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
383 DCHECK_NE(encoder_state_, kUninitialized);
384
385 if (encoder_state_ == kError) {
386 DVLOG(2) << "EncodeTask(): early out: kError state";
387 return;
388 }
389
390 encoder_input_queue_.push_back(frame);
391 EnqueueGsc();
392
393 if (force_keyframe) {
394 struct v4l2_ext_control ctrls[1];
395 struct v4l2_ext_controls control;
396 memset(&ctrls, 0, sizeof(ctrls));
397 memset(&control, 0, sizeof(control));
398 ctrls[0].id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE;
399 ctrls[0].value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME;
400 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
401 control.count = 1;
402 control.controls = ctrls;
403 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 Please comment to this effect here.
sheu 2013/08/14 01:00:42 Done.
404 }
405 }
406
407 void ExynosVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
408 scoped_ptr<BitstreamBufferRef> buffer_ref) {
409 DVLOG(3) << "UseOutputBitstreamBufferTask(): id=" << buffer_ref->id;
410 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
411
412 encoder_output_queue_.push_back(
413 linked_ptr<BitstreamBufferRef>(buffer_ref.release()));
414 EnqueueMfc();
415
416 if (encoder_state_ == kInitialized) {
417 // Finish setting up our MFC OUTPUT queue. See: Initialize().
418 // VIDIOC_REQBUFS on OUTPUT queue.
419 if (!CreateMfcInputBuffers())
420 return;
421 if (!StartDevicePoll())
422 return;
423 encoder_state_ = kEncoding;
424 }
425 }
426
427 void ExynosVideoEncodeAccelerator::DestroyTask() {
428 DVLOG(3) << "DestroyTask()";
429
430 // DestroyTask() should run regardless of encoder_state_.
431
432 // Stop streaming and the device_poll_thread_.
433 StopDevicePoll();
434
435 // Set our state to kError. Just in case.
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 This isn't "just in case" because you promise it i
sheu 2013/08/14 01:00:42 Done.
436 encoder_state_ = kError;
437 }
438
439 void ExynosVideoEncodeAccelerator::ServiceDeviceTask() {
440 DVLOG(3) << "ServiceDeviceTask()";
441 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
442 DCHECK_NE(encoder_state_, kUninitialized);
443 DCHECK_NE(encoder_state_, kInitialized);
444
445 if (encoder_state_ == kError) {
446 DVLOG(2) << "ServiceDeviceTask(): early out: kError state";
447 return;
448 }
449
450 DequeueGsc();
451 DequeueMfc();
452 EnqueueGsc();
453 EnqueueMfc();
454
455 // Clear the interrupt fd.
456 if (!ClearDevicePollInterrupt())
457 return;
458
459 unsigned int poll_fds = 0;
460 // Add GSC fd, if we should poll on it.
461 // GSC has to wait until both input and output buffers are queued.
462 if (gsc_input_buffer_queued_count_ > 0 && gsc_output_buffer_queued_count_ > 0)
463 poll_fds |= kPollGsc;
464 // Add MFC fd, if we should poll on it.
465 // MFC can be polled as soon as either input or output buffers are queued.
466 if (mfc_input_buffer_queued_count_ + mfc_output_buffer_queued_count_ > 0)
467 poll_fds |= kPollMfc;
468
469 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
470 // so either:
471 // * device_poll_thread_ is running normally
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 This is now a single-bullet list headed by "either
sheu 2013/08/14 01:00:42 A linebreak sneaked in somehow. Fixed.
472 //
473 // device_poll_thread_ scheduled us, but then a DestroyTask() shut it down,
474 // in which case we're in kError state, and we should have early-outed
475 // already.
476 DCHECK(device_poll_thread_.message_loop());
477 // Queue the DevicePollTask() now.
478 device_poll_thread_.message_loop()->PostTask(
479 FROM_HERE,
480 base::Bind(&ExynosVideoEncodeAccelerator::DevicePollTask,
481 base::Unretained(this),
482 poll_fds));
483
484 DVLOG(2) << "ServiceDeviceTask(): buffer counts: ENC["
485 << encoder_input_queue_.size() << "] => GSC["
486 << gsc_free_input_buffers_.size() << "+"
487 << gsc_input_buffer_queued_count_ << "/"
488 << gsc_input_buffer_map_.size() << "->"
489 << gsc_free_output_buffers_.size() << "+"
490 << gsc_output_buffer_queued_count_ << "/"
491 << gsc_output_buffer_map_.size() << "] => "
492 << mfc_ready_input_buffers_.size() << " => MFC["
493 << mfc_free_input_buffers_.size() << "+"
494 << mfc_input_buffer_queued_count_ << "/"
495 << mfc_input_buffer_map_.size() << "->"
496 << mfc_free_output_buffers_.size() << "+"
497 << mfc_output_buffer_queued_count_ << "/"
498 << mfc_output_buffer_map_.size() << "] => OUT["
499 << encoder_output_queue_.size() << "]";
500 }
501
502 void ExynosVideoEncodeAccelerator::EnqueueGsc() {
503 DVLOG(3) << "EnqueueGsc()";
504 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
505
506 const int old_gsc_inputs_queued = gsc_input_buffer_queued_count_;
507 while (!encoder_input_queue_.empty() && !gsc_free_input_buffers_.empty()) {
508 if (!EnqueueGscInputRecord())
509 return;
510 }
511 if (old_gsc_inputs_queued == 0 && gsc_input_buffer_queued_count_ != 0) {
512 // We started up a previously empty queue.
513 // Queue state changed; signal interrupt.
514 if (!SetDevicePollInterrupt())
515 return;
516 // Start VIDIOC_STREAMON if we haven't yet.
517 if (!gsc_input_streamon_) {
518 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
519 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_STREAMON, &type);
520 gsc_input_streamon_ = true;
521 }
522 }
523
524 // Enqueue a GSC output, only if we need one. GSC output buffers write
525 // directly to MFC input buffers, so we'll have to check for free MFC input
526 // buffers as well.
527 // GSC is liable to race conditions if more than one output buffer is
528 // simultaneously enqueued, so enqueue just one.
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 crbug for the GSC race conditions?
529 if (gsc_input_buffer_queued_count_ != 0 &&
530 gsc_output_buffer_queued_count_ == 0 &&
531 !gsc_free_output_buffers_.empty() && !mfc_free_input_buffers_.empty()) {
532 const int old_gsc_outputs_queued = gsc_output_buffer_queued_count_;
533 if (!EnqueueGscOutputRecord())
534 return;
535 if (old_gsc_outputs_queued == 0 && gsc_output_buffer_queued_count_ != 0) {
536 // We just started up a previously empty queue.
537 // Queue state changed; signal interrupt.
538 if (!SetDevicePollInterrupt())
539 return;
540 // Start VIDIOC_STREAMON if we haven't yet.
541 if (!gsc_output_streamon_) {
542 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
543 IOCTL_OR_ERROR_RETURN(gsc_fd_, VIDIOC_STREAMON, &type);
544 gsc_output_streamon_ = true;
545 }
546 }
547 }
548 DCHECK_LE(gsc_output_buffer_queued_count_, 1);
549 }
550
551 void ExynosVideoEncodeAccelerator::DequeueGsc() {
552 DVLOG(3) << "DequeueGsc()";
553 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
554
555 // Dequeue completed GSC input (VIDEO_OUTPUT) buffers, and recycle to the free
556 // list.
557 struct v4l2_buffer dqbuf;
558 struct v4l2_plane planes[3];
559 while (gsc_input_buffer_queued_count_ > 0) {
560 DCHECK(gsc_input_streamon_);
561 memset(&dqbuf, 0, sizeof(dqbuf));
562 memset(&planes, 0, sizeof(planes));
563 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
564 dqbuf.memory = V4L2_MEMORY_USERPTR;
565 dqbuf.m.planes = planes;
566 dqbuf.length = arraysize(planes);
567 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf)) != 0) {
568 if (errno == EAGAIN) {
569 // EAGAIN if we're just out of buffers to dequeue.
570 break;
571 }
572 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF";
573 NOTIFY_ERROR(kPlatformFailureError);
574 return;
575 }
576 GscInputRecord& input_record = gsc_input_buffer_map_[dqbuf.index];
577 DCHECK(input_record.at_device);
578 DCHECK(input_record.frame.get());
579 input_record.at_device = false;
580 input_record.frame = NULL;
581 gsc_free_input_buffers_.push_back(dqbuf.index);
582 gsc_input_buffer_queued_count_--;
583 }
584
585 // Dequeue completed GSC output (VIDEO_CAPTURE) buffers, and recycle to the
586 // free list. Queue the corresponding MFC buffer to the GSC->MFC holding
587 // queue.
588 while (gsc_output_buffer_queued_count_ > 0) {
589 DCHECK(gsc_output_streamon_);
590 memset(&dqbuf, 0, sizeof(dqbuf));
591 memset(&planes, 0, sizeof(planes));
592 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
593 dqbuf.memory = V4L2_MEMORY_DMABUF;
594 dqbuf.m.planes = planes;
595 dqbuf.length = 2;
596 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf)) != 0) {
597 if (errno == EAGAIN) {
598 // EAGAIN if we're just out of buffers to dequeue.
599 break;
600 }
601 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF";
602 NOTIFY_ERROR(kPlatformFailureError);
603 return;
604 }
605 GscOutputRecord& output_record = gsc_output_buffer_map_[dqbuf.index];
606 DCHECK(output_record.at_device);
607 DCHECK(output_record.mfc_input != -1);
608 mfc_ready_input_buffers_.push_back(output_record.mfc_input);
609 output_record.at_device = false;
610 output_record.mfc_input = -1;
611 gsc_free_output_buffers_.push_back(dqbuf.index);
612 gsc_output_buffer_queued_count_--;
613 }
614 }
615 void ExynosVideoEncodeAccelerator::EnqueueMfc() {
616 DVLOG(3) << "EnqueueMfc()";
617 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
618
619 // Enqueue all the MFC inputs we can.
620 const int old_mfc_inputs_queued = mfc_input_buffer_queued_count_;
621 while (!mfc_ready_input_buffers_.empty()) {
622 if (!EnqueueMfcInputRecord())
623 return;
624 }
625 if (old_mfc_inputs_queued == 0 && mfc_input_buffer_queued_count_ != 0) {
626 // We just started up a previously empty queue.
627 // Queue state changed; signal interrupt.
628 if (!SetDevicePollInterrupt())
629 return;
630 // Start VIDIOC_STREAMON if we haven't yet.
631 if (!mfc_input_streamon_) {
632 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
633 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_STREAMON, &type);
634 mfc_input_streamon_ = true;
635 }
636 }
637
638 // Enqueue all the MFC outputs we can.
639 const int old_mfc_outputs_queued = mfc_output_buffer_queued_count_;
640 while (!mfc_free_output_buffers_.empty() && !encoder_output_queue_.empty()) {
641 if (!EnqueueMfcOutputRecord())
642 return;
643 }
644 if (old_mfc_outputs_queued == 0 && mfc_output_buffer_queued_count_ != 0) {
645 // We just started up a previously empty queue.
646 // Queue state changed; signal interrupt.
647 if (!SetDevicePollInterrupt())
648 return;
649 // Start VIDIOC_STREAMON if we haven't yet.
650 if (!mfc_output_streamon_) {
651 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
652 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_STREAMON, &type);
653 mfc_output_streamon_ = true;
654 }
655 }
656 }
657
658 void ExynosVideoEncodeAccelerator::DequeueMfc() {
659 DVLOG(3) << "DequeueMfc()";
660 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
661
662 // Dequeue completed MFC input (VIDEO_OUTPUT) buffers, and recycle to the free
663 // list.
664 struct v4l2_buffer dqbuf;
665 struct v4l2_plane planes[2];
666 while (mfc_input_buffer_queued_count_ > 0) {
667 DCHECK(mfc_input_streamon_);
668 memset(&dqbuf, 0, sizeof(dqbuf));
669 memset(&planes, 0, sizeof(planes));
670 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
671 dqbuf.memory = V4L2_MEMORY_MMAP;
672 dqbuf.m.planes = planes;
673 dqbuf.length = 2;
674 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf)) != 0) {
675 if (errno == EAGAIN) {
676 // EAGAIN if we're just out of buffers to dequeue.
677 break;
678 }
679 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF";
680 NOTIFY_ERROR(kPlatformFailureError);
681 return;
682 }
683 MfcInputRecord& input_record = mfc_input_buffer_map_[dqbuf.index];
684 DCHECK(input_record.at_device);
685 input_record.at_device = false;
686 mfc_free_input_buffers_.push_back(dqbuf.index);
687 mfc_input_buffer_queued_count_--;
688 }
689
690 // Dequeue completed MFC output (VIDEO_CAPTURE) buffers, and recycle to the
691 // free list. Notify the client that an output buffer is complete.
692 while (mfc_output_buffer_queued_count_ > 0) {
693 DCHECK(mfc_output_streamon_);
694 memset(&dqbuf, 0, sizeof(dqbuf));
695 memset(planes, 0, sizeof(planes));
696 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
697 dqbuf.memory = V4L2_MEMORY_USERPTR;
698 dqbuf.m.planes = planes;
699 dqbuf.length = 1;
700 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf)) != 0) {
701 if (errno == EAGAIN) {
702 // EAGAIN if we're just out of buffers to dequeue.
703 break;
704 }
705 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF";
706 NOTIFY_ERROR(kPlatformFailureError);
707 return;
708 }
709 const bool key_frame = ((dqbuf.flags & V4L2_BUF_FLAG_KEYFRAME) != 0);
710 const size_t output_size = dqbuf.m.planes[0].bytesused;
711 MfcOutputRecord& output_record = mfc_output_buffer_map_[dqbuf.index];
712 DCHECK(output_record.at_device);
713 DCHECK(output_record.buffer_ref.get());
714 uint8* data =
715 reinterpret_cast<uint8*>(output_record.buffer_ref->shm->memory());
716 if (stream_header_size_ == 0) {
717 // Assume that the first buffer dequeued is the stream header.
718 stream_header_size_ = output_size;
719 stream_header_.reset(new uint8[stream_header_size_]);
720 memcpy(stream_header_.get(), data, stream_header_size_);
721 }
722 if (key_frame &&
723 output_buffer_byte_size_ - stream_header_size_ >= output_size) {
724 // Insert stream header before every keyframe.
725 memmove(data + stream_header_size_, data, output_size);
726 memcpy(data, stream_header_.get(), stream_header_size_);
727 }
728 DVLOG(3) << "DequeueMfc(): returning "
729 "bitstream_buffer_id=" << output_record.buffer_ref->id
730 << ", key_frame=" << key_frame;
731 child_message_loop_proxy_->PostTask(
732 FROM_HERE,
733 base::Bind(&Client::BitstreamBufferReady,
734 client_,
735 output_record.buffer_ref->id,
736 dqbuf.m.planes[0].bytesused,
737 key_frame));
738 output_record.at_device = false;
739 output_record.buffer_ref.reset();
740 mfc_free_output_buffers_.push_back(dqbuf.index);
741 mfc_output_buffer_queued_count_--;
742 }
743 }
744
745 bool ExynosVideoEncodeAccelerator::EnqueueGscInputRecord() {
746 DVLOG(3) << "EnqueueGscInputRecord()";
747 DCHECK(!encoder_input_queue_.empty());
748 DCHECK(!gsc_free_input_buffers_.empty());
749
750 // Enqueue a GSC input (VIDEO_OUTPUT) buffer for an input video frame
751 scoped_refptr<media::VideoFrame> frame = encoder_input_queue_.front();
752 const int gsc_buffer = gsc_free_input_buffers_.back();
753 GscInputRecord& input_record = gsc_input_buffer_map_[gsc_buffer];
754 DCHECK(!input_record.at_device);
755 DCHECK(!input_record.frame.get());
756 struct v4l2_buffer qbuf;
757 struct v4l2_plane qbuf_planes[3];
758 memset(&qbuf, 0, sizeof(qbuf));
759 memset(qbuf_planes, 0, sizeof(qbuf_planes));
760 qbuf.index = gsc_buffer;
761 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
762 qbuf.memory = V4L2_MEMORY_USERPTR;
763 qbuf.m.planes = qbuf_planes;
764 switch (input_format_fourcc_) {
765 case V4L2_PIX_FMT_RGB32: {
766 qbuf.m.planes[0].bytesused = input_allocated_size_.GetArea() * 4;
767 qbuf.m.planes[0].length = input_allocated_size_.GetArea() * 4;
768 qbuf.m.planes[0].m.userptr = reinterpret_cast<unsigned long>(
769 frame->data(media::VideoFrame::kRGBPlane));
770 qbuf.length = 1;
771 break;
772 }
773 case V4L2_PIX_FMT_YUV420M: {
774 qbuf.m.planes[0].bytesused = input_allocated_size_.GetArea();
775 qbuf.m.planes[0].length = input_allocated_size_.GetArea();
776 qbuf.m.planes[0].m.userptr = reinterpret_cast<unsigned long>(
777 frame->data(media::VideoFrame::kYPlane));
778 qbuf.m.planes[1].bytesused = input_allocated_size_.GetArea() / 4;
779 qbuf.m.planes[1].length = input_allocated_size_.GetArea() / 4;
780 qbuf.m.planes[1].m.userptr = reinterpret_cast<unsigned long>(
781 frame->data(media::VideoFrame::kUPlane));
782 qbuf.m.planes[2].bytesused = input_allocated_size_.GetArea() / 4;
783 qbuf.m.planes[2].length = input_allocated_size_.GetArea() / 4;
784 qbuf.m.planes[2].m.userptr = reinterpret_cast<unsigned long>(
785 frame->data(media::VideoFrame::kVPlane));
786 qbuf.length = 3;
787 break;
788 }
789 default:
790 NOTREACHED();
791 NOTIFY_ERROR(kIllegalStateError);
792 return false;
793 }
794 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_QBUF, &qbuf);
795 input_record.at_device = true;
796 input_record.frame = frame;
797 encoder_input_queue_.pop_front();
798 gsc_free_input_buffers_.pop_back();
799 gsc_input_buffer_queued_count_++;
800 return true;
801 }
802
803 bool ExynosVideoEncodeAccelerator::EnqueueGscOutputRecord() {
804 DVLOG(3) << "EnqueueGscOutputRecord()";
805 DCHECK(!gsc_free_output_buffers_.empty());
806 DCHECK(!mfc_free_input_buffers_.empty());
807
808 // Enqueue a GSC output (VIDEO_CAPTURE) buffer.
809 const int gsc_buffer = gsc_free_output_buffers_.back();
810 const int mfc_buffer = mfc_free_input_buffers_.back();
811 GscOutputRecord& output_record = gsc_output_buffer_map_[gsc_buffer];
812 MfcInputRecord& input_record = mfc_input_buffer_map_[mfc_buffer];
813 DCHECK(!output_record.at_device);
814 DCHECK_EQ(output_record.mfc_input, -1);
815 DCHECK(!input_record.at_device);
816 struct v4l2_buffer qbuf;
817 struct v4l2_plane qbuf_planes[2];
818 memset(&qbuf, 0, sizeof(qbuf));
819 memset(qbuf_planes, 0, sizeof(qbuf_planes));
820 qbuf.index = gsc_buffer;
821 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
822 qbuf.memory = V4L2_MEMORY_DMABUF;
823 qbuf.m.planes = qbuf_planes;
824 qbuf.m.planes[0].m.fd = input_record.fd[0];
825 qbuf.m.planes[1].m.fd = input_record.fd[1];
826 qbuf.length = 2;
827 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_QBUF, &qbuf);
828 output_record.at_device = true;
829 output_record.mfc_input = mfc_buffer;
830 mfc_free_input_buffers_.pop_back();
831 gsc_free_output_buffers_.pop_back();
832 gsc_output_buffer_queued_count_++;
833 return true;
834 }
835
836 bool ExynosVideoEncodeAccelerator::EnqueueMfcInputRecord() {
837 DVLOG(3) << "EnqueueMfcInputRecord()";
838 DCHECK(!mfc_ready_input_buffers_.empty());
839
840 // Enqueue a MFC input (VIDEO_OUTPUT) buffer.
841 const int mfc_buffer = mfc_ready_input_buffers_.front();
842 MfcInputRecord& input_record = mfc_input_buffer_map_[mfc_buffer];
843 DCHECK(!input_record.at_device);
844 struct v4l2_buffer qbuf;
845 struct v4l2_plane qbuf_planes[2];
846 memset(&qbuf, 0, sizeof(qbuf));
847 memset(qbuf_planes, 0, sizeof(qbuf_planes));
848 qbuf.index = mfc_buffer;
849 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
850 qbuf.memory = V4L2_MEMORY_MMAP;
851 qbuf.m.planes = qbuf_planes;
852 qbuf.length = 2;
853 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_QBUF, &qbuf);
854 input_record.at_device = true;
855 mfc_ready_input_buffers_.pop_front();
856 mfc_input_buffer_queued_count_++;
857 return true;
858 }
859
860 bool ExynosVideoEncodeAccelerator::EnqueueMfcOutputRecord() {
861 DVLOG(3) << "EnqueueMfcOutputRecord()";
862 DCHECK(!mfc_free_output_buffers_.empty());
863 DCHECK(!encoder_output_queue_.empty());
864
865 // Enqueue a MFC output (VIDEO_CAPTURE) buffer.
866 linked_ptr<BitstreamBufferRef> output_buffer = encoder_output_queue_.back();
867 const int mfc_buffer = mfc_free_output_buffers_.back();
868 MfcOutputRecord& output_record = mfc_output_buffer_map_[mfc_buffer];
869 DCHECK(!output_record.at_device);
870 DCHECK(!output_record.buffer_ref.get());
871 struct v4l2_buffer qbuf;
872 struct v4l2_plane qbuf_planes[1];
873 memset(&qbuf, 0, sizeof(qbuf));
874 memset(qbuf_planes, 0, sizeof(qbuf_planes));
875 qbuf.index = mfc_buffer;
876 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
877 qbuf.memory = V4L2_MEMORY_USERPTR;
878 qbuf.m.planes = qbuf_planes;
879 qbuf.m.planes[0].bytesused = output_buffer->size;
880 qbuf.m.planes[0].length = output_buffer->size;
881 qbuf.m.planes[0].m.userptr =
882 reinterpret_cast<unsigned long>(output_buffer->shm->memory());
883 qbuf.length = 1;
884 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_QBUF, &qbuf);
885 output_record.at_device = true;
886 output_record.buffer_ref = output_buffer;
887 encoder_output_queue_.pop_back();
888 mfc_free_output_buffers_.pop_back();
889 mfc_output_buffer_queued_count_++;
890 return true;
891 }
892
893 bool ExynosVideoEncodeAccelerator::StartDevicePoll() {
894 DVLOG(3) << "StartDevicePoll()";
895 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
896 DCHECK(!device_poll_thread_.IsRunning());
897
898 // Start up the device poll thread and schedule its first DevicePollTask().
899 if (!device_poll_thread_.Start()) {
900 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
901 NOTIFY_ERROR(kPlatformFailureError);
902 return false;
903 }
904 // Enqueue a poll task with no devices to poll on -- it will wait only on the
905 // interrupt fd.
906 device_poll_thread_.message_loop()->PostTask(
907 FROM_HERE,
908 base::Bind(&ExynosVideoEncodeAccelerator::DevicePollTask,
909 base::Unretained(this),
910 0));
911
912 return true;
913 }
914
915 bool ExynosVideoEncodeAccelerator::StopDevicePoll() {
916 DVLOG(3) << "StopDevicePoll()";
917
918 // Signal the DevicePollTask() to stop, and stop the device poll thread.
919 if (!SetDevicePollInterrupt())
920 return false;
921 device_poll_thread_.Stop();
922 // Clear the interrupt now, to be sure.
923 if (!ClearDevicePollInterrupt())
924 return false;
925
926 // Stop streaming.
927 if (gsc_input_streamon_) {
928 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
929 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_STREAMOFF, &type);
930 }
931 gsc_input_streamon_ = false;
932 if (gsc_output_streamon_) {
933 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
934 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_STREAMOFF, &type);
935 }
936 gsc_output_streamon_ = false;
937 if (mfc_input_streamon_) {
938 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
939 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_STREAMOFF, &type);
940 }
941 mfc_input_streamon_ = false;
942 if (mfc_output_streamon_) {
943 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
944 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_STREAMOFF, &type);
945 }
946 mfc_output_streamon_ = false;
947
948 // Reset all our accounting info.
949 encoder_input_queue_.clear();
950 gsc_free_input_buffers_.clear();
951 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i) {
952 GscInputRecord& input_record = gsc_input_buffer_map_[i];
953 input_record.at_device = false;
954 input_record.frame = NULL;
955 gsc_free_input_buffers_.push_back(i);
956 }
957 gsc_input_buffer_queued_count_ = 0;
958 gsc_free_output_buffers_.clear();
959 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) {
960 GscOutputRecord& output_record = gsc_output_buffer_map_[i];
961 output_record.at_device = false;
962 output_record.mfc_input = -1;
963 gsc_free_output_buffers_.push_back(i);
964 }
965 gsc_output_buffer_queued_count_ = 0;
966 mfc_ready_input_buffers_.clear();
967 mfc_free_input_buffers_.clear();
968 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) {
969 MfcInputRecord& input_record = mfc_input_buffer_map_[i];
970 input_record.at_device = false;
971 mfc_free_input_buffers_.push_back(i);
972 }
973 mfc_input_buffer_queued_count_ = 0;
974 mfc_free_output_buffers_.clear();
975 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) {
976 MfcOutputRecord& output_record = mfc_output_buffer_map_[i];
977 output_record.at_device = false;
978 output_record.buffer_ref.reset();
979 mfc_free_output_buffers_.push_back(i);
980 }
981 mfc_output_buffer_queued_count_ = 0;
982 encoder_output_queue_.clear();
983
984 DVLOG(3) << "StopDevicePoll(): device poll stopped";
985 return true;
986 }
987
988 bool ExynosVideoEncodeAccelerator::SetDevicePollInterrupt() {
989 DVLOG(3) << "SetDevicePollInterrupt()";
990
991 // We might get called here if we fail during initialization, in which case we
992 // don't have a file descriptor.
993 if (device_poll_interrupt_fd_ == -1)
994 return true;
995
996 const uint64 buf = 1;
997 if (HANDLE_EINTR((write(device_poll_interrupt_fd_, &buf, sizeof(buf)))) <
998 static_cast<ssize_t>(sizeof(buf))) {
999 DPLOG(ERROR) << "SetDevicePollInterrupt(): write() failed";
1000 NOTIFY_ERROR(kPlatformFailureError);
1001 return false;
1002 }
1003 return true;
1004 }
1005
1006 bool ExynosVideoEncodeAccelerator::ClearDevicePollInterrupt() {
1007 DVLOG(3) << "ClearDevicePollInterrupt()";
1008
1009 // We might get called here if we fail during initialization, in which case we
1010 // don't have a file descriptor.
1011 if (device_poll_interrupt_fd_ == -1)
1012 return true;
1013
1014 uint64 buf;
1015 if (HANDLE_EINTR(read(device_poll_interrupt_fd_, &buf, sizeof(buf))) <
1016 static_cast<ssize_t>(sizeof(buf))) {
1017 if (errno == EAGAIN) {
1018 // No interrupt flag set, and we're reading nonblocking. Not an error.
1019 return true;
1020 } else {
1021 DPLOG(ERROR) << "ClearDevicePollInterrupt(): read() failed";
1022 NOTIFY_ERROR(kPlatformFailureError);
1023 return false;
1024 }
1025 }
1026 return true;
1027 }
1028
1029 void ExynosVideoEncodeAccelerator::DevicePollTask(unsigned int poll_fds) {
1030 DVLOG(3) << "DevicePollTask()";
1031 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current());
1032 DCHECK_NE(device_poll_interrupt_fd_, -1);
1033
1034 // This routine just polls the set of device fds, and schedules a
1035 // ServiceDeviceTask() on encoder_thread_ when processing needs to occur.
1036 // Other threads may notify this task to return early by writing to
1037 // device_poll_interrupt_fd_.
1038 struct pollfd pollfds[3];
1039 nfds_t nfds;
1040
1041 // Add device_poll_interrupt_fd_;
1042 pollfds[0].fd = device_poll_interrupt_fd_;
1043 pollfds[0].events = POLLIN | POLLERR;
1044 nfds = 1;
1045
1046 // Add GSC fd, if we should poll on it.
1047 // GSC has to wait until both input and output buffers are queued.
1048 if (poll_fds & kPollGsc) {
1049 DVLOG(3) << "DevicePollTask(): adding GSC to poll() set";
1050 pollfds[nfds].fd = gsc_fd_;
1051 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR;
1052 nfds++;
1053 }
1054 if (poll_fds & kPollMfc) {
1055 DVLOG(3) << "DevicePollTask(): adding MFC to poll() set";
1056 pollfds[nfds].fd = mfc_fd_;
1057 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR;
1058 nfds++;
1059 }
1060
1061 // Poll it!
1062 if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) {
1063 DPLOG(ERROR) << "DevicePollTask(): poll() failed";
1064 NOTIFY_ERROR(kPlatformFailureError);
1065 return;
1066 }
1067
1068 // All processing should happen on ServiceDeviceTask(), since we shouldn't
1069 // touch encoder state from this thread.
1070 encoder_thread_.message_loop()->PostTask(
1071 FROM_HERE,
1072 base::Bind(&ExynosVideoEncodeAccelerator::ServiceDeviceTask,
1073 base::Unretained(this)));
1074 }
1075
1076 void ExynosVideoEncodeAccelerator::NotifyError(Error error) {
1077 DVLOG(1) << "NotifyError(): error=" << error;
1078
1079 if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
1080 child_message_loop_proxy_->PostTask(
1081 FROM_HERE,
1082 base::Bind(
1083 &ExynosVideoEncodeAccelerator::NotifyError, weak_this_, error));
1084 return;
1085 }
1086
1087 if (client_) {
1088 client_->NotifyError(error);
1089 client_ptr_factory_.InvalidateWeakPtrs();
1090 }
1091 }
1092
1093 void ExynosVideoEncodeAccelerator::SetEncoderState(State state) {
1094 DVLOG(3) << "SetEncoderState(): state=" << state;
1095
1096 // We can touch encoder_state_ only if this is the encoder thread or the
1097 // encoder thread isn't running.
1098 if (encoder_thread_.message_loop() != NULL &&
1099 encoder_thread_.message_loop() != base::MessageLoop::current()) {
1100 encoder_thread_.message_loop()->PostTask(
1101 FROM_HERE,
1102 base::Bind(&ExynosVideoEncodeAccelerator::SetEncoderState,
1103 base::Unretained(this),
1104 state));
1105 } else {
1106 encoder_state_ = state;
1107 }
1108 }
1109
1110 void ExynosVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
1111 uint32 bitrate,
1112 uint32 framerate) {
1113 DVLOG(3) << "RequestEncodingParametersChangeTask(): bitrate=" << bitrate
1114 << ", framerate=" << framerate;
1115 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1116
1117 struct v4l2_ext_control ctrls[1];
1118 struct v4l2_ext_controls control;
1119 memset(&ctrls, 0, sizeof(ctrls));
1120 memset(&control, 0, sizeof(control));
1121 ctrls[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
1122 ctrls[0].value = bitrate;
1123 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
1124 control.count = arraysize(ctrls);
1125 control.controls = ctrls;
1126 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
1127
1128 struct v4l2_streamparm parms;
1129 memset(&parms, 0, sizeof(parms));
1130 parms.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1131 // Note that we are provided "frames per second" but V4L2 expects "time per
1132 // frame"; hence we provide the reciprocal of the framerate here.
1133 parms.parm.output.timeperframe.numerator = 1;
1134 parms.parm.output.timeperframe.denominator = framerate;
1135 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_PARM, &parms);
1136 }
1137
1138 bool ExynosVideoEncodeAccelerator::CreateGscInputBuffers() {
1139 DVLOG(3) << "CreateGscInputBuffers()";
1140 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1141 DCHECK_EQ(encoder_state_, kUninitialized);
1142 DCHECK(!gsc_input_streamon_);
1143
1144 struct v4l2_control control;
1145 memset(&control, 0, sizeof(control));
1146 control.id = V4L2_CID_ROTATE;
1147 control.value = 0;
1148 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1149
1150 // HFLIP actually seems to control vertical mirroring for GSC, and vice-versa.
1151 memset(&control, 0, sizeof(control));
1152 control.id = V4L2_CID_HFLIP;
1153 control.value = 0;
1154 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1155
1156 memset(&control, 0, sizeof(control));
1157 control.id = V4L2_CID_VFLIP;
1158 control.value = 0;
1159 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1160
1161 memset(&control, 0, sizeof(control));
1162 control.id = V4L2_CID_GLOBAL_ALPHA;
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 I wonder if it's worth asserting that A==0xFF some
sheu 2013/08/14 01:00:42 I'm gonna say, not really worth it. We're convert
1163 control.value = 255;
1164 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1165
1166 struct v4l2_format format;
1167 memset(&format, 0, sizeof(format));
1168 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1169 format.fmt.pix_mp.width = input_allocated_size_.width();
1170 format.fmt.pix_mp.height = input_allocated_size_.height();
1171 format.fmt.pix_mp.pixelformat = input_format_fourcc_;
1172 switch (input_format_fourcc_) {
1173 case V4L2_PIX_FMT_RGB32:
1174 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1175 input_allocated_size_.GetArea() * 4;
1176 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1177 input_allocated_size_.width() * 4;
1178 format.fmt.pix_mp.num_planes = 1;
1179 break;
1180 case V4L2_PIX_FMT_YUV420M:
1181 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1182 input_allocated_size_.GetArea();
1183 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1184 input_allocated_size_.width();
1185 format.fmt.pix_mp.plane_fmt[1].sizeimage =
1186 input_allocated_size_.GetArea() / 4;
1187 format.fmt.pix_mp.plane_fmt[1].bytesperline =
1188 input_allocated_size_.width() / 2;
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 D'oh! Of course.
1189 format.fmt.pix_mp.plane_fmt[2].sizeimage =
1190 input_allocated_size_.GetArea() / 4;
1191 format.fmt.pix_mp.plane_fmt[2].bytesperline =
1192 input_allocated_size_.width() / 2;
1193 format.fmt.pix_mp.num_planes = 3;
1194 break;
1195 default:
1196 NOTREACHED();
1197 NOTIFY_ERROR(kIllegalStateError);
1198 return false;
1199 }
1200 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format);
1201
1202 struct v4l2_crop crop;
1203 memset(&crop, 0, sizeof(crop));
1204 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1205 crop.c.left = 0;
1206 crop.c.top = 0;
1207 crop.c.width = input_visible_size_.width();
1208 crop.c.height = input_visible_size_.height();
1209 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop);
1210
1211 struct v4l2_requestbuffers reqbufs;
1212 memset(&reqbufs, 0, sizeof(reqbufs));
1213 reqbufs.count = kGscInputBufferCount;
1214 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1215 reqbufs.memory = V4L2_MEMORY_USERPTR;
1216 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1217
1218 DCHECK(gsc_input_buffer_map_.empty());
1219 gsc_input_buffer_map_.resize(reqbufs.count);
1220 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i)
1221 gsc_free_input_buffers_.push_back(i);
1222
1223 return true;
1224 }
1225
1226 bool ExynosVideoEncodeAccelerator::CreateGscOutputBuffers() {
1227 DVLOG(3) << "CreateGscOutputBuffers()";
1228 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1229 DCHECK_EQ(encoder_state_, kUninitialized);
1230 DCHECK(!gsc_output_streamon_);
1231
1232 struct v4l2_format format;
1233 memset(&format, 0, sizeof(format));
1234 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1235 format.fmt.pix_mp.width = converted_allocated_size_.width();
1236 format.fmt.pix_mp.height = converted_allocated_size_.height();
1237 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
1238 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1239 converted_allocated_size_.GetArea();
1240 format.fmt.pix_mp.plane_fmt[1].sizeimage =
1241 converted_allocated_size_.GetArea() / 2;
1242 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1243 converted_allocated_size_.width();
1244 format.fmt.pix_mp.plane_fmt[1].bytesperline =
1245 converted_allocated_size_.width();
1246 format.fmt.pix_mp.num_planes = 2;
1247 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format);
1248
1249 struct v4l2_crop crop;
1250 memset(&crop, 0, sizeof(crop));
1251 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1252 crop.c.left = 0;
1253 crop.c.top = 0;
1254 crop.c.width = converted_visible_size_.width();
1255 crop.c.height = converted_visible_size_.height();
1256 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop);
1257
1258 struct v4l2_requestbuffers reqbufs;
1259 memset(&reqbufs, 0, sizeof(reqbufs));
1260 reqbufs.count = kGscOutputBufferCount;
1261 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1262 reqbufs.memory = V4L2_MEMORY_DMABUF;
1263 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1264
1265 DCHECK(gsc_output_buffer_map_.empty());
1266 gsc_output_buffer_map_.resize(reqbufs.count);
1267 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i)
1268 gsc_free_output_buffers_.push_back(i);
1269 return true;
1270 }
1271
1272 bool ExynosVideoEncodeAccelerator::SetMfcFormats() {
1273 DVLOG(3) << "SetMfcFormats()";
1274 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1275 DCHECK(!mfc_input_streamon_);
1276 DCHECK(!mfc_output_streamon_);
1277
1278 // VIDIOC_S_FMT on OUTPUT queue.
1279 struct v4l2_format format;
1280 memset(&format, 0, sizeof(format));
1281 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1282 format.fmt.pix_mp.width = input_allocated_size_.width();
1283 format.fmt.pix_mp.height = input_allocated_size_.height();
1284 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
1285 format.fmt.pix_mp.num_planes = 2;
1286 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format);
1287 // We read direct from GSC, so we rely on the HW not changing our set
1288 // size/stride.
1289 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage,
1290 static_cast<__u32>(input_allocated_size_.GetArea()));
1291 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].bytesperline,
1292 static_cast<__u32>(input_allocated_size_.width()));
1293 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].sizeimage,
1294 static_cast<__u32>(input_allocated_size_.GetArea() / 2));
1295 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].bytesperline,
1296 static_cast<__u32>(input_allocated_size_.width()));
1297
1298 struct v4l2_crop crop;
1299 memset(&crop, 0, sizeof(crop));
1300 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
1301 crop.c.left = 0;
1302 crop.c.top = 0;
1303 crop.c.width = input_visible_size_.width();
1304 crop.c.height = input_visible_size_.height();
1305 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_CROP, &crop);
1306
1307 // VIDIOC_S_FMT on CAPTURE queue.
1308 output_buffer_byte_size_ = kMfcOutputBufferSize;
1309 memset(&format, 0, sizeof(format));
1310 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1311 format.fmt.pix_mp.width = output_visible_size_.width();
1312 format.fmt.pix_mp.height = output_visible_size_.height();
1313 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
1314 format.fmt.pix_mp.plane_fmt[0].sizeimage = output_buffer_byte_size_;
1315 format.fmt.pix_mp.num_planes = 1;
1316 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format);
1317
1318 struct v4l2_ext_control ctrls[6];
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 You might enjoy a form more like: struct v4l2_ex
sheu 2013/08/14 01:00:42 There are more members to v4l2_ext_control than ju
1319 struct v4l2_ext_controls control;
1320 memset(&ctrls, 0, sizeof(ctrls));
1321 memset(&control, 0, sizeof(control));
Ami GONE FROM CHROMIUM 2013/08/13 18:13:19 Please comment of the magical values that they are
sheu 2013/08/14 01:00:42 Done.
1322 // No B-frames, for lowest decoding latency.
1323 ctrls[0].id = V4L2_CID_MPEG_VIDEO_B_FRAMES;
1324 ctrls[0].value = 0;
1325 // Enable and configure dynamic bitrate control.
1326 ctrls[1].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
1327 ctrls[1].value = 1;
1328 ctrls[2].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF;
1329 ctrls[2].value = 10;
1330 ctrls[3].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT;
1331 ctrls[3].value = 1;
1332 ctrls[4].id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP;
1333 ctrls[4].value = 51;
1334 // Separate stream header so we can cache it and insert into the stream.
1335 ctrls[5].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
1336 ctrls[5].value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE;
1337 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
1338 control.count = arraysize(ctrls);
1339 control.controls = ctrls;
1340 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
1341
1342 return true;
1343 }
1344
1345 bool ExynosVideoEncodeAccelerator::CreateMfcInputBuffers() {
1346 DVLOG(3) << "CreateMfcInputBuffers()";
1347 // This function runs on encoder_thread_ after output buffers have been
1348 // provided by the client.
1349 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
1350 DCHECK(!mfc_input_streamon_);
1351
1352 struct v4l2_requestbuffers reqbufs;
1353 memset(&reqbufs, 0, sizeof(reqbufs));
1354 reqbufs.count = 1; // Driver will allocate the appropriate number of buffers.
1355 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1356 reqbufs.memory = V4L2_MEMORY_MMAP;
1357 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1358
1359 DCHECK(mfc_input_buffer_map_.empty());
1360 mfc_input_buffer_map_.resize(reqbufs.count);
1361 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) {
1362 MfcInputRecord& input_record = mfc_input_buffer_map_[i];
1363 for (int j = 0; j < 2; ++j) {
1364 // Export the DMABUF fd so GSC can write to it.
1365 struct v4l2_exportbuffer expbuf;
1366 memset(&expbuf, 0, sizeof(expbuf));
1367 expbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1368 expbuf.index = i;
1369 expbuf.plane = j;
1370 expbuf.flags = O_CLOEXEC;
1371 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_EXPBUF, &expbuf);
1372 input_record.fd[j] = expbuf.fd;
1373 }
1374 mfc_free_input_buffers_.push_back(i);
1375 }
1376
1377 return true;
1378 }
1379
1380 bool ExynosVideoEncodeAccelerator::CreateMfcOutputBuffers() {
1381 DVLOG(3) << "CreateMfcOutputBuffers()";
1382 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1383 DCHECK(!mfc_output_streamon_);
1384
1385 struct v4l2_requestbuffers reqbufs;
1386 memset(&reqbufs, 0, sizeof(reqbufs));
1387 reqbufs.count = kMfcOutputBufferCount;
1388 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1389 reqbufs.memory = V4L2_MEMORY_USERPTR;
1390 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1391
1392 DCHECK(mfc_output_buffer_map_.empty());
1393 mfc_output_buffer_map_.resize(reqbufs.count);
1394 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i)
1395 mfc_free_output_buffers_.push_back(i);
1396
1397 return true;
1398 }
1399
1400 void ExynosVideoEncodeAccelerator::DestroyGscInputBuffers() {
1401 DVLOG(3) << "DestroyGscInputBuffers()";
1402 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1403 DCHECK(!gsc_input_streamon_);
1404
1405 struct v4l2_requestbuffers reqbufs;
1406 memset(&reqbufs, 0, sizeof(reqbufs));
1407 reqbufs.count = 0;
1408 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1409 reqbufs.memory = V4L2_MEMORY_USERPTR;
1410 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1411 DPLOG(ERROR) << "DestroyGscInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1412
1413 gsc_input_buffer_map_.clear();
1414 gsc_free_input_buffers_.clear();
1415 }
1416
1417 void ExynosVideoEncodeAccelerator::DestroyGscOutputBuffers() {
1418 DVLOG(3) << "DestroyGscOutputBuffers()";
1419 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1420 DCHECK(!gsc_output_streamon_);
1421
1422 struct v4l2_requestbuffers reqbufs;
1423 memset(&reqbufs, 0, sizeof(reqbufs));
1424 reqbufs.count = 0;
1425 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1426 reqbufs.memory = V4L2_MEMORY_DMABUF;
1427 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1428 DPLOG(ERROR) << "DestroyGscOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1429
1430 gsc_output_buffer_map_.clear();
1431 gsc_free_output_buffers_.clear();
1432 }
1433
1434 void ExynosVideoEncodeAccelerator::DestroyMfcInputBuffers() {
1435 DVLOG(3) << "DestroyMfcInputBuffers()";
1436 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1437 DCHECK(!mfc_input_streamon_);
1438
1439 struct v4l2_requestbuffers reqbufs;
1440 memset(&reqbufs, 0, sizeof(reqbufs));
1441 reqbufs.count = 0;
1442 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1443 reqbufs.memory = V4L2_MEMORY_MMAP;
1444 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1445 DPLOG(ERROR) << "DestroyMfcInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1446
1447 mfc_input_buffer_map_.clear();
1448 mfc_free_input_buffers_.clear();
1449 }
1450
1451 void ExynosVideoEncodeAccelerator::DestroyMfcOutputBuffers() {
1452 DVLOG(3) << "DestroyMfcOutputBuffers()";
1453 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1454 DCHECK(!mfc_output_streamon_);
1455
1456 struct v4l2_requestbuffers reqbufs;
1457 memset(&reqbufs, 0, sizeof(reqbufs));
1458 reqbufs.count = 0;
1459 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1460 reqbufs.memory = V4L2_MEMORY_USERPTR;
1461 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1462 DPLOG(ERROR) << "DestroyMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1463
1464 mfc_output_buffer_map_.clear();
1465 mfc_free_output_buffers_.clear();
1466 }
1467
1468 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698