v4l2codec: Add allocator and pool implementation
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Thu, 13 Feb 2020 04:45:14 +0000 (23:45 -0500)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Tue, 31 Mar 2020 13:34:05 +0000 (09:34 -0400)
This is a pooling allocator and the buffer pool does nothing other then
reusing the GstBuffer structure. Note that the pool is an internal pool, so
the start/stop/set_config virtual functions are not implemented.

sys/v4l2codecs/gstv4l2codecallocator.c [new file with mode: 0644]
sys/v4l2codecs/gstv4l2codecallocator.h [new file with mode: 0644]
sys/v4l2codecs/gstv4l2codecpool.c [new file with mode: 0644]
sys/v4l2codecs/gstv4l2codecpool.h [new file with mode: 0644]
sys/v4l2codecs/meson.build

diff --git a/sys/v4l2codecs/gstv4l2codecallocator.c b/sys/v4l2codecs/gstv4l2codecallocator.c
new file mode 100644 (file)
index 0000000..eadeba6
--- /dev/null
@@ -0,0 +1,322 @@
+/* GStreamer
+ * Copyright (C) 2020 Collabora Ltd.
+ *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.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.
+ */
+
+#include "gstv4l2codecallocator.h"
+
+#include <gst/video/video.h>
+#include <sys/types.h>
+
+#define GST_CAT_DEFAULT allocator_debug
+GST_DEBUG_CATEGORY_STATIC (allocator_debug);
+
+typedef struct _GstV4l2CodecBuffer GstV4l2CodecBuffer;
+struct _GstV4l2CodecBuffer
+{
+  GstMemory *mem[GST_VIDEO_MAX_PLANES];
+  guint num_mems;
+
+  guint outstanding_mems;
+};
+
+struct _GstV4l2CodecAllocator
+{
+  GstDmaBufAllocator parent;
+
+  GQueue pool;
+  gint pool_size;
+  gboolean detached;
+
+  GstV4l2Decoder *decoder;
+  GstPadDirection direction;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator,
+    GST_TYPE_DMABUF_ALLOCATOR,
+    GST_DEBUG_CATEGORY_INIT (allocator_debug, "v4l2codecs-allocator", 0,
+        "V4L2 Codecs Allocator"));
+
+static gboolean gst_v4l2_codec_allocator_release (GstMiniObject * mini_object);
+
+static GQuark
+gst_v4l2_codec_buffer_quark (void)
+{
+  static gsize buffer_quark = 0;
+
+  if (g_once_init_enter (&buffer_quark)) {
+    GQuark quark = g_quark_from_string ("GstV4l2CodecBuffer");
+    g_once_init_leave (&buffer_quark, quark);
+  }
+
+  return buffer_quark;
+}
+
+static GstV4l2CodecBuffer *
+gst_v4l2_codec_buffer_new (GstAllocator * allocator, GstV4l2Decoder * decoder,
+    GstPadDirection direction, gint index)
+{
+  GstV4l2CodecBuffer *buf;
+  guint i, num_mems;
+  gint fds[GST_VIDEO_MAX_PLANES];
+  gsize sizes[GST_VIDEO_MAX_PLANES];
+  gsize offsets[GST_VIDEO_MAX_PLANES];
+
+  if (!gst_v4l2_decoder_export_buffer (decoder, direction, index, fds, sizes,
+          offsets, &num_mems))
+    return NULL;
+
+  buf = g_new0 (GstV4l2CodecBuffer, 1);
+  buf->num_mems = num_mems;
+  for (i = 0; i < buf->num_mems; i++) {
+    GstMemory *mem = gst_dmabuf_allocator_alloc (allocator, fds[i], sizes[i]);
+    gst_memory_resize (mem, offsets[i], sizes[i] - offsets[i]);
+
+    GST_MINI_OBJECT (mem)->dispose = gst_v4l2_codec_allocator_release;
+    gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
+        gst_v4l2_codec_buffer_quark (), buf, NULL);
+
+    /* On outstanding memory keeps a reference on the allocator, this is
+     * needed to break the cycle. */
+    gst_object_unref (mem->allocator);
+    buf->mem[i] = mem;
+  }
+
+  GST_DEBUG_OBJECT (allocator, "Create buffer %i with %i memory fds",
+      buf->index, buf->num_mems);
+
+  return buf;
+}
+
+static void
+gst_v4l2_codec_buffer_free (GstV4l2CodecBuffer * buf)
+{
+  guint i;
+
+  g_warn_if_fail (buf->outstanding_mems == 0);
+
+  GST_DEBUG_OBJECT (buf->mem[0]->allocator, "Freeing buffer %i", buf->index);
+
+  for (i = 0; i < buf->num_mems; i++) {
+    GstMemory *mem = buf->mem[i];
+    GST_MINI_OBJECT (mem)->dispose = NULL;
+    g_object_ref (mem->allocator);
+    gst_memory_unref (mem);
+  }
+
+  g_free (buf);
+}
+
+static void
+gst_v4l2_codec_buffer_acquire (GstV4l2CodecBuffer * buf)
+{
+  buf->outstanding_mems += buf->num_mems;
+}
+
+static gboolean
+gst_v4l2_codec_buffer_release_mem (GstV4l2CodecBuffer * buf)
+{
+  return (--buf->outstanding_mems == 0);
+}
+
+static gboolean
+gst_v4l2_codec_allocator_release (GstMiniObject * mini_object)
+{
+  GstMemory *mem = GST_MEMORY_CAST (mini_object);
+  GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (mem->allocator);
+  GstV4l2CodecBuffer *buf;
+
+  GST_OBJECT_LOCK (self);
+
+  buf = gst_mini_object_get_qdata (mini_object, gst_v4l2_codec_buffer_quark ());
+  gst_memory_ref (mem);
+
+  if (gst_v4l2_codec_buffer_release_mem (buf)) {
+    GST_DEBUG_OBJECT (self, "Placing back buffer %i into pool", buf->index);
+    g_queue_push_tail (&self->pool, buf);
+  }
+
+  GST_OBJECT_UNLOCK (self);
+
+  /* Keep last in case we are holding on the last allocator ref */
+  g_object_unref (mem->allocator);
+
+  /* Returns FALSE so that our mini object isn't freed */
+  return FALSE;
+}
+
+static gboolean
+gst_v4l2_codec_allocator_prepare (GstV4l2CodecAllocator * self)
+{
+  GstV4l2Decoder *decoder = self->decoder;
+  GstPadDirection direction = self->direction;
+  gint ret;
+  guint i;
+
+  ret = gst_v4l2_decoder_request_buffers (decoder, direction, self->pool_size);
+  if (ret < self->pool_size) {
+    if (ret >= 0)
+      GST_ERROR_OBJECT (self,
+          "%i buffer was needed, but only %i could be allocated",
+          self->pool_size, ret);
+    goto failed;
+  }
+
+  for (i = 0; i < self->pool_size; i++) {
+    GstV4l2CodecBuffer *buf = gst_v4l2_codec_buffer_new (GST_ALLOCATOR (self),
+        decoder, direction, i);
+    g_queue_push_tail (&self->pool, buf);
+  }
+
+  return TRUE;
+
+failed:
+  gst_v4l2_decoder_request_buffers (decoder, direction, 0);
+  return FALSE;
+}
+
+static void
+gst_v4l2_codec_allocator_init (GstV4l2CodecAllocator * self)
+{
+}
+
+static void
+gst_v4l2_codec_allocator_dispose (GObject * object)
+{
+  GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
+  GstV4l2CodecBuffer *buf;
+
+  while ((buf = g_queue_pop_head (&self->pool)))
+    gst_v4l2_codec_buffer_free (buf);
+
+  g_clear_object (&self->decoder);
+
+  G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->dispose (object);
+}
+
+static void
+gst_v4l2_codec_allocator_finalize (GObject * object)
+{
+  GstV4l2CodecAllocator *self = GST_V4L2_CODEC_ALLOCATOR (object);
+
+  if (!self->detached)
+    gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
+
+  G_OBJECT_CLASS (gst_v4l2_codec_allocator_parent_class)->finalize (object);
+}
+
+static void
+gst_v4l2_codec_allocator_class_init (GstV4l2CodecAllocatorClass * klass)
+{
+  GstAllocatorClass *allocator_class = GST_ALLOCATOR_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gst_v4l2_codec_allocator_dispose;
+  object_class->finalize = gst_v4l2_codec_allocator_finalize;
+  allocator_class->alloc = NULL;
+}
+
+GstV4l2CodecAllocator *
+gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
+    GstPadDirection direction, guint num_buffers)
+{
+  GstV4l2CodecAllocator *self =
+      g_object_new (GST_TYPE_V4L2_CODEC_ALLOCATOR, NULL);
+
+  self->decoder = g_object_ref (decoder);
+  self->direction = direction;
+  self->pool_size = num_buffers;
+
+  if (!gst_v4l2_codec_allocator_prepare (self)) {
+    g_object_unref (self);
+    return NULL;
+  }
+
+  return self;
+}
+
+GstMemory *
+gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * self)
+{
+  GstV4l2CodecBuffer *buf;
+  GstMemory *mem = NULL;
+
+  GST_OBJECT_LOCK (self);
+  buf = g_queue_pop_head (&self->pool);
+  if (buf) {
+    GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
+    g_warn_if_fail (buf->num_mems == 1);
+    mem = buf->mem[0];
+    g_object_ref (mem->allocator);
+    buf->outstanding_mems++;
+  }
+  GST_OBJECT_UNLOCK (self);
+
+  return mem;
+}
+
+gboolean
+gst_v4l2_codec_allocator_prepare_buffer (GstV4l2CodecAllocator * self,
+    GstBuffer * gstbuf)
+{
+  GstV4l2CodecBuffer *buf;
+  guint i;
+
+  GST_OBJECT_LOCK (self);
+
+  buf = g_queue_pop_head (&self->pool);
+  if (!buf) {
+    GST_OBJECT_UNLOCK (self);
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (self, "Allocated buffer %i", buf->index);
+
+  gst_v4l2_codec_buffer_acquire (buf);
+  for (i = 0; i < buf->num_mems; i++) {
+    gst_buffer_append_memory (gstbuf, buf->mem[i]);
+    g_object_ref (buf->mem[i]->allocator);
+  }
+
+  GST_OBJECT_UNLOCK (self);
+
+  return TRUE;
+}
+
+guint
+gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator * self)
+{
+  guint size;
+
+  GST_OBJECT_LOCK (self);
+  size = self->pool_size;
+  GST_OBJECT_UNLOCK (self);
+
+  return size;
+}
+
+void
+gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self)
+{
+  GST_OBJECT_LOCK (self);
+  if (!self->detached) {
+    self->detached = TRUE;
+    gst_v4l2_decoder_request_buffers (self->decoder, self->direction, 0);
+  }
+  GST_OBJECT_UNLOCK (self);
+}
diff --git a/sys/v4l2codecs/gstv4l2codecallocator.h b/sys/v4l2codecs/gstv4l2codecallocator.h
new file mode 100644 (file)
index 0000000..62c2332
--- /dev/null
@@ -0,0 +1,48 @@
+/* GStreamer
+ * Copyright (C) 2020 Collabora Ltd.
+ *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.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.
+ */
+
+#ifndef _GST_V4L2_CODEC_ALLOCATOR_H_
+#define _GST_V4L2_CODEC_ALLOCATOR_H_
+
+#include <gst/allocators/allocators.h>
+#include <gst/gst.h>
+
+#include "gstv4l2codecdevice.h"
+#include "gstv4l2decoder.h"
+
+#define GST_TYPE_V4L2_CODEC_ALLOCATOR gst_v4l2_codec_allocator_get_type ()
+G_DECLARE_FINAL_TYPE (GstV4l2CodecAllocator, gst_v4l2_codec_allocator,
+    GST_V4L2_CODEC, ALLOCATOR, GstDmaBufAllocator);
+
+
+GstV4l2CodecAllocator  *gst_v4l2_codec_allocator_new (GstV4l2Decoder * decoder,
+                                                      GstPadDirection direction,
+                                                      guint num_buffers);
+
+GstMemory              *gst_v4l2_codec_allocator_alloc (GstV4l2CodecAllocator * allocator);
+
+gboolean                gst_v4l2_codec_allocator_prepare_buffer (GstV4l2CodecAllocator * allocator,
+                                                                 GstBuffer * buffer);
+
+guint                   gst_v4l2_codec_allocator_get_pool_size (GstV4l2CodecAllocator *self);
+
+void                    gst_v4l2_codec_allocator_detach (GstV4l2CodecAllocator * self);
+
+#endif /* _GST_V4L2_CODECS_ALLOCATOR_H_ */
diff --git a/sys/v4l2codecs/gstv4l2codecpool.c b/sys/v4l2codecs/gstv4l2codecpool.c
new file mode 100644 (file)
index 0000000..5ef9fea
--- /dev/null
@@ -0,0 +1,116 @@
+/* GStreamer
+ * Copyright (C) 2020 Collabora Ltd.
+ *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.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.
+ */
+
+#include "gstv4l2codecpool.h"
+
+struct _GstV4l2CodecPool
+{
+  GstBufferPool parent;
+  GstAtomicQueue *queue;
+  GstV4l2CodecAllocator *allocator;
+};
+
+G_DEFINE_TYPE (GstV4l2CodecPool, gst_v4l2_codec_pool, GST_TYPE_BUFFER_POOL);
+
+static GstFlowReturn
+gst_v4l2_codec_pool_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer,
+    GstBufferPoolAcquireParams * params)
+{
+  GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (pool);
+  GstBuffer *buf = gst_atomic_queue_pop (self->queue);
+  if (!buf)
+    return GST_FLOW_ERROR;
+
+  if (!gst_v4l2_codec_allocator_prepare_buffer (self->allocator, buf))
+    return GST_FLOW_ERROR;
+
+  *buffer = buf;
+  return GST_FLOW_OK;
+}
+
+static void
+gst_v4l2_codec_pool_reset_buffer (GstBufferPool * pool, GstBuffer * buffer)
+{
+  GstBufferPoolClass *klass =
+      GST_BUFFER_POOL_CLASS (gst_v4l2_codec_pool_parent_class);
+
+  /* Clears all the memories and only pool the GstBuffer objects */
+  gst_buffer_remove_all_memory (buffer);
+  klass->reset_buffer (pool, buffer);
+  GST_BUFFER_FLAGS (buffer) = 0;
+}
+
+static void
+gst_v4l2_codec_pool_release_buffer (GstBufferPool * pool, GstBuffer * buffer)
+{
+  GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (pool);
+  gst_atomic_queue_push (self->queue, buffer);
+}
+
+static void
+gst_v4l2_codec_pool_init (GstV4l2CodecPool * self)
+{
+  self->queue = gst_atomic_queue_new (4);
+}
+
+static void
+gst_v4l2_codec_pool_finalize (GObject * object)
+{
+  GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (object);
+  GstBuffer *buf;
+
+  while ((buf = gst_atomic_queue_pop (self->queue)))
+    gst_buffer_unref (buf);
+
+  gst_atomic_queue_unref (self->queue);
+  g_object_unref (self->allocator);
+
+  G_OBJECT_CLASS (gst_v4l2_codec_pool_parent_class)->finalize (object);
+}
+
+static void
+gst_v4l2_codec_pool_class_init (GstV4l2CodecPoolClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass);
+
+  object_class->finalize = gst_v4l2_codec_pool_finalize;
+  pool_class->start = NULL;
+  pool_class->acquire_buffer = gst_v4l2_codec_pool_acquire_buffer;
+  pool_class->reset_buffer = gst_v4l2_codec_pool_reset_buffer;
+  pool_class->release_buffer = gst_v4l2_codec_pool_release_buffer;
+}
+
+GstV4l2CodecPool *
+gst_v4l2_codec_pool_new (GstV4l2CodecAllocator * allocator)
+{
+  GstV4l2CodecPool *pool = g_object_new (GST_TYPE_V4L2_CODEC_POOL, NULL);
+  gsize pool_size;
+
+  pool->allocator = g_object_ref (allocator);
+
+  pool_size = gst_v4l2_codec_allocator_get_pool_size (allocator);
+  for (gsize i = 0; i < pool_size; i++) {
+    GstBuffer *buffer = gst_buffer_new ();
+    gst_atomic_queue_push (pool->queue, buffer);
+  }
+
+  return pool;
+}
diff --git a/sys/v4l2codecs/gstv4l2codecpool.h b/sys/v4l2codecs/gstv4l2codecpool.h
new file mode 100644 (file)
index 0000000..9468139
--- /dev/null
@@ -0,0 +1,34 @@
+/* GStreamer
+ * Copyright (C) 2020 Collabora Ltd.
+ *   Author: Nicolas Dufresne <nicolas.dufresne@collabora.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.
+ */
+
+#include <gst/gst.h>
+
+#include "gstv4l2codecallocator.h"
+
+#ifndef __GST_V4L2_CODEC_POOL_H__
+#define __GST_V4L2_CODEC_POOL_H__
+
+#define GST_TYPE_V4L2_CODEC_POOL gst_v4l2_codec_pool_get_type()
+G_DECLARE_FINAL_TYPE(GstV4l2CodecPool, gst_v4l2_codec_pool, GST,
+    V4L2_CODEC_POOL, GstBufferPool)
+
+GstV4l2CodecPool *gst_v4l2_codec_pool_new  (GstV4l2CodecAllocator *allocator);
+
+#endif /* __GST_V4L2_CODEC_POOL_H__ */
index 048f63baadb464208e15b8dd29c6860c2b50531d..89efe5306c9b002b884f8e05e91181a6cc903fc6 100644 (file)
@@ -1,7 +1,9 @@
 v4l2codecs_sources = [
   'plugin.c',
+  'gstv4l2codecallocator.c',
   'gstv4l2codecdevice.c',
   'gstv4l2codech264dec.c',
+  'gstv4l2codecpool.c',
   'gstv4l2decoder.c',
   'gstv4l2format.c',
 ]
@@ -14,7 +16,7 @@ if libgudev_dep.found()
     c_args : gst_plugins_bad_args,
     cpp_args: gst_plugins_bad_args,
     include_directories : [configinc],
-    dependencies : [gstbase_dep, gstcodecs_dep, libgudev_dep],
+    dependencies : [gstbase_dep, gstcodecs_dep, gstallocators_dep, libgudev_dep],
     install : true,
     install_dir : plugins_install_dir,
   )