diff options
author | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2018-02-16 15:55:45 +0200 |
---|---|---|
committer | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2018-08-03 13:20:02 +0300 |
commit | 9cf58eb3e4621ea8261e1443bda496b6da9f8833 (patch) | |
tree | d1ab351d400f0f013e55668afb9a13eaaba0f6ef | |
parent | c9226e6e804167836ce176c19ce63719c5b82822 (diff) |
libs: audio: add new GstPlanarAudioAdapter class
This is a GstAdapter, but for planar audio buffers.
https://bugzilla.gnome.org/show_bug.cgi?id=793605
-rw-r--r-- | gst-libs/gst/audio/Makefile.am | 8 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstplanaraudioadapter.c | 391 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstplanaraudioadapter.h | 95 | ||||
-rw-r--r-- | gst-libs/gst/audio/meson.build | 4 | ||||
-rw-r--r-- | tests/check/Makefile.am | 7 | ||||
-rw-r--r-- | tests/check/libs/planaraudioadapter.c | 361 | ||||
-rw-r--r-- | tests/check/meson.build | 1 |
7 files changed, 863 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') diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index b54d8780c..8d1733ff3 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -285,6 +285,7 @@ check_PROGRAMS = \ libs/h264parser \ libs/h265parser \ libs/vp8parser \ + libs/planaraudioadapter \ $(check_uvch264) \ libs/vc1parser \ $(check_x265enc) \ @@ -589,6 +590,12 @@ elements_msdk_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS elements_msdk_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_VIDEO_LIBS) $(GST_BASE_LIBS) $(LDADD) elements_msdk_SOURCES = elements/msdkh264enc.c +libs_planaraudioadapter_LDADD = \ + $(top_builddir)/gst-libs/gst/audio/libgstbadaudio-@GST_API_VERSION@.la \ + $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_AUDIO_LIBS) $(LDADD) +libs_planaraudioadapter_CFLAGS = \ + $(GST_PLUGINS_BASE_CLAGS) $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_BASE_CFLAGS) $(CFLAGS) $(AM_CFLAGS) distclean-local-orc: rm -rf orc diff --git a/tests/check/libs/planaraudioadapter.c b/tests/check/libs/planaraudioadapter.c new file mode 100644 index 000000000..8f845d8e5 --- /dev/null +++ b/tests/check/libs/planaraudioadapter.c @@ -0,0 +1,361 @@ +/* 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. + */ + +#include <gst/check/gstcheck.h> +#include <gst/audio/gstplanaraudioadapter.h> + +static GstBuffer * +generate_buffer (GstAudioInfo * info, gsize nsamples, + gsize dummy_start, gsize dummy_end, gpointer * data_ret) +{ + gpointer data; + GstBuffer *buf; + gsize buf_sz; + gsize offsets[8]; + gint c, bps; + + fail_unless (info->channels <= 8); + + bps = info->finfo->width / 8; + buf_sz = info->channels * (nsamples + dummy_start + dummy_end) * bps; + data = g_malloc (buf_sz); + fail_unless (data); + buf = gst_buffer_new_wrapped (data, buf_sz); + fail_unless (buf); + + for (c = 0; c < info->channels; c++) { + offsets[c] = + dummy_start * bps + c * (nsamples + dummy_start + dummy_end) * bps; + + /* dummy samples at the beginning of each channel plane */ + gst_buffer_memset (buf, offsets[c] - dummy_start * bps, 0xBF, + dummy_start * bps); + /* valid channel samples */ + gst_buffer_memset (buf, offsets[c], c | 0xF0, nsamples * bps); + /* dummy samples at the end of each channel plane */ + gst_buffer_memset (buf, offsets[c] + nsamples * bps, 0xEF, dummy_end * bps); + } + gst_buffer_add_audio_meta (buf, info, nsamples, offsets); + + if (data_ret) + *data_ret = data; + return buf; +} + +static void +verify_buffer_contents (GstBuffer * buf, GstAudioInfo *info, + gint expect_n_planes, gsize expect_plane_size, + gpointer base, gsize real_plane_size, gsize expect_plane_start_offset) +{ + GstAudioBuffer abuf; + gint i; + guint8 *byte; + + gst_audio_buffer_map (&abuf, info, buf, GST_MAP_READ); + fail_unless_equals_int (GST_AUDIO_BUFFER_N_PLANES (&abuf), expect_n_planes); + fail_unless_equals_int (GST_AUDIO_BUFFER_PLANE_SIZE (&abuf), + expect_plane_size); + + for (i = 0; i < GST_AUDIO_BUFFER_N_PLANES (&abuf); i++) { + if (base) { + /* if we have a base pointer, verify the plane pointer + * points to the right place */ + fail_unless_equals_pointer (abuf.planes[i], + ((guint8 *) base) + i * real_plane_size + expect_plane_start_offset); + } + + /* verify all contents */ + byte = abuf.planes[i]; + while (byte < ((guint8 *) abuf.planes[i]) + expect_plane_size) { + GST_TRACE("%d | %p", i, byte); + fail_unless_equals_int_hex (*byte, i | 0xF0); + ++byte; + } + } + gst_audio_buffer_unmap (&abuf); +} + +GST_START_TEST (test_retrieve_same) +{ + GstPlanarAudioAdapter *adapter; + GstAudioInfo info; + GstBuffer *buf; + + adapter = gst_planar_audio_adapter_new (); + + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S32, 100, 5, NULL); + info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED; + + gst_planar_audio_adapter_configure (adapter, &info); + buf = generate_buffer (&info, 20, 0, 0, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 20); + + buf = generate_buffer (&info, 20, 10, 5, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + + buf = gst_planar_audio_adapter_get_buffer (adapter, 20, GST_MAP_READ); + fail_unless (buf); + /* this buffer is shared between the adapter and us, we just ref'ed it */ + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 2); + /* the adapter still has 40 samples */ + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + gst_planar_audio_adapter_flush (adapter, 20); + /* the adapter must have dropped this buffer internally */ + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 20); + gst_buffer_unref (buf); + + buf = gst_planar_audio_adapter_take_buffer (adapter, 20, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 0); + gst_buffer_unref (buf); + + g_object_unref (adapter); +} + +GST_END_TEST; + +GST_START_TEST (test_retrieve_smaller_for_read) +{ + GstPlanarAudioAdapter *adapter; + GstAudioInfo info; + GstBuffer *buf; + gpointer data1, data2; + + adapter = gst_planar_audio_adapter_new (); + + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16, 100, 8, NULL); + info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED; + + gst_planar_audio_adapter_configure (adapter, &info); + buf = generate_buffer (&info, 40, 0, 0, &data1); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + + buf = generate_buffer (&info, 20, 10, 10, &data2); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 60); + + /* the the first 20 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 20, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + verify_buffer_contents (buf, &info, 8, 20 * sizeof (gint16), + data1, 40 * sizeof (gint16), 0); + gst_buffer_unref (buf); + + /* now the next 20 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 20, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 20); + /* still the same memory, with a 20 sample offset on each plane */ + verify_buffer_contents (buf, &info, 8, 20 * sizeof (gint16), + data1, 40 * sizeof (gint16), 20 * sizeof (gint16)); + gst_buffer_unref (buf); + + /* 5 samples from the second buffer */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 5, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 15); + /* original buffer had an offset of 10 samples on its own and + * was 40 samples long, with only 20 samples valid */ + verify_buffer_contents (buf, &info, 8, 5 * sizeof (gint16), + data2, 40 * sizeof (gint16), 10 * sizeof (gint16)); + gst_buffer_unref (buf); + + /* and the last 15 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 15, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 0); + verify_buffer_contents (buf, &info, 8, 15 * sizeof (gint16), + data2, 40 * sizeof (gint16), 15 * sizeof (gint16)); + gst_buffer_unref (buf); + + g_object_unref (adapter); +} + +GST_END_TEST; + +GST_START_TEST (test_retrieve_smaller_for_write) +{ + GstPlanarAudioAdapter *adapter; + GstAudioInfo info; + GstBuffer *buf; + + adapter = gst_planar_audio_adapter_new (); + + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16, 100, 8, NULL); + info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED; + + gst_planar_audio_adapter_configure (adapter, &info); + buf = generate_buffer (&info, 40, 0, 0, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + + buf = generate_buffer (&info, 20, 10, 10, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 60); + + /* the the first 20 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 20, GST_MAP_WRITE); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + verify_buffer_contents (buf, &info, 8, 20 * sizeof (gint16), NULL, 0, 0); + gst_buffer_unref (buf); + + /* now the next 20 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 20, GST_MAP_WRITE); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 20); + verify_buffer_contents (buf, &info, 8, 20 * sizeof (gint16), NULL, 0, 0); + gst_buffer_unref (buf); + + /* 5 samples from the second buffer */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 5, GST_MAP_WRITE); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 15); + verify_buffer_contents (buf, &info, 8, 5 * sizeof (gint16), NULL, 0, 0); + gst_buffer_unref (buf); + + /* and the last 15 samples */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 15, GST_MAP_WRITE); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 0); + verify_buffer_contents (buf, &info, 8, 15 * sizeof (gint16), NULL, 0, 0); + gst_buffer_unref (buf); + + g_object_unref (adapter); +} + +GST_END_TEST; + +GST_START_TEST (test_retrieve_combined) +{ + GstPlanarAudioAdapter *adapter; + GstAudioInfo info; + GstBuffer *buf; + gpointer data2; + + adapter = gst_planar_audio_adapter_new (); + + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_U24_32, 100, 4, NULL); + info.layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED; + + gst_planar_audio_adapter_configure (adapter, &info); + buf = generate_buffer (&info, 20, 0, 0, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 20); + + buf = generate_buffer (&info, 20, 10, 15, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 40); + + buf = generate_buffer (&info, 80, 0, 5, &data2); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 120); + + /* take the first 60 samples - buffers are combined here */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 60, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 60); + verify_buffer_contents (buf, &info, 4, 60 * sizeof (gint32), NULL, 0, 0); + gst_buffer_unref (buf); + + /* now the next 60 samples, for reading */ + + buf = gst_planar_audio_adapter_get_buffer (adapter, 60, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + /* note we didn't take the buffer, the data is still in the adapter */ + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 60); + verify_buffer_contents (buf, &info, 4, 60 * sizeof (gint32), + data2, 85 * sizeof (gint32), 20 * sizeof (gint32)); + gst_buffer_unref (buf); + + /* flush a few */ + + gst_planar_audio_adapter_flush (adapter, 10); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 50); + + /* add some more */ + + buf = generate_buffer (&info, 20, 10, 0, NULL); + gst_planar_audio_adapter_push (adapter, buf); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 70); + + /* now take 60 again */ + + buf = gst_planar_audio_adapter_take_buffer (adapter, 60, GST_MAP_READ); + fail_unless (buf); + fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (buf), 1); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 10); + verify_buffer_contents (buf, &info, 4, 60 * sizeof (gint32), NULL, 0, 0); + gst_buffer_unref (buf); + + gst_planar_audio_adapter_clear (adapter); + fail_unless_equals_int (gst_planar_audio_adapter_available (adapter), 0); + + g_object_unref (adapter); +} + +GST_END_TEST; + +static Suite * +planar_audio_adapter_suite (void) +{ + Suite *s = suite_create ("GstPlanarAudioAdapter"); + + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_retrieve_same); + tcase_add_test (tc_chain, test_retrieve_smaller_for_read); + tcase_add_test (tc_chain, test_retrieve_smaller_for_write); + tcase_add_test (tc_chain, test_retrieve_combined); + + return s; +} + +GST_CHECK_MAIN (planar_audio_adapter); diff --git a/tests/check/meson.build b/tests/check/meson.build index 275895715..bff5c8680 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -64,6 +64,7 @@ base_tests = [ [['libs/isoff.c'], not xml2_dep.found(), [gstisoff_dep, xml2_dep]], [['libs/mpegts.c'], false, [gstmpegts_dep]], [['libs/mpegvideoparser.c'], false, [gstcodecparsers_dep]], + [['libs/planaraudioadapter.c'], false, [gstbadaudio_dep]], [['libs/player.c'], not enable_gst_player_tests, [gstplayer_dep]], [['libs/vc1parser.c'], false, [gstcodecparsers_dep]], [['libs/vp8parser.c'], false, [gstcodecparsers_dep]], |