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