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

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: 8b1dcfaa Using RequestEncodingParametersChange() now for bitrate in Initialize(). 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 DVLOG(2) << "Initialize(): opening MFC device: " << kExynosMfcDevice;
201 mfc_fd_ =
202 HANDLE_EINTR(open(kExynosMfcDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC));
203 if (mfc_fd_ == -1) {
204 DPLOG(ERROR) << "Initialize(): could not open MFC device: "
205 << kExynosMfcDevice;
206 NOTIFY_ERROR(kPlatformFailureError);
207 return;
208 }
209
210 memset(&caps, 0, sizeof(caps));
211 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_QUERYCAP, &caps);
212 if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
213 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: "
214 "caps check failed: 0x" << std::hex << caps.capabilities;
215 NOTIFY_ERROR(kPlatformFailureError);
216 return;
217 }
218
219 // Create the interrupt fd.
220 DCHECK_EQ(device_poll_interrupt_fd_, -1);
221 device_poll_interrupt_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
222 if (device_poll_interrupt_fd_ == -1) {
223 DPLOG(ERROR) << "Initialize(): eventfd() failed";
224 NOTIFY_ERROR(kPlatformFailureError);
225 return;
226 }
227
228 DVLOG(3)
229 << "Initialize(): input_visible_size_=" << input_visible_size_.ToString()
230 << ", input_allocated_size_=" << input_allocated_size_.ToString()
231 << ", converted_visible_size_=" << converted_visible_size_.ToString()
232 << ", converted_allocated_size_=" << converted_allocated_size_.ToString()
233 << ", output_visible_size_=" << output_visible_size_.ToString();
234
235 if (!CreateGscInputBuffers() || !CreateGscOutputBuffers())
236 return;
237
238 // MFC setup for encoding is rather particular in ordering:
239 //
240 // 1. Format (VIDIOC_S_FMT) set first on OUTPUT and CAPTURE queues.
241 // 2. VIDIOC_REQBUFS, VIDIOC_QBUF, and VIDIOC_STREAMON on CAPTURE queue.
242 // 3. VIDIOC_REQBUFS (and later VIDIOC_QBUF and VIDIOC_STREAMON) on OUTPUT
243 // queue.
244 //
245 // Unfortunately, we cannot do (3) in Initialize() here since we have no
246 // buffers to QBUF in step (2) until the client has provided output buffers
247 // through UseOutputBitstreamBuffer(). So, we just do (1), and the
248 // VIDIOC_REQBUFS part of (2) here. The rest is done the first time we get
249 // a UseOutputBitstreamBuffer() callback.
250
251 if (!SetMfcFormats())
252 return;
253
254 // VIDIOC_REQBUFS on CAPTURE queue.
255 if (!CreateMfcOutputBuffers())
256 return;
257
258
259 if (!encoder_thread_.Start()) {
260 DLOG(ERROR) << "Initialize(): encoder thread failed to start";
261 NOTIFY_ERROR(kPlatformFailureError);
262 return;
263 }
264
265 RequestEncodingParametersChange(initial_bitrate, kInitialFramerate);
266
267 SetEncoderState(kInitialized);
268
269 child_message_loop_proxy_->PostTask(
270 FROM_HERE, base::Bind(&Client::NotifyInitializeDone, client_));
271
272 child_message_loop_proxy_->PostTask(
273 FROM_HERE,
274 base::Bind(&Client::RequireBitstreamBuffers,
275 client_,
276 gsc_input_buffer_map_.size(),
277 input_allocated_size_,
278 output_buffer_byte_size_));
279 }
280
281 void ExynosVideoEncodeAccelerator::Encode(
282 const scoped_refptr<media::VideoFrame>& frame,
283 bool force_keyframe) {
284 DVLOG(3) << "Encode(): force_keyframe=" << force_keyframe;
285 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
286
287 encoder_thread_.message_loop()->PostTask(
288 FROM_HERE,
289 base::Bind(&ExynosVideoEncodeAccelerator::EncodeTask,
290 base::Unretained(this),
291 frame,
292 force_keyframe));
293 }
294
295 void ExynosVideoEncodeAccelerator::UseOutputBitstreamBuffer(
296 const media::BitstreamBuffer& buffer) {
297 DVLOG(3) << "UseOutputBitstreamBuffer(): id=" << buffer.id();
298 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
299
300 if (buffer.size() < output_buffer_byte_size_) {
301 NOTIFY_ERROR(kInvalidArgumentError);
302 return;
303 }
304
305 scoped_ptr<base::SharedMemory> shm(
306 new base::SharedMemory(buffer.handle(), false));
307 if (!shm->Map(buffer.size())) {
308 NOTIFY_ERROR(kPlatformFailureError);
309 return;
310 }
311
312 scoped_ptr<BitstreamBufferRef> buffer_ref(
313 new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
314 encoder_thread_.message_loop()->PostTask(
315 FROM_HERE,
316 base::Bind(&ExynosVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
317 base::Unretained(this),
318 base::Passed(&buffer_ref)));
319 }
320
321 void ExynosVideoEncodeAccelerator::RequestEncodingParametersChange(
322 uint32 bitrate,
323 uint32 framerate) {
324 DVLOG(3) << "RequestEncodingParametersChange(): bitrate=" << bitrate
325 << ", framerate=" << framerate;
326 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
327
328 encoder_thread_.message_loop()->PostTask(
329 FROM_HERE,
330 base::Bind(
331 &ExynosVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
332 base::Unretained(this),
333 bitrate,
334 framerate));
335 }
336
337 void ExynosVideoEncodeAccelerator::Destroy() {
338 DVLOG(3) << "Destroy()";
339 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
340
341 // We're destroying; cancel all callbacks.
342 client_ptr_factory_.InvalidateWeakPtrs();
343
344 // If the encoder thread is running, destroy using posted task.
345 if (encoder_thread_.IsRunning()) {
346 encoder_thread_.message_loop()->PostTask(
347 FROM_HERE,
348 base::Bind(&ExynosVideoEncodeAccelerator::DestroyTask,
349 base::Unretained(this)));
350 // DestroyTask() will put the encoder into kError state and cause all tasks
351 // to no-op.
352 encoder_thread_.Stop();
353 } else {
354 // Otherwise, call the destroy task directly.
355 DestroyTask();
356 }
357
358 // Set to kError state just in case.
359 SetEncoderState(kError);
360
361 delete this;
362 }
363
364 // static
365 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
366 ExynosVideoEncodeAccelerator::GetSupportedProfiles() {
367 std::vector<SupportedProfile> profiles(1);
368 SupportedProfile& profile = profiles[0];
369 profile.profile = media::H264PROFILE_MAIN;
370 profile.max_resolution.SetSize(1920, 1088);
371 profile.max_framerate.numerator = 30;
372 profile.max_framerate.denominator = 1;
373 return profiles;
374 }
375
376 void ExynosVideoEncodeAccelerator::EncodeTask(
377 const scoped_refptr<media::VideoFrame>& frame, bool force_keyframe) {
378 DVLOG(3) << "EncodeTask(): force_keyframe=" << force_keyframe;
379 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
380 DCHECK_NE(encoder_state_, kUninitialized);
381
382 if (encoder_state_ == kError) {
383 DVLOG(2) << "EncodeTask(): early out: kError state";
384 return;
385 }
386
387 encoder_input_queue_.push_back(frame);
388 EnqueueGsc();
389
390 if (force_keyframe) {
391 // TODO(sheu): this presently makes for slightly imprecise encoding
392 // parameters updates. To precisely align the parameter updates with the
393 // incoming input frame, we should track the parameters through the GSC
394 // pipeline and only apply them when the MFC input is about to be queued.
395 struct v4l2_ext_control ctrls[1];
396 struct v4l2_ext_controls control;
397 memset(&ctrls, 0, sizeof(ctrls));
398 memset(&control, 0, sizeof(control));
399 ctrls[0].id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE;
400 ctrls[0].value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME;
401 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
402 control.count = 1;
403 control.controls = ctrls;
404 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
405 }
406 }
407
408 void ExynosVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
409 scoped_ptr<BitstreamBufferRef> buffer_ref) {
410 DVLOG(3) << "UseOutputBitstreamBufferTask(): id=" << buffer_ref->id;
411 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
412
413 encoder_output_queue_.push_back(
414 linked_ptr<BitstreamBufferRef>(buffer_ref.release()));
415 EnqueueMfc();
416
417 if (encoder_state_ == kInitialized) {
418 // Finish setting up our MFC OUTPUT queue. See: Initialize().
419 // VIDIOC_REQBUFS on OUTPUT queue.
420 if (!CreateMfcInputBuffers())
421 return;
422 if (!StartDevicePoll())
423 return;
424 encoder_state_ = kEncoding;
425 }
426 }
427
428 void ExynosVideoEncodeAccelerator::DestroyTask() {
429 DVLOG(3) << "DestroyTask()";
430
431 // DestroyTask() should run regardless of encoder_state_.
432
433 // Stop streaming and the device_poll_thread_.
434 StopDevicePoll();
435
436 // Set our state to kError, and early-out all tasks.
437 encoder_state_ = kError;
438 }
439
440 void ExynosVideoEncodeAccelerator::ServiceDeviceTask() {
441 DVLOG(3) << "ServiceDeviceTask()";
442 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
443 DCHECK_NE(encoder_state_, kUninitialized);
444 DCHECK_NE(encoder_state_, kInitialized);
445
446 if (encoder_state_ == kError) {
447 DVLOG(2) << "ServiceDeviceTask(): early out: kError state";
448 return;
449 }
450
451 DequeueGsc();
452 DequeueMfc();
453 EnqueueGsc();
454 EnqueueMfc();
455
456 // Clear the interrupt fd.
457 if (!ClearDevicePollInterrupt())
458 return;
459
460 unsigned int poll_fds = 0;
461 // Add GSC fd, if we should poll on it.
462 // GSC has to wait until both input and output buffers are queued.
463 if (gsc_input_buffer_queued_count_ > 0 && gsc_output_buffer_queued_count_ > 0)
464 poll_fds |= kPollGsc;
465 // Add MFC fd, if we should poll on it.
466 // MFC can be polled as soon as either input or output buffers are queued.
467 if (mfc_input_buffer_queued_count_ + mfc_output_buffer_queued_count_ > 0)
468 poll_fds |= kPollMfc;
469
470 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(),
471 // so either:
472 // * device_poll_thread_ is running normally
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.
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_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
1116
1117 if (bitrate < 1)
1118 bitrate = 1;
1119 if (framerate < 1)
1120 framerate = 1;
1121
1122 struct v4l2_ext_control ctrls[1];
1123 struct v4l2_ext_controls control;
1124 memset(&ctrls, 0, sizeof(ctrls));
1125 memset(&control, 0, sizeof(control));
1126 ctrls[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
1127 ctrls[0].value = bitrate;
1128 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
1129 control.count = arraysize(ctrls);
1130 control.controls = ctrls;
1131 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
1132
1133 struct v4l2_streamparm parms;
1134 memset(&parms, 0, sizeof(parms));
1135 parms.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1136 // Note that we are provided "frames per second" but V4L2 expects "time per
1137 // frame"; hence we provide the reciprocal of the framerate here.
1138 parms.parm.output.timeperframe.numerator = 1;
1139 parms.parm.output.timeperframe.denominator = framerate;
1140 IOCTL_OR_ERROR_RETURN(mfc_fd_, VIDIOC_S_PARM, &parms);
1141 }
1142
1143 bool ExynosVideoEncodeAccelerator::CreateGscInputBuffers() {
1144 DVLOG(3) << "CreateGscInputBuffers()";
1145 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1146 DCHECK_EQ(encoder_state_, kUninitialized);
1147 DCHECK(!gsc_input_streamon_);
1148
1149 struct v4l2_control control;
1150 memset(&control, 0, sizeof(control));
1151 control.id = V4L2_CID_ROTATE;
1152 control.value = 0;
1153 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1154
1155 // HFLIP actually seems to control vertical mirroring for GSC, and vice-versa.
1156 memset(&control, 0, sizeof(control));
1157 control.id = V4L2_CID_HFLIP;
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_VFLIP;
1163 control.value = 0;
1164 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1165
1166 memset(&control, 0, sizeof(control));
1167 control.id = V4L2_CID_GLOBAL_ALPHA;
1168 control.value = 255;
1169 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CTRL, &control);
1170
1171 struct v4l2_format format;
1172 memset(&format, 0, sizeof(format));
1173 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1174 format.fmt.pix_mp.width = input_allocated_size_.width();
1175 format.fmt.pix_mp.height = input_allocated_size_.height();
1176 format.fmt.pix_mp.pixelformat = input_format_fourcc_;
1177 switch (input_format_fourcc_) {
1178 case V4L2_PIX_FMT_RGB32:
1179 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1180 input_allocated_size_.GetArea() * 4;
1181 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1182 input_allocated_size_.width() * 4;
1183 format.fmt.pix_mp.num_planes = 1;
1184 break;
1185 case V4L2_PIX_FMT_YUV420M:
1186 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1187 input_allocated_size_.GetArea();
1188 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1189 input_allocated_size_.width();
1190 format.fmt.pix_mp.plane_fmt[1].sizeimage =
1191 input_allocated_size_.GetArea() / 4;
1192 format.fmt.pix_mp.plane_fmt[1].bytesperline =
1193 input_allocated_size_.width() / 2;
1194 format.fmt.pix_mp.plane_fmt[2].sizeimage =
1195 input_allocated_size_.GetArea() / 4;
1196 format.fmt.pix_mp.plane_fmt[2].bytesperline =
1197 input_allocated_size_.width() / 2;
1198 format.fmt.pix_mp.num_planes = 3;
1199 break;
1200 default:
1201 NOTREACHED();
1202 NOTIFY_ERROR(kIllegalStateError);
1203 return false;
1204 }
1205 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format);
1206
1207 struct v4l2_crop crop;
1208 memset(&crop, 0, sizeof(crop));
1209 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1210 crop.c.left = 0;
1211 crop.c.top = 0;
1212 crop.c.width = input_visible_size_.width();
1213 crop.c.height = input_visible_size_.height();
1214 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop);
1215
1216 struct v4l2_requestbuffers reqbufs;
1217 memset(&reqbufs, 0, sizeof(reqbufs));
1218 reqbufs.count = kGscInputBufferCount;
1219 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1220 reqbufs.memory = V4L2_MEMORY_USERPTR;
1221 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1222
1223 DCHECK(gsc_input_buffer_map_.empty());
1224 gsc_input_buffer_map_.resize(reqbufs.count);
1225 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i)
1226 gsc_free_input_buffers_.push_back(i);
1227
1228 return true;
1229 }
1230
1231 bool ExynosVideoEncodeAccelerator::CreateGscOutputBuffers() {
1232 DVLOG(3) << "CreateGscOutputBuffers()";
1233 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1234 DCHECK_EQ(encoder_state_, kUninitialized);
1235 DCHECK(!gsc_output_streamon_);
1236
1237 struct v4l2_format format;
1238 memset(&format, 0, sizeof(format));
1239 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1240 format.fmt.pix_mp.width = converted_allocated_size_.width();
1241 format.fmt.pix_mp.height = converted_allocated_size_.height();
1242 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
1243 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1244 converted_allocated_size_.GetArea();
1245 format.fmt.pix_mp.plane_fmt[1].sizeimage =
1246 converted_allocated_size_.GetArea() / 2;
1247 format.fmt.pix_mp.plane_fmt[0].bytesperline =
1248 converted_allocated_size_.width();
1249 format.fmt.pix_mp.plane_fmt[1].bytesperline =
1250 converted_allocated_size_.width();
1251 format.fmt.pix_mp.num_planes = 2;
1252 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_FMT, &format);
1253
1254 struct v4l2_crop crop;
1255 memset(&crop, 0, sizeof(crop));
1256 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1257 crop.c.left = 0;
1258 crop.c.top = 0;
1259 crop.c.width = converted_visible_size_.width();
1260 crop.c.height = converted_visible_size_.height();
1261 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_S_CROP, &crop);
1262
1263 struct v4l2_requestbuffers reqbufs;
1264 memset(&reqbufs, 0, sizeof(reqbufs));
1265 reqbufs.count = kGscOutputBufferCount;
1266 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1267 reqbufs.memory = V4L2_MEMORY_DMABUF;
1268 IOCTL_OR_ERROR_RETURN_FALSE(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1269
1270 DCHECK(gsc_output_buffer_map_.empty());
1271 gsc_output_buffer_map_.resize(reqbufs.count);
1272 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i)
1273 gsc_free_output_buffers_.push_back(i);
1274 return true;
1275 }
1276
1277 bool ExynosVideoEncodeAccelerator::SetMfcFormats() {
1278 DVLOG(3) << "SetMfcFormats()";
1279 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1280 DCHECK(!mfc_input_streamon_);
1281 DCHECK(!mfc_output_streamon_);
1282
1283 // VIDIOC_S_FMT on OUTPUT queue.
1284 struct v4l2_format format;
1285 memset(&format, 0, sizeof(format));
1286 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1287 format.fmt.pix_mp.width = input_allocated_size_.width();
1288 format.fmt.pix_mp.height = input_allocated_size_.height();
1289 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
1290 format.fmt.pix_mp.num_planes = 2;
1291 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format);
1292 // We read direct from GSC, so we rely on the HW not changing our set
1293 // size/stride.
1294 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].sizeimage,
1295 static_cast<__u32>(input_allocated_size_.GetArea()));
1296 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[0].bytesperline,
1297 static_cast<__u32>(input_allocated_size_.width()));
1298 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].sizeimage,
1299 static_cast<__u32>(input_allocated_size_.GetArea() / 2));
1300 DCHECK_EQ(format.fmt.pix_mp.plane_fmt[1].bytesperline,
1301 static_cast<__u32>(input_allocated_size_.width()));
1302
1303 struct v4l2_crop crop;
1304 memset(&crop, 0, sizeof(crop));
1305 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
1306 crop.c.left = 0;
1307 crop.c.top = 0;
1308 crop.c.width = input_visible_size_.width();
1309 crop.c.height = input_visible_size_.height();
1310 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_CROP, &crop);
1311
1312 // VIDIOC_S_FMT on CAPTURE queue.
1313 output_buffer_byte_size_ = kMfcOutputBufferSize;
1314 memset(&format, 0, sizeof(format));
1315 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1316 format.fmt.pix_mp.width = output_visible_size_.width();
1317 format.fmt.pix_mp.height = output_visible_size_.height();
1318 format.fmt.pix_mp.pixelformat = output_format_fourcc_;
1319 format.fmt.pix_mp.plane_fmt[0].sizeimage = output_buffer_byte_size_;
1320 format.fmt.pix_mp.num_planes = 1;
1321 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_FMT, &format);
1322
1323 struct v4l2_ext_control ctrls[6];
1324 struct v4l2_ext_controls control;
1325 memset(&ctrls, 0, sizeof(ctrls));
1326 memset(&control, 0, sizeof(control));
1327 // No B-frames, for lowest decoding latency.
1328 ctrls[0].id = V4L2_CID_MPEG_VIDEO_B_FRAMES;
1329 ctrls[0].value = 0;
1330 // Enable variable bitrate control.
1331 ctrls[1].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
1332 ctrls[1].value = 1;
1333 // Enable "loose" variable bitrate.
1334 ctrls[2].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF;
1335 ctrls[2].value = 10;
1336 // Force bitrate control to average over a GOP (for tight bitrate tolerance).
1337 ctrls[3].id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT;
1338 ctrls[3].value = 1;
1339 // Quantization parameter maximum value (for variable bitrate control).
1340 ctrls[4].id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP;
1341 ctrls[4].value = 51;
1342 // Separate stream header so we can cache it and insert into the stream.
1343 ctrls[5].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
1344 ctrls[5].value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE;
1345 control.ctrl_class = V4L2_CTRL_CLASS_MPEG;
1346 control.count = arraysize(ctrls);
1347 control.controls = ctrls;
1348 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_S_EXT_CTRLS, &control);
1349
1350 return true;
1351 }
1352
1353 bool ExynosVideoEncodeAccelerator::CreateMfcInputBuffers() {
1354 DVLOG(3) << "CreateMfcInputBuffers()";
1355 // This function runs on encoder_thread_ after output buffers have been
1356 // provided by the client.
1357 DCHECK_EQ(encoder_thread_.message_loop(), base::MessageLoop::current());
1358 DCHECK(!mfc_input_streamon_);
1359
1360 struct v4l2_requestbuffers reqbufs;
1361 memset(&reqbufs, 0, sizeof(reqbufs));
1362 reqbufs.count = 1; // Driver will allocate the appropriate number of buffers.
1363 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1364 reqbufs.memory = V4L2_MEMORY_MMAP;
1365 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1366
1367 DCHECK(mfc_input_buffer_map_.empty());
1368 mfc_input_buffer_map_.resize(reqbufs.count);
1369 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) {
1370 MfcInputRecord& input_record = mfc_input_buffer_map_[i];
1371 for (int j = 0; j < 2; ++j) {
1372 // Export the DMABUF fd so GSC can write to it.
1373 struct v4l2_exportbuffer expbuf;
1374 memset(&expbuf, 0, sizeof(expbuf));
1375 expbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1376 expbuf.index = i;
1377 expbuf.plane = j;
1378 expbuf.flags = O_CLOEXEC;
1379 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_EXPBUF, &expbuf);
1380 input_record.fd[j] = expbuf.fd;
1381 }
1382 mfc_free_input_buffers_.push_back(i);
1383 }
1384
1385 return true;
1386 }
1387
1388 bool ExynosVideoEncodeAccelerator::CreateMfcOutputBuffers() {
1389 DVLOG(3) << "CreateMfcOutputBuffers()";
1390 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1391 DCHECK(!mfc_output_streamon_);
1392
1393 struct v4l2_requestbuffers reqbufs;
1394 memset(&reqbufs, 0, sizeof(reqbufs));
1395 reqbufs.count = kMfcOutputBufferCount;
1396 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1397 reqbufs.memory = V4L2_MEMORY_USERPTR;
1398 IOCTL_OR_ERROR_RETURN_FALSE(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1399
1400 DCHECK(mfc_output_buffer_map_.empty());
1401 mfc_output_buffer_map_.resize(reqbufs.count);
1402 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i)
1403 mfc_free_output_buffers_.push_back(i);
1404
1405 return true;
1406 }
1407
1408 void ExynosVideoEncodeAccelerator::DestroyGscInputBuffers() {
1409 DVLOG(3) << "DestroyGscInputBuffers()";
1410 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1411 DCHECK(!gsc_input_streamon_);
1412
1413 struct v4l2_requestbuffers reqbufs;
1414 memset(&reqbufs, 0, sizeof(reqbufs));
1415 reqbufs.count = 0;
1416 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1417 reqbufs.memory = V4L2_MEMORY_USERPTR;
1418 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1419 DPLOG(ERROR) << "DestroyGscInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1420
1421 gsc_input_buffer_map_.clear();
1422 gsc_free_input_buffers_.clear();
1423 }
1424
1425 void ExynosVideoEncodeAccelerator::DestroyGscOutputBuffers() {
1426 DVLOG(3) << "DestroyGscOutputBuffers()";
1427 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1428 DCHECK(!gsc_output_streamon_);
1429
1430 struct v4l2_requestbuffers reqbufs;
1431 memset(&reqbufs, 0, sizeof(reqbufs));
1432 reqbufs.count = 0;
1433 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1434 reqbufs.memory = V4L2_MEMORY_DMABUF;
1435 if (HANDLE_EINTR(ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1436 DPLOG(ERROR) << "DestroyGscOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1437
1438 gsc_output_buffer_map_.clear();
1439 gsc_free_output_buffers_.clear();
1440 }
1441
1442 void ExynosVideoEncodeAccelerator::DestroyMfcInputBuffers() {
1443 DVLOG(3) << "DestroyMfcInputBuffers()";
1444 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1445 DCHECK(!mfc_input_streamon_);
1446
1447 struct v4l2_requestbuffers reqbufs;
1448 memset(&reqbufs, 0, sizeof(reqbufs));
1449 reqbufs.count = 0;
1450 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1451 reqbufs.memory = V4L2_MEMORY_MMAP;
1452 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1453 DPLOG(ERROR) << "DestroyMfcInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1454
1455 mfc_input_buffer_map_.clear();
1456 mfc_free_input_buffers_.clear();
1457 }
1458
1459 void ExynosVideoEncodeAccelerator::DestroyMfcOutputBuffers() {
1460 DVLOG(3) << "DestroyMfcOutputBuffers()";
1461 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1462 DCHECK(!mfc_output_streamon_);
1463
1464 struct v4l2_requestbuffers reqbufs;
1465 memset(&reqbufs, 0, sizeof(reqbufs));
1466 reqbufs.count = 0;
1467 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1468 reqbufs.memory = V4L2_MEMORY_USERPTR;
1469 if (HANDLE_EINTR(ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs)) != 0)
1470 DPLOG(ERROR) << "DestroyMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1471
1472 mfc_output_buffer_map_.clear();
1473 mfc_free_output_buffers_.clear();
1474 }
1475
1476 } // namespace content
OLDNEW
« no previous file with comments | « content/common/gpu/media/exynos_video_encode_accelerator.h ('k') | content/common/gpu/media/gpu_video_encode_accelerator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698