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