Index: content/common/gpu/media/omx_video_decode_accelerator.cc |
diff --git a/content/common/gpu/media/omx_video_decode_accelerator.cc b/content/common/gpu/media/omx_video_decode_accelerator.cc |
index a72756012b6c73c58183afb348725da7a437cd6e..fcbd3c751873370762b53c3314db25e2d57baf28 100644 |
--- a/content/common/gpu/media/omx_video_decode_accelerator.cc |
+++ b/content/common/gpu/media/omx_video_decode_accelerator.cc |
@@ -21,6 +21,11 @@ typedef std::pair<scoped_ptr<base::SharedMemory>, int32> SharedMemoryAndId; |
enum { kNumPictureBuffers = 8 }; |
+// Delay between polling for texture sync status. 5ms feels like a good |
+// compromise, allowing some decoding ahead (up to 3 frames/vsync) to compensate |
+// for more difficult frames. |
+enum { kSyncPollDelayMs = 5 }; |
+ |
void* omx_handle = NULL; |
typedef OMX_ERRORTYPE (*OMXInit)(); |
@@ -36,6 +41,10 @@ OMXGetComponentsOfRole omx_get_components_of_role = NULL; |
OMXFreeHandle omx_free_handle = NULL; |
OMXDeinit omx_deinit = NULL; |
+static PFNEGLCREATESYNCKHRPROC egl_create_sync_khr = NULL; |
+static PFNEGLGETSYNCATTRIBKHRPROC egl_get_sync_attrib_khr = NULL; |
+static PFNEGLDESTROYSYNCKHRPROC egl_destroy_sync_khr = NULL; |
+ |
// Maps h264-related Profile enum values to OMX_VIDEO_AVCPROFILETYPE values. |
static OMX_U32 MapH264ProfileToOMXAVCProfile(uint32 profile) { |
switch (profile) { |
@@ -89,6 +98,41 @@ static OMX_U32 MapH264ProfileToOMXAVCProfile(uint32 profile) { |
// static |
bool OmxVideoDecodeAccelerator::pre_sandbox_init_done_ = false; |
+class OmxVideoDecodeAccelerator::PictureSyncObject { |
+ public: |
+ // Create a sync object and insert into the GPU command stream. |
+ PictureSyncObject(EGLDisplay egl_display); |
+ ~PictureSyncObject(); |
+ |
+ bool IsSynced(); |
+ |
+ private: |
+ EGLSyncKHR egl_sync_obj_; |
+ EGLDisplay egl_display_; |
+}; |
+ |
+OmxVideoDecodeAccelerator::PictureSyncObject::PictureSyncObject( |
+ EGLDisplay egl_display) |
+ : egl_display_(egl_display) { |
+ DCHECK(egl_display_ != EGL_NO_DISPLAY); |
+ |
+ egl_sync_obj_ = egl_create_sync_khr(egl_display_, EGL_SYNC_FENCE_KHR, NULL); |
+ DCHECK_NE(egl_sync_obj_, EGL_NO_SYNC_KHR); |
+} |
+ |
+OmxVideoDecodeAccelerator::PictureSyncObject::~PictureSyncObject() { |
+ egl_destroy_sync_khr(egl_display_, egl_sync_obj_); |
+} |
+ |
+bool OmxVideoDecodeAccelerator::PictureSyncObject::IsSynced() { |
+ EGLint value = EGL_UNSIGNALED_KHR; |
+ EGLBoolean ret = egl_get_sync_attrib_khr( |
+ egl_display_, egl_sync_obj_, EGL_SYNC_STATUS_KHR, &value); |
+ DCHECK(ret) << "Failed getting sync object state."; |
+ |
+ return value == EGL_SIGNALED_KHR; |
+} |
+ |
OmxVideoDecodeAccelerator::OmxVideoDecodeAccelerator( |
EGLDisplay egl_display, EGLContext egl_context, |
media::VideoDecodeAccelerator::Client* client, |
@@ -387,8 +431,38 @@ void OmxVideoDecodeAccelerator::AssignPictureBuffers( |
} |
void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { |
+ DCHECK_EQ(message_loop_, MessageLoop::current()); |
TRACE_EVENT1("Video Decoder", "OVDA::ReusePictureBuffer", |
"Picture id", picture_buffer_id); |
+ scoped_ptr<PictureSyncObject> egl_sync_obj( |
+ new PictureSyncObject(egl_display_)); |
+ |
+ // Start checking sync status periodically. |
+ CheckPictureStatus(picture_buffer_id, egl_sync_obj.Pass()); |
+} |
+ |
+void OmxVideoDecodeAccelerator::CheckPictureStatus( |
+ int32 picture_buffer_id, |
+ scoped_ptr<PictureSyncObject> egl_sync_obj) { |
+ DCHECK_EQ(message_loop_, MessageLoop::current()); |
+ |
+ // It's possible for this task to never run if the message loop is |
+ // stopped. In that case we may never call QueuePictureBuffer(). |
+ // This is fine though, because all pictures, irrespective of their state, |
+ // are in pictures_ map and that's what will be used to do the clean up. |
+ if (!egl_sync_obj->IsSynced()) { |
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind( |
+ &OmxVideoDecodeAccelerator::CheckPictureStatus, weak_this_, |
+ picture_buffer_id, base::Passed(&egl_sync_obj)), |
+ base::TimeDelta::FromMilliseconds(kSyncPollDelayMs)); |
+ return; |
+ } |
+ |
+ // Synced successfully. Queue the buffer for reuse. |
+ QueuePictureBuffer(picture_buffer_id); |
+} |
+ |
+void OmxVideoDecodeAccelerator::QueuePictureBuffer(int32 picture_buffer_id) { |
DCHECK_EQ(message_loop_, MessageLoop::current()); |
// During port-flushing, do not call OMX FillThisBuffer. |
@@ -397,7 +471,11 @@ void OmxVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { |
return; |
} |
- RETURN_ON_FAILURE(CanFillBuffer(), "Can't fill buffer", ILLEGAL_STATE,); |
+ // We might have started destroying while waiting for the picture. It's safe |
+ // to drop it here, because we will free all the pictures regardless of their |
+ // state using the pictures_ map. |
+ if (!CanFillBuffer()) |
+ return; |
OutputPictureById::iterator it = pictures_.find(picture_buffer_id); |
RETURN_ON_FAILURE(it != pictures_.end(), |
@@ -1091,8 +1169,17 @@ bool OmxVideoDecodeAccelerator::PostSandboxInitialization() { |
reinterpret_cast<OMXFreeHandle>(dlsym(omx_handle, "OMX_FreeHandle")); |
omx_deinit = |
reinterpret_cast<OMXDeinit>(dlsym(omx_handle, "OMX_Deinit")); |
+ |
+ egl_create_sync_khr = reinterpret_cast<PFNEGLCREATESYNCKHRPROC>( |
+ eglGetProcAddress("eglCreateSyncKHR")); |
+ egl_get_sync_attrib_khr = reinterpret_cast<PFNEGLGETSYNCATTRIBKHRPROC>( |
+ eglGetProcAddress("eglGetSyncAttribKHR")); |
+ egl_destroy_sync_khr = reinterpret_cast<PFNEGLDESTROYSYNCKHRPROC>( |
+ eglGetProcAddress("eglDestroySyncKHR")); |
+ |
return (omx_init && omx_gethandle && omx_get_components_of_role && |
- omx_free_handle && omx_deinit); |
+ omx_free_handle && omx_deinit && egl_create_sync_khr && |
+ egl_get_sync_attrib_khr && egl_destroy_sync_khr); |
} |
// static |