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