summaryrefslogtreecommitdiff
path: root/gst-libs
diff options
context:
space:
mode:
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2018-02-16 15:55:45 +0200
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2018-08-03 13:20:02 +0300
commit9cf58eb3e4621ea8261e1443bda496b6da9f8833 (patch)
treed1ab351d400f0f013e55668afb9a13eaaba0f6ef /gst-libs
parentc9226e6e804167836ce176c19ce63719c5b82822 (diff)
downloadgstreamer-plugins-bad-9cf58eb3e4621ea8261e1443bda496b6da9f8833.tar.gz
libs: audio: add new GstPlanarAudioAdapter class
This is a GstAdapter, but for planar audio buffers. https://bugzilla.gnome.org/show_bug.cgi?id=793605
Diffstat (limited to 'gst-libs')
-rw-r--r--gst-libs/gst/audio/Makefile.am8
-rw-r--r--gst-libs/gst/audio/gstplanaraudioadapter.c391
-rw-r--r--gst-libs/gst/audio/gstplanaraudioadapter.h95
-rw-r--r--gst-libs/gst/audio/meson.build4
4 files changed, 494 insertions, 4 deletions
diff --git a/gst-libs/gst/audio/Makefile.am b/gst-libs/gst/audio/Makefile.am
index 245f93c3f..5892ec6c7 100644
--- a/gst-libs/gst/audio/Makefile.am
+++ b/gst-libs/gst/audio/Makefile.am
@@ -4,7 +4,8 @@ lib_LTLIBRARIES = libgstbadaudio-@GST_API_VERSION@.la
CLEANFILES =
libgstbadaudio_@GST_API_VERSION@_la_SOURCES = \
- gstnonstreamaudiodecoder.c
+ gstnonstreamaudiodecoder.c \
+ gstplanaraudioadapter.c
nodist_libgstbadaudio_@GST_API_VERSION@_la_SOURCES = $(BUILT_SOURCES)
@@ -23,4 +24,7 @@ libgstbadaudio_@GST_API_VERSION@_la_LIBADD = \
libgstbadaudio_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS)
libgstaudio_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/audio
-libgstaudio_@GST_API_VERSION@include_HEADERS = gstnonstreamaudiodecoder.h audio-bad-prelude.h
+libgstaudio_@GST_API_VERSION@include_HEADERS = \
+ gstnonstreamaudiodecoder.h \
+ audio-bad-prelude.h \
+ gstplanaraudioadapter.h
diff --git a/gst-libs/gst/audio/gstplanaraudioadapter.c b/gst-libs/gst/audio/gstplanaraudioadapter.c
new file mode 100644
index 000000000..c476980bf
--- /dev/null
+++ b/gst-libs/gst/audio/gstplanaraudioadapter.c
@@ -0,0 +1,391 @@
+/* GStreamer
+ * Copyright (C) 2018 Collabora Ltd
+ * @author George Kiagiadakis <george.kiagiadakis@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.
+ */
+
+/**
+ * SECTION:gstplanaraudioadapter
+ * @title: GstPlanarAudioAdapter
+ * @short_description: adapts incoming audio data on a sink pad into chunks of N samples
+ *
+ * This class is similar to GstAdapter, but it is made to work with
+ * non-interleaved (planar) audio buffers. Before using, an audio format
+ * must be configured with gst_planar_audio_adapter_configure()
+ */
+
+#include "gstplanaraudioadapter.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_planar_audio_adapter_debug);
+#define GST_CAT_DEFAULT gst_planar_audio_adapter_debug
+
+struct _GstPlanarAudioAdapter
+{
+ GObject object;
+
+ GstAudioInfo info;
+ GSList *buflist;
+ GSList *buflist_end;
+ gsize samples;
+ gsize skip;
+ guint count;
+};
+
+struct _GstPlanarAudioAdapterClass
+{
+ GObjectClass parent_class;
+};
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (gst_planar_audio_adapter_debug, "planaraudioadapter", \
+ 0, "object to splice and merge audio buffers to desired size")
+#define gst_planar_audio_adapter_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstPlanarAudioAdapter, gst_planar_audio_adapter,
+ G_TYPE_OBJECT, _do_init);
+
+static void gst_planar_audio_adapter_dispose (GObject * object);
+
+static void
+gst_planar_audio_adapter_class_init (GstPlanarAudioAdapterClass * klass)
+{
+ GObjectClass *object = G_OBJECT_CLASS (klass);
+
+ object->dispose = gst_planar_audio_adapter_dispose;
+}
+
+static void
+gst_planar_audio_adapter_init (GstPlanarAudioAdapter * adapter)
+{
+}
+
+static void
+gst_planar_audio_adapter_dispose (GObject * object)
+{
+ GstPlanarAudioAdapter *adapter = GST_PLANAR_AUDIO_ADAPTER (object);
+
+ gst_planar_audio_adapter_clear (adapter);
+
+ GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
+}
+
+/**
+ * gst_planar_audio_adapter_new:
+ *
+ * Creates a new #GstPlanarAudioAdapter. Free with g_object_unref().
+ *
+ * Returns: (transfer full): a new #GstPlanarAudioAdapter
+ */
+GstPlanarAudioAdapter *
+gst_planar_audio_adapter_new (void)
+{
+ return g_object_new (GST_TYPE_PLANAR_AUDIO_ADAPTER, NULL);
+}
+
+/**
+ * gst_planar_audio_adapter_configure:
+ * @adapter: a #GstPlanarAudioAdapter
+ * @info: a #GstAudioInfo describing the format of the audio data
+ *
+ * Sets up the @adapter to handle audio data of the specified audio format.
+ * Note that this will internally clear the adapter and re-initialize it.
+ */
+void
+gst_planar_audio_adapter_configure (GstPlanarAudioAdapter * adapter,
+ const GstAudioInfo * info)
+{
+ g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (GST_AUDIO_INFO_IS_VALID (info));
+ g_return_if_fail (info->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED);
+
+ gst_planar_audio_adapter_clear (adapter);
+ adapter->info = *info;
+}
+
+/**
+ * gst_planar_audio_adapter_clear:
+ * @adapter: a #GstPlanarAudioAdapter
+ *
+ * Removes all buffers from @adapter.
+ */
+void
+gst_planar_audio_adapter_clear (GstPlanarAudioAdapter * adapter)
+{
+ g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
+
+ g_slist_foreach (adapter->buflist, (GFunc) gst_mini_object_unref, NULL);
+ g_slist_free (adapter->buflist);
+ adapter->buflist = NULL;
+ adapter->buflist_end = NULL;
+ adapter->count = 0;
+ adapter->samples = 0;
+ adapter->skip = 0;
+}
+
+/**
+ * gst_planar_audio_adapter_push:
+ * @adapter: a #GstPlanarAudioAdapter
+ * @buf: (transfer full): a #GstBuffer to queue in the adapter
+ *
+ * Adds the data from @buf to the data stored inside @adapter and takes
+ * ownership of the buffer.
+ */
+void
+gst_planar_audio_adapter_push (GstPlanarAudioAdapter * adapter, GstBuffer * buf)
+{
+ GstAudioMeta *meta;
+ gsize samples;
+
+ g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
+ g_return_if_fail (GST_AUDIO_INFO_IS_VALID (&adapter->info));
+ g_return_if_fail (GST_IS_BUFFER (buf));
+
+ meta = gst_buffer_get_audio_meta (buf);
+ g_return_if_fail (meta != NULL);
+ g_return_if_fail (gst_audio_info_is_equal (&meta->info, &adapter->info));
+
+ samples = meta->samples;
+ adapter->samples += samples;
+
+ if (G_UNLIKELY (adapter->buflist == NULL)) {
+ GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " samples",
+ buf, samples);
+ adapter->buflist = adapter->buflist_end = g_slist_append (NULL, buf);
+ } else {
+ /* Otherwise append to the end, and advance our end pointer */
+ GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " samples at end, "
+ "samples now %" G_GSIZE_FORMAT, buf, samples, adapter->samples);
+ adapter->buflist_end = g_slist_append (adapter->buflist_end, buf);
+ adapter->buflist_end = g_slist_next (adapter->buflist_end);
+ }
+ ++adapter->count;
+}
+
+static void
+gst_planar_audio_adapter_flush_unchecked (GstPlanarAudioAdapter * adapter,
+ gsize to_flush)
+{
+ GSList *cur = adapter->buflist;
+ gsize cur_samples;
+
+ while (to_flush > 0) {
+ cur_samples = gst_buffer_get_audio_meta (cur->data)->samples;
+ cur_samples -= adapter->skip;
+
+ if (to_flush >= cur_samples) {
+ gst_buffer_unref (cur->data);
+ cur = g_slist_remove_link (cur, cur);
+
+ to_flush -= cur_samples;
+ adapter->samples -= cur_samples;
+ adapter->skip = 0;
+ --adapter->count;
+ } else {
+ adapter->samples -= to_flush;
+ adapter->skip += to_flush;
+ to_flush = 0;
+ }
+ }
+
+ adapter->buflist = cur;
+ if (!adapter->buflist)
+ adapter->buflist_end = NULL;
+}
+
+/**
+ * gst_planar_audio_adapter_flush:
+ * @adapter: a #GstPlanarAudioAdapter
+ * @to_flush: the number of samples to flush
+ *
+ * Flushes the first @to_flush samples in the @adapter. The caller must ensure
+ * that at least this many samples are available.
+ */
+void
+gst_planar_audio_adapter_flush (GstPlanarAudioAdapter * adapter, gsize to_flush)
+{
+ g_return_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter));
+ g_return_if_fail (to_flush <= adapter->samples);
+
+ /* flushing out 0 bytes will do nothing */
+ if (G_UNLIKELY (to_flush == 0))
+ return;
+
+ gst_planar_audio_adapter_flush_unchecked (adapter, to_flush);
+}
+
+/**
+ * gst_planar_audio_adapter_get_buffer:
+ * @adapter: a #GstPlanarAudioAdapter
+ * @nsamples: the number of samples to get
+ * @flags: hint the intended use of the returned buffer
+ *
+ * Returns a #GstBuffer containing the first @nsamples of the @adapter, but
+ * does not flush them from the adapter.
+ * Use gst_planar_audio_adapter_take_buffer() for flushing at the same time.
+ *
+ * The map @flags can be used to give an optimization hint to this function.
+ * When the requested buffer is meant to be mapped only for reading, it might
+ * be possible to avoid copying memory in some cases.
+ *
+ * Caller owns a reference to the returned buffer. gst_buffer_unref() after
+ * usage.
+ *
+ * Free-function: gst_buffer_unref
+ *
+ * Returns: (transfer full) (nullable): a #GstBuffer containing the first
+ * @nsamples of the adapter, or %NULL if @nsamples samples are not
+ * available. gst_buffer_unref() when no longer needed.
+ */
+GstBuffer *
+gst_planar_audio_adapter_get_buffer (GstPlanarAudioAdapter * adapter,
+ gsize nsamples, GstMapFlags flags)
+{
+ GstBuffer *buffer = NULL;
+ GstBuffer *cur;
+ gsize hsamples, skip;
+
+ g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter), NULL);
+ g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&adapter->info), NULL);
+ g_return_val_if_fail (nsamples > 0, NULL);
+
+ GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " samples",
+ nsamples);
+
+ /* we don't have enough data, return NULL. This is unlikely
+ * as one usually does an _available() first instead of grabbing a
+ * random size. */
+ if (G_UNLIKELY (nsamples > adapter->samples))
+ return NULL;
+
+ cur = adapter->buflist->data;
+ skip = adapter->skip;
+ hsamples = gst_buffer_get_audio_meta (cur)->samples;
+
+
+ if (skip == 0 && hsamples == nsamples) {
+ /* our head buffer fits exactly the requirements */
+ GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
+ " as head buffer", nsamples);
+
+ buffer = gst_buffer_ref (cur);
+
+ } else if (hsamples >= nsamples + skip && !(flags & GST_MAP_WRITE)) {
+ /* return a buffer with the same data as our head buffer but with
+ * a modified GstAudioMeta that maps only the parts of the planes
+ * that should be made available to the caller. This is more efficient
+ * for reading (no mem copy), but will hit performance if the caller
+ * decides to map for writing or otherwise do a deep copy */
+ GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
+ " via copy region", nsamples);
+
+ buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, 0, -1);
+ gst_audio_buffer_truncate (buffer, adapter->info.bpf, skip, nsamples);
+
+ } else {
+ gint c, bps;
+ GstAudioMeta *meta;
+
+ /* construct a buffer with concatenated memory chunks from the appropriate
+ * places. These memories will be copied into a single memory chunk
+ * as soon as the buffer is mapped */
+ GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " samples"
+ " via memory concatenation", nsamples);
+
+ bps = adapter->info.finfo->width / 8;
+
+ for (c = 0; c < adapter->info.channels; c++) {
+ gsize need = nsamples;
+ gsize cur_skip = skip;
+ gsize take_from_cur;
+ GSList *cur_node = adapter->buflist;
+
+ while (need > 0) {
+ cur = cur_node->data;
+ meta = gst_buffer_get_audio_meta (cur);
+ take_from_cur = need > (meta->samples - cur_skip) ?
+ meta->samples - cur_skip : need;
+
+ cur = gst_buffer_copy_region (cur, GST_BUFFER_COPY_MEMORY,
+ meta->offsets[c] + cur_skip * bps, take_from_cur * bps);
+
+ if (!buffer)
+ buffer = cur;
+ else
+ gst_buffer_append (buffer, cur);
+
+ need -= take_from_cur;
+ cur_skip = 0;
+ cur_node = g_slist_next (cur_node);
+ }
+ }
+
+ gst_buffer_add_audio_meta (buffer, &adapter->info, nsamples, NULL);
+ }
+
+ return buffer;
+}
+
+/**
+ * gst_planar_audio_adapter_take_buffer:
+ * @adapter: a #GstPlanarAudioAdapter
+ * @nsamples: the number of samples to take
+ * @flags: hint the intended use of the returned buffer
+ *
+ * Returns a #GstBuffer containing the first @nsamples bytes of the
+ * @adapter. The returned bytes will be flushed from the adapter.
+ *
+ * See gst_planar_audio_adapter_get_buffer() for more details.
+ *
+ * Caller owns a reference to the returned buffer. gst_buffer_unref() after
+ * usage.
+ *
+ * Free-function: gst_buffer_unref
+ *
+ * Returns: (transfer full) (nullable): a #GstBuffer containing the first
+ * @nsamples of the adapter, or %NULL if @nsamples samples are not
+ * available. gst_buffer_unref() when no longer needed.
+ */
+GstBuffer *
+gst_planar_audio_adapter_take_buffer (GstPlanarAudioAdapter * adapter,
+ gsize nsamples, GstMapFlags flags)
+{
+ GstBuffer *buffer;
+
+ buffer = gst_planar_audio_adapter_get_buffer (adapter, nsamples, flags);
+ if (buffer)
+ gst_planar_audio_adapter_flush_unchecked (adapter, nsamples);
+
+ return buffer;
+}
+
+/**
+ * gst_planar_audio_adapter_available:
+ * @adapter: a #GstPlanarAudioAdapter
+ *
+ * Gets the maximum amount of samples available, that is it returns the maximum
+ * value that can be supplied to gst_planar_audio_adapter_get_buffer() without
+ * that function returning %NULL.
+ *
+ * Returns: number of samples available in @adapter
+ */
+gsize
+gst_planar_audio_adapter_available (GstPlanarAudioAdapter * adapter)
+{
+ g_return_val_if_fail (GST_IS_PLANAR_AUDIO_ADAPTER (adapter), 0);
+
+ return adapter->samples;
+}
diff --git a/gst-libs/gst/audio/gstplanaraudioadapter.h b/gst-libs/gst/audio/gstplanaraudioadapter.h
new file mode 100644
index 000000000..85b6380c1
--- /dev/null
+++ b/gst-libs/gst/audio/gstplanaraudioadapter.h
@@ -0,0 +1,95 @@
+/* GStreamer
+ * Copyright (C) 2018 Collabora Ltd.
+ * @author George Kiagiadakis <george.kiagiadakis@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_PLANAR_AUDIO_ADAPTER_H__
+#define __GST_PLANAR_AUDIO_ADAPTER_H__
+
+#ifndef GST_USE_UNSTABLE_API
+#warning "The Base library from gst-plugins-bad is unstable API and may change in future."
+#warning "You can define GST_USE_UNSTABLE_API to avoid this warning."
+#endif
+
+#include <gst/gst.h>
+#include <gst/audio/audio-info.h>
+#include <gst/audio/audio-bad-prelude.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_PLANAR_AUDIO_ADAPTER \
+ (gst_planar_audio_adapter_get_type())
+#define GST_PLANAR_AUDIO_ADAPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_PLANAR_AUDIO_ADAPTER, GstPlanarAudioAdapter))
+#define GST_PLANAR_AUDIO_ADAPTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_PLANAR_AUDIO_ADAPTER, GstPlanarAudioAdapterClass))
+#define GST_PLANAR_AUDIO_ADAPTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLANAR_AUDIO_ADAPTER, GstPlanarAudioAdapterClass))
+#define GST_IS_PLANAR_AUDIO_ADAPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_PLANAR_AUDIO_ADAPTER))
+#define GST_IS_PLANAR_AUDIO_ADAPTER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_PLANAR_AUDIO_ADAPTER))
+
+/**
+ * GstPlanarAudioAdapter:
+ *
+ * The opaque #GstPlanarAudioAdapter data structure.
+ */
+typedef struct _GstPlanarAudioAdapter GstPlanarAudioAdapter;
+typedef struct _GstPlanarAudioAdapterClass GstPlanarAudioAdapterClass;
+
+GST_AUDIO_BAD_API
+GType gst_planar_audio_adapter_get_type (void);
+
+GST_AUDIO_BAD_API
+GstPlanarAudioAdapter * gst_planar_audio_adapter_new (void) G_GNUC_MALLOC;
+
+GST_AUDIO_BAD_API
+void gst_planar_audio_adapter_configure (GstPlanarAudioAdapter * adapter,
+ const GstAudioInfo * info);
+
+GST_AUDIO_BAD_API
+void gst_planar_audio_adapter_clear (GstPlanarAudioAdapter * adapter);
+
+GST_AUDIO_BAD_API
+void gst_planar_audio_adapter_push (GstPlanarAudioAdapter * adapter,
+ GstBuffer * buf);
+
+GST_AUDIO_BAD_API
+void gst_planar_audio_adapter_flush (GstPlanarAudioAdapter * adapter,
+ gsize to_flush);
+
+GST_AUDIO_BAD_API
+GstBuffer * gst_planar_audio_adapter_get_buffer (GstPlanarAudioAdapter * adapter,
+ gsize nsamples, GstMapFlags flags);
+
+GST_AUDIO_BAD_API
+GstBuffer * gst_planar_audio_adapter_take_buffer (GstPlanarAudioAdapter * adapter,
+ gsize nsamples, GstMapFlags flags);
+
+GST_AUDIO_BAD_API
+gsize gst_planar_audio_adapter_available (GstPlanarAudioAdapter * adapter);
+
+
+#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstPlanarAudioAdapter, gst_object_unref)
+#endif
+
+G_END_DECLS
+
+#endif /* __GST_PLANAR_AUDIO_ADAPTER_H__ */
diff --git a/gst-libs/gst/audio/meson.build b/gst-libs/gst/audio/meson.build
index bc62c9f6f..9d23ef3e3 100644
--- a/gst-libs/gst/audio/meson.build
+++ b/gst-libs/gst/audio/meson.build
@@ -1,5 +1,5 @@
-badaudio_sources = ['gstnonstreamaudiodecoder.c']
-badaudio_headers = ['gstnonstreamaudiodecoder.h', 'audio-bad-prelude.h']
+badaudio_sources = ['gstnonstreamaudiodecoder.c', 'gstplanaraudioadapter.c']
+badaudio_headers = ['gstnonstreamaudiodecoder.h', 'audio-bad-prelude.h', 'gstplanaraudioadapter.h']
install_headers(badaudio_headers, subdir : 'gstreamer-1.0/gst/audio')