mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
libs: audio: add new GstPlanarAudioAdapter class
This is a GstAdapter, but for planar audio buffers. https://bugzilla.gnome.org/show_bug.cgi?id=793605
This commit is contained in:
parent
c9226e6e80
commit
9cf58eb3e4
7 changed files with 863 additions and 4 deletions
|
@ -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
|
||||
|
|
391
gst-libs/gst/audio/gstplanaraudioadapter.c
Normal file
391
gst-libs/gst/audio/gstplanaraudioadapter.c
Normal file
|
@ -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;
|
||||
}
|
95
gst-libs/gst/audio/gstplanaraudioadapter.h
Normal file
95
gst-libs/gst/audio/gstplanaraudioadapter.h
Normal file
|
@ -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__ */
|
|
@ -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')
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
361
tests/check/libs/planaraudioadapter.c
Normal file
361
tests/check/libs/planaraudioadapter.c
Normal file
|
@ -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);
|
|
@ -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]],
|
||||
|
|
Loading…
Reference in a new issue