| /* |
| * # Copyright 2020 Google LLC |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/allocators/gstdmabuf.h> |
| #include <gst/gl/gstglfuncs.h> |
| |
| #include "gstglmemorydma.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_MEMORY_DMA); |
| #define GST_CAT_DEFAULT GST_CAT_GL_MEMORY_DMA |
| |
| #define parent_class gst_gl_memory_dma_allocator_parent_class |
| G_DEFINE_TYPE (GstGLMemoryDmaAllocator, gst_gl_memory_dma_allocator, |
| GST_TYPE_GL_MEMORY_ALLOCATOR); |
| |
| static void gst_gl_memory_dma_allocator_finalize (GObject * object); |
| static GstGLBaseMemory * _gl_mem_dma_alloc |
| (GstGLBaseMemoryAllocator * allocator, GstGLAllocationParams * params); |
| static gboolean _gl_mem_dma_create (GstGLBaseMemory * glmem, GError ** error); |
| static void _gl_mem_dma_destroy (GstGLBaseMemory * glmem); |
| static GstMemory * _mem_alloc (GstAllocator * allocator, gsize size, |
| GstAllocationParams * params); |
| |
| |
| static void |
| gst_gl_memory_dma_allocator_class_init (GstGLMemoryDmaAllocatorClass * klass) |
| { |
| GST_ALLOCATOR_CLASS (klass)->alloc = _mem_alloc; |
| |
| GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->alloc = _gl_mem_dma_alloc; |
| GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->create = _gl_mem_dma_create; |
| GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (klass)->destroy = _gl_mem_dma_destroy; |
| |
| G_OBJECT_CLASS (klass)->finalize = gst_gl_memory_dma_allocator_finalize; |
| |
| GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_MEMORY_DMA, "glmemorydma", 0, |
| "OpenGL DMA memory"); |
| } |
| |
| static void |
| gst_gl_memory_dma_allocator_init (GstGLMemoryDmaAllocator * alloc) |
| { |
| GST_DEBUG_OBJECT (alloc, "init"); |
| GST_ALLOCATOR (alloc)->mem_type = GST_GL_MEMORY_DMA_ALLOCATOR_NAME; |
| GST_OBJECT_FLAG_SET (GST_OBJECT (alloc), GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); |
| |
| /* ION allocator is in the "coral" plugin, load it. */ |
| alloc->plugin = gst_plugin_load_by_name ("coral"); |
| if (!alloc->plugin) { |
| GST_ERROR_OBJECT (alloc, "coral plugin not found"); |
| return; |
| } |
| |
| alloc->ion_allocator = gst_allocator_find ("ion"); |
| if (!alloc->ion_allocator) { |
| GST_ERROR_OBJECT (alloc, "ION allocator not found"); |
| return; |
| } |
| |
| GST_INFO_OBJECT (alloc, "ION allocator loaded"); |
| } |
| |
| static void |
| gst_gl_memory_dma_allocator_finalize (GObject * object) |
| { |
| GstGLMemoryDmaAllocator *alloc = GST_GL_MEMORY_DMA_ALLOCATOR (object); |
| |
| GST_DEBUG_OBJECT (alloc, "finalize"); |
| |
| if (alloc->ion_allocator) { |
| gst_object_unref (alloc->ion_allocator); |
| alloc->ion_allocator = NULL; |
| } |
| |
| if (alloc->plugin) { |
| gst_object_unref (alloc->plugin); |
| alloc->plugin = NULL; |
| } |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| |
| static GstGLBaseMemory * |
| _gl_mem_dma_alloc (GstGLBaseMemoryAllocator * allocator, |
| GstGLAllocationParams * alloc_params) |
| { |
| GstGLMemoryDmaAllocator *alloc; |
| GstGLMemoryDMA *dmamem; |
| GstGLVideoAllocationParams *params; |
| gsize size; |
| |
| alloc = GST_GL_MEMORY_DMA_ALLOCATOR (allocator); |
| params = (GstGLVideoAllocationParams *) alloc_params; |
| |
| g_return_val_if_fail (params->parent.alloc_flags & |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, NULL); |
| |
| dmamem = g_new0 (GstGLMemoryDMA, 1); |
| dmamem->params = params->parent.alloc_params; |
| |
| /* GstVideoInfo defaults to 4 b stride align for RGB/BGR but we want to |
| * pack the buffer as tightly as possible (i.e. stride == width * bpp). |
| */ |
| switch (params->v_info->finfo->format) { |
| case GST_VIDEO_FORMAT_RGB: |
| case GST_VIDEO_FORMAT_BGR: |
| params->v_info->stride[0] = params->v_info->width * 3; |
| params->v_info->size = params->v_info->stride[0] * params->v_info->height; |
| break; |
| default: |
| break; |
| } |
| |
| size = gst_gl_get_plane_data_size (params->v_info, params->valign, |
| params->plane); |
| dmamem->dma = gst_allocator_alloc (alloc->ion_allocator, size, |
| dmamem->params); |
| |
| if (!dmamem->dma) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY_DMA, "ION alloc of %zu b failed", size); |
| g_free (dmamem); |
| return NULL; |
| } |
| |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "ION alloc %zu b", size); |
| |
| gst_gl_memory_init (GST_GL_MEMORY_CAST (dmamem), GST_ALLOCATOR_CAST (alloc), |
| NULL, params->parent.context, params->target, params->tex_format, |
| params->parent.alloc_params, params->v_info, params->plane, |
| params->valign, params->parent.user_data, params->parent.notify); |
| |
| return GST_GL_BASE_MEMORY_CAST (dmamem); |
| } |
| |
| static gboolean |
| _gl_mem_dma_create (GstGLBaseMemory * glmem, GError ** error) |
| { |
| GstGLMemoryDMA *dmamem; |
| const GstGLFuncs *gl; |
| gint fd; |
| guint tex_id; |
| |
| g_return_val_if_fail (glmem, FALSE); |
| g_return_val_if_fail (glmem->context, FALSE); |
| gl = glmem->context->gl_vtable; |
| |
| if (!GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->create (glmem, |
| error)) { |
| return FALSE; |
| } |
| |
| dmamem = GST_GL_MEMORY_DMA_CAST (glmem); |
| fd = gst_dmabuf_memory_get_fd (dmamem->dma); |
| tex_id = GST_GL_MEMORY_CAST (&glmem->mem)->tex_id; |
| |
| dmamem->image = gst_egl_image_from_dmabuf (glmem->context, fd, |
| &GST_GL_MEMORY_CAST (glmem)->info, 0, 0); |
| if (!dmamem->image) { |
| GST_CAT_ERROR (GST_CAT_GL_MEMORY_DMA, "gst_egl_image_from_dmabuf failed"); |
| return FALSE; |
| } |
| |
| gl->ActiveTexture (GL_TEXTURE0); |
| gl->BindTexture (GL_TEXTURE_2D, tex_id); |
| gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, gst_egl_image_get_image ( |
| dmamem->image)); |
| |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "created dma mem %p fd %d tex_id %u", |
| glmem, fd, tex_id); |
| |
| return TRUE; |
| } |
| |
| static void |
| _gl_mem_dma_destroy (GstGLBaseMemory * glmem) |
| { |
| GstGLMemoryDMA *dmamem; |
| |
| GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->destroy (glmem); |
| |
| dmamem = GST_GL_MEMORY_DMA_CAST (glmem); |
| |
| if (dmamem->image) { |
| gst_egl_image_unref (dmamem->image); |
| dmamem->image = NULL; |
| } |
| |
| if (dmamem->dma) { |
| gst_memory_unref (dmamem->dma); |
| dmamem->dma = NULL; |
| } |
| |
| /* Base class frees dmamem. */ |
| GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->destroy (glmem); |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "destroyed dma mem %p", dmamem); |
| } |
| |
| static GstMemory * |
| _mem_alloc (GstAllocator * allocator, gsize size, |
| GstAllocationParams * params) |
| { |
| /* Only used as an init check, does not support actual GL alloc. */ |
| |
| GstGLMemoryDmaAllocator *alloc; |
| |
| alloc = GST_GL_MEMORY_DMA_ALLOCATOR (allocator); |
| |
| g_return_val_if_fail (alloc->ion_allocator, NULL); |
| |
| return gst_allocator_alloc (alloc->ion_allocator, size, params); |
| } |
| |
| static EGLDisplay |
| _get_egl_display (GstGLContext * context) |
| { |
| EGLDisplay egl_display; |
| GstGLDisplayEGL *display_egl; |
| if (!context->display) { |
| return EGL_NO_DISPLAY; |
| } |
| display_egl = gst_gl_display_egl_from_gl_display (context->display); |
| egl_display = |
| (EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (display_egl)); |
| gst_object_unref (display_egl); |
| return egl_display; |
| } |
| |
| static EGLSyncKHR |
| _eglCreateSyncKHR (GstGLContext * context) |
| { |
| GstClockTime ts; |
| EGLSyncKHR sync; |
| EGLSyncKHR (*gst_eglCreateSyncKHR) (EGLDisplay dpy, EGLenum type, |
| const EGLAttrib * attrib_list); |
| EGLDisplay dpy = _get_egl_display (context); |
| g_assert (dpy != EGL_NO_DISPLAY); |
| |
| ts = gst_util_get_timestamp (); |
| gst_eglCreateSyncKHR = gst_gl_context_get_proc_address (context, |
| "eglCreateSyncKHR"); |
| sync = gst_eglCreateSyncKHR (dpy, EGL_SYNC_FENCE_KHR, NULL); |
| GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "created egl sync object %p", sync); |
| ts = gst_util_get_timestamp () - ts; |
| GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "create %.2g ms",(double) ts / GST_MSECOND); |
| return sync; |
| } |
| |
| static EGLBoolean |
| _eglClientWaitSyncKHR (GstGLContext * context, EGLSyncKHR sync) |
| { |
| EGLint (*gst_eglClientWaitSyncKHR) (EGLDisplay dpy, EGLSyncKHR sync, |
| EGLint flags, EGLTimeKHR timeout); |
| EGLDisplay dpy = _get_egl_display (context); |
| g_assert (dpy != EGL_NO_DISPLAY); |
| |
| GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "waiting on egl sync object %p", sync); |
| gst_eglClientWaitSyncKHR = gst_gl_context_get_proc_address (context, |
| "eglClientWaitSyncKHR"); |
| return gst_eglClientWaitSyncKHR (dpy, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, |
| 1000000000 /* 1s */ ) == EGL_CONDITION_SATISFIED_KHR; |
| } |
| |
| static EGLBoolean |
| _eglDestroySyncKHR (GstGLContext * context, EGLSyncKHR sync) |
| { |
| EGLBoolean (*gst_eglDestroySyncKHR) (EGLDisplay dpy, EGLSyncKHR sync); |
| EGLDisplay dpy = _get_egl_display (context); |
| if (dpy == EGL_NO_DISPLAY) { |
| return EGL_FALSE; |
| } |
| |
| gst_eglDestroySyncKHR = gst_gl_context_get_proc_address (context, |
| "eglDestroySyncKHR"); |
| GST_CAT_LOG (GST_CAT_GL_MEMORY_DMA, "deleting egl sync object %p", sync); |
| return gst_eglDestroySyncKHR (dpy, sync); |
| } |
| |
| static void |
| _create_sync_gl (GstGLContext * context, gpointer data) |
| { |
| EGLSyncKHR *sync = (EGLSyncKHR *) data; |
| *sync = _eglCreateSyncKHR (context); |
| } |
| |
| GstBuffer * |
| gst_gl_memory_dma_sync_buffer (GstBuffer * glbuf) |
| { |
| GstBuffer *buf; |
| GstVideoInfo *info; |
| GstMemory *mem; |
| GstGLBaseMemory *glmem; |
| GstGLMemoryDMA *dmamem; |
| GstGLSyncMeta *sync_meta; |
| EGLSyncKHR sync; |
| EGLBoolean res; |
| GstClockTime ts; |
| |
| mem = gst_buffer_peek_memory (glbuf, 0); |
| g_assert (gst_is_gl_memory_dma (mem)); |
| |
| glmem = GST_GL_BASE_MEMORY_CAST (mem); |
| dmamem = GST_GL_MEMORY_DMA_CAST (mem); |
| sync_meta = gst_buffer_get_gl_sync_meta (glbuf); |
| |
| if (sync_meta) { |
| ts = gst_util_get_timestamp (); |
| gst_gl_sync_meta_wait_cpu (sync_meta, glmem->context); |
| ts = gst_util_get_timestamp () - ts; |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "wait_cpu %.2g ms", |
| (double) ts / GST_MSECOND); |
| } else { |
| ts = gst_util_get_timestamp (); |
| gst_gl_context_thread_add (glmem->context, _create_sync_gl, &sync); |
| g_assert (sync != EGL_NO_SYNC_KHR); |
| |
| do { |
| res = _eglClientWaitSyncKHR (glmem->context, sync); |
| } while (res == EGL_FALSE); |
| ts = gst_util_get_timestamp () - ts; |
| |
| res = _eglDestroySyncKHR (glmem->context, sync); |
| g_assert (res == EGL_TRUE); |
| ts = gst_util_get_timestamp () - ts; |
| GST_CAT_DEBUG (GST_CAT_GL_MEMORY_DMA, "egl_sync %.2g ms", |
| (double) ts / GST_MSECOND); |
| } |
| |
| info = &dmamem->mem.info; |
| buf = gst_buffer_new (); |
| gst_buffer_add_parent_buffer_meta (buf, glbuf); |
| gst_buffer_append_memory (buf, gst_memory_ref (dmamem->dma)); |
| gst_buffer_add_video_meta_full (buf, 0, |
| GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info), |
| GST_VIDEO_INFO_HEIGHT (info), 1, info->offset, info->stride); |
| |
| return buf; |
| } |
| |
| gboolean |
| gst_is_gl_memory_dma (GstMemory * mem) |
| { |
| return mem && mem->allocator && g_type_is_a (G_OBJECT_TYPE (mem->allocator), |
| GST_TYPE_GL_MEMORY_DMA_ALLOCATOR); |
| } |
| |
| void |
| gst_gl_memory_dma_init_once (void) |
| { |
| static volatile gsize _init = 0; |
| |
| if (g_once_init_enter (&_init)) { |
| GstAllocator *alloc = g_object_new (GST_TYPE_GL_MEMORY_DMA_ALLOCATOR, NULL); |
| gst_object_ref_sink (alloc); |
| gst_allocator_register (GST_GL_MEMORY_DMA_ALLOCATOR_NAME, alloc); |
| g_once_init_leave (&_init, 1); |
| } |
| } |