From ceecdb8e1ddabf96ced519c40fb80104b380befb Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Mon, 18 Feb 2013 15:18:38 +0100 Subject: [PATCH] allocators: Add dmabuf-based GstMemory and GstAllocator Create new GstMemory and GstAllocator base on dmabuf. Memory is not allocated/freed by userland but mapped/unmmaped from a dmabuf file descriptor when requested. This allocator is included in a new lib called libgstallocators https://bugzilla.gnome.org/show_bug.cgi?id=693826 --- configure.ac | 15 ++ gst-libs/gst/Makefile.am | 3 +- gst-libs/gst/allocators/Makefile.am | 74 ++++++ gst-libs/gst/allocators/gstdmabuf.c | 325 +++++++++++++++++++++++ gst-libs/gst/allocators/gstdmabuf.h | 35 +++ pkgconfig/Makefile.am | 3 + pkgconfig/gstreamer-allocators-uninstalled.pc.in | 16 ++ pkgconfig/gstreamer-allocators.pc.in | 16 ++ 8 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 gst-libs/gst/allocators/Makefile.am create mode 100644 gst-libs/gst/allocators/gstdmabuf.c create mode 100644 gst-libs/gst/allocators/gstdmabuf.h create mode 100644 pkgconfig/gstreamer-allocators-uninstalled.pc.in create mode 100644 pkgconfig/gstreamer-allocators.pc.in diff --git a/configure.ac b/configure.ac index 1eed0af..b9721f9 100644 --- a/configure.ac +++ b/configure.ac @@ -405,6 +405,18 @@ case $ac_cv_audioresample_format in AC_SUBST(AUDIORESAMPLE_FORMAT_AUTO) esac +dnl Check for mmap (needed by allocators library) +AC_MSG_CHECKING(if mmap is supported) +AC_TRY_LINK( + [#include + #include + #include ], + [char * p = (char *)mmap(NULL, 10, PROT_READ, MAP_SHARED, -1, 2); + munmap(p,10);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_MMAP, 1, [Defined if mmap is supported])], + [AC_MSG_RESULT(no)] ) + dnl *** plug-ins to include *** dnl these are all the gst plug-ins, compilable without additional libs @@ -865,6 +877,7 @@ ext/theora/Makefile ext/vorbis/Makefile gst-libs/Makefile gst-libs/gst/Makefile +gst-libs/gst/allocators/Makefile gst-libs/gst/audio/Makefile gst-libs/gst/app/Makefile gst-libs/gst/fft/Makefile @@ -878,6 +891,8 @@ gst-libs/gst/pbutils/gstpluginsbaseversion.h gst-libs/gst/video/Makefile tools/Makefile pkgconfig/Makefile +pkgconfig/gstreamer-allocators.pc +pkgconfig/gstreamer-allocators-uninstalled.pc pkgconfig/gstreamer-audio.pc pkgconfig/gstreamer-audio-uninstalled.pc pkgconfig/gstreamer-app.pc diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am index 514a0b8..f7bb92e 100644 --- a/gst-libs/gst/Makefile.am +++ b/gst-libs/gst/Makefile.am @@ -8,7 +8,8 @@ SUBDIRS = \ audio \ pbutils \ riff \ - app + app \ + allocators noinst_HEADERS = gettext.h gst-i18n-plugin.h glib-compat-private.h diff --git a/gst-libs/gst/allocators/Makefile.am b/gst-libs/gst/allocators/Makefile.am new file mode 100644 index 0000000..6cc127c --- /dev/null +++ b/gst-libs/gst/allocators/Makefile.am @@ -0,0 +1,74 @@ +lib_LTLIBRARIES = libgstallocators-@GST_API_VERSION@.la + +libgstallocators_@GST_API_VERSION@_includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/allocators + +libgstallocators_@GST_API_VERSION@_include_HEADERS = \ + gstdmabuf.h + +noinst_HEADERS = + +libgstallocators_@GST_API_VERSION@_la_SOURCES = \ + gstdmabuf.c + +libgstallocators_@GST_API_VERSION@_la_LIBADD = $(GST_LIBS) $(LIBM) +libgstallocators_@GST_API_VERSION@_la_CFLAGS = $(GST_CFLAGS) +libgstallocators_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) + +if HAVE_INTROSPECTION +BUILT_GIRSOURCES = GstAllocators-@GST_API_VERSION@.gir + +gir_headers=$(patsubst %,$(srcdir)/%, $(libgstallocators_@GST_API_VERSION@_include_HEADERS)) +gir_sources=$(patsubst %,$(srcdir)/%, $(libgstallocators_@GST_API_VERSION@_la_SOURCES)) +gir_cincludes=$(patsubst %,--c-include='gst/allocators/%',$(libgstallocators_@GST_API_VERSION@_include_HEADERS)) + +GstAllocators-@GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libgstallocators-@GST_API_VERSION@.la + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + GST_PLUGIN_SYSTEM_PATH="" GST_PLUGIN_PATH="" GST_REGISTRY_UPDATE=no \ + $(INTROSPECTION_SCANNER) -v --namespace GstAllocators \ + --nsversion=@GST_API_VERSION@ \ + --strip-prefix=Gst \ + --warn-all \ + $(gir_cincludes) \ + --add-include-path=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \ + --library=libgstallocators-@GST_API_VERSION@.la \ + --include=Gst-@GST_API_VERSION@ \ + --libtool="$(top_builddir)/libtool" \ + --pkg gstreamer-@GST_API_VERSION@ \ + --pkg-export gstreamer-allocators-@GST_API_VERSION@ \ + --output $@ \ + $(gir_headers) \ + $(gir_sources) + +# INTROSPECTION_GIRDIR/INTROSPECTION_TYPELIBDIR aren't the right place to +# install anything - we need to install inside our prefix. +girdir = $(datadir)/gir-1.0 +gir_DATA = $(BUILT_GIRSOURCES) + +typelibsdir = $(libdir)/girepository-1.0/ + +typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) + +%.typelib: %.gir $(INTROSPECTION_COMPILER) + $(AM_V_GEN)PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" \ + $(INTROSPECTION_COMPILER) \ + --includedir=$(srcdir) \ + --includedir=$(builddir) \ + --includedir=`PKG_CONFIG_PATH="$(GST_PKG_CONFIG_PATH)" $(PKG_CONFIG) --variable=girdir gstreamer-@GST_API_VERSION@` \ + $(INTROSPECTION_COMPILER_OPTS) $< -o $(@F) + +CLEANFILES = $(BUILT_GIRSOURCES) $(typelibs_DATA) +endif + +Android.mk: Makefile.am + androgenizer -:PROJECT libgstallocators -:SHARED libgstallocators-@GST_API_VERSION@ \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstallocators_@GST_API_VERSION@_la_SOURCES) \ + -:CFLAGS $(DEFS) $(libgstallocators_@GST_API_VERSION@_la_CFLAGS) \ + -:LDFLAGS $(libgstallocators_@GST_API_VERSION@_la_LDFLAGS) \ + $(libgstallocators_@GST_API_VERSION@_la_LIBADD) \ + -ldl \ + -:HEADER_TARGET gstreamer-@GST_API_VERSION@/gst/allocators \ + -:HEADERS $(libgstallocatorsinclude_HEADERS) \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + > $@ diff --git a/gst-libs/gst/allocators/gstdmabuf.c b/gst-libs/gst/allocators/gstdmabuf.c new file mode 100644 index 0000000..67d5053 --- /dev/null +++ b/gst-libs/gst/allocators/gstdmabuf.c @@ -0,0 +1,325 @@ +/* + * gstdmabuf.c + * + * Copyright (C) Linaro SA 2013 + * Author: Benjamin Gaignard for Linaro. + * + * 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. + */ + +#include "gstdmabuf.h" + +#ifdef HAVE_MMAP +#include +#include + +/** + * GstDmaBufMemory + * @fd: the file descriptor associated this memory + * @data: mmapped address + * @mmapping_flags: mmapping flags + * @mmap_count: mmapping counter + * @lock: a mutex to make mmapping thread safe + */ +typedef struct +{ + GstMemory mem; + + gint fd; + gpointer data; + gint mmapping_flags; + gint mmap_count; + GMutex lock; +} GstDmaBufMemory; + +#define ALLOCATOR_NAME "dmabuf" + +GST_DEBUG_CATEGORY_STATIC (dmabuf_debug); +#define GST_CAT_DEFAULT dmabuf_debug + +static GstMemory * +_dmabuf_alloc (GstAllocator * allocator, gsize size, + GstAllocationParams * params) +{ + g_warning ("Use dmabuf_mem_alloc() to allocate from this allocator"); + + return NULL; +} + +static void +_dmabuf_free (GstAllocator * allocator, GstMemory * mem) +{ + GstDmaBufMemory *dbmem = (GstDmaBufMemory *) mem; + + if (dbmem->data) + g_warning ("Freeing memory still mapped"); + + close (dbmem->fd); + g_mutex_clear (&dbmem->lock); + g_slice_free (GstDmaBufMemory, dbmem); + GST_DEBUG ("%p: freed", dbmem); +} + +static gpointer +_dmabuf_mem_map (GstDmaBufMemory * mem, gsize maxsize, GstMapFlags flags) +{ + gint prot; + gpointer ret = NULL; + + g_mutex_lock (&mem->lock); + + prot = flags & GST_MAP_READ ? PROT_READ : 0; + prot |= flags & GST_MAP_WRITE ? PROT_WRITE : 0; + + /* do not mmap twice the buffer */ + if (mem->data) { + /* only return address if mapping flags are a subset + * of the previous flags */ + if (mem->mmapping_flags & prot) + ret = mem->data; + + goto out; + } + + if (mem->fd != -1) + mem->data = mmap (0, maxsize, prot, MAP_SHARED, mem->fd, 0); + + GST_DEBUG ("%p: fd %d: mapped %p", mem, mem->fd, mem->data); + + if (mem->data) { + mem->mmapping_flags = prot; + mem->mem.size = maxsize; + mem->mmap_count++; + ret = mem->data; + } + +out: + g_mutex_unlock (&mem->lock); + return ret; +} + +static gboolean +_dmabuf_mem_unmap (GstDmaBufMemory * mem) +{ + g_mutex_lock (&mem->lock); + + if (mem->data && !(--mem->mmap_count)) { + munmap ((void *) mem->data, mem->mem.size); + mem->data = NULL; + mem->mem.size = 0; + mem->mmapping_flags = 0; + GST_DEBUG ("%p: fd %d unmapped", mem, mem->fd); + } + g_mutex_unlock (&mem->lock); + return TRUE; +} + +static GstDmaBufMemory * +_dmabuf_mem_share (GstDmaBufMemory * mem, gssize offset, gsize size) +{ + GstDmaBufMemory *sub; + GstMemory *parent; + GST_DEBUG ("%p: share %" G_GSSIZE_FORMAT " %" G_GSIZE_FORMAT, mem, offset, + size); + + /* find the real parent */ + if ((parent = mem->mem.parent) == NULL) + parent = (GstMemory *) mem; + + if (size == -1) + size = mem->mem.size - offset; + + sub = g_slice_new (GstDmaBufMemory); + /* the shared memory is always readonly */ + gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) | + GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->mem.allocator, parent, + mem->mem.maxsize, mem->mem.align, mem->mem.offset + offset, size); + + return sub; +} + +static GstDmaBufMemory * +_dmabuf_mem_copy (GstDmaBufMemory * mem, gssize offset, gsize size) +{ + gint newfd = dup (mem->fd); + + if (newfd == -1) { + GST_WARNING ("Can't duplicate dmabuf file descriptor"); + return NULL; + } + + GST_DEBUG ("%p: copy %" G_GSSIZE_FORMAT " %" G_GSIZE_FORMAT, mem, offset, + size); + return (GstDmaBufMemory *) gst_dmabuf_allocator_alloc (mem->mem.allocator, + newfd, size); +} + +typedef struct +{ + GstAllocator parent; +} dmabuf_mem_Allocator; + +typedef struct +{ + GstAllocatorClass parent_class; +} dmabuf_mem_AllocatorClass; + +GType dmabuf_mem_allocator_get_type (void); +G_DEFINE_TYPE (dmabuf_mem_Allocator, dmabuf_mem_allocator, GST_TYPE_ALLOCATOR); + +#define GST_TYPE_DMABUF_ALLOCATOR (dmabuf_mem_allocator_get_type()) +#define GST_IS_DMABUF_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_DMABUF_ALLOCATOR)) + +static void +dmabuf_mem_allocator_class_init (dmabuf_mem_AllocatorClass * klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = _dmabuf_alloc; + allocator_class->free = _dmabuf_free; +} + +static void +dmabuf_mem_allocator_init (dmabuf_mem_Allocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = ALLOCATOR_NAME; + alloc->mem_map = (GstMemoryMapFunction) _dmabuf_mem_map; + alloc->mem_unmap = (GstMemoryUnmapFunction) _dmabuf_mem_unmap; + alloc->mem_share = (GstMemoryShareFunction) _dmabuf_mem_share; + alloc->mem_copy = (GstMemoryCopyFunction) _dmabuf_mem_copy; +} + +static void +_dmabuf_mem_init (void) +{ + GstAllocator *allocator = + g_object_new (dmabuf_mem_allocator_get_type (), NULL); + gst_allocator_register (ALLOCATOR_NAME, allocator); + + GST_DEBUG_CATEGORY_INIT (dmabuf_debug, "dmabuf", 0, "dmabuf memory"); +} + +/** + * gst_dmabuf_allocator_obtain + * return a dmabuf allocator or NULL if the allocator isn't found + * Use gst_object_unref() to release the allocator after usage. + */ +GstAllocator * +gst_dmabuf_allocator_obtain (void) +{ + static GOnce dmabuf_allocator_once = G_ONCE_INIT; + GstAllocator *allocator; + g_once (&dmabuf_allocator_once, (GThreadFunc) _dmabuf_mem_init, NULL); + + allocator = gst_allocator_find (ALLOCATOR_NAME); + if (!allocator) + GST_WARNING ("No allocator named %s found", ALLOCATOR_NAME); + return allocator; +} + +/* + * gst_dmabuf_allocator_alloc + * @allocator: allocator to be used for this memory + * @fd: dmabuf file descriptor + * @size: memory size + * return a GstMemory based on @allocator. + * When the buffer will be released dmabuf allocator will close the @fd. + * The memory is only mmapped on gst_buffer_mmap request. + */ +GstMemory * +gst_dmabuf_allocator_alloc (GstAllocator * allocator, gint fd, gsize size) +{ + GstDmaBufMemory *mem; + + if (!allocator) { + allocator = gst_dmabuf_allocator_obtain(); + } + + if (!GST_IS_DMABUF_ALLOCATOR (allocator)) { + GST_WARNING ("it isn't the correct allocator for dmabuf"); + return NULL; + } + + GST_DEBUG ("alloc from allocator %p", allocator); + + mem = g_slice_new (GstDmaBufMemory); + + gst_memory_init (GST_MEMORY_CAST (mem), 0, allocator, NULL, size, 0, 0, 0); + + mem->fd = fd; + mem->data = NULL; + mem->mmapping_flags = 0; + mem->mmap_count = 0; + g_mutex_init (&mem->lock); + + GST_DEBUG ("%p: fd: %d size %d", mem, mem->fd, mem->mem.maxsize); + return (GstMemory *) mem; +} + +/** + * gst_dmabuf_memory_get_fd + * @mem: the memory to get the file descriptor + * return the file descriptor associated with the memory + * else return -1 + */ +gint +gst_dmabuf_memory_get_fd (GstMemory * mem) +{ + GstDmaBufMemory *dbmem = (GstDmaBufMemory *) mem; + + g_return_val_if_fail (gst_is_dmabuf_memory (mem), -1); + + return dbmem->fd; +} + +/** + * gst_is_dmabuf_memory + * @mem: the memory to be check + * return true is the memory allocator is the dmabuf one + */ +gboolean +gst_is_dmabuf_memory (GstMemory * mem) +{ + return g_strcmp0 (mem->allocator->mem_type, ALLOCATOR_NAME) == 0; +} + +#else + +GstAllocator * gst_dmabuf_allocator_obtain(void) +{ + return NULL; +} + +GstMemory * gst_dmabuf_allocator_alloc(GstAllocator * allocator, gint fd, gsize size) +{ + return NULL; +} + +gint gst_dmabuf_memory_get_fd(GstMemory * mem) +{ + return -1; +} + +gboolean gst_is_dmabuf_memory(GstMemory * mem) +{ + return FALSE; +} + +#endif /* HAVE_MMAP */ diff --git a/gst-libs/gst/allocators/gstdmabuf.h b/gst-libs/gst/allocators/gstdmabuf.h new file mode 100644 index 0000000..02d7440 --- /dev/null +++ b/gst-libs/gst/allocators/gstdmabuf.h @@ -0,0 +1,35 @@ +/* + * gstdmabuf.h + * + * Copyright (C) Linaro SA 2013 + * Author: Benjamin Gaignard for Linaro. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GST_DMABUF_H__ +#define __GST_DMABUF_H__ + +#include + +GstAllocator * gst_dmabuf_allocator_obtain(void); + +GstMemory * gst_dmabuf_allocator_alloc(GstAllocator * allocator, gint fd, gsize size); + +gint gst_dmabuf_memory_get_fd(GstMemory * mem); + +gboolean gst_is_dmabuf_memory(GstMemory * mem); + +#endif /* __GST_DMABUF_H__ */ diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am index 766305a..9976f95 100644 --- a/pkgconfig/Makefile.am +++ b/pkgconfig/Makefile.am @@ -1,5 +1,6 @@ ### all of the standard pc files we need to generate pcverfiles = \ + gstreamer-allocators-@GST_API_VERSION@.pc \ gstreamer-audio-@GST_API_VERSION@.pc \ gstreamer-app-@GST_API_VERSION@.pc \ gstreamer-fft-@GST_API_VERSION@.pc \ @@ -12,6 +13,7 @@ pcverfiles = \ gstreamer-video-@GST_API_VERSION@.pc \ gstreamer-plugins-base-@GST_API_VERSION@.pc pcverfiles_uninstalled = \ + gstreamer-allocators-@GST_API_VERSION@-uninstalled.pc \ gstreamer-audio-@GST_API_VERSION@-uninstalled.pc \ gstreamer-app-@GST_API_VERSION@-uninstalled.pc \ gstreamer-fft-@GST_API_VERSION@-uninstalled.pc \ @@ -41,6 +43,7 @@ pkgconfig_DATA = $(pcverfiles) CLEANFILES = $(pcverfiles) $(pcverfiles_uninstalled) pcinfiles = \ + gstreamer-allocators.pc.in gstreamer-allocators-uninstalled.pc.in \ gstreamer-audio.pc.in gstreamer-audio-uninstalled.pc.in \ gstreamer-app.pc.in gstreamer-app-uninstalled.pc.in \ gstreamer-fft.pc.in gstreamer-fft-uninstalled.pc.in \ diff --git a/pkgconfig/gstreamer-allocators-uninstalled.pc.in b/pkgconfig/gstreamer-allocators-uninstalled.pc.in new file mode 100644 index 0000000..9736c10 --- /dev/null +++ b/pkgconfig/gstreamer-allocators-uninstalled.pc.in @@ -0,0 +1,16 @@ +# the standard variables don't make sense for an uninstalled copy +prefix= +exec_prefix= +libdir= +# includedir is builddir because it is used to find gstconfig.h in places +includedir=@abs_top_builddir@/gst-libs +girdir=@abs_top_builddir@/gst-libs/gst/allocators +typelibdir=@abs_top_builddir@/gst-libs/gst/allocators + +Name: GStreamer Allocators Library, Uninstalled +Description: Allocators implementation, uninstalled +Version: @VERSION@ +Requires: gstreamer-@GST_API_VERSION@ +Libs: @abs_top_builddir@/gst-libs/gst/allocators/libgstallocators-@GST_API_VERSION@.la +Cflags: -I@abs_top_srcdir@/gst-libs -I@abs_top_builddir@/gst-libs + diff --git a/pkgconfig/gstreamer-allocators.pc.in b/pkgconfig/gstreamer-allocators.pc.in new file mode 100644 index 0000000..f109158 --- /dev/null +++ b/pkgconfig/gstreamer-allocators.pc.in @@ -0,0 +1,16 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/gstreamer-@GST_API_VERSION@ +datarootdir=${prefix}/share +datadir=${datarootdir} +girdir=${datadir}/gir-1.0 +typelibdir=${libdir}/girepository-1.0 + +Name: GStreamer Allocators Library +Description: Allocators implementation +Requires: gstreamer-@GST_API_VERSION@ +Version: @VERSION@ +Libs: -L${libdir} -lgstallocators-@GST_API_VERSION@ +Cflags: -I${includedir} + -- 2.7.4