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