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