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

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

Issue 11198060: VDA implementation for Exynos, using V4L2 (Closed) Base URL: https://git.chromium.org/git/chromium/src@git-svn
Patch Set: Final?? Created 8 years, 1 month 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) 2012 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 <dlfcn.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <linux/videodev2.h>
9 #include <sys/epoll.h>
10 #include <sys/ioctl.h>
11 #include <sys/mman.h>
12
13 #include "base/bind.h"
14 #include "base/message_loop.h"
15 #include "base/message_loop_proxy.h"
16 #include "base/shared_memory.h"
17 #include "content/common/gpu/media/exynos_video_decode_accelerator.h"
18 #include "ui/gl/gl_bindings.h"
19 #include "ui/gl/gl_context.h"
20 #include "ui/gl/gl_context_egl.h"
21 #include "ui/gl/gl_surface_egl.h"
22
23 namespace content {
24
25 #define NOTIFY_ERROR(x) \
26 do { \
27 SetDecoderState(kError); \
28 LOG(ERROR) << "calling NotifyError(): " << x; \
29 NotifyError(x); \
30 } while (0)
31
32 #define EXYNOS_MFC_DEVICE "/dev/mfc-dec"
33 #define EXYNOS_GSC_DEVICE "/dev/gsc1"
Pawel Osciak 2012/11/06 19:26:17 We'd like to be using all gsc nodes available. Cou
sheu 2012/11/08 21:16:31 I did some tracing, and GSC time barely factors in
Pawel Osciak 2012/11/13 18:10:39 Hm, what resolution was that? What was the MFC tim
34 #define EXYNOS_MALI_DRIVER "libmali.so"
35
36 static void* libmali_handle = NULL;
37 static EGLBoolean(*mali_egl_image_get_buffer_ext_phandle)(
38 EGLImageKHR, EGLint*, void*) = NULL;
39 static EGLSyncKHR(*egl_create_sync_khr)(
40 EGLDisplay, EGLenum, const EGLint*) = NULL;
41 static EGLBoolean(*egl_destroy_sync_khr)(
42 EGLDisplay, EGLSyncKHR) = NULL;
43 static EGLint(*egl_client_wait_sync_khr)(
44 EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR) = NULL;
45
46 ExynosVideoDecodeAccelerator::BitstreamBufferRecord::BitstreamBufferRecord(
47 base::SharedMemory* shm, size_t size, int32 input_id)
48 : shm(shm),
49 size(size),
50 input_id(input_id) {
51 }
52
53 ExynosVideoDecodeAccelerator::BitstreamBufferRecord::~BitstreamBufferRecord() {
54 }
55
56 ExynosVideoDecodeAccelerator::MfcInputRecord::MfcInputRecord()
57 : at_device(false),
58 offset(NULL),
59 length(0),
60 bytes_used(0),
61 input_id(-1) {
62 }
63
64 ExynosVideoDecodeAccelerator::MfcInputRecord::~MfcInputRecord() {
65 }
66
67 ExynosVideoDecodeAccelerator::MfcOutputRecord::MfcOutputRecord()
68 : at_device(false),
69 input_id(-1) {
70 bytes_used[0] = 0;
71 bytes_used[1] = 0;
72 offset[0] = NULL;
73 offset[1] = NULL;
74 length[0] = 0;
75 length[1] = 0;
76 }
77
78 ExynosVideoDecodeAccelerator::MfcOutputRecord::~MfcOutputRecord() {
79 }
80
81 ExynosVideoDecodeAccelerator::GscInputRecord::GscInputRecord()
82 : at_device(false),
83 mfc_output(-1) {
84 }
85
86 ExynosVideoDecodeAccelerator::GscInputRecord::~GscInputRecord() {
87 }
88
89 ExynosVideoDecodeAccelerator::GscOutputRecord::GscOutputRecord()
90 : at_device(false),
91 at_client(false),
92 fd(-1),
93 egl_image(EGL_NO_IMAGE_KHR),
94 egl_sync(EGL_NO_SYNC_KHR),
95 picture_id(-1) {
96 }
97
98 ExynosVideoDecodeAccelerator::GscOutputRecord::~GscOutputRecord() {
99 }
100
101 ExynosVideoDecodeAccelerator::EGLImageKHRArrayRef::EGLImageKHRArrayRef(
102 EGLDisplay egl_display, EGLImageKHR egl_images[], int egl_image_fds[],
103 int egl_images_count)
104 : egl_display(egl_display),
105 egl_images(egl_images),
106 egl_image_fds(egl_image_fds),
107 egl_images_count(egl_images_count) {
108 }
109
110 ExynosVideoDecodeAccelerator::EGLImageKHRArrayRef::~EGLImageKHRArrayRef() {
111 DCHECK_EQ(egl_images != NULL, egl_image_fds != NULL);
112 if (egl_images == NULL)
113 return;
114
115 for (int i = 0; i < egl_images_count; ++i) {
116 if (egl_images[i] != EGL_NO_IMAGE_KHR)
117 eglDestroyImageKHR(egl_display, egl_images[i]);
118 if (egl_image_fds[i] != -1)
119 close(egl_image_fds[i]);
120 }
121 }
122
123 ExynosVideoDecodeAccelerator::EGLSyncKHRRef::EGLSyncKHRRef(
124 EGLDisplay egl_display, EGLSyncKHR egl_sync)
125 : egl_display(egl_display),
126 egl_sync(egl_sync) {
127 }
128
129 ExynosVideoDecodeAccelerator::EGLSyncKHRRef::~EGLSyncKHRRef() {
130 if (egl_sync != EGL_NO_SYNC_KHR)
131 egl_destroy_sync_khr(egl_display, egl_sync);
132 }
133
134 ExynosVideoDecodeAccelerator::ExynosVideoDecodeAccelerator(
135 gfx::GLContext* gl_context,
136 Client* client,
137 const base::Callback<bool(void)>& make_context_current)
138 : child_message_loop_proxy_(base::MessageLoopProxy::current()),
139 weak_this_(base::AsWeakPtr(this)),
140 client_ptr_factory_(client),
141 client_(client_ptr_factory_.GetWeakPtr()),
142 decoder_thread_("ExynosDecoderThread"),
143 decoder_state_(kUninitialized),
144 decoder_current_bitstream_buffer_(NULL),
145 decoder_current_input_buffer_(-1),
146 decoder_decode_buffer_tasks_scheduled_(0),
147 decoder_frames_inflight_(0),
148 decoder_frames_at_client_(0),
149 decoder_flush_notify_requested_(false),
150 mfc_fd_(-1),
151 mfc_fd_closer_(&mfc_fd_),
152 mfc_input_streamon_(false),
153 mfc_input_buffer_count_(0),
154 mfc_input_buffer_queued_count_(0),
155 mfc_output_streamon_(false),
156 mfc_output_buffer_count_(0),
157 mfc_output_buffer_pixelformat_(0),
158 gsc_fd_(-1),
159 gsc_fd_closer_(&gsc_fd_),
160 gsc_input_streamon_(false),
161 gsc_input_buffer_count_(0),
162 gsc_output_streamon_(false),
163 gsc_output_buffer_count_(0),
164 gsc_output_buffer_prepared_count_(0),
165 gsc_output_buffer_queued_count_(0),
166 frame_buffer_size_(0, 0),
167 device_poll_thread_("ExynosDevicePollThread"),
168 gl_context_(gl_context),
169 make_context_current_(make_context_current),
170 egl_context_(EGL_NO_CONTEXT),
171 egl_display_(EGL_NO_DISPLAY),
172 video_profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN) {
173 }
174
175 ExynosVideoDecodeAccelerator::~ExynosVideoDecodeAccelerator() {
176 // These maps have members that should be manually destroyed, e.g. file
177 // descriptors, mmap() segments, etc.
178 DCHECK(mfc_input_buffer_map_.empty());
179 DCHECK(mfc_output_buffer_map_.empty());
180 DCHECK(gsc_input_buffer_map_.empty());
181 DCHECK(gsc_output_buffer_map_.empty());
182 }
183
184 bool ExynosVideoDecodeAccelerator::Initialize(
185 media::VideoCodecProfile profile) {
186 DVLOG(3) << "Initialize()";
187 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
188 DCHECK_EQ(decoder_state_, kUninitialized);
189
190 switch (profile) {
191 case media::H264PROFILE_BASELINE:
192 DVLOG(2) << "Initialize(): profile H264PROFILE_BASELINE";
193 break;
194 case media::H264PROFILE_MAIN:
195 DVLOG(2) << "Initialize(): profile H264PROFILE_MAIN";
196 break;
197 case media::H264PROFILE_HIGH:
198 DVLOG(2) << "Initialize(): profile H264PROFILE_HIGH";
199 break;
200 case media::VP8PROFILE_MAIN:
201 DVLOG(2) << "Initialize(): profile VP8PROFILE_MAIN";
202 break;
203 default:
204 DLOG(ERROR) << "Initialize(): unsupported profile=" << profile;
205 return false;
206 };
207 video_profile_ = profile;
208
209 gfx::GLContextEGL* context_egl = static_cast<gfx::GLContextEGL*>(gl_context_);
210 static bool sandbox_initialized = PostSandboxInitialization();
211 if (!sandbox_initialized) {
212 DLOG(ERROR) << "Initialize(): PostSandboxInitialization() failed";
213 NOTIFY_ERROR(PLATFORM_FAILURE);
214 return false;
215 }
216
217 egl_context_ = reinterpret_cast<EGLContext>(context_egl->GetHandle());
218 if (egl_context_ == EGL_NO_CONTEXT) {
219 DLOG(ERROR) << "Initialize(): could not get EGLContext";
220 NOTIFY_ERROR(PLATFORM_FAILURE);
221 return false;
222 }
223 egl_display_ = gfx::GLSurfaceEGL::GetHardwareDisplay();
224 if (egl_display_ == EGL_NO_DISPLAY) {
225 DLOG(ERROR) << "Initialize(): could not get EGLDisplay";
226 NOTIFY_ERROR(PLATFORM_FAILURE);
227 return false;
228 }
229
230 // Open the video devices.
231 DVLOG(2) << "Initialize(): opening MFC device: " << EXYNOS_MFC_DEVICE;
232 errno = 0;
Pawel Osciak 2012/11/06 19:26:17 Why zero-out errno? Your are not using it...
sheu 2012/11/08 21:16:31 Paranoia, and DPLOG uses it. Fine, I'll take it o
233 mfc_fd_ = open(EXYNOS_MFC_DEVICE, O_RDWR | O_NONBLOCK);
234 if (mfc_fd_ == -1) {
235 DPLOG(ERROR) << "Initialize(): could not open MFC device: "
236 << EXYNOS_MFC_DEVICE;
237 NOTIFY_ERROR(PLATFORM_FAILURE);
238 return false;
239 }
240 DVLOG(2) << "Initialize(): opening GSC device: " << EXYNOS_GSC_DEVICE;
241 errno = 0;
242 gsc_fd_ = open(EXYNOS_GSC_DEVICE, O_RDWR | O_NONBLOCK);
243 if (gsc_fd_ == -1) {
244 DPLOG(ERROR) << "Initialize(): could not open GSC device: "
245 << EXYNOS_GSC_DEVICE;
246 NOTIFY_ERROR(PLATFORM_FAILURE);
247 return false;
248 }
249
250 // Capabilities check.
251 struct v4l2_capability caps;
252 const __u32 kCapsRequired =
253 V4L2_CAP_VIDEO_CAPTURE_MPLANE |
254 V4L2_CAP_VIDEO_OUTPUT_MPLANE |
255 V4L2_CAP_STREAMING;
256 errno = 0;
Pawel Osciak 2012/11/06 19:26:17 You have quite a lot of those 6 lines everywhere.
sheu 2012/11/08 21:16:31 Done.
257 if (ioctl(mfc_fd_, VIDIOC_QUERYCAP, &caps) != 0) {
258 DPLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP";
259 NOTIFY_ERROR(PLATFORM_FAILURE);
260 return false;
261 } else if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
262 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP"
263 ", caps check failed: 0x" << std::hex << caps.capabilities;
264 NOTIFY_ERROR(PLATFORM_FAILURE);
265 return false;
266 }
267 errno = 0;
268 if (ioctl(gsc_fd_, VIDIOC_QUERYCAP, &caps) != 0) {
269 DPLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP";
270 NOTIFY_ERROR(PLATFORM_FAILURE);
271 return false;
272 } else if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
273 DLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP"
274 ", caps check failed: 0x" << std::hex << caps.capabilities;
275 NOTIFY_ERROR(PLATFORM_FAILURE);
276 return false;
277 }
278
279 // Some random ioctls that Exynos requires.
280 struct v4l2_control control;
281 memset(&control, 0, sizeof(control));
282 control.id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY; // also VP8
283 control.value = 8; // Magic number from Samsung folks.
284 errno = 0;
285 if (ioctl(mfc_fd_, VIDIOC_S_CTRL, &control) != 0) {
286 DPLOG(ERROR) << "Initialize(): ioctl() failed: "
287 "V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY";
288 NOTIFY_ERROR(PLATFORM_FAILURE);
289 return false;
290 }
291
292 if (!make_context_current_.Run()) {
293 DLOG(ERROR) << "Initialize(): could not make context current";
294 NOTIFY_ERROR(PLATFORM_FAILURE);
295 return false;
296 }
297
298 if (!CreateMfcInputBuffers()) {
299 NOTIFY_ERROR(PLATFORM_FAILURE);
300 return false;
301 }
302
303 // MFC output format has to be setup before streaming starts.
304 struct v4l2_format format;
305 memset(&format, 0, sizeof(format));
306 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
307 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16;
308 if (ioctl(mfc_fd_, VIDIOC_S_FMT, &format) != 0) {
309 DPLOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_S_FMT";
310 return false;
311 }
312
313 if (!decoder_thread_.Start()) {
314 DLOG(ERROR) << "Initialize(): decoder thread failed to start";
315 NOTIFY_ERROR(PLATFORM_FAILURE);
316 return false;
317 }
318
319 SetDecoderState(kInitialized);
320
321 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
322 &Client::NotifyInitializeDone, client_));
323 return true;
324 }
Pawel Osciak 2012/11/06 19:26:17 If this function fails somewhere along the way, wi
sheu 2012/11/08 21:16:31 I assume that the client will take care of calling
Pawel Osciak 2012/11/13 18:10:39 Not exactly, in VAVDA, VaapiH264Decoder::Destroy()
sheu 2012/11/17 03:25:08 Ami mentioned that the destructor isn't called exp
Pawel Osciak 2012/11/20 18:36:00 My point is, this is very different from VAVDA. In
325
326 void ExynosVideoDecodeAccelerator::Decode(
327 const media::BitstreamBuffer& bitstream_buffer) {
328 DVLOG(1) << "Decode(): input_id=" << bitstream_buffer.id()
329 << ", size=" << bitstream_buffer.size();
330 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
331
332 scoped_ptr<BitstreamBufferRecord> bitstream_record(new BitstreamBufferRecord(
333 new base::SharedMemory(bitstream_buffer.handle(), true),
334 bitstream_buffer.size(), bitstream_buffer.id()));
335 if (!bitstream_record->shm->Map(bitstream_buffer.size())) {
336 DLOG(ERROR) << "Decode(): could not map bitstream_buffer";
337 NOTIFY_ERROR(UNREADABLE_INPUT);
338 return;
339 }
340 DVLOG(3) << "Decode(): mapped to addr=" << bitstream_record->shm->memory();
341
342 // DecodeTask() will take care of running a DecodeBufferTask().
343 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
344 &ExynosVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
345 base::Passed(&bitstream_record)));
346 }
347
348 void ExynosVideoDecodeAccelerator::AssignPictureBuffers(
349 const std::vector<media::PictureBuffer>& buffers) {
350 DVLOG(3) << "AssignPictureBuffers(): buffer_count=" << buffers.size();
351 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
352
353 if (!make_context_current_.Run()) {
354 DLOG(ERROR) << "AssignPictureBuffers(): could not make context current";
355 NOTIFY_ERROR(PLATFORM_FAILURE);
356 return;
357 }
358
359 DCHECK_EQ(gsc_output_buffer_count_, static_cast<int>(buffers.size()));
360 scoped_ptr<EGLImageKHRArrayRef> egl_images_ref(
361 new EGLImageKHRArrayRef(
362 egl_display_, new EGLImageKHR[buffers.size()],
363 new int[buffers.size()], buffers.size()));
364 for (int i = 0; i < egl_images_ref->egl_images_count; ++i) {
365 egl_images_ref->egl_images[i] = EGL_NO_IMAGE_KHR;
366 egl_images_ref->egl_image_fds[i] = -1;
367 }
368
369 const static EGLint kImageAttrs[] = {
370 EGL_IMAGE_PRESERVED_KHR, 0,
371 EGL_NONE,
372 };
373 Display* x_display = base::MessagePumpForUI::GetDefaultXDisplay();
374 glActiveTexture(GL_TEXTURE0);
375 for (int i = 0; i < egl_images_ref->egl_images_count; ++i) {
376 // Create the X pixmap and then create an EGLImageKHR from it, so we can
377 // get dma_buf backing.
378 Pixmap pixmap = XCreatePixmap(x_display, RootWindow(x_display, 0),
379 buffers[i].size().width(), buffers[i].size().height(), 32);
380 if (!pixmap) {
381 DLOG(ERROR) << "AssignPictureBuffers(): could not create X pixmap";
382 NOTIFY_ERROR(PLATFORM_FAILURE);
383 return;
384 }
385 glBindTexture(GL_TEXTURE_2D, buffers[i].texture_id());
386 EGLImageKHR egl_image;
387 egl_image = eglCreateImageKHR(
388 egl_display_, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
389 (EGLClientBuffer)pixmap, kImageAttrs);
390 // We can free the X pixmap immediately -- according to the
391 // EGL_KHR_image_base spec, the backing storage does not go away until the
392 // last referencing EGLImage is destroyed.
393 XFreePixmap(x_display, pixmap);
394 if (egl_image == EGL_NO_IMAGE_KHR) {
395 DLOG(ERROR) << "AssignPictureBuffers(): could not create EGLImageKHR";
396 NOTIFY_ERROR(PLATFORM_FAILURE);
397 return;
398 }
399 egl_images_ref->egl_images[i] = egl_image;
400 int fd;
401 if (!mali_egl_image_get_buffer_ext_phandle(
402 egl_images_ref->egl_images[i], NULL, &fd)) {
403 DLOG(ERROR) << "AssignPictureBuffers(): "
404 << "could not get EGLImageKHR dmabuf fd";
405 NOTIFY_ERROR(PLATFORM_FAILURE);
406 return;
407 }
408 egl_images_ref->egl_image_fds[i] = fd;
409 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image);
410 }
411 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
412 &ExynosVideoDecodeAccelerator::AssignPictureBuffersTask,
413 base::Unretained(this), base::Passed(&egl_images_ref)));
414 }
415
416 void ExynosVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
417 DVLOG(3) << "ReusePictureBuffer(): picture_buffer_id=" << picture_buffer_id;
418 // Must be run on child thread, as we'll insert a sync in the EGL context.
419 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
420
421 if (!make_context_current_.Run()) {
422 DLOG(ERROR) << "ReusePictureBuffer(): could not make context current";
423 NOTIFY_ERROR(PLATFORM_FAILURE);
424 return;
425 }
426
427 EGLSyncKHR egl_sync;
428 egl_sync = egl_create_sync_khr(egl_display_, EGL_SYNC_FENCE_KHR, NULL);
429 if (egl_sync == EGL_NO_SYNC_KHR) {
430 DLOG(ERROR) << "ReusePictureBuffer(): eglCreateSyncKHR() failed";
431 NOTIFY_ERROR(PLATFORM_FAILURE);
432 return;
433 }
434
435 scoped_ptr<EGLSyncKHRRef> egl_sync_ref(new EGLSyncKHRRef(
436 egl_display_, egl_sync));
437 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
438 &ExynosVideoDecodeAccelerator::ReusePictureBufferTask,
439 base::Unretained(this), picture_buffer_id, base::Passed(&egl_sync_ref)));
440 }
441
442 void ExynosVideoDecodeAccelerator::Flush() {
443 DVLOG(3) << "Flush()";
444 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
445 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
446 &ExynosVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
447 }
448
449 void ExynosVideoDecodeAccelerator::Reset() {
450 DVLOG(3) << "Reset()";
451 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
452 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
453 &ExynosVideoDecodeAccelerator::ResetTask, base::Unretained(this)));
454 }
455
456 void ExynosVideoDecodeAccelerator::Destroy() {
457 DVLOG(3) << "Destroy()";
458 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
459
460 // We're destroying; cancel all callbacks.
461 client_ptr_factory_.InvalidateWeakPtrs();
462
463 // If the decoder thread is running, destroy using posted task.
464 if (decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
465 &ExynosVideoDecodeAccelerator::DestroyTask, base::Unretained(this)))) {
466 // DestroyTask() will cause the decoder_thread_ to flush all tasks.
467 decoder_thread_.Stop();
468 } else {
469 // Otherwise, call the destroy task directly.
470 DestroyTask();
471 }
472
473 // Nuke the entire site from orbit -- it's the only way to be sure.
474 if (gsc_fd_ != -1) {
475 DestroyGscInputBuffers();
476 DestroyGscOutputBuffers();
477 close(gsc_fd_);
478 gsc_fd_ = -1;
479 }
480 if (mfc_fd_ != -1) {
481 DestroyMfcInputBuffers();
482 DestroyMfcOutputBuffers();
483 close(mfc_fd_);
484 mfc_fd_ = -1;
485 }
486
487 // Decoder thread is stopped by now; safe to touch decoder_state_.
488 // Set to kError state just in case.
489 SetDecoderState(kError);
490
491 delete this;
492 }
493
494 // static
495 void ExynosVideoDecodeAccelerator::PreSandboxInitialization() {
496 DVLOG(3) << "PreSandboxInitialization()";
497 errno = 0;
498 libmali_handle = dlopen(EXYNOS_MALI_DRIVER, RTLD_LAZY | RTLD_LOCAL);
499 if (libmali_handle == NULL) {
500 DPLOG(ERROR) << "failed to dlopen() " << EXYNOS_MALI_DRIVER;
501 }
502 }
503
504 // static
505 bool ExynosVideoDecodeAccelerator::PostSandboxInitialization() {
506 DVLOG(3) << "PostSandboxInitialization()";
507 if (libmali_handle == NULL) {
508 DLOG(ERROR) << "PostSandboxInitialization(): no " << EXYNOS_MALI_DRIVER
509 " driver handle";
510 return false;
511 }
512
513 errno = 0;
514 mali_egl_image_get_buffer_ext_phandle =
515 reinterpret_cast<EGLBoolean(*)(EGLImageKHR, EGLint*, void*)>(
516 dlsym(libmali_handle, "mali_egl_image_get_buffer_ext_phandle"));
517 if (mali_egl_image_get_buffer_ext_phandle == NULL) {
518 DPLOG(ERROR) << "PostSandboxInitialization(): failed to dlsym()"
519 " mali_egl_image_get_buffer_ext_phandle";
520 return false;
521 }
522
523 errno = 0;
524 egl_create_sync_khr =
525 reinterpret_cast<EGLSyncKHR(*)(EGLDisplay, EGLenum, const EGLint*)>(
526 dlsym(libmali_handle, "eglCreateSyncKHR"));
527 if (egl_create_sync_khr == NULL) {
528 DPLOG(ERROR) << "PostSandboxInitialization(): failed to dlsym()"
529 " eglCreateSyncKHR";
530 return false;
531 }
532
533 errno = 0;
534 egl_destroy_sync_khr =
535 reinterpret_cast<EGLBoolean(*)(EGLDisplay, EGLSyncKHR)>(
536 dlsym(libmali_handle, "eglDestroySyncKHR"));
537 if (egl_destroy_sync_khr == NULL) {
538 DPLOG(ERROR) << "PostSandboxInitialization(): failed to dlsym()"
539 " eglDestroySyncKHR";
540 return false;
541 }
542
543 errno = 0;
544 egl_client_wait_sync_khr =
545 reinterpret_cast<EGLint(*)(EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)>(
546 dlsym(libmali_handle, "eglClientWaitSyncKHR"));
547 if (egl_client_wait_sync_khr == NULL) {
548 DPLOG(ERROR) << "PostSandboxInitialization(): failed to dlsym()"
549 " eglClientWaitSyncKHR";
550 return false;
551 }
552
553 return true;
554 }
555
556 void ExynosVideoDecodeAccelerator::DecodeTask(
557 scoped_ptr<BitstreamBufferRecord> bitstream_record) {
558 DVLOG(3) << "DecodeTask(): input_id=" << bitstream_record->input_id;
559 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
560 DCHECK_NE(decoder_state_, kUninitialized);
561
562 if (decoder_state_ == kResetting) {
563 DVLOG(2) << "DecodeTask(): early out: kResetting state";
564 return;
565 } else if (decoder_state_ == kError) {
566 DVLOG(2) << "DecodeTask(): early out: kError state";
567 return;
568 }
569
570 decoder_input_queue_.push_front(
571 linked_ptr<BitstreamBufferRecord>(bitstream_record.release()));
572 decoder_decode_buffer_tasks_scheduled_++;
573 DecodeBufferTask();
574 }
575
576 void ExynosVideoDecodeAccelerator::DecodeBufferTask() {
577 DVLOG(3) << "DecodeBufferTask()";
578 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
579 DCHECK_NE(decoder_state_, kUninitialized);
580
581 decoder_decode_buffer_tasks_scheduled_--;
582
583 if (decoder_state_ == kResetting) {
584 DVLOG(2) << "DecodeBufferTask(): early out: kResetting state";
585 return;
586 } else if (decoder_state_ == kError) {
587 DVLOG(2) << "DecodeBufferTask(): early out: kError state";
588 return;
589 }
590
591 if (decoder_current_bitstream_buffer_ == NULL) {
592 if (decoder_input_queue_.empty()) {
593 // We're waiting for a new buffer -- exit without scheduling a new task.
594 return;
595 }
596 // Setup to use the next buffer.
597 decoder_current_bitstream_buffer_.reset(
598 decoder_input_queue_.back().release());
599 decoder_input_queue_.pop_back();
600 DVLOG(3) << "DecodeBufferTask(): reading input_id="
601 << decoder_current_bitstream_buffer_->input_id
602 << ", addr=" << decoder_current_bitstream_buffer_->shm->memory()
603 << ", size=" << decoder_current_bitstream_buffer_->size;
604 }
605 bool decode_result = false;
606 const void* data = decoder_current_bitstream_buffer_->shm->memory();
607 size_t size = decoder_current_bitstream_buffer_->size;
Pawel Osciak 2012/11/06 19:26:17 Why not use shm->size() ?
sheu 2012/11/08 21:16:31 shm->size() doesn't exist. I could add it.
Pawel Osciak 2012/11/13 18:10:39 Oh hah, I was seeing things. Nevermind.
608 switch (decoder_state_) {
609 case kInitialized:
610 case kAfterReset:
611 decode_result = DecodeBufferInitial(data, size);
612 break;
613 case kDecoding:
614 decode_result = DecodeBufferContinue(data, size);
615 break;
616 default:
617 NOTIFY_ERROR(ILLEGAL_STATE);
618 return;
619 }
620 if (decoder_state_ == kError) {
621 // Failed during decode.
622 return;
623 } else if (!decode_result) {
624 // We might not have failed decode completely, but returned false due to
625 // insufficient resources, etc. Retry this this buffer later; exit without
626 // scheduling another task.
627 return;
628 }
629
630 // Our current bitstream buffer is done; return it.
631 int32 input_id = decoder_current_bitstream_buffer_->input_id;
632 DVLOG(3) << "DecodeBufferTask(): finished input_id=" << input_id;
633 decoder_current_bitstream_buffer_.reset(NULL);
634 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
635 &Client::NotifyEndOfBitstreamBuffer, client_, input_id));
636
637 // If we're behind on tasks, schedule another one.
638 if (decoder_decode_buffer_tasks_scheduled_ <
639 static_cast<int>(decoder_input_queue_.size())) {
640 decoder_decode_buffer_tasks_scheduled_++;
641 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
642 &ExynosVideoDecodeAccelerator::DecodeBufferTask,
643 base::Unretained(this)));
644 }
645 }
646
647 bool ExynosVideoDecodeAccelerator::DecodeBufferInitial(
648 const void* data, size_t size) {
649 DVLOG(3) << "DecodeBufferInitial(): data=" << data << ", size=" << size;
650 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
651 DCHECK_NE(decoder_state_, kUninitialized);
652 DCHECK_NE(decoder_state_, kDecoding);
653 DCHECK(!device_poll_thread_.IsRunning());
654 // Initial decode. We haven't been able to get output stream format info yet.
655 // Get it, and start decoding.
656
657 // Copy in and send to HW.
658 if (!AppendToInputFrame(data, size) || !FlushInputFrame())
659 return false;
660
661 // Recycle buffers.
662 DequeueMfc();
663
664 // Check and see if we have format info yet.
665 struct v4l2_format format;
666 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
667 errno = 0;
668 if (ioctl(mfc_fd_, VIDIOC_G_FMT, &format) != 0) {
669 if (errno == EINVAL) {
670 // We will get EINVAL if we haven't seen sufficient stream to decode the
671 // format. Return true and go to the next buffer.
672 return true;
673 } else {
674 DPLOG(ERROR) << "DecodeBufferInitial(): ioctl() failed: VIDIOC_G_FMT";
675 NOTIFY_ERROR(PLATFORM_FAILURE);
676 return false;
677 }
678 }
679
680 // Run this initialization only on first startup.
681 if (decoder_state_ == kInitialized) {
682 DVLOG(3) << "DecodeBufferInitial(): running one-time initialization";
683 // Success! Setup our parameters.
684 DCHECK_EQ(format.fmt.pix_mp.num_planes, 2);
Pawel Osciak 2012/11/06 19:26:17 I'm wondering if maybe this should be more than a
sheu 2012/11/08 21:16:31 Done.
685 // We don't handle midstream resizes right now.
686 if (!frame_buffer_size_.IsEmpty() && frame_buffer_size_ !=
687 gfx::Size(format.fmt.pix_mp.width, frame_buffer_size_.height())) {
688 // We don't handle mistream resizes right now.
689 NOTIMPLEMENTED();
690 NOTIFY_ERROR(UNREADABLE_INPUT);
691 return false;
692 }
693 frame_buffer_size_.SetSize(
694 format.fmt.pix_mp.width, format.fmt.pix_mp.height);
695 mfc_output_buffer_size_[0] = format.fmt.pix_mp.plane_fmt[0].sizeimage;
696 mfc_output_buffer_size_[1] = format.fmt.pix_mp.plane_fmt[1].sizeimage;
697 mfc_output_buffer_pixelformat_ = format.fmt.pix_mp.pixelformat;
698
699 // Create our other buffers.
700 if (!CreateMfcOutputBuffers() || !CreateGscInputBuffers() ||
701 !CreateGscOutputBuffers()) {
702 NOTIFY_ERROR(PLATFORM_FAILURE);
703 return false;
704 }
705 }
706
707 // StartDevicePoll will raise the error if there is one.
708 if (!StartDevicePoll()) {
709 return false;
710 }
711
712 decoder_state_ = kDecoding;
713
714 // This buffer contained the header that kicks off decoding of the video
715 // stream. Return false here so it gets recycled into the next
716 // DecodeBufferContinue() and the start of actual output stream.
717 return false;
718 }
719
720 bool ExynosVideoDecodeAccelerator::DecodeBufferContinue(
721 const void* data, size_t size) {
722 DVLOG(3) << "DecodeBufferContinue(): data=" << data << ", size=" << size;
723 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
724 DCHECK_EQ(decoder_state_, kDecoding);
725
726 // We've already setup our output stream parameters, so just keep on truckin'.
727 return (AppendToInputFrame(data, size) && FlushInputFrame());
728 }
729
730 bool ExynosVideoDecodeAccelerator::AppendToInputFrame(
731 const void* data, size_t size) {
732 DVLOG(3) << "AppendToInputFrame()";
733 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
734 // We should have started streaming when we created MFC input buffers.
735 DCHECK(mfc_input_streamon_);
736 DCHECK(decoder_state_ == kInitialized || decoder_state_ == kDecoding);
737
738 // Flush if we're too big
739 if (decoder_current_input_buffer_ != -1) {
740 MfcInputRecord& input_record =
741 mfc_input_buffer_map_[decoder_current_input_buffer_];
742 if (input_record.bytes_used + size > input_record.length) {
743 if (!FlushInputFrame())
744 return false;
745 decoder_current_input_buffer_ = -1;
746 }
747 }
748
749 // Try to get an available input buffer
750 if (decoder_current_input_buffer_ == -1) {
751 if (mfc_free_input_buffers_.empty()) {
752 // See if we can get more free buffers from HW
753 DequeueMfc();
754 if (mfc_free_input_buffers_.empty()) {
755 // Nope!
756 DVLOG(2) << "AppendToInputFrame(): stalled for input buffers";
757 return false;
758 }
759 }
760 decoder_current_input_buffer_ = mfc_free_input_buffers_.back();
761 mfc_free_input_buffers_.pop_back();
762 MfcInputRecord& input_record =
763 mfc_input_buffer_map_[decoder_current_input_buffer_];
764 DCHECK_EQ(input_record.bytes_used, 0);
765 DCHECK_EQ(input_record.input_id, -1);
766 DCHECK(decoder_current_bitstream_buffer_ != NULL);
767 input_record.input_id = decoder_current_bitstream_buffer_->input_id;
768 }
769
770 // Copy in to the buffer.
771 MfcInputRecord& input_record =
772 mfc_input_buffer_map_[decoder_current_input_buffer_];
773 if (size > input_record.length - input_record.bytes_used) {
774 LOG(ERROR) << "AppendToInputFrame(): over-size frame, erroring";
775 NOTIFY_ERROR(UNREADABLE_INPUT);
776 return false;
777 }
778 memcpy((char*)input_record.offset + input_record.bytes_used, data, size);
779 input_record.bytes_used += size;
780
781 return true;
782 }
783
784 bool ExynosVideoDecodeAccelerator::FlushInputFrame() {
785 DVLOG(3) << "FlushInputFrame()";
786 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
787 DCHECK(decoder_state_ == kInitialized || decoder_state_ == kDecoding);
788 if (decoder_current_input_buffer_ == -1)
789 return true;
790
791 MfcInputRecord& input_record =
792 mfc_input_buffer_map_[decoder_current_input_buffer_];
793 if (input_record.bytes_used == 0)
794 return true;
795
796 // Queue it to MFC.
797 mfc_input_ready_queue_.push_back(decoder_current_input_buffer_);
798 decoder_frames_inflight_++;
799 decoder_current_input_buffer_ = -1;
800 DVLOG(3) << "FlushInputFrame(): submitting input_id="
801 << input_record.input_id;
802 // Kick the MFC once since there's new available input for it.
803 EnqueueMfc();
804
805 return (decoder_state_ != kError);
806 }
807
808 void ExynosVideoDecodeAccelerator::AssignPictureBuffersTask(
809 scoped_ptr<EGLImageKHRArrayRef> egl_images_ref) {
810 DVLOG(3) << "AssignPictureBuffersTask()";
811 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
812 DCHECK_NE(decoder_state_, kUninitialized);
813
814 // We run AssignPictureBuffersTask even if we're in kResetting.
815 if (decoder_state_ == kError) {
816 DVLOG(2) << "AssignPictureBuffersTask(): early out: kError state";
817 return;
818 }
819
820 DCHECK_EQ(egl_images_ref->egl_images_count,
821 static_cast<int>(gsc_output_buffer_map_.size()));
822 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) {
823 // We should be blank right now.
824 GscOutputRecord& output_record = gsc_output_buffer_map_[i];
825 DCHECK_EQ(output_record.fd, -1);
826 DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR);
827 DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR);
828 DCHECK_EQ(output_record.picture_id, -1);
829 output_record.fd = egl_images_ref->egl_image_fds[i];
830 output_record.egl_image = egl_images_ref->egl_images[i];
831 output_record.picture_id = i;
832
833 // Take ownership of the EGLImage and fd.
834 egl_images_ref->egl_images[i] = EGL_NO_IMAGE_KHR;
835 egl_images_ref->egl_image_fds[i] = -1;
836 // And add this buffer to the free list.
837 gsc_free_output_buffers_.push_front(i);
838 }
839
840 // StartDevicePoll will raise the error if there is one.
841 StartDevicePoll();
842 }
843
844 void ExynosVideoDecodeAccelerator::ServiceDeviceTask() {
845 DVLOG(3) << "ServiceDeviceTask()";
846 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
847 DCHECK_NE(decoder_state_, kUninitialized);
848 DCHECK_NE(decoder_state_, kInitialized);
849 DCHECK_NE(decoder_state_, kAfterReset);
850
851 if (decoder_state_ == kResetting) {
852 DVLOG(2) << "ServiceDeviceTask(): early out: kResetting state";
853 return;
854 } else if (decoder_state_ == kError) {
855 DVLOG(2) << "ServiceDeviceTask(): early out: kError state";
856 return;
857 }
858
859 DequeueMfc();
860 DequeueGsc();
861 EnqueueMfc();
862 EnqueueGsc();
863
864 DVLOG(1) << "ServiceDeviceTask(): buffer counts: DEC["
865 << decoder_input_queue_.size() << "->"
866 << mfc_input_ready_queue_.size() << "] => MFC["
867 << mfc_free_input_buffers_.size() << "/"
868 << mfc_input_buffer_count_ << "->"
869 << mfc_free_output_buffers_.size() << "/"
870 << mfc_output_buffer_count_ << "] => "
871 << mfc_output_gsc_input_queue_.size() << " => GSC["
872 << gsc_free_input_buffers_.size() << "/"
873 << gsc_input_buffer_count_ << "->"
874 << gsc_free_output_buffers_.size() << "/"
875 << gsc_output_buffer_count_ << "] => VDA["
876 << decoder_frames_at_client_ << "]";
877
878 // If we're behind on decode, schedule another one.
Pawel Osciak 2012/11/06 19:26:17 Extract l.878-885 into a function and use it here
sheu 2012/11/08 21:16:31 It's just two lines -- that seems kind of excessiv
Pawel Osciak 2012/11/13 18:10:39 I meant extracting 8 lines and having: void Sched
sheu 2012/11/17 03:25:08 Fine :-P
879 if (decoder_decode_buffer_tasks_scheduled_ <
880 static_cast<int>(decoder_input_queue_.size())) {
881 decoder_decode_buffer_tasks_scheduled_++;
882 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
883 &ExynosVideoDecodeAccelerator::DecodeBufferTask,
884 base::Unretained(this)));
885 }
886 }
887
888 void ExynosVideoDecodeAccelerator::EnqueueMfc() {
889 DVLOG(3) << "EnqueueMfc()";
890 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
891 DCHECK_NE(decoder_state_, kUninitialized);
892
893 // Drain the pipe of completed decode buffers.
894 struct v4l2_buffer qbuf;
895 struct v4l2_plane qbuf_planes[2];
896 while (!mfc_input_ready_queue_.empty()) {
897 // Enqueue the MFC input (VIDEO_OUTPUT) buffer.
898 int buffer = mfc_input_ready_queue_.back();
899 MfcInputRecord& input_record = mfc_input_buffer_map_[buffer];
900 DCHECK(!input_record.at_device);
901 memset(&qbuf, 0, sizeof(qbuf));
902 memset(qbuf_planes, 0, sizeof(qbuf_planes));
903 qbuf.index = buffer;
904 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
905 qbuf.timestamp.tv_sec = input_record.input_id;
906 qbuf.memory = V4L2_MEMORY_MMAP;
907 qbuf.m.planes = qbuf_planes;
908 qbuf.m.planes[0].bytesused = input_record.bytes_used;
909 qbuf.length = 1;
910 errno = 0;
911 if (ioctl(mfc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
912 DPLOG(ERROR) << "EnqueueMfc(): ioctl() failed: VIDIOC_QBUF";
913 NOTIFY_ERROR(PLATFORM_FAILURE);
914 return;
915 }
916 input_record.at_device = true;
917 mfc_input_ready_queue_.pop_back();
918 mfc_input_buffer_queued_count_++;
919 DVLOG(3) << "EnqueueMfc(): enqueued input_id=" << input_record.input_id;
920 if (!mfc_input_streamon_) {
921 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
922 errno = 0;
923 if (ioctl(mfc_fd_, VIDIOC_STREAMON, &type) != 0) {
924 DPLOG(ERROR) << "EnqueueMfc(): ioctl() failed: VIDIOC_STREAMON";
925 NOTIFY_ERROR(PLATFORM_FAILURE);
926 return;
927 }
928 mfc_input_streamon_ = true;
929 }
930 }
931
932 // Enqueue all the MFC output (VIDEO_CAPTURE) buffers we can.
933 while (!mfc_free_output_buffers_.empty()) {
934 int buffer = mfc_free_output_buffers_.back();
935 MfcOutputRecord& output_record = mfc_output_buffer_map_[buffer];
936 DCHECK(!output_record.at_device);
937 DCHECK_EQ(output_record.input_id, -1);
938 memset(&qbuf, 0, sizeof(qbuf));
939 memset(qbuf_planes, 0, sizeof(qbuf_planes));
940 qbuf.index = buffer;
941 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
942 qbuf.memory = V4L2_MEMORY_MMAP;
943 qbuf.m.planes = qbuf_planes;
944 qbuf.length = 2;
945 errno = 0;
946 if (ioctl(mfc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
947 DPLOG(ERROR) << "EnqueueMfc(): ioctl() failed: VIDIOC_QBUF";
948 NOTIFY_ERROR(PLATFORM_FAILURE);
949 return;
950 }
951 output_record.at_device = true;
952 mfc_free_output_buffers_.pop_back();
953 if (!mfc_output_streamon_) {
954 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
955 errno = 0;
956 if (ioctl(mfc_fd_, VIDIOC_STREAMON, &type) != 0) {
957 DPLOG(ERROR) << "EnqueueMfc(): ioctl() failed: VIDIOC_STREAMON";
958 NOTIFY_ERROR(PLATFORM_FAILURE);
959 return;
960 }
961 mfc_output_streamon_ = true;
962 }
963 }
964 }
965
966 void ExynosVideoDecodeAccelerator::DequeueMfc() {
967 DVLOG(3) << "DequeueMfc()";
968 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
969 DCHECK_NE(decoder_state_, kUninitialized);
970 DCHECK_NE(decoder_state_, kInitialized);
971
972 // Dequeue completed MFC input (VIDEO_OUTPUT) buffers, and recycle to the free
973 // list. Note that if we ever run completely dry of input buffers on the MFC
974 // device, epoll() will return EPOLLERR and our DevicePollLoop() will exit
975 // early. Work around this by never _completely_ draining the MFC input
976 // queue.
977 struct v4l2_buffer dqbuf;
978 struct v4l2_plane planes[2];
979 DCHECK_EQ((decoder_current_input_buffer_ != -1 ? 1 : 0) +
980 static_cast<int>(mfc_input_ready_queue_.size()) +
981 static_cast<int>(mfc_free_input_buffers_.size()) +
982 mfc_input_buffer_queued_count_, mfc_input_buffer_count_);
983 while (mfc_input_buffer_queued_count_ > 1) {
984 memset(&dqbuf, 0, sizeof(dqbuf));
985 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
986 dqbuf.memory = V4L2_MEMORY_MMAP;
987 errno = 0;
988 if (ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) {
989 if (errno == EAGAIN) {
990 // Done all that we could.
Pawel Osciak 2012/11/06 19:26:17 s/Done all that we could./No buffers to dequeue./
sheu 2012/11/08 21:16:31 Done.
991 break;
992 } else if (errno == EINVAL) {
Pawel Osciak 2012/11/06 19:26:17 s/errno == EINVAL/errno == EINVAL && !mfc_output_s
sheu 2012/11/08 21:16:31 Done.
993 // We're not streaming this queue; skip.
994 DVLOG(2) << "DequeueMfc(): VIDEO_OUTPUT not streaming, skipping";
995 break;
996 } else {
997 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF";
998 NOTIFY_ERROR(PLATFORM_FAILURE);
999 return;
1000 }
1001 }
1002 MfcInputRecord& input_record = mfc_input_buffer_map_[dqbuf.index];
1003 DCHECK(input_record.at_device);
1004 input_record.at_device = false;
1005 input_record.bytes_used = 0;
1006 input_record.input_id = -1;
1007 mfc_free_input_buffers_.push_back(dqbuf.index);
1008 mfc_input_buffer_queued_count_--;
1009 }
1010
1011 // Dequeue completed MFC output (VIDEO_CAPTURE) buffers, and queue to the
1012 // completed queue.
1013 memset(&dqbuf, 0, sizeof(dqbuf));
1014 memset(planes, 0, sizeof(planes));
1015 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1016 dqbuf.memory = V4L2_MEMORY_MMAP;
1017 dqbuf.m.planes = planes;
1018 dqbuf.length = 2;
1019 errno = 0;
1020 while (ioctl(mfc_fd_, VIDIOC_DQBUF, &dqbuf) == 0) {
1021 MfcOutputRecord& output_record = mfc_output_buffer_map_[dqbuf.index];
1022 DCHECK(output_record.at_device);
1023 output_record.at_device = false;
1024 output_record.input_id = dqbuf.timestamp.tv_sec;
1025 output_record.bytes_used[0] = dqbuf.m.planes[0].bytesused;
1026 output_record.bytes_used[1] = dqbuf.m.planes[1].bytesused;
1027 DVLOG(3) << "DequeueMfc(): dequeued input_id=" << dqbuf.timestamp.tv_sec;
1028 mfc_output_gsc_input_queue_.push_front(dqbuf.index);
1029 memset(&dqbuf, 0, sizeof(dqbuf));
Pawel Osciak 2012/11/06 19:26:17 Perhaps refactor the while loop to be: while(1) {
sheu 2012/11/08 21:16:31 Done.
1030 memset(planes, 0, sizeof(planes));
1031 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1032 dqbuf.memory = V4L2_MEMORY_MMAP;
1033 dqbuf.m.planes = planes;
1034 dqbuf.length = 2;
1035 errno = 0;
1036 }
1037 if (errno == EINVAL) {
1038 // We're not streaming this queue; skip.
1039 DVLOG(2) << "DequeueMfc(): VIDEO_CAPTURE not streaming, skipping";
1040 } else if (errno != EAGAIN) {
1041 DPLOG(ERROR) << "DequeueMfc(): ioctl() failed: VIDIOC_DQBUF";
1042 NOTIFY_ERROR(PLATFORM_FAILURE);
1043 return;
1044 }
1045 }
1046
1047 void ExynosVideoDecodeAccelerator::EnqueueGsc() {
1048 DVLOG(3) << "EnqueueGsc()";
1049 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1050 DCHECK_NE(decoder_state_, kUninitialized);
1051 DCHECK_NE(decoder_state_, kInitialized);
1052 DCHECK_NE(decoder_state_, kAfterReset);
1053
1054 // Drain the pipe of completed MFC output buffers.
1055 struct v4l2_buffer qbuf;
1056 struct v4l2_plane qbuf_planes[2];
1057 DCHECK(gsc_output_streamon_);
1058 while (!mfc_output_gsc_input_queue_.empty()) {
1059 if (gsc_free_input_buffers_.empty())
1060 break;
1061 // Bug workaround: GSC is liable to race conditions if more than one
Pawel Osciak 2012/11/06 19:26:17 This is bug-worthy for SLSI I'd say...
sheu 2012/11/08 21:16:31 They are aware of this bug, and are as usual not d
1062 // buffer is simultaneously queued.
1063 if (gsc_output_buffer_queued_count_ > 0)
1064 break;
1065 if (gsc_output_buffer_prepared_count_ == 0) {
1066 // Enqueue a GSC output (VIDEO_CAPTURE) buffer for the incoming GSC input
1067 // buffer.
1068 if (gsc_free_output_buffers_.empty())
1069 break;
1070 int buffer = gsc_free_output_buffers_.back();
1071 GscOutputRecord& output_record = gsc_output_buffer_map_[buffer];
1072 DCHECK(!output_record.at_device);
1073 DCHECK(!output_record.at_client);
1074 if (output_record.egl_sync != EGL_NO_SYNC_KHR) {
1075 // If we have to wait for completion, wait. Note that
1076 // gsc_free_output_buffers_ is a FIFO queue.
1077 egl_client_wait_sync_khr(egl_display_, output_record.egl_sync, 0,
1078 EGL_FOREVER_KHR);
1079 egl_destroy_sync_khr(egl_display_, output_record.egl_sync);
Pawel Osciak 2012/11/06 19:26:17 Is it valid to do this on decoder thread, i.e. a d
sheu 2012/11/08 21:16:31 Yes.
1080 output_record.egl_sync = EGL_NO_SYNC_KHR;
1081 }
1082 memset(&qbuf, 0, sizeof(qbuf));
1083 memset(qbuf_planes, 0, sizeof(qbuf_planes));
1084 qbuf.index = buffer;
1085 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1086 qbuf.memory = V4L2_MEMORY_DMABUF;
1087 qbuf.m.planes = qbuf_planes;
1088 qbuf.m.planes[0].m.fd = output_record.fd;
1089 qbuf.length = 1;
1090 errno = 0;
1091 if (ioctl(gsc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
1092 DPLOG(ERROR) << "EnqueueGsc(): ioctl() failed: VIDIOC_QBUF";
1093 NOTIFY_ERROR(PLATFORM_FAILURE);
1094 return;
1095 }
1096 output_record.at_device = true;
1097 gsc_free_output_buffers_.pop_back();
1098 gsc_output_buffer_prepared_count_++;
1099 }
1100 // Now enqueue the GSC input (VIDEO_OUTPUT) buffer for the complete MFC
1101 // output buffer. We defer requeuing the MFC output buffer to its free
1102 // list, as the GSC input will be using its data.
1103 int mfc_buffer, gsc_buffer;
1104 mfc_buffer = mfc_output_gsc_input_queue_.back();
1105 gsc_buffer = gsc_free_input_buffers_.back();
1106 MfcOutputRecord& output_record = mfc_output_buffer_map_[mfc_buffer];
1107 DCHECK(!output_record.at_device);
1108 GscInputRecord& input_record = gsc_input_buffer_map_[gsc_buffer];
1109 DCHECK(!input_record.at_device);
1110 DCHECK_EQ(input_record.mfc_output, -1);
1111 memset(&qbuf, 0, sizeof(qbuf));
1112 memset(qbuf_planes, 0, sizeof(qbuf_planes));
1113 qbuf.index = gsc_buffer;
1114 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1115 qbuf.timestamp.tv_sec = output_record.input_id;
1116 qbuf.memory = V4L2_MEMORY_USERPTR;
1117 qbuf.m.planes = qbuf_planes;
1118 qbuf.m.planes[0].bytesused = output_record.bytes_used[0];
1119 qbuf.m.planes[0].length = mfc_output_buffer_size_[0];
1120 qbuf.m.planes[0].m.userptr = (unsigned long)output_record.offset[0];
1121 qbuf.m.planes[1].bytesused = output_record.bytes_used[1];
1122 qbuf.m.planes[1].length = mfc_output_buffer_size_[1];
1123 qbuf.m.planes[1].m.userptr = (unsigned long)output_record.offset[1];
1124 qbuf.length = 2;
1125 errno = 0;
1126 if (ioctl(gsc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
1127 DPLOG(ERROR) << "EnqueueGsc(): ioctl() failed: VIDIOC_QBUF";
1128 NOTIFY_ERROR(PLATFORM_FAILURE);
1129 return;
1130 }
1131 input_record.at_device = true;
1132 input_record.mfc_output = mfc_buffer;
1133 output_record.bytes_used[0] = 0;
1134 output_record.bytes_used[1] = 0;
1135 mfc_output_gsc_input_queue_.pop_back();
1136 gsc_free_input_buffers_.pop_back();
1137 gsc_output_buffer_prepared_count_--;
1138 gsc_output_buffer_queued_count_++;
1139 DVLOG(3) << "EnqueueGsc(): enqueued input_id=" << output_record.input_id;
1140 if (!gsc_input_streamon_) {
1141 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1142 errno = 0;
1143 if (ioctl(gsc_fd_, VIDIOC_STREAMON, &type) != 0) {
1144 DPLOG(ERROR) << "EnqueueGsc(): ioctl() failed: VIDIOC_STREAMON";
1145 NOTIFY_ERROR(PLATFORM_FAILURE);
1146 return;
1147 }
1148 gsc_input_streamon_ = true;
1149 }
1150 }
1151 }
1152
1153 void ExynosVideoDecodeAccelerator::DequeueGsc() {
1154 DVLOG(3) << "DequeueGsc()";
1155 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1156 DCHECK_NE(decoder_state_, kUninitialized);
1157 DCHECK_NE(decoder_state_, kInitialized);
1158 DCHECK_NE(decoder_state_, kAfterReset);
1159
1160 // Dequeue completed GSC input (VIDEO_OUTPUT) buffers, and recycle to the free
1161 // list. Also recycle the corresponding MFC output buffers at this time.
1162 // Note that if we ever run completely dry of input buffers on the GSC device,
1163 // epoll() will return EPOLLERR and our DevicePollLoop() will exit early.
1164 // Work around this by never _completely_ draining the GSC input queue.
1165 struct v4l2_buffer dqbuf;
1166 while (gsc_input_buffer_count_ >
1167 static_cast<int>(gsc_free_input_buffers_.size()) + 1) {
1168 memset(&dqbuf, 0, sizeof(dqbuf));
1169 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1170 dqbuf.memory = V4L2_MEMORY_DMABUF;
1171 errno = 0;
1172 if (ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf) != 0) {
1173 if (errno == EAGAIN) {
1174 // Done all that we could.
1175 break;
1176 } else if (errno == EINVAL) {
1177 // We're not streaming this queue; skip.
1178 DVLOG(2) << "DequeueGsc(): VIDEO_OUTPUT not streaming, skipping";
1179 break;
1180 } else {
1181 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF";
1182 NOTIFY_ERROR(PLATFORM_FAILURE);
1183 return;
1184 }
1185 }
1186 GscInputRecord& input_record = gsc_input_buffer_map_[dqbuf.index];
1187 DCHECK(input_record.at_device);
1188 input_record.at_device = false;
1189 mfc_output_buffer_map_[input_record.mfc_output].input_id = -1;
1190 mfc_free_output_buffers_.push_back(input_record.mfc_output);
1191 input_record.mfc_output = -1;
1192 gsc_free_input_buffers_.push_back(dqbuf.index);
1193 }
1194
1195 // Dequeue completed GSC output (VIDEO_CAPTURE) buffers, and send them off to
1196 // the client. Don't recycle to its free list yet -- we can't do that until
1197 // ReusePictureBuffer() returns it to us.
1198 memset(&dqbuf, 0, sizeof(dqbuf));
1199 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1200 dqbuf.memory = V4L2_MEMORY_DMABUF;
1201 errno = 0;
1202 while (ioctl(gsc_fd_, VIDIOC_DQBUF, &dqbuf) == 0) {
1203 GscOutputRecord& output_record = gsc_output_buffer_map_[dqbuf.index];
1204 DCHECK(output_record.at_device);
1205 DCHECK(!output_record.at_client);
1206 DCHECK_EQ(output_record.egl_sync, EGL_NO_SYNC_KHR);
1207 output_record.at_device = false;
1208 output_record.at_client = true;
1209 gsc_output_buffer_queued_count_--;
1210 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1211 &Client::PictureReady, client_, media::Picture(
1212 output_record.picture_id, dqbuf.timestamp.tv_sec)));
1213 decoder_frames_inflight_--;
1214 decoder_frames_at_client_++;
1215 DVLOG(1) << "DequeueGsc(): dequeued input_id=" << dqbuf.timestamp.tv_sec
1216 << " as picture_id=" << output_record.picture_id;
1217 if (decoder_frames_inflight_ == 0 && decoder_flush_notify_requested_) {
1218 // We were asked for a flush notification, so let's do it.
1219 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1220 &Client::NotifyFlushDone, client_));
1221 }
1222 memset(&dqbuf, 0, sizeof(dqbuf));
1223 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1224 dqbuf.memory = V4L2_MEMORY_DMABUF;
1225 errno = 0;
1226 }
1227 if (errno == EINVAL) {
1228 // We're not streaming this queue; skip.
1229 DVLOG(2) << "DequeueGsc(): VIDEO_CAPTURE not streaming, skipping";
1230 } else if (errno != EAGAIN) {
1231 DPLOG(ERROR) << "DequeueGsc(): ioctl() failed: VIDIOC_DQBUF";
1232 NOTIFY_ERROR(PLATFORM_FAILURE);
1233 return;
1234 }
1235 }
1236
1237 void ExynosVideoDecodeAccelerator::ReusePictureBufferTask(
1238 int32 picture_buffer_id, scoped_ptr<EGLSyncKHRRef> egl_sync_ref) {
1239 DVLOG(3) << "ReusePictureBufferTask(): picture_buffer_id="
1240 << picture_buffer_id;
1241 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1242
1243 // We run ReusePictureBufferTask even if we're in kResetting.
1244 if (decoder_state_ == kError) {
1245 DVLOG(2) << "ReusePictureBufferTask(): early out: kError state";
1246 return;
1247 }
1248
1249 size_t index;
1250 for (index = 0; index < gsc_output_buffer_map_.size(); ++index)
1251 if (gsc_output_buffer_map_[index].picture_id == picture_buffer_id)
1252 break;
1253
1254 if (index >= gsc_output_buffer_map_.size()) {
1255 DLOG(ERROR) << "ReusePictureBufferTask(): picture_buffer_id not found";
1256 NOTIFY_ERROR(INVALID_ARGUMENT);
1257 return;
1258 }
1259
1260 GscOutputRecord& output_record = gsc_output_buffer_map_[index];
1261 DCHECK(!output_record.at_device);
1262 DCHECK(output_record.at_client);
1263 output_record.at_client = false;
1264 output_record.egl_sync = egl_sync_ref->egl_sync;
1265 gsc_free_output_buffers_.push_front(index);
1266 decoder_frames_at_client_--;
1267 // Take ownership of the EGLSync.
1268 egl_sync_ref->egl_sync = EGL_NO_SYNC_KHR;
1269 // We got a buffer back, so kick the GSC.
1270 EnqueueGsc();
1271 }
1272
1273 void ExynosVideoDecodeAccelerator::FlushTask() {
1274 DVLOG(3) << "FlushTask()";
1275 // Flush the currently-building frame.
1276
1277 if (decoder_state_ == kResetting) {
1278 DVLOG(2) << "FlushTask(): early out: kResetting state";
1279 return;
1280 } else if (decoder_state_ == kError) {
1281 DVLOG(2) << "FlushTask(): early out: kError state";
1282 return;
1283 }
1284
1285 FlushInputFrame();
1286
1287 if (decoder_frames_inflight_ == 0) {
1288 // If we don't have anything actually queued, we can notify immediatey.
Pawel Osciak 2012/11/06 19:26:17 s/immediatey/immediately
sheu 2012/11/08 21:16:31 Done.
1289 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1290 &Client::NotifyFlushDone, client_));
1291 } else {
1292 // We'll flag that we want a flush-finished notification, and just return.
1293 decoder_flush_notify_requested_ = true;
1294 }
1295 }
1296
1297 void ExynosVideoDecodeAccelerator::ResetTask() {
1298 DVLOG(3) << "ResetTask()";
1299 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1300 // We stop streaming, but we _don't_ destroy our buffers.
1301 if (!StopDevicePoll())
1302 return;
1303
1304 if (decoder_current_bitstream_buffer_ != NULL) {
1305 int input_id;
1306 input_id = decoder_current_bitstream_buffer_->input_id;
1307 decoder_current_bitstream_buffer_.reset(NULL);
Pawel Osciak 2012/11/06 19:26:17 Enough to say reset()
sheu 2012/11/08 21:16:31 Done.
1308 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1309 &Client::NotifyEndOfBitstreamBuffer, client_, input_id));
1310 }
1311 while (!decoder_input_queue_.empty()) {
1312 int input_id;
1313 scoped_ptr<BitstreamBufferRecord>
1314 bitstream_record(decoder_input_queue_.back().release());
1315 decoder_input_queue_.pop_back();
1316 input_id = bitstream_record->input_id;
1317 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1318 &Client::NotifyEndOfBitstreamBuffer, client_, input_id));
1319 }
1320
1321 decoder_current_input_buffer_ = -1;
1322 decoder_decode_buffer_tasks_scheduled_ = 0;
1323 decoder_frames_inflight_ = 0;
1324 decoder_flush_notify_requested_ = false;
1325
1326 // Mark that we're resetting, then enqueue a ResetDoneTask(). All intervening
1327 // jobs will early-out in the kResetting state.
1328 decoder_state_ = kResetting;
1329 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
1330 &ExynosVideoDecodeAccelerator::ResetDoneTask, base::Unretained(this)));
1331 }
1332
1333 void ExynosVideoDecodeAccelerator::ResetDoneTask() {
1334 DVLOG(3) << "ResetDoneTask()";
1335 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1336
1337 // Jobs drained, we're finished resetting.
1338 DCHECK_EQ(decoder_state_, kResetting);
1339 decoder_state_ = kAfterReset;
1340 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1341 &Client::NotifyResetDone, client_));
1342 }
1343
1344 void ExynosVideoDecodeAccelerator::DestroyTask() {
1345 DVLOG(3) << "DestroyTask()";
1346
1347 // DestroyTask() should run regardless of decoder_state_.
1348
1349 // Stop streaming and the device_poll_thread_.
1350 StopDevicePoll();
1351
1352 decoder_current_bitstream_buffer_.reset(NULL);
1353 decoder_current_input_buffer_ = -1;
1354 decoder_decode_buffer_tasks_scheduled_ = 0;
1355 decoder_frames_inflight_ = 0;
1356 decoder_frames_at_client_ = 0;
1357 decoder_flush_notify_requested_ = false;
1358 decoder_input_queue_.clear();
1359
1360 // Set our state to kError. This will cause all subsequent tasks to
1361 // early-exit.
1362 decoder_state_ = kError;
1363 }
1364
1365 bool ExynosVideoDecodeAccelerator::StartDevicePoll() {
1366 DVLOG(3) << "StartDevicePoll()";
1367 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1368
1369 // Early-out if we're already running.
1370 if (device_poll_thread_.IsRunning()) {
1371 DVLOG(2) << "StartDevicePoll(): early out: "
1372 << "device poll thread already running";
1373 return true;
1374 }
1375
1376 // At least one of the OUTPUT or CAPTURE queues for each of MFC and GSC must
1377 // be in STREAMON state for epoll() not to return EPOLERR. So:
1378 // * for MFC, we'll start the CAPTURE queue
1379 // * for GSC, we'll start the CAPTURE queue
1380 // STREAMON requires, unfortunately, that we have buffers already queued.
1381 // We'll need to have the buffers available to queue. If they aren't
1382 // available yet, this is not an error; for example, GSC output buffers won't
1383 // be available until AssignPictureBuffers() is called.
1384 if ((!mfc_output_streamon_ && mfc_free_output_buffers_.empty()) ||
1385 gsc_free_output_buffers_.empty()) {
1386 DVLOG(2) << "StartDevicePoll(): early out: output buffers unavailable";
1387 return true;
1388 }
1389
1390 // The MFC output queue may already have been started when we started
1391 // enqueuing MFC input buffers.
1392 struct v4l2_buffer qbuf;
1393 struct v4l2_plane qbuf_planes[2];
1394 if (!mfc_output_streamon_) {
1395 // Queue and start the MFC CAPTURE queue
1396 int buffer;
1397 __u32 type;
1398 memset(&qbuf, 0, sizeof(qbuf));
1399 memset(qbuf_planes, 0, sizeof(qbuf_planes));
1400 buffer = mfc_free_output_buffers_.back();
1401 MfcOutputRecord& output_record = mfc_output_buffer_map_[buffer];
1402 DCHECK(!output_record.at_device);
1403 DCHECK_EQ(output_record.input_id, -1);
1404 qbuf.index = buffer;
Pawel Osciak 2012/11/06 19:26:17 There is some code duplication here, this is basic
1405 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1406 qbuf.memory = V4L2_MEMORY_MMAP;
1407 qbuf.m.planes = qbuf_planes;
1408 qbuf.length = 2;
1409 errno = 0;
1410 if (ioctl(mfc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
1411 DPLOG(ERROR) << "StartDevicePoll(): ioctl() failed: VIDIOC_QBUF";
1412 NOTIFY_ERROR(PLATFORM_FAILURE);
1413 return false;
1414 }
1415 output_record.at_device = true;
1416 mfc_free_output_buffers_.pop_back();
1417 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1418 errno = 0;
1419 if (ioctl(mfc_fd_, VIDIOC_STREAMON, &type) != 0) {
Pawel Osciak 2012/11/06 19:26:17 Same here, streamon-related code is duplicated in
sheu 2012/11/08 21:16:31 Replaced with the IOCTL macro, and it's much small
1420 DPLOG(ERROR) << "StartDevicePoll(): ioctl() failed: VIDIOC_STREAMON";
1421 NOTIFY_ERROR(PLATFORM_FAILURE);
1422 return false;
1423 }
1424 mfc_output_streamon_ = true;
1425 }
1426
1427 // GSC output should not have been started before we received output buffers.
1428 DCHECK(!gsc_output_streamon_);
1429 {
1430 // Queue and start the GSC CAPTURE queue
1431 int buffer;
1432 __u32 type;
1433 memset(&qbuf, 0, sizeof(qbuf));
1434 memset(qbuf_planes, 0, sizeof(qbuf_planes));
1435 buffer = gsc_free_output_buffers_.back();
1436 GscOutputRecord& output_record = gsc_output_buffer_map_[buffer];
1437 DCHECK(!output_record.at_device);
1438 DCHECK(!output_record.at_client);
1439 qbuf.index = buffer;
1440 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1441 qbuf.memory = V4L2_MEMORY_DMABUF;
1442 qbuf.m.planes = qbuf_planes;
1443 qbuf.m.planes[0].m.fd = gsc_output_buffer_map_[buffer].fd;
1444 qbuf.length = 1;
1445 errno = 0;
1446 if (ioctl(gsc_fd_, VIDIOC_QBUF, &qbuf) != 0) {
1447 DPLOG(ERROR) << "StartDevicePoll(): ioctl() failed: VIDIOC_QBUF";
1448 NOTIFY_ERROR(PLATFORM_FAILURE);
1449 return false;
1450 }
1451 output_record.at_device = true;
1452 gsc_free_output_buffers_.pop_back();
1453 gsc_output_buffer_prepared_count_++;
1454 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1455 errno = 0;
1456 if (ioctl(gsc_fd_, VIDIOC_STREAMON, &type) != 0) {
1457 DPLOG(ERROR) << "StartDevicePoll(): ioctl() failed: VIDIOC_STREAMON";
1458 NOTIFY_ERROR(PLATFORM_FAILURE);
1459 return false;
1460 }
1461 gsc_output_streamon_ = true;
1462 }
1463
1464 if (!device_poll_thread_.Start()) {
1465 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start";
1466 NOTIFY_ERROR(PLATFORM_FAILURE);
1467 return false;
1468 }
1469 device_poll_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
1470 &ExynosVideoDecodeAccelerator::DevicePollLoop, base::Unretained(this)));
1471
1472 return true;
1473 }
1474
1475 bool ExynosVideoDecodeAccelerator::StopDevicePoll() {
1476 DVLOG(3) << "StopDevicePoll()";
1477 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
1478
1479 // Stop streaming.
1480 if (mfc_input_streamon_) {
1481 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
Pawel Osciak 2012/11/06 19:26:17 Wrap all ifs into DeviceStreamoff(fd, type) ?
sheu 2012/11/08 21:16:31 Replaced with the IOCTL macro, and it's much small
1482 errno = 0;
1483 if (ioctl(mfc_fd_, VIDIOC_STREAMOFF, &type) != 0) {
1484 DPLOG(ERROR) << "StopDevicePoll(): ioctl() failed: VIDIOC_STREAMOFF";
1485 NOTIFY_ERROR(PLATFORM_FAILURE);
1486 return false;
1487 }
1488 }
1489 mfc_input_streamon_ = false;
1490 if (mfc_output_streamon_) {
1491 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1492 errno = 0;
1493 if (ioctl(mfc_fd_, VIDIOC_STREAMOFF, &type) != 0) {
1494 DPLOG(ERROR) << "StopDevicePoll(): ioctl() failed: VIDIOC_STREAMOFF";
1495 NOTIFY_ERROR(PLATFORM_FAILURE);
1496 return false;
1497 }
1498 }
1499 mfc_output_streamon_ = false;
1500 if (gsc_input_streamon_) {
1501 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1502 errno = 0;
1503 if (ioctl(gsc_fd_, VIDIOC_STREAMOFF, &type) != 0) {
1504 DPLOG(ERROR) << "StopDevicePoll(): ioctl() failed: VIDIOC_STREAMOFF";
1505 NOTIFY_ERROR(PLATFORM_FAILURE);
1506 return false;
1507 }
1508 }
1509 gsc_input_streamon_ = false;
1510 if (gsc_output_streamon_) {
1511 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1512 errno = 0;
1513 if (ioctl(gsc_fd_, VIDIOC_STREAMOFF, &type) != 0) {
1514 DPLOG(ERROR) << "StopDevicePoll(): ioctl() failed: VIDIOC_STREAMOFF";
1515 NOTIFY_ERROR(PLATFORM_FAILURE);
1516 return false;
1517 }
1518 }
1519 gsc_output_streamon_ = false;
1520
1521 // Stopping streaming should cause DevicePollLoop() to exit. Join the thread.
1522 device_poll_thread_.Stop();
1523
1524 // Reset all our accounting info.
1525 mfc_input_ready_queue_.clear();
1526 mfc_free_input_buffers_.clear();
1527 DCHECK_EQ(mfc_input_buffer_count_,
1528 static_cast<int>(mfc_input_buffer_map_.size()));
1529 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) {
1530 mfc_free_input_buffers_.push_back(i);
1531 mfc_input_buffer_map_[i].at_device = false;
1532 mfc_input_buffer_map_[i].bytes_used = 0;
1533 mfc_input_buffer_map_[i].input_id = -1;
1534 }
1535 mfc_input_buffer_queued_count_ = 0;
1536 mfc_free_output_buffers_.clear();
1537 DCHECK_EQ(mfc_output_buffer_count_,
1538 static_cast<int>(mfc_output_buffer_map_.size()));
1539 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) {
1540 mfc_free_output_buffers_.push_back(i);
1541 mfc_output_buffer_map_[i].at_device = false;
1542 mfc_output_buffer_map_[i].input_id = -1;
1543 }
1544 mfc_output_gsc_input_queue_.clear();
1545 gsc_free_input_buffers_.clear();
1546 DCHECK_EQ(gsc_input_buffer_count_,
1547 static_cast<int>(gsc_input_buffer_map_.size()));
1548 for (size_t i = 0; i < gsc_input_buffer_map_.size(); ++i) {
1549 gsc_free_input_buffers_.push_back(i);
1550 gsc_input_buffer_map_[i].at_device = false;
1551 gsc_input_buffer_map_[i].mfc_output = -1;
1552 }
1553 gsc_free_output_buffers_.clear();
1554 DCHECK_EQ(gsc_output_buffer_count_,
1555 static_cast<int>(gsc_output_buffer_map_.size()));
1556 for (size_t i = 0; i < gsc_output_buffer_map_.size(); ++i) {
1557 // Only mark those free that aren't being held by the VDA.
1558 if (!gsc_output_buffer_map_[i].at_client) {
1559 gsc_free_output_buffers_.push_back(i);
1560 gsc_output_buffer_map_[i].at_device = false;
1561 }
1562 if (gsc_output_buffer_map_[i].egl_sync != EGL_NO_SYNC_KHR) {
1563 egl_destroy_sync_khr(egl_display_, gsc_output_buffer_map_[i].egl_sync);
1564 gsc_output_buffer_map_[i].egl_sync = EGL_NO_SYNC_KHR;
1565 }
1566 }
1567 gsc_output_buffer_prepared_count_ = 0;
1568 gsc_output_buffer_queued_count_ = 0;
1569
1570 DVLOG(3) << "StopDevicePoll(): device poll stopped";
1571 return true;
1572 }
1573
1574 void ExynosVideoDecodeAccelerator::DevicePollLoop() {
1575 DVLOG(3) << "DevicePollLoop()";
1576 DCHECK_EQ(device_poll_thread_.message_loop(), MessageLoop::current());
1577 // This routine just polls on the V4L2 devices, and notifies the
1578 // decoder_thread_ when processing needs to occur. The main loop will
1579 // terminate when we return an EPOLLERR to epoll() on the device file
1580 // descriptors, which should occur when the devices are sent VIDIOC_STREAMOFF
1581 // on their queues.
1582
1583 int ret;
1584 int epoll_fd = -1;
1585 file_util::ScopedFD epoll_fd_closer(&epoll_fd);
1586 struct epoll_event event;
1587 errno = 0;
1588 epoll_fd = epoll_create(2);
1589 if (epoll_fd == -1) {
1590 DPLOG(ERROR) << "DevicePollLoop(): epoll() failed";
1591 NOTIFY_ERROR(PLATFORM_FAILURE);
1592 return;
1593 }
1594 event.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLET;
1595 event.data.fd = mfc_fd_;
1596 errno = 0;
1597 ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mfc_fd_, &event);
1598 if (ret != 0) {
1599 DPLOG(ERROR) << "DevicePollLoop(): epoll_ctl() failed";
1600 NOTIFY_ERROR(PLATFORM_FAILURE);
1601 return;
1602 }
1603 event.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLET;
1604 event.data.fd = gsc_fd_;
1605 errno = 0;
1606 ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, gsc_fd_, &event);
1607 if (ret != 0) {
1608 DPLOG(ERROR) << "DevicePollLoop(): epoll_ctl() failed";
1609 NOTIFY_ERROR(PLATFORM_FAILURE);
1610 return;
1611 }
1612
1613 for (;;) {
1614 // We epoll() and wait for more interesting things to happen.
1615 // If we get a VIDIOC_STREAMOFF to the device, epoll() will return EPOLLERR,
1616 // and we know to exit the loop that way.
1617 DVLOG(3) << "DevicePollLoop(): epoll()";
1618 struct epoll_event event;
1619 do {
1620 errno = 0;
1621 ret = epoll_wait(epoll_fd, &event, 1, -1);
1622 } while (ret < 1 && errno == EINTR);
1623 if (ret == -1) {
1624 DPLOG(ERROR) << "DevicePollLoop(): epoll_wait() failed";
1625 NOTIFY_ERROR(PLATFORM_FAILURE);
1626 return;
1627 }
1628 if (event.data.fd != mfc_fd_ && event.data.fd != gsc_fd_) {
1629 DLOG(ERROR) << "DevicePollLoop(): epoll() returned unknown fd";
1630 NOTIFY_ERROR(PLATFORM_FAILURE);
1631 return;
1632 }
1633 if ((event.events & EPOLLERR) != 0) {
1634 DVLOG(2) << "DevicePollLoop(): epoll() returned EPOLERR for "
1635 << (event.data.fd == mfc_fd_ ? "mfc_fd_" : "gsc_fd_");
1636 // Not necessarily an error.
1637 return;
1638 }
1639 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
1640 &ExynosVideoDecodeAccelerator::ServiceDeviceTask,
1641 base::Unretained(this)));
1642 }
1643 }
1644
1645 void ExynosVideoDecodeAccelerator::NotifyError(Error error) {
1646 DVLOG(2) << "NotifyError()";
1647
1648 if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
1649 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1650 &ExynosVideoDecodeAccelerator::NotifyError, weak_this_, error));
1651 return;
1652 }
1653
1654 if (client_) {
1655 client_->NotifyError(error);
1656 client_ptr_factory_.InvalidateWeakPtrs();
1657 }
1658 }
1659
1660 void ExynosVideoDecodeAccelerator::SetDecoderState(State state) {
1661 DVLOG(3) << "SetDecoderState()";
1662
1663 // We can touch decoder_state_ only if this is the decoder thread or the
1664 // decoder thread isn't running.
1665 if (!decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()) {
1666 if (!decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
1667 &ExynosVideoDecodeAccelerator::SetDecoderState,
Pawel Osciak 2012/11/06 19:26:17 Indent error?
sheu 2012/11/08 21:16:31 Done.
1668 base::Unretained(this), state))) {
1669 decoder_state_ = state;
1670 }
1671 } else {
1672 decoder_state_ = state;
1673 }
1674 }
1675
1676 bool ExynosVideoDecodeAccelerator::CreateMfcInputBuffers() {
1677 DVLOG(3) << "CreateMfcInputBuffers()";
1678 // We always run this as we prepare to initialize.
1679 DCHECK_EQ(decoder_state_, kUninitialized);
1680 DCHECK(!mfc_input_streamon_);
1681 DCHECK_EQ(mfc_input_buffer_count_, 0);
1682
1683 __u32 pixelformat = 0;
1684 if (video_profile_ >= media::H264PROFILE_MIN &&
1685 video_profile_ <= media::H264PROFILE_MAX) {
1686 pixelformat = V4L2_PIX_FMT_H264;
1687 } else if (video_profile_ >= media::VP8PROFILE_MIN &&
1688 video_profile_ <= media::VP8PROFILE_MAX) {
1689 pixelformat = V4L2_PIX_FMT_VP8;
1690 } else {
1691 NOTREACHED();
1692 }
1693
1694 int ret;
1695 struct v4l2_format format;
1696 memset(&format, 0, sizeof(format));
1697 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1698 format.fmt.pix_mp.pixelformat = pixelformat;
1699 format.fmt.pix_mp.plane_fmt[0].sizeimage = kMfcInputBufferMaxSize;
1700 format.fmt.pix_mp.num_planes = 1;
1701 errno = 0;
1702 ret = ioctl(mfc_fd_, VIDIOC_S_FMT, &format);
1703 if (ret != 0) {
1704 DPLOG(ERROR) << "CreateMfcInputBuffers(): ioctl() failed: VIDIOC_S_FMT";
1705 return false;
1706 }
1707
1708 struct v4l2_requestbuffers reqbufs;
1709 memset(&reqbufs, 0, sizeof(reqbufs));
1710 reqbufs.count = kMfcInputBufferCount;
1711 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1712 reqbufs.memory = V4L2_MEMORY_MMAP;
1713 errno = 0;
1714 ret = ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1715 if (ret != 0) {
1716 DPLOG(ERROR) << "CreateMfcInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1717 return false;
1718 }
1719 mfc_input_buffer_count_ = reqbufs.count;
1720 mfc_input_buffer_map_.resize(mfc_input_buffer_count_);
1721 for (int i = 0; i < mfc_input_buffer_count_; ++i) {
Pawel Osciak 2012/11/06 19:26:17 This whole sequence could be extracted into a func
sheu 2012/11/08 21:16:31 The *_buffer_map_ map that we're updating is diffe
Pawel Osciak 2012/11/13 18:10:39 I'd really suggest making them the same class. The
1722 mfc_free_input_buffers_.push_back(i);
1723
1724 // Query for the MEMORY_MMAP pointer.
1725 struct v4l2_plane planes[1];
1726 struct v4l2_buffer buffer;
1727 memset(&buffer, 0, sizeof(buffer));
1728 memset(planes, 0, sizeof(planes));
1729 buffer.index = i;
1730 buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1731 buffer.memory = V4L2_MEMORY_MMAP;
1732 buffer.m.planes = planes;
1733 buffer.length = 1;
1734 errno = 0;
1735 ret = ioctl(mfc_fd_, VIDIOC_QUERYBUF, &buffer);
1736 if (ret != 0) {
1737 DPLOG(ERROR) << "CreateMfcInputBuffers(): "
1738 << "ioctl() failed: VIDIOC_QUERYBUF";
1739 return false;
1740 }
1741 errno = 0;
1742 void* offset = mmap(NULL, buffer.m.planes[0].length,
Pawel Osciak 2012/11/06 19:26:17 Nit: calling this an "offset" is a bit misleading,
sheu 2012/11/08 21:16:31 Done.
1743 PROT_READ | PROT_WRITE, MAP_SHARED, mfc_fd_,
1744 buffer.m.planes[0].m.mem_offset);
1745 if (offset == MAP_FAILED) {
1746 DPLOG(ERROR) << "CreateMfcInputBuffers(): mmap() failed";
1747 return false;
1748 }
1749 mfc_input_buffer_map_[i].offset = offset;
1750 mfc_input_buffer_map_[i].length = buffer.m.planes[0].length;
1751 }
1752
1753 return true;
1754 }
1755
1756 bool ExynosVideoDecodeAccelerator::CreateMfcOutputBuffers() {
1757 DVLOG(3) << "CreateMfcOutputBuffers()";
1758 DCHECK_EQ(decoder_state_, kInitialized);
1759 DCHECK(!mfc_output_streamon_);
1760 DCHECK_EQ(mfc_output_buffer_count_, 0);
1761
1762 // Number of MFC output buffers we need.
1763 int ret;
1764 struct v4l2_control ctrl;
1765 memset(&ctrl, 0, sizeof(ctrl));
1766 ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
1767 errno = 0;
1768 ret = ioctl(mfc_fd_, VIDIOC_G_CTRL, &ctrl);
1769 if (ret != 0) {
1770 DPLOG(ERROR) << "CreateMfcOutputBuffers(): ioctl() failed: VIDIOC_G_CTRL";
1771 return false;
1772 }
1773
1774 // Output format setup in Initialize().
1775
1776 // Allocate the output buffers.
1777 struct v4l2_requestbuffers reqbufs;
1778 memset(&reqbufs, 0, sizeof(reqbufs));
1779 reqbufs.count = ctrl.value + kMfcOutputBufferExtraCount;
1780 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1781 reqbufs.memory = V4L2_MEMORY_MMAP;
1782 errno = 0;
1783 ret = ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1784 if (ret != 0) {
1785 DPLOG(ERROR) << "CreateMfcOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1786 return false;
1787 }
1788
1789 // Fill our free-buffers list, and create DMABUFs from them.
1790 mfc_output_buffer_count_ = reqbufs.count;
1791 mfc_output_buffer_map_.resize(mfc_output_buffer_count_);
1792 for (int i = 0; i < mfc_output_buffer_count_; ++i) {
1793 mfc_free_output_buffers_.push_back(i);
1794
1795 // Query for the MEMORY_MMAP pointer.
1796 struct v4l2_plane planes[2];
1797 struct v4l2_buffer buffer;
1798 memset(&buffer, 0, sizeof(buffer));
1799 memset(planes, 0, sizeof(planes));
1800 buffer.index = i;
1801 buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1802 buffer.memory = V4L2_MEMORY_MMAP;
1803 buffer.m.planes = planes;
1804 buffer.length = 2;
1805 errno = 0;
1806 ret = ioctl(mfc_fd_, VIDIOC_QUERYBUF, &buffer);
1807 if (ret != 0) {
1808 DPLOG(ERROR) << "CreateMfcOutputBuffers(): "
1809 << "ioctl() failed: VIDIOC_QUERYBUF";
1810 return false;
1811 }
1812
1813 // Get their user memory for GSC input.
1814 for (int j = 0; j < 2; ++j) {
1815 errno = 0;
1816 void* offset = mmap(NULL, buffer.m.planes[j].length,
1817 PROT_READ | PROT_WRITE, MAP_SHARED, mfc_fd_,
1818 buffer.m.planes[j].m.mem_offset);
1819 if (offset == MAP_FAILED) {
1820 DPLOG(ERROR) << "CreateMfcInputBuffers(): mmap() failed";
1821 return false;
1822 }
1823 mfc_output_buffer_map_[i].offset[j] = offset;
1824 mfc_output_buffer_map_[i].length[j] = buffer.m.planes[j].length;
1825 }
1826 }
1827
1828 return true;
1829 }
1830
1831 bool ExynosVideoDecodeAccelerator::CreateGscInputBuffers() {
1832 DVLOG(3) << "CreateGscInputBuffers()";
1833 DCHECK_EQ(decoder_state_, kInitialized);
1834 DCHECK(!gsc_input_streamon_);
1835 DCHECK_EQ(gsc_input_buffer_count_, 0);
1836
1837 int ret;
1838 struct v4l2_format format;
1839 memset(&format, 0, sizeof(format));
1840 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1841 format.fmt.pix_mp.width = frame_buffer_size_.width();
1842 format.fmt.pix_mp.height = frame_buffer_size_.height();
1843 DCHECK_EQ(mfc_output_buffer_pixelformat_, V4L2_PIX_FMT_NV12MT_16X16);
1844 format.fmt.pix_mp.pixelformat = mfc_output_buffer_pixelformat_;
1845 format.fmt.pix_mp.plane_fmt[0].sizeimage = mfc_output_buffer_size_[0];
1846 format.fmt.pix_mp.plane_fmt[1].sizeimage = mfc_output_buffer_size_[1];
1847 // NV12MT_16X16 is a tiled format for which bytesperline doesn't make too much
1848 // sense. Convention seems to be to assume 8bpp for these tiled formats.
1849 format.fmt.pix_mp.plane_fmt[0].bytesperline = frame_buffer_size_.width();
1850 format.fmt.pix_mp.plane_fmt[1].bytesperline = frame_buffer_size_.width();
1851 format.fmt.pix_mp.num_planes = 2;
1852 errno = 0;
1853 ret = ioctl(gsc_fd_, VIDIOC_S_FMT, &format);
1854 if (ret != 0) {
1855 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_S_FMT";
1856 return false;
1857 }
1858
1859 struct v4l2_control control;
1860 memset(&control, 0, sizeof(control));
Pawel Osciak 2012/11/06 19:26:17 Wrap all of those sequences into DeviceSetCtrl(fd,
sheu 2012/11/08 21:16:31 Possibly, but I replaced these with the IOCTL macr
1861 control.id = V4L2_CID_ROTATE;
1862 control.value = 0;
1863 errno = 0;
1864 ret = ioctl(gsc_fd_, VIDIOC_S_CTRL, &control);
1865 if (ret != 0) {
1866 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_S_CTRL";
1867 return false;
1868 }
1869
1870 memset(&control, 0, sizeof(control));
1871 control.id = V4L2_CID_HFLIP;
1872 control.value = 0;
1873 errno = 0;
1874 ret = ioctl(gsc_fd_, VIDIOC_S_CTRL, &control);
1875 if (ret != 0) {
1876 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_S_CTRL";
1877 return false;
1878 }
1879
1880 memset(&control, 0, sizeof(control));
1881 control.id = V4L2_CID_VFLIP;
1882 control.value = 0;
1883 errno = 0;
1884 ret = ioctl(gsc_fd_, VIDIOC_S_CTRL, &control);
1885 if (ret != 0) {
1886 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_S_CTRL";
1887 return false;
1888 }
1889
1890 memset(&control, 0, sizeof(control));
1891 control.id = V4L2_CID_GLOBAL_ALPHA;
1892 control.value = 255;
1893 errno = 0;
1894 ret = ioctl(gsc_fd_, VIDIOC_S_CTRL, &control);
1895 if (ret != 0) {
1896 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_S_CTRL";
1897 return false;
1898 }
1899
1900 struct v4l2_requestbuffers reqbufs;
1901 memset(&reqbufs, 0, sizeof(reqbufs));
1902 reqbufs.count = kGscOutputBufferCount;
1903 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1904 reqbufs.memory = V4L2_MEMORY_USERPTR;
1905 errno = 0;
1906 ret = ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1907 if (ret != 0 ) {
1908 DPLOG(ERROR) << "CreateGscInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1909 return false;
1910 }
1911
1912 gsc_input_buffer_count_ = reqbufs.count;
1913 gsc_input_buffer_map_.resize(gsc_input_buffer_count_);
1914 for (int i = 0; i < gsc_input_buffer_count_; ++i) {
1915 gsc_free_input_buffers_.push_back(i);
1916 gsc_input_buffer_map_[i].mfc_output = -1;
1917 }
1918
1919 return true;
1920 }
1921
1922 bool ExynosVideoDecodeAccelerator::CreateGscOutputBuffers() {
1923 DVLOG(3) << "CreateGscOutputBuffers()";
1924 DCHECK_EQ(decoder_state_, kInitialized);
1925 DCHECK(!gsc_output_streamon_);
1926 DCHECK_EQ(gsc_output_buffer_count_, 0);
1927
1928 // GSC outputs into the EGLImages we create from the textures we are
1929 // assigned. Assume RGBA8888 format.
1930 int ret;
1931 struct v4l2_format format;
1932 memset(&format, 0, sizeof(format));
1933 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1934 format.fmt.pix_mp.width = frame_buffer_size_.width();
1935 format.fmt.pix_mp.height = frame_buffer_size_.height();
1936 format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_RGB32;
1937 format.fmt.pix_mp.plane_fmt[0].sizeimage =
1938 frame_buffer_size_.width() * frame_buffer_size_.height() * 4;
1939 format.fmt.pix_mp.plane_fmt[0].bytesperline = frame_buffer_size_.width() * 4;
1940 format.fmt.pix_mp.num_planes = 1;
1941 errno = 0;
1942 ret = ioctl(gsc_fd_, VIDIOC_S_FMT, &format);
1943 if (ret != 0) {
1944 DPLOG(ERROR) << "CreateGscOutputBuffers(): ioctl() failed: VIDIOC_S_FMT";
1945 return false;
1946 }
1947
1948 struct v4l2_requestbuffers reqbufs;
1949 memset(&reqbufs, 0, sizeof(reqbufs));
1950 reqbufs.count = kGscOutputBufferCount;
1951 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
1952 reqbufs.memory = V4L2_MEMORY_DMABUF;
1953 errno = 0;
1954 ret = ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
1955 if (ret != 0) {
1956 DPLOG(ERROR) << "CreateGscOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1957 return false;
1958 }
1959
1960 // We don't actually fill in the freelist or the map here. That happens once
1961 // we have actual usable buffers, after AssignPictureBuffers();
1962 gsc_output_buffer_count_ = reqbufs.count;
1963 gsc_output_buffer_map_.resize(gsc_output_buffer_count_);
1964
1965 DVLOG(3) << "CreateGscOutputBuffers(): ProvidePictureBuffers(): "
1966 << "buffer_count=" << gsc_output_buffer_count_
1967 << ", width=" << frame_buffer_size_.width()
1968 << ", height=" << frame_buffer_size_.height();
1969 child_message_loop_proxy_->PostTask(FROM_HERE, base::Bind(
1970 &Client::ProvidePictureBuffers, client_, gsc_output_buffer_count_,
1971 gfx::Size(frame_buffer_size_.width(), frame_buffer_size_.height()),
1972 GL_TEXTURE_2D));
1973
1974 return true;
1975 }
1976
1977 void ExynosVideoDecodeAccelerator::DestroyMfcInputBuffers() {
Pawel Osciak 2012/11/06 19:26:17 DestroyDeviceBuffers(fd, type, memory, num_planes)
sheu 2012/11/08 21:16:31 Unfortunately each device buffer type has its own
Pawel Osciak 2012/11/13 18:10:39 Please see comment at l.1721.
1978 DVLOG(3) << "DestroyMfcInputBuffers()";
1979 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
1980 DCHECK(!mfc_input_streamon_);
1981
1982 for (size_t i = 0; i < mfc_input_buffer_map_.size(); ++i) {
1983 if (mfc_input_buffer_map_[i].offset != NULL) {
1984 munmap(mfc_input_buffer_map_[i].offset,
1985 mfc_input_buffer_map_[i].length);
1986 }
1987 }
1988
1989 int ret;
1990 struct v4l2_requestbuffers reqbufs;
1991 memset(&reqbufs, 0, sizeof(reqbufs));
1992 reqbufs.count = 0;
1993 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
1994 reqbufs.memory = V4L2_MEMORY_MMAP;
1995 errno = 0;
1996 ret = ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
1997 if (ret != 0)
1998 DPLOG(ERROR) << "DestroyMfcInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
1999
2000 mfc_input_buffer_map_.clear();
2001 mfc_free_input_buffers_.clear();
2002 mfc_input_buffer_count_ = 0;
2003 }
2004
2005 void ExynosVideoDecodeAccelerator::DestroyMfcOutputBuffers() {
2006 DVLOG(3) << "DestroyMfcOutputBuffers()";
2007 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
2008 DCHECK(!mfc_output_streamon_);
2009
2010 for (size_t i = 0; i < mfc_output_buffer_map_.size(); ++i) {
2011 if (mfc_output_buffer_map_[i].offset[0] != NULL)
2012 munmap(mfc_output_buffer_map_[i].offset[0],
2013 mfc_output_buffer_map_[i].length[0]);
2014 if (mfc_output_buffer_map_[i].offset[1] != NULL)
2015 munmap(mfc_output_buffer_map_[i].offset[1],
2016 mfc_output_buffer_map_[i].length[1]);
2017 }
2018
2019 int ret;
2020 struct v4l2_requestbuffers reqbufs;
2021 memset(&reqbufs, 0, sizeof(reqbufs));
2022 reqbufs.count = 0;
2023 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
2024 reqbufs.memory = V4L2_MEMORY_MMAP;
2025 errno = 0;
2026 ret = ioctl(mfc_fd_, VIDIOC_REQBUFS, &reqbufs);
2027 if (ret != 0)
2028 DPLOG(ERROR) << "DestroyMfcOutputBuffers() ioctl() failed: VIDIOC_REQBUFS";
2029
2030 mfc_output_buffer_map_.clear();
2031 mfc_free_output_buffers_.clear();
2032 mfc_output_buffer_count_ = 0;
2033 }
2034
2035 void ExynosVideoDecodeAccelerator::DestroyGscInputBuffers() {
2036 DVLOG(3) << "DestroyGscInputBuffers()";
2037 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
2038 DCHECK(!gsc_input_streamon_);
2039
2040 int ret;
2041 struct v4l2_requestbuffers reqbufs;
2042 memset(&reqbufs, 0, sizeof(reqbufs));
2043 reqbufs.count = 0;
2044 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
2045 reqbufs.memory = V4L2_MEMORY_DMABUF;
2046 errno = 0;
2047 ret = ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
2048 if (ret != 0)
2049 DPLOG(ERROR) << "DestroyGscInputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
2050
2051 gsc_input_buffer_map_.clear();
2052 gsc_free_input_buffers_.clear();
2053 gsc_input_buffer_count_ = 0;
2054 }
2055
2056 void ExynosVideoDecodeAccelerator::DestroyGscOutputBuffers() {
2057 DVLOG(3) << "DestroyGscOutputBuffers()";
2058 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
2059 DCHECK(!gsc_output_streamon_);
2060
2061 if (gsc_output_buffer_map_.size() != 0) {
2062 if (!make_context_current_.Run())
2063 DLOG(ERROR) << "DestroyGscOutputBuffers(): "
2064 << "could not make context current";
2065
2066 size_t i = 0;
2067 do {
2068 GscOutputRecord& output_record = gsc_output_buffer_map_[i];
2069 if (output_record.fd != -1)
2070 close(output_record.fd);
2071 if (output_record.egl_image != EGL_NO_IMAGE_KHR)
2072 eglDestroyImageKHR(egl_display_, output_record.egl_image);
2073 if (output_record.egl_sync != EGL_NO_SYNC_KHR)
2074 egl_destroy_sync_khr(egl_display_, output_record.egl_sync);
2075 if (client_)
2076 client_->DismissPictureBuffer(output_record.picture_id);
2077 ++i;
2078 } while (i < gsc_output_buffer_map_.size());
2079 }
2080
2081 int ret;
2082 struct v4l2_requestbuffers reqbufs;
2083 memset(&reqbufs, 0, sizeof(reqbufs));
2084 reqbufs.count = 0;
2085 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
2086 reqbufs.memory = V4L2_MEMORY_DMABUF;
2087 errno = 0;
2088 ret = ioctl(gsc_fd_, VIDIOC_REQBUFS, &reqbufs);
2089 if (ret != 0)
2090 DPLOG(ERROR) << "DestroyGscOutputBuffers(): ioctl() failed: VIDIOC_REQBUFS";
2091
2092 gsc_output_buffer_map_.clear();
2093 gsc_free_output_buffers_.clear();
2094 gsc_output_buffer_count_ = 0;
2095 }
2096
2097 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698