mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-22 16:26:39 +00:00
9d867356df
Fixes a potential GPU stall if an immediately freed texture/buffer is attempted to be reused immediately by the CPU, e.g. when uploading. Problematic scenario is this: 1. element does GPU processing reading from texture 2. frees the buffer back to the pool 3. pool acquire returns the just released buffer 4. GPU processing then has to wait for the previous GPU operation to complete causing a stall If there was a reliable way to know whether a buffer had been finished with across all GPU drivers, we would use it. However as that does not exist, this workaround is to keep the released buffer unusable until the next released buffer. This is the same approach as is used in the qml (Qt5) elements. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5144>
1493 lines
44 KiB
C
1493 lines
44 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2012 Matthew Waters <ystree00@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/gl/gl.h>
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
#include <gst/gl/egl/gsteglimage.h>
|
|
#include <gst/allocators/gstdmabuf.h>
|
|
#endif
|
|
|
|
#include "gstglelements.h"
|
|
#include "gstgldownloadelement.h"
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_gl_download_element_debug);
|
|
#define GST_CAT_DEFAULT gst_gl_download_element_debug
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
#include <gst/gl/egl/gstgldisplay_egl.h>
|
|
#include <gst/gl/egl/gstglmemoryegl.h>
|
|
|
|
#include "nvbuf_utils.h"
|
|
|
|
static const char *
|
|
nv_buffer_payload_type_to_string (NvBufferPayloadType ptype)
|
|
{
|
|
switch (ptype) {
|
|
case NvBufferPayload_SurfArray:
|
|
return "SurfArray";
|
|
case NvBufferPayload_MemHandle:
|
|
return "MemHandle";
|
|
default:
|
|
return "<unknown>";
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
nv_buffer_pixel_format_to_string (NvBufferColorFormat fmt)
|
|
{
|
|
switch (fmt) {
|
|
case NvBufferColorFormat_YUV420:
|
|
return "YUV420";
|
|
case NvBufferColorFormat_YVU420:
|
|
return "YVU420";
|
|
case NvBufferColorFormat_YUV422:
|
|
return "YUV422";
|
|
case NvBufferColorFormat_YUV420_ER:
|
|
return "YUV420_ER";
|
|
case NvBufferColorFormat_YVU420_ER:
|
|
return "YVU420_ER";
|
|
case NvBufferColorFormat_NV12:
|
|
return "NV12";
|
|
case NvBufferColorFormat_NV12_ER:
|
|
return "NV12_ER";
|
|
case NvBufferColorFormat_NV21:
|
|
return "NV21";
|
|
case NvBufferColorFormat_NV21_ER:
|
|
return "NV21_ER";
|
|
case NvBufferColorFormat_UYVY:
|
|
return "UYVY";
|
|
case NvBufferColorFormat_UYVY_ER:
|
|
return "UYVY_ER";
|
|
case NvBufferColorFormat_VYUY:
|
|
return "VYUY";
|
|
case NvBufferColorFormat_VYUY_ER:
|
|
return "VYUY_ER";
|
|
case NvBufferColorFormat_YUYV:
|
|
return "YUYV";
|
|
case NvBufferColorFormat_YUYV_ER:
|
|
return "YUYV_ER";
|
|
case NvBufferColorFormat_YVYU:
|
|
return "YVYU";
|
|
case NvBufferColorFormat_YVYU_ER:
|
|
return "YVYU_ER";
|
|
case NvBufferColorFormat_ABGR32:
|
|
return "ABGR32";
|
|
case NvBufferColorFormat_XRGB32:
|
|
return "XRGB32";
|
|
case NvBufferColorFormat_ARGB32:
|
|
return "ARGB32";
|
|
case NvBufferColorFormat_NV12_10LE:
|
|
return "NV12_10LE";
|
|
case NvBufferColorFormat_NV12_10LE_709:
|
|
return "NV12_10LE_709";
|
|
case NvBufferColorFormat_NV12_10LE_709_ER:
|
|
return "NV12_10LE_709_ER";
|
|
case NvBufferColorFormat_NV12_10LE_2020:
|
|
return "NV12_2020";
|
|
case NvBufferColorFormat_NV21_10LE:
|
|
return "NV21_10LE";
|
|
case NvBufferColorFormat_NV12_12LE:
|
|
return "NV12_12LE";
|
|
case NvBufferColorFormat_NV12_12LE_2020:
|
|
return "NV12_12LE_2020";
|
|
case NvBufferColorFormat_NV21_12LE:
|
|
return "NV21_12LE";
|
|
case NvBufferColorFormat_YUV420_709:
|
|
return "YUV420_709";
|
|
case NvBufferColorFormat_YUV420_709_ER:
|
|
return "YUV420_709_ER";
|
|
case NvBufferColorFormat_NV12_709:
|
|
return "NV12_709";
|
|
case NvBufferColorFormat_NV12_709_ER:
|
|
return "NV12_709_ER";
|
|
case NvBufferColorFormat_YUV420_2020:
|
|
return "YUV420_2020";
|
|
case NvBufferColorFormat_NV12_2020:
|
|
return "NV12_2020";
|
|
case NvBufferColorFormat_SignedR16G16:
|
|
return "SignedR16G16";
|
|
case NvBufferColorFormat_A32:
|
|
return "A32";
|
|
case NvBufferColorFormat_YUV444:
|
|
return "YUV444";
|
|
case NvBufferColorFormat_GRAY8:
|
|
return "GRAY8";
|
|
case NvBufferColorFormat_NV16:
|
|
return "NV16";
|
|
case NvBufferColorFormat_NV16_10LE:
|
|
return "NV16_10LE";
|
|
case NvBufferColorFormat_NV24:
|
|
return "NV24";
|
|
case NvBufferColorFormat_NV16_ER:
|
|
return "NV16_ER";
|
|
case NvBufferColorFormat_NV24_ER:
|
|
return "NV24_ER";
|
|
case NvBufferColorFormat_NV16_709:
|
|
return "NV16_709";
|
|
case NvBufferColorFormat_NV24_709:
|
|
return "NV24_709";
|
|
case NvBufferColorFormat_NV16_709_ER:
|
|
return "NV16_709_ER";
|
|
case NvBufferColorFormat_NV24_709_ER:
|
|
return "NV24_709_ER";
|
|
case NvBufferColorFormat_NV24_10LE_709:
|
|
return "NV24_10LE_709";
|
|
case NvBufferColorFormat_NV24_10LE_709_ER:
|
|
return "NV24_10LE_709_ER";
|
|
case NvBufferColorFormat_NV24_10LE_2020:
|
|
return "NV24_10LE_2020";
|
|
case NvBufferColorFormat_NV24_12LE_2020:
|
|
return "NV24_12LE_2020";
|
|
case NvBufferColorFormat_RGBA_10_10_10_2_709:
|
|
return "RGBA_10_10_10_2_709";
|
|
case NvBufferColorFormat_RGBA_10_10_10_2_2020:
|
|
return "RGBA_10_10_10_2_2020";
|
|
case NvBufferColorFormat_BGRA_10_10_10_2_709:
|
|
return "BGRA_10_10_10_2_709";
|
|
case NvBufferColorFormat_BGRA_10_10_10_2_2020:
|
|
return "BGRA_10_10_10_2_2020";
|
|
case NvBufferColorFormat_Invalid:
|
|
return "Invalid";
|
|
default:
|
|
return "<unknown>";
|
|
}
|
|
}
|
|
|
|
static void
|
|
nv_buffer_dump_params (GstObject * debug_object, NvBufferParamsEx * params)
|
|
{
|
|
GST_DEBUG_OBJECT (debug_object, "nvbuffer fd: %u size %i nv_buffer: %p of "
|
|
"size %u, payload: (0x%x) %s, pixel format: (0x%x) %s, n_planes: %u, "
|
|
"plane 0 { wxh: %ux%u, pitch: %u, offset: %u, psize: %u, layout: %u } "
|
|
"plane 1 { wxh: %ux%u, pitch: %u, offset: %u, psize: %u, layout: %u } "
|
|
"plane 2 { wxh: %ux%u, pitch: %u, offset: %u, psize: %u, layout: %u }",
|
|
params->params.dmabuf_fd, params->params.memsize,
|
|
params->params.nv_buffer, params->params.nv_buffer_size,
|
|
params->params.payloadType,
|
|
nv_buffer_payload_type_to_string (params->params.payloadType),
|
|
params->params.pixel_format,
|
|
nv_buffer_pixel_format_to_string (params->params.pixel_format),
|
|
params->params.num_planes, params->params.width[0],
|
|
params->params.height[0], params->params.pitch[0],
|
|
params->params.offset[0], params->params.psize[0],
|
|
params->params.offset[0], params->params.width[1],
|
|
params->params.height[1], params->params.pitch[1],
|
|
params->params.offset[1], params->params.psize[1],
|
|
params->params.offset[1], params->params.width[2],
|
|
params->params.height[2], params->params.pitch[2],
|
|
params->params.offset[2], params->params.psize[2],
|
|
params->params.offset[2]);
|
|
}
|
|
|
|
struct _GstMemoryNVMM
|
|
{
|
|
GstMemory parent;
|
|
|
|
int dmabuf_fd;
|
|
NvBufferParamsEx params;
|
|
};
|
|
|
|
typedef struct _GstMemoryNVMM GstMemoryNVMM;
|
|
|
|
struct _GstAllocatorNVMM
|
|
{
|
|
GstAllocator parent;
|
|
};
|
|
|
|
typedef struct _GstAllocatorNVMM GstAllocatorNVMM;
|
|
|
|
struct _GstAllocatorNVMMClass
|
|
{
|
|
GstAllocatorClass parent_class;
|
|
};
|
|
|
|
typedef struct _GstAllocatorNVMMClass GstAllocatorNVMMClass;
|
|
|
|
GType gst_allocator_nvmm_get_type (void);
|
|
G_DEFINE_TYPE (GstAllocatorNVMM, gst_allocator_nvmm, GST_TYPE_ALLOCATOR);
|
|
|
|
static gboolean
|
|
gst_memory_nvmm_init (GstMemoryNVMM * nvmm, GstMemoryFlags flags,
|
|
GstAllocator * allocator, GstMemory * parent, const GstVideoInfo * vinfo)
|
|
{
|
|
gsize size = NvBufferGetSize ();
|
|
NvBufferCreateParams create_params = {
|
|
.width = GST_VIDEO_INFO_WIDTH (vinfo),
|
|
.height = GST_VIDEO_INFO_HEIGHT (vinfo),
|
|
.payloadType = NvBufferPayload_SurfArray,
|
|
.memsize = GST_VIDEO_INFO_SIZE (vinfo),
|
|
.layout = NvBufferLayout_BlockLinear,
|
|
.colorFormat = NvBufferColorFormat_ABGR32,
|
|
.nvbuf_tag = NvBufferTag_NONE,
|
|
};
|
|
|
|
nvmm->dmabuf_fd = -1;
|
|
|
|
if (NvBufferCreateEx (&nvmm->dmabuf_fd, &create_params)) {
|
|
GST_WARNING_OBJECT (allocator, "Failed to create NvBuffer");
|
|
return FALSE;
|
|
}
|
|
|
|
if (NvBufferGetParamsEx (nvmm->dmabuf_fd, &nvmm->params)) {
|
|
GST_WARNING_OBJECT (allocator, "Failed to get NvBuffer params");
|
|
NvReleaseFd (nvmm->dmabuf_fd);
|
|
nvmm->dmabuf_fd = -1;
|
|
return FALSE;
|
|
}
|
|
nv_buffer_dump_params ((GstObject *) allocator, &nvmm->params);
|
|
|
|
gst_memory_init (&nvmm->parent, flags, allocator, parent, size, 0, 0, size);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gpointer
|
|
gst_memory_nvmm_map_full (GstMemory * mem, GstMapInfo * info, gsize size)
|
|
{
|
|
GstMemoryNVMM *nvmm = (GstMemoryNVMM *) mem;
|
|
|
|
GST_TRACE ("%p fd:%i map", mem, nvmm->dmabuf_fd);
|
|
|
|
// This is what the Nvidia elements do so...
|
|
return nvmm->params.params.nv_buffer;
|
|
}
|
|
|
|
static void
|
|
gst_memory_nvmm_unmap_full (GstMemory * mem, GstMapInfo * info)
|
|
{
|
|
GstMemoryNVMM *nvmm = (GstMemoryNVMM *) mem;
|
|
|
|
GST_TRACE ("%p fd:%i unmap", mem, nvmm->dmabuf_fd);
|
|
}
|
|
|
|
static GstMemory *
|
|
gst_memory_nvmm_copy (GstMemory * mem, gssize offset, gssize size)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static GstMemory *
|
|
gst_memory_nvmm_share (GstMemory * mem, gssize offset, gssize size)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
gst_memory_nvmm_is_span (GstMemory * mem, GstMemory * mem2, gsize * offset)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_is_memory_nvmm (GstMemory * mem)
|
|
{
|
|
return mem && mem->allocator
|
|
&& g_type_is_a (G_OBJECT_TYPE (mem->allocator),
|
|
gst_allocator_nvmm_get_type ());
|
|
}
|
|
|
|
static GstAllocator *_nvmm_allocator;
|
|
|
|
static void
|
|
init_nvmm_allocator (void)
|
|
{
|
|
static gsize _init = 0;
|
|
|
|
if (g_once_init_enter (&_init)) {
|
|
_nvmm_allocator = g_object_new (gst_allocator_nvmm_get_type (), NULL);
|
|
/* gst_allocator_register ("NvBuffer", _nvmm_allocator); */
|
|
GST_OBJECT_FLAG_SET (_nvmm_allocator, GST_OBJECT_FLAG_MAY_BE_LEAKED);
|
|
g_once_init_leave (&_init, 1);
|
|
}
|
|
}
|
|
|
|
static GstMemory *
|
|
gst_allocator_nvmm_alloc (const GstVideoInfo * info)
|
|
{
|
|
GstMemoryNVMM *nvmm = g_new0 (GstMemoryNVMM, 1);
|
|
|
|
init_nvmm_allocator ();
|
|
|
|
if (!gst_memory_nvmm_init (nvmm, 0, _nvmm_allocator, NULL, info)) {
|
|
g_free (nvmm);
|
|
return NULL;
|
|
}
|
|
|
|
return (GstMemory *) nvmm;
|
|
}
|
|
|
|
static GstMemory *
|
|
_gst_allocator_nvmm_alloc (GstAllocator * alloc, gsize size,
|
|
GstAllocationParams * params)
|
|
{
|
|
g_warning
|
|
("Can't allocate using gst_allocator_alloc(). Use gst_allocator_nvmm_alloc() instead");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_gst_allocator_nvmm_free (GstAllocator * alloc, GstMemory * mem)
|
|
{
|
|
GstMemoryNVMM *nvmm = (GstMemoryNVMM *) mem;
|
|
|
|
if (nvmm->dmabuf_fd > 0)
|
|
NvReleaseFd (nvmm->dmabuf_fd);
|
|
nvmm->dmabuf_fd = -1;
|
|
|
|
g_free (nvmm);
|
|
}
|
|
|
|
static void
|
|
gst_allocator_nvmm_class_init (GstAllocatorNVMMClass * klass)
|
|
{
|
|
GstAllocatorClass *alloc_class = (GstAllocatorClass *) klass;
|
|
|
|
alloc_class->alloc = _gst_allocator_nvmm_alloc;
|
|
alloc_class->free = _gst_allocator_nvmm_free;
|
|
}
|
|
|
|
static void
|
|
gst_allocator_nvmm_init (GstAllocatorNVMM * nvmm)
|
|
{
|
|
GstAllocator *alloc = (GstAllocator *) nvmm;
|
|
|
|
alloc->mem_map_full = gst_memory_nvmm_map_full;
|
|
alloc->mem_unmap_full = gst_memory_nvmm_unmap_full;
|
|
alloc->mem_copy = gst_memory_nvmm_copy;
|
|
alloc->mem_share = gst_memory_nvmm_share;
|
|
alloc->mem_is_span = gst_memory_nvmm_is_span;
|
|
}
|
|
|
|
GType gst_nvmm_parent_meta_api_get_type (void);
|
|
#define GST_NVMM_PARENT_META_API_TYPE (gst_nvmm_parent_meta_api_get_type())
|
|
|
|
#define gst_buffer_get_nvmm_parent_meta(b) \
|
|
((GstNVMMParentMeta*)gst_buffer_get_meta((b),GST_NVMM_PARENT_META_API_TYPE))
|
|
|
|
const GstMetaInfo *gst_nvmm_parent_meta_get_info (void);
|
|
#define GST_NVMM_PARENT_META_INFO (gst_nvmm_parent_meta_get_info())
|
|
|
|
/* GstParentBufferMeta but supporting NULL and no copying to avoid accidentally
|
|
* introducing a circular reference when copying GstMeta's */
|
|
struct _GstNVMMParentMeta
|
|
{
|
|
GstMeta parent;
|
|
|
|
GstBuffer *buffer;
|
|
};
|
|
typedef struct _GstNVMMParentMeta GstNVMMParentMeta;
|
|
|
|
static GstNVMMParentMeta *
|
|
gst_buffer_add_nvmm_parent_meta (GstBuffer * buffer, GstBuffer * ref)
|
|
{
|
|
GstNVMMParentMeta *meta;
|
|
|
|
g_return_val_if_fail (GST_IS_BUFFER (ref), NULL);
|
|
|
|
meta =
|
|
(GstNVMMParentMeta *) gst_buffer_add_meta (buffer,
|
|
GST_NVMM_PARENT_META_INFO, NULL);
|
|
|
|
if (!meta)
|
|
return NULL;
|
|
|
|
if (ref)
|
|
meta->buffer = gst_buffer_ref (ref);
|
|
|
|
return meta;
|
|
}
|
|
|
|
static gboolean
|
|
_gst_nvmm_parent_meta_transform (GstBuffer * dest, GstMeta * meta,
|
|
GstBuffer * buffer, GQuark type, gpointer data)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
_gst_nvmm_parent_meta_free (GstNVMMParentMeta * parent_meta, GstBuffer * buffer)
|
|
{
|
|
GST_DEBUG ("Dropping reference on buffer %p", parent_meta->buffer);
|
|
gst_clear_buffer (&parent_meta->buffer);
|
|
}
|
|
|
|
static gboolean
|
|
_gst_nvmm_parent_meta_init (GstNVMMParentMeta * parent_meta,
|
|
gpointer params, GstBuffer * buffer)
|
|
{
|
|
parent_meta->buffer = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GType
|
|
gst_nvmm_parent_meta_api_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
static const gchar *tags[] = { GST_META_TAG_MEMORY_STR, NULL };
|
|
|
|
if (g_once_init_enter (&type)) {
|
|
GType _type = gst_meta_api_type_register ("GstNVMMParentMetaAPI", tags);
|
|
g_once_init_leave (&type, _type);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
const GstMetaInfo *
|
|
gst_nvmm_parent_meta_get_info (void)
|
|
{
|
|
static const GstMetaInfo *meta_info = NULL;
|
|
|
|
if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
|
|
const GstMetaInfo *meta =
|
|
gst_meta_register (gst_nvmm_parent_meta_api_get_type (),
|
|
"GstNVMMParentMeta",
|
|
sizeof (GstNVMMParentMeta),
|
|
(GstMetaInitFunction) _gst_nvmm_parent_meta_init,
|
|
(GstMetaFreeFunction) _gst_nvmm_parent_meta_free,
|
|
_gst_nvmm_parent_meta_transform);
|
|
g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
|
|
}
|
|
|
|
return meta_info;
|
|
}
|
|
|
|
static GstMiniObjectDisposeFunction parent_gst_buffer_dispose = NULL;
|
|
|
|
static gboolean
|
|
gst_buffer_nvmm_dispose (GstMiniObject * obj)
|
|
{
|
|
GstBuffer *buf = (GstBuffer *) obj;
|
|
GstNVMMParentMeta *nv_buf_meta = gst_buffer_get_nvmm_parent_meta (buf);
|
|
|
|
GST_TRACE ("nvmm buffer dispose %p, parent_buf_meta %p", obj, nv_buf_meta);
|
|
if (nv_buf_meta && nv_buf_meta->buffer) {
|
|
GstNVMMParentMeta *gl_buf_meta;
|
|
|
|
gl_buf_meta = gst_buffer_get_nvmm_parent_meta (nv_buf_meta->buffer);
|
|
if (gl_buf_meta && !gl_buf_meta->buffer) {
|
|
// reattache the NVMM buffer to the parent buffer
|
|
GST_LOG ("readding nvmm buffer %p %i, to glmemory buffer %p %i", buf,
|
|
GST_MINI_OBJECT_REFCOUNT_VALUE (buf), nv_buf_meta->buffer,
|
|
GST_MINI_OBJECT_REFCOUNT_VALUE (nv_buf_meta->buffer));
|
|
gl_buf_meta->buffer = gst_buffer_ref (buf);
|
|
gst_clear_buffer (&nv_buf_meta->buffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return parent_gst_buffer_dispose (obj);
|
|
}
|
|
|
|
struct _GstGLBufferPoolNVMMPrivate
|
|
{
|
|
GstGLVideoAllocationParams *gl_params;
|
|
};
|
|
typedef struct _GstGLBufferPoolNVMMPrivate GstGLBufferPoolNVMMPrivate;
|
|
|
|
struct _GstGLBufferPoolNVMM
|
|
{
|
|
GstGLBufferPool parent;
|
|
};
|
|
|
|
#define NVMM_POOL_GET_PRIV(obj) gst_gl_buffer_pool_nvmm_get_instance_private((GstGLBufferPoolNVMM *)(obj));
|
|
|
|
G_DECLARE_FINAL_TYPE (GstGLBufferPoolNVMM, gst_gl_buffer_pool_nvmm, GST,
|
|
GL_BUFFER_POOL_NVMM, GstGLBufferPool);
|
|
G_DEFINE_TYPE_WITH_CODE (GstGLBufferPoolNVMM, gst_gl_buffer_pool_nvmm,
|
|
GST_TYPE_GL_BUFFER_POOL, G_ADD_PRIVATE (GstGLBufferPoolNVMM));
|
|
|
|
static gboolean
|
|
gst_gl_buffer_pool_nvmm_set_config (GstBufferPool * pool, GstStructure * config)
|
|
{
|
|
GstGLBufferPoolNVMMPrivate *priv;
|
|
GstGLBufferPool *glpool = GST_GL_BUFFER_POOL (pool);
|
|
GstGLVideoAllocationParams *parent_gl_params;
|
|
GstCaps *caps = NULL;
|
|
GstVideoInfo vinfo;
|
|
GstAllocationParams alloc_params;
|
|
|
|
priv = NVMM_POOL_GET_PRIV (pool);
|
|
|
|
if (!gst_buffer_pool_config_get_allocator (config, NULL, &alloc_params))
|
|
goto wrong_config;
|
|
|
|
if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
|
|
goto wrong_config;
|
|
|
|
if (caps == NULL)
|
|
goto no_caps;
|
|
|
|
/* now parse the caps from the config */
|
|
if (!gst_video_info_from_caps (&vinfo, caps))
|
|
goto wrong_caps;
|
|
|
|
// TODO: fallback to regular GLMemory PBO/GetTexImage downloads?
|
|
if (GST_VIDEO_INFO_FORMAT (&vinfo) != GST_VIDEO_FORMAT_RGBA)
|
|
goto wrong_vformat;
|
|
|
|
if (!GST_BUFFER_POOL_CLASS (gst_gl_buffer_pool_nvmm_parent_class)->set_config
|
|
(pool, config))
|
|
return FALSE;
|
|
|
|
parent_gl_params = (GstGLVideoAllocationParams *)
|
|
gst_gl_buffer_pool_get_gl_allocation_params (glpool);
|
|
|
|
if (priv->gl_params)
|
|
gst_gl_allocation_params_free ((GstGLAllocationParams *) priv->gl_params);
|
|
priv->gl_params =
|
|
gst_gl_video_allocation_params_new_wrapped_gl_handle
|
|
(parent_gl_params->parent.context, parent_gl_params->parent.alloc_params,
|
|
parent_gl_params->v_info, 0, parent_gl_params->valign,
|
|
parent_gl_params->target, parent_gl_params->tex_format, NULL, NULL, NULL);
|
|
|
|
gst_buffer_pool_config_set_gl_allocation_params (config,
|
|
(GstGLAllocationParams *) priv->gl_params);
|
|
gst_gl_allocation_params_free ((GstGLAllocationParams *) parent_gl_params);
|
|
|
|
if (!GST_BUFFER_POOL_CLASS (gst_gl_buffer_pool_nvmm_parent_class)->set_config
|
|
(pool, config))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
wrong_config:
|
|
{
|
|
GST_WARNING_OBJECT (pool, "invalid config");
|
|
return FALSE;
|
|
}
|
|
no_caps:
|
|
{
|
|
GST_WARNING_OBJECT (pool, "no caps in config");
|
|
return FALSE;
|
|
}
|
|
wrong_caps:
|
|
{
|
|
GST_WARNING_OBJECT (pool,
|
|
"failed getting geometry from caps %" GST_PTR_FORMAT, caps);
|
|
return FALSE;
|
|
}
|
|
wrong_vformat:
|
|
{
|
|
GST_WARNING_OBJECT (pool, "This pool only deals with RGBA textures");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nv_buffer_egl_image_mem_unref (GstEGLImage * image, GstMemory * mem)
|
|
{
|
|
GstGLDisplayEGL *egl_display = NULL;
|
|
EGLDisplay display;
|
|
|
|
egl_display = gst_gl_display_egl_from_gl_display (image->context->display);
|
|
if (!egl_display) {
|
|
GST_ERROR ("Could not retrieve GstGLDisplayEGL from GstGLDisplay");
|
|
return;
|
|
}
|
|
display =
|
|
(EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (egl_display));
|
|
|
|
if (NvDestroyEGLImage (display, image->image)) {
|
|
GST_ERROR ("Failed to destroy EGLImage %p from NvBuffer", image->image);
|
|
} else {
|
|
GST_DEBUG ("destroyed EGLImage %p from NvBuffer", image->image);
|
|
}
|
|
|
|
gst_memory_unref (mem);
|
|
gst_object_unref (egl_display);
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_gl_buffer_pool_nvmm_alloc (GstBufferPool * pool, GstBuffer ** outbuf,
|
|
GstBufferPoolAcquireParams * acquire_params)
|
|
{
|
|
GstGLBufferPool *gl_pool = GST_GL_BUFFER_POOL (pool);
|
|
GstGLBufferPoolNVMMPrivate *priv;
|
|
GstFlowReturn ret = GST_FLOW_ERROR;
|
|
GstBuffer *downstream_buf = NULL;
|
|
GstMapInfo in_map_info = GST_MAP_INFO_INIT;
|
|
GstGLDisplayEGL *egl_display = NULL;
|
|
GstEGLImage *eglimage = NULL;
|
|
EGLDisplay display = EGL_NO_DISPLAY;
|
|
EGLImageKHR image = EGL_NO_IMAGE;
|
|
GstGLMemoryAllocator *allocator = NULL;
|
|
GstMemory *nvmm_mem = NULL;
|
|
int in_dmabuf_fd;
|
|
|
|
priv = NVMM_POOL_GET_PRIV (pool);
|
|
|
|
*outbuf = NULL;
|
|
downstream_buf = gst_buffer_new ();
|
|
if (!parent_gst_buffer_dispose)
|
|
parent_gst_buffer_dispose = ((GstMiniObject *) downstream_buf)->dispose;
|
|
((GstMiniObject *) downstream_buf)->dispose = gst_buffer_nvmm_dispose;
|
|
|
|
nvmm_mem = gst_allocator_nvmm_alloc (priv->gl_params->v_info);
|
|
if (!nvmm_mem) {
|
|
GST_WARNING_OBJECT (pool, "Failed to create NVMM GstMemory");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
gst_buffer_append_memory (downstream_buf, nvmm_mem);
|
|
in_dmabuf_fd = ((GstMemoryNVMM *) nvmm_mem)->dmabuf_fd;
|
|
|
|
egl_display = gst_gl_display_egl_from_gl_display (gl_pool->context->display);
|
|
if (!egl_display) {
|
|
GST_WARNING ("Failed to retrieve GstGLDisplayEGL from GstGLDisplay");
|
|
goto done;
|
|
}
|
|
display =
|
|
(EGLDisplay) gst_gl_display_get_handle (GST_GL_DISPLAY (egl_display));
|
|
|
|
image = NvEGLImageFromFd (display, in_dmabuf_fd);
|
|
if (!image) {
|
|
GST_DEBUG_OBJECT (pool, "Failed construct EGLImage "
|
|
"from NvBuffer fd %i", in_dmabuf_fd);
|
|
goto done;
|
|
}
|
|
GST_DEBUG_OBJECT (pool, "constructed EGLImage %p "
|
|
"from NvBuffer fd %i", image, in_dmabuf_fd);
|
|
|
|
eglimage = gst_egl_image_new_wrapped (gl_pool->context, image,
|
|
GST_GL_RGBA, gst_memory_ref (nvmm_mem),
|
|
(GstEGLImageDestroyNotify) nv_buffer_egl_image_mem_unref);
|
|
if (!eglimage) {
|
|
GST_WARNING_OBJECT (pool, "Failed to wrap constructed "
|
|
"EGLImage from NvBuffer");
|
|
goto done;
|
|
}
|
|
|
|
gst_buffer_unmap (downstream_buf, &in_map_info);
|
|
in_map_info = (GstMapInfo) GST_MAP_INFO_INIT;
|
|
|
|
allocator =
|
|
GST_GL_MEMORY_ALLOCATOR (gst_allocator_find
|
|
(GST_GL_MEMORY_EGL_ALLOCATOR_NAME));
|
|
|
|
/* TODO: buffer pool */
|
|
*outbuf = gst_buffer_new ();
|
|
if (!gst_gl_memory_setup_buffer (allocator, *outbuf, priv->gl_params,
|
|
NULL, (gpointer *) & eglimage, 1)) {
|
|
GST_WARNING_OBJECT (pool, "Failed to setup NVMM -> EGLImage buffer");
|
|
goto done;
|
|
}
|
|
|
|
gst_egl_image_unref (eglimage);
|
|
|
|
/* TODO: NvBuffer has some sync functions that may be more useful here */
|
|
{
|
|
GstGLSyncMeta *sync_meta;
|
|
|
|
sync_meta = gst_buffer_add_gl_sync_meta (gl_pool->context, *outbuf);
|
|
if (sync_meta) {
|
|
gst_gl_sync_meta_set_sync_point (sync_meta, gl_pool->context);
|
|
}
|
|
}
|
|
|
|
// possible circular reference here
|
|
gst_buffer_add_nvmm_parent_meta (*outbuf, downstream_buf);
|
|
gst_buffer_unref (downstream_buf);
|
|
|
|
ret = GST_FLOW_OK;
|
|
|
|
done:
|
|
if (in_map_info.memory)
|
|
gst_buffer_unmap (downstream_buf, &in_map_info);
|
|
|
|
gst_clear_object (&egl_display);
|
|
gst_clear_object (&allocator);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
gst_gl_buffer_pool_nvmm_finalize (GObject * object)
|
|
{
|
|
GstGLBufferPoolNVMMPrivate *priv = NVMM_POOL_GET_PRIV (object);
|
|
|
|
if (priv->gl_params)
|
|
gst_gl_allocation_params_free ((GstGLAllocationParams *) priv->gl_params);
|
|
priv->gl_params = NULL;
|
|
|
|
G_OBJECT_CLASS (gst_gl_buffer_pool_nvmm_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_gl_buffer_pool_nvmm_init (GstGLBufferPoolNVMM * pool)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gst_gl_buffer_pool_nvmm_class_init (GstGLBufferPoolNVMMClass * klass)
|
|
{
|
|
GstBufferPoolClass *pool_class = (GstBufferPoolClass *) klass;
|
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
|
|
pool_class->set_config = gst_gl_buffer_pool_nvmm_set_config;
|
|
pool_class->alloc_buffer = gst_gl_buffer_pool_nvmm_alloc;
|
|
|
|
gobject_class->finalize = gst_gl_buffer_pool_nvmm_finalize;
|
|
}
|
|
|
|
static GstBufferPool *
|
|
gst_gl_buffer_pool_nvmm_new (GstGLContext * context)
|
|
{
|
|
GstGLBufferPoolNVMM *pool;
|
|
GstGLBufferPool *gl_pool;
|
|
|
|
pool = g_object_new (gst_gl_buffer_pool_nvmm_get_type (), NULL);
|
|
gst_object_ref_sink (pool);
|
|
gl_pool = GST_GL_BUFFER_POOL (pool);
|
|
gl_pool->context = gst_object_ref (context);
|
|
|
|
GST_LOG_OBJECT (pool, "new NVMM GL buffer pool for context %" GST_PTR_FORMAT,
|
|
context);
|
|
|
|
return GST_BUFFER_POOL_CAST (pool);
|
|
}
|
|
#endif /* GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM) */
|
|
|
|
#define gst_gl_download_element_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstGLDownloadElement, gst_gl_download_element,
|
|
GST_TYPE_GL_BASE_FILTER,
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_download_element_debug, "gldownloadelement",
|
|
0, "download element"););
|
|
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (gldownload, "gldownload",
|
|
GST_RANK_NONE, GST_TYPE_GL_DOWNLOAD_ELEMENT, gl_element_init (plugin));
|
|
|
|
static gboolean gst_gl_download_element_start (GstBaseTransform * bt);
|
|
static gboolean gst_gl_download_element_stop (GstBaseTransform * bt);
|
|
static gboolean gst_gl_download_element_get_unit_size (GstBaseTransform * trans,
|
|
GstCaps * caps, gsize * size);
|
|
static GstCaps *gst_gl_download_element_transform_caps (GstBaseTransform * bt,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * filter);
|
|
static GstCaps *gst_gl_download_element_fixate_caps (GstBaseTransform * trans,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
|
|
static gboolean gst_gl_download_element_set_caps (GstBaseTransform * bt,
|
|
GstCaps * in_caps, GstCaps * out_caps);
|
|
static GstFlowReturn
|
|
gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
|
|
GstBuffer * buffer, GstBuffer ** outbuf);
|
|
static GstFlowReturn gst_gl_download_element_transform (GstBaseTransform * bt,
|
|
GstBuffer * buffer, GstBuffer * outbuf);
|
|
static gboolean gst_gl_download_element_transform_meta (GstBaseTransform * bt,
|
|
GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf);
|
|
static gboolean gst_gl_download_element_decide_allocation (GstBaseTransform *
|
|
trans, GstQuery * query);
|
|
static gboolean gst_gl_download_element_sink_event (GstBaseTransform * bt,
|
|
GstEvent * event);
|
|
static gboolean gst_gl_download_element_src_event (GstBaseTransform * bt,
|
|
GstEvent * event);
|
|
static gboolean gst_gl_download_element_propose_allocation (GstBaseTransform *
|
|
bt, GstQuery * decide_query, GstQuery * query);
|
|
static void gst_gl_download_element_finalize (GObject * object);
|
|
|
|
#define GST_CAPS_FEATURE_MEMORY_NVMM "memory:NVMM"
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
#define EXTRA_CAPS_TEMPLATE1 "video/x-raw(" GST_CAPS_FEATURE_MEMORY_NVMM "), format=(string)RGBA; "
|
|
#else
|
|
#define EXTRA_CAPS_TEMPLATE1
|
|
#endif
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
#define EXTRA_CAPS_TEMPLATE2 "video/x-raw(" GST_CAPS_FEATURE_MEMORY_DMABUF "); "
|
|
#else
|
|
#define EXTRA_CAPS_TEMPLATE2
|
|
#endif
|
|
|
|
static GstStaticPadTemplate gst_gl_download_element_src_pad_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (EXTRA_CAPS_TEMPLATE1 EXTRA_CAPS_TEMPLATE2
|
|
"video/x-raw; video/x-raw(memory:GLMemory)"));
|
|
|
|
static GstStaticPadTemplate gst_gl_download_element_sink_pad_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("video/x-raw(memory:GLMemory); video/x-raw"));
|
|
|
|
static void
|
|
gst_gl_download_element_class_init (GstGLDownloadElementClass * klass)
|
|
{
|
|
GstBaseTransformClass *bt_class = GST_BASE_TRANSFORM_CLASS (klass);
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
bt_class->start = gst_gl_download_element_start;
|
|
bt_class->stop = gst_gl_download_element_stop;
|
|
bt_class->transform_caps = gst_gl_download_element_transform_caps;
|
|
bt_class->fixate_caps = gst_gl_download_element_fixate_caps;
|
|
bt_class->set_caps = gst_gl_download_element_set_caps;
|
|
bt_class->get_unit_size = gst_gl_download_element_get_unit_size;
|
|
bt_class->prepare_output_buffer =
|
|
gst_gl_download_element_prepare_output_buffer;
|
|
bt_class->transform = gst_gl_download_element_transform;
|
|
bt_class->decide_allocation = gst_gl_download_element_decide_allocation;
|
|
bt_class->sink_event = gst_gl_download_element_sink_event;
|
|
bt_class->src_event = gst_gl_download_element_src_event;
|
|
bt_class->propose_allocation = gst_gl_download_element_propose_allocation;
|
|
bt_class->transform_meta = gst_gl_download_element_transform_meta;
|
|
|
|
bt_class->passthrough_on_same_caps = TRUE;
|
|
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&gst_gl_download_element_src_pad_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&gst_gl_download_element_sink_pad_template);
|
|
|
|
gst_element_class_set_metadata (element_class,
|
|
"OpenGL downloader", "Filter/Video",
|
|
"Downloads data from OpenGL", "Matthew Waters <matthew@centricular.com>");
|
|
|
|
object_class->finalize = gst_gl_download_element_finalize;
|
|
}
|
|
|
|
static void
|
|
gst_gl_download_element_init (GstGLDownloadElement * download)
|
|
{
|
|
gst_base_transform_set_prefer_passthrough (GST_BASE_TRANSFORM (download),
|
|
TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_start (GstBaseTransform * bt)
|
|
{
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
|
|
dl->dmabuf_allocator = gst_dmabuf_allocator_new ();
|
|
g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_stop (GstBaseTransform * bt)
|
|
{
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
|
|
if (dl->dmabuf_allocator) {
|
|
gst_object_unref (GST_OBJECT (dl->dmabuf_allocator));
|
|
dl->dmabuf_allocator = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
|
|
GstCaps * out_caps)
|
|
{
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
GstVideoInfo out_info;
|
|
GstCapsFeatures *features = NULL;
|
|
|
|
if (!gst_video_info_from_caps (&out_info, out_caps))
|
|
return FALSE;
|
|
|
|
features = gst_caps_get_features (out_caps, 0);
|
|
|
|
if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
|
|
dl->mode = GST_GL_DOWNLOAD_MODE_PASSTHROUGH;
|
|
GST_INFO_OBJECT (dl, "caps signal passthrough");
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
} else if (gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_NVMM)) {
|
|
dl->mode = GST_GL_DOWNLOAD_MODE_NVMM;
|
|
GST_INFO_OBJECT (dl, "caps signal NVMM");
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
} else if (g_atomic_int_get (&dl->try_dmabuf_exports) &&
|
|
gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
|
|
dl->mode = GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS;
|
|
GST_INFO_OBJECT (dl, "caps signal dma-buf export");
|
|
#endif
|
|
} else {
|
|
/* System Memory */
|
|
dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
|
|
GST_INFO_OBJECT (dl, "caps signal sysmem download");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstCaps *
|
|
_set_caps_features (const GstCaps * caps, const gchar * feature_name)
|
|
{
|
|
GstCaps *tmp = gst_caps_copy (caps);
|
|
guint n = gst_caps_get_size (tmp);
|
|
guint i = 0;
|
|
|
|
for (i = 0; i < n; i++)
|
|
gst_caps_set_features (tmp, i,
|
|
gst_caps_features_from_string (feature_name));
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static void
|
|
_remove_field (GstCaps * caps, const gchar * field)
|
|
{
|
|
guint n = gst_caps_get_size (caps);
|
|
guint i = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
GstStructure *s = gst_caps_get_structure (caps, i);
|
|
gst_structure_remove_field (s, field);
|
|
}
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_gl_download_element_transform_caps (GstBaseTransform * bt,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
|
|
{
|
|
GstCaps *result, *tmp;
|
|
|
|
if (direction == GST_PAD_SRC) {
|
|
GstCaps *sys_caps = gst_caps_simplify (_set_caps_features (caps,
|
|
GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY));
|
|
|
|
tmp = _set_caps_features (sys_caps, GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
|
|
tmp = gst_caps_merge (tmp, sys_caps);
|
|
} else {
|
|
GstCaps *newcaps;
|
|
tmp = gst_caps_ref (caps);
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_NVMM);
|
|
_remove_field (newcaps, "texture-target");
|
|
// FIXME: RGBA-only?
|
|
tmp = gst_caps_merge (tmp, newcaps);
|
|
#endif
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_DMABUF);
|
|
_remove_field (newcaps, "texture-target");
|
|
tmp = gst_caps_merge (tmp, newcaps);
|
|
#endif
|
|
|
|
newcaps = _set_caps_features (caps, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
|
|
_remove_field (newcaps, "texture-target");
|
|
tmp = gst_caps_merge (tmp, newcaps);
|
|
}
|
|
|
|
if (filter) {
|
|
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
|
gst_caps_unref (tmp);
|
|
} else {
|
|
result = tmp;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (bt, "returning caps %" GST_PTR_FORMAT, result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_gl_download_element_fixate_caps (GstBaseTransform * bt,
|
|
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
|
|
{
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
|
|
/* Remove DMABuf features if try_dmabuf_exports is not set */
|
|
if (direction == GST_PAD_SINK && !g_atomic_int_get (&dl->try_dmabuf_exports)) {
|
|
gint i;
|
|
|
|
for (i = 0; i < gst_caps_get_size (othercaps); i++) {
|
|
GstCapsFeatures *features = gst_caps_get_features (othercaps, i);
|
|
|
|
if (features && gst_caps_features_contains (features,
|
|
GST_CAPS_FEATURE_MEMORY_DMABUF)) {
|
|
caps = gst_caps_make_writable (othercaps);
|
|
gst_caps_remove_structure (othercaps, i--);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->fixate_caps (bt, direction,
|
|
caps, othercaps);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
|
|
gsize * size)
|
|
{
|
|
gboolean ret = FALSE;
|
|
GstVideoInfo info;
|
|
|
|
ret = gst_video_info_from_caps (&info, caps);
|
|
if (ret)
|
|
*size = GST_VIDEO_INFO_SIZE (&info);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
|
|
struct DmabufInfo
|
|
{
|
|
GstMemory *dmabuf;
|
|
gint stride;
|
|
gsize offset;
|
|
};
|
|
|
|
static void
|
|
_free_dmabuf_info (struct DmabufInfo *info)
|
|
{
|
|
gst_memory_unref (info->dmabuf);
|
|
g_free (info);
|
|
}
|
|
|
|
static GQuark
|
|
_dmabuf_info_quark (void)
|
|
{
|
|
static GQuark quark = 0;
|
|
|
|
if (!quark)
|
|
quark = g_quark_from_static_string ("GstGLDownloadDmabufInfo");
|
|
return quark;
|
|
}
|
|
|
|
static struct DmabufInfo *
|
|
_get_cached_dmabuf_info (GstGLMemory * mem)
|
|
{
|
|
return gst_mini_object_get_qdata (GST_MINI_OBJECT (mem),
|
|
_dmabuf_info_quark ());
|
|
}
|
|
|
|
static void
|
|
_set_cached_dmabuf_info (GstGLMemory * mem, struct DmabufInfo *info)
|
|
{
|
|
return gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
|
|
_dmabuf_info_quark (), info, (GDestroyNotify) _free_dmabuf_info);
|
|
}
|
|
|
|
struct DmabufTransfer
|
|
{
|
|
GstGLDownloadElement *download;
|
|
GstGLMemory *glmem;
|
|
struct DmabufInfo *info;
|
|
};
|
|
|
|
static void
|
|
_create_cached_dmabuf_info (GstGLContext * context, gpointer data)
|
|
{
|
|
struct DmabufTransfer *transfer = (struct DmabufTransfer *) data;
|
|
GstEGLImage *image;
|
|
|
|
image = gst_egl_image_from_texture (context, transfer->glmem, NULL);
|
|
if (image) {
|
|
int fd;
|
|
gint stride;
|
|
gsize offset;
|
|
|
|
if (gst_egl_image_export_dmabuf (image, &fd, &stride, &offset)) {
|
|
GstGLDownloadElement *download = transfer->download;
|
|
struct DmabufInfo *info;
|
|
gsize size;
|
|
|
|
size =
|
|
gst_gl_memory_get_texture_height (transfer->glmem) * stride + offset;
|
|
|
|
info = g_new0 (struct DmabufInfo, 1);
|
|
info->dmabuf =
|
|
gst_dmabuf_allocator_alloc (download->dmabuf_allocator, fd, size);
|
|
info->stride = stride;
|
|
info->offset = offset;
|
|
|
|
transfer->info = info;
|
|
}
|
|
|
|
gst_egl_image_unref (image);
|
|
}
|
|
}
|
|
|
|
static GstBuffer *
|
|
_try_export_dmabuf (GstGLDownloadElement * download, GstBuffer * inbuf)
|
|
{
|
|
GstGLMemory *glmem;
|
|
GstBuffer *buffer = NULL;
|
|
int i;
|
|
gsize offset[GST_VIDEO_MAX_PLANES];
|
|
gint stride[GST_VIDEO_MAX_PLANES];
|
|
GstCaps *src_caps;
|
|
GstVideoInfo out_info;
|
|
gsize total_offset;
|
|
GstVideoAlignment *alig = NULL;
|
|
|
|
glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, 0));
|
|
if (glmem) {
|
|
GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
|
|
if (gst_gl_context_get_gl_platform (context) != GST_GL_PLATFORM_EGL)
|
|
return NULL;
|
|
alig = &glmem->valign;
|
|
}
|
|
|
|
buffer = gst_buffer_new ();
|
|
total_offset = 0;
|
|
|
|
for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
|
|
struct DmabufInfo *info;
|
|
|
|
glmem = GST_GL_MEMORY_CAST (gst_buffer_peek_memory (inbuf, i));
|
|
info = _get_cached_dmabuf_info (glmem);
|
|
if (!info) {
|
|
GstGLContext *context = GST_GL_BASE_MEMORY_CAST (glmem)->context;
|
|
struct DmabufTransfer transfer;
|
|
|
|
transfer.download = download;
|
|
transfer.glmem = glmem;
|
|
transfer.info = NULL;
|
|
gst_gl_context_thread_add (context, _create_cached_dmabuf_info,
|
|
&transfer);
|
|
info = transfer.info;
|
|
|
|
if (info)
|
|
_set_cached_dmabuf_info (glmem, info);
|
|
}
|
|
|
|
if (info) {
|
|
offset[i] = total_offset + info->offset;
|
|
stride[i] = info->stride;
|
|
total_offset += gst_memory_get_sizes (info->dmabuf, NULL, NULL);
|
|
gst_buffer_insert_memory (buffer, -1, gst_memory_ref (info->dmabuf));
|
|
} else {
|
|
gst_buffer_unref (buffer);
|
|
buffer = NULL;
|
|
goto export_complete;
|
|
}
|
|
}
|
|
|
|
src_caps = gst_pad_get_current_caps (GST_BASE_TRANSFORM (download)->srcpad);
|
|
gst_video_info_from_caps (&out_info, src_caps);
|
|
gst_caps_unref (src_caps);
|
|
|
|
if (download->add_videometa) {
|
|
GstVideoMeta *meta;
|
|
|
|
meta = gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
|
|
out_info.finfo->format, out_info.width, out_info.height,
|
|
out_info.finfo->n_planes, offset, stride);
|
|
|
|
if (alig)
|
|
gst_video_meta_set_alignment (meta, *alig);
|
|
} else {
|
|
int i;
|
|
gboolean match = TRUE;
|
|
for (i = 0; i < gst_buffer_n_memory (inbuf); i++) {
|
|
if (offset[i] != out_info.offset[i] || stride[i] != out_info.stride[i]) {
|
|
match = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match) {
|
|
gst_buffer_unref (buffer);
|
|
buffer = NULL;
|
|
}
|
|
}
|
|
|
|
export_complete:
|
|
|
|
return buffer;
|
|
}
|
|
#endif /* GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF */
|
|
|
|
static GstFlowReturn
|
|
gst_gl_download_element_prepare_output_buffer (GstBaseTransform * bt,
|
|
GstBuffer * inbuf, GstBuffer ** outbuf)
|
|
{
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (bt);
|
|
GstGLContext *context = GST_GL_BASE_FILTER (bt)->context;
|
|
GstGLSyncMeta *in_sync_meta;
|
|
gint i, n;
|
|
|
|
*outbuf = inbuf;
|
|
|
|
(void) bclass;
|
|
|
|
in_sync_meta = gst_buffer_get_gl_sync_meta (inbuf);
|
|
if (in_sync_meta) {
|
|
if (context) {
|
|
gst_gl_sync_meta_wait (in_sync_meta, context);
|
|
} else if (dl->mode != GST_GL_DOWNLOAD_MODE_PASSTHROUGH) {
|
|
GST_WARNING_OBJECT (dl, "No configured GL context in non-passthrough "
|
|
"mode. Cannot wait on incoming `GstGLSyncMeta`");
|
|
}
|
|
}
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
if (dl->mode == GST_GL_DOWNLOAD_MODE_NVMM) {
|
|
GstNVMMParentMeta *buf_meta = gst_buffer_get_nvmm_parent_meta (inbuf);
|
|
GstMemory *mem;
|
|
GstMemoryNVMM *nvmm_mem;
|
|
|
|
if (!buf_meta || !buf_meta->buffer) {
|
|
// TODO: remove this restriction with an e.g. copy...
|
|
GST_ERROR_OBJECT (dl,
|
|
"Cannot push upstream created buffer when outputting NVMM");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if (!(mem = gst_buffer_peek_memory (buf_meta->buffer, 0))) {
|
|
GST_ERROR_OBJECT (dl, "No memory in buffer?");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
if (!gst_is_memory_nvmm (mem)) {
|
|
GST_ERROR_OBJECT (dl,
|
|
"Upstream buffer does not contain an attached NVMM GstMemory");
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
nvmm_mem = (GstMemoryNVMM *) mem;
|
|
|
|
/* switch up the parent buffer references so that when the NVMM buffer is
|
|
* released, the associated EGLImage/OpenGL texture is as well
|
|
*/
|
|
GST_DEBUG_OBJECT (dl, "NVMM buffer fd:%i passed through %" GST_PTR_FORMAT,
|
|
nvmm_mem->dmabuf_fd, buf_meta->buffer);
|
|
*outbuf = buf_meta->buffer;
|
|
bclass->copy_metadata (bt, inbuf, *outbuf);
|
|
buf_meta->buffer = NULL;
|
|
buf_meta = gst_buffer_get_nvmm_parent_meta (*outbuf);
|
|
if (!buf_meta) {
|
|
buf_meta = gst_buffer_add_nvmm_parent_meta (*outbuf, inbuf);
|
|
} else {
|
|
gst_clear_buffer (&buf_meta->buffer);
|
|
buf_meta->buffer = gst_buffer_ref (inbuf);
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
#endif
|
|
#if GST_GL_HAVE_PLATFORM_EGL && GST_GL_HAVE_DMABUF
|
|
if (dl->mode == GST_GL_DOWNLOAD_MODE_DMABUF_EXPORTS) {
|
|
GstBuffer *buffer = _try_export_dmabuf (dl, inbuf);
|
|
|
|
if (buffer) {
|
|
if (GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata) {
|
|
if (!GST_BASE_TRANSFORM_GET_CLASS (bt)->copy_metadata (bt, inbuf,
|
|
buffer)) {
|
|
GST_ELEMENT_WARNING (GST_ELEMENT (bt), STREAM, NOT_IMPLEMENTED,
|
|
("could not copy metadata"), (NULL));
|
|
}
|
|
}
|
|
|
|
*outbuf = buffer;
|
|
} else {
|
|
GstCaps *src_caps;
|
|
GstCapsFeatures *features;
|
|
gboolean ret;
|
|
|
|
src_caps = gst_pad_get_current_caps (bt->srcpad);
|
|
src_caps = gst_caps_make_writable (src_caps);
|
|
features = gst_caps_get_features (src_caps, 0);
|
|
gst_caps_features_remove (features, GST_CAPS_FEATURE_MEMORY_DMABUF);
|
|
g_atomic_int_set (&dl->try_dmabuf_exports, FALSE);
|
|
dl->mode = GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS;
|
|
|
|
ret = gst_base_transform_update_src_caps (bt, src_caps);
|
|
gst_caps_unref (src_caps);
|
|
|
|
if (!ret) {
|
|
GST_ERROR_OBJECT (bt, "DMABuf exportation didn't work and system "
|
|
"memory is not supported.");
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (dl->mode == GST_GL_DOWNLOAD_MODE_PBO_TRANSFERS) {
|
|
n = gst_buffer_n_memory (*outbuf);
|
|
for (i = 0; i < n; i++) {
|
|
GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
|
|
|
|
if (gst_is_gl_memory_pbo (mem))
|
|
gst_gl_memory_pbo_download_transfer ((GstGLMemoryPBO *) mem);
|
|
}
|
|
}
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_gl_download_element_transform (GstBaseTransform * bt,
|
|
GstBuffer * inbuf, GstBuffer * outbuf)
|
|
{
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_transform_meta (GstBaseTransform * bt,
|
|
GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf)
|
|
{
|
|
if (g_type_is_a (meta->info->api, GST_GL_SYNC_META_API_TYPE)) {
|
|
GST_LOG_OBJECT (bt, "not copying GstGLSyncMeta onto output buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (bt, outbuf,
|
|
meta, inbuf);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_decide_allocation (GstBaseTransform * trans,
|
|
GstQuery * query)
|
|
{
|
|
GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (trans);
|
|
|
|
if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) {
|
|
download->add_videometa = TRUE;
|
|
} else {
|
|
download->add_videometa = FALSE;
|
|
}
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
|
|
query);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_sink_event (GstBaseTransform * bt, GstEvent * event)
|
|
{
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
|
|
/* Retry exporting whenever we have new caps from upstream */
|
|
if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS)
|
|
g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (bt, event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_src_event (GstBaseTransform * bt, GstEvent * event)
|
|
{
|
|
GstGLDownloadElement *dl = GST_GL_DOWNLOAD_ELEMENT (bt);
|
|
|
|
/* Retry exporting whenever downstream have changed */
|
|
if (GST_EVENT_TYPE (event) == GST_EVENT_RECONFIGURE)
|
|
g_atomic_int_set (&dl->try_dmabuf_exports, TRUE);
|
|
|
|
return GST_BASE_TRANSFORM_CLASS (parent_class)->src_event (bt, event);
|
|
}
|
|
|
|
static gboolean
|
|
gst_gl_download_element_propose_allocation (GstBaseTransform * bt,
|
|
GstQuery * decide_query, GstQuery * query)
|
|
{
|
|
GstBufferPool *pool = NULL;
|
|
GstCaps *caps;
|
|
GstGLContext *context;
|
|
GstStructure *config;
|
|
GstVideoInfo info;
|
|
gsize size;
|
|
|
|
if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (bt,
|
|
decide_query, query))
|
|
return FALSE;
|
|
|
|
gst_query_parse_allocation (query, &caps, NULL);
|
|
if (caps == NULL)
|
|
goto invalid_caps;
|
|
|
|
context = GST_GL_BASE_FILTER (bt)->context;
|
|
if (!context) {
|
|
GST_ERROR_OBJECT (context, "got no GLContext");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_video_info_from_caps (&info, caps))
|
|
goto invalid_caps;
|
|
|
|
#if GST_GL_HAVE_PLATFORM_EGL && defined(HAVE_NVMM)
|
|
if (!pool && decide_query) {
|
|
GstCaps *decide_caps;
|
|
|
|
gst_query_parse_allocation (decide_query, &decide_caps, NULL);
|
|
if (decide_caps && gst_caps_get_size (decide_caps) > 0) {
|
|
GstCapsFeatures *features = gst_caps_get_features (decide_caps, 0);
|
|
|
|
if (gst_caps_features_contains (features, GST_CAPS_FEATURE_MEMORY_NVMM)) {
|
|
pool = gst_gl_buffer_pool_nvmm_new (context);
|
|
GST_INFO_OBJECT (bt, "have NVMM downstream, proposing NVMM "
|
|
"pool %" GST_PTR_FORMAT, pool);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (!pool) {
|
|
pool = gst_gl_buffer_pool_new (context);
|
|
}
|
|
config = gst_buffer_pool_get_config (pool);
|
|
|
|
/* the normal size of a frame */
|
|
size = info.size;
|
|
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
|
|
gst_buffer_pool_config_set_gl_min_free_queue_size (config, 1);
|
|
gst_buffer_pool_config_add_option (config,
|
|
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
|
|
|
if (!gst_buffer_pool_set_config (pool, config)) {
|
|
gst_object_unref (pool);
|
|
goto config_failed;
|
|
}
|
|
gst_query_add_allocation_pool (query, pool, size, 1, 0);
|
|
|
|
gst_object_unref (pool);
|
|
return TRUE;
|
|
|
|
invalid_caps:
|
|
{
|
|
GST_ERROR_OBJECT (bt, "Invalid Caps specified");
|
|
return FALSE;
|
|
}
|
|
config_failed:
|
|
{
|
|
GST_ERROR_OBJECT (bt, "failed setting config");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gst_gl_download_element_finalize (GObject * object)
|
|
{
|
|
GstGLDownloadElement *download = GST_GL_DOWNLOAD_ELEMENT_CAST (object);
|
|
|
|
if (download->dmabuf_allocator) {
|
|
gst_object_unref (GST_OBJECT (download->dmabuf_allocator));
|
|
download->dmabuf_allocator = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|