Merge branch 'dev-dma' into 'main'

video: dma heap buffer pool for sharing fd between elements

See merge request gstreamer/gstreamer!6694
This commit is contained in:
HuQian 2024-05-03 20:25:27 +00:00
commit 01e264d3bb
6 changed files with 459 additions and 5 deletions

View file

@ -0,0 +1,309 @@
/* GStreamer dmaheap allocator
*
* Copyright (C) 2024 Mediatek
*
* 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 mordetails.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdmaheap.h"
/**
* SECTION:gstdmaheap
* @title: GstDmaHeapAllocator
* @short_description: Memory wrapper for Linux dmaheap memory
* @see_also: #GstMemory
*
*/
#ifdef HAVE_LINUX_DMA_HEAP_H
#include <sys/ioctl.h>
#include <linux/dma-heap.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <inttypes.h>
#endif
GST_DEBUG_CATEGORY_STATIC (dmaheap_debug);
#define GST_CAT_DEFAULT dmaheap_debug
#define _do_init \
GST_DEBUG_CATEGORY_INIT (dmaheap_debug, \
"dmaheap", 0, "dmaheap memory");
#define parent_class gst_dmaheap_allocator_parent_class
G_DEFINE_TYPE_WITH_CODE (GstDmaHeapAllocator, gst_dmaheap_allocator,
GST_TYPE_DMABUF_ALLOCATOR, _do_init);
/**
* gst_dmaheap_allocator_new:
*
* Return a new dmaheap allocator.
*
* @device: device path to open
* @fd_flags: file descriptor flags when alloc buffer, such as O_RDWR | O_CLOEXEC
* @heap_flags: heap flags when alloc buffer, such as DMA_HEAP_VALID_HEAP_FLAGS
*
* Returns: (transfer full): a new dmaheap allocator, or NULL if the allocator
* isn't available. Use gst_object_unref() to release the allocator after
* usage
*/
GstAllocator *
gst_dmaheap_allocator_new (char *device, guint32 fd_flags, guint64 heap_flags)
{
int device_fd = -1;
GstAllocator *alloc = NULL;
GstDmaHeapAllocator *dmaheap_alloc = NULL;
#ifdef HAVE_LINUX_DMA_HEAP_H
g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (g_file_test (device, G_FILE_TEST_EXISTS), NULL);
/* try to open device */
device_fd = open (device, O_RDONLY | O_CLOEXEC);
if (device_fd < 0) {
GST_ERROR ("Failed to open dma heap device %s", device);
return NULL;
}
/* new allocator instance */
dmaheap_alloc = g_object_new (GST_TYPE_DMAHEAP_ALLOCATOR, NULL);
gst_object_ref_sink (dmaheap_alloc);
dmaheap_alloc->device_fd = device_fd;
dmaheap_alloc->fd_flags = fd_flags;
dmaheap_alloc->heap_flags = heap_flags;
/* some memory are required to be contiguous */
dmaheap_alloc->contiguous_memory =
!g_strcmp0 (device, "/dev/dma_heap/linux,cma");
GST_LOG_OBJECT (dmaheap_alloc,
"Creating dma heap allocator %p, dmaheap fd: %d device: %s",
dmaheap_alloc, dmaheap_alloc->device_fd, device);
return GST_ALLOCATOR (dmaheap_alloc);
#else
return NULL;
#endif
}
static void
gst_dmaheap_allocator_finalize (GObject * object)
{
GstDmaHeapAllocator *dmaheap_alloc = GST_DMAHEAP_ALLOCATOR (object);
if (dmaheap_alloc->device_fd >= 0) {
GST_LOG_OBJECT (dmaheap_alloc,
"Close dmaheap fd %d", dmaheap_alloc->device_fd);
close (dmaheap_alloc->device_fd);
dmaheap_alloc->device_fd = -1;
}
GST_LOG_OBJECT (dmaheap_alloc,
"Finalizing DMA heap allocator %p", dmaheap_alloc);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gst_dmaheap_allocator_alloc:
* @allocator: allocator to be used for this memory
* @size: memory size
* @params: allocator params
*
* Return a %GstMemory that wraps a dmaheap file descriptor.
*
* Returns: (transfer full): a GstMemory based on @allocator.
* When the buffer will be released dmaheap allocator will close the @fd.
* The memory is only mmapped on gst_buffer_map() request.
*/
static GstMemory *
gst_dmaheap_allocator_alloc (GstAllocator * allocator, gsize size,
GstAllocationParams * params)
{
GstDmaHeapAllocator *dmaheap_alloc = GST_DMAHEAP_ALLOCATOR (allocator);
gsize align = 0;
gsize maxsize = 0;
gsize prefix = 0;
gsize padding = 0;
GstMemory *new_mem = NULL;
#ifdef HAVE_LINUX_DMA_HEAP_H
g_return_val_if_fail (GST_IS_DMAHEAP_ALLOCATOR (allocator), NULL);
if (params) {
g_return_val_if_fail (((params->align + 1) & params->align) == 0, NULL);
align = params->align | gst_memory_alignment;
prefix = params->prefix;
padding = params->padding;
}
maxsize = size + prefix + padding;
struct dma_heap_allocation_data alloc_data = {
.len = maxsize,
.fd_flags = dmaheap_alloc->fd_flags,
.heap_flags = dmaheap_alloc->heap_flags,
};
if (ioctl (dmaheap_alloc->device_fd, DMA_HEAP_IOCTL_ALLOC, &alloc_data) < 0) {
GST_ERROR_OBJECT (allocator, "Failed to allocate DMA buffer");
goto alloc_dma_buffer_failed;
} else if (alloc_data.fd < 0) {
GST_ERROR_OBJECT (allocator,
"Failed to get file descriptor from DMA buffer");
goto alloc_dma_buffer_failed;
} else if (alloc_data.len < maxsize) {
GST_ERROR_OBJECT (allocator,
"Failed to allocate enough space on heap, request %" G_GSIZE_FORMAT
" bytes, got %llu bytes", maxsize, alloc_data.len);
close (alloc_data.fd);
goto alloc_dma_buffer_failed;
}
new_mem = gst_fd_allocator_alloc ((GstAllocator *) dmaheap_alloc,
alloc_data.fd, alloc_data.len, GST_FD_MEMORY_FLAG_NONE);
new_mem->align = align;
new_mem->offset = prefix;
new_mem->size = size;
new_mem->maxsize = alloc_data.len;
/* setup the memory flags */
GST_MEMORY_FLAGS (new_mem)
|= dmaheap_alloc->contiguous_memory ?
(params->flags | GST_MEMORY_FLAG_PHYSICALLY_CONTIGUOUS) : params->flags;
GST_DEBUG_OBJECT (dmaheap_alloc,
"Allocated dma mem %p with maxsize %" G_GSIZE_FORMAT " size %"
G_GSIZE_FORMAT " fd %d", new_mem, new_mem->maxsize, new_mem->size,
alloc_data.fd);
return new_mem;
alloc_dma_buffer_failed:
return NULL;
#else
return NULL;
#endif
}
/**
* gst_is_dmaheap_memory:
* @mem: the memory to be check
*
* Check if @mem is dmaheap memory.
*
* Returns: %TRUE if @mem is dmaheap memory, otherwise %FALSE
*/
gboolean
gst_is_dmaheap_memory (GstMemory * mem)
{
g_return_val_if_fail (mem != NULL, FALSE);
return GST_IS_DMAHEAP_ALLOCATOR (mem->allocator);
}
static gpointer
gst_dmabuf_mem_map_full (GstMemory * mem, GstMapInfo * info, gsize maxsize)
{
gpointer data = NULL;
gsize padding;
GstDmaHeapAllocator *dmaheap_alloc = NULL;
g_return_val_if_fail (mem != NULL, NULL);
g_return_val_if_fail (info != NULL, NULL);
dmaheap_alloc = GST_DMAHEAP_ALLOCATOR (mem->allocator);
if (GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_NOT_MAPPABLE)) {
GST_ERROR ("memory %p not mappable", mem);
return NULL;
}
if (GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_READONLY)
&& (info->flags & GST_MAP_WRITE)) {
GST_ERROR ("memory: %p read only, should not map with write access", mem);
return NULL;
}
data = dmaheap_alloc->parent_mem_map_full (mem, info, maxsize);
/* check if data is align with mem->align */
if (((guintptr) data & mem->align) != 0) {
GST_WARNING ("memory: %p data: %p is not aligned with alignment: (%u + 1)",
mem, data, mem->align);
}
if (data && GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_ZERO_PREFIXED))
memset (data, 0, mem->offset);
padding = mem->maxsize - (mem->offset + mem->size);
if (padding && GST_MEMORY_FLAG_IS_SET (mem, GST_MEMORY_FLAG_ZERO_PREFIXED))
memset (data + mem->offset + mem->size, 0, padding);
GST_DEBUG ("memory: %p map data: %p", mem, data);
return data;
}
static void
gst_dmaheap_mem_unmap_full (GstMemory * mem, GstMapInfo * info)
{
GstDmaHeapAllocator *dmaheap_alloc = NULL;
g_return_if_fail (mem != NULL);
g_return_if_fail (info != NULL);
dmaheap_alloc = GST_DMAHEAP_ALLOCATOR (mem->allocator);
GST_DEBUG ("memory: %p unmap", mem);
dmaheap_alloc->parent_mem_unmap_full (mem, info);
}
static void
gst_dmaheap_allocator_class_init (GstDmaHeapAllocatorClass * klass)
{
GstAllocatorClass *allocator_klass = GST_ALLOCATOR_CLASS (klass);
GObjectClass *gobject_klass = G_OBJECT_CLASS (klass);
allocator_klass->alloc = gst_dmaheap_allocator_alloc;
gobject_klass->finalize = gst_dmaheap_allocator_finalize;
}
static void
gst_dmaheap_allocator_init (GstDmaHeapAllocator * allocator)
{
GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
GstDmaHeapAllocator *dmaheap_alloc = GST_DMAHEAP_ALLOCATOR (alloc);
alloc->mem_type = GST_ALLOCATOR_DMAHEAP;
dmaheap_alloc->parent_mem_map_full = alloc->mem_map_full;
dmaheap_alloc->parent_mem_unmap_full = alloc->mem_unmap_full;
alloc->mem_map_full = gst_dmabuf_mem_map_full;
alloc->mem_unmap_full = gst_dmaheap_mem_unmap_full;
}

