From fb8339de403d2568fe88db91add55c2e3f4d944d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 13 Nov 2014 15:40:15 -0500 Subject: [PATCH] audiointerleave: Add interleave element based on audioaggregator https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- gst/audiomixer/Makefile.am | 4 +- gst/audiomixer/gstaudiointerleave.c | 921 ++++++++++++++++++++++++++++ gst/audiomixer/gstaudiointerleave.h | 101 +++ gst/audiomixer/gstaudiomixer.c | 6 + 4 files changed, 1030 insertions(+), 2 deletions(-) create mode 100644 gst/audiomixer/gstaudiointerleave.c create mode 100644 gst/audiomixer/gstaudiointerleave.h diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index b98ae8adba..a126f90ef7 100644 --- a/gst/audiomixer/Makefile.am +++ b/gst/audiomixer/Makefile.am @@ -4,7 +4,7 @@ ORC_SOURCE=gstaudiomixerorc include $(top_srcdir)/common/orc.mak -libgstaudiomixer_la_SOURCES = gstaudiomixer.c gstaudioaggregator.c +libgstaudiomixer_la_SOURCES = gstaudiomixer.c gstaudioaggregator.c gstaudiointerleave.c nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) libgstaudiomixer_la_CFLAGS = \ -I$(top_srcdir)/gst-libs \ @@ -18,5 +18,5 @@ libgstaudiomixer_la_LIBADD = \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) -noinst_HEADERS = gstaudiomixer.h gstaudioaggregator.h +noinst_HEADERS = gstaudiomixer.h gstaudioaggregator.h gstaudiointerleave.h diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c new file mode 100644 index 0000000000..f06ea28243 --- /dev/null +++ b/gst/audiomixer/gstaudiointerleave.c @@ -0,0 +1,921 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * 2005 Wim Taymans + * 2007 Andy Wingo + * 2008 Sebastian Dröge + * 2014 Collabora + * Olivier Crete + * + * gstaudiointerleave.c: audiointerleave element, N in, one out, + * samples are added + * + * 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:element-audiointerleave + * + * + */ + +/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstaudiointerleave.h" +#include + +#include + +#define GST_CAT_DEFAULT gst_audio_interleave_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_PAD_0, + PROP_PAD_CHANNEL +}; + +G_DEFINE_TYPE (GstAudioInterleavePad, gst_audio_interleave_pad, + GST_TYPE_AUDIO_AGGREGATOR_PAD); + +static void +gst_audio_interleave_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (object); + + switch (prop_id) { + case PROP_PAD_CHANNEL: + g_value_set_uint (value, pad->channel); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_audio_interleave_pad_class_init (GstAudioInterleavePadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->get_property = gst_audio_interleave_pad_get_property; + + g_object_class_install_property (gobject_class, + PROP_PAD_CHANNEL, + g_param_spec_uint ("channel", + "Channel number", + "Number of the channel of this pad in the output", 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_audio_interleave_pad_init (GstAudioInterleavePad * pad) +{ +} + +enum +{ + PROP_0, + PROP_CHANNEL_POSITIONS, + PROP_CHANNEL_POSITIONS_FROM_INPUT +}; + +/* elementfactory information */ + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define CAPS \ + GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \ + ", layout = (string) { interleaved, non-interleaved }" +#else +#define CAPS \ + GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \ + ", layout = (string) { interleaved, non-interleaved }" +#endif + +static GstStaticPadTemplate gst_audio_interleave_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("audio/x-raw, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 1, " + "format = (string) " GST_AUDIO_FORMATS_ALL ", " + "layout = (string) {non-interleaved, interleaved}") + ); + +static GstStaticPadTemplate gst_audio_interleave_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ], " + "format = (string) " GST_AUDIO_FORMATS_ALL ", " + "layout = (string) interleaved") + ); + +static void gst_audio_interleave_child_proxy_init (gpointer g_iface, + gpointer iface_data); + +#define gst_audio_interleave_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstAudioInterleave, gst_audio_interleave, + GST_TYPE_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_audio_interleave_child_proxy_init)); + +static void gst_audio_interleave_finalize (GObject * object); +static void gst_audio_interleave_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audio_interleave_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_audio_interleave_setcaps (GstAudioInterleave * self, + GstPad * pad, GstCaps * caps); +static GstPad *gst_audio_interleave_request_new_pad (GstElement * element, + GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); +static void gst_audio_interleave_release_pad (GstElement * element, + GstPad * pad); + +static gboolean gst_audio_interleave_stop (GstAggregator * agg); + +static gboolean +gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_samples); + + +static void +__remove_channels (GstCaps * caps) +{ + GstStructure *s; + gint i, size; + + size = gst_caps_get_size (caps); + for (i = 0; i < size; i++) { + s = gst_caps_get_structure (caps, i); + gst_structure_remove_field (s, "channel-mask"); + gst_structure_remove_field (s, "channels"); + } +} + +static void +__set_channels (GstCaps * caps, gint channels) +{ + GstStructure *s; + gint i, size; + + size = gst_caps_get_size (caps); + for (i = 0; i < size; i++) { + s = gst_caps_get_structure (caps, i); + if (channels > 0) + gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL); + else + gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); + } +} + +/* we can only accept caps that we and downstream can handle. + * if we have filtercaps set, use those to constrain the target caps. + */ +static GstCaps * +gst_audio_interleave_sink_getcaps (GstAggregator * agg, GstPad * pad, + GstCaps * filter) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + GstCaps *result = NULL, *peercaps, *sinkcaps; + + GST_OBJECT_LOCK (self); + /* If we already have caps on one of the sink pads return them */ + if (self->sinkcaps) + result = gst_caps_copy (self->sinkcaps); + GST_OBJECT_UNLOCK (self); + + if (result == NULL) { + /* get the downstream possible caps */ + peercaps = gst_pad_peer_query_caps (agg->srcpad, NULL); + + /* get the allowed caps on this sinkpad */ + sinkcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + __remove_channels (sinkcaps); + if (peercaps) { + peercaps = gst_caps_make_writable (peercaps); + __remove_channels (peercaps); + /* if the peer has caps, intersect */ + GST_DEBUG_OBJECT (pad, "intersecting peer and template caps"); + result = gst_caps_intersect (peercaps, sinkcaps); + gst_caps_unref (peercaps); + gst_caps_unref (sinkcaps); + } else { + /* the peer has no caps (or there is no peer), just use the allowed caps + * of this sinkpad. */ + GST_DEBUG_OBJECT (pad, "no peer caps, using sinkcaps"); + result = sinkcaps; + } + __set_channels (result, 1); + } + + if (filter != NULL) { + GstCaps *caps = result; + + GST_LOG_OBJECT (pad, "intersecting filter caps %" GST_PTR_FORMAT " with " + "preliminary result %" GST_PTR_FORMAT, filter, caps); + + result = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (caps); + } + + GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, result); + + return result; +} + +static gboolean +gst_audio_interleave_sink_query (GstAggregator * agg, GstAggregatorPad * aggpad, + GstQuery * query) +{ + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter, *caps; + + gst_query_parse_caps (query, &filter); + caps = gst_audio_interleave_sink_getcaps (agg, GST_PAD (aggpad), filter); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + res = TRUE; + break; + } + default: + res = + GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, aggpad, query); + break; + } + + return res; +} + +static gint +compare_positions (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const gint i = *(const gint *) a; + const gint j = *(const gint *) b; + const gint *pos = (const gint *) user_data; + + if (pos[i] < pos[j]) + return -1; + else if (pos[i] > pos[j]) + return 1; + else + return 0; +} + +static gboolean +gst_audio_interleave_channel_positions_to_mask (GValueArray * positions, + gint default_ordering_map[64], guint64 * mask) +{ + gint i; + guint channels; + GstAudioChannelPosition *pos; + gboolean ret; + + channels = positions->n_values; + pos = g_new (GstAudioChannelPosition, channels); + + for (i = 0; i < channels; i++) { + GValue *val; + + val = g_value_array_get_nth (positions, i); + pos[i] = g_value_get_enum (val); + } + + /* sort the default ordering map according to the position order */ + for (i = 0; i < channels; i++) { + default_ordering_map[i] = i; + } + g_qsort_with_data (default_ordering_map, channels, + sizeof (*default_ordering_map), compare_positions, pos); + + ret = gst_audio_channel_positions_to_mask (pos, channels, FALSE, mask); + g_free (pos); + + return ret; +} + +static void +gst_audio_interleave_set_channel_positions (GstAudioInterleave * self, + GstStructure * s) +{ + guint64 channel_mask = 0; + + GST_OBJECT_LOCK (self); + + if (self->channel_positions != NULL && + self->channels == self->channel_positions->n_values) { + if (!gst_audio_interleave_channel_positions_to_mask + (self->channel_positions, self->default_channels_ordering_map, + &channel_mask)) { + GST_WARNING_OBJECT (self, "Invalid channel positions, using NONE"); + channel_mask = 0; + } + } else { + GST_WARNING_OBJECT (self, "Using NONE channel positions"); + } + gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); + + GST_OBJECT_UNLOCK (self); +} + + +#define MAKE_FUNC(type) \ +static void interleave_##type (guint##type *out, guint##type *in, \ + guint stride, guint nframes) \ +{ \ + gint i; \ + \ + for (i = 0; i < nframes; i++) { \ + *out = in[i]; \ + out += stride; \ + } \ +} + +MAKE_FUNC (8); +MAKE_FUNC (16); +MAKE_FUNC (32); +MAKE_FUNC (64); + +static void +interleave_24 (guint8 * out, guint8 * in, guint stride, guint nframes) +{ + gint i; + + for (i = 0; i < nframes; i++) { + memcpy (out, in, 3); + out += stride * 3; + in += 3; + } +} + +static void +gst_audio_interleave_set_process_function (GstAudioInterleave * self) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); + + switch (GST_AUDIO_INFO_WIDTH (&aagg->info)) { + case 8: + self->func = (GstInterleaveFunc) interleave_8; + break; + case 16: + self->func = (GstInterleaveFunc) interleave_16; + break; + case 24: + self->func = (GstInterleaveFunc) interleave_24; + break; + case 32: + self->func = (GstInterleaveFunc) interleave_32; + break; + case 64: + self->func = (GstInterleaveFunc) interleave_64; + break; + default: + g_assert_not_reached (); + break; + } +} + + +/* the first caps we receive on any of the sinkpads will define the caps for all + * the other sinkpads because we can only mix streams with the same caps. + */ +static gboolean +gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad, + GstCaps * caps) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); + GstAudioAggregatorPad *aaggpad = GST_AUDIO_AGGREGATOR_PAD (pad); + GstAudioInfo info; + GValue *val; + guint channel; + GstCaps *srccaps; + GstStructure *s; + gboolean ret; + + if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps)) + goto cannot_change_caps; + + if (!gst_audio_info_from_caps (&info, caps)) + goto invalid_format; + + if (aaggpad->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN) + g_atomic_int_add (&self->configured_sinkpads_counter, 1); + + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + caps); + + if (self->channel_positions_from_input + && GST_AUDIO_INFO_CHANNELS (&info) == 1) { + channel = GST_AUDIO_INTERLEAVE_PAD (pad)->channel; + val = g_value_array_get_nth (self->input_channel_positions, channel); + g_value_set_enum (val, GST_AUDIO_INFO_POSITION (&info, 0)); + } + + if (g_atomic_int_get (&self->configured_sinkpads_counter) < self->channels) + return TRUE; + + srccaps = gst_caps_copy (caps); + s = gst_caps_get_structure (srccaps, 0); + + gst_structure_remove_field (s, "channel-mask"); + + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout", + G_TYPE_STRING, "interleaved", NULL); + gst_audio_interleave_set_channel_positions (self, s); + + ret = gst_audio_aggregator_set_src_caps (aagg, srccaps); + + gst_caps_unref (srccaps); + + if (!ret) + goto src_did_not_accept; + + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + caps); + + gst_audio_interleave_set_process_function (self); + + if (!self->sinkcaps) { + GstCaps *sinkcaps = gst_caps_copy (caps); + GstStructure *s = gst_caps_get_structure (sinkcaps, 0); + + gst_structure_remove_field (s, "channel-mask"); + + GST_DEBUG_OBJECT (self, "setting sinkcaps %" GST_PTR_FORMAT, sinkcaps); + + gst_caps_replace (&self->sinkcaps, sinkcaps); + + gst_caps_unref (sinkcaps); + } + + + GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); + + return TRUE; + + /* ERRORS */ +invalid_format: + { + GST_WARNING_OBJECT (self, "invalid format set as caps: %" GST_PTR_FORMAT, + caps); + return FALSE; + } +cannot_change_caps: + { + GST_WARNING_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't " + "change", self->sinkcaps); + return FALSE; + } +src_did_not_accept: + { + GST_WARNING_OBJECT (self, "src did not accept setcaps()"); + return FALSE; + } +} + +static gboolean +gst_audio_interleave_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, + GstEvent * event) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + gboolean res = TRUE; + + GST_DEBUG_OBJECT (aggpad, "Got %s event on sink pad", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_audio_interleave_setcaps (self, GST_PAD_CAST (aggpad), caps); + gst_event_unref (event); + event = NULL; + break; + } + default: + break; + } + + if (event != NULL) + return GST_AGGREGATOR_CLASS (parent_class)->sink_event (agg, aggpad, event); + + return res; +} + +static void +gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; + GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass; + + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "audiointerleave", 0, + "audio interleaving element"); + + gobject_class->set_property = gst_audio_interleave_set_property; + gobject_class->get_property = gst_audio_interleave_get_property; + gobject_class->finalize = gst_audio_interleave_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audio_interleave_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audio_interleave_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, "AudioInterleave", + "Generic/Audio", + "Mixes multiple audio streams", + "Olivier Crete "); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_audio_interleave_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_audio_interleave_release_pad); + + + agg_class->sinkpads_type = GST_TYPE_AUDIO_INTERLEAVE_PAD; + + agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_query); + agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_event); + agg_class->stop = gst_audio_interleave_stop; + + aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer; + + + /** + * GstInterleave:channel-positions + * + * Channel positions: This property controls the channel positions + * that are used on the src caps. The number of elements should be + * the same as the number of sink pads and the array should contain + * a valid list of channel positions. The n-th element of the array + * is the position of the n-th sink pad. + * + * These channel positions will only be used if they're valid and the + * number of elements is the same as the number of channels. If this + * is not given a NONE layout will be used. + * + */ + g_object_class_install_property (gobject_class, PROP_CHANNEL_POSITIONS, + g_param_spec_value_array ("channel-positions", "Channel positions", + "Channel positions used on the output", + g_param_spec_enum ("channel-position", "Channel position", + "Channel position of the n-th input", + GST_TYPE_AUDIO_CHANNEL_POSITION, + GST_AUDIO_CHANNEL_POSITION_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstInterleave:channel-positions-from-input + * + * Channel positions from input: If this property is set to %TRUE the channel + * positions will be taken from the input caps if valid channel positions for + * the output can be constructed from them. If this is set to %TRUE setting the + * channel-positions property overwrites this property again. + * + */ + g_object_class_install_property (gobject_class, + PROP_CHANNEL_POSITIONS_FROM_INPUT, + g_param_spec_boolean ("channel-positions-from-input", + "Channel positions from input", + "Take channel positions from the input", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_audio_interleave_init (GstAudioInterleave * self) +{ + self->input_channel_positions = g_value_array_new (0); + self->channel_positions_from_input = TRUE; + self->channel_positions = self->input_channel_positions; +} + +static void +gst_audio_interleave_finalize (GObject * object) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object); + + if (self->channel_positions + && self->channel_positions != self->input_channel_positions) { + g_value_array_free (self->channel_positions); + self->channel_positions = NULL; + } + + if (self->input_channel_positions) { + g_value_array_free (self->input_channel_positions); + self->input_channel_positions = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_audio_interleave_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object); + + switch (prop_id) { + case PROP_CHANNEL_POSITIONS: + g_return_if_fail ( + ((GValueArray *) g_value_get_boxed (value))->n_values > 0); + + if (self->channel_positions && + self->channel_positions != self->input_channel_positions) + g_value_array_free (self->channel_positions); + + self->channel_positions = g_value_dup_boxed (value); + self->channel_positions_from_input = FALSE; + self->channels = self->channel_positions->n_values; + break; + case PROP_CHANNEL_POSITIONS_FROM_INPUT: + self->channel_positions_from_input = g_value_get_boolean (value); + + if (self->channel_positions_from_input) { + if (self->channel_positions && + self->channel_positions != self->input_channel_positions) + g_value_array_free (self->channel_positions); + self->channel_positions = self->input_channel_positions; + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_interleave_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object); + + switch (prop_id) { + case PROP_CHANNEL_POSITIONS: + g_value_set_boxed (value, self->channel_positions); + break; + case PROP_CHANNEL_POSITIONS_FROM_INPUT: + g_value_set_boolean (value, self->channel_positions_from_input); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_audio_interleave_stop (GstAggregator * agg) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + + if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) + return FALSE; + + gst_caps_replace (&self->sinkcaps, NULL); + + return TRUE; +} + +static GstPad * +gst_audio_interleave_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (element); + GstAudioInterleavePad *newpad; + gchar *pad_name; + gint channels, padnumber; + GValue val = { 0, }; + + /* FIXME: We ignore req_name, this is evil! */ + + padnumber = g_atomic_int_add (&self->padcounter, 1); + if (self->channel_positions_from_input) + channels = g_atomic_int_add (&self->channels, 1); + else + channels = padnumber; + + pad_name = g_strdup_printf ("sink_%u", padnumber); + newpad = (GstAudioInterleavePad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, pad_name, caps); + g_free (pad_name); + if (newpad == NULL) + goto could_not_create; + + newpad->channel = channels; + gst_pad_use_fixed_caps (GST_PAD (newpad)); + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + + g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); + g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_NONE); + self->input_channel_positions = + g_value_array_append (self->input_channel_positions, &val); + g_value_unset (&val); + + /* Update the src caps if we already have them */ + if (self->sinkcaps) { + GstCaps *srccaps; + GstStructure *s; + + /* Take lock to make sure processing finishes first */ + + srccaps = gst_caps_copy (self->sinkcaps); + s = gst_caps_get_structure (srccaps, 0); + + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL); + + gst_audio_interleave_set_channel_positions (self, s); + + gst_audio_aggregator_set_src_caps (GST_AUDIO_AGGREGATOR (self), srccaps); + gst_caps_unref (srccaps); + } + + return GST_PAD_CAST (newpad); + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + return NULL; + } +} + +static void +gst_audio_interleave_release_pad (GstElement * element, GstPad * pad) +{ + GstAudioInterleave *self; + gint position; + GList *l; + + self = GST_AUDIO_INTERLEAVE (element); + + /* Take lock to make sure we're not changing this when processing buffers */ + GST_OBJECT_LOCK (self); + + g_atomic_int_add (&self->channels, -1); + + if (gst_pad_has_current_caps (pad)) + g_atomic_int_add (&self->configured_sinkpads_counter, -1); + + position = GST_AUDIO_INTERLEAVE_PAD (pad)->channel; + g_value_array_remove (self->input_channel_positions, position); + + /* Update channel numbers */ + /* Taken above, GST_OBJECT_LOCK (self); */ + for (l = GST_ELEMENT_CAST (self)->sinkpads; l != NULL; l = l->next) { + GstAudioInterleavePad *ipad = GST_AUDIO_INTERLEAVE_PAD (l->data); + + if (GST_AUDIO_INTERLEAVE_PAD (pad)->channel < ipad->channel) + ipad->channel--; + } + + + /* Update the src caps if we already have them */ + if (self->sinkcaps) { + if (self->channels > 0) { + GstCaps *srccaps; + GstStructure *s; + + srccaps = gst_caps_copy (self->sinkcaps); + s = gst_caps_get_structure (srccaps, 0); + + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL); + gst_audio_interleave_set_channel_positions (self, s); + + GST_OBJECT_UNLOCK (self); + + gst_audio_aggregator_set_src_caps (GST_AUDIO_AGGREGATOR (self), srccaps); + gst_caps_unref (srccaps); + } else { + gst_caps_replace (&self->sinkcaps, NULL); + GST_OBJECT_UNLOCK (self); + } + } else { + GST_OBJECT_UNLOCK (self); + } + + + + + GST_DEBUG_OBJECT (self, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + + GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); +} + + +/* Called with object lock and pad object lock held */ +static gboolean +gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_frames) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (aagg); + GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (aaggpad); + GstMapInfo inmap; + GstMapInfo outmap; + gint out_width, in_bpf, out_bpf; + guint8 *outdata; + + out_width = GST_AUDIO_INFO_WIDTH (&aagg->info) / 8; + in_bpf = GST_AUDIO_INFO_BPF (&aaggpad->info); + out_bpf = GST_AUDIO_INFO_BPF (&aagg->info); + + gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); + gst_buffer_map (inbuf, &inmap, GST_MAP_READ); + GST_LOG_OBJECT (pad, "interleaves %u frames on channel %d/%d at offset %u" + " from offset %u", num_frames, pad->channel, self->channels, + out_offset * out_bpf, in_offset * in_bpf); + + outdata = outmap.data + (out_offset * out_bpf) + + (out_width * self->default_channels_ordering_map[pad->channel]); + + + self->func (outdata, inmap.data + (in_offset * in_bpf), self->channels, + num_frames); + + + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unmap (outbuf, &outmap); + + return TRUE; +} + + +/* GstChildProxy implementation */ +static GObject * +gst_audio_interleave_child_proxy_get_child_by_index (GstChildProxy * + child_proxy, guint index) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (self); + obj = g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (self); + + return obj; +} + +static guint +gst_audio_interleave_child_proxy_get_children_count (GstChildProxy * + child_proxy) +{ + guint count = 0; + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (child_proxy); + + GST_OBJECT_LOCK (self); + count = GST_ELEMENT_CAST (self)->numsinkpads; + GST_OBJECT_UNLOCK (self); + GST_INFO_OBJECT (self, "Children Count: %d", count); + + return count; +} + +static void +gst_audio_interleave_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + GST_INFO ("intializing child proxy interface"); + iface->get_child_by_index = + gst_audio_interleave_child_proxy_get_child_by_index; + iface->get_children_count = + gst_audio_interleave_child_proxy_get_children_count; +} diff --git a/gst/audiomixer/gstaudiointerleave.h b/gst/audiomixer/gstaudiointerleave.h new file mode 100644 index 0000000000..87f6385e39 --- /dev/null +++ b/gst/audiomixer/gstaudiointerleave.h @@ -0,0 +1,101 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * Copyright (C) 2013 Sebastian Dröge + * + * gstaudiointerleave.h: Header for audiointerleave element + * + * 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_AUDIO_INTERLEAVE_H__ +#define __GST_AUDIO_INTERLEAVE_H__ + +#include +#include + +#include "gstaudioaggregator.h" + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIO_INTERLEAVE (gst_audio_interleave_get_type()) +#define GST_AUDIO_INTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_INTERLEAVE,GstAudioInterleave)) +#define GST_IS_AUDIO_INTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_INTERLEAVE)) +#define GST_AUDIO_INTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_INTERLEAVE,GstAudioInterleaveClass)) +#define GST_IS_AUDIO_INTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_INTERLEAVE)) +#define GST_AUDIO_INTERLEAVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_INTERLEAVE,GstAudioInterleaveClass)) + +typedef struct _GstAudioInterleave GstAudioInterleave; +typedef struct _GstAudioInterleaveClass GstAudioInterleaveClass; + +typedef struct _GstAudioInterleavePad GstAudioInterleavePad; +typedef struct _GstAudioInterleavePadClass GstAudioInterleavePadClass; + +typedef void (*GstInterleaveFunc) (gpointer out, gpointer in, guint stride, + guint nframes); + +/** + * GstAudioInterleave: + * + * The GstAudioInterleave object structure. + */ +struct _GstAudioInterleave { + GstAudioAggregator parent; + + gint padcounter; + guint channels; + + GstCaps *sinkcaps; + gint configured_sinkpads_counter; + + GValueArray *channel_positions; + GValueArray *input_channel_positions; + gboolean channel_positions_from_input; + + gint default_channels_ordering_map[64]; + + GstInterleaveFunc func; +}; + +struct _GstAudioInterleaveClass { + GstAudioAggregatorClass parent_class; +}; + +GType gst_audio_interleave_get_type (void); + +#define GST_TYPE_AUDIO_INTERLEAVE_PAD (gst_audio_interleave_pad_get_type()) +#define GST_AUDIO_INTERLEAVE_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_INTERLEAVE_PAD,GstAudioInterleavePad)) +#define GST_IS_AUDIO_INTERLEAVE_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_INTERLEAVE_PAD)) +#define GST_AUDIO_INTERLEAVE_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_INTERLEAVE_PAD,GstAudioInterleavePadClass)) +#define GST_IS_AUDIO_INTERLEAVE_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_INTERLEAVE_PAD)) +#define GST_AUDIO_INTERLEAVE_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_INTERLEAVE_PAD,GstAudioInterleavePadClass)) + +struct _GstAudioInterleavePad { + GstAudioAggregatorPad parent; + + guint channel; +}; + +struct _GstAudioInterleavePadClass { + GstAudioAggregatorPadClass parent_class; +}; + +GType gst_audio_interleave_pad_get_type (void); + +G_END_DECLS + + +#endif /* __GST_AUDIO_INTERLEAVE_H__ */ diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 3ff370690c..40ede84346 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -60,6 +60,8 @@ #include /* strcmp */ #include "gstaudiomixerorc.h" +#include "gstaudiointerleave.h" + #define GST_CAT_DEFAULT gst_audiomixer_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); @@ -764,6 +766,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_AUDIO_MIXER)) return FALSE; + if (!gst_element_register (plugin, "audiointerleave", GST_RANK_NONE, + GST_TYPE_AUDIO_INTERLEAVE)) + return FALSE; + return TRUE; }