View file

@ -0,0 +1,89 @@
/* GStreamer dmaheap allocator
*
* Copyright (C) 2024 Mediatek
*
* 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 mordetails.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_DMAHEAP_H__
#define __GST_DMAHEAP_H__
#include <gst/gst.h>
#include <gst/allocators/gstfdmemory.h>
G_BEGIN_DECLS
#define GST_ALLOCATOR_DMAHEAP "dmaheap"
#define GST_TYPE_DMAHEAP_ALLOCATOR (gst_dmaheap_allocator_get_type())
#define GST_IS_DMAHEAP_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DMAHEAP_ALLOCATOR))
#define GST_IS_DMAHEAP_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_DMAHEAP_ALLOCATOR))
#define GST_DMAHEAP_ALLOCATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_DMAHEAP_ALLOCATOR, GstDmaHeapAllocatorClass))
#define GST_DMAHEAP_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_DMAHEAP_ALLOCATOR, GstDmaHeapAllocator))
#define GST_DMAHEAP_ALLOCATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DMAHEAP_ALLOCATOR, GstDmaHeapAllocatorClass))
#define GST_DMAHEAP_ALLOCATOR_CAST(obj) ((GstDmaHeapAllocator *)(obj))
typedef struct _GstDmaHeapAllocator GstDmaHeapAllocator;
typedef struct _GstDmaHeapAllocatorClass GstDmaHeapAllocatorClass;
/**
* GstDmaHeapAllocator:
*
* Base class for allocators with dmaheap-backed memory
*
* Since: 1.12
*/
struct _GstDmaHeapAllocator
{
GstDmaBufAllocator parent;
int device_fd;
guint32 fd_flags;
guint64 heap_flags;
gboolean contiguous_memory;
GstMemoryMapFullFunction parent_mem_map_full;
GstMemoryUnmapFullFunction parent_mem_unmap_full;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
struct _GstDmaHeapAllocatorClass
{
GstDmaBufAllocatorClass parent_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GST_ALLOCATORS_API
GType gst_dmaheap_allocator_get_type (void);
GST_ALLOCATORS_API
GstAllocator * gst_dmaheap_allocator_new (char* device,
guint32 fd_flags, guint64 heap_flags);
GST_ALLOCATORS_API
gboolean gst_is_dmaheap_memory (GstMemory * mem);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDmaHeapAllocator, gst_object_unref)
G_END_DECLS
#endif /* __GST_DMAHEAP_H__ */

View file

@ -6,6 +6,7 @@ gst_allocators_headers = files([
'gstdmabuf.h',
'gstdrmdumb.h',
'gstshmallocator.h',
'gstdmaheap.h',
])
install_headers(gst_allocators_headers, subdir : 'gstreamer-1.0/gst/allocators/')
@ -15,6 +16,7 @@ gst_allocators_sources = files([
'gstfdmemory.c',
'gstphysmemory.c',
'gstshmallocator.c',
'gstdmaheap.c',
])
gst_allocators_cargs = [

View file

@ -101,6 +101,7 @@ struct _GstVideoBufferPoolPrivate
GstVideoAlignment video_align;
gboolean add_videometa;
gboolean need_alignment;
gboolean multi_plane;
GstAllocator *allocator;
GstAllocationParams params;
};
@ -115,7 +116,8 @@ static const gchar **
video_buffer_pool_get_options (GstBufferPool * pool)
{
static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT,
GST_BUFFER_POOL_OPTION_VIDEO_MULTI_PLANE, NULL
};
return options;
}
@ -169,6 +171,10 @@ video_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
priv->need_alignment = gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
/* parse multi plane info */
priv->multi_plane = gst_buffer_pool_config_has_option (config,
GST_BUFFER_POOL_OPTION_VIDEO_MULTI_PLANE);
if (priv->need_alignment && priv->add_videometa) {
guint max_align, n;
@ -235,6 +241,25 @@ failed_to_align:
}
}
static int
video_buffer_pool_get_plane_size (GstVideoInfo * info, int plane)
{
int plane_num = GST_VIDEO_INFO_N_PLANES (info);
gsize cur_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, plane);
gsize next_offset = (plane == (plane_num - 1)) ?
GST_VIDEO_INFO_SIZE (info) :
GST_VIDEO_INFO_PLANE_OFFSET (info, plane + 1);
gsize plane_size = next_offset - cur_offset;
GST_DEBUG ("plane: %d size: %" G_GSIZE_FORMAT
" cur_offset: %" G_GSIZE_FORMAT
" next_offset: %" G_GSIZE_FORMAT,
plane, plane_size, cur_offset, next_offset);
return plane_size;
}
static GstFlowReturn
video_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
GstBufferPoolAcquireParams * params)
@ -247,10 +272,30 @@ video_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
GST_DEBUG_OBJECT (pool, "alloc %" G_GSIZE_FORMAT, info->size);
*buffer =
gst_buffer_new_allocate (priv->allocator, info->size, &priv->params);
if (*buffer == NULL)
goto no_memory;
if (!priv->multi_plane) {
*buffer =
gst_buffer_new_allocate (priv->allocator, info->size, &priv->params);
if (*buffer == NULL)
goto no_memory;
} else {
gint plane_num = GST_VIDEO_INFO_N_PLANES (info);
*buffer = gst_buffer_new ();
if (*buffer == NULL)
goto no_memory;
for (int plane = 0; plane < plane_num; plane++) {
GstMemory *mem = gst_allocator_alloc (priv->allocator,
video_buffer_pool_get_plane_size (info, plane), &priv->params);
if (mem == NULL) {
gst_buffer_unref (*buffer);
*buffer = NULL;
goto no_memory;
}
gst_buffer_append_memory (*buffer, mem);
}
}
if (priv->add_videometa) {
GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");

View file

@ -45,6 +45,14 @@ G_BEGIN_DECLS
*/
#define GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT "GstBufferPoolOptionVideoAlignment"
/**
* GST_BUFFER_POOL_OPTION_VIDEO_MULTI_PLANE:
*
* An option that can be activated on bufferpool to request
* multi gstmemory in a gstbuffer when possible
*/
#define GST_BUFFER_POOL_OPTION_VIDEO_MULTI_PLANE "GstBufferPoolOptionVideoMultiPlane"
/* setting a bufferpool config */
GST_VIDEO_API

View file

@ -183,6 +183,7 @@ check_headers = [
['HAVE_WINSOCK2_H', 'winsock2.h'],
['HAVE_XMMINTRIN_H', 'xmmintrin.h'],
['HAVE_LINUX_DMA_BUF_H', 'linux/dma-buf.h'],
['HAVE_LINUX_DMA_HEAP_H', 'linux/dma-heap.h'],
]
foreach h : check_headers
if cc.has_header(h.get(1))