From 39459b2f302e954094e8720db1c5deb42d5edeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 6 Nov 2013 15:18:50 +0100 Subject: [PATCH 001/153] audiomixer: Add new element based on adder that does synchronized audio mixing --- gst/audiomixer/Makefile.am | 18 + gst/audiomixer/gstaudiomixer.c | 1963 +++++++++++++++++ gst/audiomixer/gstaudiomixer.h | 126 ++ gst/audiomixer/gstaudiomixerorc-dist.c | 2661 ++++++++++++++++++++++++ gst/audiomixer/gstaudiomixerorc-dist.h | 106 + gst/audiomixer/gstaudiomixerorc.orc | 176 ++ tests/check/elements/audiomixer.c | 1296 ++++++++++++ 7 files changed, 6346 insertions(+) create mode 100644 gst/audiomixer/Makefile.am create mode 100644 gst/audiomixer/gstaudiomixer.c create mode 100644 gst/audiomixer/gstaudiomixer.h create mode 100644 gst/audiomixer/gstaudiomixerorc-dist.c create mode 100644 gst/audiomixer/gstaudiomixerorc-dist.h create mode 100644 gst/audiomixer/gstaudiomixerorc.orc create mode 100644 tests/check/elements/audiomixer.c diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am new file mode 100644 index 0000000000..90328bc925 --- /dev/null +++ b/gst/audiomixer/Makefile.am @@ -0,0 +1,18 @@ +plugin_LTLIBRARIES = libgstaudiomixer.la + +ORC_SOURCE=gstaudiomixerorc +include $(top_srcdir)/common/orc.mak + + +libgstaudiomixer_la_SOURCES = gstaudiomixer.c +nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstaudiomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) +libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstaudiomixer_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstaudio-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) +libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstaudiomixer.h + diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c new file mode 100644 index 0000000000..6073732dbd --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.c @@ -0,0 +1,1963 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2001 Thomas + * 2005,2006 Wim Taymans + * 2013 Sebastian Dröge + * + * audiomixer.c: AudioMixer 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-audiomixer + * + * The audiomixer allows to mix several streams into one by adding the data. + * Mixed data is clamped to the min/max values of the data format. + * + * The audiomixer currently mixes all data received on the sinkpads as soon as + * possible without trying to synchronize the streams. + * + * + * Example launch line + * |[ + * gst-launch audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. + * ]| This pipeline produces two sine waves mixed together. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstaudiomixer.h" +#include +#include /* strcmp */ +#include "gstaudiomixerorc.h" + +#define GST_CAT_DEFAULT gst_audiomixer_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +typedef struct _GstAudioMixerCollect GstAudioMixerCollect; +struct _GstAudioMixerCollect +{ + GstCollectData collect; /* we extend the CollectData */ + + GstBuffer *buffer; /* current buffer we're mixing, + for comparison with collect.buffer + to see if we need to update our + cached values. */ + guint position, size; + + guint64 output_offset; /* Offset in output segment that + collect.pos refers to in the + current buffer. */ + + guint64 next_offset; /* Next expected offset in the input segment */ +}; + +#define DEFAULT_PAD_VOLUME (1.0) +#define DEFAULT_PAD_MUTE (FALSE) + +/* some defines for audio processing */ +/* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0 + * we map 1.0 to VOLUME_UNITY_INT* + */ +#define VOLUME_UNITY_INT8 8 /* internal int for unity 2^(8-5) */ +#define VOLUME_UNITY_INT8_BIT_SHIFT 3 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT16 2048 /* internal int for unity 2^(16-5) */ +#define VOLUME_UNITY_INT16_BIT_SHIFT 11 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT24 524288 /* internal int for unity 2^(24-5) */ +#define VOLUME_UNITY_INT24_BIT_SHIFT 19 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT32 134217728 /* internal int for unity 2^(32-5) */ +#define VOLUME_UNITY_INT32_BIT_SHIFT 27 + +enum +{ + PROP_PAD_0, + PROP_PAD_VOLUME, + PROP_PAD_MUTE +}; + +G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, GST_TYPE_PAD); + +static void +gst_audiomixer_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object); + + switch (prop_id) { + case PROP_PAD_VOLUME: + g_value_set_double (value, pad->volume); + break; + case PROP_PAD_MUTE: + g_value_set_boolean (value, pad->mute); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object); + + switch (prop_id) { + case PROP_PAD_VOLUME: + GST_OBJECT_LOCK (pad); + pad->volume = g_value_get_double (value); + pad->volume_i8 = pad->volume * VOLUME_UNITY_INT8; + pad->volume_i16 = pad->volume * VOLUME_UNITY_INT16; + pad->volume_i32 = pad->volume * VOLUME_UNITY_INT32; + GST_OBJECT_UNLOCK (pad); + break; + case PROP_PAD_MUTE: + GST_OBJECT_LOCK (pad); + pad->mute = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_audiomixer_pad_set_property; + gobject_class->get_property = gst_audiomixer_pad_get_property; + + g_object_class_install_property (gobject_class, PROP_PAD_VOLUME, + g_param_spec_double ("volume", "Volume", "Volume of this pad", + 0.0, 10.0, DEFAULT_PAD_VOLUME, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_MUTE, + g_param_spec_boolean ("mute", "Mute", "Mute this pad", + DEFAULT_PAD_MUTE, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_audiomixer_pad_init (GstAudioMixerPad * pad) +{ + pad->volume = DEFAULT_PAD_VOLUME; + pad->mute = DEFAULT_PAD_MUTE; +} + +#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) +#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) +#define DEFAULT_BLOCKSIZE (1024) + +enum +{ + PROP_0, + PROP_FILTER_CAPS, + PROP_ALIGNMENT_THRESHOLD, + PROP_DISCONT_WAIT, + PROP_BLOCKSIZE +}; + +/* 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_audiomixer_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CAPS) + ); + +static GstStaticPadTemplate gst_audiomixer_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS (CAPS) + ); + +static void gst_audiomixer_child_proxy_init (gpointer g_iface, + gpointer iface_data); + +#define gst_audiomixer_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_audiomixer_child_proxy_init)); + +static void gst_audiomixer_dispose (GObject * object); +static void gst_audiomixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audiomixer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, + GstPad * pad, GstCaps * caps); +static gboolean gst_audiomixer_src_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_audiomixer_sink_query (GstCollectPads * pads, + GstCollectData * pad, GstQuery * query, gpointer user_data); +static gboolean gst_audiomixer_src_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_audiomixer_sink_event (GstCollectPads * pads, + GstCollectData * pad, GstEvent * event, gpointer user_data); + +static GstPad *gst_audiomixer_request_new_pad (GstElement * element, + GstPadTemplate * temp, const gchar * unused, const GstCaps * caps); +static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); + +static GstStateChangeReturn gst_audiomixer_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_audiomixer_do_clip (GstCollectPads * pads, + GstCollectData * data, GstBuffer * buffer, GstBuffer ** out, + gpointer user_data); +static GstFlowReturn gst_audiomixer_collected (GstCollectPads * pads, + gpointer user_data); + +/* 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_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) +{ + GstAudioMixer *audiomixer; + GstCaps *result, *peercaps, *current_caps, *filter_caps; + + audiomixer = GST_AUDIO_MIXER (GST_PAD_PARENT (pad)); + + GST_OBJECT_LOCK (audiomixer); + /* take filter */ + if ((filter_caps = audiomixer->filter_caps)) { + if (filter) + filter_caps = + gst_caps_intersect_full (filter, filter_caps, + GST_CAPS_INTERSECT_FIRST); + else + gst_caps_ref (filter_caps); + } else { + filter_caps = filter ? gst_caps_ref (filter) : NULL; + } + GST_OBJECT_UNLOCK (audiomixer); + + if (filter_caps && gst_caps_is_empty (filter_caps)) { + GST_WARNING_OBJECT (pad, "Empty filter caps"); + return filter_caps; + } + + /* get the downstream possible caps */ + peercaps = gst_pad_peer_query_caps (audiomixer->srcpad, filter_caps); + + /* get the allowed caps on this sinkpad */ + GST_OBJECT_LOCK (audiomixer); + current_caps = + audiomixer->current_caps ? gst_caps_ref (audiomixer->current_caps) : NULL; + if (current_caps == NULL) { + current_caps = gst_pad_get_pad_template_caps (pad); + if (!current_caps) + current_caps = gst_caps_new_any (); + } + GST_OBJECT_UNLOCK (audiomixer); + + if (peercaps) { + /* if the peer has caps, intersect */ + GST_DEBUG_OBJECT (audiomixer, "intersecting peer and our caps"); + result = + gst_caps_intersect_full (peercaps, current_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (peercaps); + gst_caps_unref (current_caps); + } else { + /* the peer has no caps (or there is no peer), just use the allowed caps + * of this sinkpad. */ + /* restrict with filter-caps if any */ + if (filter_caps) { + GST_DEBUG_OBJECT (audiomixer, "no peer caps, using filtered caps"); + result = + gst_caps_intersect_full (filter_caps, current_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (current_caps); + } else { + GST_DEBUG_OBJECT (audiomixer, "no peer caps, using our caps"); + result = current_caps; + } + } + + if (filter_caps) + gst_caps_unref (filter_caps); + + GST_LOG_OBJECT (audiomixer, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, + pad, GST_PAD_NAME (pad), result); + + return result; +} + +static gboolean +gst_audiomixer_sink_query (GstCollectPads * pads, GstCollectData * pad, + GstQuery * query, gpointer user_data) +{ + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter, *caps; + + gst_query_parse_caps (query, &filter); + caps = gst_audiomixer_sink_getcaps (pad->pad, filter); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + res = TRUE; + break; + } + default: + res = gst_collect_pads_query_default (pads, pad, query, FALSE); + break; + } + + return res; +} + +/* 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_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, + GstCaps * caps) +{ + GstAudioInfo info; + + if (!gst_audio_info_from_caps (&info, caps)) + goto invalid_format; + + GST_OBJECT_LOCK (audiomixer); + /* don't allow reconfiguration for now; there's still a race between the + * different upstream threads doing query_caps + accept_caps + sending + * (possibly different) CAPS events, but there's not much we can do about + * that, upstream needs to deal with it. */ + if (audiomixer->current_caps != NULL) { + if (gst_audio_info_is_equal (&info, &audiomixer->info)) { + GST_OBJECT_UNLOCK (audiomixer); + return TRUE; + } else { + GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but " + "current caps are %" GST_PTR_FORMAT, caps, audiomixer->current_caps); + GST_OBJECT_UNLOCK (audiomixer); + gst_pad_push_event (pad, gst_event_new_reconfigure ()); + return FALSE; + } + } + + GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps); + audiomixer->current_caps = gst_caps_ref (caps); + + memcpy (&audiomixer->info, &info, sizeof (info)); + GST_OBJECT_UNLOCK (audiomixer); + /* send caps event later, after stream-start event */ + + GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); + + return TRUE; + + /* ERRORS */ +invalid_format: + { + GST_WARNING_OBJECT (audiomixer, "invalid format set as caps"); + return FALSE; + } +} + +/* FIXME, the duration query should reflect how long you will produce + * data, that is the amount of stream time until you will emit EOS. + * + * For synchronized mixing this is always the max of all the durations + * of upstream since we emit EOS when all of them finished. + * + * We don't do synchronized mixing so this really depends on where the + * streams where punched in and what their relative offsets are against + * eachother which we can get from the first timestamps we see. + * + * When we add a new stream (or remove a stream) the duration might + * also become invalid again and we need to post a new DURATION + * message to notify this fact to the parent. + * For now we take the max of all the upstream elements so the simple + * cases work at least somewhat. + */ +static gboolean +gst_audiomixer_query_duration (GstAudioMixer * audiomixer, GstQuery * query) +{ + gint64 max; + gboolean res; + GstFormat format; + GstIterator *it; + gboolean done; + GValue item = { 0, }; + + /* parse format */ + gst_query_parse_duration (query, &format, NULL); + + max = -1; + res = TRUE; + done = FALSE; + + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (!done) { + GstIteratorResult ires; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + gint64 duration; + + /* ask sink peer for duration */ + res &= gst_pad_peer_query_duration (pad, format, &duration); + /* take max from all valid return values */ + if (res) { + /* valid unknown length, stop searching */ + if (duration == -1) { + max = duration; + done = TRUE; + } + /* else see if bigger than current max */ + else if (duration > max) + max = duration; + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (audiomixer, "Total duration in format %s: %" + GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); + gst_query_set_duration (query, format, max); + } + + return res; +} + +static gboolean +gst_audiomixer_query_latency (GstAudioMixer * audiomixer, GstQuery * query) +{ + GstClockTime min, max; + gboolean live; + gboolean res; + GstIterator *it; + gboolean done; + GValue item = { 0, }; + + res = TRUE; + done = FALSE; + + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + + /* Take maximum of all latency values */ + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (!done) { + GstIteratorResult ires; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + GstQuery *peerquery; + GstClockTime min_cur, max_cur; + gboolean live_cur; + + peerquery = gst_query_new_latency (); + + /* Ask peer for latency */ + res &= gst_pad_peer_query (pad, peerquery); + + /* take max from all valid return values */ + if (res) { + gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); + + if (min_cur > min) + min = min_cur; + + if (max_cur != GST_CLOCK_TIME_NONE && + ((max != GST_CLOCK_TIME_NONE && max_cur > max) || + (max == GST_CLOCK_TIME_NONE))) + max = max_cur; + + live = live || live_cur; + } + + gst_query_unref (peerquery); + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* store the results */ + GST_DEBUG_OBJECT (audiomixer, "Calculated total latency: live %s, min %" + GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, + (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + gst_query_set_latency (query, live, min, max); + } + + return res; +} + +static gboolean +gst_audiomixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (parent); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + switch (format) { + case GST_FORMAT_TIME: + /* FIXME, bring to stream time, might be tricky */ + gst_query_set_position (query, format, audiomixer->segment.position); + res = TRUE; + break; + case GST_FORMAT_DEFAULT: + gst_query_set_position (query, format, audiomixer->offset); + res = TRUE; + break; + default: + break; + } + break; + } + case GST_QUERY_DURATION: + res = gst_audiomixer_query_duration (audiomixer, query); + break; + case GST_QUERY_LATENCY: + res = gst_audiomixer_query_latency (audiomixer, query); + break; + default: + /* FIXME, needs a custom query handler because we have multiple + * sinkpads */ + res = gst_pad_query_default (pad, parent, query); + break; + } + + return res; +} + +/* event handling */ + +typedef struct +{ + GstEvent *event; + gboolean flush; +} EventData; + +/* FIXME: What is this supposed to solve? */ +static gboolean +forward_event_func (const GValue * val, GValue * ret, EventData * data) +{ + GstPad *pad = g_value_get_object (val); + GstEvent *event = data->event; + GstPad *peer; + + gst_event_ref (event); + GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); + peer = gst_pad_get_peer (pad); + /* collect pad might have been set flushing, + * so bypass core checking that and send directly to peer */ + if (!peer || !gst_pad_send_event (peer, event)) { + if (!peer) + gst_event_unref (event); + GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", + event, GST_EVENT_TYPE_NAME (event)); + /* quick hack to unflush the pads, ideally we need a way to just unflush + * this single collect pad */ + if (data->flush) + gst_pad_send_event (pad, gst_event_new_flush_stop (TRUE)); + } else { + g_value_set_boolean (ret, TRUE); + GST_LOG_OBJECT (pad, "Sent event %p (%s).", + event, GST_EVENT_TYPE_NAME (event)); + } + if (peer) + gst_object_unref (peer); + + /* continue on other pads, even if one failed */ + return TRUE; +} + +/* forwards the event to all sinkpads, takes ownership of the + * event + * + * Returns: TRUE if the event could be forwarded on all + * sinkpads. + */ +static gboolean +forward_event (GstAudioMixer * audiomixer, GstEvent * event, gboolean flush) +{ + gboolean ret; + GstIterator *it; + GstIteratorResult ires; + GValue vret = { 0 }; + EventData data; + + GST_LOG_OBJECT (audiomixer, "Forwarding event %p (%s)", event, + GST_EVENT_TYPE_NAME (event)); + + data.event = event; + data.flush = flush; + + g_value_init (&vret, G_TYPE_BOOLEAN); + g_value_set_boolean (&vret, FALSE); + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (TRUE) { + ires = + gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, + &vret, &data); + switch (ires) { + case GST_ITERATOR_RESYNC: + GST_WARNING ("resync"); + gst_iterator_resync (it); + g_value_set_boolean (&vret, TRUE); + break; + case GST_ITERATOR_OK: + case GST_ITERATOR_DONE: + ret = g_value_get_boolean (&vret); + goto done; + default: + ret = FALSE; + goto done; + } + } +done: + gst_iterator_free (it); + GST_LOG_OBJECT (audiomixer, "Forwarded event %p (%s), ret=%d", event, + GST_EVENT_TYPE_NAME (event), ret); + gst_event_unref (event); + + return ret; +} + +static gboolean +gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstAudioMixer *audiomixer; + gboolean result; + + audiomixer = GST_AUDIO_MIXER (parent); + + GST_DEBUG_OBJECT (pad, "Got %s event on src pad", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + /* TODO: Update from videomixer */ + case GST_EVENT_SEEK: + { + GstSeekFlags flags; + gdouble rate; + GstSeekType start_type, stop_type; + gint64 start, stop; + GstFormat seek_format, dest_format; + gboolean flush; + + /* parse the seek parameters */ + gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, + &start, &stop_type, &stop); + + if ((start_type != GST_SEEK_TYPE_NONE) + && (start_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek type for start: %d", start_type); + goto done; + } + if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek type for end: %d", stop_type); + goto done; + } + + dest_format = audiomixer->segment.format; + if (seek_format != dest_format) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek format: %d", seek_format); + goto done; + } + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + /* check if we are flushing */ + if (flush) { + /* flushing seek, start flush downstream, the flush will be done + * when all pads received a FLUSH_STOP. + * Make sure we accept nothing anymore and return WRONG_STATE. + * We send a flush-start before, to ensure no streaming is done + * as we need to take the stream lock. + */ + gst_pad_push_event (audiomixer->srcpad, gst_event_new_flush_start ()); + gst_collect_pads_set_flushing (audiomixer->collect, TRUE); + + /* We can't send FLUSH_STOP here since upstream could start pushing data + * after we unlock audiomixer->collect. + * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after + * forwarding the seek upstream or from gst_audiomixer_collected, + * whichever happens first. + */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + audiomixer->flush_stop_pending = TRUE; + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + GST_DEBUG_OBJECT (audiomixer, "mark pending flush stop event"); + } + GST_DEBUG_OBJECT (audiomixer, "handling seek event: %" GST_PTR_FORMAT, + event); + + /* now wait for the collected to be finished and mark a new + * segment. After we have the lock, no collect function is running and no + * new collect function will be called for as long as we're flushing. */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + /* clip position and update our segment */ + if (audiomixer->segment.stop != -1) { + audiomixer->segment.position = audiomixer->segment.stop; + } + gst_segment_do_seek (&audiomixer->segment, rate, seek_format, flags, + start_type, start, stop_type, stop, NULL); + + if (flush) { + /* Yes, we need to call _set_flushing again *WHEN* the streaming threads + * have stopped so that the cookie gets properly updated. */ + gst_collect_pads_set_flushing (audiomixer->collect, TRUE); + } + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + GST_DEBUG_OBJECT (audiomixer, "forwarding seek event: %" GST_PTR_FORMAT, + event); + GST_DEBUG_OBJECT (audiomixer, "updated segment: %" GST_SEGMENT_FORMAT, + &audiomixer->segment); + + /* we're forwarding seek to all upstream peers and wait for one to reply + * with a newsegment-event before we send a newsegment-event downstream */ + g_atomic_int_set (&audiomixer->segment_pending, TRUE); + result = forward_event (audiomixer, event, flush); + /* FIXME: We should use the seek segment and forward that downstream next time + * not any upstream segment event */ + if (!result) { + /* seek failed. maybe source is a live source. */ + GST_DEBUG_OBJECT (audiomixer, "seeking failed"); + } + if (g_atomic_int_compare_and_exchange (&audiomixer->flush_stop_pending, + TRUE, FALSE)) { + GST_DEBUG_OBJECT (audiomixer, "pending flush stop"); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_flush_stop (TRUE))) { + GST_WARNING_OBJECT (audiomixer, "Sending flush stop event failed"); + } + } + break; + } + case GST_EVENT_QOS: + /* QoS might be tricky */ + result = FALSE; + gst_event_unref (event); + break; + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + result = FALSE; + gst_event_unref (event); + break; + default: + /* just forward the rest for now */ + GST_DEBUG_OBJECT (audiomixer, "forward unhandled event: %s", + GST_EVENT_TYPE_NAME (event)); + result = forward_event (audiomixer, event, FALSE); + break; + } + +done: + + return result; +} + +static gboolean +gst_audiomixer_sink_event (GstCollectPads * pads, GstCollectData * pad, + GstEvent * event, gpointer user_data) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); + GstAudioMixerCollect *adata = (GstAudioMixerCollect *) pad; + gboolean res = TRUE, discard = FALSE; + + GST_DEBUG_OBJECT (pad->pad, "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_audiomixer_setcaps (audiomixer, pad->pad, caps); + gst_event_unref (event); + event = NULL; + break; + } + /* FIXME: Who cares about flushes from upstream? We should + * not forward them at all */ + case GST_EVENT_FLUSH_START: + /* ensure that we will send a flush stop */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + audiomixer->flush_stop_pending = TRUE; + res = gst_collect_pads_event_default (pads, pad, event, discard); + event = NULL; + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + break; + case GST_EVENT_FLUSH_STOP: + /* we received a flush-stop. We will only forward it when + * flush_stop_pending is set, and we will unset it then. + */ + g_atomic_int_set (&audiomixer->segment_pending, TRUE); + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + if (audiomixer->flush_stop_pending) { + GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop"); + res = gst_collect_pads_event_default (pads, pad, event, discard); + audiomixer->flush_stop_pending = FALSE; + event = NULL; + gst_buffer_replace (&audiomixer->current_buffer, NULL); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } else { + discard = TRUE; + GST_DEBUG_OBJECT (pad->pad, "eating flush stop"); + } + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + /* Clear pending tags */ + if (audiomixer->pending_events) { + g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, + NULL); + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + adata->position = adata->size = 0; + adata->output_offset = adata->next_offset = -1; + gst_buffer_replace (&adata->buffer, NULL); + break; + case GST_EVENT_TAG: + /* collect tags here so we can push them out when we collect data */ + audiomixer->pending_events = + g_list_append (audiomixer->pending_events, event); + event = NULL; + break; + case GST_EVENT_SEGMENT:{ + const GstSegment *segment; + gst_event_parse_segment (event, &segment); + if (segment->rate != audiomixer->segment.rate) { + GST_ERROR_OBJECT (pad->pad, + "Got segment event with wrong rate %lf, expected %lf", + segment->rate, audiomixer->segment.rate); + res = FALSE; + gst_event_unref (event); + event = NULL; + } else if (segment->rate < 0.0) { + GST_ERROR_OBJECT (pad->pad, "Negative rates not supported yet"); + res = FALSE; + gst_event_unref (event); + event = NULL; + } + discard = TRUE; + break; + } + default: + break; + } + + if (G_LIKELY (event)) + return gst_collect_pads_event_default (pads, pad, event, discard); + else + return res; +} + +static void +gst_audiomixer_class_init (GstAudioMixerClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->set_property = gst_audiomixer_set_property; + gobject_class->get_property = gst_audiomixer_get_property; + gobject_class->dispose = gst_audiomixer_dispose; + + g_object_class_install_property (gobject_class, PROP_FILTER_CAPS, + g_param_spec_boxed ("caps", "Target caps", + "Set target format for mixing (NULL means ANY). " + "Setting this property takes a reference to the supplied GstCaps " + "object", GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, + g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", + "Timestamp alignment threshold in nanoseconds", 0, + G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, + g_param_spec_uint64 ("discont-wait", "Discont Wait", + "Window of time in nanoseconds to wait before " + "creating a discontinuity", 0, + G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, + g_param_spec_uint ("blocksize", "Block Size", + "Output block size in number of samples", 0, + G_MAXUINT, DEFAULT_BLOCKSIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audiomixer_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audiomixer_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, "AudioMixer", + "Generic/Audio", + "Mixes multiple audio streams", + "Sebastian Dröge "); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_audiomixer_change_state); +} + +static void +gst_audiomixer_init (GstAudioMixer * audiomixer) +{ + GstPadTemplate *template; + + template = gst_static_pad_template_get (&gst_audiomixer_src_template); + audiomixer->srcpad = gst_pad_new_from_template (template, "src"); + gst_object_unref (template); + + gst_pad_set_query_function (audiomixer->srcpad, + GST_DEBUG_FUNCPTR (gst_audiomixer_src_query)); + gst_pad_set_event_function (audiomixer->srcpad, + GST_DEBUG_FUNCPTR (gst_audiomixer_src_event)); + GST_PAD_SET_PROXY_CAPS (audiomixer->srcpad); + gst_element_add_pad (GST_ELEMENT (audiomixer), audiomixer->srcpad); + + audiomixer->current_caps = NULL; + gst_audio_info_init (&audiomixer->info); + audiomixer->padcount = 0; + + audiomixer->filter_caps = NULL; + audiomixer->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; + audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; + audiomixer->blocksize = DEFAULT_BLOCKSIZE; + + /* keep track of the sinkpads requested */ + audiomixer->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_collected), audiomixer); + gst_collect_pads_set_clip_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_do_clip), audiomixer); + gst_collect_pads_set_event_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event), audiomixer); + gst_collect_pads_set_query_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query), audiomixer); +} + +static void +gst_audiomixer_dispose (GObject * object) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + if (audiomixer->collect) { + gst_object_unref (audiomixer->collect); + audiomixer->collect = NULL; + } + gst_caps_replace (&audiomixer->filter_caps, NULL); + gst_caps_replace (&audiomixer->current_caps, NULL); + + if (audiomixer->pending_events) { + g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, NULL); + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_audiomixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + switch (prop_id) { + case PROP_FILTER_CAPS:{ + GstCaps *new_caps = NULL; + GstCaps *old_caps; + const GstCaps *new_caps_val = gst_value_get_caps (value); + + if (new_caps_val != NULL) { + new_caps = (GstCaps *) new_caps_val; + gst_caps_ref (new_caps); + } + + GST_OBJECT_LOCK (audiomixer); + old_caps = audiomixer->filter_caps; + audiomixer->filter_caps = new_caps; + GST_OBJECT_UNLOCK (audiomixer); + + if (old_caps) + gst_caps_unref (old_caps); + + GST_DEBUG_OBJECT (audiomixer, "set new caps %" GST_PTR_FORMAT, new_caps); + break; + } + case PROP_ALIGNMENT_THRESHOLD: + audiomixer->alignment_threshold = g_value_get_uint64 (value); + break; + case PROP_DISCONT_WAIT: + audiomixer->discont_wait = g_value_get_uint64 (value); + break; + case PROP_BLOCKSIZE: + audiomixer->blocksize = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + switch (prop_id) { + case PROP_FILTER_CAPS: + GST_OBJECT_LOCK (audiomixer); + gst_value_set_caps (value, audiomixer->filter_caps); + GST_OBJECT_UNLOCK (audiomixer); + break; + case PROP_ALIGNMENT_THRESHOLD: + g_value_set_uint64 (value, audiomixer->alignment_threshold); + break; + case PROP_DISCONT_WAIT: + g_value_set_uint64 (value, audiomixer->discont_wait); + break; + case PROP_BLOCKSIZE: + g_value_set_uint (value, audiomixer->blocksize); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +free_pad (GstCollectData * data) +{ + GstAudioMixerCollect *adata = (GstAudioMixerCollect *) data; + + gst_buffer_replace (&adata->buffer, NULL); +} + +static GstPad * +gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * unused, const GstCaps * caps) +{ + gchar *name; + GstAudioMixer *audiomixer; + GstPad *newpad; + gint padcount; + GstCollectData *cdata; + GstAudioMixerCollect *adata; + + if (templ->direction != GST_PAD_SINK) + goto not_sink; + + audiomixer = GST_AUDIO_MIXER (element); + + /* increment pad counter */ + padcount = g_atomic_int_add (&audiomixer->padcount, 1); + + name = g_strdup_printf ("sink_%u", padcount); + newpad = g_object_new (GST_TYPE_AUDIO_MIXER_PAD, "name", name, "direction", + templ->direction, "template", templ, NULL); + GST_DEBUG_OBJECT (audiomixer, "request new pad %s", name); + g_free (name); + + cdata = + gst_collect_pads_add_pad (audiomixer->collect, newpad, + sizeof (GstAudioMixerCollect), free_pad, TRUE); + adata = (GstAudioMixerCollect *) cdata; + adata->buffer = NULL; + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + adata->next_offset = -1; + + /* takes ownership of the pad */ + if (!gst_element_add_pad (GST_ELEMENT (audiomixer), newpad)) + goto could_not_add; + + gst_child_proxy_child_added (GST_CHILD_PROXY (audiomixer), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return newpad; + + /* errors */ +not_sink: + { + g_warning ("gstaudiomixer: request new pad that is not a SINK pad\n"); + return NULL; + } +could_not_add: + { + GST_DEBUG_OBJECT (audiomixer, "could not add pad"); + gst_collect_pads_remove_pad (audiomixer->collect, newpad); + gst_object_unref (newpad); + return NULL; + } +} + +static void +gst_audiomixer_release_pad (GstElement * element, GstPad * pad) +{ + GstAudioMixer *audiomixer; + + audiomixer = GST_AUDIO_MIXER (element); + + GST_DEBUG_OBJECT (audiomixer, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (audiomixer), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + if (audiomixer->collect) + gst_collect_pads_remove_pad (audiomixer->collect, pad); + gst_element_remove_pad (element, pad); +} + +static GstFlowReturn +gst_audiomixer_do_clip (GstCollectPads * pads, GstCollectData * data, + GstBuffer * buffer, GstBuffer ** out, gpointer user_data) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); + gint rate, bpf; + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf); + + *out = buffer; + return GST_FLOW_OK; +} + +static gboolean +gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, + GstCollectData * collect_data, GstAudioMixerCollect * adata, + GstBuffer * inbuf) +{ + GstClockTime start_time, end_time; + gboolean discont = FALSE; + guint64 start_offset, end_offset; + GstClockTime timestamp, stream_time; + gint rate, bpf; + + g_assert (adata->buffer == NULL); + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + timestamp = GST_BUFFER_TIMESTAMP (inbuf); + stream_time = + gst_segment_to_stream_time (&collect_data->segment, GST_FORMAT_TIME, + timestamp); + + /* sync object properties on stream time */ + /* TODO: Ideally we would want to do that on every sample */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (GST_OBJECT (collect_data->pad), stream_time); + + adata->position = 0; + adata->size = gst_buffer_get_size (inbuf); + + start_time = GST_BUFFER_TIMESTAMP (inbuf); + end_time = + start_time + gst_util_uint64_scale_ceil (adata->size / bpf, + GST_SECOND, rate); + + start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); + end_offset = start_offset + adata->size / bpf; + + if (GST_BUFFER_IS_DISCONT (inbuf) || adata->next_offset == -1) { + discont = TRUE; + } else { + guint64 diff, max_sample_diff; + + /* Check discont, based on audiobasesink */ + if (start_offset <= adata->next_offset) + diff = adata->next_offset - start_offset; + else + diff = start_offset - adata->next_offset; + + max_sample_diff = + gst_util_uint64_scale_int (audiomixer->alignment_threshold, rate, + GST_SECOND); + + /* Discont! */ + if (G_UNLIKELY (diff >= max_sample_diff)) { + if (audiomixer->discont_wait > 0) { + if (audiomixer->discont_time == GST_CLOCK_TIME_NONE) { + audiomixer->discont_time = start_time; + } else if (start_time - audiomixer->discont_time >= + audiomixer->discont_wait) { + discont = TRUE; + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + } else { + discont = TRUE; + } + } else if (G_UNLIKELY (audiomixer->discont_time != GST_CLOCK_TIME_NONE)) { + /* we have had a discont, but are now back on track! */ + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + } + + if (discont) { + /* Have discont, need resync */ + if (adata->next_offset != -1) + GST_INFO_OBJECT (collect_data->pad, "Have discont. Expected %" + G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, + adata->next_offset, start_offset); + adata->output_offset = -1; + } else { + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + + adata->next_offset = end_offset; + + if (adata->output_offset == -1) { + GstClockTime start_running_time; + GstClockTime end_running_time; + guint64 start_running_time_offset; + guint64 end_running_time_offset; + + start_running_time = + gst_segment_to_running_time (&collect_data->segment, + GST_FORMAT_TIME, start_time); + end_running_time = + gst_segment_to_running_time (&collect_data->segment, + GST_FORMAT_TIME, end_time); + start_running_time_offset = + gst_util_uint64_scale (start_running_time, rate, GST_SECOND); + end_running_time_offset = + gst_util_uint64_scale (end_running_time, rate, GST_SECOND); + + if (end_running_time_offset < audiomixer->offset) { + /* Before output segment, drop */ + gst_buffer_unref (inbuf); + adata->buffer = NULL; + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" + G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); + return FALSE; + } + + if (start_running_time_offset < audiomixer->offset) { + guint diff = (audiomixer->offset - start_running_time_offset) * bpf; + adata->position += diff; + adata->size -= diff; + /* FIXME: This could only happen due to rounding errors */ + if (adata->size == 0) { + /* Empty buffer, drop */ + gst_buffer_unref (inbuf); + adata->buffer = NULL; + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT + " < %" G_GUINT64_FORMAT, end_running_time_offset, + audiomixer->offset); + return FALSE; + } + } + + adata->output_offset = MAX (start_running_time_offset, audiomixer->offset); + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer resynced: Pad offset %" G_GUINT64_FORMAT + ", current mixer offset %" G_GUINT64_FORMAT, adata->output_offset, + audiomixer->offset); + } + + GST_LOG_OBJECT (collect_data->pad, + "Queued new buffer at offset %" G_GUINT64_FORMAT, adata->output_offset); + adata->buffer = inbuf; + + return TRUE; +} + +static void +gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, + GstCollectData * collect_data, GstAudioMixerCollect * adata, + GstMapInfo * outmap) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (adata->collect.pad); + guint overlap; + guint out_start; + GstBuffer *inbuf; + GstMapInfo inmap; + gint bpf; + + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + /* Overlap => mix */ + if (audiomixer->offset < adata->output_offset) + out_start = adata->output_offset - audiomixer->offset; + else + out_start = 0; + + if (audiomixer->offset + audiomixer->blocksize + adata->position / bpf < + adata->output_offset + adata->size / bpf + out_start) + overlap = audiomixer->blocksize - out_start; + else + overlap = adata->size / bpf - adata->position / bpf; + + inbuf = gst_collect_pads_peek (pads, collect_data); + g_assert (inbuf != NULL && inbuf == adata->buffer); + + GST_OBJECT_LOCK (pad); + if (pad->mute || pad->volume < G_MINDOUBLE) { + GST_DEBUG_OBJECT (pad, "Skipping muted pad"); + gst_buffer_unref (inbuf); + adata->position += adata->size; + adata->output_offset += adata->size / bpf; + if (adata->position >= adata->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + } + GST_OBJECT_UNLOCK (pad); + return; + } + + if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + /* skip gap buffer */ + GST_LOG_OBJECT (pad, "skipping GAP buffer"); + gst_buffer_unref (inbuf); + adata->position += adata->size; + adata->output_offset += adata->size / bpf; + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + GST_OBJECT_UNLOCK (pad); + return; + } + + gst_buffer_map (inbuf, &inmap, GST_MAP_READ); + GST_LOG_OBJECT (pad, "mixing %u bytes at offset %u from offset %u", + overlap * bpf, out_start * bpf, adata->position); + /* further buffers, need to add them */ + if (pad->volume == 1.0) { + switch (audiomixer->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + audiomixer_orc_add_u8 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S8: + audiomixer_orc_add_s8 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U16: + audiomixer_orc_add_u16 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S16: + audiomixer_orc_add_s16 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U32: + audiomixer_orc_add_u32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S32: + audiomixer_orc_add_s32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F32: + audiomixer_orc_add_f32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F64: + audiomixer_orc_add_f64 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } else { + switch (audiomixer->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + audiomixer_orc_add_volume_u8 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i8, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S8: + audiomixer_orc_add_volume_s8 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i8, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U16: + audiomixer_orc_add_volume_u16 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i16, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S16: + audiomixer_orc_add_volume_s16 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i16, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U32: + audiomixer_orc_add_volume_u32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i32, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S32: + audiomixer_orc_add_volume_s32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i32, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F32: + audiomixer_orc_add_volume_f32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F64: + audiomixer_orc_add_volume_f64 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume, overlap * audiomixer->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unref (inbuf); + + adata->position += overlap * bpf; + adata->output_offset += overlap; + + if (adata->position == adata->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); + } + + GST_OBJECT_UNLOCK (pad); +} + +static GstFlowReturn +gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) +{ + /* Get all pads that have data for us and store them in a + * new list. + * + * Calculate the current output offset/timestamp and + * offset_end/timestamp_end. Allocate a silence buffer + * for this and store it. + * + * For all pads: + * 1) Once per input buffer (cached) + * 1) Check discont (flag and timestamp with tolerance) + * 2) If discont or new, resync. That means: + * 1) Drop all start data of the buffer that comes before + * the current position/offset. + * 2) Calculate the offset (output segment!) that the first + * frame of the input buffer corresponds to. Base this on + * the running time. + * + * 2) If the current pad's offset/offset_end overlaps with the output + * offset/offset_end, mix it at the appropiate position in the output + * buffer and advance the pad's position. Remember if this pad needs + * a new buffer to advance behind the output offset_end. + * + * 3) If we had no pad with a buffer, go EOS. + * + * 4) If we had at least one pad that did not advance behind output + * offset_end, let collected be called again for the current + * output offset/offset_end. + */ + GstAudioMixer *audiomixer; + GSList *collected; + GstFlowReturn ret; + GstBuffer *outbuf = NULL; + GstMapInfo outmap; + gint64 next_offset; + gint64 next_timestamp; + gint rate, bpf; + gboolean dropped = FALSE; + gboolean is_eos = TRUE; + gboolean is_done = TRUE; + gboolean handled_buffer = FALSE; + + audiomixer = GST_AUDIO_MIXER (user_data); + + /* this is fatal */ + if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) + goto not_negotiated; + + if (audiomixer->flush_stop_pending == TRUE) { + GST_INFO_OBJECT (audiomixer->srcpad, "send pending flush stop event"); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_flush_stop (TRUE))) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending flush stop event failed"); + } + + audiomixer->flush_stop_pending = FALSE; + gst_buffer_replace (&audiomixer->current_buffer, NULL); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + + if (audiomixer->send_stream_start) { + gchar s_id[32]; + + GST_INFO_OBJECT (audiomixer->srcpad, "send pending stream start event"); + /* stream-start (FIXME: create id based on input ids) */ + g_snprintf (s_id, sizeof (s_id), "audiomixer-%08x", g_random_int ()); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_stream_start (s_id))) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending stream start event failed"); + } + audiomixer->send_stream_start = FALSE; + } + + if (audiomixer->send_caps) { + GstEvent *caps_event; + + caps_event = gst_event_new_caps (audiomixer->current_caps); + GST_INFO_OBJECT (audiomixer->srcpad, + "send pending caps event %" GST_PTR_FORMAT, caps_event); + if (!gst_pad_push_event (audiomixer->srcpad, caps_event)) { + GST_WARNING_OBJECT (audiomixer->srcpad, "Sending caps event failed"); + } + audiomixer->send_caps = FALSE; + } + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + if (g_atomic_int_compare_and_exchange (&audiomixer->segment_pending, TRUE, + FALSE)) { + GstEvent *event; + + /* + * When seeking we set the start and stop positions as given in the seek + * event. We also adjust offset & timestamp accordingly. + * This basically ignores all newsegments sent by upstream. + * + * FIXME: We require that all inputs have the same rate currently + * as we do no rate conversion! + */ + event = gst_event_new_segment (&audiomixer->segment); + if (audiomixer->segment.rate > 0.0) { + audiomixer->segment.position = audiomixer->segment.start; + } else { + audiomixer->segment.position = audiomixer->segment.stop; + } + audiomixer->offset = gst_util_uint64_scale (audiomixer->segment.position, + rate, GST_SECOND); + + GST_INFO_OBJECT (audiomixer->srcpad, "sending pending new segment event %" + GST_SEGMENT_FORMAT, &audiomixer->segment); + if (event) { + if (!gst_pad_push_event (audiomixer->srcpad, event)) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending new segment event failed"); + } + } else { + GST_WARNING_OBJECT (audiomixer->srcpad, "Creating new segment event for " + "start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT " failed", + audiomixer->segment.start, audiomixer->segment.stop); + } + } + + if (G_UNLIKELY (audiomixer->pending_events)) { + GList *tmp = audiomixer->pending_events; + + while (tmp) { + GstEvent *ev = (GstEvent *) tmp->data; + + gst_pad_push_event (audiomixer->srcpad, ev); + tmp = g_list_next (tmp); + } + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + + /* for the next timestamp, use the sample counter, which will + * never accumulate rounding errors */ + + /* FIXME: Reverse mixing does not work at all yet */ + if (audiomixer->segment.rate > 0.0) { + next_offset = audiomixer->offset + audiomixer->blocksize; + } else { + next_offset = audiomixer->offset - audiomixer->blocksize; + } + + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + + if (audiomixer->current_buffer) { + outbuf = audiomixer->current_buffer; + } else { + outbuf = gst_buffer_new_and_alloc (audiomixer->blocksize * bpf); + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + gst_audio_format_fill_silence (audiomixer->info.finfo, outmap.data, + outmap.size); + gst_buffer_unmap (outbuf, &outmap); + audiomixer->current_buffer = outbuf; + } + + GST_LOG_OBJECT (audiomixer, + "Starting to mix %u samples for offset %" G_GUINT64_FORMAT + " with timestamp %" GST_TIME_FORMAT, audiomixer->blocksize, + audiomixer->offset, GST_TIME_ARGS (audiomixer->segment.position)); + + gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); + + for (collected = pads->data; collected; collected = collected->next) { + GstCollectData *collect_data; + GstAudioMixerCollect *adata; + GstBuffer *inbuf; + + collect_data = (GstCollectData *) collected->data; + adata = (GstAudioMixerCollect *) collect_data; + + inbuf = gst_collect_pads_peek (pads, collect_data); + if (!inbuf) + continue; + + /* New buffer? */ + if (!adata->buffer || adata->buffer != inbuf) { + /* Takes ownership of buffer */ + if (!gst_audio_mixer_fill_buffer (audiomixer, pads, collect_data, adata, + inbuf)) { + dropped = TRUE; + continue; + } + } else { + gst_buffer_unref (inbuf); + } + + if (!adata->buffer && !dropped + && GST_COLLECT_PADS_STATE_IS_SET (&adata->collect, + GST_COLLECT_PADS_STATE_EOS)) { + GST_DEBUG_OBJECT (collect_data->pad, "Pad is in EOS state"); + } else { + is_eos = FALSE; + } + + /* At this point adata->output_offset >= audiomixer->offset or we have no buffer anymore */ + if (adata->output_offset >= audiomixer->offset + && adata->output_offset < + audiomixer->offset + audiomixer->blocksize && adata->buffer) { + GST_LOG_OBJECT (collect_data->pad, "Mixing buffer for current offset"); + gst_audio_mixer_mix_buffer (audiomixer, pads, collect_data, adata, + &outmap); + if (adata->output_offset >= next_offset) { + GST_DEBUG_OBJECT (collect_data->pad, + "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" + G_GUINT64_FORMAT, adata->output_offset, next_offset); + } else { + is_done = FALSE; + } + handled_buffer = TRUE; + } + } + + gst_buffer_unmap (outbuf, &outmap); + + if (dropped) { + /* We dropped a buffer, retry */ + GST_DEBUG_OBJECT (audiomixer, + "A pad dropped a buffer, wait for the next one"); + return GST_FLOW_OK; + } + + if (!is_done && !is_eos) { + /* Get more buffers */ + GST_DEBUG_OBJECT (audiomixer, + "We're not done yet for the current offset," " waiting for more data"); + return GST_FLOW_OK; + } + + if (is_eos) { + gint64 max_offset = 0; + + GST_DEBUG_OBJECT (audiomixer, "We're EOS"); + + /* This means EOS or no pads at all */ + if (!handled_buffer) { + gst_buffer_replace (&audiomixer->current_buffer, NULL); + goto eos; + } + + for (collected = pads->data; collected; collected = collected->next) { + GstCollectData *collect_data; + GstAudioMixerCollect *adata; + + collect_data = (GstCollectData *) collected->data; + adata = (GstAudioMixerCollect *) collect_data; + + max_offset = MAX (max_offset, adata->output_offset); + } + + if (max_offset <= next_offset) { + GST_DEBUG_OBJECT (audiomixer, + "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" + G_GUINT64_FORMAT, max_offset, next_offset); + next_offset = max_offset; + + gst_buffer_resize (outbuf, 0, (next_offset - audiomixer->offset) * bpf); + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + } + } + + /* set timestamps on the output buffer */ + if (audiomixer->segment.rate > 0.0) { + GST_BUFFER_TIMESTAMP (outbuf) = audiomixer->segment.position; + GST_BUFFER_OFFSET (outbuf) = audiomixer->offset; + GST_BUFFER_OFFSET_END (outbuf) = next_offset; + GST_BUFFER_DURATION (outbuf) = + next_timestamp - audiomixer->segment.position; + } else { + GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp; + GST_BUFFER_OFFSET (outbuf) = next_offset; + GST_BUFFER_OFFSET_END (outbuf) = audiomixer->offset; + GST_BUFFER_DURATION (outbuf) = + audiomixer->segment.position - next_timestamp; + } + + audiomixer->offset = next_offset; + audiomixer->segment.position = next_timestamp; + + /* send it out */ + GST_LOG_OBJECT (audiomixer, + "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_BUFFER_OFFSET (outbuf)); + + ret = gst_pad_push (audiomixer->srcpad, outbuf); + audiomixer->current_buffer = NULL; + + GST_LOG_OBJECT (audiomixer, "pushed outbuf, result = %s", + gst_flow_get_name (ret)); + + if (ret == GST_FLOW_OK && is_eos) + goto eos; + + return ret; + /* ERRORS */ +not_negotiated: + { + GST_ELEMENT_ERROR (audiomixer, STREAM, FORMAT, (NULL), + ("Unknown data received, not negotiated")); + return GST_FLOW_NOT_NEGOTIATED; + } + +eos: + { + GST_DEBUG_OBJECT (audiomixer, "EOS"); + gst_pad_push_event (audiomixer->srcpad, gst_event_new_eos ()); + return GST_FLOW_EOS; + } +} + +static GstStateChangeReturn +gst_audiomixer_change_state (GstElement * element, GstStateChange transition) +{ + GstAudioMixer *audiomixer; + GstStateChangeReturn ret; + + audiomixer = GST_AUDIO_MIXER (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + audiomixer->offset = 0; + audiomixer->flush_stop_pending = FALSE; + audiomixer->segment_pending = TRUE; + audiomixer->send_stream_start = TRUE; + audiomixer->send_caps = TRUE; + gst_caps_replace (&audiomixer->current_caps, NULL); + gst_segment_init (&audiomixer->segment, GST_FORMAT_TIME); + gst_collect_pads_start (audiomixer->collect); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* need to unblock the collectpads before calling the + * parent change_state so that streaming can finish */ + gst_collect_pads_stop (audiomixer->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_buffer_replace (&audiomixer->current_buffer, NULL); + break; + default: + break; + } + + return ret; +} + +/* GstChildProxy implementation */ +static GObject * +gst_audiomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (audiomixer); + obj = g_list_nth_data (GST_ELEMENT_CAST (audiomixer)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (audiomixer); + + return obj; +} + +static guint +gst_audiomixer_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy); + + GST_OBJECT_LOCK (audiomixer); + count = GST_ELEMENT_CAST (audiomixer)->numsinkpads; + GST_OBJECT_UNLOCK (audiomixer); + GST_INFO_OBJECT (audiomixer, "Children Count: %d", count); + + return count; +} + +static void +gst_audiomixer_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_audiomixer_child_proxy_get_child_by_index; + iface->get_children_count = gst_audiomixer_child_proxy_get_children_count; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "audiomixer", 0, + "audio mixing element"); + + if (!gst_element_register (plugin, "audiomixer", GST_RANK_NONE, + GST_TYPE_AUDIO_MIXER)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + audiomixer, + "Mixes multiple audio streams", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h new file mode 100644 index 0000000000..40a25c94eb --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.h @@ -0,0 +1,126 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * Copyright (C) 2013 Sebastian Dröge + * + * gstaudiomixer.h: Header for GstAudioMixer 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_MIXER_H__ +#define __GST_AUDIO_MIXER_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIO_MIXER (gst_audiomixer_get_type()) +#define GST_AUDIO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_MIXER,GstAudioMixer)) +#define GST_IS_AUDIO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_MIXER)) +#define GST_AUDIO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_MIXER,GstAudioMixerClass)) +#define GST_IS_AUDIO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_MIXER)) +#define GST_AUDIO_MIXER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER,GstAudioMixerClass)) + +typedef struct _GstAudioMixer GstAudioMixer; +typedef struct _GstAudioMixerClass GstAudioMixerClass; + +typedef struct _GstAudioMixerPad GstAudioMixerPad; +typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; + +/** + * GstAudioMixer: + * + * The audiomixer object structure. + */ +struct _GstAudioMixer { + GstElement element; + + GstPad *srcpad; + GstCollectPads *collect; + /* pad counter, used for creating unique request pads */ + gint padcount; + + /* the next are valid for both int and float */ + GstAudioInfo info; + + /* counters to keep track of timestamps */ + gint64 offset; + /* Buffer starting at offset containing block_size samples */ + GstBuffer *current_buffer; + + /* sink event handling */ + GstSegment segment; + volatile gboolean segment_pending; + volatile gboolean flush_stop_pending; + + /* current caps */ + GstCaps *current_caps; + + /* target caps (set via property) */ + GstCaps *filter_caps; + + GstClockTime alignment_threshold; + GstClockTime discont_wait; + + /* Last time we noticed a discont */ + GstClockTime discont_time; + + /* Size in samples that is output per buffer */ + guint blocksize; + + /* Pending inline events */ + GList *pending_events; + + gboolean send_stream_start; + gboolean send_caps; +}; + +struct _GstAudioMixerClass { + GstElementClass parent_class; +}; + +GType gst_audiomixer_get_type (void); + +#define GST_TYPE_AUDIO_MIXER_PAD (gst_audiomixer_pad_get_type()) +#define GST_AUDIO_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPad)) +#define GST_IS_AUDIO_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_MIXER_PAD)) +#define GST_AUDIO_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) +#define GST_IS_AUDIO_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_MIXER_PAD)) +#define GST_AUDIO_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) + +struct _GstAudioMixerPad { + GstPad parent; + + gdouble volume; + gint volume_i32; + gint volume_i16; + gint volume_i8; + gboolean mute; +}; + +struct _GstAudioMixerPadClass { + GstPadClass parent_class; +}; + +GType gst_audiomixer_pad_get_type (void); + +G_END_DECLS + + +#endif /* __GST_AUDIO_MIXER_H__ */ diff --git a/gst/audiomixer/gstaudiomixerorc-dist.c b/gst/audiomixer/gstaudiomixerorc-dist.c new file mode 100644 index 0000000000..1a54e14e24 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.c @@ -0,0 +1,2661 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union +{ + orc_int16 i; + orc_int8 x2[2]; +} orc_union16; +typedef union +{ + orc_int32 i; + float f; + orc_int16 x2[2]; + orc_int8 x4[4]; +} orc_union32; +typedef union +{ + orc_int64 i; + double f; + orc_int32 x2[2]; + float x2f[2]; + orc_int16 x4[4]; +} orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + + +#ifndef DISABLE_ORC +#include +#endif +void audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, int n); +void audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n); +void audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n); +void audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n); + + +/* begin Orc C target preamble */ +#define ORC_CLAMP(x,a,b) ((x)<(a) ? (a) : ((x)>(b) ? (b) : (x))) +#define ORC_ABS(a) ((a)<0 ? -(a) : (a)) +#define ORC_MIN(a,b) ((a)<(b) ? (a) : (b)) +#define ORC_MAX(a,b) ((a)>(b) ? (a) : (b)) +#define ORC_SB_MAX 127 +#define ORC_SB_MIN (-1-ORC_SB_MAX) +#define ORC_UB_MAX 255 +#define ORC_UB_MIN 0 +#define ORC_SW_MAX 32767 +#define ORC_SW_MIN (-1-ORC_SW_MAX) +#define ORC_UW_MAX 65535 +#define ORC_UW_MIN 0 +#define ORC_SL_MAX 2147483647 +#define ORC_SL_MIN (-1-ORC_SL_MAX) +#define ORC_UL_MAX 4294967295U +#define ORC_UL_MIN 0 +#define ORC_CLAMP_SB(x) ORC_CLAMP(x,ORC_SB_MIN,ORC_SB_MAX) +#define ORC_CLAMP_UB(x) ORC_CLAMP(x,ORC_UB_MIN,ORC_UB_MAX) +#define ORC_CLAMP_SW(x) ORC_CLAMP(x,ORC_SW_MIN,ORC_SW_MAX) +#define ORC_CLAMP_UW(x) ORC_CLAMP(x,ORC_UW_MIN,ORC_UW_MAX) +#define ORC_CLAMP_SL(x) ORC_CLAMP(x,ORC_SL_MIN,ORC_SL_MAX) +#define ORC_CLAMP_UL(x) ORC_CLAMP(x,ORC_UL_MIN,ORC_UL_MAX) +#define ORC_SWAP_W(x) ((((x)&0xff)<<8) | (((x)&0xff00)>>8)) +#define ORC_SWAP_L(x) ((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>24)) +#define ORC_SWAP_Q(x) ((((x)&ORC_UINT64_C(0xff))<<56) | (((x)&ORC_UINT64_C(0xff00))<<40) | (((x)&ORC_UINT64_C(0xff0000))<<24) | (((x)&ORC_UINT64_C(0xff000000))<<8) | (((x)&ORC_UINT64_C(0xff00000000))>>8) | (((x)&ORC_UINT64_C(0xff0000000000))>>24) | (((x)&ORC_UINT64_C(0xff000000000000))>>40) | (((x)&ORC_UINT64_C(0xff00000000000000))>>56)) +#define ORC_PTR_OFFSET(ptr,offset) ((void *)(((unsigned char *)(ptr)) + (offset))) +#define ORC_DENORMAL(x) ((x) & ((((x)&0x7f800000) == 0) ? 0xff800000 : 0xffffffff)) +#define ORC_ISNAN(x) ((((x)&0x7f800000) == 0x7f800000) && (((x)&0x007fffff) != 0)) +#define ORC_DENORMAL_DOUBLE(x) ((x) & ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == 0) ? ORC_UINT64_C(0xfff0000000000000) : ORC_UINT64_C(0xffffffffffffffff))) +#define ORC_ISNAN_DOUBLE(x) ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == ORC_UINT64_C(0x7ff0000000000000)) && (((x)&ORC_UINT64_C(0x000fffffffffffff)) != 0)) +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif +/* end Orc C target preamble */ + + + +/* audiomixer_orc_add_s32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addssl */ + var34.i = ORC_CLAMP_SL ((orc_int64) var32.i + (orc_int64) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addssl */ + var34.i = ORC_CLAMP_SL ((orc_int64) var32.i + (orc_int64) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 51, 50, 11, 4, 4, 12, 4, 4, 104, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addssl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_s16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addssw */ + var34.i = ORC_CLAMP_SW (var32.i + var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addssw */ + var34.i = ORC_CLAMP_SW (var32.i + var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 49, 54, 11, 2, 2, 12, 2, 2, 71, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s16"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + + orc_program_append_2 (p, "addssw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_s8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, + int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addssb */ + var34 = ORC_CLAMP_SB (var32 + var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addssb */ + var34 = ORC_CLAMP_SB (var32 + var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 21, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 56, 11, 1, 1, 12, 1, 1, 34, 0, + 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + + orc_program_append_2 (p, "addssb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addusl */ + var34.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var32.i + + (orc_int64) (orc_uint32) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addusl */ + var34.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var32.i + + (orc_int64) (orc_uint32) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 51, 50, 11, 4, 4, 12, 4, 4, 105, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addusl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addusw */ + var34.i = ORC_CLAMP_UW ((orc_uint16) var32.i + (orc_uint16) var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addusw */ + var34.i = ORC_CLAMP_UW ((orc_uint16) var32.i + (orc_uint16) var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 49, 54, 11, 2, 2, 12, 2, 2, 72, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u16"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + + orc_program_append_2 (p, "addusw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, + int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addusb */ + var34 = ORC_CLAMP_UB ((orc_uint8) var32 + (orc_uint8) var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addusb */ + var34 = ORC_CLAMP_UB ((orc_uint8) var32 + (orc_uint8) var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 21, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 56, 11, 1, 1, 12, 1, 1, 35, 0, + 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + + orc_program_append_2 (p, "addusb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_f32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, const float *ORC_RESTRICT s1, + int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var32.i); + _src2.i = ORC_DENORMAL (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL (_dest1.i); + } + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_f32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var32.i); + _src2.i = ORC_DENORMAL (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL (_dest1.i); + } + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, const float *ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 102, 51, 50, 11, 4, 4, 12, 4, 4, 200, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_f32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addf", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_f64 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, const double *ORC_RESTRICT s1, + int n) +{ + int i; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var32; + orc_union64 var33; + orc_union64 var34; + + ptr0 = (orc_union64 *) d1; + ptr4 = (orc_union64 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var32 = ptr0[i]; + /* 1: loadq */ + var33 = ptr4[i]; + /* 2: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var32.i); + _src2.i = ORC_DENORMAL_DOUBLE (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: storeq */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_f64 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var32; + orc_union64 var33; + orc_union64 var34; + + ptr0 = (orc_union64 *) ex->arrays[0]; + ptr4 = (orc_union64 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var32 = ptr0[i]; + /* 1: loadq */ + var33 = ptr4[i]; + /* 2: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var32.i); + _src2.i = ORC_DENORMAL_DOUBLE (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: storeq */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, const double *ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 102, 54, 52, 11, 8, 8, 12, 8, 8, 212, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f64); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_f64"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f64); + orc_program_add_destination (p, 8, "d1"); + orc_program_add_source (p, 8, "s1"); + + orc_program_append_2 (p, "addd", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_volume_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + + ptr0 = (orc_int8 *) d1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var39 = var34 ^ var35; + /* 4: mulsbw */ + var40.i = var39 * var36; + /* 5: shrsw */ + var41.i = var40.i >> 3; + /* 6: convssswb */ + var42 = ORC_CLAMP_SB (var41.i); + /* 8: xorb */ + var38 = var42 ^ var37; + /* 9: storeb */ + ptr0[i] = var38; + } + +} + +#else +static void +_backup_audiomixer_orc_volume_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + + ptr0 = (orc_int8 *) ex->arrays[0]; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = ex->params[24]; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var39 = var34 ^ var35; + /* 4: mulsbw */ + var40.i = var39 * var36; + /* 5: shrsw */ + var41.i = var40.i >> 3; + /* 6: convssswb */ + var42 = ORC_CLAMP_SB (var41.i); + /* 8: xorb */ + var38 = var42 ^ var37; + /* 9: storeb */ + ptr0[i] = var38; + } + +} + +void +audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 24, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, 1, 1, 14, 1, + 128, 0, 0, 0, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, + 68, 33, 0, 16, 174, 32, 33, 24, 94, 32, 32, 17, 159, 33, 32, 68, + 0, 33, 16, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_volume_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_volume_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_volume_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_constant (p, 1, 0x00000080, "c1"); + orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_D1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorb", 0, ORC_VAR_D1, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_int8 var40; + orc_union16 var41; + orc_union16 var42; + orc_int8 var43; + orc_int8 var44; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: xorb */ + var40 = var34 ^ var35; + /* 4: mulsbw */ + var41.i = var40 * var36; + /* 5: shrsw */ + var42.i = var41.i >> 3; + /* 6: convssswb */ + var43 = ORC_CLAMP_SB (var42.i); + /* 8: xorb */ + var44 = var43 ^ var37; + /* 9: loadb */ + var38 = ptr0[i]; + /* 10: addusb */ + var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); + /* 11: storeb */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_int8 var40; + orc_union16 var41; + orc_union16 var42; + orc_int8 var43; + orc_int8 var44; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = ex->params[24]; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: xorb */ + var40 = var34 ^ var35; + /* 4: mulsbw */ + var41.i = var40 * var36; + /* 5: shrsw */ + var42.i = var41.i >> 3; + /* 6: convssswb */ + var43 = ORC_CLAMP_SB (var42.i); + /* 8: xorb */ + var44 = var43 ^ var37; + /* 9: loadb */ + var38 = ptr0[i]; + /* 10: addusb */ + var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); + /* 11: storeb */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, + 1, 1, 12, 1, 1, 14, 1, 128, 0, 0, 0, 14, 4, 3, 0, 0, + 0, 16, 1, 20, 2, 20, 1, 68, 33, 4, 16, 174, 32, 33, 24, 94, + 32, 32, 17, 159, 33, 32, 68, 33, 33, 16, 35, 0, 0, 33, 2, 0, + + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + orc_program_add_constant (p, 1, 0x00000080, "c1"); + orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_int8 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_int8 var40; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + /* 1: loadpb */ + var35 = p1; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: mulsbw */ + var38.i = var34 * var35; + /* 3: shrsw */ + var39.i = var38.i >> 3; + /* 4: convssswb */ + var40 = ORC_CLAMP_SB (var39.i); + /* 5: loadb */ + var36 = ptr0[i]; + /* 6: addssb */ + var37 = ORC_CLAMP_SB (var36 + var40); + /* 7: storeb */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_int8 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_int8 var40; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + /* 1: loadpb */ + var35 = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: mulsbw */ + var38.i = var34 * var35; + /* 3: shrsw */ + var39.i = var38.i >> 3; + /* 4: convssswb */ + var40 = ORC_CLAMP_SB (var39.i); + /* 5: loadb */ + var36 = ptr0[i]; + /* 6: addssb */ + var37 = ORC_CLAMP_SB (var36 + var40); + /* 7: storeb */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 56, 11, + 1, 1, 12, 1, 1, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, + 1, 174, 32, 4, 24, 94, 32, 32, 16, 159, 33, 32, 34, 0, 0, 33, + 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_s8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_s8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + orc_program_add_constant (p, 4, 0x00000003, "c1"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var35; +#else + orc_union16 var35; +#endif + orc_union16 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var37; +#else + orc_union16 var37; +#endif + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_union16 var44; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + /* 1: loadpw */ + var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + /* 3: loadpw */ + var36.i = p1; + /* 7: loadpw */ + var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var40.i = var34.i ^ var35.i; + /* 4: mulswl */ + var41.i = var40.i * var36.i; + /* 5: shrsl */ + var42.i = var41.i >> 11; + /* 6: convssslw */ + var43.i = ORC_CLAMP_SW (var42.i); + /* 8: xorw */ + var44.i = var43.i ^ var37.i; + /* 9: loadw */ + var38 = ptr0[i]; + /* 10: addusw */ + var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); + /* 11: storew */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var35; +#else + orc_union16 var35; +#endif + orc_union16 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var37; +#else + orc_union16 var37; +#endif + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_union16 var44; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + /* 1: loadpw */ + var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + /* 3: loadpw */ + var36.i = ex->params[24]; + /* 7: loadpw */ + var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var40.i = var34.i ^ var35.i; + /* 4: mulswl */ + var41.i = var40.i * var36.i; + /* 5: shrsl */ + var42.i = var41.i >> 11; + /* 6: convssslw */ + var43.i = ORC_CLAMP_SW (var42.i); + /* 8: xorw */ + var44.i = var43.i ^ var37.i; + /* 9: loadw */ + var38 = ptr0[i]; + /* 10: addusw */ + var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); + /* 11: storew */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 49, 54, + 11, 2, 2, 12, 2, 2, 14, 2, 0, 128, 0, 0, 14, 4, 11, 0, + 0, 0, 16, 2, 20, 4, 20, 2, 101, 33, 4, 16, 176, 32, 33, 24, + 125, 32, 32, 17, 165, 33, 32, 101, 33, 33, 16, 72, 0, 0, 33, 2, + 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u16"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + orc_program_add_constant (p, 2, 0x00008000, "c1"); + orc_program_add_constant (p, 4, 0x0000000b, "c2"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + + orc_program_append_2 (p, "xorw", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulswl", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssslw", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorw", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; + orc_union16 var35; + orc_union16 var36; + orc_union16 var37; + orc_union32 var38; + orc_union32 var39; + orc_union16 var40; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + /* 1: loadpw */ + var35.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: mulswl */ + var38.i = var34.i * var35.i; + /* 3: shrsl */ + var39.i = var38.i >> 11; + /* 4: convssslw */ + var40.i = ORC_CLAMP_SW (var39.i); + /* 5: loadw */ + var36 = ptr0[i]; + /* 6: addssw */ + var37.i = ORC_CLAMP_SW (var36.i + var40.i); + /* 7: storew */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; + orc_union16 var35; + orc_union16 var36; + orc_union16 var37; + orc_union32 var38; + orc_union32 var39; + orc_union16 var40; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + /* 1: loadpw */ + var35.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: mulswl */ + var38.i = var34.i * var35.i; + /* 3: shrsl */ + var39.i = var38.i >> 11; + /* 4: convssslw */ + var40.i = ORC_CLAMP_SW (var39.i); + /* 5: loadw */ + var36 = ptr0[i]; + /* 6: addssw */ + var37.i = ORC_CLAMP_SW (var36.i + var40.i); + /* 7: storew */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 49, 54, + 11, 2, 2, 12, 2, 2, 14, 4, 11, 0, 0, 0, 16, 2, 20, 4, + 20, 2, 176, 32, 4, 24, 125, 32, 32, 16, 165, 33, 32, 71, 0, 0, + 33, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s16"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + orc_program_add_constant (p, 4, 0x0000000b, "c1"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + + orc_program_append_2 (p, "mulswl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convssslw", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var35; +#else + orc_union32 var35; +#endif + orc_union32 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var37; +#else + orc_union32 var37; +#endif + orc_union32 var38; + orc_union32 var39; + orc_union32 var40; + orc_union64 var41; + orc_union64 var42; + orc_union32 var43; + orc_union32 var44; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + /* 3: loadpl */ + var36.i = p1; + /* 7: loadpl */ + var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var40.i = var34.i ^ var35.i; + /* 4: mulslq */ + var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var42.i = var41.i >> 27; + /* 6: convsssql */ + var43.i = ORC_CLAMP_SL (var42.i); + /* 8: xorl */ + var44.i = var43.i ^ var37.i; + /* 9: loadl */ + var38 = ptr0[i]; + /* 10: addusl */ + var39.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + + (orc_int64) (orc_uint32) var44.i); + /* 11: storel */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var35; +#else + orc_union32 var35; +#endif + orc_union32 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var37; +#else + orc_union32 var37; +#endif + orc_union32 var38; + orc_union32 var39; + orc_union32 var40; + orc_union64 var41; + orc_union64 var42; + orc_union32 var43; + orc_union32 var44; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + /* 3: loadpl */ + var36.i = ex->params[24]; + /* 7: loadpl */ + var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var40.i = var34.i ^ var35.i; + /* 4: mulslq */ + var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var42.i = var41.i >> 27; + /* 6: convsssql */ + var43.i = ORC_CLAMP_SL (var42.i); + /* 8: xorl */ + var44.i = var43.i ^ var37.i; + /* 9: loadl */ + var38 = ptr0[i]; + /* 10: addusl */ + var39.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + + (orc_int64) (orc_uint32) var44.i); + /* 11: storel */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 51, 50, + 11, 4, 4, 12, 4, 4, 14, 4, 0, 0, 0, 128, 14, 4, 27, 0, + 0, 0, 16, 4, 20, 8, 20, 4, 132, 33, 4, 16, 178, 32, 33, 24, + 147, 32, 32, 17, 170, 33, 32, 132, 33, 33, 16, 105, 0, 0, 33, 2, + 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x80000000, "c1"); + orc_program_add_constant (p, 4, 0x0000001b, "c2"); + orc_program_add_parameter (p, 4, "p1"); + orc_program_add_temporary (p, 8, "t1"); + orc_program_add_temporary (p, 4, "t2"); + + orc_program_append_2 (p, "xorl", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulslq", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsq", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convsssql", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + orc_union64 var38; + orc_union64 var39; + orc_union32 var40; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var35.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: mulslq */ + var38.i = ((orc_int64) var34.i) * ((orc_int64) var35.i); + /* 3: shrsq */ + var39.i = var38.i >> 27; + /* 4: convsssql */ + var40.i = ORC_CLAMP_SL (var39.i); + /* 5: loadl */ + var36 = ptr0[i]; + /* 6: addssl */ + var37.i = ORC_CLAMP_SL ((orc_int64) var36.i + (orc_int64) var40.i); + /* 7: storel */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + orc_union64 var38; + orc_union64 var39; + orc_union32 var40; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var35.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: mulslq */ + var38.i = ((orc_int64) var34.i) * ((orc_int64) var35.i); + /* 3: shrsq */ + var39.i = var38.i >> 27; + /* 4: convsssql */ + var40.i = ORC_CLAMP_SL (var39.i); + /* 5: loadl */ + var36 = ptr0[i]; + /* 6: addssl */ + var37.i = ORC_CLAMP_SL ((orc_int64) var36.i + (orc_int64) var40.i); + /* 7: storel */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 51, 50, + 11, 4, 4, 12, 4, 4, 14, 4, 27, 0, 0, 0, 16, 4, 20, 8, + 20, 4, 178, 32, 4, 24, 147, 32, 32, 16, 170, 33, 32, 104, 0, 0, + 33, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x0000001b, "c1"); + orc_program_add_parameter (p, 4, "p1"); + orc_program_add_temporary (p, 8, "t1"); + orc_program_add_temporary (p, 4, "t2"); + + orc_program_append_2 (p, "mulslq", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsq", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convsssql", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_f32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var33; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var34.f = p1; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var33 = ptr4[i]; + /* 2: mulf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var33.i); + _src2.i = ORC_DENORMAL (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL (_dest1.i); + } + /* 3: loadl */ + var35 = ptr0[i]; + /* 4: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var35.i); + _src2.i = ORC_DENORMAL (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL (_dest1.i); + } + /* 5: storel */ + ptr0[i] = var36; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_f32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var33; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var34.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var33 = ptr4[i]; + /* 2: mulf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var33.i); + _src2.i = ORC_DENORMAL (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL (_dest1.i); + } + /* 3: loadl */ + var35 = ptr0[i]; + /* 4: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var35.i); + _src2.i = ORC_DENORMAL (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL (_dest1.i); + } + /* 5: storel */ + ptr0[i] = var36; + } + +} + +void +audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 102, 51, 50, + 11, 4, 4, 12, 4, 4, 17, 4, 20, 4, 202, 32, 4, 24, 200, 0, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_f32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_parameter_float (p, 4, "p1"); + orc_program_add_temporary (p, 4, "t1"); + + orc_program_append_2 (p, "mulf", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "addf", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + { + orc_union32 tmp; + tmp.f = p1; + ex->params[ORC_VAR_P1] = tmp.i; + } + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_f64 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n) +{ + int i; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var33; + orc_union64 var34; + orc_union64 var35; + orc_union64 var36; + orc_union64 var37; + + ptr0 = (orc_union64 *) d1; + ptr4 = (orc_union64 *) s1; + + /* 1: loadpq */ + var34.f = p1; + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var33 = ptr4[i]; + /* 2: muld */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var33.i); + _src2.i = ORC_DENORMAL_DOUBLE (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: loadq */ + var35 = ptr0[i]; + /* 4: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var35.i); + _src2.i = ORC_DENORMAL_DOUBLE (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 5: storeq */ + ptr0[i] = var36; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_f64 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var33; + orc_union64 var34; + orc_union64 var35; + orc_union64 var36; + orc_union64 var37; + + ptr0 = (orc_union64 *) ex->arrays[0]; + ptr4 = (orc_union64 *) ex->arrays[4]; + + /* 1: loadpq */ + var34.i = + (ex->params[24] & 0xffffffff) | ((orc_uint64) (ex->params[24 + + (ORC_VAR_T1 - ORC_VAR_P1)]) << 32); + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var33 = ptr4[i]; + /* 2: muld */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var33.i); + _src2.i = ORC_DENORMAL_DOUBLE (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: loadq */ + var35 = ptr0[i]; + /* 4: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var35.i); + _src2.i = ORC_DENORMAL_DOUBLE (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 5: storeq */ + ptr0[i] = var36; + } + +} + +void +audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 102, 54, 52, + 11, 8, 8, 12, 8, 8, 18, 8, 20, 8, 214, 32, 4, 24, 212, 0, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f64); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_f64"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f64); + orc_program_add_destination (p, 8, "d1"); + orc_program_add_source (p, 8, "s1"); + orc_program_add_parameter_double (p, 8, "p1"); + orc_program_add_temporary (p, 8, "t1"); + + orc_program_append_2 (p, "muld", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "addd", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + { + orc_union64 tmp; + tmp.f = p1; + ex->params[ORC_VAR_P1] = tmp.x2[0]; + ex->params[ORC_VAR_T1] = tmp.x2[1]; + } + + func = c->exec; + func (ex); +} +#endif diff --git a/gst/audiomixer/gstaudiomixerorc-dist.h b/gst/audiomixer/gstaudiomixerorc-dist.h new file mode 100644 index 0000000000..af0de01394 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.h @@ -0,0 +1,106 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifndef _GSTAUDIOMIXERORC_H_ +#define _GSTAUDIOMIXERORC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union { orc_int16 i; orc_int8 x2[2]; } orc_union16; +typedef union { orc_int32 i; float f; orc_int16 x2[2]; orc_int8 x4[4]; } orc_union32; +typedef union { orc_int64 i; double f; orc_int32 x2[2]; float x2f[2]; orc_int16 x4[4]; } orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + +void audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, const gint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, const gint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f32 (float * ORC_RESTRICT d1, const float * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f64 (double * ORC_RESTRICT d1, const double * ORC_RESTRICT s1, int n); +void audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n); +void audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, const gint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, const gint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_f32 (float * ORC_RESTRICT d1, const float * ORC_RESTRICT s1, float p1, int n); +void audiomixer_orc_add_volume_f64 (double * ORC_RESTRICT d1, const double * ORC_RESTRICT s1, double p1, int n); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gst/audiomixer/gstaudiomixerorc.orc b/gst/audiomixer/gstaudiomixerorc.orc new file mode 100644 index 0000000000..5eaff2395b --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc.orc @@ -0,0 +1,176 @@ +.function audiomixer_orc_add_s32 +.dest 4 d1 gint32 +.source 4 s1 gint32 + +addssl d1, d1, s1 + + +.function audiomixer_orc_add_s16 +.dest 2 d1 gint16 +.source 2 s1 gint16 + +addssw d1, d1, s1 + + +.function audiomixer_orc_add_s8 +.dest 1 d1 gint8 +.source 1 s1 gint8 + +addssb d1, d1, s1 + + +.function audiomixer_orc_add_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 + +addusl d1, d1, s1 + + +.function audiomixer_orc_add_u16 +.dest 2 d1 guint16 +.source 2 s1 guint16 + +addusw d1, d1, s1 + + +.function audiomixer_orc_add_u8 +.dest 1 d1 guint8 +.source 1 s1 guint8 + +addusb d1, d1, s1 + + +.function audiomixer_orc_add_f32 +.dest 4 d1 float +.source 4 s1 float + +addf d1, d1, s1 + +.function audiomixer_orc_add_f64 +.dest 8 d1 double +.source 8 s1 double + +addd d1, d1, s1 + + +.function audiomixer_orc_volume_u8 +.dest 1 d1 guint8 +.param 1 p1 +.const 1 c1 0x80 +.temp 2 t1 +.temp 1 t2 + +xorb t2, d1, c1 +mulsbw t1, t2, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +xorb d1, t2, c1 + + +.function audiomixer_orc_add_volume_u8 +.dest 1 d1 guint8 +.source 1 s1 guint8 +.param 1 p1 +.const 1 c1 0x80 +.temp 2 t1 +.temp 1 t2 + +xorb t2, s1, c1 +mulsbw t1, t2, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +xorb t2, t2, c1 +addusb d1, d1, t2 + + +.function audiomixer_orc_add_volume_s8 +.dest 1 d1 gint8 +.source 1 s1 gint8 +.param 1 p1 +.temp 2 t1 +.temp 1 t2 + +mulsbw t1, s1, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +addssb d1, d1, t2 + + +.function audiomixer_orc_add_volume_u16 +.dest 2 d1 guint16 +.source 2 s1 guint16 +.param 2 p1 +.const 2 c1 0x8000 +.temp 4 t1 +.temp 2 t2 + +xorw t2, s1, c1 +mulswl t1, t2, p1 +shrsl t1, t1, 11 +convssslw t2, t1 +xorw t2, t2, c1 +addusw d1, d1, t2 + + +.function audiomixer_orc_add_volume_s16 +.dest 2 d1 gint16 +.source 2 s1 gint16 +.param 2 p1 +.temp 4 t1 +.temp 2 t2 + +mulswl t1, s1, p1 +shrsl t1, t1, 11 +convssslw t2, t1 +addssw d1, d1, t2 + + +.function audiomixer_orc_add_volume_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 +.param 4 p1 +.const 4 c1 0x80000000 +.temp 8 t1 +.temp 4 t2 + +xorl t2, s1, c1 +mulslq t1, t2, p1 +shrsq t1, t1, 27 +convsssql t2, t1 +xorl t2, t2, c1 +addusl d1, d1, t2 + + +.function audiomixer_orc_add_volume_s32 +.dest 4 d1 gint32 +.source 4 s1 gint32 +.param 4 p1 +.temp 8 t1 +.temp 4 t2 + +mulslq t1, s1, p1 +shrsq t1, t1, 27 +convsssql t2, t1 +addssl d1, d1, t2 + + +.function audiomixer_orc_add_volume_f32 +.dest 4 d1 float +.source 4 s1 float +.floatparam 4 p1 +.temp 4 t1 + +mulf t1, s1, p1 +addf d1, d1, t1 + + +.function audiomixer_orc_add_volume_f64 +.dest 8 d1 double +.source 8 s1 double +.doubleparam 8 p1 +.temp 8 t1 + +muld t1, s1, p1 +addd d1, d1, t1 + + diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c new file mode 100644 index 0000000000..313cc49a55 --- /dev/null +++ b/tests/check/elements/audiomixer.c @@ -0,0 +1,1296 @@ +/* GStreamer + * + * unit test for audiomixer + * + * Copyright (C) <2005> Thomas Vander Stichele + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_VALGRIND +# include +#endif + +#include + +#include +#include +#include + +static GMainLoop *main_loop; + +/* make sure downstream gets a CAPS event before buffers are sent */ +GST_START_TEST (test_caps) +{ + GstElement *pipeline, *src, *audiomixer, *sink; + GstStateChangeReturn state_res; + GstCaps *caps; + GstPad *pad; + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + + src = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); + + fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); + + /* prepare playing */ + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + + /* wait for preroll */ + state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); + + /* check caps on fakesink */ + pad = gst_element_get_static_pad (sink, "sink"); + caps = gst_pad_get_current_caps (pad); + fail_unless (caps != NULL); + gst_caps_unref (caps); + gst_object_unref (pad); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +/* check that caps set on the property are honoured */ +GST_START_TEST (test_filter_caps) +{ + GstElement *pipeline, *src, *audiomixer, *sink; + GstStateChangeReturn state_res; + GstCaps *filter_caps, *caps; + GstPad *pad; + + filter_caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "F32LE", + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL); + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + + src = gst_element_factory_make ("audiotestsrc", NULL); + g_object_set (src, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", NULL); + g_object_set (audiomixer, "caps", filter_caps, NULL); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); + + fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); + + /* prepare playing */ + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + + /* wait for preroll */ + state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); + + /* check caps on fakesink */ + pad = gst_element_get_static_pad (sink, "sink"); + caps = gst_pad_get_current_caps (pad); + fail_unless (caps != NULL); + GST_INFO_OBJECT (pipeline, "received caps: %" GST_PTR_FORMAT, caps); + fail_unless (gst_caps_is_equal_fixed (caps, filter_caps)); + gst_caps_unref (caps); + gst_object_unref (pad); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gst_caps_unref (filter_caps); +} + +GST_END_TEST; + +static void +message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_EOS: + g_main_loop_quit (main_loop); + break; + case GST_MESSAGE_WARNING:{ + GError *gerror; + gchar *debug; + + gst_message_parse_warning (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + break; + } + case GST_MESSAGE_ERROR:{ + GError *gerror; + gchar *debug; + + gst_message_parse_error (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + g_main_loop_quit (main_loop); + break; + } + default: + break; + } +} + + +static GstFormat format = GST_FORMAT_UNDEFINED; +static gint64 position = -1; + +static void +test_event_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + gst_message_parse_segment_done (message, &format, &position); + GST_INFO ("received segment_done : %" G_GINT64_FORMAT, position); + g_main_loop_quit (main_loop); + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_event) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + GstPad *srcpad, *sinkpad; + GstStreamConsistency *chk_1, *chk_2, *chk_3; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + chk_3 = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + /* create consistency checkers for the pads */ + srcpad = gst_element_get_static_pad (src1, "src"); + chk_1 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (src2, "src"); + chk_2 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + format = GST_FORMAT_UNDEFINED; + position = -1; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_event_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (position, 2 * GST_SECOND); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (chk_1); + gst_consistency_checker_free (chk_2); + gst_consistency_checker_free (chk_3); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +static guint play_count = 0; +static GstEvent *play_seek_event = NULL; + +static void +test_play_twice_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + play_count++; + if (play_count == 1) { + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* prepare playing again */ + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (GST_ELEMENT (bin), + gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + state_res = + gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + } else { + g_main_loop_quit (main_loop); + } + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_play_twice) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + play_count = 0; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (consist); + gst_event_ref (play_seek_event); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_play_twice_then_add_and_play_again) +{ + GstElement *bin, *src1, *src2, *src3, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + gint i; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* run it twice */ + for (i = 0; i < 2; i++) { + play_count = 0; + + GST_INFO ("starting test-loop %d", i); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* plug another source */ + if (i == 0) { + src3 = gst_element_factory_make ("audiotestsrc", "src3"); + g_object_set (src3, "wave", 4, NULL); /* silence */ + gst_bin_add (GST_BIN (bin), src3); + + res = gst_element_link (src3, audiomixer); + fail_unless (res == TRUE, NULL); + } + + gst_consistency_checker_reset (consist); + } + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_event_ref (play_seek_event); + gst_consistency_checker_free (consist); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + + +static void +test_live_seeking_eos_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_EOS: + g_main_loop_quit (main_loop); + break; + default: + g_assert_not_reached (); + break; + } +} + +static GstElement * +test_live_seeking_try_audiosrc (const gchar * factory_name) +{ + GstElement *src; + GstStateChangeReturn state_res; + + if (!(src = gst_element_factory_make (factory_name, NULL))) { + GST_INFO ("can't make '%s', skipping", factory_name); + return NULL; + } + + /* Test that the audio source can get to ready, else skip */ + state_res = gst_element_set_state (src, GST_STATE_READY); + gst_element_set_state (src, GST_STATE_NULL); + + if (state_res == GST_STATE_CHANGE_FAILURE) { + GST_INFO_OBJECT (src, "can't go to ready, skipping"); + gst_object_unref (src); + return NULL; + } + + return src; +} + +/* test failing seeks on live-sources */ +GST_START_TEST (test_live_seeking) +{ + GstElement *bin, *src1 = NULL, *src2, *ac1, *ac2, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstPad *srcpad; + gint i; + GstStateChangeReturn state_res; + GstStreamConsistency *consist; + /* don't use autoaudiosrc, as then we can't set anything here */ + const gchar *audio_src_factories[] = { + "alsasrc", + "pulseaudiosrc" + }; + + GST_INFO ("preparing test"); + main_loop = NULL; + play_seek_event = NULL; + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + for (i = 0; (i < G_N_ELEMENTS (audio_src_factories) && src1 == NULL); i++) { + src1 = test_live_seeking_try_audiosrc (audio_src_factories[i]); + } + if (!src1) { + /* normal audiosources behave differently than audiotestsrc */ + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, "is-live", TRUE, NULL); /* silence */ + } else { + /* live sources ignore seeks, force eos after 2 sec (4 buffers half second + * each) + */ + g_object_set (src1, "num-buffers", 4, "blocksize", 44100, NULL); + } + + ac1 = gst_element_factory_make ("audioconvert", "ac1"); + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + ac2 = gst_element_factory_make ("audioconvert", "ac2"); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, ac1, src2, ac2, audiomixer, sink, + NULL); + + res = gst_element_link (src1, ac1); + fail_unless (res == TRUE, NULL); + res = gst_element_link (ac1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, ac2); + fail_unless (res == TRUE, NULL); + res = gst_element_link (ac2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", + (GCallback) test_live_seeking_eos_message_received, bin); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + GST_INFO ("starting test"); + + /* run it twice */ + for (i = 0; i < 2; i++) { + + GST_INFO ("starting test-loop %d", i); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("playing"); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + gst_consistency_checker_reset (consist); + } + + /* cleanup */ + GST_INFO ("cleaning up"); + gst_consistency_checker_free (consist); + if (main_loop) + g_main_loop_unref (main_loop); + if (play_seek_event) + gst_event_unref (play_seek_event); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if adding pads work as expected */ +GST_START_TEST (test_add_pad) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstPad *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + /* one buffer less, we connect with 1 buffer of delay */ + g_object_set (src2, "num-buffers", 3, NULL); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* add other element */ + gst_bin_add_many (GST_BIN (bin), src2, NULL); + + /* now link the second element */ + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + + /* set to PAUSED as well */ + state_res = gst_element_set_state (src2, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if removing pads work as expected */ +GST_START_TEST (test_remove_pad) +{ + GstElement *bin, *src, *audiomixer, *sink; + GstBus *bus; + GstPad *pad, *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src = gst_element_factory_make ("audiotestsrc", "src"); + g_object_set (src, "num-buffers", 4, NULL); + g_object_set (src, "wave", 4, NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src, audiomixer, sink, NULL); + + res = gst_element_link (src, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + /* create an unconnected sinkpad in audiomixer */ + pad = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (pad == NULL, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing, this will not preroll as audiomixer is waiting + * on the unconnected sinkpad. */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion for one second, will return ASYNC */ + state_res = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, GST_SECOND); + ck_assert_int_eq (state_res, GST_STATE_CHANGE_ASYNC); + + /* get rid of the pad now, audiomixer should stop waiting on it and + * continue the preroll */ + gst_element_release_request_pad (audiomixer, pad); + gst_object_unref (pad); + + /* wait for completion, should work now */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (G_OBJECT (bus)); + gst_object_unref (G_OBJECT (bin)); +} + +GST_END_TEST; + + +static GstBuffer *handoff_buffer = NULL; +static void +handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + GST_DEBUG ("got buffer %p", buffer); + gst_buffer_replace (&handoff_buffer, buffer); +} + +/* check if clipping works as expected */ +GST_START_TEST (test_clip) +{ + GstSegment segment; + GstElement *bin, *audiomixer, *sink; + GstBus *bus; + GstPad *sinkpad; + gboolean res; + GstStateChangeReturn state_res; + GstFlowReturn ret; + GstEvent *event; + GstBuffer *buffer; + GstCaps *caps; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* just an audiomixer and a fakesink */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); + gst_bin_add_many (GST_BIN (bin), audiomixer, sink, NULL); + + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* create an unconnected sinkpad in audiomixer, should also automatically activate + * the pad */ + sinkpad = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (sinkpad == NULL, NULL); + + gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test")); + + caps = gst_caps_new_simple ("audio/x-raw", +#if G_BYTE_ORDER == G_BIG_ENDIAN + "format", G_TYPE_STRING, "S16BE", +#else + "format", G_TYPE_STRING, "S16LE", +#endif + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, NULL); + + gst_pad_set_caps (sinkpad, caps); + gst_caps_unref (caps); + + /* send segment to audiomixer */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = GST_SECOND; + segment.stop = 2 * GST_SECOND; + segment.time = 0; + event = gst_event_new_segment (&segment); + gst_pad_send_event (sinkpad, event); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 0; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + /* should be partially clipped */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer != NULL); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should not be clipped */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer != NULL); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + gst_element_release_request_pad (audiomixer, sinkpad); + gst_object_unref (sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_is_max) +{ + GstElement *bin, *src[3], *audiomixer, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an audiomixer and a fakesink */ + src[0] = gst_element_factory_make ("audiotestsrc", NULL); + src[1] = gst_element_factory_make ("audiotestsrc", NULL); + src[2] = gst_element_factory_make ("audiotestsrc", NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], audiomixer, sink, + NULL); + + gst_element_link (src[0], audiomixer); + gst_element_link (src[1], audiomixer); + gst_element_link (src[2], audiomixer); + gst_element_link (audiomixer, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = 1000; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, 3000); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_unknown_overrides) +{ + GstElement *bin, *src[3], *audiomixer, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an audiomixer and a fakesink */ + src[0] = gst_element_factory_make ("audiotestsrc", NULL); + src[1] = gst_element_factory_make ("audiotestsrc", NULL); + src[2] = gst_element_factory_make ("audiotestsrc", NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], audiomixer, sink, + NULL); + + gst_element_link (src[0], audiomixer); + gst_element_link (src[1], audiomixer); + gst_element_link (src[2], audiomixer); + gst_element_link (audiomixer, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = GST_CLOCK_TIME_NONE; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, GST_CLOCK_TIME_NONE); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + + +static gboolean looped = FALSE; + +static void +loop_segment_done (GstBus * bus, GstMessage * message, GstElement * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + if (looped) { + g_main_loop_quit (main_loop); + } else { + GstEvent *seek_event; + gboolean res; + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + looped = TRUE; + } +} + +GST_START_TEST (test_loop) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) loop_segment_done, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_flush_start_flush_stop) +{ + GstPadTemplate *sink_template; + GstPad *tmppad, *sinkpad1, *sinkpad2, *audiomixer_src; + GstElement *pipeline, *src1, *src2, *audiomixer, *sink; + + GST_INFO ("preparing test"); + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src1, src2, audiomixer, sink, NULL); + + sink_template = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (audiomixer), + "sink_%u"); + fail_unless (GST_IS_PAD_TEMPLATE (sink_template)); + sinkpad1 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); + tmppad = gst_element_get_static_pad (src1, "src"); + gst_pad_link (tmppad, sinkpad1); + gst_object_unref (tmppad); + + sinkpad2 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); + tmppad = gst_element_get_static_pad (src2, "src"); + gst_pad_link (tmppad, sinkpad2); + gst_object_unref (tmppad); + + gst_element_link (audiomixer, sink); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_unless (gst_element_get_state (pipeline, NULL, NULL, + GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS); + + audiomixer_src = gst_element_get_static_pad (audiomixer, "src"); + fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_start ()); + fail_unless (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE)); + fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_object_unref (audiomixer_src); + + gst_element_release_request_pad (audiomixer, sinkpad1); + gst_object_unref (sinkpad1); + gst_element_release_request_pad (audiomixer, sinkpad2); + gst_object_unref (sinkpad2); + + /* cleanup */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + + +static Suite * +audiomixer_suite (void) +{ + Suite *s = suite_create ("audiomixer"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_caps); + tcase_add_test (tc_chain, test_filter_caps); + tcase_add_test (tc_chain, test_event); + tcase_add_test (tc_chain, test_play_twice); + tcase_add_test (tc_chain, test_play_twice_then_add_and_play_again); + tcase_add_test (tc_chain, test_live_seeking); + tcase_add_test (tc_chain, test_add_pad); + tcase_add_test (tc_chain, test_remove_pad); + tcase_add_test (tc_chain, test_clip); + tcase_add_test (tc_chain, test_duration_is_max); + tcase_add_test (tc_chain, test_duration_unknown_overrides); + tcase_add_test (tc_chain, test_loop); + tcase_add_test (tc_chain, test_flush_start_flush_stop); + + /* Use a longer timeout */ +#ifdef HAVE_VALGRIND + if (RUNNING_ON_VALGRIND) { + tcase_set_timeout (tc_chain, 5 * 60); + } else +#endif + { + /* this is shorter than the default 60 seconds?! (tpm) */ + /* tcase_set_timeout (tc_chain, 6); */ + } + + return s; +} + +GST_CHECK_MAIN (audiomixer); From e368bee1a9187b0d928e204c3e1170ff19c03ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 6 Nov 2013 15:18:58 +0100 Subject: [PATCH 002/153] audiomixer: Add simply synchronization test --- tests/check/elements/audiomixer.c | 201 +++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 1 deletion(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 313cc49a55..e1b3f74374 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -2,7 +2,8 @@ * * unit test for audiomixer * - * Copyright (C) <2005> Thomas Vander Stichele + * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2013 Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -1257,6 +1258,203 @@ GST_START_TEST (test_flush_start_flush_stop) GST_END_TEST; +static void +handoff_buffer_collect_cb (GstElement * fakesink, GstBuffer * buffer, + GstPad * pad, gpointer user_data) +{ + GList **received_buffers = user_data; + + GST_DEBUG ("got buffer %p", buffer); + *received_buffers = + g_list_append (*received_buffers, gst_buffer_ref (buffer)); +} + +GST_START_TEST (test_sync) +{ + GstSegment segment; + GstElement *bin, *audiomixer, *queue1, *queue2, *sink; + GstBus *bus; + GstPad *sinkpad1, *sinkpad2; + GstPad *queue1_sinkpad, *queue2_sinkpad; + GstPad *pad; + gboolean res; + GstStateChangeReturn state_res; + GstFlowReturn ret; + GstEvent *event; + GstBuffer *buffer; + GstCaps *caps; + GList *received_buffers = NULL, *l; + gint i; + GstMapInfo map; + + GST_INFO ("preparing test"); + + main_loop = g_main_loop_new (NULL, FALSE); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* just an audiomixer and a fakesink */ + queue1 = gst_element_factory_make ("queue", "queue1"); + queue2 = gst_element_factory_make ("queue", "queue2"); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + g_object_set (audiomixer, "blocksize", 500, NULL); + sink = gst_element_factory_make ("fakesink", "sink"); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_collect_cb, + &received_buffers); + gst_bin_add_many (GST_BIN (bin), queue1, queue2, audiomixer, sink, NULL); + + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* create an unconnected sinkpad in audiomixer, should also automatically activate + * the pad */ + sinkpad1 = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (sinkpad1 == NULL, NULL); + + queue1_sinkpad = gst_element_get_static_pad (queue1, "sink"); + pad = gst_element_get_static_pad (queue1, "src"); + fail_unless (gst_pad_link (pad, sinkpad1) == GST_PAD_LINK_OK); + gst_object_unref (pad); + + sinkpad2 = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (sinkpad2 == NULL, NULL); + + queue2_sinkpad = gst_element_get_static_pad (queue2, "sink"); + pad = gst_element_get_static_pad (queue2, "src"); + fail_unless (gst_pad_link (pad, sinkpad2) == GST_PAD_LINK_OK); + gst_object_unref (pad); + + gst_pad_send_event (queue1_sinkpad, gst_event_new_stream_start ("test")); + gst_pad_send_event (queue2_sinkpad, gst_event_new_stream_start ("test")); + + caps = gst_caps_new_simple ("audio/x-raw", +#if G_BYTE_ORDER == G_BIG_ENDIAN + "format", G_TYPE_STRING, "S16BE", +#else + "format", G_TYPE_STRING, "S16LE", +#endif + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 1000, "channels", G_TYPE_INT, 1, NULL); + + gst_pad_set_caps (queue1_sinkpad, caps); + gst_pad_set_caps (queue2_sinkpad, caps); + gst_caps_unref (caps); + + /* send segment to audiomixer */ + gst_segment_init (&segment, GST_FORMAT_TIME); + event = gst_event_new_segment (&segment); + gst_pad_send_event (queue1_sinkpad, gst_event_ref (event)); + gst_pad_send_event (queue2_sinkpad, event); + + /* Push buffers */ + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (queue1_sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (queue1_sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (queue1_sinkpad, gst_event_new_eos ()); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (queue2_sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (queue2_sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (queue2_sinkpad, gst_event_new_eos ()); + + /* Collect buffers and messages */ + g_main_loop_run (main_loop); + + /* Here we get once we got EOS, for errors we failed */ + + /* Should have 8 * 0.5s buffers */ + fail_unless_equals_int (g_list_length (received_buffers), 8); + for (i = 0, l = received_buffers; l; l = l->next, i++) { + buffer = l->data; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (i == 0 && GST_BUFFER_TIMESTAMP (buffer) == 0) { + fail_unless (map.data[0] == 0); + } else if (i == 1 && GST_BUFFER_TIMESTAMP (buffer) == 500 * GST_MSECOND) { + fail_unless (map.data[0] == 0); + } else if (i == 2 && GST_BUFFER_TIMESTAMP (buffer) == 1000 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + } else if (i == 3 && GST_BUFFER_TIMESTAMP (buffer) == 1500 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + } else if (i == 4 && GST_BUFFER_TIMESTAMP (buffer) == 2000 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + } else if (i == 5 && GST_BUFFER_TIMESTAMP (buffer) == 2500 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + } else if (i == 6 && GST_BUFFER_TIMESTAMP (buffer) == 3000 * GST_MSECOND) { + fail_unless (map.data[0] == 2); + } else if (i == 7 && GST_BUFFER_TIMESTAMP (buffer) == 3500 * GST_MSECOND) { + fail_unless (map.data[0] == 2); + } else { + g_assert_not_reached (); + } + + gst_buffer_unmap (buffer, &map); + + } + + g_list_free_full (received_buffers, (GDestroyNotify) gst_buffer_unref); + + gst_element_release_request_pad (audiomixer, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (queue1_sinkpad); + gst_element_release_request_pad (audiomixer, sinkpad2); + gst_object_unref (sinkpad2); + gst_object_unref (queue2_sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); + g_main_loop_unref (main_loop); +} + +GST_END_TEST; static Suite * audiomixer_suite (void) @@ -1278,6 +1476,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_duration_unknown_overrides); tcase_add_test (tc_chain, test_loop); tcase_add_test (tc_chain, test_flush_start_flush_stop); + tcase_add_test (tc_chain, test_sync); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 4cf0fc0054c1d2a869856424c3aae07b762a937e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 6 Nov 2013 15:50:08 +0100 Subject: [PATCH 003/153] audiomixer: Don't take channel mask in consideration in mono or stereo This could cause negotiation to fail. https://bugzilla.gnome.org/show_bug.cgi?id=708633 --- gst/audiomixer/gstaudiomixer.c | 38 ++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 6073732dbd..09da1675c5 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -249,6 +249,8 @@ gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) { GstAudioMixer *audiomixer; GstCaps *result, *peercaps, *current_caps, *filter_caps; + GstStructure *s; + gint i, n; audiomixer = GST_AUDIO_MIXER (GST_PAD_PARENT (pad)); @@ -309,6 +311,22 @@ gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) } } + result = gst_caps_make_writable (result); + + n = gst_caps_get_size (result); + for (i = 0; i < n; i++) { + GstStructure *sref; + + s = gst_caps_get_structure (result, i); + sref = gst_structure_copy (s); + gst_structure_set (sref, "channels", GST_TYPE_INT_RANGE, 0, 2, NULL); + if (gst_structure_is_subset (s, sref)) { + /* This field is irrelevant when in mono or stereo */ + gst_structure_remove_field (s, "channel-mask"); + } + gst_structure_free (sref); + } + if (filter_caps) gst_caps_unref (filter_caps); @@ -349,9 +367,19 @@ gst_audiomixer_sink_query (GstCollectPads * pads, GstCollectData * pad, */ static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, - GstCaps * caps) + GstCaps * orig_caps) { + GstCaps *caps; GstAudioInfo info; + GstStructure *s; + gint channels; + + caps = gst_caps_copy (orig_caps); + + s = gst_caps_get_structure (caps, 0); + if (gst_structure_get_int (s, "channels", &channels)) + if (channels <= 2) + gst_structure_remove_field (s, "channel-mask"); if (!gst_audio_info_from_caps (&info, caps)) goto invalid_format; @@ -364,30 +392,36 @@ gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, if (audiomixer->current_caps != NULL) { if (gst_audio_info_is_equal (&info, &audiomixer->info)) { GST_OBJECT_UNLOCK (audiomixer); + gst_caps_unref (caps); return TRUE; } else { GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but " "current caps are %" GST_PTR_FORMAT, caps, audiomixer->current_caps); GST_OBJECT_UNLOCK (audiomixer); gst_pad_push_event (pad, gst_event_new_reconfigure ()); + gst_caps_unref (caps); return FALSE; } } GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps); - audiomixer->current_caps = gst_caps_ref (caps); + gst_caps_replace (&audiomixer->current_caps, caps); memcpy (&audiomixer->info, &info, sizeof (info)); + audiomixer->send_caps = TRUE; GST_OBJECT_UNLOCK (audiomixer); /* send caps event later, after stream-start event */ GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); + gst_caps_unref (caps); + return TRUE; /* ERRORS */ invalid_format: { + gst_caps_unref (caps); GST_WARNING_OBJECT (audiomixer, "invalid format set as caps"); return FALSE; } From b78316dc06e0dedea38e3260cba092ba6f38e9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Nov 2013 13:48:06 +0100 Subject: [PATCH 004/153] audiomixer: Remove some racy-ness from the unit test We might already be EOS when we start the mainloop, so only set the pipeline to PLAYING from the main loop. --- tests/check/elements/audiomixer.c | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index e1b3f74374..989879d00c 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -127,6 +127,17 @@ GST_START_TEST (test_filter_caps) GST_END_TEST; +static gboolean +set_playing (GstElement * element) +{ + GstStateChangeReturn state_res; + + state_res = gst_element_set_state (element, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + return FALSE; +} + static void message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) { @@ -267,8 +278,7 @@ GST_START_TEST (test_event) fail_unless (res == TRUE, NULL); /* run pipeline */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); GST_INFO ("running main loop"); g_main_loop_run (main_loop); @@ -405,8 +415,7 @@ GST_START_TEST (test_play_twice) GST_INFO ("seeked"); /* run pipeline */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); g_main_loop_run (main_loop); @@ -496,8 +505,7 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_INFO ("seeked"); /* run pipeline */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); g_main_loop_run (main_loop); @@ -672,8 +680,7 @@ GST_START_TEST (test_live_seeking) GST_INFO ("seeked"); /* run pipeline */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); GST_INFO ("playing"); @@ -764,8 +771,7 @@ GST_START_TEST (test_add_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* now play all */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); g_main_loop_run (main_loop); @@ -846,8 +852,7 @@ GST_START_TEST (test_remove_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* now play all */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); g_main_loop_run (main_loop); @@ -1183,8 +1188,7 @@ GST_START_TEST (test_loop) fail_unless (res == TRUE, NULL); /* run pipeline */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + g_idle_add ((GSourceFunc) set_playing, bin); GST_INFO ("running main loop"); g_main_loop_run (main_loop); @@ -1314,8 +1318,8 @@ GST_START_TEST (test_sync) res = gst_element_link (audiomixer, sink); fail_unless (res == TRUE, NULL); - /* set to playing */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + /* set to paused */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* create an unconnected sinkpad in audiomixer, should also automatically activate @@ -1403,6 +1407,9 @@ GST_START_TEST (test_sync) gst_pad_send_event (queue2_sinkpad, gst_event_new_eos ()); + /* Set PLAYING */ + g_idle_add ((GSourceFunc) set_playing, bin); + /* Collect buffers and messages */ g_main_loop_run (main_loop); From 73854f8c020082583c7312561bd7e6ae7f10629e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Nov 2013 13:55:32 +0100 Subject: [PATCH 005/153] audiomixer: Refactor sync test to be more reusable --- tests/check/elements/audiomixer.c | 91 ++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 989879d00c..22ff17935a 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1273,7 +1273,12 @@ handoff_buffer_collect_cb (GstElement * fakesink, GstBuffer * buffer, g_list_append (*received_buffers, gst_buffer_ref (buffer)); } -GST_START_TEST (test_sync) +typedef void (*SendBuffersFunction) (GstPad * pad1, GstPad * pad2); +typedef void (*CheckBuffersFunction) (GList * buffers); + +static void +run_sync_test (SendBuffersFunction send_buffers, + CheckBuffersFunction check_buffers) { GstSegment segment; GstElement *bin, *audiomixer, *queue1, *queue2, *sink; @@ -1283,13 +1288,9 @@ GST_START_TEST (test_sync) GstPad *pad; gboolean res; GstStateChangeReturn state_res; - GstFlowReturn ret; GstEvent *event; - GstBuffer *buffer; GstCaps *caps; - GList *received_buffers = NULL, *l; - gint i; - GstMapInfo map; + GList *received_buffers = NULL; GST_INFO ("preparing test"); @@ -1363,6 +1364,40 @@ GST_START_TEST (test_sync) gst_pad_send_event (queue2_sinkpad, event); /* Push buffers */ + send_buffers (queue1_sinkpad, queue2_sinkpad); + + /* Set PLAYING */ + g_idle_add ((GSourceFunc) set_playing, bin); + + /* Collect buffers and messages */ + g_main_loop_run (main_loop); + + /* Here we get once we got EOS, for errors we failed */ + + check_buffers (received_buffers); + + g_list_free_full (received_buffers, (GDestroyNotify) gst_buffer_unref); + + gst_element_release_request_pad (audiomixer, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (queue1_sinkpad); + gst_element_release_request_pad (audiomixer, sinkpad2); + gst_object_unref (sinkpad2); + gst_object_unref (queue2_sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); + g_main_loop_unref (main_loop); +} + +static void +send_buffers_sync (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstMapInfo map; + GstFlowReturn ret; + buffer = gst_buffer_new_and_alloc (2000); gst_buffer_map (buffer, &map, GST_MAP_WRITE); memset (map.data, 1, map.size); @@ -1370,7 +1405,7 @@ GST_START_TEST (test_sync) GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; GST_DEBUG ("pushing buffer %p", buffer); - ret = gst_pad_chain (queue1_sinkpad, buffer); + ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); buffer = gst_buffer_new_and_alloc (2000); @@ -1380,10 +1415,10 @@ GST_START_TEST (test_sync) GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; GST_DEBUG ("pushing buffer %p", buffer); - ret = gst_pad_chain (queue1_sinkpad, buffer); + ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - gst_pad_send_event (queue1_sinkpad, gst_event_new_eos ()); + gst_pad_send_event (pad1, gst_event_new_eos ()); buffer = gst_buffer_new_and_alloc (2000); gst_buffer_map (buffer, &map, GST_MAP_WRITE); @@ -1392,7 +1427,7 @@ GST_START_TEST (test_sync) GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; GST_DEBUG ("pushing buffer %p", buffer); - ret = gst_pad_chain (queue2_sinkpad, buffer); + ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); buffer = gst_buffer_new_and_alloc (2000); @@ -1402,18 +1437,19 @@ GST_START_TEST (test_sync) GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; GST_DEBUG ("pushing buffer %p", buffer); - ret = gst_pad_chain (queue2_sinkpad, buffer); + ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - gst_pad_send_event (queue2_sinkpad, gst_event_new_eos ()); + gst_pad_send_event (pad2, gst_event_new_eos ()); +} - /* Set PLAYING */ - g_idle_add ((GSourceFunc) set_playing, bin); - - /* Collect buffers and messages */ - g_main_loop_run (main_loop); - - /* Here we get once we got EOS, for errors we failed */ +static void +check_buffers_sync (GList * received_buffers) +{ + GstBuffer *buffer; + GList *l; + gint i; + GstMapInfo map; /* Should have 8 * 0.5s buffers */ fail_unless_equals_int (g_list_length (received_buffers), 8); @@ -1445,20 +1481,11 @@ GST_START_TEST (test_sync) gst_buffer_unmap (buffer, &map); } +} - g_list_free_full (received_buffers, (GDestroyNotify) gst_buffer_unref); - - gst_element_release_request_pad (audiomixer, sinkpad1); - gst_object_unref (sinkpad1); - gst_object_unref (queue1_sinkpad); - gst_element_release_request_pad (audiomixer, sinkpad2); - gst_object_unref (sinkpad2); - gst_object_unref (queue2_sinkpad); - gst_element_set_state (bin, GST_STATE_NULL); - gst_bus_remove_signal_watch (bus); - gst_object_unref (bus); - gst_object_unref (bin); - g_main_loop_unref (main_loop); +GST_START_TEST (test_sync) +{ + run_sync_test (send_buffers_sync, check_buffers_sync); } GST_END_TEST; From 6e48dcfe47f694e6dffb88d09e73a7a4382fe3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Nov 2013 13:57:35 +0100 Subject: [PATCH 006/153] audiomixer: Add test for the discont handling --- tests/check/elements/audiomixer.c | 117 ++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 22ff17935a..5f1600ccc3 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1460,20 +1460,28 @@ check_buffers_sync (GList * received_buffers) if (i == 0 && GST_BUFFER_TIMESTAMP (buffer) == 0) { fail_unless (map.data[0] == 0); + fail_unless (map.data[map.size - 1] == 0); } else if (i == 1 && GST_BUFFER_TIMESTAMP (buffer) == 500 * GST_MSECOND) { fail_unless (map.data[0] == 0); + fail_unless (map.data[map.size - 1] == 0); } else if (i == 2 && GST_BUFFER_TIMESTAMP (buffer) == 1000 * GST_MSECOND) { fail_unless (map.data[0] == 1); + fail_unless (map.data[map.size - 1] == 1); } else if (i == 3 && GST_BUFFER_TIMESTAMP (buffer) == 1500 * GST_MSECOND) { fail_unless (map.data[0] == 1); + fail_unless (map.data[map.size - 1] == 1); } else if (i == 4 && GST_BUFFER_TIMESTAMP (buffer) == 2000 * GST_MSECOND) { fail_unless (map.data[0] == 3); + fail_unless (map.data[map.size - 1] == 3); } else if (i == 5 && GST_BUFFER_TIMESTAMP (buffer) == 2500 * GST_MSECOND) { fail_unless (map.data[0] == 3); + fail_unless (map.data[map.size - 1] == 3); } else if (i == 6 && GST_BUFFER_TIMESTAMP (buffer) == 3000 * GST_MSECOND) { fail_unless (map.data[0] == 2); + fail_unless (map.data[map.size - 1] == 2); } else if (i == 7 && GST_BUFFER_TIMESTAMP (buffer) == 3500 * GST_MSECOND) { fail_unless (map.data[0] == 2); + fail_unless (map.data[map.size - 1] == 2); } else { g_assert_not_reached (); } @@ -1490,6 +1498,114 @@ GST_START_TEST (test_sync) GST_END_TEST; +static void +send_buffers_sync_discont (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstMapInfo map; + GstFlowReturn ret; + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad1, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad1, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (pad1, gst_event_new_eos ()); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad2, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad2, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (pad2, gst_event_new_eos ()); +} + +static void +check_buffers_sync_discont (GList * received_buffers) +{ + GstBuffer *buffer; + GList *l; + gint i; + GstMapInfo map; + + /* Should have 8 * 0.5s buffers */ + fail_unless_equals_int (g_list_length (received_buffers), 8); + for (i = 0, l = received_buffers; l; l = l->next, i++) { + buffer = l->data; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (i == 0 && GST_BUFFER_TIMESTAMP (buffer) == 0) { + fail_unless (map.data[0] == 0); + fail_unless (map.data[map.size - 1] == 0); + } else if (i == 1 && GST_BUFFER_TIMESTAMP (buffer) == 500 * GST_MSECOND) { + fail_unless (map.data[0] == 0); + fail_unless (map.data[map.size - 1] == 0); + } else if (i == 2 && GST_BUFFER_TIMESTAMP (buffer) == 1000 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + fail_unless (map.data[map.size - 1] == 1); + } else if (i == 3 && GST_BUFFER_TIMESTAMP (buffer) == 1500 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + fail_unless (map.data[map.size - 1] == 1); + } else if (i == 4 && GST_BUFFER_TIMESTAMP (buffer) == 2000 * GST_MSECOND) { + fail_unless (map.data[0] == 2); + fail_unless (map.data[map.size - 1] == 2); + } else if (i == 5 && GST_BUFFER_TIMESTAMP (buffer) == 2500 * GST_MSECOND) { + fail_unless (map.data[0] == 2); + fail_unless (map.data[map.size - 1] == 2); + } else if (i == 6 && GST_BUFFER_TIMESTAMP (buffer) == 3000 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + fail_unless (map.data[map.size - 1] == 3); + } else if (i == 7 && GST_BUFFER_TIMESTAMP (buffer) == 3500 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + fail_unless (map.data[map.size - 1] == 3); + } else { + g_assert_not_reached (); + } + + gst_buffer_unmap (buffer, &map); + + } +} + +GST_START_TEST (test_sync_discont) +{ + run_sync_test (send_buffers_sync_discont, check_buffers_sync_discont); +} + +GST_END_TEST; + static Suite * audiomixer_suite (void) { @@ -1511,6 +1627,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_loop); tcase_add_test (tc_chain, test_flush_start_flush_stop); tcase_add_test (tc_chain, test_sync); + tcase_add_test (tc_chain, test_sync_discont); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 9fa3fb9434226f91c514b4155218dd7d9b8bc294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Nov 2013 14:12:00 +0100 Subject: [PATCH 007/153] audiomixer: Fix EOS handling if we have some pending data --- gst/audiomixer/gstaudiomixer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 09da1675c5..28f8e513d0 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1614,7 +1614,6 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) gboolean dropped = FALSE; gboolean is_eos = TRUE; gboolean is_done = TRUE; - gboolean handled_buffer = FALSE; audiomixer = GST_AUDIO_MIXER (user_data); @@ -1788,7 +1787,6 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) } else { is_done = FALSE; } - handled_buffer = TRUE; } } @@ -1810,14 +1808,10 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) if (is_eos) { gint64 max_offset = 0; + gboolean empty_buffer = TRUE; GST_DEBUG_OBJECT (audiomixer, "We're EOS"); - /* This means EOS or no pads at all */ - if (!handled_buffer) { - gst_buffer_replace (&audiomixer->current_buffer, NULL); - goto eos; - } for (collected = pads->data; collected; collected = collected->next) { GstCollectData *collect_data; @@ -1827,6 +1821,14 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) adata = (GstAudioMixerCollect *) collect_data; max_offset = MAX (max_offset, adata->output_offset); + if (adata->output_offset > audiomixer->offset) + empty_buffer = FALSE; + } + + /* This means EOS or no pads at all */ + if (empty_buffer) { + gst_buffer_replace (&audiomixer->current_buffer, NULL); + goto eos; } if (max_offset <= next_offset) { From 5fcc53e55260a9a836f1a8efd409b34cb3396bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Nov 2013 14:12:17 +0100 Subject: [PATCH 008/153] audiomixer: Add unit test for handling unaligned buffers That is, buffers that start or end in the middle of an output block. --- tests/check/elements/audiomixer.c | 120 ++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 5f1600ccc3..1d1b091e67 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1606,6 +1606,125 @@ GST_START_TEST (test_sync_discont) GST_END_TEST; +static void +send_buffers_sync_unaligned (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstMapInfo map; + GstFlowReturn ret; + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 750 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad1, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 1, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 1750 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad1, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (pad1, gst_event_new_eos ()); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 1750 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad2, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + buffer = gst_buffer_new_and_alloc (2000); + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, 2, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = 2750 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (pad2, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + + gst_pad_send_event (pad2, gst_event_new_eos ()); +} + +static void +check_buffers_sync_unaligned (GList * received_buffers) +{ + GstBuffer *buffer; + GList *l; + gint i; + GstMapInfo map; + + /* Should have 8 * 0.5s buffers */ + fail_unless_equals_int (g_list_length (received_buffers), 8); + for (i = 0, l = received_buffers; l; l = l->next, i++) { + buffer = l->data; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (i == 0 && GST_BUFFER_TIMESTAMP (buffer) == 0) { + fail_unless (map.data[0] == 0); + fail_unless (map.data[map.size - 1] == 0); + } else if (i == 1 && GST_BUFFER_TIMESTAMP (buffer) == 500 * GST_MSECOND) { + fail_unless (map.data[0] == 0); + fail_unless (map.data[499] == 0); + fail_unless (map.data[500] == 1); + fail_unless (map.data[map.size - 1] == 1); + } else if (i == 2 && GST_BUFFER_TIMESTAMP (buffer) == 1000 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + fail_unless (map.data[map.size - 1] == 1); + } else if (i == 3 && GST_BUFFER_TIMESTAMP (buffer) == 1500 * GST_MSECOND) { + fail_unless (map.data[0] == 1); + fail_unless (map.data[499] == 1); + fail_unless (map.data[500] == 3); + fail_unless (map.data[map.size - 1] == 3); + } else if (i == 4 && GST_BUFFER_TIMESTAMP (buffer) == 2000 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + fail_unless (map.data[499] == 3); + fail_unless (map.data[500] == 3); + fail_unless (map.data[map.size - 1] == 3); + } else if (i == 5 && GST_BUFFER_TIMESTAMP (buffer) == 2500 * GST_MSECOND) { + fail_unless (map.data[0] == 3); + fail_unless (map.data[499] == 3); + fail_unless (map.data[500] == 2); + fail_unless (map.data[map.size - 1] == 2); + } else if (i == 6 && GST_BUFFER_TIMESTAMP (buffer) == 3000 * GST_MSECOND) { + fail_unless (map.data[0] == 2); + fail_unless (map.data[499] == 2); + fail_unless (map.data[500] == 2); + fail_unless (map.data[map.size - 1] == 2); + } else if (i == 7 && GST_BUFFER_TIMESTAMP (buffer) == 3500 * GST_MSECOND) { + fail_unless (map.size == 500); + fail_unless (GST_BUFFER_DURATION (buffer) == 250 * GST_MSECOND); + fail_unless (map.data[0] == 2); + fail_unless (map.data[499] == 2); + } else { + g_assert_not_reached (); + } + + gst_buffer_unmap (buffer, &map); + + } +} + +GST_START_TEST (test_sync_unaligned) +{ + run_sync_test (send_buffers_sync_unaligned, check_buffers_sync_unaligned); +} + +GST_END_TEST; + static Suite * audiomixer_suite (void) { @@ -1628,6 +1747,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_flush_start_flush_stop); tcase_add_test (tc_chain, test_sync); tcase_add_test (tc_chain, test_sync_discont); + tcase_add_test (tc_chain, test_sync_unaligned); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From a1f95f562513fa5366ecc5e3c4adf497fb54ea72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 19 Dec 2013 21:59:09 +0100 Subject: [PATCH 009/153] audiomixer: Also resync timestamps on the RESYNC flag --- gst/audiomixer/gstaudiomixer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 28f8e513d0..2605cc3705 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1289,7 +1289,9 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); end_offset = start_offset + adata->size / bpf; - if (GST_BUFFER_IS_DISCONT (inbuf) || adata->next_offset == -1) { + if (GST_BUFFER_IS_DISCONT (inbuf) + || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) + || adata->next_offset == -1) { discont = TRUE; } else { guint64 diff, max_sample_diff; From c29e04674a2b5fffd9c29e9e6950693345030ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 15 Jan 2014 23:30:28 +0100 Subject: [PATCH 010/153] audiomixer: Fix and simplify overlap calculation --- gst/audiomixer/gstaudiomixer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 2605cc3705..5929851064 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1424,11 +1424,9 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, else out_start = 0; - if (audiomixer->offset + audiomixer->blocksize + adata->position / bpf < - adata->output_offset + adata->size / bpf + out_start) + overlap = adata->size / bpf - adata->position / bpf; + if (overlap > audiomixer->blocksize - out_start) overlap = audiomixer->blocksize - out_start; - else - overlap = adata->size / bpf - adata->position / bpf; inbuf = gst_collect_pads_peek (pads, collect_data); g_assert (inbuf != NULL && inbuf == adata->buffer); @@ -1441,6 +1439,8 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, adata->output_offset += adata->size / bpf; if (adata->position >= adata->size) { /* Buffer done, drop it */ + adata->position = 0; + adata->size = 0; gst_buffer_replace (&adata->buffer, NULL); gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); } From ee15c8fd1f3c8c0d085b0e6d1145cf1447bc34e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 16 Jan 2014 12:18:45 +0100 Subject: [PATCH 011/153] audiomixer: Remove some useless assignments and skip the current part of the buffer if the pad is muted --- gst/audiomixer/gstaudiomixer.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 5929851064..f604ce7433 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1435,12 +1435,10 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, if (pad->mute || pad->volume < G_MINDOUBLE) { GST_DEBUG_OBJECT (pad, "Skipping muted pad"); gst_buffer_unref (inbuf); - adata->position += adata->size; - adata->output_offset += adata->size / bpf; + adata->position += overlap * bpf; + adata->output_offset += overlap; if (adata->position >= adata->size) { /* Buffer done, drop it */ - adata->position = 0; - adata->size = 0; gst_buffer_replace (&adata->buffer, NULL); gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); } @@ -1452,7 +1450,6 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); gst_buffer_unref (inbuf); - adata->position += adata->size; adata->output_offset += adata->size / bpf; /* Buffer done, drop it */ gst_buffer_replace (&adata->buffer, NULL); From ec17e33f649ccbfc2d882cbe5b158e53332a8e25 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 11 Mar 2014 22:36:01 +0100 Subject: [PATCH 012/153] audiomixer: set a group-id on the stream-start event Set a default group-id to fix a warning printed by the sink. --- gst/audiomixer/gstaudiomixer.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index f604ce7433..cf3f707ac3 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1635,12 +1635,17 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) if (audiomixer->send_stream_start) { gchar s_id[32]; + GstEvent *event; GST_INFO_OBJECT (audiomixer->srcpad, "send pending stream start event"); - /* stream-start (FIXME: create id based on input ids) */ + /* FIXME: create id based on input ids, we can't use + * gst_pad_create_stream_id() though as that only handles 0..1 sink-pad + */ g_snprintf (s_id, sizeof (s_id), "audiomixer-%08x", g_random_int ()); - if (!gst_pad_push_event (audiomixer->srcpad, - gst_event_new_stream_start (s_id))) { + event = gst_event_new_stream_start (s_id); + gst_event_set_group_id (event, gst_util_group_id_next ()); + + if (!gst_pad_push_event (audiomixer->srcpad, event)) { GST_WARNING_OBJECT (audiomixer->srcpad, "Sending stream start event failed"); } From 2ddbaaf8662abbd23ed6998054925c57ab7eea8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 3 May 2014 18:16:21 +0200 Subject: [PATCH 013/153] Release 1.3.1 --- gst/audiomixer/gstaudiomixerorc-dist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixerorc-dist.c b/gst/audiomixer/gstaudiomixerorc-dist.c index 1a54e14e24..092a464beb 100644 --- a/gst/audiomixer/gstaudiomixerorc-dist.c +++ b/gst/audiomixer/gstaudiomixerorc-dist.c @@ -153,8 +153,8 @@ void audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, #define ORC_CLAMP_UW(x) ORC_CLAMP(x,ORC_UW_MIN,ORC_UW_MAX) #define ORC_CLAMP_SL(x) ORC_CLAMP(x,ORC_SL_MIN,ORC_SL_MAX) #define ORC_CLAMP_UL(x) ORC_CLAMP(x,ORC_UL_MIN,ORC_UL_MAX) -#define ORC_SWAP_W(x) ((((x)&0xff)<<8) | (((x)&0xff00)>>8)) -#define ORC_SWAP_L(x) ((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>24)) +#define ORC_SWAP_W(x) ((((x)&0xffU)<<8) | (((x)&0xff00U)>>8)) +#define ORC_SWAP_L(x) ((((x)&0xffU)<<24) | (((x)&0xff00U)<<8) | (((x)&0xff0000U)>>8) | (((x)&0xff000000U)>>24)) #define ORC_SWAP_Q(x) ((((x)&ORC_UINT64_C(0xff))<<56) | (((x)&ORC_UINT64_C(0xff00))<<40) | (((x)&ORC_UINT64_C(0xff0000))<<24) | (((x)&ORC_UINT64_C(0xff000000))<<8) | (((x)&ORC_UINT64_C(0xff00000000))>>8) | (((x)&ORC_UINT64_C(0xff0000000000))>>24) | (((x)&ORC_UINT64_C(0xff000000000000))>>40) | (((x)&ORC_UINT64_C(0xff00000000000000))>>56)) #define ORC_PTR_OFFSET(ptr,offset) ((void *)(((unsigned char *)(ptr)) + (offset))) #define ORC_DENORMAL(x) ((x) & ((((x)&0x7f800000) == 0) ? 0xff800000 : 0xffffffff)) From 183f4b3227b8f87afa91152d72a877a49f6aa67a Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 28 May 2014 16:29:37 +0200 Subject: [PATCH 014/153] audiomixer: Port to GstAggregator https://bugzilla.gnome.org/show_bug.cgi?id=737183 Co-Authored by: Mathieu Duponchelle --- gst/audiomixer/Makefile.am | 6 +- gst/audiomixer/gstaudiomixer.c | 958 ++++++++++-------------------- gst/audiomixer/gstaudiomixer.h | 40 +- tests/check/elements/audiomixer.c | 75 ++- 4 files changed, 406 insertions(+), 673 deletions(-) diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index 90328bc925..372c006869 100644 --- a/gst/audiomixer/Makefile.am +++ b/gst/audiomixer/Makefile.am @@ -6,11 +6,15 @@ include $(top_srcdir)/common/orc.mak libgstaudiomixer_la_SOURCES = gstaudiomixer.c nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) -libgstaudiomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) +libgstaudiomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) $(ORC_CFLAGS) \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstaudiomixer_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ -lgstaudio-@GST_API_VERSION@ \ + $(top_builddir)/gst-libs/gst/base/libgstbadbase-$(GST_API_VERSION).la \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index cf3f707ac3..d95b91e0f1 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -51,24 +51,6 @@ #define GST_CAT_DEFAULT gst_audiomixer_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); -typedef struct _GstAudioMixerCollect GstAudioMixerCollect; -struct _GstAudioMixerCollect -{ - GstCollectData collect; /* we extend the CollectData */ - - GstBuffer *buffer; /* current buffer we're mixing, - for comparison with collect.buffer - to see if we need to update our - cached values. */ - guint position, size; - - guint64 output_offset; /* Offset in output segment that - collect.pos refers to in the - current buffer. */ - - guint64 next_offset; /* Next expected offset in the input segment */ -}; - #define DEFAULT_PAD_VOLUME (1.0) #define DEFAULT_PAD_MUTE (FALSE) @@ -92,7 +74,7 @@ enum PROP_PAD_MUTE }; -G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, GST_TYPE_PAD); +G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, GST_TYPE_AGGREGATOR_PAD); static void gst_audiomixer_pad_get_property (GObject * object, guint prop_id, @@ -139,10 +121,26 @@ gst_audiomixer_pad_set_property (GObject * object, guint prop_id, } } +static gboolean +gst_audiomixer_pad_flush_pad (GstAggregatorPad * aggpad, + GstAggregator * aggregator) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); + + GST_OBJECT_LOCK (aggpad); + pad->position = pad->size = 0; + pad->output_offset = pad->next_offset = -1; + gst_buffer_replace (&pad->buffer, NULL); + GST_OBJECT_UNLOCK (aggpad); + + return TRUE; +} + static void gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass; gobject_class->set_property = gst_audiomixer_pad_set_property; gobject_class->get_property = gst_audiomixer_pad_get_property; @@ -155,6 +153,8 @@ gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) g_param_spec_boolean ("mute", "Mute", "Mute this pad", DEFAULT_PAD_MUTE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + aggpadclass->flush = GST_DEBUG_FUNCPTR (gst_audiomixer_pad_flush_pad); } static void @@ -162,6 +162,13 @@ gst_audiomixer_pad_init (GstAudioMixerPad * pad) { pad->volume = DEFAULT_PAD_VOLUME; pad->mute = DEFAULT_PAD_MUTE; + + pad->buffer = NULL; + pad->position = 0; + pad->size = 0; + pad->output_offset = -1; + pad->next_offset = -1; + } #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) @@ -207,7 +214,7 @@ static void gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data); #define gst_audiomixer_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_ELEMENT, +G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, gst_audiomixer_child_proxy_init)); @@ -219,27 +226,14 @@ static void gst_audiomixer_get_property (GObject * object, guint prop_id, static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, GstCaps * caps); -static gboolean gst_audiomixer_src_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static gboolean gst_audiomixer_sink_query (GstCollectPads * pads, - GstCollectData * pad, GstQuery * query, gpointer user_data); -static gboolean gst_audiomixer_src_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_audiomixer_sink_event (GstCollectPads * pads, - GstCollectData * pad, GstEvent * event, gpointer user_data); - static GstPad *gst_audiomixer_request_new_pad (GstElement * element, - GstPadTemplate * temp, const gchar * unused, const GstCaps * caps); + GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); -static GstStateChangeReturn gst_audiomixer_change_state (GstElement * element, - GstStateChange transition); - -static GstFlowReturn gst_audiomixer_do_clip (GstCollectPads * pads, - GstCollectData * data, GstBuffer * buffer, GstBuffer ** out, - gpointer user_data); -static GstFlowReturn gst_audiomixer_collected (GstCollectPads * pads, - gpointer user_data); +static GstFlowReturn +gst_audiomixer_do_clip (GstAggregator * agg, + GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); +static GstFlowReturn gst_audiomixer_aggregate (GstAggregator * agg); /* we can only accept caps that we and downstream can handle. * if we have filtercaps set, use those to constrain the target caps. @@ -247,12 +241,14 @@ static GstFlowReturn gst_audiomixer_collected (GstCollectPads * pads, static GstCaps * gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) { + GstAggregator *agg; GstAudioMixer *audiomixer; GstCaps *result, *peercaps, *current_caps, *filter_caps; GstStructure *s; gint i, n; audiomixer = GST_AUDIO_MIXER (GST_PAD_PARENT (pad)); + agg = GST_AGGREGATOR (audiomixer); GST_OBJECT_LOCK (audiomixer); /* take filter */ @@ -274,7 +270,7 @@ gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) } /* get the downstream possible caps */ - peercaps = gst_pad_peer_query_caps (audiomixer->srcpad, filter_caps); + peercaps = gst_pad_peer_query_caps (agg->srcpad, filter_caps); /* get the allowed caps on this sinkpad */ GST_OBJECT_LOCK (audiomixer); @@ -337,8 +333,8 @@ gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) } static gboolean -gst_audiomixer_sink_query (GstCollectPads * pads, GstCollectData * pad, - GstQuery * query, gpointer user_data) +gst_audiomixer_sink_query (GstAggregator * agg, GstAggregatorPad * aggpad, + GstQuery * query) { gboolean res = FALSE; @@ -348,14 +344,15 @@ gst_audiomixer_sink_query (GstCollectPads * pads, GstCollectData * pad, GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); - caps = gst_audiomixer_sink_getcaps (pad->pad, filter); + caps = gst_audiomixer_sink_getcaps (GST_PAD (aggpad), filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; break; } default: - res = gst_collect_pads_query_default (pads, pad, query, FALSE); + res = + GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, aggpad, query); break; } @@ -600,9 +597,9 @@ gst_audiomixer_query_latency (GstAudioMixer * audiomixer, GstQuery * query) } static gboolean -gst_audiomixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) +gst_audiomixer_src_query (GstAggregator * agg, GstQuery * query) { - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (parent); + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { @@ -615,7 +612,7 @@ gst_audiomixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) switch (format) { case GST_FORMAT_TIME: /* FIXME, bring to stream time, might be tricky */ - gst_query_set_position (query, format, audiomixer->segment.position); + gst_query_set_position (query, format, agg->segment.position); res = TRUE; break; case GST_FORMAT_DEFAULT: @@ -636,7 +633,8 @@ gst_audiomixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) default: /* FIXME, needs a custom query handler because we have multiple * sinkpads */ - res = gst_pad_query_default (pad, parent, query); + res = gst_pad_query_default (GST_PAD (agg->srcpad), GST_OBJECT (agg), + query); break; } @@ -651,105 +649,25 @@ typedef struct gboolean flush; } EventData; -/* FIXME: What is this supposed to solve? */ static gboolean -forward_event_func (const GValue * val, GValue * ret, EventData * data) +gst_audiomixer_src_event (GstAggregator * agg, GstEvent * event) { - GstPad *pad = g_value_get_object (val); - GstEvent *event = data->event; - GstPad *peer; - - gst_event_ref (event); - GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); - peer = gst_pad_get_peer (pad); - /* collect pad might have been set flushing, - * so bypass core checking that and send directly to peer */ - if (!peer || !gst_pad_send_event (peer, event)) { - if (!peer) - gst_event_unref (event); - GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", - event, GST_EVENT_TYPE_NAME (event)); - /* quick hack to unflush the pads, ideally we need a way to just unflush - * this single collect pad */ - if (data->flush) - gst_pad_send_event (pad, gst_event_new_flush_stop (TRUE)); - } else { - g_value_set_boolean (ret, TRUE); - GST_LOG_OBJECT (pad, "Sent event %p (%s).", - event, GST_EVENT_TYPE_NAME (event)); - } - if (peer) - gst_object_unref (peer); - - /* continue on other pads, even if one failed */ - return TRUE; -} - -/* forwards the event to all sinkpads, takes ownership of the - * event - * - * Returns: TRUE if the event could be forwarded on all - * sinkpads. - */ -static gboolean -forward_event (GstAudioMixer * audiomixer, GstEvent * event, gboolean flush) -{ - gboolean ret; - GstIterator *it; - GstIteratorResult ires; - GValue vret = { 0 }; - EventData data; - - GST_LOG_OBJECT (audiomixer, "Forwarding event %p (%s)", event, - GST_EVENT_TYPE_NAME (event)); - - data.event = event; - data.flush = flush; - - g_value_init (&vret, G_TYPE_BOOLEAN); - g_value_set_boolean (&vret, FALSE); - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); - while (TRUE) { - ires = - gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, - &vret, &data); - switch (ires) { - case GST_ITERATOR_RESYNC: - GST_WARNING ("resync"); - gst_iterator_resync (it); - g_value_set_boolean (&vret, TRUE); - break; - case GST_ITERATOR_OK: - case GST_ITERATOR_DONE: - ret = g_value_get_boolean (&vret); - goto done; - default: - ret = FALSE; - goto done; - } - } -done: - gst_iterator_free (it); - GST_LOG_OBJECT (audiomixer, "Forwarded event %p (%s), ret=%d", event, - GST_EVENT_TYPE_NAME (event), ret); - gst_event_unref (event); - - return ret; -} - -static gboolean -gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstAudioMixer *audiomixer; gboolean result; - audiomixer = GST_AUDIO_MIXER (parent); - - GST_DEBUG_OBJECT (pad, "Got %s event on src pad", + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); + GST_DEBUG_OBJECT (agg->srcpad, "Got %s event on src pad", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { - /* TODO: Update from videomixer */ + case GST_EVENT_QOS: + /* QoS might be tricky */ + gst_event_unref (event); + return FALSE; + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + gst_event_unref (event); + return FALSE; + break; case GST_EVENT_SEEK: { GstSeekFlags flags; @@ -757,12 +675,12 @@ gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) GstSeekType start_type, stop_type; gint64 start, stop; GstFormat seek_format, dest_format; - gboolean flush; /* parse the seek parameters */ gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, &start, &stop_type, &stop); + /* Check the seeking parametters before linking up */ if ((start_type != GST_SEEK_TYPE_NONE) && (start_type != GST_SEEK_TYPE_SET)) { result = FALSE; @@ -777,7 +695,7 @@ gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) goto done; } - dest_format = audiomixer->segment.format; + dest_format = agg->segment.format; if (seek_format != dest_format) { result = FALSE; GST_DEBUG_OBJECT (audiomixer, @@ -785,107 +703,30 @@ gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) goto done; } - flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + /* Link up */ + result = GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); - /* check if we are flushing */ - if (flush) { - /* flushing seek, start flush downstream, the flush will be done - * when all pads received a FLUSH_STOP. - * Make sure we accept nothing anymore and return WRONG_STATE. - * We send a flush-start before, to ensure no streaming is done - * as we need to take the stream lock. - */ - gst_pad_push_event (audiomixer->srcpad, gst_event_new_flush_start ()); - gst_collect_pads_set_flushing (audiomixer->collect, TRUE); - - /* We can't send FLUSH_STOP here since upstream could start pushing data - * after we unlock audiomixer->collect. - * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after - * forwarding the seek upstream or from gst_audiomixer_collected, - * whichever happens first. - */ - GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); - audiomixer->flush_stop_pending = TRUE; - GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); - GST_DEBUG_OBJECT (audiomixer, "mark pending flush stop event"); - } - GST_DEBUG_OBJECT (audiomixer, "handling seek event: %" GST_PTR_FORMAT, - event); - - /* now wait for the collected to be finished and mark a new - * segment. After we have the lock, no collect function is running and no - * new collect function will be called for as long as we're flushing. */ - GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); - /* clip position and update our segment */ - if (audiomixer->segment.stop != -1) { - audiomixer->segment.position = audiomixer->segment.stop; - } - gst_segment_do_seek (&audiomixer->segment, rate, seek_format, flags, - start_type, start, stop_type, stop, NULL); - - if (flush) { - /* Yes, we need to call _set_flushing again *WHEN* the streaming threads - * have stopped so that the cookie gets properly updated. */ - gst_collect_pads_set_flushing (audiomixer->collect, TRUE); - } - GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); - GST_DEBUG_OBJECT (audiomixer, "forwarding seek event: %" GST_PTR_FORMAT, - event); - GST_DEBUG_OBJECT (audiomixer, "updated segment: %" GST_SEGMENT_FORMAT, - &audiomixer->segment); - - /* we're forwarding seek to all upstream peers and wait for one to reply - * with a newsegment-event before we send a newsegment-event downstream */ - g_atomic_int_set (&audiomixer->segment_pending, TRUE); - result = forward_event (audiomixer, event, flush); - /* FIXME: We should use the seek segment and forward that downstream next time - * not any upstream segment event */ - if (!result) { - /* seek failed. maybe source is a live source. */ - GST_DEBUG_OBJECT (audiomixer, "seeking failed"); - } - if (g_atomic_int_compare_and_exchange (&audiomixer->flush_stop_pending, - TRUE, FALSE)) { - GST_DEBUG_OBJECT (audiomixer, "pending flush stop"); - if (!gst_pad_push_event (audiomixer->srcpad, - gst_event_new_flush_stop (TRUE))) { - GST_WARNING_OBJECT (audiomixer, "Sending flush stop event failed"); - } - } - break; + goto done; } - case GST_EVENT_QOS: - /* QoS might be tricky */ - result = FALSE; - gst_event_unref (event); - break; - case GST_EVENT_NAVIGATION: - /* navigation is rather pointless. */ - result = FALSE; - gst_event_unref (event); break; default: - /* just forward the rest for now */ - GST_DEBUG_OBJECT (audiomixer, "forward unhandled event: %s", - GST_EVENT_TYPE_NAME (event)); - result = forward_event (audiomixer, event, FALSE); break; } -done: + return GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); +done: return result; } static gboolean -gst_audiomixer_sink_event (GstCollectPads * pads, GstCollectData * pad, - GstEvent * event, gpointer user_data) +gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, + GstEvent * event) { - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); - GstAudioMixerCollect *adata = (GstAudioMixerCollect *) pad; - gboolean res = TRUE, discard = FALSE; + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); + gboolean res = TRUE; - GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad", + GST_DEBUG_OBJECT (aggpad, "Got %s event on sink pad", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { @@ -894,90 +735,100 @@ gst_audiomixer_sink_event (GstCollectPads * pads, GstCollectData * pad, GstCaps *caps; gst_event_parse_caps (event, &caps); - res = gst_audiomixer_setcaps (audiomixer, pad->pad, caps); + res = gst_audiomixer_setcaps (audiomixer, GST_PAD_CAST (aggpad), caps); gst_event_unref (event); event = NULL; break; } - /* FIXME: Who cares about flushes from upstream? We should - * not forward them at all */ - case GST_EVENT_FLUSH_START: - /* ensure that we will send a flush stop */ - GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); - audiomixer->flush_stop_pending = TRUE; - res = gst_collect_pads_event_default (pads, pad, event, discard); - event = NULL; - GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); - break; - case GST_EVENT_FLUSH_STOP: - /* we received a flush-stop. We will only forward it when - * flush_stop_pending is set, and we will unset it then. - */ - g_atomic_int_set (&audiomixer->segment_pending, TRUE); - GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); - if (audiomixer->flush_stop_pending) { - GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop"); - res = gst_collect_pads_event_default (pads, pad, event, discard); - audiomixer->flush_stop_pending = FALSE; - event = NULL; - gst_buffer_replace (&audiomixer->current_buffer, NULL); - audiomixer->discont_time = GST_CLOCK_TIME_NONE; - } else { - discard = TRUE; - GST_DEBUG_OBJECT (pad->pad, "eating flush stop"); - } - GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); - /* Clear pending tags */ - if (audiomixer->pending_events) { - g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, - NULL); - g_list_free (audiomixer->pending_events); - audiomixer->pending_events = NULL; - } - adata->position = adata->size = 0; - adata->output_offset = adata->next_offset = -1; - gst_buffer_replace (&adata->buffer, NULL); - break; - case GST_EVENT_TAG: - /* collect tags here so we can push them out when we collect data */ - audiomixer->pending_events = - g_list_append (audiomixer->pending_events, event); - event = NULL; - break; - case GST_EVENT_SEGMENT:{ + case GST_EVENT_SEGMENT: + { const GstSegment *segment; gst_event_parse_segment (event, &segment); - if (segment->rate != audiomixer->segment.rate) { - GST_ERROR_OBJECT (pad->pad, + if (segment->rate != agg->segment.rate) { + GST_ERROR_OBJECT (aggpad, "Got segment event with wrong rate %lf, expected %lf", - segment->rate, audiomixer->segment.rate); + segment->rate, agg->segment.rate); res = FALSE; gst_event_unref (event); event = NULL; } else if (segment->rate < 0.0) { - GST_ERROR_OBJECT (pad->pad, "Negative rates not supported yet"); + GST_ERROR_OBJECT (aggpad, "Negative rates not supported yet"); res = FALSE; gst_event_unref (event); event = NULL; } - discard = TRUE; + + if (event) { + res = + GST_AGGREGATOR_CLASS (parent_class)->sink_event (agg, aggpad, + event); + + if (res) + aggpad->segment.position = segment->start + segment->offset; + + event = NULL; + } break; } default: break; } - if (G_LIKELY (event)) - return gst_collect_pads_event_default (pads, pad, event, discard); - else - return res; + if (event != NULL) + return GST_AGGREGATOR_CLASS (parent_class)->sink_event (agg, aggpad, event); + + return res; } +static void +gst_audiomixer_reset (GstAudioMixer *audiomixer) +{ + audiomixer->offset = 0; + gst_caps_replace (&audiomixer->current_caps, NULL); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; +} + +static gboolean +gst_audiomixer_start (GstAggregator * agg) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); + + if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg)) + return FALSE; + + gst_audiomixer_reset (audiomixer); + + return TRUE; +} + +static gboolean +gst_audiomixer_stop (GstAggregator * agg) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); + + if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) + return FALSE; + + gst_audiomixer_reset (audiomixer); + + return TRUE; +} + +static GstFlowReturn +gst_audiomixer_flush (GstAggregator * agg) +{ + gst_audiomixer_reset (GST_AUDIO_MIXER (agg)); + + return GST_FLOW_OK; +} + + static void gst_audiomixer_class_init (GstAudioMixerClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; GstElementClass *gstelement_class = (GstElementClass *) klass; + GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; gobject_class->set_property = gst_audiomixer_set_property; gobject_class->get_property = gst_audiomixer_get_property; @@ -1021,45 +872,33 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_audiomixer_change_state); + + agg_class->sinkpads_type = GST_TYPE_AUDIO_MIXER_PAD; + agg_class->start = gst_audiomixer_start; + agg_class->stop = gst_audiomixer_stop; + + agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); + agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); + + agg_class->aggregate = GST_DEBUG_FUNCPTR (gst_audiomixer_aggregate); + agg_class->clip = GST_DEBUG_FUNCPTR (gst_audiomixer_do_clip); + + agg_class->src_event = GST_DEBUG_FUNCPTR (gst_audiomixer_src_event); + agg_class->src_query = GST_DEBUG_FUNCPTR (gst_audiomixer_src_query); + + agg_class->flush = GST_DEBUG_FUNCPTR (gst_audiomixer_flush); } static void gst_audiomixer_init (GstAudioMixer * audiomixer) { - GstPadTemplate *template; - - template = gst_static_pad_template_get (&gst_audiomixer_src_template); - audiomixer->srcpad = gst_pad_new_from_template (template, "src"); - gst_object_unref (template); - - gst_pad_set_query_function (audiomixer->srcpad, - GST_DEBUG_FUNCPTR (gst_audiomixer_src_query)); - gst_pad_set_event_function (audiomixer->srcpad, - GST_DEBUG_FUNCPTR (gst_audiomixer_src_event)); - GST_PAD_SET_PROXY_CAPS (audiomixer->srcpad); - gst_element_add_pad (GST_ELEMENT (audiomixer), audiomixer->srcpad); - audiomixer->current_caps = NULL; gst_audio_info_init (&audiomixer->info); - audiomixer->padcount = 0; audiomixer->filter_caps = NULL; audiomixer->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; audiomixer->blocksize = DEFAULT_BLOCKSIZE; - - /* keep track of the sinkpads requested */ - audiomixer->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (audiomixer->collect, - GST_DEBUG_FUNCPTR (gst_audiomixer_collected), audiomixer); - gst_collect_pads_set_clip_function (audiomixer->collect, - GST_DEBUG_FUNCPTR (gst_audiomixer_do_clip), audiomixer); - gst_collect_pads_set_event_function (audiomixer->collect, - GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event), audiomixer); - gst_collect_pads_set_query_function (audiomixer->collect, - GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query), audiomixer); } static void @@ -1067,19 +906,9 @@ gst_audiomixer_dispose (GObject * object) { GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); - if (audiomixer->collect) { - gst_object_unref (audiomixer->collect); - audiomixer->collect = NULL; - } gst_caps_replace (&audiomixer->filter_caps, NULL); gst_caps_replace (&audiomixer->current_caps, NULL); - if (audiomixer->pending_events) { - g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, NULL); - g_list_free (audiomixer->pending_events); - audiomixer->pending_events = NULL; - } - G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1153,69 +982,27 @@ gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, } } -static void -free_pad (GstCollectData * data) -{ - GstAudioMixerCollect *adata = (GstAudioMixerCollect *) data; - - gst_buffer_replace (&adata->buffer, NULL); -} - static GstPad * gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * unused, const GstCaps * caps) + const gchar * req_name, const GstCaps * caps) { - gchar *name; - GstAudioMixer *audiomixer; - GstPad *newpad; - gint padcount; - GstCollectData *cdata; - GstAudioMixerCollect *adata; + GstAudioMixerPad *newpad; - if (templ->direction != GST_PAD_SINK) - goto not_sink; + newpad = (GstAudioMixerPad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, req_name, caps); - audiomixer = GST_AUDIO_MIXER (element); + if (newpad == NULL) + goto could_not_create; - /* increment pad counter */ - padcount = g_atomic_int_add (&audiomixer->padcount, 1); - - name = g_strdup_printf ("sink_%u", padcount); - newpad = g_object_new (GST_TYPE_AUDIO_MIXER_PAD, "name", name, "direction", - templ->direction, "template", templ, NULL); - GST_DEBUG_OBJECT (audiomixer, "request new pad %s", name); - g_free (name); - - cdata = - gst_collect_pads_add_pad (audiomixer->collect, newpad, - sizeof (GstAudioMixerCollect), free_pad, TRUE); - adata = (GstAudioMixerCollect *) cdata; - adata->buffer = NULL; - adata->position = 0; - adata->size = 0; - adata->output_offset = -1; - adata->next_offset = -1; - - /* takes ownership of the pad */ - if (!gst_element_add_pad (GST_ELEMENT (audiomixer), newpad)) - goto could_not_add; - - gst_child_proxy_child_added (GST_CHILD_PROXY (audiomixer), G_OBJECT (newpad), + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), GST_OBJECT_NAME (newpad)); - return newpad; + return GST_PAD_CAST (newpad); - /* errors */ -not_sink: +could_not_create: { - g_warning ("gstaudiomixer: request new pad that is not a SINK pad\n"); - return NULL; - } -could_not_add: - { - GST_DEBUG_OBJECT (audiomixer, "could not add pad"); - gst_collect_pads_remove_pad (audiomixer->collect, newpad); - gst_object_unref (newpad); + GST_DEBUG_OBJECT (element, "could not create/add pad"); return NULL; } } @@ -1231,30 +1018,28 @@ gst_audiomixer_release_pad (GstElement * element, GstPad * pad) gst_child_proxy_child_removed (GST_CHILD_PROXY (audiomixer), G_OBJECT (pad), GST_OBJECT_NAME (pad)); - if (audiomixer->collect) - gst_collect_pads_remove_pad (audiomixer->collect, pad); - gst_element_remove_pad (element, pad); + + GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); } static GstFlowReturn -gst_audiomixer_do_clip (GstCollectPads * pads, GstCollectData * data, - GstBuffer * buffer, GstBuffer ** out, gpointer user_data) +gst_audiomixer_do_clip (GstAggregator * agg, + GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** out) { - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); gint rate, bpf; rate = GST_AUDIO_INFO_RATE (&audiomixer->info); bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf); + buffer = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); *out = buffer; return GST_FLOW_OK; } static gboolean -gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, - GstCollectData * collect_data, GstAudioMixerCollect * adata, +gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstBuffer * inbuf) { GstClockTime start_time, end_time; @@ -1263,44 +1048,46 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, GstClockTime timestamp, stream_time; gint rate, bpf; - g_assert (adata->buffer == NULL); + GstAggregator *agg = GST_AGGREGATOR (audiomixer); + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); + + g_assert (pad->buffer == NULL); rate = GST_AUDIO_INFO_RATE (&audiomixer->info); bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); timestamp = GST_BUFFER_TIMESTAMP (inbuf); - stream_time = - gst_segment_to_stream_time (&collect_data->segment, GST_FORMAT_TIME, + stream_time = gst_segment_to_stream_time (&agg->segment, GST_FORMAT_TIME, timestamp); /* sync object properties on stream time */ /* TODO: Ideally we would want to do that on every sample */ if (GST_CLOCK_TIME_IS_VALID (stream_time)) - gst_object_sync_values (GST_OBJECT (collect_data->pad), stream_time); + gst_object_sync_values (GST_OBJECT (pad), stream_time); - adata->position = 0; - adata->size = gst_buffer_get_size (inbuf); + pad->position = 0; + pad->size = gst_buffer_get_size (inbuf); - start_time = GST_BUFFER_TIMESTAMP (inbuf); + start_time = GST_BUFFER_PTS (inbuf); end_time = - start_time + gst_util_uint64_scale_ceil (adata->size / bpf, + start_time + gst_util_uint64_scale_ceil (pad->size / bpf, GST_SECOND, rate); start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); - end_offset = start_offset + adata->size / bpf; + end_offset = start_offset + pad->size / bpf; if (GST_BUFFER_IS_DISCONT (inbuf) || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) - || adata->next_offset == -1) { + || pad->next_offset == -1) { discont = TRUE; } else { guint64 diff, max_sample_diff; /* Check discont, based on audiobasesink */ - if (start_offset <= adata->next_offset) - diff = adata->next_offset - start_offset; + if (start_offset <= pad->next_offset) + diff = pad->next_offset - start_offset; else - diff = start_offset - adata->next_offset; + diff = start_offset - pad->next_offset; max_sample_diff = gst_util_uint64_scale_int (audiomixer->alignment_threshold, rate, @@ -1327,28 +1114,28 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, if (discont) { /* Have discont, need resync */ - if (adata->next_offset != -1) - GST_INFO_OBJECT (collect_data->pad, "Have discont. Expected %" + if (pad->next_offset != -1) + GST_INFO_OBJECT (pad, "Have discont. Expected %" G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, - adata->next_offset, start_offset); - adata->output_offset = -1; + pad->next_offset, start_offset); + pad->output_offset = -1; } else { audiomixer->discont_time = GST_CLOCK_TIME_NONE; } - adata->next_offset = end_offset; + pad->next_offset = end_offset; - if (adata->output_offset == -1) { + if (pad->output_offset == -1) { GstClockTime start_running_time; GstClockTime end_running_time; guint64 start_running_time_offset; guint64 end_running_time_offset; start_running_time = - gst_segment_to_running_time (&collect_data->segment, + gst_segment_to_running_time (&aggpad->segment, GST_FORMAT_TIME, start_time); end_running_time = - gst_segment_to_running_time (&collect_data->segment, + gst_segment_to_running_time (&aggpad->segment, GST_FORMAT_TIME, end_time); start_running_time_offset = gst_util_uint64_scale (start_running_time, rate, GST_SECOND); @@ -1356,33 +1143,40 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, gst_util_uint64_scale (end_running_time, rate, GST_SECOND); if (end_running_time_offset < audiomixer->offset) { + GstBuffer *buf; + /* Before output segment, drop */ gst_buffer_unref (inbuf); - adata->buffer = NULL; - gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); - adata->position = 0; - adata->size = 0; - adata->output_offset = -1; - GST_DEBUG_OBJECT (collect_data->pad, + pad->buffer = NULL; + buf = gst_aggregator_pad_steal_buffer (aggpad); + if (buf) + gst_buffer_unref (buf); + pad->position = 0; + pad->size = 0; + pad->output_offset = -1; + GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); return FALSE; } if (start_running_time_offset < audiomixer->offset) { + GstBuffer *buf; guint diff = (audiomixer->offset - start_running_time_offset) * bpf; - adata->position += diff; - adata->size -= diff; + pad->position += diff; + pad->size -= diff; /* FIXME: This could only happen due to rounding errors */ - if (adata->size == 0) { + if (pad->size == 0) { /* Empty buffer, drop */ gst_buffer_unref (inbuf); - adata->buffer = NULL; - gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); - adata->position = 0; - adata->size = 0; - adata->output_offset = -1; - GST_DEBUG_OBJECT (collect_data->pad, + pad->buffer = NULL; + buf = gst_aggregator_pad_steal_buffer (aggpad); + if (buf) + gst_buffer_unref (buf); + pad->position = 0; + pad->size = 0; + pad->output_offset = -1; + GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); @@ -1390,118 +1184,125 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, } } - adata->output_offset = MAX (start_running_time_offset, audiomixer->offset); - GST_DEBUG_OBJECT (collect_data->pad, + pad->output_offset = MAX (start_running_time_offset, audiomixer->offset); + GST_DEBUG_OBJECT (pad, "Buffer resynced: Pad offset %" G_GUINT64_FORMAT - ", current mixer offset %" G_GUINT64_FORMAT, adata->output_offset, + ", current mixer offset %" G_GUINT64_FORMAT, pad->output_offset, audiomixer->offset); } - GST_LOG_OBJECT (collect_data->pad, - "Queued new buffer at offset %" G_GUINT64_FORMAT, adata->output_offset); - adata->buffer = inbuf; + GST_LOG_OBJECT (pad, + "Queued new buffer at offset %" G_GUINT64_FORMAT, pad->output_offset); + pad->buffer = inbuf; return TRUE; } static void -gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, - GstCollectData * collect_data, GstAudioMixerCollect * adata, +gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstMapInfo * outmap) { - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (adata->collect.pad); guint overlap; guint out_start; GstBuffer *inbuf; GstMapInfo inmap; gint bpf; + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); /* Overlap => mix */ - if (audiomixer->offset < adata->output_offset) - out_start = adata->output_offset - audiomixer->offset; + if (audiomixer->offset < pad->output_offset) + out_start = pad->output_offset - audiomixer->offset; else out_start = 0; - overlap = adata->size / bpf - adata->position / bpf; + overlap = pad->size / bpf - pad->position / bpf; if (overlap > audiomixer->blocksize - out_start) overlap = audiomixer->blocksize - out_start; - inbuf = gst_collect_pads_peek (pads, collect_data); - g_assert (inbuf != NULL && inbuf == adata->buffer); + inbuf = gst_aggregator_pad_get_buffer (aggpad); + if (inbuf == NULL) + return; GST_OBJECT_LOCK (pad); if (pad->mute || pad->volume < G_MINDOUBLE) { GST_DEBUG_OBJECT (pad, "Skipping muted pad"); gst_buffer_unref (inbuf); - adata->position += overlap * bpf; - adata->output_offset += overlap; - if (adata->position >= adata->size) { + pad->position += overlap * bpf; + pad->output_offset += overlap; + if (pad->position >= pad->size) { + GstBuffer *buf; /* Buffer done, drop it */ - gst_buffer_replace (&adata->buffer, NULL); - gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + gst_buffer_replace (&pad->buffer, NULL); + buf = gst_aggregator_pad_steal_buffer (aggpad); + if (buf) + gst_buffer_unref (buf); } GST_OBJECT_UNLOCK (pad); return; } if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer (aggpad); + /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); gst_buffer_unref (inbuf); - adata->output_offset += adata->size / bpf; + pad->output_offset += pad->size / bpf; /* Buffer done, drop it */ - gst_buffer_replace (&adata->buffer, NULL); - gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + gst_buffer_replace (&pad->buffer, NULL); + if (aggpadbuf) + gst_buffer_unref (aggpadbuf); GST_OBJECT_UNLOCK (pad); return; } gst_buffer_map (inbuf, &inmap, GST_MAP_READ); GST_LOG_OBJECT (pad, "mixing %u bytes at offset %u from offset %u", - overlap * bpf, out_start * bpf, adata->position); + overlap * bpf, out_start * bpf, pad->position); /* further buffers, need to add them */ if (pad->volume == 1.0) { switch (audiomixer->info.finfo->format) { case GST_AUDIO_FORMAT_U8: audiomixer_orc_add_u8 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S8: audiomixer_orc_add_s8 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_U16: audiomixer_orc_add_u16 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S16: audiomixer_orc_add_s16 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_U32: audiomixer_orc_add_u32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S32: audiomixer_orc_add_s32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_F32: audiomixer_orc_add_f32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_F64: audiomixer_orc_add_f64 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + adata->position), + (gpointer) (inmap.data + pad->position), overlap * audiomixer->info.channels); break; default: @@ -1512,42 +1313,42 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, switch (audiomixer->info.finfo->format) { case GST_AUDIO_FORMAT_U8: audiomixer_orc_add_volume_u8 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i8, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S8: audiomixer_orc_add_volume_s8 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i8, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_U16: audiomixer_orc_add_volume_u16 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i16, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S16: audiomixer_orc_add_volume_s16 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i16, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_U32: audiomixer_orc_add_volume_u32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i32, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_S32: audiomixer_orc_add_volume_s32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume_i32, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_F32: audiomixer_orc_add_volume_f32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume, overlap * audiomixer->info.channels); break; case GST_AUDIO_FORMAT_F64: audiomixer_orc_add_volume_f64 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + adata->position), + out_start * bpf), (gpointer) (inmap.data + pad->position), pad->volume, overlap * audiomixer->info.channels); break; default: @@ -1558,13 +1359,17 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, gst_buffer_unmap (inbuf, &inmap); gst_buffer_unref (inbuf); - adata->position += overlap * bpf; - adata->output_offset += overlap; + pad->position += overlap * bpf; + pad->output_offset += overlap; + + if (pad->position == pad->size) { + GstBuffer *buf; - if (adata->position == adata->size) { /* Buffer done, drop it */ - gst_buffer_replace (&adata->buffer, NULL); - gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + gst_buffer_replace (&pad->buffer, NULL); + buf = gst_aggregator_pad_steal_buffer (aggpad); + if (buf) + gst_buffer_unref (buf); GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); } @@ -1572,7 +1377,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, } static GstFlowReturn -gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) +gst_audiomixer_aggregate (GstAggregator * agg) { /* Get all pads that have data for us and store them in a * new list. @@ -1603,7 +1408,7 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) * output offset/offset_end. */ GstAudioMixer *audiomixer; - GSList *collected; + GList *iter; GstFlowReturn ret; GstBuffer *outbuf = NULL; GstMapInfo outmap; @@ -1614,112 +1419,34 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) gboolean is_eos = TRUE; gboolean is_done = TRUE; - audiomixer = GST_AUDIO_MIXER (user_data); + audiomixer = GST_AUDIO_MIXER (agg); /* this is fatal */ if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) goto not_negotiated; - if (audiomixer->flush_stop_pending == TRUE) { - GST_INFO_OBJECT (audiomixer->srcpad, "send pending flush stop event"); - if (!gst_pad_push_event (audiomixer->srcpad, - gst_event_new_flush_stop (TRUE))) { - GST_WARNING_OBJECT (audiomixer->srcpad, - "Sending flush stop event failed"); - } - - audiomixer->flush_stop_pending = FALSE; - gst_buffer_replace (&audiomixer->current_buffer, NULL); - audiomixer->discont_time = GST_CLOCK_TIME_NONE; - } - - if (audiomixer->send_stream_start) { - gchar s_id[32]; - GstEvent *event; - - GST_INFO_OBJECT (audiomixer->srcpad, "send pending stream start event"); - /* FIXME: create id based on input ids, we can't use - * gst_pad_create_stream_id() though as that only handles 0..1 sink-pad - */ - g_snprintf (s_id, sizeof (s_id), "audiomixer-%08x", g_random_int ()); - event = gst_event_new_stream_start (s_id); - gst_event_set_group_id (event, gst_util_group_id_next ()); - - if (!gst_pad_push_event (audiomixer->srcpad, event)) { - GST_WARNING_OBJECT (audiomixer->srcpad, - "Sending stream start event failed"); - } - audiomixer->send_stream_start = FALSE; - } - if (audiomixer->send_caps) { - GstEvent *caps_event; + gst_aggregator_set_src_caps (agg, audiomixer->current_caps); + + if (agg->segment.rate > 0.0) + agg->segment.position = agg->segment.start; + else + agg->segment.position = agg->segment.stop; + + audiomixer->offset = gst_util_uint64_scale (agg->segment.position, + GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); - caps_event = gst_event_new_caps (audiomixer->current_caps); - GST_INFO_OBJECT (audiomixer->srcpad, - "send pending caps event %" GST_PTR_FORMAT, caps_event); - if (!gst_pad_push_event (audiomixer->srcpad, caps_event)) { - GST_WARNING_OBJECT (audiomixer->srcpad, "Sending caps event failed"); - } audiomixer->send_caps = FALSE; } rate = GST_AUDIO_INFO_RATE (&audiomixer->info); bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - if (g_atomic_int_compare_and_exchange (&audiomixer->segment_pending, TRUE, - FALSE)) { - GstEvent *event; - - /* - * When seeking we set the start and stop positions as given in the seek - * event. We also adjust offset & timestamp accordingly. - * This basically ignores all newsegments sent by upstream. - * - * FIXME: We require that all inputs have the same rate currently - * as we do no rate conversion! - */ - event = gst_event_new_segment (&audiomixer->segment); - if (audiomixer->segment.rate > 0.0) { - audiomixer->segment.position = audiomixer->segment.start; - } else { - audiomixer->segment.position = audiomixer->segment.stop; - } - audiomixer->offset = gst_util_uint64_scale (audiomixer->segment.position, - rate, GST_SECOND); - - GST_INFO_OBJECT (audiomixer->srcpad, "sending pending new segment event %" - GST_SEGMENT_FORMAT, &audiomixer->segment); - if (event) { - if (!gst_pad_push_event (audiomixer->srcpad, event)) { - GST_WARNING_OBJECT (audiomixer->srcpad, - "Sending new segment event failed"); - } - } else { - GST_WARNING_OBJECT (audiomixer->srcpad, "Creating new segment event for " - "start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT " failed", - audiomixer->segment.start, audiomixer->segment.stop); - } - } - - if (G_UNLIKELY (audiomixer->pending_events)) { - GList *tmp = audiomixer->pending_events; - - while (tmp) { - GstEvent *ev = (GstEvent *) tmp->data; - - gst_pad_push_event (audiomixer->srcpad, ev); - tmp = g_list_next (tmp); - } - g_list_free (audiomixer->pending_events); - audiomixer->pending_events = NULL; - } - /* for the next timestamp, use the sample counter, which will * never accumulate rounding errors */ /* FIXME: Reverse mixing does not work at all yet */ - if (audiomixer->segment.rate > 0.0) { + if (agg->segment.rate > 0.0) { next_offset = audiomixer->offset + audiomixer->blocksize; } else { next_offset = audiomixer->offset - audiomixer->blocksize; @@ -1738,30 +1465,28 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) audiomixer->current_buffer = outbuf; } - GST_LOG_OBJECT (audiomixer, + GST_LOG_OBJECT (agg, "Starting to mix %u samples for offset %" G_GUINT64_FORMAT " with timestamp %" GST_TIME_FORMAT, audiomixer->blocksize, - audiomixer->offset, GST_TIME_ARGS (audiomixer->segment.position)); + audiomixer->offset, GST_TIME_ARGS (agg->segment.position)); gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); - for (collected = pads->data; collected; collected = collected->next) { - GstCollectData *collect_data; - GstAudioMixerCollect *adata; + GST_OBJECT_LOCK (agg); + for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { GstBuffer *inbuf; + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (iter->data); - collect_data = (GstCollectData *) collected->data; - adata = (GstAudioMixerCollect *) collect_data; - inbuf = gst_collect_pads_peek (pads, collect_data); + inbuf = gst_aggregator_pad_get_buffer (aggpad); if (!inbuf) continue; /* New buffer? */ - if (!adata->buffer || adata->buffer != inbuf) { + if (!pad->buffer || pad->buffer != inbuf) { /* Takes ownership of buffer */ - if (!gst_audio_mixer_fill_buffer (audiomixer, pads, collect_data, adata, - inbuf)) { + if (!gst_audio_mixer_fill_buffer (audiomixer, pad, inbuf)) { dropped = TRUE; continue; } @@ -1769,43 +1494,41 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) gst_buffer_unref (inbuf); } - if (!adata->buffer && !dropped - && GST_COLLECT_PADS_STATE_IS_SET (&adata->collect, - GST_COLLECT_PADS_STATE_EOS)) { - GST_DEBUG_OBJECT (collect_data->pad, "Pad is in EOS state"); + if (!pad->buffer && !dropped && GST_AGGREGATOR_PAD (pad)->eos) { + GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); } else { is_eos = FALSE; } /* At this point adata->output_offset >= audiomixer->offset or we have no buffer anymore */ - if (adata->output_offset >= audiomixer->offset - && adata->output_offset < - audiomixer->offset + audiomixer->blocksize && adata->buffer) { - GST_LOG_OBJECT (collect_data->pad, "Mixing buffer for current offset"); - gst_audio_mixer_mix_buffer (audiomixer, pads, collect_data, adata, - &outmap); - if (adata->output_offset >= next_offset) { - GST_DEBUG_OBJECT (collect_data->pad, + if (pad->output_offset >= audiomixer->offset + && pad->output_offset < + audiomixer->offset + audiomixer->blocksize && pad->buffer) { + GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); + gst_audio_mixer_mix_buffer (audiomixer, pad, &outmap); + if (pad->output_offset >= next_offset) { + GST_DEBUG_OBJECT (pad, "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" - G_GUINT64_FORMAT, adata->output_offset, next_offset); + G_GUINT64_FORMAT, pad->output_offset, next_offset); } else { is_done = FALSE; } } } + GST_OBJECT_UNLOCK (agg); gst_buffer_unmap (outbuf, &outmap); if (dropped) { /* We dropped a buffer, retry */ - GST_DEBUG_OBJECT (audiomixer, + GST_INFO_OBJECT (audiomixer, "A pad dropped a buffer, wait for the next one"); return GST_FLOW_OK; } if (!is_done && !is_eos) { /* Get more buffers */ - GST_DEBUG_OBJECT (audiomixer, + GST_INFO_OBJECT (audiomixer, "We're not done yet for the current offset," " waiting for more data"); return GST_FLOW_OK; } @@ -1817,17 +1540,15 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) GST_DEBUG_OBJECT (audiomixer, "We're EOS"); - for (collected = pads->data; collected; collected = collected->next) { - GstCollectData *collect_data; - GstAudioMixerCollect *adata; + GST_OBJECT_LOCK (agg); + for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); - collect_data = (GstCollectData *) collected->data; - adata = (GstAudioMixerCollect *) collect_data; - - max_offset = MAX (max_offset, adata->output_offset); - if (adata->output_offset > audiomixer->offset) + max_offset = MAX ((gint64) max_offset, (gint64) pad->output_offset); + if (pad->output_offset > audiomixer->offset) empty_buffer = FALSE; } + GST_OBJECT_UNLOCK (agg); /* This means EOS or no pads at all */ if (empty_buffer) { @@ -1847,22 +1568,20 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) } /* set timestamps on the output buffer */ - if (audiomixer->segment.rate > 0.0) { - GST_BUFFER_TIMESTAMP (outbuf) = audiomixer->segment.position; + if (agg->segment.rate > 0.0) { + GST_BUFFER_TIMESTAMP (outbuf) = agg->segment.position; GST_BUFFER_OFFSET (outbuf) = audiomixer->offset; GST_BUFFER_OFFSET_END (outbuf) = next_offset; - GST_BUFFER_DURATION (outbuf) = - next_timestamp - audiomixer->segment.position; + GST_BUFFER_DURATION (outbuf) = next_timestamp - agg->segment.position; } else { GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp; GST_BUFFER_OFFSET (outbuf) = next_offset; GST_BUFFER_OFFSET_END (outbuf) = audiomixer->offset; - GST_BUFFER_DURATION (outbuf) = - audiomixer->segment.position - next_timestamp; + GST_BUFFER_DURATION (outbuf) = agg->segment.position - next_timestamp; } audiomixer->offset = next_offset; - audiomixer->segment.position = next_timestamp; + agg->segment.position = next_timestamp; /* send it out */ GST_LOG_OBJECT (audiomixer, @@ -1870,7 +1589,7 @@ gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_BUFFER_OFFSET (outbuf)); - ret = gst_pad_push (audiomixer->srcpad, outbuf); + ret = gst_aggregator_finish_buffer (agg, audiomixer->current_buffer); audiomixer->current_buffer = NULL; GST_LOG_OBJECT (audiomixer, "pushed outbuf, result = %s", @@ -1891,57 +1610,10 @@ not_negotiated: eos: { GST_DEBUG_OBJECT (audiomixer, "EOS"); - gst_pad_push_event (audiomixer->srcpad, gst_event_new_eos ()); return GST_FLOW_EOS; } } -static GstStateChangeReturn -gst_audiomixer_change_state (GstElement * element, GstStateChange transition) -{ - GstAudioMixer *audiomixer; - GstStateChangeReturn ret; - - audiomixer = GST_AUDIO_MIXER (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - audiomixer->offset = 0; - audiomixer->flush_stop_pending = FALSE; - audiomixer->segment_pending = TRUE; - audiomixer->send_stream_start = TRUE; - audiomixer->send_caps = TRUE; - gst_caps_replace (&audiomixer->current_caps, NULL); - gst_segment_init (&audiomixer->segment, GST_FORMAT_TIME); - gst_collect_pads_start (audiomixer->collect); - audiomixer->discont_time = GST_CLOCK_TIME_NONE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* need to unblock the collectpads before calling the - * parent change_state so that streaming can finish */ - gst_collect_pads_stop (audiomixer->collect); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_buffer_replace (&audiomixer->current_buffer, NULL); - break; - default: - break; - } - - return ret; -} - /* GstChildProxy implementation */ static GObject * gst_audiomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 40a25c94eb..6433ea1a4c 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -25,7 +25,7 @@ #define __GST_AUDIO_MIXER_H__ #include -#include +#include #include G_BEGIN_DECLS @@ -49,12 +49,7 @@ typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; * The audiomixer object structure. */ struct _GstAudioMixer { - GstElement element; - - GstPad *srcpad; - GstCollectPads *collect; - /* pad counter, used for creating unique request pads */ - gint padcount; + GstAggregator element; /* the next are valid for both int and float */ GstAudioInfo info; @@ -64,13 +59,9 @@ struct _GstAudioMixer { /* Buffer starting at offset containing block_size samples */ GstBuffer *current_buffer; - /* sink event handling */ - GstSegment segment; - volatile gboolean segment_pending; - volatile gboolean flush_stop_pending; - /* current caps */ GstCaps *current_caps; + gboolean send_caps; /* target caps (set via property) */ GstCaps *filter_caps; @@ -83,16 +74,10 @@ struct _GstAudioMixer { /* Size in samples that is output per buffer */ guint blocksize; - - /* Pending inline events */ - GList *pending_events; - - gboolean send_stream_start; - gboolean send_caps; }; struct _GstAudioMixerClass { - GstElementClass parent_class; + GstAggregatorClass parent_class; }; GType gst_audiomixer_get_type (void); @@ -105,17 +90,30 @@ GType gst_audiomixer_get_type (void); #define GST_AUDIO_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) struct _GstAudioMixerPad { - GstPad parent; + GstAggregatorPad parent; gdouble volume; gint volume_i32; gint volume_i16; gint volume_i8; gboolean mute; + + /* < private > */ + GstBuffer *buffer; /* current buffer we're mixing, + for comparison with collect.buffer + to see if we need to update our + cached values. */ + guint position, size; + + guint64 output_offset; /* Offset in output segment that + collect.pos refers to in the + current buffer. */ + + guint64 next_offset; /* Next expected offset in the input segment */ }; struct _GstAudioMixerPadClass { - GstPadClass parent_class; + GstAggregatorPadClass parent_class; }; GType gst_audiomixer_pad_get_type (void); diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 1d1b091e67..5d4c614199 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -870,12 +870,35 @@ GST_END_TEST; static GstBuffer *handoff_buffer = NULL; +static gboolean +_quit (GMainLoop * ml) +{ + g_main_loop_quit (ml); + + return G_SOURCE_REMOVE; +} + static void handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, - gpointer user_data) + GstClockTime * wanted_end) { - GST_DEBUG ("got buffer %p", buffer); + GST_DEBUG ("got buffer -- SIZE: %ld -- %p DURATION is %" GST_TIME_FORMAT + " -- WANTED END %" GST_TIME_FORMAT, gst_buffer_get_size (buffer), buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer)), + GST_TIME_ARGS (*wanted_end)); + gst_buffer_replace (&handoff_buffer, buffer); + + + /* Buffers we push in will be 'cut' into different smaller buffers, + * we make sure that the last chunck was pushes before we concider the buffer + * we pushed as being done */ + if (main_loop && *wanted_end + && *wanted_end <= + GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer)) { + *wanted_end = 0; + g_idle_add ((GSourceFunc) _quit, main_loop); + } } /* check if clipping works as expected */ @@ -891,9 +914,14 @@ GST_START_TEST (test_clip) GstEvent *event; GstBuffer *buffer; GstCaps *caps; + GMainLoop *local_mainloop; + GstClockTime wanted_end = 0; GST_INFO ("preparing test"); + local_mainloop = g_main_loop_new (NULL, FALSE); + main_loop = NULL; + /* build pipeline */ bin = gst_pipeline_new ("pipeline"); bus = gst_element_get_bus (bin); @@ -907,7 +935,8 @@ GST_START_TEST (test_clip) audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); sink = gst_element_factory_make ("fakesink", "sink"); g_object_set (sink, "signal-handoffs", TRUE, NULL); - g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, + &wanted_end); gst_bin_add_many (GST_BIN (bin), audiomixer, sink, NULL); res = gst_element_link (audiomixer, sink); @@ -948,8 +977,19 @@ GST_START_TEST (test_clip) buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 0; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_DEBUG ("pushing buffer %p", buffer); + GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, + buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); + main_loop = local_mainloop; + + /* The aggregation is done in a dedicated thread, so we can't + * not know when it is actually going to happen, so we just add\ + * a 100 ms timeout to be able to then check that the aggregation + * did happen as we do not have much other choice. + */ + g_timeout_add (100, (GSourceFunc) _quit, main_loop); + g_main_loop_run (main_loop); ck_assert_int_eq (ret, GST_FLOW_OK); fail_unless (handoff_buffer == NULL); @@ -957,9 +997,16 @@ GST_START_TEST (test_clip) buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_DEBUG ("pushing buffer %p", buffer); + + wanted_end = 135 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p START %" GST_TIME_FORMAT " -- DURATION is %" + GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); + + main_loop = local_mainloop; ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); + g_main_loop_run (main_loop); fail_unless (handoff_buffer != NULL); gst_buffer_replace (&handoff_buffer, NULL); @@ -967,18 +1014,28 @@ GST_START_TEST (test_clip) buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_DEBUG ("pushing buffer %p", buffer); + + wanted_end = 390 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, + buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); + g_main_loop_run (main_loop); fail_unless (handoff_buffer != NULL); gst_buffer_replace (&handoff_buffer, NULL); + fail_unless (handoff_buffer == NULL); /* should be clipped and ok */ buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_DEBUG ("pushing buffer %p", buffer); + GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, + buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); + g_timeout_add (100, (GSourceFunc) _quit, main_loop); + g_main_loop_run (main_loop); ck_assert_int_eq (ret, GST_FLOW_OK); fail_unless (handoff_buffer == NULL); @@ -1245,9 +1302,11 @@ GST_START_TEST (test_flush_start_flush_stop) audiomixer_src = gst_element_get_static_pad (audiomixer, "src"); fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); gst_pad_send_event (sinkpad1, gst_event_new_flush_start ()); - fail_unless (GST_PAD_IS_FLUSHING (audiomixer_src)); + fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + fail_unless (GST_PAD_IS_FLUSHING (sinkpad1)); gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE)); fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + fail_if (GST_PAD_IS_FLUSHING (sinkpad1)); gst_object_unref (audiomixer_src); gst_element_release_request_pad (audiomixer, sinkpad1); From c158e019c18fdf51c821aae5348a00ae0dbb1bc6 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Mon, 21 Jul 2014 11:07:19 +0200 Subject: [PATCH 015/153] audiomixer: Set the sinkpad segments basetime after seeking Otherwise stream offset and running time comparison will not be correct, leading to segfaults after seeks --- gst/audiomixer/gstaudiomixer.c | 3 +++ gst/audiomixer/gstaudiomixer.h | 1 + 2 files changed, 4 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index d95b91e0f1..ef0b21c904 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -706,6 +706,8 @@ gst_audiomixer_src_event (GstAggregator * agg, GstEvent * event) /* Link up */ result = GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); + if (result) + audiomixer->base_time = agg->segment.start; goto done; } break; @@ -1131,6 +1133,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, guint64 start_running_time_offset; guint64 end_running_time_offset; + aggpad->segment.base = audiomixer->base_time; start_running_time = gst_segment_to_running_time (&aggpad->segment, GST_FORMAT_TIME, start_time); diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 6433ea1a4c..f5db18ffeb 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -71,6 +71,7 @@ struct _GstAudioMixer { /* Last time we noticed a discont */ GstClockTime discont_time; + gint64 base_time; /* Size in samples that is output per buffer */ guint blocksize; From 22da31f42a554da6701fecffe164f79183678276 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Mon, 6 Oct 2014 13:09:00 +0200 Subject: [PATCH 016/153] audiomixer: Handle seek event in READY state --- gst/audiomixer/gstaudiomixer.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index ef0b21c904..77b8637d3e 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -824,6 +824,23 @@ gst_audiomixer_flush (GstAggregator * agg) return GST_FLOW_OK; } +static gboolean +gst_audiomixer_send_event (GstElement * element, GstEvent * event) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (element); + + gboolean res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); + + GST_STATE_LOCK (element); + if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK && + GST_STATE (element) < GST_STATE_PAUSED) { + audiomixer->base_time = GST_AGGREGATOR (element)->segment.start; + } + GST_STATE_UNLOCK (element); + + return res; +} + static void gst_audiomixer_class_init (GstAudioMixerClass * klass) @@ -874,6 +891,8 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); + gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_audiomixer_send_event); + agg_class->sinkpads_type = GST_TYPE_AUDIO_MIXER_PAD; agg_class->start = gst_audiomixer_start; From 57c8272c75629a3c5bd1d810d036df4ffc7000ae Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 6 Oct 2014 21:46:24 +1100 Subject: [PATCH 017/153] aggregator: add latency query handling --- gst/audiomixer/gstaudiomixer.c | 91 ++-------------------------------- 1 file changed, 4 insertions(+), 87 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 77b8637d3e..4fd8d99e2d 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -511,91 +511,6 @@ gst_audiomixer_query_duration (GstAudioMixer * audiomixer, GstQuery * query) return res; } -static gboolean -gst_audiomixer_query_latency (GstAudioMixer * audiomixer, GstQuery * query) -{ - GstClockTime min, max; - gboolean live; - gboolean res; - GstIterator *it; - gboolean done; - GValue item = { 0, }; - - res = TRUE; - done = FALSE; - - live = FALSE; - min = 0; - max = GST_CLOCK_TIME_NONE; - - /* Take maximum of all latency values */ - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); - while (!done) { - GstIteratorResult ires; - - ires = gst_iterator_next (it, &item); - switch (ires) { - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_OK: - { - GstPad *pad = g_value_get_object (&item); - GstQuery *peerquery; - GstClockTime min_cur, max_cur; - gboolean live_cur; - - peerquery = gst_query_new_latency (); - - /* Ask peer for latency */ - res &= gst_pad_peer_query (pad, peerquery); - - /* take max from all valid return values */ - if (res) { - gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); - - if (min_cur > min) - min = min_cur; - - if (max_cur != GST_CLOCK_TIME_NONE && - ((max != GST_CLOCK_TIME_NONE && max_cur > max) || - (max == GST_CLOCK_TIME_NONE))) - max = max_cur; - - live = live || live_cur; - } - - gst_query_unref (peerquery); - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - live = FALSE; - min = 0; - max = GST_CLOCK_TIME_NONE; - res = TRUE; - gst_iterator_resync (it); - break; - default: - res = FALSE; - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (it); - - if (res) { - /* store the results */ - GST_DEBUG_OBJECT (audiomixer, "Calculated total latency: live %s, min %" - GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, - (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); - gst_query_set_latency (query, live, min, max); - } - - return res; -} - static gboolean gst_audiomixer_src_query (GstAggregator * agg, GstQuery * query) { @@ -628,7 +543,9 @@ gst_audiomixer_src_query (GstAggregator * agg, GstQuery * query) res = gst_audiomixer_query_duration (audiomixer, query); break; case GST_QUERY_LATENCY: - res = gst_audiomixer_query_latency (audiomixer, query); + res = + GST_AGGREGATOR_CLASS (gst_audiomixer_parent_class)->src_query + (agg, query); break; default: /* FIXME, needs a custom query handler because we have multiple @@ -783,7 +700,7 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, } static void -gst_audiomixer_reset (GstAudioMixer *audiomixer) +gst_audiomixer_reset (GstAudioMixer * audiomixer) { audiomixer->offset = 0; gst_caps_replace (&audiomixer->current_caps, NULL); From c5224553c45c7ac589084e8cb1cce97d73cf3077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Zanelli?= Date: Thu, 9 Oct 2014 14:51:56 +0200 Subject: [PATCH 018/153] test: use G_GSIZE_FORMAT in audiomixer test https://bugzilla.gnome.org/show_bug.cgi?id=738227 --- tests/check/elements/audiomixer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 5d4c614199..9afca2c14f 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -882,8 +882,9 @@ static void handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, GstClockTime * wanted_end) { - GST_DEBUG ("got buffer -- SIZE: %ld -- %p DURATION is %" GST_TIME_FORMAT - " -- WANTED END %" GST_TIME_FORMAT, gst_buffer_get_size (buffer), buffer, + GST_DEBUG ("got buffer -- SIZE: %" G_GSIZE_FORMAT + " -- %p DURATION is %" GST_TIME_FORMAT " -- WANTED END %" GST_TIME_FORMAT, + gst_buffer_get_size (buffer), buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer)), GST_TIME_ARGS (*wanted_end)); From d38e242a724b5cf8f818a328d26227ce80b54d69 Mon Sep 17 00:00:00 2001 From: Vineeth T M Date: Mon, 20 Oct 2014 18:25:08 +0530 Subject: [PATCH 019/153] audiomixer: critical error for blocksize, timeout min/max values Audiomixer blocksize, cant be 0, hence adjusting the minimum value to 1 timeout value of aggregator is defined with MAX of MAXINT64, but it cannot cross G_MAXLONG * GST_SECOND - 1 Hence changed the max value of the same https://bugzilla.gnome.org/show_bug.cgi?id=738845 --- gst/audiomixer/gstaudiomixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 4fd8d99e2d..41de0da737 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -791,7 +791,7 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, g_param_spec_uint ("blocksize", "Block Size", - "Output block size in number of samples", 0, + "Output block size in number of samples", 1, G_MAXUINT, DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); From 7e75f21a412f8a43d3f109faf857b3d2efbf1af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 25 Oct 2014 18:50:42 +0100 Subject: [PATCH 020/153] tests: fix audiomixer test on big endian systems --- tests/check/elements/audiomixer.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 9afca2c14f..ff9703b298 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -33,6 +33,7 @@ #include #include +#include #include static GMainLoop *main_loop; @@ -86,7 +87,7 @@ GST_START_TEST (test_filter_caps) GstPad *pad; filter_caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, "F32LE", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL); @@ -955,11 +956,7 @@ GST_START_TEST (test_clip) gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test")); caps = gst_caps_new_simple ("audio/x-raw", -#if G_BYTE_ORDER == G_BIG_ENDIAN - "format", G_TYPE_STRING, "S16BE", -#else - "format", G_TYPE_STRING, "S16LE", -#endif + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, NULL); @@ -1405,11 +1402,7 @@ run_sync_test (SendBuffersFunction send_buffers, gst_pad_send_event (queue2_sinkpad, gst_event_new_stream_start ("test")); caps = gst_caps_new_simple ("audio/x-raw", -#if G_BYTE_ORDER == G_BIG_ENDIAN - "format", G_TYPE_STRING, "S16BE", -#else - "format", G_TYPE_STRING, "S16LE", -#endif + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 1000, "channels", G_TYPE_INT, 1, NULL); From d5a171cae9304df5405bf119d9af115d70ff17d3 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 27 Nov 2014 16:43:39 +0100 Subject: [PATCH 021/153] audiomixer: Do not try to resize a buffer to a negative size on EOS --- gst/audiomixer/gstaudiomixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 41de0da737..2c56d7e557 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1500,8 +1500,9 @@ gst_audiomixer_aggregate (GstAggregator * agg) "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" G_GUINT64_FORMAT, max_offset, next_offset); next_offset = max_offset; + if (next_offset > audiomixer->offset) + gst_buffer_resize (outbuf, 0, (next_offset - audiomixer->offset) * bpf); - gst_buffer_resize (outbuf, 0, (next_offset - audiomixer->offset) * bpf); next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); } } From cf90f534f1aaeed433192d35c05e95b7e0916d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 16 Dec 2014 17:37:12 +0100 Subject: [PATCH 022/153] audiomixer: Implement get_next_time() --- gst/audiomixer/gstaudiomixer.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 2c56d7e557..9cac2d7b92 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -235,6 +235,15 @@ gst_audiomixer_do_clip (GstAggregator * agg, GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); static GstFlowReturn gst_audiomixer_aggregate (GstAggregator * agg); +static GstClockTime +gst_audiomixer_get_next_time (GstAggregator * agg) +{ + if (agg->segment.position == -1) + return agg->segment.start; + else + return agg->segment.position; +} + /* we can only accept caps that we and downstream can handle. * if we have filtercaps set, use those to constrain the target caps. */ @@ -815,6 +824,8 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) agg_class->start = gst_audiomixer_start; agg_class->stop = gst_audiomixer_stop; + agg_class->get_next_time = gst_audiomixer_get_next_time; + agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); From 67ef96c82dfc3cdbf1bfbb1789c534b95399dd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 17 Dec 2014 18:20:15 +0100 Subject: [PATCH 023/153] audiomixer: Add queues after the (live) sources in the unit test --- tests/check/elements/audiomixer.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index ff9703b298..74212c8ed7 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -587,7 +587,8 @@ test_live_seeking_try_audiosrc (const gchar * factory_name) /* test failing seeks on live-sources */ GST_START_TEST (test_live_seeking) { - GstElement *bin, *src1 = NULL, *src2, *ac1, *ac2, *audiomixer, *sink; + GstElement *bin, *src1 = + NULL, *src2, *ac1, *ac2, *q1, *q2, *audiomixer, *sink; GstBus *bus; gboolean res; GstPad *srcpad; @@ -624,21 +625,27 @@ GST_START_TEST (test_live_seeking) } ac1 = gst_element_factory_make ("audioconvert", "ac1"); + q1 = gst_element_factory_make ("queue", "q1"); src2 = gst_element_factory_make ("audiotestsrc", "src2"); g_object_set (src2, "wave", 4, NULL); /* silence */ ac2 = gst_element_factory_make ("audioconvert", "ac2"); + q2 = gst_element_factory_make ("queue", "q2"); audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (bin), src1, ac1, src2, ac2, audiomixer, sink, - NULL); + gst_bin_add_many (GST_BIN (bin), src1, ac1, q1, src2, ac2, q2, audiomixer, + sink, NULL); res = gst_element_link (src1, ac1); fail_unless (res == TRUE, NULL); - res = gst_element_link (ac1, audiomixer); + res = gst_element_link (ac1, q1); + fail_unless (res == TRUE, NULL); + res = gst_element_link (q1, audiomixer); fail_unless (res == TRUE, NULL); res = gst_element_link (src2, ac2); fail_unless (res == TRUE, NULL); - res = gst_element_link (ac2, audiomixer); + res = gst_element_link (ac2, q2); + fail_unless (res == TRUE, NULL); + res = gst_element_link (q2, audiomixer); fail_unless (res == TRUE, NULL); res = gst_element_link (audiomixer, sink); fail_unless (res == TRUE, NULL); From d508b39952418a8631a11ec3343ca53a0fae08d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 17 Dec 2014 17:54:09 +0100 Subject: [PATCH 024/153] aggregator: Add a timeout parameter to ::aggregate() When this is TRUE, we really have to produce output. This happens in live mixing mode when we have to output something for the current time, no matter if we have enough input or not. --- gst/audiomixer/gstaudiomixer.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 9cac2d7b92..5929b2dcc2 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -233,7 +233,8 @@ static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); static GstFlowReturn gst_audiomixer_do_clip (GstAggregator * agg, GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); -static GstFlowReturn gst_audiomixer_aggregate (GstAggregator * agg); +static GstFlowReturn gst_audiomixer_aggregate (GstAggregator * agg, + gboolean timeout); static GstClockTime gst_audiomixer_get_next_time (GstAggregator * agg) @@ -1327,7 +1328,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, } static GstFlowReturn -gst_audiomixer_aggregate (GstAggregator * agg) +gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) { /* Get all pads that have data for us and store them in a * new list. @@ -1401,7 +1402,6 @@ gst_audiomixer_aggregate (GstAggregator * agg) } else { next_offset = audiomixer->offset - audiomixer->blocksize; } - next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); if (audiomixer->current_buffer) { @@ -1428,13 +1428,14 @@ gst_audiomixer_aggregate (GstAggregator * agg) GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (iter->data); - inbuf = gst_aggregator_pad_get_buffer (aggpad); if (!inbuf) continue; + g_assert (!pad->buffer || pad->buffer == inbuf); + /* New buffer? */ - if (!pad->buffer || pad->buffer != inbuf) { + if (!pad->buffer) { /* Takes ownership of buffer */ if (!gst_audio_mixer_fill_buffer (audiomixer, pad, inbuf)) { dropped = TRUE; @@ -1451,11 +1452,13 @@ gst_audiomixer_aggregate (GstAggregator * agg) } /* At this point adata->output_offset >= audiomixer->offset or we have no buffer anymore */ + g_assert (!pad->buffer || pad->output_offset >= audiomixer->offset); if (pad->output_offset >= audiomixer->offset && pad->output_offset < audiomixer->offset + audiomixer->blocksize && pad->buffer) { GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); gst_audio_mixer_mix_buffer (audiomixer, pad, &outmap); + if (pad->output_offset >= next_offset) { GST_DEBUG_OBJECT (pad, "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" @@ -1469,17 +1472,17 @@ gst_audiomixer_aggregate (GstAggregator * agg) gst_buffer_unmap (outbuf, &outmap); - if (dropped) { + if (dropped && !timeout) { /* We dropped a buffer, retry */ GST_INFO_OBJECT (audiomixer, "A pad dropped a buffer, wait for the next one"); return GST_FLOW_OK; } - if (!is_done && !is_eos) { + if (!is_done && !is_eos && !timeout) { /* Get more buffers */ GST_INFO_OBJECT (audiomixer, - "We're not done yet for the current offset," " waiting for more data"); + "We're not done yet for the current offset, waiting for more data"); return GST_FLOW_OK; } @@ -1489,7 +1492,6 @@ gst_audiomixer_aggregate (GstAggregator * agg) GST_DEBUG_OBJECT (audiomixer, "We're EOS"); - GST_OBJECT_LOCK (agg); for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); From 46f713c598e9115fbcb4322068d4d6ba774567d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 17 Dec 2014 19:37:22 +0100 Subject: [PATCH 025/153] audiomixer: Make sure to not have pads being behind the current offset We would break sync between the different streams then. --- gst/audiomixer/gstaudiomixer.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 5929b2dcc2..d04e29ebab 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1447,12 +1447,39 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) if (!pad->buffer && !dropped && GST_AGGREGATOR_PAD (pad)->eos) { GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); + continue; } else { is_eos = FALSE; } - /* At this point adata->output_offset >= audiomixer->offset or we have no buffer anymore */ - g_assert (!pad->buffer || pad->output_offset >= audiomixer->offset); + g_assert (pad->buffer); + + /* This pad is lacking behind, we need to update the offset + * and maybe drop the current buffer */ + if (pad->output_offset < audiomixer->offset) { + gint64 diff = audiomixer->offset - pad->output_offset; + gint bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + pad->position += diff * bpf; + if (pad->position > pad->size) { + diff = (pad->position - pad->size) / bpf; + pad->position = pad->size; + } + pad->output_offset += diff; + + if (pad->position == pad->size) { + GstBuffer *buf; + + /* Buffer done, drop it */ + gst_buffer_replace (&pad->buffer, NULL); + buf = gst_aggregator_pad_steal_buffer (aggpad); + if (buf) + gst_buffer_unref (buf); + dropped = TRUE; + continue; + } + } + if (pad->output_offset >= audiomixer->offset && pad->output_offset < audiomixer->offset + audiomixer->blocksize && pad->buffer) { From 06f6d3c65ce15ee659192dc83b374bd14a5b6928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 17 Dec 2014 19:51:32 +0100 Subject: [PATCH 026/153] aggregator: Add function to allow subclasses to set their own latency For audiomixer this is one blocksize, for videoaggregator this should be the duration of one output frame. --- gst/audiomixer/gstaudiomixer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index d04e29ebab..3559750343 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1378,6 +1378,9 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) if (audiomixer->send_caps) { gst_aggregator_set_src_caps (agg, audiomixer->current_caps); + gst_aggregator_set_latency (agg, + gst_util_uint64_scale (audiomixer->blocksize, GST_SECOND, + GST_AUDIO_INFO_RATE (&audiomixer->info)), GST_CLOCK_TIME_NONE); if (agg->segment.rate > 0.0) agg->segment.position = agg->segment.start; From eff64c7ddccf73857dba7a5cad72cd846f6d6d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 18 Dec 2014 22:42:14 +0100 Subject: [PATCH 027/153] audiomixer: The pad's size is always supposed to be the whole buffer size And the offset the offset into that buffer. Changing the size will cause all kinds of assumptions to fail and cause crashes. --- gst/audiomixer/gstaudiomixer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 3559750343..e219132cbc 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1114,10 +1114,9 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, if (start_running_time_offset < audiomixer->offset) { GstBuffer *buf; guint diff = (audiomixer->offset - start_running_time_offset) * bpf; + pad->position += diff; - pad->size -= diff; - /* FIXME: This could only happen due to rounding errors */ - if (pad->size == 0) { + if (pad->position >= pad->size) { /* Empty buffer, drop */ gst_buffer_unref (inbuf); pad->buffer = NULL; From bc418c7a85caf63e891ab54ee688356d686eccc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 18 Dec 2014 23:33:58 +0100 Subject: [PATCH 028/153] audiomixer: We're only EOS if all our pads are actually EOS Having a buffer or not on the pad is irrelevant. --- gst/audiomixer/gstaudiomixer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index e219132cbc..0e8c30632a 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1430,6 +1430,9 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (iter->data); + if (!GST_AGGREGATOR_PAD (pad)->eos) + is_eos = FALSE; + inbuf = gst_aggregator_pad_get_buffer (aggpad); if (!inbuf) continue; @@ -1450,8 +1453,6 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) if (!pad->buffer && !dropped && GST_AGGREGATOR_PAD (pad)->eos) { GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); continue; - } else { - is_eos = FALSE; } g_assert (pad->buffer); From bf3896b2bde4d93a40826f97fa7fef629cc6efdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 19 Dec 2014 13:04:58 +0100 Subject: [PATCH 029/153] audiomixer: Track discont-time per pad instead of globally We do discont handling per pad, not per element! --- gst/audiomixer/gstaudiomixer.c | 20 +++++++++----------- gst/audiomixer/gstaudiomixer.h | 5 +++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 0e8c30632a..faa1a0af48 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -130,6 +130,7 @@ gst_audiomixer_pad_flush_pad (GstAggregatorPad * aggpad, GST_OBJECT_LOCK (aggpad); pad->position = pad->size = 0; pad->output_offset = pad->next_offset = -1; + pad->discont_time = GST_CLOCK_TIME_NONE; gst_buffer_replace (&pad->buffer, NULL); GST_OBJECT_UNLOCK (aggpad); @@ -168,7 +169,7 @@ gst_audiomixer_pad_init (GstAudioMixerPad * pad) pad->size = 0; pad->output_offset = -1; pad->next_offset = -1; - + pad->discont_time = GST_CLOCK_TIME_NONE; } #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) @@ -714,7 +715,6 @@ gst_audiomixer_reset (GstAudioMixer * audiomixer) { audiomixer->offset = 0; gst_caps_replace (&audiomixer->current_caps, NULL); - audiomixer->discont_time = GST_CLOCK_TIME_NONE; } static gboolean @@ -768,7 +768,6 @@ gst_audiomixer_send_event (GstElement * element, GstEvent * event) return res; } - static void gst_audiomixer_class_init (GstAudioMixerClass * klass) { @@ -1046,19 +1045,18 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, /* Discont! */ if (G_UNLIKELY (diff >= max_sample_diff)) { if (audiomixer->discont_wait > 0) { - if (audiomixer->discont_time == GST_CLOCK_TIME_NONE) { - audiomixer->discont_time = start_time; - } else if (start_time - audiomixer->discont_time >= - audiomixer->discont_wait) { + if (pad->discont_time == GST_CLOCK_TIME_NONE) { + pad->discont_time = start_time; + } else if (start_time - pad->discont_time >= audiomixer->discont_wait) { discont = TRUE; - audiomixer->discont_time = GST_CLOCK_TIME_NONE; + pad->discont_time = GST_CLOCK_TIME_NONE; } } else { discont = TRUE; } - } else if (G_UNLIKELY (audiomixer->discont_time != GST_CLOCK_TIME_NONE)) { + } else if (G_UNLIKELY (pad->discont_time != GST_CLOCK_TIME_NONE)) { /* we have had a discont, but are now back on track! */ - audiomixer->discont_time = GST_CLOCK_TIME_NONE; + pad->discont_time = GST_CLOCK_TIME_NONE; } } @@ -1070,7 +1068,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, pad->next_offset, start_offset); pad->output_offset = -1; } else { - audiomixer->discont_time = GST_CLOCK_TIME_NONE; + pad->discont_time = GST_CLOCK_TIME_NONE; } pad->next_offset = end_offset; diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index f5db18ffeb..67ecd96736 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -69,8 +69,6 @@ struct _GstAudioMixer { GstClockTime alignment_threshold; GstClockTime discont_wait; - /* Last time we noticed a discont */ - GstClockTime discont_time; gint64 base_time; /* Size in samples that is output per buffer */ @@ -111,6 +109,9 @@ struct _GstAudioMixerPad { current buffer. */ guint64 next_offset; /* Next expected offset in the input segment */ + + /* Last time we noticed a discont */ + GstClockTime discont_time; }; struct _GstAudioMixerPadClass { From b2fef1f9d25ea2dfe93ed225446205195e31835d Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 21 Dec 2014 07:47:25 -0500 Subject: [PATCH 030/153] audiomixer: fix build flag order Have the libraries/inlcudes from plugins-bad first to avoid picking up the installed version. Fixes the build when the local api changed. --- gst/audiomixer/Makefile.am | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index 372c006869..2a3051ddb1 100644 --- a/gst/audiomixer/Makefile.am +++ b/gst/audiomixer/Makefile.am @@ -6,15 +6,15 @@ include $(top_srcdir)/common/orc.mak libgstaudiomixer_la_SOURCES = gstaudiomixer.c nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) -libgstaudiomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ - $(GST_CFLAGS) $(ORC_CFLAGS) \ +libgstaudiomixer_la_CFLAGS = \ -I$(top_srcdir)/gst-libs \ - -I$(top_builddir)/gst-libs + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) $(ORC_CFLAGS) libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstaudiomixer_la_LIBADD = \ - $(GST_PLUGINS_BASE_LIBS) \ - -lgstaudio-@GST_API_VERSION@ \ $(top_builddir)/gst-libs/gst/base/libgstbadbase-$(GST_API_VERSION).la \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) From 20a79bda491d30a1804964c640f360b183417a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 22 Dec 2014 22:11:13 +0100 Subject: [PATCH 031/153] audiomixer: Use the src query implementation of aggregator as the default case --- gst/audiomixer/gstaudiomixer.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index faa1a0af48..12a7ac0d6b 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -553,17 +553,11 @@ gst_audiomixer_src_query (GstAggregator * agg, GstQuery * query) case GST_QUERY_DURATION: res = gst_audiomixer_query_duration (audiomixer, query); break; - case GST_QUERY_LATENCY: + default: res = GST_AGGREGATOR_CLASS (gst_audiomixer_parent_class)->src_query (agg, query); break; - default: - /* FIXME, needs a custom query handler because we have multiple - * sinkpads */ - res = gst_pad_query_default (GST_PAD (agg->srcpad), GST_OBJECT (agg), - query); - break; } return res; From 8465c0915ef97bce80660616ed35bb7a565066f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 23 Dec 2014 11:45:50 +0100 Subject: [PATCH 032/153] audiomixer: Change blocksize property to output-buffer-duration in time format This makes the interface of audiomixer independent of the actual caps. --- gst/audiomixer/gstaudiomixer.c | 54 +++++++++++++++++++------------ gst/audiomixer/gstaudiomixer.h | 4 +-- tests/check/elements/audiomixer.c | 2 +- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 12a7ac0d6b..0ce25135ee 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -174,7 +174,7 @@ gst_audiomixer_pad_init (GstAudioMixerPad * pad) #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) #define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) -#define DEFAULT_BLOCKSIZE (1024) +#define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) enum { @@ -182,7 +182,7 @@ enum PROP_FILTER_CAPS, PROP_ALIGNMENT_THRESHOLD, PROP_DISCONT_WAIT, - PROP_BLOCKSIZE + PROP_OUTPUT_BUFFER_DURATION }; /* elementfactory information */ @@ -792,10 +792,10 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, - g_param_spec_uint ("blocksize", "Block Size", + g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION, + g_param_spec_uint64 ("output-buffer-duration", "Output Buffer Duration", "Output block size in number of samples", 1, - G_MAXUINT, DEFAULT_BLOCKSIZE, + G_MAXUINT64, DEFAULT_OUTPUT_BUFFER_DURATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_add_pad_template (gstelement_class, @@ -841,7 +841,9 @@ gst_audiomixer_init (GstAudioMixer * audiomixer) audiomixer->filter_caps = NULL; audiomixer->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; - audiomixer->blocksize = DEFAULT_BLOCKSIZE; + audiomixer->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; + gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), + audiomixer->output_buffer_duration, GST_CLOCK_TIME_NONE); } static void @@ -889,8 +891,10 @@ gst_audiomixer_set_property (GObject * object, guint prop_id, case PROP_DISCONT_WAIT: audiomixer->discont_wait = g_value_get_uint64 (value); break; - case PROP_BLOCKSIZE: - audiomixer->blocksize = g_value_get_uint (value); + case PROP_OUTPUT_BUFFER_DURATION: + audiomixer->output_buffer_duration = g_value_get_uint64 (value); + gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), + audiomixer->output_buffer_duration, GST_CLOCK_TIME_NONE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -916,8 +920,8 @@ gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, case PROP_DISCONT_WAIT: g_value_set_uint64 (value, audiomixer->discont_wait); break; - case PROP_BLOCKSIZE: - g_value_set_uint (value, audiomixer->blocksize); + case PROP_OUTPUT_BUFFER_DURATION: + g_value_set_uint64 (value, audiomixer->output_buffer_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1149,9 +1153,15 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstBuffer *inbuf; GstMapInfo inmap; gint bpf; + guint blocksize; GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); + blocksize = + gst_util_uint64_scale (audiomixer->output_buffer_duration, + GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); + blocksize = MAX (1, blocksize); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); /* Overlap => mix */ @@ -1161,8 +1171,8 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, out_start = 0; overlap = pad->size / bpf - pad->position / bpf; - if (overlap > audiomixer->blocksize - out_start) - overlap = audiomixer->blocksize - out_start; + if (overlap > blocksize - out_start) + overlap = blocksize - out_start; inbuf = gst_aggregator_pad_get_buffer (aggpad); if (inbuf == NULL) @@ -1360,6 +1370,7 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) gboolean dropped = FALSE; gboolean is_eos = TRUE; gboolean is_done = TRUE; + guint blocksize; audiomixer = GST_AUDIO_MIXER (agg); @@ -1367,11 +1378,13 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) goto not_negotiated; + blocksize = + gst_util_uint64_scale (audiomixer->output_buffer_duration, + GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); + blocksize = MAX (1, blocksize); + if (audiomixer->send_caps) { gst_aggregator_set_src_caps (agg, audiomixer->current_caps); - gst_aggregator_set_latency (agg, - gst_util_uint64_scale (audiomixer->blocksize, GST_SECOND, - GST_AUDIO_INFO_RATE (&audiomixer->info)), GST_CLOCK_TIME_NONE); if (agg->segment.rate > 0.0) agg->segment.position = agg->segment.start; @@ -1392,16 +1405,16 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) /* FIXME: Reverse mixing does not work at all yet */ if (agg->segment.rate > 0.0) { - next_offset = audiomixer->offset + audiomixer->blocksize; + next_offset = audiomixer->offset + blocksize; } else { - next_offset = audiomixer->offset - audiomixer->blocksize; + next_offset = audiomixer->offset - blocksize; } next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); if (audiomixer->current_buffer) { outbuf = audiomixer->current_buffer; } else { - outbuf = gst_buffer_new_and_alloc (audiomixer->blocksize * bpf); + outbuf = gst_buffer_new_and_alloc (blocksize * bpf); gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); gst_audio_format_fill_silence (audiomixer->info.finfo, outmap.data, outmap.size); @@ -1411,7 +1424,7 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) GST_LOG_OBJECT (agg, "Starting to mix %u samples for offset %" G_GUINT64_FORMAT - " with timestamp %" GST_TIME_FORMAT, audiomixer->blocksize, + " with timestamp %" GST_TIME_FORMAT, blocksize, audiomixer->offset, GST_TIME_ARGS (agg->segment.position)); gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); @@ -1476,8 +1489,7 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) } if (pad->output_offset >= audiomixer->offset - && pad->output_offset < - audiomixer->offset + audiomixer->blocksize && pad->buffer) { + && pad->output_offset < audiomixer->offset + blocksize && pad->buffer) { GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); gst_audio_mixer_mix_buffer (audiomixer, pad, &outmap); diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 67ecd96736..f626d78c9b 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -71,8 +71,8 @@ struct _GstAudioMixer { gint64 base_time; - /* Size in samples that is output per buffer */ - guint blocksize; + /* Duration of every output buffer */ + GstClockTime output_buffer_duration; }; struct _GstAudioMixerClass { diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 74212c8ed7..bb8c8da1b5 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1373,7 +1373,7 @@ run_sync_test (SendBuffersFunction send_buffers, queue1 = gst_element_factory_make ("queue", "queue1"); queue2 = gst_element_factory_make ("queue", "queue2"); audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - g_object_set (audiomixer, "blocksize", 500, NULL); + g_object_set (audiomixer, "output-buffer-duration", 500 * GST_MSECOND, NULL); sink = gst_element_factory_make ("fakesink", "sink"); g_object_set (sink, "signal-handoffs", TRUE, NULL); g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_collect_cb, From eefea80daeff310192098e12092df34fcf8c5b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 23 Dec 2014 12:15:50 +0100 Subject: [PATCH 033/153] audiomixer: Make sure to release the current buffer in reset() If we didn't output the last one in aggregate because we were shutting down earlier we might otherwise leak it. --- gst/audiomixer/gstaudiomixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 0ce25135ee..a12a3f1b31 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -709,6 +709,7 @@ gst_audiomixer_reset (GstAudioMixer * audiomixer) { audiomixer->offset = 0; gst_caps_replace (&audiomixer->current_caps, NULL); + gst_buffer_replace (&audiomixer->current_buffer, NULL); } static gboolean From cd256acf03c40581933d7b875e10910515d34fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 23 Dec 2014 12:24:48 +0100 Subject: [PATCH 034/153] audiomixer: If getting a timeout before having caps, just advance our position This can happen if this is a live pipeline and no source produced any buffer and sent no caps until the an output buffer should've been produced according to the latency. --- gst/audiomixer/gstaudiomixer.c | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index a12a3f1b31..35e6cc0ffb 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -707,7 +707,11 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, static void gst_audiomixer_reset (GstAudioMixer * audiomixer) { + GstAggregator *agg = GST_AGGREGATOR (audiomixer); + audiomixer->offset = 0; + agg->segment.position = -1; + gst_caps_replace (&audiomixer->current_caps, NULL); gst_buffer_replace (&audiomixer->current_buffer, NULL); } @@ -1375,9 +1379,32 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) audiomixer = GST_AUDIO_MIXER (agg); - /* this is fatal */ - if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) - goto not_negotiated; + /* Update position from the segment start/stop if needed */ + if (agg->segment.position == -1) { + if (agg->segment.rate > 0.0) + agg->segment.position = agg->segment.start; + else + agg->segment.position = agg->segment.stop; + } + + if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) { + if (timeout) { + GST_DEBUG_OBJECT (audiomixer, + "Got timeout before receiving any caps, don't output anything"); + + /* Advance position */ + if (agg->segment.rate > 0.0) + agg->segment.position += audiomixer->output_buffer_duration; + else if (agg->segment.position > audiomixer->output_buffer_duration) + agg->segment.position -= audiomixer->output_buffer_duration; + else + agg->segment.position = 0; + + return GST_FLOW_OK; + } else { + goto not_negotiated; + } + } blocksize = gst_util_uint64_scale (audiomixer->output_buffer_duration, @@ -1387,11 +1414,6 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) if (audiomixer->send_caps) { gst_aggregator_set_src_caps (agg, audiomixer->current_caps); - if (agg->segment.rate > 0.0) - agg->segment.position = agg->segment.start; - else - agg->segment.position = agg->segment.stop; - audiomixer->offset = gst_util_uint64_scale (agg->segment.position, GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); From ecc709be31a941679f54bcd566d24ba1900062a2 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sat, 27 Dec 2014 15:49:39 +0530 Subject: [PATCH 035/153] audiomixer: Document the pad properties --- gst/audiomixer/gstaudiomixer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 35e6cc0ffb..48ef528a9c 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -30,6 +30,18 @@ * The audiomixer currently mixes all data received on the sinkpads as soon as * possible without trying to synchronize the streams. * + * The input pads are from a GstPad subclass and have additional + * properties to mute each pad individually and set the volume: + * + * + * + * "mute": Whether to mute the pad or not (#gboolean) + * + * + * "volume": The volume of the pad, between 0.0 and 10.0 (#gdouble) + * + * + * * * Example launch line * |[ From 5cf0b8c44579e5211c0efa1593fdb203efa0b793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 30 Dec 2014 15:29:36 +0000 Subject: [PATCH 036/153] audiomixer: fix output-block-size property description --- gst/audiomixer/gstaudiomixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 48ef528a9c..a96b09d5a1 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -811,7 +811,7 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION, g_param_spec_uint64 ("output-buffer-duration", "Output Buffer Duration", - "Output block size in number of samples", 1, + "Output block size in nanoseconds", 1, G_MAXUINT64, DEFAULT_OUTPUT_BUFFER_DURATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); From d7880f217e0338daa65c4be2f3497123d084e08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 30 Dec 2014 18:01:34 +0000 Subject: [PATCH 037/153] audiomixer: update for aggregator start/stop vfunc change --- gst/audiomixer/gstaudiomixer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index a96b09d5a1..9f75a8e46c 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -733,9 +733,6 @@ gst_audiomixer_start (GstAggregator * agg) { GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - if (!GST_AGGREGATOR_CLASS (parent_class)->start (agg)) - return FALSE; - gst_audiomixer_reset (audiomixer); return TRUE; @@ -746,9 +743,6 @@ gst_audiomixer_stop (GstAggregator * agg) { GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) - return FALSE; - gst_audiomixer_reset (audiomixer); return TRUE; From 9071b8487c1c4e730d82cb88474cf487b8c35928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 14 Jan 2015 14:35:15 -0500 Subject: [PATCH 038/153] aggregator: Replace event lock with pad's object lock Reduce the number of locks simplify code, what is protects is exposed, but the lock was not. Also means adding an _unlocked version of gst_aggregator_pad_steal_buffer(). https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- gst/audiomixer/gstaudiomixer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 9f75a8e46c..01521e517a 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1199,7 +1199,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstBuffer *buf; /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer (aggpad); + buf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); if (buf) gst_buffer_unref (buf); } @@ -1208,7 +1208,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, } if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { - GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer (aggpad); + GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); @@ -1330,7 +1330,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer (aggpad); + buf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); if (buf) gst_buffer_unref (buf); GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); From 33f412d6db589a2cdfdefd403f5d6d4e7c8f4107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 21 Jan 2015 18:39:24 -0500 Subject: [PATCH 039/153] audiomixer: Don't reset caps on flush A flush event doesn't invalidate the previous caps event. https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- gst/audiomixer/gstaudiomixer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 01521e517a..44ec1550a4 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -751,7 +751,11 @@ gst_audiomixer_stop (GstAggregator * agg) static GstFlowReturn gst_audiomixer_flush (GstAggregator * agg) { - gst_audiomixer_reset (GST_AUDIO_MIXER (agg)); + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); + + audiomixer->offset = 0; + agg->segment.position = -1; + gst_buffer_replace (&audiomixer->current_buffer, NULL); return GST_FLOW_OK; } From 9afd2b333999922e79bad9611b71eccc2b58784f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 21 Jan 2015 18:47:40 -0500 Subject: [PATCH 040/153] audiomixer: Clear GstAudioInfo the the caps When clearing the caps, also clear the matching GstAudioInfo https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- gst/audiomixer/gstaudiomixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 44ec1550a4..179c186286 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -724,6 +724,7 @@ gst_audiomixer_reset (GstAudioMixer * audiomixer) audiomixer->offset = 0; agg->segment.position = -1; + gst_audio_info_init (&audiomixer->info); gst_caps_replace (&audiomixer->current_caps, NULL); gst_buffer_replace (&audiomixer->current_buffer, NULL); } From 402c0d4c5cb979af4b860bb831ddd0017d847afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 21 Jan 2015 19:32:34 -0500 Subject: [PATCH 041/153] audiomixer: Avoid race in caps negotiation With the current audiomixer, the input caps need to be the same, otherwise there is an unavoidable race in the caps negotiation. So enforce that using capsfilters https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- tests/check/elements/audiomixer.c | 53 +++++++++++++++++++------------ 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index bb8c8da1b5..b56a3887e5 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -587,11 +587,12 @@ test_live_seeking_try_audiosrc (const gchar * factory_name) /* test failing seeks on live-sources */ GST_START_TEST (test_live_seeking) { - GstElement *bin, *src1 = - NULL, *src2, *ac1, *ac2, *q1, *q2, *audiomixer, *sink; + GstElement *bin, *src1 = NULL, *cf, *src2, *audiomixer, *sink; + GstCaps *caps; GstBus *bus; gboolean res; GstPad *srcpad; + GstPad *sinkpad; gint i; GstStateChangeReturn state_res; GstStreamConsistency *consist; @@ -624,32 +625,44 @@ GST_START_TEST (test_live_seeking) g_object_set (src1, "num-buffers", 4, "blocksize", 44100, NULL); } - ac1 = gst_element_factory_make ("audioconvert", "ac1"); - q1 = gst_element_factory_make ("queue", "q1"); - src2 = gst_element_factory_make ("audiotestsrc", "src2"); - g_object_set (src2, "wave", 4, NULL); /* silence */ - ac2 = gst_element_factory_make ("audioconvert", "ac2"); - q2 = gst_element_factory_make ("queue", "q2"); audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + cf = gst_element_factory_make ("capsfilter", "capsfilter"); sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (bin), src1, ac1, q1, src2, ac2, q2, audiomixer, - sink, NULL); - res = gst_element_link (src1, ac1); + gst_bin_add_many (GST_BIN (bin), src1, cf, audiomixer, sink, NULL); + res = gst_element_link (src1, cf); fail_unless (res == TRUE, NULL); - res = gst_element_link (ac1, q1); - fail_unless (res == TRUE, NULL); - res = gst_element_link (q1, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (src2, ac2); - fail_unless (res == TRUE, NULL); - res = gst_element_link (ac2, q2); - fail_unless (res == TRUE, NULL); - res = gst_element_link (q2, audiomixer); + res = gst_element_link (cf, audiomixer); fail_unless (res == TRUE, NULL); res = gst_element_link (audiomixer, sink); fail_unless (res == TRUE, NULL); + gst_element_set_state (bin, GST_STATE_PLAYING); + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + sinkpad = gst_element_get_static_pad (sink, "sink"); + fail_unless (sinkpad != NULL); + caps = gst_pad_get_current_caps (sinkpad); + fail_unless (caps != NULL); + gst_object_unref (sinkpad); + + gst_element_set_state (bin, GST_STATE_NULL); + + g_object_set (cf, "caps", caps, NULL); + + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + gst_bin_add (GST_BIN (bin), src2); + + res = gst_element_link_filtered (src2, audiomixer, caps); + fail_unless (res == TRUE, NULL); + + gst_caps_unref (caps); + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, (GstClockTime) 0, From 0955a39a3d7bd1b7e57073c58ef481ee6392347e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 22 Jan 2015 17:41:24 -0500 Subject: [PATCH 042/153] audiomixer: Replace racy timeout based tested with drain query Using the drain query, we can be certain that the buffer has done going through the aggregator by taking the stream locks. https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- tests/check/elements/audiomixer.c | 58 ++++++++----------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index b56a3887e5..85299ff25b 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -891,36 +891,17 @@ GST_END_TEST; static GstBuffer *handoff_buffer = NULL; -static gboolean -_quit (GMainLoop * ml) -{ - g_main_loop_quit (ml); - - return G_SOURCE_REMOVE; -} static void handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, - GstClockTime * wanted_end) + gpointer user_data) { GST_DEBUG ("got buffer -- SIZE: %" G_GSIZE_FORMAT - " -- %p DURATION is %" GST_TIME_FORMAT " -- WANTED END %" GST_TIME_FORMAT, + " -- %p DURATION is %" GST_TIME_FORMAT, gst_buffer_get_size (buffer), buffer, - GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer)), - GST_TIME_ARGS (*wanted_end)); + GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); gst_buffer_replace (&handoff_buffer, buffer); - - - /* Buffers we push in will be 'cut' into different smaller buffers, - * we make sure that the last chunck was pushes before we concider the buffer - * we pushed as being done */ - if (main_loop && *wanted_end - && *wanted_end <= - GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer)) { - *wanted_end = 0; - g_idle_add ((GSourceFunc) _quit, main_loop); - } } /* check if clipping works as expected */ @@ -936,14 +917,10 @@ GST_START_TEST (test_clip) GstEvent *event; GstBuffer *buffer; GstCaps *caps; - GMainLoop *local_mainloop; - GstClockTime wanted_end = 0; + GstQuery *drain = gst_query_new_drain (); GST_INFO ("preparing test"); - local_mainloop = g_main_loop_new (NULL, FALSE); - main_loop = NULL; - /* build pipeline */ bin = gst_pipeline_new ("pipeline"); bus = gst_element_get_bus (bin); @@ -957,8 +934,7 @@ GST_START_TEST (test_clip) audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); sink = gst_element_factory_make ("fakesink", "sink"); g_object_set (sink, "signal-handoffs", TRUE, NULL); - g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, - &wanted_end); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); gst_bin_add_many (GST_BIN (bin), audiomixer, sink, NULL); res = gst_element_link (audiomixer, sink); @@ -999,16 +975,13 @@ GST_START_TEST (test_clip) buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); - main_loop = local_mainloop; + ck_assert_int_eq (ret, GST_FLOW_OK); /* The aggregation is done in a dedicated thread, so we can't - * not know when it is actually going to happen, so we just add\ - * a 100 ms timeout to be able to then check that the aggregation - * did happen as we do not have much other choice. + * not know when it is actually going to happen, so we use a DRAIN query + * to wait for it to complete. */ - g_timeout_add (100, (GSourceFunc) _quit, main_loop); - g_main_loop_run (main_loop); - ck_assert_int_eq (ret, GST_FLOW_OK); + gst_pad_query (sinkpad, drain); fail_unless (handoff_buffer == NULL); /* should be partially clipped */ @@ -1016,15 +989,14 @@ GST_START_TEST (test_clip) GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - wanted_end = 135 * GST_MSECOND; GST_DEBUG ("pushing buffer %p START %" GST_TIME_FORMAT " -- DURATION is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); - main_loop = local_mainloop; ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - g_main_loop_run (main_loop); + gst_pad_query (sinkpad, drain); + fail_unless (handoff_buffer != NULL); gst_buffer_replace (&handoff_buffer, NULL); @@ -1033,18 +1005,18 @@ GST_START_TEST (test_clip) GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - wanted_end = 390 * GST_MSECOND; GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - g_main_loop_run (main_loop); + gst_pad_query (sinkpad, drain); fail_unless (handoff_buffer != NULL); gst_buffer_replace (&handoff_buffer, NULL); fail_unless (handoff_buffer == NULL); /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; @@ -1052,9 +1024,8 @@ GST_START_TEST (test_clip) buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); - g_timeout_add (100, (GSourceFunc) _quit, main_loop); - g_main_loop_run (main_loop); ck_assert_int_eq (ret, GST_FLOW_OK); + gst_pad_query (sinkpad, drain); fail_unless (handoff_buffer == NULL); gst_element_release_request_pad (audiomixer, sinkpad); @@ -1063,6 +1034,7 @@ GST_START_TEST (test_clip) gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); + gst_query_unref (drain); } GST_END_TEST; From 660ddd40c29cdffcfcbe8bcd448ba3817bdc7cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 21 Jan 2015 19:09:13 -0500 Subject: [PATCH 043/153] audiomixer: Make flush start/stop test non-racy The flush stop could have happened between the source trying to push the segment event and the buffer, this would cause a warning. Prevent that by taking the source's stream lock while flushing. https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- tests/check/elements/audiomixer.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 85299ff25b..e2007453a9 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1254,7 +1254,7 @@ GST_END_TEST; GST_START_TEST (test_flush_start_flush_stop) { GstPadTemplate *sink_template; - GstPad *tmppad, *sinkpad1, *sinkpad2, *audiomixer_src; + GstPad *tmppad, *srcpad1, *sinkpad1, *sinkpad2, *audiomixer_src; GstElement *pipeline, *src1, *src2, *audiomixer, *sink; GST_INFO ("preparing test"); @@ -1274,9 +1274,8 @@ GST_START_TEST (test_flush_start_flush_stop) "sink_%u"); fail_unless (GST_IS_PAD_TEMPLATE (sink_template)); sinkpad1 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); - tmppad = gst_element_get_static_pad (src1, "src"); - gst_pad_link (tmppad, sinkpad1); - gst_object_unref (tmppad); + srcpad1 = gst_element_get_static_pad (src1, "src"); + gst_pad_link (srcpad1, sinkpad1); sinkpad2 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); tmppad = gst_element_get_static_pad (src2, "src"); @@ -1294,7 +1293,11 @@ GST_START_TEST (test_flush_start_flush_stop) gst_pad_send_event (sinkpad1, gst_event_new_flush_start ()); fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); fail_unless (GST_PAD_IS_FLUSHING (sinkpad1)); + /* Hold the streamlock to make sure the flush stop is not between + the attempted push of a segment event and of the following buffer. */ + GST_PAD_STREAM_LOCK (srcpad1); gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE)); + GST_PAD_STREAM_UNLOCK (srcpad1); fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); fail_if (GST_PAD_IS_FLUSHING (sinkpad1)); gst_object_unref (audiomixer_src); @@ -1303,6 +1306,7 @@ GST_START_TEST (test_flush_start_flush_stop) gst_object_unref (sinkpad1); gst_element_release_request_pad (audiomixer, sinkpad2); gst_object_unref (sinkpad2); + gst_object_unref (srcpad1); /* cleanup */ gst_element_set_state (pipeline, GST_STATE_NULL); From 198b16c563d03913c3608b47a6ea462fb888a214 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Mon, 26 Jan 2015 11:25:54 +0100 Subject: [PATCH 044/153] aggregator: Hide GstAggregatorPad buffer and EOS fileds And add a getter for the EOS. The user should always use the various getters to access those fields https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- gst/audiomixer/gstaudiomixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 179c186286..8fca8e407e 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1469,7 +1469,7 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (iter->data); - if (!GST_AGGREGATOR_PAD (pad)->eos) + if (!gst_aggregator_pad_is_eos (aggpad)) is_eos = FALSE; inbuf = gst_aggregator_pad_get_buffer (aggpad); @@ -1489,7 +1489,7 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) gst_buffer_unref (inbuf); } - if (!pad->buffer && !dropped && GST_AGGREGATOR_PAD (pad)->eos) { + if (!pad->buffer && !dropped && gst_aggregator_pad_is_eos (aggpad)) { GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); continue; } From b1eef4f4362ec8f125948a1760a496c089e1f488 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Mon, 26 Jan 2015 11:29:08 +0100 Subject: [PATCH 045/153] aggregator: Make the PAD_LOCK private Instead of using the GST_OBJECT_LOCK we should have a dedicated mutex for the pad as it is also associated with the mutex on the EVENT_MUTEX on which we wait in the _chain function of the pad. The GstAggregatorPad.segment is still protected with the GST_OBJECT_LOCK. Remove the gst_aggregator_pad_peak_unlocked method as it does not make sense anymore with a private lock. https://bugzilla.gnome.org/show_bug.cgi?id=742684 --- gst/audiomixer/gstaudiomixer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 8fca8e407e..d5ff6ebe73 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1204,7 +1204,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstBuffer *buf; /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); + buf = gst_aggregator_pad_steal_buffer (aggpad); if (buf) gst_buffer_unref (buf); } @@ -1213,7 +1213,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, } if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { - GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); + GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer (aggpad); /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); @@ -1335,7 +1335,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer_unlocked (aggpad); + buf = gst_aggregator_pad_steal_buffer (aggpad); if (buf) gst_buffer_unref (buf); GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); From 6d6c6932547460abf9f8c40351b2551abc4071b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 5 Feb 2015 16:02:54 +0100 Subject: [PATCH 046/153] audiomixer: Remove weird and wrong segment handling There's no reason why audiomixer should override the segment base of upstream with whatever value it got from a SEEK event, or even worse... with 0 if there was no SEEK event yet. This broke synchronization if upstream provided a segment base other than 0, e.g. when using pad offsets. Also that this code did things conditional on the element's state should've been a big warning already that something is just wrong. If this breaks anything else now, let's fix it properly :) Also don't do fancy segment position trickery when receiving a segment event. It's just not correct. --- gst/audiomixer/gstaudiomixer.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index d5ff6ebe73..e512863c7f 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -639,9 +639,6 @@ gst_audiomixer_src_event (GstAggregator * agg, GstEvent * event) /* Link up */ result = GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); - - if (result) - audiomixer->base_time = agg->segment.start; goto done; } break; @@ -693,17 +690,6 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, gst_event_unref (event); event = NULL; } - - if (event) { - res = - GST_AGGREGATOR_CLASS (parent_class)->sink_event (agg, aggpad, - event); - - if (res) - aggpad->segment.position = segment->start + segment->offset; - - event = NULL; - } break; } default: @@ -761,23 +747,6 @@ gst_audiomixer_flush (GstAggregator * agg) return GST_FLOW_OK; } -static gboolean -gst_audiomixer_send_event (GstElement * element, GstEvent * event) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (element); - - gboolean res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); - - GST_STATE_LOCK (element); - if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK && - GST_STATE (element) < GST_STATE_PAUSED) { - audiomixer->base_time = GST_AGGREGATOR (element)->segment.start; - } - GST_STATE_UNLOCK (element); - - return res; -} - static void gst_audiomixer_class_init (GstAudioMixerClass * klass) { @@ -827,8 +796,6 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); - gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_audiomixer_send_event); - agg_class->sinkpads_type = GST_TYPE_AUDIO_MIXER_PAD; agg_class->start = gst_audiomixer_start; @@ -1093,7 +1060,6 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, guint64 start_running_time_offset; guint64 end_running_time_offset; - aggpad->segment.base = audiomixer->base_time; start_running_time = gst_segment_to_running_time (&aggpad->segment, GST_FORMAT_TIME, start_time); From e54829aa4f6d7be47bc7575418224edada00459b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 5 Feb 2015 15:23:04 +0000 Subject: [PATCH 047/153] tests: audiomixer: add unit test for proper segment.base handling As adjusted by gst_pad_set_offset(), or when doing segment seeks or looping for example. See previous audiomixer commit. --- tests/check/elements/audiomixer.c | 65 +++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index e2007453a9..15dbc77562 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1774,6 +1774,68 @@ GST_START_TEST (test_sync_unaligned) GST_END_TEST; +#ifndef GST_DISABLE_PARSE +GST_START_TEST (test_segment_base_handling) +{ + GstElement *pipeline, *sink, *mix, *src1, *src2; + GstPad *srcpad, *sinkpad; + GstClockTime end_time; + GstSample *last_sample = NULL; + GstSample *sample; + GstBuffer *buf; + GstCaps *caps; + + caps = gst_caps_new_simple ("audio/x-raw", "rate", G_TYPE_INT, 44100, + "channels", G_TYPE_INT, 2, NULL); + + pipeline = gst_pipeline_new ("pipeline"); + mix = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("appsink", "sink"); + g_object_set (sink, "caps", caps, "sync", FALSE, NULL); + gst_caps_unref (caps); + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "samplesperbuffer", 4410, "num-buffers", 50, NULL); + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "samplesperbuffer", 4410, "num-buffers", 50, NULL); + gst_bin_add_many (GST_BIN (pipeline), src1, src2, mix, sink, NULL); + fail_unless (gst_element_link (mix, sink)); + + srcpad = gst_element_get_static_pad (src1, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_1"); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (src2, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_2"); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_pad_set_offset (sinkpad, 5 * GST_SECOND); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + do { + g_signal_emit_by_name (sink, "pull-sample", &sample); + if (sample == NULL) + break; + if (last_sample) + gst_sample_unref (last_sample); + last_sample = sample; + } while (TRUE); + + buf = gst_sample_get_buffer (last_sample); + end_time = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); + fail_unless_equals_int64 (end_time, 10 * GST_SECOND); + gst_sample_unref (last_sample); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; +#endif + static Suite * audiomixer_suite (void) { @@ -1797,6 +1859,9 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_sync); tcase_add_test (tc_chain, test_sync_discont); tcase_add_test (tc_chain, test_sync_unaligned); +#ifndef GST_DISABLE_PARSE + tcase_add_test (tc_chain, test_segment_base_handling); +#endif /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 68515c44399f6092f511bffe23e0fc7e17156116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 5 Feb 2015 15:57:59 +0000 Subject: [PATCH 048/153] audiomixer: remove now-unused base_time field in object structure --- gst/audiomixer/gstaudiomixer.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index f626d78c9b..9e14582951 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -49,7 +49,7 @@ typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; * The audiomixer object structure. */ struct _GstAudioMixer { - GstAggregator element; + GstAggregator aggregator; /* the next are valid for both int and float */ GstAudioInfo info; @@ -69,8 +69,6 @@ struct _GstAudioMixer { GstClockTime alignment_threshold; GstClockTime discont_wait; - gint64 base_time; - /* Duration of every output buffer */ GstClockTime output_buffer_duration; }; From 3c9ae895b06085971e89b53ef33b483f80945fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 11 Feb 2015 14:16:21 +0100 Subject: [PATCH 049/153] Improve and fix LATENCY query handling This now follows the design docs everywhere, especially the maximum latency handling. https://bugzilla.gnome.org/show_bug.cgi?id=744106 --- gst/audiomixer/gstaudiomixer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index e512863c7f..a58ef74f97 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -826,7 +826,7 @@ gst_audiomixer_init (GstAudioMixer * audiomixer) audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; audiomixer->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, GST_CLOCK_TIME_NONE); + audiomixer->output_buffer_duration, audiomixer->output_buffer_duration); } static void @@ -877,7 +877,8 @@ gst_audiomixer_set_property (GObject * object, guint prop_id, case PROP_OUTPUT_BUFFER_DURATION: audiomixer->output_buffer_duration = g_value_get_uint64 (value); gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, GST_CLOCK_TIME_NONE); + audiomixer->output_buffer_duration, + audiomixer->output_buffer_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); From 195e54e06a4baebac03966e18f2fff783690ac11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 12 Feb 2015 11:26:44 +0000 Subject: [PATCH 050/153] audiomixer: calculate stream_time used to sync pad values correctly Use pad (input) segment to calculate the stream time from the input timestamp, not the aggregator (output) segment. --- gst/audiomixer/gstaudiomixer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index a58ef74f97..f48e268b65 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -973,22 +973,20 @@ static gboolean gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstBuffer * inbuf) { + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); GstClockTime start_time, end_time; gboolean discont = FALSE; guint64 start_offset, end_offset; GstClockTime timestamp, stream_time; gint rate, bpf; - GstAggregator *agg = GST_AGGREGATOR (audiomixer); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - g_assert (pad->buffer == NULL); rate = GST_AUDIO_INFO_RATE (&audiomixer->info); bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); timestamp = GST_BUFFER_TIMESTAMP (inbuf); - stream_time = gst_segment_to_stream_time (&agg->segment, GST_FORMAT_TIME, + stream_time = gst_segment_to_stream_time (&aggpad->segment, GST_FORMAT_TIME, timestamp); /* sync object properties on stream time */ From 91ff1a2957b397f74d7a6b78aa7ed1a21768223b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 12 Feb 2015 15:48:07 +0000 Subject: [PATCH 051/153] tests: remove GST_DISABLE_PARSE guards from two tests that don't require it --- tests/check/elements/audiomixer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 15dbc77562..d758e75eea 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1774,7 +1774,6 @@ GST_START_TEST (test_sync_unaligned) GST_END_TEST; -#ifndef GST_DISABLE_PARSE GST_START_TEST (test_segment_base_handling) { GstElement *pipeline, *sink, *mix, *src1, *src2; @@ -1834,7 +1833,6 @@ GST_START_TEST (test_segment_base_handling) } GST_END_TEST; -#endif static Suite * audiomixer_suite (void) @@ -1859,9 +1857,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_sync); tcase_add_test (tc_chain, test_sync_discont); tcase_add_test (tc_chain, test_sync_unaligned); -#ifndef GST_DISABLE_PARSE tcase_add_test (tc_chain, test_segment_base_handling); -#endif /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 5fc7d39090fb8596e64643eaca22b631bb547d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 13 Feb 2015 16:06:34 +0000 Subject: [PATCH 052/153] audiomixer: use new gst_aggregator_pad_drop_buffer() --- gst/audiomixer/gstaudiomixer.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index f48e268b65..8881d63ebb 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1071,14 +1071,10 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, gst_util_uint64_scale (end_running_time, rate, GST_SECOND); if (end_running_time_offset < audiomixer->offset) { - GstBuffer *buf; - /* Before output segment, drop */ gst_buffer_unref (inbuf); pad->buffer = NULL; - buf = gst_aggregator_pad_steal_buffer (aggpad); - if (buf) - gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (aggpad); pad->position = 0; pad->size = 0; pad->output_offset = -1; @@ -1089,7 +1085,6 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, } if (start_running_time_offset < audiomixer->offset) { - GstBuffer *buf; guint diff = (audiomixer->offset - start_running_time_offset) * bpf; pad->position += diff; @@ -1097,9 +1092,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, /* Empty buffer, drop */ gst_buffer_unref (inbuf); pad->buffer = NULL; - buf = gst_aggregator_pad_steal_buffer (aggpad); - if (buf) - gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (aggpad); pad->position = 0; pad->size = 0; pad->output_offset = -1; @@ -1166,28 +1159,22 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, pad->position += overlap * bpf; pad->output_offset += overlap; if (pad->position >= pad->size) { - GstBuffer *buf; /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer (aggpad); - if (buf) - gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (aggpad); } GST_OBJECT_UNLOCK (pad); return; } if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { - GstBuffer *aggpadbuf = gst_aggregator_pad_steal_buffer (aggpad); - /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); gst_buffer_unref (inbuf); pad->output_offset += pad->size / bpf; /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - if (aggpadbuf) - gst_buffer_unref (aggpadbuf); + gst_aggregator_pad_drop_buffer (aggpad); GST_OBJECT_UNLOCK (pad); return; } @@ -1296,13 +1283,9 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, pad->output_offset += overlap; if (pad->position == pad->size) { - GstBuffer *buf; - /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer (aggpad); - if (buf) - gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (aggpad); GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); } @@ -1475,13 +1458,9 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) pad->output_offset += diff; if (pad->position == pad->size) { - GstBuffer *buf; - /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - buf = gst_aggregator_pad_steal_buffer (aggpad); - if (buf) - gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (aggpad); dropped = TRUE; continue; } From fc917fb8cfd7d4e200857d0872fd841bbe676a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 3 Mar 2015 20:03:55 +0100 Subject: [PATCH 053/153] audiomixer: Latency is twice the output buffer duration, not only once Let's assume a source that outputs outputs 20ms buffers, and audiomixer having a 20ms output buffer duration. However timestamps don't align perfectly, the source buffers are offsetted by 5ms. For our ASCII art picture, each letter is 5ms, each pipe is the start of a 20ms buffer. So what happens is the following: 0 20 40 60 OOOOOOOOOOOOOOOO | | | | 5 25 45 65 IIIIIIIIIIIIIIII | | | | This means that the second output buffer (20 to 40ms) only gets its last 5ms at time 45ms (the timestamp of the next buffer is the time when the buffer arrives). But if we only have a latency of 20ms, we would wait until 40ms to generate the output buffer and miss the last 5ms of the input buffer. --- gst/audiomixer/gstaudiomixer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 8881d63ebb..da0884238d 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -826,7 +826,8 @@ gst_audiomixer_init (GstAudioMixer * audiomixer) audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; audiomixer->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, audiomixer->output_buffer_duration); + 2 * audiomixer->output_buffer_duration, + 2 * audiomixer->output_buffer_duration); } static void @@ -877,8 +878,8 @@ gst_audiomixer_set_property (GObject * object, guint prop_id, case PROP_OUTPUT_BUFFER_DURATION: audiomixer->output_buffer_duration = g_value_get_uint64 (value); gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, - audiomixer->output_buffer_duration); + 2 * audiomixer->output_buffer_duration, + 2 * audiomixer->output_buffer_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); From 38cf87aaea3f88a6a204a62bb1309c706a1db6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Mar 2015 13:16:03 +0100 Subject: [PATCH 054/153] Revert "audiomixer: Latency is twice the output buffer duration, not only once" This reverts commit d387cf67df91b59540f32ee17b4c02f747969add. The analysis was wrong: The first 20ms of latency are introduced by the source already and put into the latency query, making it only necessary to cover the additional 20ms of audiomixer inside audiomixer. --- gst/audiomixer/gstaudiomixer.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index da0884238d..8881d63ebb 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -826,8 +826,7 @@ gst_audiomixer_init (GstAudioMixer * audiomixer) audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; audiomixer->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - 2 * audiomixer->output_buffer_duration, - 2 * audiomixer->output_buffer_duration); + audiomixer->output_buffer_duration, audiomixer->output_buffer_duration); } static void @@ -878,8 +877,8 @@ gst_audiomixer_set_property (GObject * object, guint prop_id, case PROP_OUTPUT_BUFFER_DURATION: audiomixer->output_buffer_duration = g_value_get_uint64 (value); gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - 2 * audiomixer->output_buffer_duration, - 2 * audiomixer->output_buffer_duration); + audiomixer->output_buffer_duration, + audiomixer->output_buffer_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); From 8227310d223119a28151baca492b995a74d9aa5b Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 12 Mar 2015 01:56:44 +0530 Subject: [PATCH 055/153] audiomixer: Mark a discont when we receive a new segment event This allows us to handle new segment events correctly; either by dropping buffers or inserting silence; for example if the offset is changed on an srcpad connected to audiomixer. --- gst/audiomixer/gstaudiomixer.c | 9 ++++++++- gst/audiomixer/gstaudiomixer.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 8881d63ebb..2165ab70e1 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -675,7 +675,9 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, } case GST_EVENT_SEGMENT: { + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); const GstSegment *segment; + gst_event_parse_segment (event, &segment); if (segment->rate != agg->segment.rate) { GST_ERROR_OBJECT (aggpad, @@ -689,6 +691,10 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, res = FALSE; gst_event_unref (event); event = NULL; + } else { + /* Ideally, this should only be set when the new segment causes running + * times to change, and hence needs discont calculation in fill_buffer */ + pad->new_segment = TRUE; } break; } @@ -1007,8 +1013,9 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, if (GST_BUFFER_IS_DISCONT (inbuf) || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) - || pad->next_offset == -1) { + || pad->new_segment || pad->next_offset == -1) { discont = TRUE; + pad->new_segment = FALSE; } else { guint64 diff, max_sample_diff; diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 9e14582951..9507e6c5d8 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -110,6 +110,9 @@ struct _GstAudioMixerPad { /* Last time we noticed a discont */ GstClockTime discont_time; + + /* A new unhandled segment event has been received */ + gboolean new_segment; }; struct _GstAudioMixerPadClass { From 4e221b7a65fe34d394164ece1ba89c636b1f7e48 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 12 Mar 2015 01:49:58 +0530 Subject: [PATCH 056/153] audiomixer: Add locking to fill_buffer and fix mix_buffer The audiomixer pad struct fields may be changed from other threads --- gst/audiomixer/gstaudiomixer.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 2165ab70e1..973abc7eed 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -675,7 +675,6 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, } case GST_EVENT_SEGMENT: { - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); const GstSegment *segment; gst_event_parse_segment (event, &segment); @@ -692,9 +691,13 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, gst_event_unref (event); event = NULL; } else { + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); + /* Ideally, this should only be set when the new segment causes running * times to change, and hence needs discont calculation in fill_buffer */ + GST_OBJECT_LOCK (pad); pad->new_segment = TRUE; + GST_OBJECT_UNLOCK (pad); } break; } @@ -991,7 +994,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, rate = GST_AUDIO_INFO_RATE (&audiomixer->info); bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - timestamp = GST_BUFFER_TIMESTAMP (inbuf); + timestamp = GST_BUFFER_PTS (inbuf); stream_time = gst_segment_to_stream_time (&aggpad->segment, GST_FORMAT_TIME, timestamp); @@ -1000,6 +1003,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, if (GST_CLOCK_TIME_IS_VALID (stream_time)) gst_object_sync_values (GST_OBJECT (pad), stream_time); + GST_OBJECT_LOCK (pad); pad->position = 0; pad->size = gst_buffer_get_size (inbuf); @@ -1088,6 +1092,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); + GST_OBJECT_UNLOCK (pad); return FALSE; } @@ -1107,6 +1112,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); + GST_OBJECT_UNLOCK (pad); return FALSE; } } @@ -1122,6 +1128,7 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, "Queued new buffer at offset %" G_GUINT64_FORMAT, pad->output_offset); pad->buffer = inbuf; + GST_OBJECT_UNLOCK (pad); return TRUE; } @@ -1145,6 +1152,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + GST_OBJECT_LOCK (pad); /* Overlap => mix */ if (audiomixer->offset < pad->output_offset) out_start = pad->output_offset - audiomixer->offset; @@ -1156,10 +1164,11 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, overlap = blocksize - out_start; inbuf = gst_aggregator_pad_get_buffer (aggpad); - if (inbuf == NULL) + if (inbuf == NULL) { + GST_OBJECT_UNLOCK (pad); return; + } - GST_OBJECT_LOCK (pad); if (pad->mute || pad->volume < G_MINDOUBLE) { GST_DEBUG_OBJECT (pad, "Skipping muted pad"); gst_buffer_unref (inbuf); From 25d8f76ecdc208b93f6b20a053e67cfad64763e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 12 Mar 2015 17:11:31 +0000 Subject: [PATCH 057/153] audiomixer: Fix discont detection and buffer alignment code Actually accumulate the sample counter to check the accumulated error between actual timestamps and expected ones instead of just resetting the error back to 0 with every new buffer. Also don't reset discont_time whenever we don't resync. The whole point of discont_time is to remember when we first detected a discont until we actually act on it a bit later if the discont stayed around for discont_wait time. https://bugzilla.gnome.org/show_bug.cgi?id=746032 --- gst/audiomixer/gstaudiomixer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 973abc7eed..fe1e3e04ec 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1058,12 +1058,11 @@ gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, pad->next_offset, start_offset); pad->output_offset = -1; + pad->next_offset = end_offset; } else { - pad->discont_time = GST_CLOCK_TIME_NONE; + pad->next_offset += pad->size / bpf; } - pad->next_offset = end_offset; - if (pad->output_offset == -1) { GstClockTime start_running_time; GstClockTime end_running_time; From 3f59bc95b8508f52a4fb15f1c91954400e4793d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 6 Mar 2015 20:25:03 -0500 Subject: [PATCH 058/153] audiomixer: Only advance by the buffer size when a buffer is late https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudiomixer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index fe1e3e04ec..f8959234b8 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1465,11 +1465,9 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) gint64 diff = audiomixer->offset - pad->output_offset; gint bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + if (pad->position + (diff * bpf) > pad->size) + diff = (pad->size - pad->position) / bpf; pad->position += diff * bpf; - if (pad->position > pad->size) { - diff = (pad->position - pad->size) / bpf; - pad->position = pad->size; - } pad->output_offset += diff; if (pad->position == pad->size) { From 3b2bc85ec6c60f35c2fd0f8534619a51840a8e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 6 Mar 2015 20:22:13 -0500 Subject: [PATCH 059/153] audiomixer: Only ignore pads with no buffers on timeout When the timeout is reached, only ignore pads with no buffers, iterate over the other pads until all buffers have been read. This is important in the cases where the input buffers are smaller than the output buffer. https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudiomixer.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index f8959234b8..4ea6310350 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1436,8 +1436,20 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) is_eos = FALSE; inbuf = gst_aggregator_pad_get_buffer (aggpad); - if (!inbuf) + if (!inbuf) { + if (timeout) { + if (pad->output_offset < next_offset) { + gint64 diff = next_offset - pad->output_offset; + + GST_LOG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT " frames (%" + GST_TIME_FORMAT ")", diff, + GST_TIME_ARGS (gst_util_uint64_scale (diff, GST_SECOND, rate))); + } + } else if (!gst_aggregator_pad_is_eos (aggpad)) { + is_done = FALSE; + } continue; + } g_assert (!pad->buffer || pad->buffer == inbuf); @@ -1497,14 +1509,14 @@ gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) gst_buffer_unmap (outbuf, &outmap); - if (dropped && !timeout) { + if (dropped) { /* We dropped a buffer, retry */ GST_INFO_OBJECT (audiomixer, "A pad dropped a buffer, wait for the next one"); return GST_FLOW_OK; } - if (!is_done && !is_eos && !timeout) { + if (!is_done && !is_eos) { /* Get more buffers */ GST_INFO_OBJECT (audiomixer, "We're not done yet for the current offset, waiting for more data"); From 66807c14fddd8b8e76a3b55c9b80e8da09125e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 7 Mar 2015 22:08:40 -0500 Subject: [PATCH 060/153] audiomixer: Release pad object lock before dropping buffer Otherwise, the locking order is violated and deadlocks happen. https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudiomixer.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 4ea6310350..d1daff6526 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -1141,6 +1141,7 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, GstMapInfo inmap; gint bpf; guint blocksize; + gboolean drop_buf = FALSE; GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); @@ -1176,9 +1177,11 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, if (pad->position >= pad->size) { /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - gst_aggregator_pad_drop_buffer (aggpad); + drop_buf = TRUE; } GST_OBJECT_UNLOCK (pad); + if (drop_buf) + gst_aggregator_pad_drop_buffer (aggpad); return; } @@ -1189,8 +1192,8 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, pad->output_offset += pad->size / bpf; /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - gst_aggregator_pad_drop_buffer (aggpad); GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); return; } @@ -1300,11 +1303,14 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, if (pad->position == pad->size) { /* Buffer done, drop it */ gst_buffer_replace (&pad->buffer, NULL); - gst_aggregator_pad_drop_buffer (aggpad); GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); + drop_buf = TRUE; } GST_OBJECT_UNLOCK (pad); + + if (drop_buf) + gst_aggregator_pad_drop_buffer (aggpad); } static GstFlowReturn From 1eef58c3ce9c94e462cdb6f6fc4381cad844ebbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 6 Nov 2014 17:15:17 -0500 Subject: [PATCH 061/153] audioaggregator: Split base class from audiomixer Also: - Don't modify size on early buffer The size is the size of the buffer, not of remaining part. - Use the input caps when manipulating the input buffer Also store in in the sink pad - Reply to the position query in bytes too - Put GAP flag on output if all inputs are GAP data - Only try to clip buffer if the incoming segment is in time or samples - Use incoming segment with incoming timestamp Handle non-time segments and NONE timestamps - Don't reset the position when pushing out new caps - Make a number of member variables private - Correctly handle case where no pad has a buffer If none of the pads have buffers that can be handled, don't claim to be EOS. - Ensure proper locking - Only support time segments https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- gst/audiomixer/Makefile.am | 4 +- gst/audiomixer/gstaudioaggregator.c | 1270 +++++++++++++++++++++++++++ gst/audiomixer/gstaudioaggregator.h | 171 ++++ gst/audiomixer/gstaudiomixer.c | 1087 ++--------------------- gst/audiomixer/gstaudiomixer.h | 47 +- 5 files changed, 1543 insertions(+), 1036 deletions(-) create mode 100644 gst/audiomixer/gstaudioaggregator.c create mode 100644 gst/audiomixer/gstaudioaggregator.h diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index 2a3051ddb1..b98ae8adba 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 +libgstaudiomixer_la_SOURCES = gstaudiomixer.c gstaudioaggregator.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 +noinst_HEADERS = gstaudiomixer.h gstaudioaggregator.h diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c new file mode 100644 index 0000000000..5939c1dc92 --- /dev/null +++ b/gst/audiomixer/gstaudioaggregator.c @@ -0,0 +1,1270 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2001 Thomas + * 2005,2006 Wim Taymans + * 2013 Sebastian Dröge + * 2014 Collabora + * Olivier Crete + * + * gstaudioaggregator.c: + * + * 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: gstaudioaggregator + * @short_description: manages a set of pads with the purpose of + * aggregating their buffers for raw audio + * @see_also: #GstAggregator + * + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstaudioaggregator.h" + +#include + +GST_DEBUG_CATEGORY_STATIC (audio_aggregator_debug); +#define GST_CAT_DEFAULT audio_aggregator_debug + +struct _GstAudioAggregatorPadPrivate +{ + /* All members are protected by the pad object lock */ + + GstBuffer *buffer; /* current buffer we're mixing, + for comparison with collect.buffer + to see if we need to update our + cached values. */ + guint position, size; + + guint64 output_offset; /* Offset in output segment that + collect.pos refers to in the + current buffer. */ + + guint64 next_offset; /* Next expected offset in the input segment */ + + /* Last time we noticed a discont */ + GstClockTime discont_time; + + /* A new unhandled segment event has been received */ + gboolean new_segment; +}; + + +/***************************************** + * GstAudioAggregatorPad implementation * + *****************************************/ +G_DEFINE_TYPE (GstAudioAggregatorPad, gst_audio_aggregator_pad, + GST_TYPE_AGGREGATOR_PAD); + +static gboolean +gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, + GstAggregator * aggregator); + +static void +gst_audio_aggregator_pad_class_init (GstAudioAggregatorPadClass * klass) +{ + GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass; + + g_type_class_add_private (klass, sizeof (GstAudioAggregatorPadPrivate)); + + aggpadclass->flush = GST_DEBUG_FUNCPTR (gst_audio_aggregator_pad_flush_pad); +} + +static void +gst_audio_aggregator_pad_init (GstAudioAggregatorPad * pad) +{ + pad->priv = + G_TYPE_INSTANCE_GET_PRIVATE (pad, GST_TYPE_AUDIO_AGGREGATOR_PAD, + GstAudioAggregatorPadPrivate); + + gst_audio_info_init (&pad->info); + + pad->priv->buffer = NULL; + pad->priv->position = 0; + pad->priv->size = 0; + pad->priv->output_offset = -1; + pad->priv->next_offset = -1; + pad->priv->discont_time = GST_CLOCK_TIME_NONE; +} + + +static gboolean +gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, + GstAggregator * aggregator) +{ + GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (aggpad); + + GST_OBJECT_LOCK (aggpad); + pad->priv->position = pad->priv->size = 0; + pad->priv->output_offset = pad->priv->next_offset = -1; + pad->priv->discont_time = GST_CLOCK_TIME_NONE; + gst_buffer_replace (&pad->priv->buffer, NULL); + GST_OBJECT_UNLOCK (aggpad); + + return TRUE; +} + + + +/************************************** + * GstAudioAggregator implementation * + **************************************/ + +struct _GstAudioAggregatorPrivate +{ + GMutex mutex; + + gboolean send_caps; /* aagg lock */ + + /* All three properties are unprotected, can't be modified while streaming */ + /* Size in frames that is output per buffer */ + GstClockTime output_buffer_duration; + GstClockTime alignment_threshold; + GstClockTime discont_wait; + + /* Protected by srcpad stream clock */ + /* Buffer starting at offset containing block_size frames */ + GstBuffer *current_buffer; + + /* counters to keep track of timestamps */ + /* Readable with object lock, writable with both aag lock and object lock */ + gint64 offset; +}; + +#define GST_AUDIO_AGGREGATOR_LOCK(self) g_mutex_lock (&(self)->priv->mutex); +#define GST_AUDIO_AGGREGATOR_UNLOCK(self) g_mutex_unlock (&(self)->priv->mutex); + +static void gst_audio_aggregator_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audio_aggregator_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_audio_aggregator_dispose (GObject * object); + +static gboolean gst_audio_aggregator_src_event (GstAggregator * agg, + GstEvent * event); +static gboolean gst_audio_aggregator_sink_event (GstAggregator * agg, + GstAggregatorPad * aggpad, GstEvent * event); +static gboolean gst_audio_aggregator_src_query (GstAggregator * agg, + GstQuery * query); +static gboolean gst_audio_aggregator_start (GstAggregator * agg); +static gboolean gst_audio_aggregator_stop (GstAggregator * agg); +static GstFlowReturn gst_audio_aggregator_flush (GstAggregator * agg); + +static GstBuffer *gst_audio_aggregator_create_output_buffer (GstAudioAggregator + * aagg, guint num_frames); +static GstFlowReturn gst_audio_aggregator_do_clip (GstAggregator * agg, + GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); +static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, + gboolean timeout); + +#define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) +#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) +#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) + +enum +{ + PROP_0, + PROP_OUTPUT_BUFFER_DURATION, + PROP_ALIGNMENT_THRESHOLD, + PROP_DISCONT_WAIT, +}; + +G_DEFINE_ABSTRACT_TYPE (GstAudioAggregator, gst_audio_aggregator, + GST_TYPE_AGGREGATOR); + +static GstClockTime +gst_audio_aggregator_get_next_time (GstAggregator * agg) +{ + GstClockTime next_time; + + GST_OBJECT_LOCK (agg); + if (agg->segment.position == -1) + next_time = agg->segment.start; + else + next_time = agg->segment.position; + GST_OBJECT_UNLOCK (agg); + + return next_time; +} + +static void +gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstAggregatorClass *gstaggregator_class = (GstAggregatorClass *) klass; + + g_type_class_add_private (klass, sizeof (GstAudioAggregatorPrivate)); + + gobject_class->set_property = gst_audio_aggregator_set_property; + gobject_class->get_property = gst_audio_aggregator_get_property; + gobject_class->dispose = gst_audio_aggregator_dispose; + + gstaggregator_class->src_event = + GST_DEBUG_FUNCPTR (gst_audio_aggregator_src_event); + gstaggregator_class->sink_event = + GST_DEBUG_FUNCPTR (gst_audio_aggregator_sink_event); + gstaggregator_class->src_query = + GST_DEBUG_FUNCPTR (gst_audio_aggregator_src_query); + gstaggregator_class->start = gst_audio_aggregator_start; + gstaggregator_class->stop = gst_audio_aggregator_stop; + gstaggregator_class->flush = gst_audio_aggregator_flush; + gstaggregator_class->aggregate = + GST_DEBUG_FUNCPTR (gst_audio_aggregator_aggregate); + gstaggregator_class->clip = GST_DEBUG_FUNCPTR (gst_audio_aggregator_do_clip); + gstaggregator_class->get_next_time = gst_audio_aggregator_get_next_time; + + klass->create_output_buffer = gst_audio_aggregator_create_output_buffer; + + GST_DEBUG_CATEGORY_INIT (audio_aggregator_debug, "audioaggregator", + GST_DEBUG_FG_MAGENTA, "GstAudioAggregator"); + + g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION, + g_param_spec_uint64 ("output-buffer-duration", "Output Buffer Duration", + "Output block size in nanoseconds", 1, + G_MAXUINT64, DEFAULT_OUTPUT_BUFFER_DURATION, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, + g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", + "Timestamp alignment threshold in nanoseconds", 0, + G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, + g_param_spec_uint64 ("discont-wait", "Discont Wait", + "Window of time in nanoseconds to wait before " + "creating a discontinuity", 0, + G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_audio_aggregator_init (GstAudioAggregator * aagg) +{ + aagg->priv = + G_TYPE_INSTANCE_GET_PRIVATE (aagg, GST_TYPE_AUDIO_AGGREGATOR, + GstAudioAggregatorPrivate); + + g_mutex_init (&aagg->priv->mutex); + + aagg->priv->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; + aagg->priv->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; + aagg->priv->discont_wait = DEFAULT_DISCONT_WAIT; + + aagg->current_caps = NULL; + gst_audio_info_init (&aagg->info); + + gst_aggregator_set_latency (GST_AGGREGATOR (aagg), + aagg->priv->output_buffer_duration, aagg->priv->output_buffer_duration); +} + +static void +gst_audio_aggregator_dispose (GObject * object) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (object); + + gst_caps_replace (&aagg->current_caps, NULL); + + g_mutex_clear (&aagg->priv->mutex); + + G_OBJECT_CLASS (gst_audio_aggregator_parent_class)->dispose (object); +} + +static void +gst_audio_aggregator_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (object); + + switch (prop_id) { + case PROP_OUTPUT_BUFFER_DURATION: + aagg->priv->output_buffer_duration = g_value_get_uint64 (value); + gst_aggregator_set_latency (GST_AGGREGATOR (aagg), + aagg->priv->output_buffer_duration, + aagg->priv->output_buffer_duration); + break; + case PROP_ALIGNMENT_THRESHOLD: + aagg->priv->alignment_threshold = g_value_get_uint64 (value); + break; + case PROP_DISCONT_WAIT: + aagg->priv->discont_wait = g_value_get_uint64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_aggregator_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (object); + + switch (prop_id) { + case PROP_OUTPUT_BUFFER_DURATION: + g_value_set_uint64 (value, aagg->priv->output_buffer_duration); + break; + case PROP_ALIGNMENT_THRESHOLD: + g_value_set_uint64 (value, aagg->priv->alignment_threshold); + break; + case PROP_DISCONT_WAIT: + g_value_set_uint64 (value, aagg->priv->discont_wait); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/* event handling */ + +static gboolean +gst_audio_aggregator_src_event (GstAggregator * agg, GstEvent * event) +{ + gboolean result; + + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + GST_DEBUG_OBJECT (agg->srcpad, "Got %s event on src pad", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_QOS: + /* QoS might be tricky */ + gst_event_unref (event); + return FALSE; + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + gst_event_unref (event); + return FALSE; + break; + case GST_EVENT_SEEK: + { + GstSeekFlags flags; + gdouble rate; + GstSeekType start_type, stop_type; + gint64 start, stop; + GstFormat seek_format, dest_format; + + /* parse the seek parameters */ + gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, + &start, &stop_type, &stop); + + /* Check the seeking parametters before linking up */ + if ((start_type != GST_SEEK_TYPE_NONE) + && (start_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (aagg, + "seeking failed, unhandled seek type for start: %d", start_type); + goto done; + } + if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (aagg, + "seeking failed, unhandled seek type for end: %d", stop_type); + goto done; + } + + GST_OBJECT_LOCK (agg); + dest_format = agg->segment.format; + GST_OBJECT_UNLOCK (agg); + if (seek_format != dest_format) { + result = FALSE; + GST_DEBUG_OBJECT (aagg, + "seeking failed, unhandled seek format: %s", + gst_format_get_name (seek_format)); + goto done; + } + } + break; + default: + break; + } + + return + GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->src_event (agg, + event); + +done: + return result; +} + + +static gboolean +gst_audio_aggregator_sink_event (GstAggregator * agg, + GstAggregatorPad * aggpad, GstEvent * event) +{ + 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_SEGMENT: + { + const GstSegment *segment; + gst_event_parse_segment (event, &segment); + + if (segment->format != GST_FORMAT_TIME) { + GST_ERROR_OBJECT (agg, "Segment of type %s are not supported," + " only TIME segments are supported", + gst_format_get_name (segment->format)); + gst_event_unref (event); + event = NULL; + res = FALSE; + break; + } + + GST_OBJECT_LOCK (agg); + if (segment->rate != agg->segment.rate) { + GST_ERROR_OBJECT (aggpad, + "Got segment event with wrong rate %lf, expected %lf", + segment->rate, agg->segment.rate); + res = FALSE; + gst_event_unref (event); + event = NULL; + } else if (segment->rate < 0.0) { + GST_ERROR_OBJECT (aggpad, "Negative rates not supported yet"); + res = FALSE; + gst_event_unref (event); + event = NULL; + } else { + GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (aggpad); + + GST_OBJECT_LOCK (pad); + pad->priv->new_segment = TRUE; + GST_OBJECT_UNLOCK (pad); + } + GST_OBJECT_UNLOCK (agg); + + break; + } + default: + break; + } + + if (event != NULL) + return + GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->sink_event + (agg, aggpad, event); + + return res; +} + +/* FIXME, the duration query should reflect how long you will produce + * data, that is the amount of stream time until you will emit EOS. + * + * For synchronized mixing this is always the max of all the durations + * of upstream since we emit EOS when all of them finished. + * + * We don't do synchronized mixing so this really depends on where the + * streams where punched in and what their relative offsets are against + * eachother which we can get from the first timestamps we see. + * + * When we add a new stream (or remove a stream) the duration might + * also become invalid again and we need to post a new DURATION + * message to notify this fact to the parent. + * For now we take the max of all the upstream elements so the simple + * cases work at least somewhat. + */ +static gboolean +gst_audio_aggregator_query_duration (GstAudioAggregator * aagg, + GstQuery * query) +{ + gint64 max; + gboolean res; + GstFormat format; + GstIterator *it; + gboolean done; + GValue item = { 0, }; + + /* parse format */ + gst_query_parse_duration (query, &format, NULL); + + max = -1; + res = TRUE; + done = FALSE; + + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (aagg)); + while (!done) { + GstIteratorResult ires; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + gint64 duration; + + /* ask sink peer for duration */ + res &= gst_pad_peer_query_duration (pad, format, &duration); + /* take max from all valid return values */ + if (res) { + /* valid unknown length, stop searching */ + if (duration == -1) { + max = duration; + done = TRUE; + } + /* else see if bigger than current max */ + else if (duration > max) + max = duration; + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (aagg, "Total duration in format %s: %" + GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); + gst_query_set_duration (query, format, max); + } + + return res; +} + + +static gboolean +gst_audio_aggregator_src_query (GstAggregator * agg, GstQuery * query) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + res = gst_audio_aggregator_query_duration (aagg, query); + break; + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + GST_OBJECT_LOCK (aagg); + + switch (format) { + case GST_FORMAT_TIME: + /* FIXME, bring to stream time, might be tricky */ + gst_query_set_position (query, format, agg->segment.position); + res = TRUE; + break; + case GST_FORMAT_BYTES: + if (GST_AUDIO_INFO_BPF (&aagg->info)) { + gst_query_set_position (query, format, aagg->priv->offset * + GST_AUDIO_INFO_BPF (&aagg->info)); + res = TRUE; + } + break; + case GST_FORMAT_DEFAULT: + gst_query_set_position (query, format, aagg->priv->offset); + res = TRUE; + break; + default: + break; + } + + GST_OBJECT_UNLOCK (aagg); + + break; + } + default: + res = + GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->src_query + (agg, query); + break; + } + + return res; +} + + +void +gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstCaps * caps) +{ + GST_OBJECT_LOCK (pad); + gst_audio_info_from_caps (&pad->info, caps); + GST_OBJECT_UNLOCK (pad); +} + + +gboolean +gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps) +{ + GstAudioInfo info; + + if (!gst_audio_info_from_caps (&info, caps)) { + GST_WARNING_OBJECT (aagg, "Rejecting invalid caps: %" GST_PTR_FORMAT, caps); + return FALSE; + } + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (aagg); + + GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps); + gst_caps_replace (&aagg->current_caps, caps); + + memcpy (&aagg->info, &info, sizeof (info)); + aagg->priv->send_caps = TRUE; + + GST_OBJECT_UNLOCK (aagg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + /* send caps event later, after stream-start event */ + + return TRUE; +} + + +/* Must hold object lock and aagg lock to call */ + +static void +gst_audio_aggregator_reset (GstAudioAggregator * aagg) +{ + GstAggregator *agg = GST_AGGREGATOR (aagg); + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (aagg); + agg->segment.position = -1; + aagg->priv->offset = 0; + gst_audio_info_init (&aagg->info); + gst_caps_replace (&aagg->current_caps, NULL); + gst_buffer_replace (&aagg->priv->current_buffer, NULL); + GST_OBJECT_UNLOCK (aagg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); +} + +static gboolean +gst_audio_aggregator_start (GstAggregator * agg) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + + gst_audio_aggregator_reset (aagg); + + return TRUE; +} + +static gboolean +gst_audio_aggregator_stop (GstAggregator * agg) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + + gst_audio_aggregator_reset (aagg); + + return TRUE; +} + +static GstFlowReturn +gst_audio_aggregator_flush (GstAggregator * agg) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (aagg); + agg->segment.position = -1; + aagg->priv->offset = 0; + gst_buffer_replace (&aagg->priv->current_buffer, NULL); + GST_OBJECT_UNLOCK (aagg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_audio_aggregator_do_clip (GstAggregator * agg, + GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** out) +{ + GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (bpad); + gint rate, bpf; + + + rate = GST_AUDIO_INFO_RATE (&pad->info); + bpf = GST_AUDIO_INFO_BPF (&pad->info); + + GST_OBJECT_LOCK (bpad); + *out = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); + GST_OBJECT_UNLOCK (bpad); + + return GST_FLOW_OK; +} + +/* Called with the object lock for both the element and pad held, + * as well as the aagg lock + */ +static gboolean +gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstBuffer * inbuf) +{ + GstClockTime start_time, end_time; + gboolean discont = FALSE; + guint64 start_offset, end_offset; + GstClockTime timestamp, stream_time = GST_CLOCK_TIME_NONE; + gint rate, bpf; + + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); + + g_assert (pad->priv->buffer == NULL); + + rate = GST_AUDIO_INFO_RATE (&pad->info); + bpf = GST_AUDIO_INFO_BPF (&pad->info); + + pad->priv->position = 0; + pad->priv->size = gst_buffer_get_size (inbuf) / bpf; + + if (!GST_BUFFER_PTS_IS_VALID (inbuf)) { + if (pad->priv->output_offset == -1) + pad->priv->output_offset = aagg->priv->offset; + if (pad->priv->next_offset == -1) + pad->priv->next_offset = pad->priv->size; + else + pad->priv->next_offset += pad->priv->size; + goto done; + } + + timestamp = GST_BUFFER_PTS (inbuf); + stream_time = gst_segment_to_stream_time (&aggpad->segment, GST_FORMAT_TIME, + timestamp); + + /* sync object properties on stream time */ + /* TODO: Ideally we would want to do that on every sample */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (GST_OBJECT (pad), stream_time); + + start_time = GST_BUFFER_PTS (inbuf); + end_time = + start_time + gst_util_uint64_scale_ceil (pad->priv->size, GST_SECOND, + rate); + + start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); + end_offset = start_offset + pad->priv->size; + + if (GST_BUFFER_IS_DISCONT (inbuf) + || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) + || pad->priv->new_segment || pad->priv->next_offset == -1) { + discont = TRUE; + pad->priv->new_segment = FALSE; + } else { + guint64 diff, max_sample_diff; + + /* Check discont, based on audiobasesink */ + if (start_offset <= pad->priv->next_offset) + diff = pad->priv->next_offset - start_offset; + else + diff = start_offset - pad->priv->next_offset; + + max_sample_diff = + gst_util_uint64_scale_int (aagg->priv->alignment_threshold, rate, + GST_SECOND); + + /* Discont! */ + if (G_UNLIKELY (diff >= max_sample_diff)) { + if (aagg->priv->discont_wait > 0) { + if (pad->priv->discont_time == GST_CLOCK_TIME_NONE) { + pad->priv->discont_time = start_time; + } else if (start_time - pad->priv->discont_time >= + aagg->priv->discont_wait) { + discont = TRUE; + pad->priv->discont_time = GST_CLOCK_TIME_NONE; + } + } else { + discont = TRUE; + } + } else if (G_UNLIKELY (pad->priv->discont_time != GST_CLOCK_TIME_NONE)) { + /* we have had a discont, but are now back on track! */ + pad->priv->discont_time = GST_CLOCK_TIME_NONE; + } + } + + if (discont) { + /* Have discont, need resync */ + if (pad->priv->next_offset != -1) + GST_INFO_OBJECT (pad, "Have discont. Expected %" + G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, + pad->priv->next_offset, start_offset); + pad->priv->output_offset = -1; + pad->priv->next_offset = end_offset; + } else { + pad->priv->next_offset += pad->priv->size; + } + + if (pad->priv->output_offset == -1) { + GstClockTime start_running_time; + GstClockTime end_running_time; + guint64 start_running_time_offset; + guint64 end_running_time_offset; + + start_running_time = + gst_segment_to_running_time (&aggpad->segment, + GST_FORMAT_TIME, start_time); + end_running_time = + gst_segment_to_running_time (&aggpad->segment, + GST_FORMAT_TIME, end_time); + start_running_time_offset = + gst_util_uint64_scale (start_running_time, rate, GST_SECOND); + end_running_time_offset = + gst_util_uint64_scale (end_running_time, rate, GST_SECOND); + + if (end_running_time_offset < aagg->priv->offset) { + /* Before output segment, drop */ + gst_buffer_unref (inbuf); + pad->priv->buffer = NULL; + pad->priv->position = 0; + pad->priv->size = 0; + pad->priv->output_offset = -1; + GST_DEBUG_OBJECT (pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" + G_GUINT64_FORMAT, end_running_time_offset, aagg->priv->offset); + return FALSE; + } + + if (start_running_time_offset < aagg->priv->offset) { + guint diff = aagg->priv->offset - start_running_time_offset; + + pad->priv->position += diff; + if (pad->priv->position >= pad->priv->size) { + /* Empty buffer, drop */ + gst_buffer_unref (inbuf); + pad->priv->buffer = NULL; + pad->priv->position = 0; + pad->priv->size = 0; + pad->priv->output_offset = -1; + GST_DEBUG_OBJECT (pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT + " < %" G_GUINT64_FORMAT, end_running_time_offset, + aagg->priv->offset); + return FALSE; + } + } + + pad->priv->output_offset = + MAX (start_running_time_offset, aagg->priv->offset); + GST_DEBUG_OBJECT (pad, + "Buffer resynced: Pad offset %" G_GUINT64_FORMAT + ", current audio aggregator offset %" G_GUINT64_FORMAT, + pad->priv->output_offset, aagg->priv->offset); + } + +done: + + GST_LOG_OBJECT (pad, + "Queued new buffer at offset %" G_GUINT64_FORMAT, + pad->priv->output_offset); + pad->priv->buffer = inbuf; + + return TRUE; +} + +/* Called with pad object lock held */ + +static gboolean +gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstBuffer * inbuf, GstBuffer * outbuf) +{ + guint overlap; + guint out_start; + gboolean filled; + guint blocksize; + + blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, + GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); + blocksize = MAX (1, blocksize); + + /* Overlap => mix */ + if (aagg->priv->offset < pad->priv->output_offset) + out_start = pad->priv->output_offset - aagg->priv->offset; + else + out_start = 0; + + overlap = pad->priv->size - pad->priv->position; + if (overlap > blocksize - out_start) + overlap = blocksize - out_start; + + if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + /* skip gap buffer */ + GST_LOG_OBJECT (pad, "skipping GAP buffer"); + pad->priv->output_offset += pad->priv->size; + pad->priv->position = pad->priv->size; + + gst_buffer_replace (&pad->priv->buffer, NULL); + return FALSE; + } + + filled = GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->aggregate_one_buffer (aagg, + pad, inbuf, pad->priv->position, outbuf, out_start, overlap); + + if (filled) + GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP); + + pad->priv->position += overlap; + pad->priv->output_offset += overlap; + + if (pad->priv->position == pad->priv->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&pad->priv->buffer, NULL); + GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); + return FALSE; + } + + return TRUE; +} + +static GstBuffer * +gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, + guint num_frames) +{ + GstBuffer *outbuf = gst_buffer_new_and_alloc (num_frames * + GST_AUDIO_INFO_BPF (&aagg->info)); + GstMapInfo outmap; + + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + gst_audio_format_fill_silence (aagg->info.finfo, outmap.data, outmap.size); + gst_buffer_unmap (outbuf, &outmap); + + return outbuf; +} + +static GstFlowReturn +gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) +{ + /* Get all pads that have data for us and store them in a + * new list. + * + * Calculate the current output offset/timestamp and + * offset_end/timestamp_end. Allocate a silence buffer + * for this and store it. + * + * For all pads: + * 1) Once per input buffer (cached) + * 1) Check discont (flag and timestamp with tolerance) + * 2) If discont or new, resync. That means: + * 1) Drop all start data of the buffer that comes before + * the current position/offset. + * 2) Calculate the offset (output segment!) that the first + * frame of the input buffer corresponds to. Base this on + * the running time. + * + * 2) If the current pad's offset/offset_end overlaps with the output + * offset/offset_end, mix it at the appropiate position in the output + * buffer and advance the pad's position. Remember if this pad needs + * a new buffer to advance behind the output offset_end. + * + * 3) If we had no pad with a buffer, go EOS. + * + * 4) If we had at least one pad that did not advance behind output + * offset_end, let collected be called again for the current + * output offset/offset_end. + */ + GstElement *element; + GstAudioAggregator *aagg; + GList *iter; + GstFlowReturn ret; + GstBuffer *outbuf = NULL; + gint64 next_offset; + gint64 next_timestamp; + gint rate, bpf; + gboolean dropped = FALSE; + gboolean is_eos = TRUE; + gboolean is_done = TRUE; + guint blocksize; + + element = GST_ELEMENT (agg); + aagg = GST_AUDIO_AGGREGATOR (agg); + + blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, + GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); + blocksize = MAX (1, blocksize); + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (agg); + + /* Update position from the segment start/stop if needed */ + if (agg->segment.position == -1) { + if (agg->segment.rate > 0.0) + agg->segment.position = agg->segment.start; + else + agg->segment.position = agg->segment.stop; + } + + if (G_UNLIKELY (aagg->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) { + if (timeout) { + GST_DEBUG_OBJECT (aagg, + "Got timeout before receiving any caps, don't output anything"); + + /* Advance position */ + if (agg->segment.rate > 0.0) + agg->segment.position += aagg->priv->output_buffer_duration; + else if (agg->segment.position > aagg->priv->output_buffer_duration) + agg->segment.position -= aagg->priv->output_buffer_duration; + else + agg->segment.position = 0; + + GST_OBJECT_UNLOCK (agg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + return GST_FLOW_OK; + } else { + GST_OBJECT_UNLOCK (agg); + goto not_negotiated; + } + } + + if (aagg->priv->send_caps) { + GST_OBJECT_UNLOCK (agg); + gst_aggregator_set_src_caps (agg, aagg->current_caps); + GST_OBJECT_LOCK (agg); + aagg->priv->offset = gst_util_uint64_scale (agg->segment.position, + GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); + + aagg->priv->send_caps = FALSE; + } + + + rate = GST_AUDIO_INFO_RATE (&aagg->info); + bpf = GST_AUDIO_INFO_BPF (&aagg->info); + + + /* for the next timestamp, use the sample counter, which will + * never accumulate rounding errors */ + + /* FIXME: Reverse mixing does not work at all yet */ + if (agg->segment.rate > 0.0) { + next_offset = aagg->priv->offset + blocksize; + } else { + next_offset = aagg->priv->offset - blocksize; + } + + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + + if (aagg->priv->current_buffer == NULL) { + GST_OBJECT_UNLOCK (agg); + aagg->priv->current_buffer = + GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->create_output_buffer (aagg, + blocksize); + /* Be careful, some things could have changed ? */ + GST_OBJECT_LOCK (agg); + GST_BUFFER_FLAG_SET (aagg->priv->current_buffer, GST_BUFFER_FLAG_GAP); + } + outbuf = aagg->priv->current_buffer; + + GST_LOG_OBJECT (agg, + "Starting to mix %u samples for offset %" G_GUINT64_FORMAT + " with timestamp %" GST_TIME_FORMAT, blocksize, + aagg->priv->offset, GST_TIME_ARGS (agg->segment.position)); + + for (iter = element->sinkpads; iter; iter = iter->next) { + GstBuffer *inbuf; + GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) iter->data; + GstAggregatorPad *aggpad = (GstAggregatorPad *) iter->data; + gboolean drop_buf = FALSE; + gboolean pad_eos = gst_aggregator_pad_is_eos (aggpad); + + if (!pad_eos) + is_eos = FALSE; + + inbuf = gst_aggregator_pad_get_buffer (aggpad); + + GST_OBJECT_LOCK (pad); + if (!inbuf) { + if (timeout) { + if (pad->priv->output_offset < next_offset) { + gint64 diff = next_offset - pad->priv->output_offset; + GST_LOG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT " frames (%" + GST_TIME_FORMAT ")", diff, + GST_TIME_ARGS (gst_util_uint64_scale (diff, GST_SECOND, + GST_AUDIO_INFO_RATE (&aagg->info)))); + } + } else if (!pad_eos) { + is_done = FALSE; + } + GST_OBJECT_UNLOCK (pad); + continue; + } + + g_assert (!pad->priv->buffer || pad->priv->buffer == inbuf); + + /* New buffer? */ + if (!pad->priv->buffer) { + /* Takes ownership of buffer */ + if (!gst_audio_aggregator_fill_buffer (aagg, pad, inbuf)) { + dropped = TRUE; + GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); + continue; + } + } else { + gst_buffer_unref (inbuf); + } + + if (!pad->priv->buffer && !dropped && pad_eos) { + GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); + GST_OBJECT_UNLOCK (pad); + continue; + } + + g_assert (pad->priv->buffer); + + /* This pad is lacking behind, we need to update the offset + * and maybe drop the current buffer */ + if (pad->priv->output_offset < aagg->priv->offset) { + gint64 diff = aagg->priv->offset - pad->priv->output_offset; + + if (pad->priv->position + diff > pad->priv->size) + diff = pad->priv->size - pad->priv->position; + pad->priv->position += diff; + pad->priv->output_offset += diff; + + if (pad->priv->position == pad->priv->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&pad->priv->buffer, NULL); + dropped = TRUE; + GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); + continue; + } + } + + + if (pad->priv->output_offset >= aagg->priv->offset + && pad->priv->output_offset < + aagg->priv->offset + blocksize && pad->priv->buffer) { + GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); + drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer, + outbuf); + if (pad->priv->output_offset >= next_offset) { + GST_DEBUG_OBJECT (pad, + "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" + G_GUINT64_FORMAT, pad->priv->output_offset, next_offset); + } else { + is_done = FALSE; + } + } + + GST_OBJECT_UNLOCK (pad); + if (drop_buf) + gst_aggregator_pad_drop_buffer (aggpad); + + } + GST_OBJECT_UNLOCK (agg); + + if (dropped) { + /* We dropped a buffer, retry */ + GST_INFO_OBJECT (aagg, "A pad dropped a buffer, wait for the next one"); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + return GST_FLOW_OK; + } + + if (!is_done && !is_eos) { + /* Get more buffers */ + GST_INFO_OBJECT (aagg, + "We're not done yet for the current offset," " waiting for more data"); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + return GST_FLOW_OK; + } + + if (is_eos) { + gint64 max_offset = 0; + + GST_DEBUG_OBJECT (aagg, "We're EOS"); + + GST_OBJECT_LOCK (agg); + for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { + GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (iter->data); + + max_offset = MAX ((gint64) max_offset, (gint64) pad->priv->output_offset); + } + GST_OBJECT_UNLOCK (agg); + + /* This means EOS or nothing mixed in at all */ + if (aagg->priv->offset == max_offset) { + gst_buffer_replace (&aagg->priv->current_buffer, NULL); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + return GST_FLOW_EOS; + } + + if (max_offset <= next_offset) { + GST_DEBUG_OBJECT (aagg, + "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" + G_GUINT64_FORMAT, max_offset, next_offset); + next_offset = max_offset; + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + + if (next_offset > aagg->priv->offset) + gst_buffer_resize (outbuf, 0, (next_offset - aagg->priv->offset) * bpf); + } + } + + /* set timestamps on the output buffer */ + GST_OBJECT_LOCK (agg); + if (agg->segment.rate > 0.0) { + GST_BUFFER_PTS (outbuf) = agg->segment.position; + GST_BUFFER_OFFSET (outbuf) = aagg->priv->offset; + GST_BUFFER_OFFSET_END (outbuf) = next_offset; + GST_BUFFER_DURATION (outbuf) = next_timestamp - agg->segment.position; + } else { + GST_BUFFER_PTS (outbuf) = next_timestamp; + GST_BUFFER_OFFSET (outbuf) = next_offset; + GST_BUFFER_OFFSET_END (outbuf) = aagg->priv->offset; + GST_BUFFER_DURATION (outbuf) = agg->segment.position - next_timestamp; + } + + aagg->priv->offset = next_offset; + agg->segment.position = next_timestamp; + + GST_OBJECT_UNLOCK (agg); + + /* send it out */ + GST_LOG_OBJECT (aagg, + "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)), + GST_BUFFER_OFFSET (outbuf)); + + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + ret = gst_aggregator_finish_buffer (agg, aagg->priv->current_buffer); + aagg->priv->current_buffer = NULL; + + GST_LOG_OBJECT (aagg, "pushed outbuf, result = %s", gst_flow_get_name (ret)); + + return ret; + /* ERRORS */ +not_negotiated: + { + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + GST_ELEMENT_ERROR (aagg, STREAM, FORMAT, (NULL), + ("Unknown data received, not negotiated")); + return GST_FLOW_NOT_NEGOTIATED; + } +} diff --git a/gst/audiomixer/gstaudioaggregator.h b/gst/audiomixer/gstaudioaggregator.h new file mode 100644 index 0000000000..304bad2871 --- /dev/null +++ b/gst/audiomixer/gstaudioaggregator.h @@ -0,0 +1,171 @@ +/* GStreamer + * Copyright (C) 2014 Collabora + * Author: Olivier Crete + * + * gstaudioaggregator.h: + * + * 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_AGGREGATOR_H__ +#define __GST_AUDIO_AGGREGATOR_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 +#include +#include + +G_BEGIN_DECLS + +/******************************* + * GstAudioAggregator Structs * + *******************************/ + +typedef struct _GstAudioAggregator GstAudioAggregator; +typedef struct _GstAudioAggregatorPrivate GstAudioAggregatorPrivate; +typedef struct _GstAudioAggregatorClass GstAudioAggregatorClass; + + +/************************ + * GstAudioAggregatorPad API * + ***********************/ + +#define GST_TYPE_AUDIO_AGGREGATOR_PAD (gst_audio_aggregator_pad_get_type()) +#define GST_AUDIO_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_AGGREGATOR_PAD, GstAudioAggregatorPad)) +#define GST_AUDIO_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_AGGREGATOR_PAD, GstAudioAggregatorPadClass)) +#define GST_AUDIO_AGGREGATOR_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIO_AGGREGATOR_PAD, GstAudioAggregatorPadClass)) +#define GST_IS_AUDIO_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_AGGREGATOR_PAD)) +#define GST_IS_AUDIO_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_AGGREGATOR_PAD)) + +/**************************** + * GstAudioAggregatorPad Structs * + ***************************/ + +typedef struct _GstAudioAggregatorPad GstAudioAggregatorPad; +typedef struct _GstAudioAggregatorPadClass GstAudioAggregatorPadClass; +typedef struct _GstAudioAggregatorPadPrivate GstAudioAggregatorPadPrivate; + +/** + * GstAudioAggregatorPad: + * @parent: The parent #GstAggregatorPad + * @info: The audio info for this pad set from the incoming caps + * + * The implementation the GstPad to use with #GstAudioAggregator + */ +struct _GstAudioAggregatorPad +{ + GstAggregatorPad parent; + + GstAudioInfo info; + + /*< private >*/ + GstAudioAggregatorPadPrivate * priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstAudioAggregatorPadClass: + * + */ +struct _GstAudioAggregatorPadClass +{ + GstAggregatorPadClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_audio_aggregator_pad_get_type (void); + +/************************** + * GstAudioAggregator API * + **************************/ + +#define GST_TYPE_AUDIO_AGGREGATOR (gst_audio_aggregator_get_type()) +#define GST_AUDIO_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_AGGREGATOR,GstAudioAggregator)) +#define GST_AUDIO_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_AGGREGATOR,GstAudioAggregatorClass)) +#define GST_AUDIO_AGGREGATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIO_AGGREGATOR,GstAudioAggregatorClass)) +#define GST_IS_AUDIO_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_AGGREGATOR)) +#define GST_IS_AUDIO_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_AGGREGATOR)) + +#define GST_FLOW_CUSTOM_SUCCESS GST_FLOW_NOT_HANDLED + +/** + * GstAudioAggregator: + * @parent: The parent #GstAggregator + * @info: The information parsed from the current caps + * @current_caps: The caps set by the subclass + * + * GstAudioAggregator object + */ +struct _GstAudioAggregator +{ + GstAggregator parent; + + /* All member are read only for subclasses, must hold OBJECT lock */ + GstAudioInfo info; + + GstCaps *current_caps; + + /*< private >*/ + GstAudioAggregatorPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstAudioAggregatorClass: + * @create_output_buffer: Create a new output buffer contains num_frames frames. + * @aggregate_one_buffer: Aggregates one input buffer to the output + * buffer. The in_offset and out_offset are in "frames", which is + * the size of a sample times the number of channels. Returns TRUE if + * any non-silence was added to the buffer + */ +struct _GstAudioAggregatorClass { + GstAggregatorClass parent_class; + + GstBuffer * (* create_output_buffer) (GstAudioAggregator * aagg, + guint num_frames); + gboolean (* aggregate_one_buffer) (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_frames); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +/************************* + * GstAggregator methods * + ************************/ + +GType gst_audio_aggregator_get_type(void); + +void +gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstCaps * caps); + +gboolean +gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps); + + +G_END_DECLS + +#endif /* __GST_AUDIO_AGGREGATOR_H__ */ diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index d1daff6526..3ff370690c 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -86,7 +86,8 @@ enum PROP_PAD_MUTE }; -G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, GST_TYPE_AGGREGATOR_PAD); +G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, + GST_TYPE_AUDIO_AGGREGATOR_PAD); static void gst_audiomixer_pad_get_property (GObject * object, guint prop_id, @@ -133,27 +134,10 @@ gst_audiomixer_pad_set_property (GObject * object, guint prop_id, } } -static gboolean -gst_audiomixer_pad_flush_pad (GstAggregatorPad * aggpad, - GstAggregator * aggregator) -{ - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); - - GST_OBJECT_LOCK (aggpad); - pad->position = pad->size = 0; - pad->output_offset = pad->next_offset = -1; - pad->discont_time = GST_CLOCK_TIME_NONE; - gst_buffer_replace (&pad->buffer, NULL); - GST_OBJECT_UNLOCK (aggpad); - - return TRUE; -} - static void gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; - GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass; gobject_class->set_property = gst_audiomixer_pad_set_property; gobject_class->get_property = gst_audiomixer_pad_get_property; @@ -166,8 +150,6 @@ gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) g_param_spec_boolean ("mute", "Mute", "Mute this pad", DEFAULT_PAD_MUTE, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); - - aggpadclass->flush = GST_DEBUG_FUNCPTR (gst_audiomixer_pad_flush_pad); } static void @@ -175,26 +157,12 @@ gst_audiomixer_pad_init (GstAudioMixerPad * pad) { pad->volume = DEFAULT_PAD_VOLUME; pad->mute = DEFAULT_PAD_MUTE; - - pad->buffer = NULL; - pad->position = 0; - pad->size = 0; - pad->output_offset = -1; - pad->next_offset = -1; - pad->discont_time = GST_CLOCK_TIME_NONE; } -#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) -#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) -#define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) - enum { PROP_0, - PROP_FILTER_CAPS, - PROP_ALIGNMENT_THRESHOLD, - PROP_DISCONT_WAIT, - PROP_OUTPUT_BUFFER_DURATION + PROP_FILTER_CAPS }; /* elementfactory information */ @@ -227,8 +195,8 @@ static void gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data); #define gst_audiomixer_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_AGGREGATOR, - G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, +G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, + GST_TYPE_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, gst_audiomixer_child_proxy_init)); static void gst_audiomixer_dispose (GObject * object); @@ -243,35 +211,27 @@ static GstPad *gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); -static GstFlowReturn -gst_audiomixer_do_clip (GstAggregator * agg, - GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); -static GstFlowReturn gst_audiomixer_aggregate (GstAggregator * agg, - gboolean timeout); +static gboolean +gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_samples); -static GstClockTime -gst_audiomixer_get_next_time (GstAggregator * agg) -{ - if (agg->segment.position == -1) - return agg->segment.start; - else - return agg->segment.position; -} /* 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_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) +gst_audiomixer_sink_getcaps (GstAggregator * agg, GstPad * pad, + GstCaps * filter) { - GstAggregator *agg; + GstAudioAggregator *aagg; GstAudioMixer *audiomixer; GstCaps *result, *peercaps, *current_caps, *filter_caps; GstStructure *s; gint i, n; - audiomixer = GST_AUDIO_MIXER (GST_PAD_PARENT (pad)); - agg = GST_AGGREGATOR (audiomixer); + audiomixer = GST_AUDIO_MIXER (agg); + aagg = GST_AUDIO_AGGREGATOR (agg); GST_OBJECT_LOCK (audiomixer); /* take filter */ @@ -297,8 +257,7 @@ gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) /* get the allowed caps on this sinkpad */ GST_OBJECT_LOCK (audiomixer); - current_caps = - audiomixer->current_caps ? gst_caps_ref (audiomixer->current_caps) : NULL; + current_caps = aagg->current_caps ? gst_caps_ref (aagg->current_caps) : NULL; if (current_caps == NULL) { current_caps = gst_pad_get_pad_template_caps (pad); if (!current_caps) @@ -367,7 +326,7 @@ gst_audiomixer_sink_query (GstAggregator * agg, GstAggregatorPad * aggpad, GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); - caps = gst_audiomixer_sink_getcaps (GST_PAD (aggpad), filter); + caps = gst_audiomixer_sink_getcaps (agg, GST_PAD (aggpad), filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; @@ -389,10 +348,12 @@ static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, GstCaps * orig_caps) { + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (audiomixer); GstCaps *caps; GstAudioInfo info; GstStructure *s; gint channels; + gboolean ret; caps = gst_caps_copy (orig_caps); @@ -409,34 +370,35 @@ gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, * different upstream threads doing query_caps + accept_caps + sending * (possibly different) CAPS events, but there's not much we can do about * that, upstream needs to deal with it. */ - if (audiomixer->current_caps != NULL) { - if (gst_audio_info_is_equal (&info, &audiomixer->info)) { + if (aagg->current_caps != NULL) { + if (gst_audio_info_is_equal (&info, &aagg->info)) { GST_OBJECT_UNLOCK (audiomixer); gst_caps_unref (caps); + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + orig_caps); return TRUE; } else { GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but " - "current caps are %" GST_PTR_FORMAT, caps, audiomixer->current_caps); + "current caps are %" GST_PTR_FORMAT, caps, aagg->current_caps); GST_OBJECT_UNLOCK (audiomixer); gst_pad_push_event (pad, gst_event_new_reconfigure ()); gst_caps_unref (caps); return FALSE; } } - - GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps); - gst_caps_replace (&audiomixer->current_caps, caps); - - memcpy (&audiomixer->info, &info, sizeof (info)); - audiomixer->send_caps = TRUE; GST_OBJECT_UNLOCK (audiomixer); - /* send caps event later, after stream-start event */ + + ret = gst_audio_aggregator_set_src_caps (aagg, caps); + + if (ret) + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + orig_caps); GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); - return TRUE; + return ret; /* ERRORS */ invalid_format: @@ -447,211 +409,6 @@ invalid_format: } } -/* FIXME, the duration query should reflect how long you will produce - * data, that is the amount of stream time until you will emit EOS. - * - * For synchronized mixing this is always the max of all the durations - * of upstream since we emit EOS when all of them finished. - * - * We don't do synchronized mixing so this really depends on where the - * streams where punched in and what their relative offsets are against - * eachother which we can get from the first timestamps we see. - * - * When we add a new stream (or remove a stream) the duration might - * also become invalid again and we need to post a new DURATION - * message to notify this fact to the parent. - * For now we take the max of all the upstream elements so the simple - * cases work at least somewhat. - */ -static gboolean -gst_audiomixer_query_duration (GstAudioMixer * audiomixer, GstQuery * query) -{ - gint64 max; - gboolean res; - GstFormat format; - GstIterator *it; - gboolean done; - GValue item = { 0, }; - - /* parse format */ - gst_query_parse_duration (query, &format, NULL); - - max = -1; - res = TRUE; - done = FALSE; - - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); - while (!done) { - GstIteratorResult ires; - - ires = gst_iterator_next (it, &item); - switch (ires) { - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_OK: - { - GstPad *pad = g_value_get_object (&item); - gint64 duration; - - /* ask sink peer for duration */ - res &= gst_pad_peer_query_duration (pad, format, &duration); - /* take max from all valid return values */ - if (res) { - /* valid unknown length, stop searching */ - if (duration == -1) { - max = duration; - done = TRUE; - } - /* else see if bigger than current max */ - else if (duration > max) - max = duration; - } - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - max = -1; - res = TRUE; - gst_iterator_resync (it); - break; - default: - res = FALSE; - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (it); - - if (res) { - /* and store the max */ - GST_DEBUG_OBJECT (audiomixer, "Total duration in format %s: %" - GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); - gst_query_set_duration (query, format, max); - } - - return res; -} - -static gboolean -gst_audiomixer_src_query (GstAggregator * agg, GstQuery * query) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - gboolean res = FALSE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - - switch (format) { - case GST_FORMAT_TIME: - /* FIXME, bring to stream time, might be tricky */ - gst_query_set_position (query, format, agg->segment.position); - res = TRUE; - break; - case GST_FORMAT_DEFAULT: - gst_query_set_position (query, format, audiomixer->offset); - res = TRUE; - break; - default: - break; - } - break; - } - case GST_QUERY_DURATION: - res = gst_audiomixer_query_duration (audiomixer, query); - break; - default: - res = - GST_AGGREGATOR_CLASS (gst_audiomixer_parent_class)->src_query - (agg, query); - break; - } - - return res; -} - -/* event handling */ - -typedef struct -{ - GstEvent *event; - gboolean flush; -} EventData; - -static gboolean -gst_audiomixer_src_event (GstAggregator * agg, GstEvent * event) -{ - gboolean result; - - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - GST_DEBUG_OBJECT (agg->srcpad, "Got %s event on src pad", - GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_QOS: - /* QoS might be tricky */ - gst_event_unref (event); - return FALSE; - case GST_EVENT_NAVIGATION: - /* navigation is rather pointless. */ - gst_event_unref (event); - return FALSE; - break; - case GST_EVENT_SEEK: - { - GstSeekFlags flags; - gdouble rate; - GstSeekType start_type, stop_type; - gint64 start, stop; - GstFormat seek_format, dest_format; - - /* parse the seek parameters */ - gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, - &start, &stop_type, &stop); - - /* Check the seeking parametters before linking up */ - if ((start_type != GST_SEEK_TYPE_NONE) - && (start_type != GST_SEEK_TYPE_SET)) { - result = FALSE; - GST_DEBUG_OBJECT (audiomixer, - "seeking failed, unhandled seek type for start: %d", start_type); - goto done; - } - if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) { - result = FALSE; - GST_DEBUG_OBJECT (audiomixer, - "seeking failed, unhandled seek type for end: %d", stop_type); - goto done; - } - - dest_format = agg->segment.format; - if (seek_format != dest_format) { - result = FALSE; - GST_DEBUG_OBJECT (audiomixer, - "seeking failed, unhandled seek format: %d", seek_format); - goto done; - } - - /* Link up */ - result = GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); - goto done; - } - break; - default: - break; - } - - return GST_AGGREGATOR_CLASS (parent_class)->src_event (agg, event); - -done: - return result; -} - static gboolean gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, GstEvent * event) @@ -673,34 +430,6 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, event = NULL; break; } - case GST_EVENT_SEGMENT: - { - const GstSegment *segment; - - gst_event_parse_segment (event, &segment); - if (segment->rate != agg->segment.rate) { - GST_ERROR_OBJECT (aggpad, - "Got segment event with wrong rate %lf, expected %lf", - segment->rate, agg->segment.rate); - res = FALSE; - gst_event_unref (event); - event = NULL; - } else if (segment->rate < 0.0) { - GST_ERROR_OBJECT (aggpad, "Negative rates not supported yet"); - res = FALSE; - gst_event_unref (event); - event = NULL; - } else { - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aggpad); - - /* Ideally, this should only be set when the new segment causes running - * times to change, and hence needs discont calculation in fill_buffer */ - GST_OBJECT_LOCK (pad); - pad->new_segment = TRUE; - GST_OBJECT_UNLOCK (pad); - } - break; - } default: break; } @@ -711,57 +440,13 @@ gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, return res; } -static void -gst_audiomixer_reset (GstAudioMixer * audiomixer) -{ - GstAggregator *agg = GST_AGGREGATOR (audiomixer); - - audiomixer->offset = 0; - agg->segment.position = -1; - - gst_audio_info_init (&audiomixer->info); - gst_caps_replace (&audiomixer->current_caps, NULL); - gst_buffer_replace (&audiomixer->current_buffer, NULL); -} - -static gboolean -gst_audiomixer_start (GstAggregator * agg) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - - gst_audiomixer_reset (audiomixer); - - return TRUE; -} - -static gboolean -gst_audiomixer_stop (GstAggregator * agg) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - - gst_audiomixer_reset (audiomixer); - - return TRUE; -} - -static GstFlowReturn -gst_audiomixer_flush (GstAggregator * agg) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - - audiomixer->offset = 0; - agg->segment.position = -1; - gst_buffer_replace (&audiomixer->current_buffer, NULL); - - return GST_FLOW_OK; -} - static void gst_audiomixer_class_init (GstAudioMixerClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; GstElementClass *gstelement_class = (GstElementClass *) klass; GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; + GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass; gobject_class->set_property = gst_audiomixer_set_property; gobject_class->get_property = gst_audiomixer_get_property; @@ -773,25 +458,6 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) "Setting this property takes a reference to the supplied GstCaps " "object", GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, - g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", - "Timestamp alignment threshold in nanoseconds", 0, - G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, - g_param_spec_uint64 ("discont-wait", "Discont Wait", - "Window of time in nanoseconds to wait before " - "creating a discontinuity", 0, - G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_OUTPUT_BUFFER_DURATION, - g_param_spec_uint64 ("output-buffer-duration", "Output Buffer Duration", - "Output block size in nanoseconds", 1, - G_MAXUINT64, DEFAULT_OUTPUT_BUFFER_DURATION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&gst_audiomixer_src_template)); gst_element_class_add_pad_template (gstelement_class, @@ -807,35 +473,17 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); agg_class->sinkpads_type = GST_TYPE_AUDIO_MIXER_PAD; - agg_class->start = gst_audiomixer_start; - agg_class->stop = gst_audiomixer_stop; - - agg_class->get_next_time = gst_audiomixer_get_next_time; agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); - agg_class->aggregate = GST_DEBUG_FUNCPTR (gst_audiomixer_aggregate); - agg_class->clip = GST_DEBUG_FUNCPTR (gst_audiomixer_do_clip); - - agg_class->src_event = GST_DEBUG_FUNCPTR (gst_audiomixer_src_event); - agg_class->src_query = GST_DEBUG_FUNCPTR (gst_audiomixer_src_query); - - agg_class->flush = GST_DEBUG_FUNCPTR (gst_audiomixer_flush); + aagg_class->aggregate_one_buffer = gst_audiomixer_aggregate_one_buffer; } static void gst_audiomixer_init (GstAudioMixer * audiomixer) { - audiomixer->current_caps = NULL; - gst_audio_info_init (&audiomixer->info); - audiomixer->filter_caps = NULL; - audiomixer->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; - audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; - audiomixer->output_buffer_duration = DEFAULT_OUTPUT_BUFFER_DURATION; - gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, audiomixer->output_buffer_duration); } static void @@ -844,7 +492,6 @@ gst_audiomixer_dispose (GObject * object) GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); gst_caps_replace (&audiomixer->filter_caps, NULL); - gst_caps_replace (&audiomixer->current_caps, NULL); G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -877,18 +524,6 @@ gst_audiomixer_set_property (GObject * object, guint prop_id, GST_DEBUG_OBJECT (audiomixer, "set new caps %" GST_PTR_FORMAT, new_caps); break; } - case PROP_ALIGNMENT_THRESHOLD: - audiomixer->alignment_threshold = g_value_get_uint64 (value); - break; - case PROP_DISCONT_WAIT: - audiomixer->discont_wait = g_value_get_uint64 (value); - break; - case PROP_OUTPUT_BUFFER_DURATION: - audiomixer->output_buffer_duration = g_value_get_uint64 (value); - gst_aggregator_set_latency (GST_AGGREGATOR (audiomixer), - audiomixer->output_buffer_duration, - audiomixer->output_buffer_duration); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -907,15 +542,6 @@ gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, gst_value_set_caps (value, audiomixer->filter_caps); GST_OBJECT_UNLOCK (audiomixer); break; - case PROP_ALIGNMENT_THRESHOLD: - g_value_set_uint64 (value, audiomixer->alignment_threshold); - break; - case PROP_DISCONT_WAIT: - g_value_set_uint64 (value, audiomixer->discont_wait); - break; - case PROP_OUTPUT_BUFFER_DURATION: - g_value_set_uint64 (value, audiomixer->output_buffer_duration); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -962,332 +588,118 @@ gst_audiomixer_release_pad (GstElement * element, GstPad * pad) GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); } -static GstFlowReturn -gst_audiomixer_do_clip (GstAggregator * agg, - GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** out) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (agg); - gint rate, bpf; - - rate = GST_AUDIO_INFO_RATE (&audiomixer->info); - bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - - buffer = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); - - *out = buffer; - return GST_FLOW_OK; -} +/* Called with object lock and pad object lock held */ static gboolean -gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, - GstBuffer * inbuf) +gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_frames) { - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - GstClockTime start_time, end_time; - gboolean discont = FALSE; - guint64 start_offset, end_offset; - GstClockTime timestamp, stream_time; - gint rate, bpf; - - g_assert (pad->buffer == NULL); - - rate = GST_AUDIO_INFO_RATE (&audiomixer->info); - bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - - timestamp = GST_BUFFER_PTS (inbuf); - stream_time = gst_segment_to_stream_time (&aggpad->segment, GST_FORMAT_TIME, - timestamp); - - /* sync object properties on stream time */ - /* TODO: Ideally we would want to do that on every sample */ - if (GST_CLOCK_TIME_IS_VALID (stream_time)) - gst_object_sync_values (GST_OBJECT (pad), stream_time); - - GST_OBJECT_LOCK (pad); - pad->position = 0; - pad->size = gst_buffer_get_size (inbuf); - - start_time = GST_BUFFER_PTS (inbuf); - end_time = - start_time + gst_util_uint64_scale_ceil (pad->size / bpf, - GST_SECOND, rate); - - start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); - end_offset = start_offset + pad->size / bpf; - - if (GST_BUFFER_IS_DISCONT (inbuf) - || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) - || pad->new_segment || pad->next_offset == -1) { - discont = TRUE; - pad->new_segment = FALSE; - } else { - guint64 diff, max_sample_diff; - - /* Check discont, based on audiobasesink */ - if (start_offset <= pad->next_offset) - diff = pad->next_offset - start_offset; - else - diff = start_offset - pad->next_offset; - - max_sample_diff = - gst_util_uint64_scale_int (audiomixer->alignment_threshold, rate, - GST_SECOND); - - /* Discont! */ - if (G_UNLIKELY (diff >= max_sample_diff)) { - if (audiomixer->discont_wait > 0) { - if (pad->discont_time == GST_CLOCK_TIME_NONE) { - pad->discont_time = start_time; - } else if (start_time - pad->discont_time >= audiomixer->discont_wait) { - discont = TRUE; - pad->discont_time = GST_CLOCK_TIME_NONE; - } - } else { - discont = TRUE; - } - } else if (G_UNLIKELY (pad->discont_time != GST_CLOCK_TIME_NONE)) { - /* we have had a discont, but are now back on track! */ - pad->discont_time = GST_CLOCK_TIME_NONE; - } - } - - if (discont) { - /* Have discont, need resync */ - if (pad->next_offset != -1) - GST_INFO_OBJECT (pad, "Have discont. Expected %" - G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, - pad->next_offset, start_offset); - pad->output_offset = -1; - pad->next_offset = end_offset; - } else { - pad->next_offset += pad->size / bpf; - } - - if (pad->output_offset == -1) { - GstClockTime start_running_time; - GstClockTime end_running_time; - guint64 start_running_time_offset; - guint64 end_running_time_offset; - - start_running_time = - gst_segment_to_running_time (&aggpad->segment, - GST_FORMAT_TIME, start_time); - end_running_time = - gst_segment_to_running_time (&aggpad->segment, - GST_FORMAT_TIME, end_time); - start_running_time_offset = - gst_util_uint64_scale (start_running_time, rate, GST_SECOND); - end_running_time_offset = - gst_util_uint64_scale (end_running_time, rate, GST_SECOND); - - if (end_running_time_offset < audiomixer->offset) { - /* Before output segment, drop */ - gst_buffer_unref (inbuf); - pad->buffer = NULL; - gst_aggregator_pad_drop_buffer (aggpad); - pad->position = 0; - pad->size = 0; - pad->output_offset = -1; - GST_DEBUG_OBJECT (pad, - "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" - G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); - GST_OBJECT_UNLOCK (pad); - return FALSE; - } - - if (start_running_time_offset < audiomixer->offset) { - guint diff = (audiomixer->offset - start_running_time_offset) * bpf; - - pad->position += diff; - if (pad->position >= pad->size) { - /* Empty buffer, drop */ - gst_buffer_unref (inbuf); - pad->buffer = NULL; - gst_aggregator_pad_drop_buffer (aggpad); - pad->position = 0; - pad->size = 0; - pad->output_offset = -1; - GST_DEBUG_OBJECT (pad, - "Buffer before segment or current position: %" G_GUINT64_FORMAT - " < %" G_GUINT64_FORMAT, end_running_time_offset, - audiomixer->offset); - GST_OBJECT_UNLOCK (pad); - return FALSE; - } - } - - pad->output_offset = MAX (start_running_time_offset, audiomixer->offset); - GST_DEBUG_OBJECT (pad, - "Buffer resynced: Pad offset %" G_GUINT64_FORMAT - ", current mixer offset %" G_GUINT64_FORMAT, pad->output_offset, - audiomixer->offset); - } - - GST_LOG_OBJECT (pad, - "Queued new buffer at offset %" G_GUINT64_FORMAT, pad->output_offset); - pad->buffer = inbuf; - - GST_OBJECT_UNLOCK (pad); - return TRUE; -} - -static void -gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, - GstMapInfo * outmap) -{ - guint overlap; - guint out_start; - GstBuffer *inbuf; + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aaggpad); GstMapInfo inmap; + GstMapInfo outmap; gint bpf; - guint blocksize; - gboolean drop_buf = FALSE; - - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - blocksize = - gst_util_uint64_scale (audiomixer->output_buffer_duration, - GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); - blocksize = MAX (1, blocksize); - - bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - - GST_OBJECT_LOCK (pad); - /* Overlap => mix */ - if (audiomixer->offset < pad->output_offset) - out_start = pad->output_offset - audiomixer->offset; - else - out_start = 0; - - overlap = pad->size / bpf - pad->position / bpf; - if (overlap > blocksize - out_start) - overlap = blocksize - out_start; - - inbuf = gst_aggregator_pad_get_buffer (aggpad); - if (inbuf == NULL) { - GST_OBJECT_UNLOCK (pad); - return; - } if (pad->mute || pad->volume < G_MINDOUBLE) { GST_DEBUG_OBJECT (pad, "Skipping muted pad"); - gst_buffer_unref (inbuf); - pad->position += overlap * bpf; - pad->output_offset += overlap; - if (pad->position >= pad->size) { - /* Buffer done, drop it */ - gst_buffer_replace (&pad->buffer, NULL); - drop_buf = TRUE; - } - GST_OBJECT_UNLOCK (pad); - if (drop_buf) - gst_aggregator_pad_drop_buffer (aggpad); - return; + return FALSE; } - if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { - /* skip gap buffer */ - GST_LOG_OBJECT (pad, "skipping GAP buffer"); - gst_buffer_unref (inbuf); - pad->output_offset += pad->size / bpf; - /* Buffer done, drop it */ - gst_buffer_replace (&pad->buffer, NULL); - GST_OBJECT_UNLOCK (pad); - gst_aggregator_pad_drop_buffer (aggpad); - return; - } + 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, "mixing %u bytes at offset %u from offset %u", - overlap * bpf, out_start * bpf, pad->position); + num_frames * bpf, out_offset * bpf, in_offset * bpf); + /* further buffers, need to add them */ if (pad->volume == 1.0) { - switch (audiomixer->info.finfo->format) { + switch (aagg->info.finfo->format) { case GST_AUDIO_FORMAT_U8: - audiomixer_orc_add_u8 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_u8 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S8: - audiomixer_orc_add_s8 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_s8 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_U16: - audiomixer_orc_add_u16 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_u16 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S16: - audiomixer_orc_add_s16 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_s16 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_U32: - audiomixer_orc_add_u32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_u32 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S32: - audiomixer_orc_add_s32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_s32 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_F32: - audiomixer_orc_add_f32 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_f32 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_F64: - audiomixer_orc_add_f64 ((gpointer) (outmap->data + out_start * bpf), - (gpointer) (inmap.data + pad->position), - overlap * audiomixer->info.channels); + audiomixer_orc_add_f64 ((gpointer) (outmap.data + out_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); break; default: g_assert_not_reached (); break; } } else { - switch (audiomixer->info.finfo->format) { + switch (aagg->info.finfo->format) { case GST_AUDIO_FORMAT_U8: - audiomixer_orc_add_volume_u8 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i8, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_u8 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i8, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S8: - audiomixer_orc_add_volume_s8 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i8, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_s8 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i8, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_U16: - audiomixer_orc_add_volume_u16 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i16, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_u16 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i16, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S16: - audiomixer_orc_add_volume_s16 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i16, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_s16 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i16, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_U32: - audiomixer_orc_add_volume_u32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i32, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_u32 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i32, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_S32: - audiomixer_orc_add_volume_s32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume_i32, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_s32 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume_i32, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_F32: - audiomixer_orc_add_volume_f32 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_f32 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume, num_frames * aagg->info.channels); break; case GST_AUDIO_FORMAT_F64: - audiomixer_orc_add_volume_f64 ((gpointer) (outmap->data + - out_start * bpf), (gpointer) (inmap.data + pad->position), - pad->volume, overlap * audiomixer->info.channels); + audiomixer_orc_add_volume_f64 ((gpointer) (outmap.data + + out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume, num_frames * aagg->info.channels); break; default: g_assert_not_reached (); @@ -1295,321 +707,12 @@ gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstAudioMixerPad * pad, } } gst_buffer_unmap (inbuf, &inmap); - gst_buffer_unref (inbuf); - - pad->position += overlap * bpf; - pad->output_offset += overlap; - - if (pad->position == pad->size) { - /* Buffer done, drop it */ - gst_buffer_replace (&pad->buffer, NULL); - GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); - drop_buf = TRUE; - } - - GST_OBJECT_UNLOCK (pad); - - if (drop_buf) - gst_aggregator_pad_drop_buffer (aggpad); -} - -static GstFlowReturn -gst_audiomixer_aggregate (GstAggregator * agg, gboolean timeout) -{ - /* Get all pads that have data for us and store them in a - * new list. - * - * Calculate the current output offset/timestamp and - * offset_end/timestamp_end. Allocate a silence buffer - * for this and store it. - * - * For all pads: - * 1) Once per input buffer (cached) - * 1) Check discont (flag and timestamp with tolerance) - * 2) If discont or new, resync. That means: - * 1) Drop all start data of the buffer that comes before - * the current position/offset. - * 2) Calculate the offset (output segment!) that the first - * frame of the input buffer corresponds to. Base this on - * the running time. - * - * 2) If the current pad's offset/offset_end overlaps with the output - * offset/offset_end, mix it at the appropiate position in the output - * buffer and advance the pad's position. Remember if this pad needs - * a new buffer to advance behind the output offset_end. - * - * 3) If we had no pad with a buffer, go EOS. - * - * 4) If we had at least one pad that did not advance behind output - * offset_end, let collected be called again for the current - * output offset/offset_end. - */ - GstAudioMixer *audiomixer; - GList *iter; - GstFlowReturn ret; - GstBuffer *outbuf = NULL; - GstMapInfo outmap; - gint64 next_offset; - gint64 next_timestamp; - gint rate, bpf; - gboolean dropped = FALSE; - gboolean is_eos = TRUE; - gboolean is_done = TRUE; - guint blocksize; - - audiomixer = GST_AUDIO_MIXER (agg); - - /* Update position from the segment start/stop if needed */ - if (agg->segment.position == -1) { - if (agg->segment.rate > 0.0) - agg->segment.position = agg->segment.start; - else - agg->segment.position = agg->segment.stop; - } - - if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) { - if (timeout) { - GST_DEBUG_OBJECT (audiomixer, - "Got timeout before receiving any caps, don't output anything"); - - /* Advance position */ - if (agg->segment.rate > 0.0) - agg->segment.position += audiomixer->output_buffer_duration; - else if (agg->segment.position > audiomixer->output_buffer_duration) - agg->segment.position -= audiomixer->output_buffer_duration; - else - agg->segment.position = 0; - - return GST_FLOW_OK; - } else { - goto not_negotiated; - } - } - - blocksize = - gst_util_uint64_scale (audiomixer->output_buffer_duration, - GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); - blocksize = MAX (1, blocksize); - - if (audiomixer->send_caps) { - gst_aggregator_set_src_caps (agg, audiomixer->current_caps); - - audiomixer->offset = gst_util_uint64_scale (agg->segment.position, - GST_AUDIO_INFO_RATE (&audiomixer->info), GST_SECOND); - - audiomixer->send_caps = FALSE; - } - - rate = GST_AUDIO_INFO_RATE (&audiomixer->info); - bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - - /* for the next timestamp, use the sample counter, which will - * never accumulate rounding errors */ - - /* FIXME: Reverse mixing does not work at all yet */ - if (agg->segment.rate > 0.0) { - next_offset = audiomixer->offset + blocksize; - } else { - next_offset = audiomixer->offset - blocksize; - } - next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); - - if (audiomixer->current_buffer) { - outbuf = audiomixer->current_buffer; - } else { - outbuf = gst_buffer_new_and_alloc (blocksize * bpf); - gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); - gst_audio_format_fill_silence (audiomixer->info.finfo, outmap.data, - outmap.size); - gst_buffer_unmap (outbuf, &outmap); - audiomixer->current_buffer = outbuf; - } - - GST_LOG_OBJECT (agg, - "Starting to mix %u samples for offset %" G_GUINT64_FORMAT - " with timestamp %" GST_TIME_FORMAT, blocksize, - audiomixer->offset, GST_TIME_ARGS (agg->segment.position)); - - gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); - - GST_OBJECT_LOCK (agg); - for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { - GstBuffer *inbuf; - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (iter->data); - - if (!gst_aggregator_pad_is_eos (aggpad)) - is_eos = FALSE; - - inbuf = gst_aggregator_pad_get_buffer (aggpad); - if (!inbuf) { - if (timeout) { - if (pad->output_offset < next_offset) { - gint64 diff = next_offset - pad->output_offset; - - GST_LOG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT " frames (%" - GST_TIME_FORMAT ")", diff, - GST_TIME_ARGS (gst_util_uint64_scale (diff, GST_SECOND, rate))); - } - } else if (!gst_aggregator_pad_is_eos (aggpad)) { - is_done = FALSE; - } - continue; - } - - g_assert (!pad->buffer || pad->buffer == inbuf); - - /* New buffer? */ - if (!pad->buffer) { - /* Takes ownership of buffer */ - if (!gst_audio_mixer_fill_buffer (audiomixer, pad, inbuf)) { - dropped = TRUE; - continue; - } - } else { - gst_buffer_unref (inbuf); - } - - if (!pad->buffer && !dropped && gst_aggregator_pad_is_eos (aggpad)) { - GST_DEBUG_OBJECT (aggpad, "Pad is in EOS state"); - continue; - } - - g_assert (pad->buffer); - - /* This pad is lacking behind, we need to update the offset - * and maybe drop the current buffer */ - if (pad->output_offset < audiomixer->offset) { - gint64 diff = audiomixer->offset - pad->output_offset; - gint bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); - - if (pad->position + (diff * bpf) > pad->size) - diff = (pad->size - pad->position) / bpf; - pad->position += diff * bpf; - pad->output_offset += diff; - - if (pad->position == pad->size) { - /* Buffer done, drop it */ - gst_buffer_replace (&pad->buffer, NULL); - gst_aggregator_pad_drop_buffer (aggpad); - dropped = TRUE; - continue; - } - } - - if (pad->output_offset >= audiomixer->offset - && pad->output_offset < audiomixer->offset + blocksize && pad->buffer) { - GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); - gst_audio_mixer_mix_buffer (audiomixer, pad, &outmap); - - if (pad->output_offset >= next_offset) { - GST_DEBUG_OBJECT (pad, - "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" - G_GUINT64_FORMAT, pad->output_offset, next_offset); - } else { - is_done = FALSE; - } - } - } - GST_OBJECT_UNLOCK (agg); - gst_buffer_unmap (outbuf, &outmap); - if (dropped) { - /* We dropped a buffer, retry */ - GST_INFO_OBJECT (audiomixer, - "A pad dropped a buffer, wait for the next one"); - return GST_FLOW_OK; - } - - if (!is_done && !is_eos) { - /* Get more buffers */ - GST_INFO_OBJECT (audiomixer, - "We're not done yet for the current offset, waiting for more data"); - return GST_FLOW_OK; - } - - if (is_eos) { - gint64 max_offset = 0; - gboolean empty_buffer = TRUE; - - GST_DEBUG_OBJECT (audiomixer, "We're EOS"); - - GST_OBJECT_LOCK (agg); - for (iter = GST_ELEMENT (agg)->sinkpads; iter; iter = iter->next) { - GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (iter->data); - - max_offset = MAX ((gint64) max_offset, (gint64) pad->output_offset); - if (pad->output_offset > audiomixer->offset) - empty_buffer = FALSE; - } - GST_OBJECT_UNLOCK (agg); - - /* This means EOS or no pads at all */ - if (empty_buffer) { - gst_buffer_replace (&audiomixer->current_buffer, NULL); - goto eos; - } - - if (max_offset <= next_offset) { - GST_DEBUG_OBJECT (audiomixer, - "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" - G_GUINT64_FORMAT, max_offset, next_offset); - next_offset = max_offset; - if (next_offset > audiomixer->offset) - gst_buffer_resize (outbuf, 0, (next_offset - audiomixer->offset) * bpf); - - next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); - } - } - - /* set timestamps on the output buffer */ - if (agg->segment.rate > 0.0) { - GST_BUFFER_TIMESTAMP (outbuf) = agg->segment.position; - GST_BUFFER_OFFSET (outbuf) = audiomixer->offset; - GST_BUFFER_OFFSET_END (outbuf) = next_offset; - GST_BUFFER_DURATION (outbuf) = next_timestamp - agg->segment.position; - } else { - GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp; - GST_BUFFER_OFFSET (outbuf) = next_offset; - GST_BUFFER_OFFSET_END (outbuf) = audiomixer->offset; - GST_BUFFER_DURATION (outbuf) = agg->segment.position - next_timestamp; - } - - audiomixer->offset = next_offset; - agg->segment.position = next_timestamp; - - /* send it out */ - GST_LOG_OBJECT (audiomixer, - "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" - G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_BUFFER_OFFSET (outbuf)); - - ret = gst_aggregator_finish_buffer (agg, audiomixer->current_buffer); - audiomixer->current_buffer = NULL; - - GST_LOG_OBJECT (audiomixer, "pushed outbuf, result = %s", - gst_flow_get_name (ret)); - - if (ret == GST_FLOW_OK && is_eos) - goto eos; - - return ret; - /* ERRORS */ -not_negotiated: - { - GST_ELEMENT_ERROR (audiomixer, STREAM, FORMAT, (NULL), - ("Unknown data received, not negotiated")); - return GST_FLOW_NOT_NEGOTIATED; - } - -eos: - { - GST_DEBUG_OBJECT (audiomixer, "EOS"); - return GST_FLOW_EOS; - } + return TRUE; } + /* GstChildProxy implementation */ static GObject * gst_audiomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 9507e6c5d8..add6e32f47 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -25,8 +25,8 @@ #define __GST_AUDIO_MIXER_H__ #include -#include #include +#include "gstaudioaggregator.h" G_BEGIN_DECLS @@ -49,32 +49,14 @@ typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; * The audiomixer object structure. */ struct _GstAudioMixer { - GstAggregator aggregator; - - /* the next are valid for both int and float */ - GstAudioInfo info; - - /* counters to keep track of timestamps */ - gint64 offset; - /* Buffer starting at offset containing block_size samples */ - GstBuffer *current_buffer; - - /* current caps */ - GstCaps *current_caps; - gboolean send_caps; + GstAudioAggregator element; /* target caps (set via property) */ GstCaps *filter_caps; - - GstClockTime alignment_threshold; - GstClockTime discont_wait; - - /* Duration of every output buffer */ - GstClockTime output_buffer_duration; }; struct _GstAudioMixerClass { - GstAggregatorClass parent_class; + GstAudioAggregatorClass parent_class; }; GType gst_audiomixer_get_type (void); @@ -87,36 +69,17 @@ GType gst_audiomixer_get_type (void); #define GST_AUDIO_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) struct _GstAudioMixerPad { - GstAggregatorPad parent; + GstAudioAggregatorPad parent; gdouble volume; gint volume_i32; gint volume_i16; gint volume_i8; gboolean mute; - - /* < private > */ - GstBuffer *buffer; /* current buffer we're mixing, - for comparison with collect.buffer - to see if we need to update our - cached values. */ - guint position, size; - - guint64 output_offset; /* Offset in output segment that - collect.pos refers to in the - current buffer. */ - - guint64 next_offset; /* Next expected offset in the input segment */ - - /* Last time we noticed a discont */ - GstClockTime discont_time; - - /* A new unhandled segment event has been received */ - gboolean new_segment; }; struct _GstAudioMixerPadClass { - GstAggregatorPadClass parent_class; + GstAudioAggregatorPadClass parent_class; }; GType gst_audiomixer_pad_get_type (void); From acf7745188912f4695095be27027fd6ece07a9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 15 Nov 2014 17:54:51 -0500 Subject: [PATCH 062/153] audioaggregator: Don't re-send the caps if they did not change https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- gst/audiomixer/gstaudioaggregator.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 5939c1dc92..2fb0a41eac 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -635,11 +635,14 @@ gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps) GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (aagg); - GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps); - gst_caps_replace (&aagg->current_caps, caps); + if (!gst_audio_info_is_equal (&info, &aagg->info)) { + GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps); + gst_caps_replace (&aagg->current_caps, caps); - memcpy (&aagg->info, &info, sizeof (info)); - aagg->priv->send_caps = TRUE; + memcpy (&aagg->info, &info, sizeof (info)); + aagg->priv->send_caps = TRUE; + + } GST_OBJECT_UNLOCK (aagg); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); From 15369ba016aed6123679bcee331322c62b60bc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 6 Mar 2015 16:51:12 -0500 Subject: [PATCH 063/153] audioaggregator: Print a message when a buffer is late https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- gst/audiomixer/gstaudioaggregator.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 2fb0a41eac..b52635015d 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -1143,6 +1143,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) * and maybe drop the current buffer */ if (pad->priv->output_offset < aagg->priv->offset) { gint64 diff = aagg->priv->offset - pad->priv->output_offset; + gint64 odiff = diff; if (pad->priv->position + diff > pad->priv->size) diff = pad->priv->size - pad->priv->position; @@ -1150,6 +1151,10 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) pad->priv->output_offset += diff; if (pad->priv->position == pad->priv->size) { + GST_LOG_OBJECT (pad, "Buffer was late by %" GST_TIME_FORMAT + ", dropping %" GST_PTR_FORMAT, + GST_TIME_ARGS (gst_util_uint64_scale (odiff, GST_SECOND, + GST_AUDIO_INFO_RATE (&aagg->info))), pad->priv->buffer); /* Buffer done, drop it */ gst_buffer_replace (&pad->priv->buffer, NULL); dropped = TRUE; 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 064/153] 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; } From edde3c326e1802502c57d07c7fe1439446d96817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 6 Mar 2015 13:49:48 -0500 Subject: [PATCH 065/153] audiointerleave: Set src caps in aggregate This prevents races between the setcaps of the sink pads https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- gst/audiomixer/gstaudiointerleave.c | 182 ++++++++++++---------------- gst/audiomixer/gstaudiointerleave.h | 2 +- 2 files changed, 79 insertions(+), 105 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index f06ea28243..fc984298e7 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -322,14 +322,14 @@ gst_audio_interleave_channel_positions_to_mask (GValueArray * positions, return ret; } -static void -gst_audio_interleave_set_channel_positions (GstAudioInterleave * self, - GstStructure * s) + +/* Must be called with the object lock held */ + +static guint64 +gst_audio_interleave_get_channel_mask (GstAudioInterleave * self) { 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 @@ -341,9 +341,8 @@ gst_audio_interleave_set_channel_positions (GstAudioInterleave * self, } 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); + return channel_mask; } @@ -377,11 +376,10 @@ interleave_24 (guint8 * out, guint8 * in, guint stride, guint nframes) } static void -gst_audio_interleave_set_process_function (GstAudioInterleave * self) +gst_audio_interleave_set_process_function (GstAudioInterleave * self, + GstAudioInfo * info) { - GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); - - switch (GST_AUDIO_INFO_WIDTH (&aagg->info)) { + switch (GST_AUDIO_INFO_WIDTH (info)) { case 8: self->func = (GstInterleaveFunc) interleave_8; break; @@ -412,56 +410,17 @@ 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; + gboolean new = FALSE; 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); + GST_OBJECT_LOCK (self); + if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps)) + goto cannot_change_caps; if (!self->sinkcaps) { GstCaps *sinkcaps = gst_caps_copy (caps); @@ -474,8 +433,23 @@ gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad, gst_caps_replace (&self->sinkcaps, sinkcaps); gst_caps_unref (sinkcaps); + new = TRUE; + self->new_caps = TRUE; } + 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)); + } + GST_OBJECT_UNLOCK (self); + + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + caps); + + if (!new) + return TRUE; GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); @@ -490,15 +464,11 @@ invalid_format: } cannot_change_caps: { + GST_OBJECT_UNLOCK (self); 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 @@ -532,6 +502,48 @@ gst_audio_interleave_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, return res; } +static GstFlowReturn +gst_audio_interleave_aggregate (GstAggregator * aggregator, gboolean timeout) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (aggregator); + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (aggregator); + + GST_OBJECT_LOCK (aggregator); + if (self->new_caps) { + GstCaps *srccaps; + GstStructure *s; + gboolean ret; + + srccaps = gst_caps_copy (self->sinkcaps); + s = gst_caps_get_structure (srccaps, 0); + + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout", + G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK, + gst_audio_interleave_get_channel_mask (self), NULL); + + + GST_OBJECT_UNLOCK (aggregator); + ret = gst_audio_aggregator_set_src_caps (aagg, srccaps); + gst_caps_unref (srccaps); + + if (!ret) + goto src_did_not_accept; + + GST_OBJECT_LOCK (aggregator); + + gst_audio_interleave_set_process_function (self, &aagg->info); + + self->new_caps = FALSE; + } + GST_OBJECT_UNLOCK (aggregator); + + return GST_AGGREGATOR_CLASS (parent_class)->aggregate (aggregator, timeout); + +src_did_not_accept: + GST_WARNING_OBJECT (self, "src did not accept setcaps()"); + return GST_FLOW_NOT_NEGOTIATED;; +} + static void gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) { @@ -567,6 +579,7 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) 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; + agg_class->aggregate = gst_audio_interleave_aggregate; aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer; @@ -701,6 +714,7 @@ gst_audio_interleave_stop (GstAggregator * agg) if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) return FALSE; + self->new_caps = FALSE; gst_caps_replace (&self->sinkcaps, NULL); return TRUE; @@ -746,22 +760,9 @@ gst_audio_interleave_request_new_pad (GstElement * element, 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); - } + GST_OBJECT_LOCK (self); + self->new_caps = TRUE; + GST_OBJECT_UNLOCK (self); return GST_PAD_CAST (newpad); @@ -786,9 +787,6 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad) 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); @@ -801,32 +799,8 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad) 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); - } - - + self->new_caps = TRUE; + GST_OBJECT_UNLOCK (self); GST_DEBUG_OBJECT (self, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); diff --git a/gst/audiomixer/gstaudiointerleave.h b/gst/audiomixer/gstaudiointerleave.h index 87f6385e39..0473b45e03 100644 --- a/gst/audiomixer/gstaudiointerleave.h +++ b/gst/audiomixer/gstaudiointerleave.h @@ -58,8 +58,8 @@ struct _GstAudioInterleave { gint padcounter; guint channels; + gboolean new_caps; GstCaps *sinkcaps; - gint configured_sinkpads_counter; GValueArray *channel_positions; GValueArray *input_channel_positions; From 7975cefff03d72b2396ba88138aa01b1f3f31d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 13 Nov 2014 20:39:11 -0500 Subject: [PATCH 066/153] audiointerleave: Add unit tests Almost a copy of the "interleave" unit tests, improved to support the thread on the src pad on GstAggregator. https://bugzilla.gnome.org/show_bug.cgi?id=740236 --- tests/check/elements/audiointerleave.c | 870 +++++++++++++++++++++++++ 1 file changed, 870 insertions(+) create mode 100644 tests/check/elements/audiointerleave.c diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c new file mode 100644 index 0000000000..83aaf0f48f --- /dev/null +++ b/tests/check/elements/audiointerleave.c @@ -0,0 +1,870 @@ +/* GStreamer unit tests for the audiointerleave element + * Copyright (C) 2007 Tim-Philipp Müller + * Copyright (C) 2008 Sebastian Dröge + * + * 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. + */ + +/* 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 + +#ifdef HAVE_VALGRIND +# include +#endif + +#include +#include +#include + +static void +gst_check_setup_events_audiointerleave (GstPad * srcpad, GstElement * element, + GstCaps * caps, GstFormat format, const gchar * stream_id) +{ + GstSegment segment; + + gst_segment_init (&segment, format); + + fail_unless (gst_pad_push_event (srcpad, + gst_event_new_stream_start (stream_id))); + if (caps) + fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps))); + fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment))); +} + +GST_START_TEST (test_create_and_unref) +{ + GstElement *interleave; + + interleave = gst_element_factory_make ("audiointerleave", NULL); + fail_unless (interleave != NULL); + + gst_element_set_state (interleave, GST_STATE_NULL); + gst_object_unref (interleave); +} + +GST_END_TEST; + +GST_START_TEST (test_request_pads) +{ + GstElement *interleave; + GstPad *pad1, *pad2; + + interleave = gst_element_factory_make ("audiointerleave", NULL); + fail_unless (interleave != NULL); + + pad1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (pad1 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (pad1), "sink_0"); + + pad2 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (pad2 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (pad2), "sink_1"); + + gst_element_release_request_pad (interleave, pad2); + gst_object_unref (pad2); + gst_element_release_request_pad (interleave, pad1); + gst_object_unref (pad1); + + gst_element_set_state (interleave, GST_STATE_NULL); + gst_object_unref (interleave); +} + +GST_END_TEST; + +static GstPad **mysrcpads, *mysinkpad; +static GstBus *bus; +static GstElement *interleave; +static GMutex data_mutex; +static GCond data_cond; +static gint have_data; +static gfloat input[2]; + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (F32) ", " + "channels = (int) 2, layout = (string) {interleaved, non-interleaved}, rate = (int) 48000")); + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (F32) ", " + "channels = (int) 1, layout = (string) interleaved, rate = (int) 48000")); + +#define CAPS_48khz \ + "audio/x-raw, " \ + "format = (string) " GST_AUDIO_NE (F32) ", " \ + "channels = (int) 1, layout = (string) non-interleaved," \ + "rate = (int) 48000" + +static GstFlowReturn +interleave_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstMapInfo map; + gfloat *outdata; + gint i; + + fail_unless (GST_IS_BUFFER (buffer)); + fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)); + gst_buffer_map (buffer, &map, GST_MAP_READ); + outdata = (gfloat *) map.data; + fail_unless (outdata != NULL); + +#ifdef HAVE_VALGRIND + if (!(RUNNING_ON_VALGRIND)) +#endif + for (i = 0; i < map.size / sizeof (float); i += 2) { + fail_unless_equals_float (outdata[i], input[0]); + fail_unless_equals_float (outdata[i + 1], input[1]); + } + + g_mutex_lock (&data_mutex); + have_data += map.size; + g_cond_signal (&data_cond); + g_mutex_unlock (&data_mutex); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + + return GST_FLOW_OK; +} + +GST_START_TEST (test_audiointerleave_2ch) +{ + GstElement *queue; + GstPad *sink0, *sink1, *src, *tmp; + GstCaps *caps; + gint i; + GstBuffer *inbuf; + gfloat *indata; + GstMapInfo map; + + mysrcpads = g_new0 (GstPad *, 2); + + have_data = 0; + + interleave = gst_element_factory_make ("audiointerleave", NULL); + fail_unless (interleave != NULL); + + g_object_set (interleave, "latency", GST_SECOND / 4, NULL); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + + sink0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sink0 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (sink0), "sink_0"); + + sink1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sink1 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (sink1), "sink_1"); + + mysrcpads[0] = gst_pad_new_from_static_template (&srctemplate, "src0"); + fail_unless (mysrcpads[0] != NULL); + + caps = gst_caps_from_string (CAPS_48khz); + gst_pad_set_active (mysrcpads[0], TRUE); + gst_check_setup_events_audiointerleave (mysrcpads[0], interleave, caps, + GST_FORMAT_TIME, "0"); + gst_pad_use_fixed_caps (mysrcpads[0]); + + mysrcpads[1] = gst_pad_new_from_static_template (&srctemplate, "src1"); + fail_unless (mysrcpads[1] != NULL); + + gst_pad_set_active (mysrcpads[1], TRUE); + gst_check_setup_events_audiointerleave (mysrcpads[1], interleave, caps, + GST_FORMAT_TIME, "1"); + gst_pad_use_fixed_caps (mysrcpads[1]); + + tmp = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (mysrcpads[0], tmp) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sink0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + fail_unless (gst_pad_link (mysrcpads[1], sink1) == GST_PAD_LINK_OK); + + mysinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); + fail_unless (mysinkpad != NULL); + gst_pad_set_chain_function (mysinkpad, interleave_chain_func); + gst_pad_set_active (mysinkpad, TRUE); + + src = gst_element_get_static_pad (interleave, "src"); + fail_unless (src != NULL); + fail_unless (gst_pad_link (src, mysinkpad) == GST_PAD_LINK_OK); + gst_object_unref (src); + + bus = gst_bus_new (); + gst_element_set_bus (interleave, bus); + + fail_unless (gst_element_set_state (interleave, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + fail_unless (gst_element_set_state (queue, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + + input[0] = -1.0; + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + //GST_BUFFER_PTS (inbuf) = 0; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = -1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK); + + input[1] = 1.0; + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + //GST_BUFFER_PTS (inbuf) = 0; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = 1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK); + + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + //GST_BUFFER_PTS (inbuf) = GST_SECOND; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = -1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK); + + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + //GST_BUFFER_PTS (inbuf) = GST_SECOND; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = 1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK); + + g_mutex_lock (&data_mutex); + while (have_data < 48000 * 2 * 2 * sizeof (float)) + g_cond_wait (&data_cond, &data_mutex); + g_mutex_unlock (&data_mutex); + + gst_element_set_state (interleave, GST_STATE_NULL); + gst_element_set_state (queue, GST_STATE_NULL); + + gst_object_unref (mysrcpads[0]); + gst_object_unref (mysrcpads[1]); + gst_object_unref (mysinkpad); + + gst_element_release_request_pad (interleave, sink0); + gst_object_unref (sink0); + gst_element_release_request_pad (interleave, sink1); + gst_object_unref (sink1); + + gst_object_unref (interleave); + gst_object_unref (queue); + gst_object_unref (bus); + gst_caps_unref (caps); + + g_free (mysrcpads); +} + +GST_END_TEST; + +GST_START_TEST (test_audiointerleave_2ch_1eos) +{ + GstElement *queue; + GstPad *sink0, *sink1, *src, *tmp; + GstCaps *caps; + gint i; + GstBuffer *inbuf; + gfloat *indata; + GstMapInfo map; + + mysrcpads = g_new0 (GstPad *, 2); + + have_data = 0; + + interleave = gst_element_factory_make ("audiointerleave", NULL); + fail_unless (interleave != NULL); + + g_object_set (interleave, "latency", GST_SECOND / 4, NULL); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + + sink0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sink0 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (sink0), "sink_0"); + + sink1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sink1 != NULL); + fail_unless_equals_string (GST_OBJECT_NAME (sink1), "sink_1"); + + mysrcpads[0] = gst_pad_new_from_static_template (&srctemplate, "src0"); + fail_unless (mysrcpads[0] != NULL); + + caps = gst_caps_from_string (CAPS_48khz); + gst_pad_set_active (mysrcpads[0], TRUE); + gst_check_setup_events_audiointerleave (mysrcpads[0], interleave, caps, + GST_FORMAT_TIME, "0"); + gst_pad_use_fixed_caps (mysrcpads[0]); + + mysrcpads[1] = gst_pad_new_from_static_template (&srctemplate, "src1"); + fail_unless (mysrcpads[1] != NULL); + + gst_pad_set_active (mysrcpads[1], TRUE); + gst_check_setup_events_audiointerleave (mysrcpads[1], interleave, caps, + GST_FORMAT_TIME, "1"); + gst_pad_use_fixed_caps (mysrcpads[1]); + + tmp = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (mysrcpads[0], tmp) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sink0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + fail_unless (gst_pad_link (mysrcpads[1], sink1) == GST_PAD_LINK_OK); + + mysinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); + fail_unless (mysinkpad != NULL); + gst_pad_set_chain_function (mysinkpad, interleave_chain_func); + gst_pad_set_active (mysinkpad, TRUE); + + src = gst_element_get_static_pad (interleave, "src"); + fail_unless (src != NULL); + fail_unless (gst_pad_link (src, mysinkpad) == GST_PAD_LINK_OK); + gst_object_unref (src); + + bus = gst_bus_new (); + gst_element_set_bus (interleave, bus); + + fail_unless (gst_element_set_state (interleave, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + fail_unless (gst_element_set_state (queue, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); + + input[0] = -1.0; + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + GST_BUFFER_PTS (inbuf) = 0; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = -1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK); + + input[1] = 1.0; + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + GST_BUFFER_PTS (inbuf) = 0; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = 1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK); + + g_mutex_lock (&data_mutex); + /* 48000 samples per buffer * 2 sources * 2 buffers */ + while (have_data != 48000 * 2 * sizeof (float)) + g_cond_wait (&data_cond, &data_mutex); + g_mutex_unlock (&data_mutex); + + input[0] = 0.0; + gst_pad_push_event (mysrcpads[0], gst_event_new_eos ()); + + input[1] = 1.0; + inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat)); + GST_BUFFER_PTS (inbuf) = GST_SECOND; + gst_buffer_map (inbuf, &map, GST_MAP_WRITE); + indata = (gfloat *) map.data; + for (i = 0; i < 48000; i++) + indata[i] = 1.0; + gst_buffer_unmap (inbuf, &map); + fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK); + + g_mutex_lock (&data_mutex); + /* 48000 samples per buffer * 2 sources * 2 buffers */ + while (have_data != 48000 * 2 * 2 * sizeof (float)) + g_cond_wait (&data_cond, &data_mutex); + g_mutex_unlock (&data_mutex); + + gst_element_set_state (interleave, GST_STATE_NULL); + gst_element_set_state (queue, GST_STATE_NULL); + + gst_object_unref (mysrcpads[0]); + gst_object_unref (mysrcpads[1]); + gst_object_unref (mysinkpad); + + gst_element_release_request_pad (interleave, sink0); + gst_object_unref (sink0); + gst_element_release_request_pad (interleave, sink1); + gst_object_unref (sink1); + + gst_object_unref (interleave); + gst_object_unref (queue); + gst_object_unref (bus); + gst_caps_unref (caps); + + g_free (mysrcpads); +} + +GST_END_TEST; + +static void +src_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, + gboolean interleaved, gpointer user_data) +{ + gint n = GPOINTER_TO_INT (user_data); + gfloat *data; + gint i; + gsize size; + GstCaps *caps; + guint64 mask; + GstAudioChannelPosition pos; + + fail_unless (gst_buffer_is_writable (buffer)); + + switch (n) { + case 0: + case 1: + case 2: + pos = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; + break; + case 3: + pos = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; + break; + default: + pos = GST_AUDIO_CHANNEL_POSITION_INVALID; + break; + } + + mask = G_GUINT64_CONSTANT (1) << pos; + + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), + "channels", G_TYPE_INT, 1, + "layout", G_TYPE_STRING, interleaved ? "interleaved" : "non-interleaved", + "channel-mask", GST_TYPE_BITMASK, mask, "rate", G_TYPE_INT, 48000, NULL); + + gst_pad_set_caps (pad, caps); + gst_caps_unref (caps); + + size = 48000 * sizeof (gfloat); + data = g_malloc (size); + for (i = 0; i < 48000; i++) + data[i] = (n % 2 == 0) ? -1.0 : 1.0; + + gst_buffer_append_memory (buffer, gst_memory_new_wrapped (0, data, + size, 0, size, data, g_free)); + + GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; + GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_DURATION (buffer) = GST_SECOND; +} + +static void +src_handoff_float32_audiointerleaved (GstElement * element, GstBuffer * buffer, + GstPad * pad, gpointer user_data) +{ + src_handoff_float32 (element, buffer, pad, TRUE, user_data); +} + +static void +src_handoff_float32_non_audiointerleaved (GstElement * element, GstBuffer * buffer, + GstPad * pad, gpointer user_data) +{ + src_handoff_float32 (element, buffer, pad, FALSE, user_data); +} + +static void +sink_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + gint i; + GstMapInfo map; + gfloat *data; + GstCaps *caps, *ccaps; + gint n = GPOINTER_TO_INT (user_data); + guint64 mask; + + fail_unless (GST_IS_BUFFER (buffer)); + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = (gfloat *) map.data; + + /* Give a little leeway for rounding errors */ + fail_unless (gst_util_uint64_scale (map.size, GST_SECOND, + 48000 * 2 * sizeof (gfloat)) <= GST_BUFFER_DURATION (buffer) + 1 || + gst_util_uint64_scale (map.size, GST_SECOND, + 48000 * 2 * sizeof (gfloat)) >= GST_BUFFER_DURATION (buffer) - 1); + + if (n == 0) { + GstAudioChannelPosition pos[2] = + { GST_AUDIO_CHANNEL_POSITION_NONE, GST_AUDIO_CHANNEL_POSITION_NONE }; + gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask); + } else if (n == 1) { + GstAudioChannelPosition pos[2] = { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT + }; + gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask); + } else if (n == 2) { + GstAudioChannelPosition pos[2] = { GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER + }; + gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask); + } else { + g_assert_not_reached (); + } + + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), + "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000, + "layout", G_TYPE_STRING, "interleaved", + "channel-mask", GST_TYPE_BITMASK, mask, NULL); + + ccaps = gst_pad_get_current_caps (pad); + fail_unless (gst_caps_is_equal (caps, ccaps)); + gst_caps_unref (ccaps); + gst_caps_unref (caps); + +#ifdef HAVE_VALGRIND + if (!(RUNNING_ON_VALGRIND)) +#endif + for (i = 0; i < map.size / sizeof (float); i += 2) { + fail_unless_equals_float (data[i], -1.0); + fail_unless_equals_float (data[i + 1], 1.0); + } + have_data += map.size; + + gst_buffer_unmap (buffer, &map); + +} + +static void +test_audiointerleave_2ch_pipeline (gboolean interleaved) +{ + GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink; + GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2; + GstMessage *msg; + void *src_handoff_float32 = + interleaved ? &src_handoff_float32_audiointerleaved : + &src_handoff_float32_non_audiointerleaved; + + have_data = 0; + + pipeline = (GstElement *) gst_pipeline_new ("pipeline"); + fail_unless (pipeline != NULL); + + src1 = gst_element_factory_make ("fakesrc", "src1"); + fail_unless (src1 != NULL); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src1, "handoff", G_CALLBACK (src_handoff_float32), + GINT_TO_POINTER (0)); + gst_bin_add (GST_BIN (pipeline), src1); + + src2 = gst_element_factory_make ("fakesrc", "src2"); + fail_unless (src2 != NULL); + g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src2, "handoff", G_CALLBACK (src_handoff_float32), + GINT_TO_POINTER (1)); + gst_bin_add (GST_BIN (pipeline), src2); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + gst_bin_add (GST_BIN (pipeline), queue); + + interleave = gst_element_factory_make ("audiointerleave", "audiointerleave"); + fail_unless (interleave != NULL); + gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave)); + + sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad0 != NULL); + tmp = gst_element_get_static_pad (src1, "src"); + fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad1 != NULL); + tmp = gst_element_get_static_pad (src2, "src"); + tmp2 = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sink = gst_element_factory_make ("fakesink", "sink"); + fail_unless (sink != NULL); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32), + GINT_TO_POINTER (0)); + gst_bin_add (GST_BIN (pipeline), sink); + tmp = gst_element_get_static_pad (interleave, "src"); + tmp2 = gst_element_get_static_pad (sink, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1); + gst_message_unref (msg); + + /* 48000 samples per buffer * 2 sources * 4 buffers */ + fail_unless (have_data == 48000 * 2 * 4 * sizeof (gfloat)); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (interleave, sinkpad0); + gst_object_unref (sinkpad0); + gst_element_release_request_pad (interleave, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (interleave); + gst_object_unref (pipeline); +} + +GST_START_TEST (test_audiointerleave_2ch_pipeline_audiointerleaved) +{ + test_audiointerleave_2ch_pipeline (TRUE); +} + +GST_END_TEST; + +GST_START_TEST (test_audiointerleave_2ch_pipeline_non_audiointerleaved) +{ + test_audiointerleave_2ch_pipeline (FALSE); +} + +GST_END_TEST; + +GST_START_TEST (test_audiointerleave_2ch_pipeline_input_chanpos) +{ + GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink; + GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2; + GstMessage *msg; + + have_data = 0; + + pipeline = (GstElement *) gst_pipeline_new ("pipeline"); + fail_unless (pipeline != NULL); + + src1 = gst_element_factory_make ("fakesrc", "src1"); + fail_unless (src1 != NULL); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src1, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (2)); + gst_bin_add (GST_BIN (pipeline), src1); + + src2 = gst_element_factory_make ("fakesrc", "src2"); + fail_unless (src2 != NULL); + g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src2, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (3)); + gst_bin_add (GST_BIN (pipeline), src2); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + gst_bin_add (GST_BIN (pipeline), queue); + + interleave = gst_element_factory_make ("audiointerleave", "audiointerleave"); + fail_unless (interleave != NULL); + g_object_set (interleave, "channel-positions-from-input", TRUE, NULL); + gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave)); + + sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad0 != NULL); + tmp = gst_element_get_static_pad (src1, "src"); + fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad1 != NULL); + tmp = gst_element_get_static_pad (src2, "src"); + tmp2 = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sink = gst_element_factory_make ("fakesink", "sink"); + fail_unless (sink != NULL); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32), + GINT_TO_POINTER (1)); + gst_bin_add (GST_BIN (pipeline), sink); + tmp = gst_element_get_static_pad (interleave, "src"); + tmp2 = gst_element_get_static_pad (sink, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1); + gst_message_unref (msg); + + /* 48000 samples per buffer * 2 sources * 4 buffers */ + fail_unless (have_data == 48000 * 2 * 4 * sizeof (gfloat)); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (interleave, sinkpad0); + gst_object_unref (sinkpad0); + gst_element_release_request_pad (interleave, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (interleave); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +GST_START_TEST (test_audiointerleave_2ch_pipeline_custom_chanpos) +{ + GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink; + GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2; + GstMessage *msg; + GValueArray *arr; + GValue val = { 0, }; + + have_data = 0; + + pipeline = (GstElement *) gst_pipeline_new ("pipeline"); + fail_unless (pipeline != NULL); + + src1 = gst_element_factory_make ("fakesrc", "src1"); + fail_unless (src1 != NULL); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src1, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (0)); + gst_bin_add (GST_BIN (pipeline), src1); + + src2 = gst_element_factory_make ("fakesrc", "src2"); + fail_unless (src2 != NULL); + g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src2, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (1)); + gst_bin_add (GST_BIN (pipeline), src2); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + gst_bin_add (GST_BIN (pipeline), queue); + + interleave = gst_element_factory_make ("audiointerleave", "audiointerleave"); + fail_unless (interleave != NULL); + g_object_set (interleave, "channel-positions-from-input", FALSE, NULL); + arr = g_value_array_new (2); + + g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); + g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER); + g_value_array_append (arr, &val); + g_value_reset (&val); + g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER); + g_value_array_append (arr, &val); + g_value_unset (&val); + + g_object_set (interleave, "channel-positions", arr, NULL); + g_value_array_free (arr); + gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave)); + + sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad0 != NULL); + tmp = gst_element_get_static_pad (src1, "src"); + fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad1 != NULL); + tmp = gst_element_get_static_pad (src2, "src"); + tmp2 = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sink = gst_element_factory_make ("fakesink", "sink"); + fail_unless (sink != NULL); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32), + GINT_TO_POINTER (2)); + gst_bin_add (GST_BIN (pipeline), sink); + tmp = gst_element_get_static_pad (interleave, "src"); + tmp2 = gst_element_get_static_pad (sink, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1); + gst_message_unref (msg); + + /* 48000 samples per buffer * 2 sources * 4 buffers */ + fail_unless (have_data == 48000 * 2 * 4 * sizeof (gfloat)); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (interleave, sinkpad0); + gst_object_unref (sinkpad0); + gst_element_release_request_pad (interleave, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (interleave); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +static Suite * +audiointerleave_suite (void) +{ + Suite *s = suite_create ("audiointerleave"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_set_timeout (tc_chain, 180); + tcase_add_test (tc_chain, test_create_and_unref); + tcase_add_test (tc_chain, test_request_pads); + tcase_add_test (tc_chain, test_audiointerleave_2ch); + tcase_add_test (tc_chain, test_audiointerleave_2ch_1eos); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_audiointerleaved); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_non_audiointerleaved); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_input_chanpos); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_custom_chanpos); + + return s; +} + +GST_CHECK_MAIN (audiointerleave); From a7cfb6240ff0da02ecc0147a83c11131737dfa6f Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Tue, 24 Mar 2015 15:45:25 +0000 Subject: [PATCH 067/153] audioaggregator: check sink caps are valid CID #1291622 --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index b52635015d..647f36a234 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -617,7 +617,7 @@ gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps) { GST_OBJECT_LOCK (pad); - gst_audio_info_from_caps (&pad->info, caps); + g_assert (gst_audio_info_from_caps (&pad->info, caps)); GST_OBJECT_UNLOCK (pad); } From 8199405dd790f61da6c914542832abd8304024d3 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Tue, 24 Mar 2015 16:17:00 +0000 Subject: [PATCH 068/153] Revert "audioaggregator: check sink caps are valid" This reverts commit 6d4d0d1cdf7c7531fbf72ce39e1eab1260d20550. Never put code with side effects into an assertion, it can be compiled out --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 647f36a234..b52635015d 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -617,7 +617,7 @@ gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps) { GST_OBJECT_LOCK (pad); - g_assert (gst_audio_info_from_caps (&pad->info, caps)); + gst_audio_info_from_caps (&pad->info, caps); GST_OBJECT_UNLOCK (pad); } From 1011a50766a94911db6de33deaa18946bc054b39 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Tue, 24 Mar 2015 16:18:22 +0000 Subject: [PATCH 069/153] audioaggregator: check sink caps are valid --- gst/audiomixer/gstaudioaggregator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index b52635015d..eb2fb1ebd7 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -616,9 +616,13 @@ void gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps) { + gboolean valid; + GST_OBJECT_LOCK (pad); - gst_audio_info_from_caps (&pad->info, caps); + valid = gst_audio_info_from_caps (&pad->info, caps); GST_OBJECT_UNLOCK (pad); + + g_assert (valid); } From 5d78c5cca6532ce1e3593a9dbcc14f38700ff9e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 1 Apr 2015 20:32:41 -0400 Subject: [PATCH 070/153] audiomixer: Allow downstream caps with a non-default channel-mask Instead of failing, take the downstream channel mask if the channel count is 1. --- gst/audiomixer/gstaudiomixer.c | 28 +++++++++++++++++++++++++++- tests/check/elements/audiomixer.c | 3 ++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 40ede84346..f0e8f66ebc 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -350,11 +350,12 @@ static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, GstCaps * orig_caps) { + GstAggregator *agg = GST_AGGREGATOR (audiomixer); GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (audiomixer); GstCaps *caps; GstAudioInfo info; GstStructure *s; - gint channels; + gint channels = 0; gboolean ret; caps = gst_caps_copy (orig_caps); @@ -367,6 +368,31 @@ gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, if (!gst_audio_info_from_caps (&info, caps)) goto invalid_format; + if (channels == 1) { + GstCaps *filter; + GstCaps *downstream_caps; + + if (audiomixer->filter_caps) + filter = gst_caps_intersect_full (caps, audiomixer->filter_caps, + GST_CAPS_INTERSECT_FIRST); + else + filter = gst_caps_ref (caps); + + downstream_caps = gst_pad_peer_query_caps (agg->srcpad, filter); + gst_caps_unref (filter); + + if (downstream_caps) { + gst_caps_unref (caps); + caps = downstream_caps; + + if (gst_caps_is_empty (caps)) { + gst_caps_unref (caps); + return FALSE; + } + caps = gst_caps_fixate (caps); + } + } + GST_OBJECT_LOCK (audiomixer); /* don't allow reconfiguration for now; there's still a race between the * different upstream threads doing query_caps + accept_caps + sending diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index d758e75eea..b6235dce0c 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -89,7 +89,8 @@ GST_START_TEST (test_filter_caps) filter_caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "layout", G_TYPE_STRING, "interleaved", - "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL); + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, + "channel-mask", GST_TYPE_BITMASK, 0x04, NULL); /* build pipeline */ pipeline = gst_pipeline_new ("pipeline"); From 609f6703f4fe682655c3fad039ae54080449926c Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 9 Apr 2015 16:20:44 +0100 Subject: [PATCH 071/153] tests: fix type mismatch in varargs passing A bitmask is 64 bits, but integer immediates are passed as int in varargs, which happen to be 32 bit with high probability. This triggered a valgrind jump-relies-on-uninitalized-value report well away from the site, since it doesn't trigger on stack accesses, and there must have been enough zeroes to stop g_object_set at the right place. --- tests/check/elements/audiomixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index b6235dce0c..ddfdbe188e 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -90,7 +90,7 @@ GST_START_TEST (test_filter_caps) "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, - "channel-mask", GST_TYPE_BITMASK, 0x04, NULL); + "channel-mask", GST_TYPE_BITMASK, (guint64) 0x04, NULL); /* build pipeline */ pipeline = gst_pipeline_new ("pipeline"); From 891c7c6149e6c9a869b6edfa257b6e865560a9a5 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Sat, 16 May 2015 23:38:14 -0400 Subject: [PATCH 072/153] doc: Workaround gtkdoc issue With gtkdoc 1.22, the XML generator fails when a itemizedlist is followed by a refsect2. Workaround the issue by wrapping the refsect2 into para. --- gst/audiomixer/gstaudiomixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index f0e8f66ebc..57be45f2cf 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -42,13 +42,14 @@ * * * + * * * Example launch line * |[ * gst-launch audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. * ]| This pipeline produces two sine waves mixed together. * - * + * */ #ifdef HAVE_CONFIG_H From af5c05caf15fb51acffa0ee5ae8e618a90e2e81f Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 18 May 2015 20:16:32 +0200 Subject: [PATCH 073/153] Revert "doc: Workaround gtkdoc issue" This reverts commit ff6c736fe08e01f4320c4b02e811a0b57cf97cc1. This is fixed by the gtk-doc 1.23 release. cannot contain : http://www.docbook.org/tdg/en/html/para.html http://www.docbook.org/tdg/en/html/refsect2.html --- gst/audiomixer/gstaudiomixer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 57be45f2cf..f0e8f66ebc 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -42,14 +42,13 @@ * * * - * * * Example launch line * |[ * gst-launch audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. * ]| This pipeline produces two sine waves mixed together. * - * + * */ #ifdef HAVE_CONFIG_H From 47d7b546c908291bad6f6c18dfdd354058edf21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 1 Jun 2015 19:42:49 -0400 Subject: [PATCH 074/153] audiointerleave: Use the channel count from the set caps This is the same number that was used to allocate the buffer --- gst/audiomixer/gstaudiointerleave.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index fc984298e7..5b55f301f7 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -822,24 +822,25 @@ gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (aaggpad); GstMapInfo inmap; GstMapInfo outmap; - gint out_width, in_bpf, out_bpf; + gint out_width, in_bpf, out_bpf, out_channels; 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); + out_channels = GST_AUDIO_INFO_CHANNELS (&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, + " from offset %u", num_frames, pad->channel, out_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, + self->func (outdata, inmap.data + (in_offset * in_bpf), out_channels, num_frames); From 0fbf2da1bb84fbd48f11e7bf0dbe6374d6e115b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 1 Jun 2015 19:43:20 -0400 Subject: [PATCH 075/153] audiointerleave: Always have "channels" be the actual pad count Don't force it anywhere https://bugzilla.gnome.org/show_bug.cgi?id=750252 --- gst/audiomixer/gstaudiointerleave.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index 5b55f301f7..01ce19ccaf 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -669,7 +669,6 @@ gst_audio_interleave_set_property (GObject * object, guint prop_id, 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); @@ -727,16 +726,15 @@ gst_audio_interleave_request_new_pad (GstElement * element, GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (element); GstAudioInterleavePad *newpad; gchar *pad_name; - gint channels, padnumber; + gint channel, 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; + channel = g_atomic_int_add (&self->channels, 1); + if (!self->channel_positions_from_input) + channel = padnumber; pad_name = g_strdup_printf ("sink_%u", padnumber); newpad = (GstAudioInterleavePad *) @@ -746,7 +744,7 @@ gst_audio_interleave_request_new_pad (GstElement * element, if (newpad == NULL) goto could_not_create; - newpad->channel = channels; + newpad->channel = channel; gst_pad_use_fixed_caps (GST_PAD (newpad)); gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), From 28fc944c6e46c9b0a2e9ea4616b7cbba9ba8c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 2 Jun 2015 15:44:57 -0400 Subject: [PATCH 076/153] tests: Fix indentation in audiointerleave test --- tests/check/elements/audiointerleave.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index 83aaf0f48f..4d09b45bee 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -491,8 +491,8 @@ src_handoff_float32_audiointerleaved (GstElement * element, GstBuffer * buffer, } static void -src_handoff_float32_non_audiointerleaved (GstElement * element, GstBuffer * buffer, - GstPad * pad, gpointer user_data) +src_handoff_float32_non_audiointerleaved (GstElement * element, + GstBuffer * buffer, GstPad * pad, gpointer user_data) { src_handoff_float32 (element, buffer, pad, FALSE, user_data); } @@ -860,7 +860,8 @@ audiointerleave_suite (void) tcase_add_test (tc_chain, test_audiointerleave_2ch); tcase_add_test (tc_chain, test_audiointerleave_2ch_1eos); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_audiointerleaved); - tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_non_audiointerleaved); + tcase_add_test (tc_chain, + test_audiointerleave_2ch_pipeline_non_audiointerleaved); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_input_chanpos); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_custom_chanpos); From b57388bce78a35e2295ac6b08453bf71a3a9a5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 2 Jun 2015 15:45:13 -0400 Subject: [PATCH 077/153] tests: audiointerleave: test not setting positions Disable "channel-positions-from-input", but without actually giving a position table, so every position should be NONE --- tests/check/elements/audiointerleave.c | 87 ++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index 4d09b45bee..41788e1a6d 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -847,6 +847,92 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_custom_chanpos) GST_END_TEST; +GST_START_TEST (test_audiointerleave_2ch_pipeline_no_chanpos) +{ + GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink; + GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2; + GstMessage *msg; + + have_data = 0; + + pipeline = (GstElement *) gst_pipeline_new ("pipeline"); + fail_unless (pipeline != NULL); + + src1 = gst_element_factory_make ("fakesrc", "src1"); + fail_unless (src1 != NULL); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src1, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (0)); + gst_bin_add (GST_BIN (pipeline), src1); + + src2 = gst_element_factory_make ("fakesrc", "src2"); + fail_unless (src2 != NULL); + g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "format", GST_FORMAT_TIME, NULL); + g_signal_connect (src2, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (1)); + gst_bin_add (GST_BIN (pipeline), src2); + + queue = gst_element_factory_make ("queue", "queue"); + fail_unless (queue != NULL); + gst_bin_add (GST_BIN (pipeline), queue); + + interleave = gst_element_factory_make ("audiointerleave", "audiointerleave"); + fail_unless (interleave != NULL); + g_object_set (interleave, "channel-positions-from-input", FALSE, NULL); + gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave)); + + sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad0 != NULL); + tmp = gst_element_get_static_pad (src1, "src"); + fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u"); + fail_unless (sinkpad1 != NULL); + tmp = gst_element_get_static_pad (src2, "src"); + tmp2 = gst_element_get_static_pad (queue, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + tmp = gst_element_get_static_pad (queue, "src"); + fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + + sink = gst_element_factory_make ("fakesink", "sink"); + fail_unless (sink != NULL); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32), + GINT_TO_POINTER (0)); + gst_bin_add (GST_BIN (pipeline), sink); + tmp = gst_element_get_static_pad (interleave, "src"); + tmp2 = gst_element_get_static_pad (sink, "sink"); + fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK); + gst_object_unref (tmp); + gst_object_unref (tmp2); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1); + gst_message_unref (msg); + + /* 48000 samples per buffer * 2 sources * 4 buffers */ + fail_unless (have_data == 48000 * 2 * 4 * sizeof (gfloat)); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (interleave, sinkpad0); + gst_object_unref (sinkpad0); + gst_element_release_request_pad (interleave, sinkpad1); + gst_object_unref (sinkpad1); + gst_object_unref (interleave); + gst_object_unref (pipeline); +} + +GST_END_TEST; + static Suite * audiointerleave_suite (void) { @@ -864,6 +950,7 @@ audiointerleave_suite (void) test_audiointerleave_2ch_pipeline_non_audiointerleaved); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_input_chanpos); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_custom_chanpos); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_no_chanpos); return s; } From baff8aa729a7e531a689fae8ac44205c25779d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 7 Jun 2015 10:55:35 +0200 Subject: [PATCH 078/153] Release 1.5.1 --- gst/audiomixer/gstaudiomixerorc-dist.c | 306 ++++++++++--------------- 1 file changed, 125 insertions(+), 181 deletions(-) diff --git a/gst/audiomixer/gstaudiomixerorc-dist.c b/gst/audiomixer/gstaudiomixerorc-dist.c index 092a464beb..be377f7054 100644 --- a/gst/audiomixer/gstaudiomixerorc-dist.c +++ b/gst/audiomixer/gstaudiomixerorc-dist.c @@ -1120,16 +1120,11 @@ audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) orc_int8 var35; #endif orc_int8 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_int8 var37; -#else orc_int8 var37; -#endif orc_int8 var38; - orc_int8 var39; + orc_union16 var39; orc_union16 var40; - orc_union16 var41; - orc_int8 var42; + orc_int8 var41; ptr0 = (orc_int8 *) d1; @@ -1137,24 +1132,22 @@ audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ /* 3: loadpb */ var36 = p1; - /* 7: loadpb */ - var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ var34 = ptr0[i]; /* 2: xorb */ - var39 = var34 ^ var35; + var38 = var34 ^ var35; /* 4: mulsbw */ - var40.i = var39 * var36; + var39.i = var38 * var36; /* 5: shrsw */ - var41.i = var40.i >> 3; + var40.i = var39.i >> 3; /* 6: convssswb */ - var42 = ORC_CLAMP_SB (var41.i); - /* 8: xorb */ - var38 = var42 ^ var37; - /* 9: storeb */ - ptr0[i] = var38; + var41 = ORC_CLAMP_SB (var40.i); + /* 7: xorb */ + var37 = var41 ^ var35; + /* 8: storeb */ + ptr0[i] = var37; } } @@ -1173,16 +1166,11 @@ _backup_audiomixer_orc_volume_u8 (OrcExecutor * ORC_RESTRICT ex) orc_int8 var35; #endif orc_int8 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_int8 var37; -#else orc_int8 var37; -#endif orc_int8 var38; - orc_int8 var39; + orc_union16 var39; orc_union16 var40; - orc_union16 var41; - orc_int8 var42; + orc_int8 var41; ptr0 = (orc_int8 *) ex->arrays[0]; @@ -1190,24 +1178,22 @@ _backup_audiomixer_orc_volume_u8 (OrcExecutor * ORC_RESTRICT ex) var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ /* 3: loadpb */ var36 = ex->params[24]; - /* 7: loadpb */ - var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ var34 = ptr0[i]; /* 2: xorb */ - var39 = var34 ^ var35; + var38 = var34 ^ var35; /* 4: mulsbw */ - var40.i = var39 * var36; + var39.i = var38 * var36; /* 5: shrsw */ - var41.i = var40.i >> 3; + var40.i = var39.i >> 3; /* 6: convssswb */ - var42 = ORC_CLAMP_SB (var41.i); - /* 8: xorb */ - var38 = var42 ^ var37; - /* 9: storeb */ - ptr0[i] = var38; + var41 = ORC_CLAMP_SB (var40.i); + /* 7: xorb */ + var37 = var41 ^ var35; + /* 8: storeb */ + ptr0[i] = var37; } } @@ -1229,7 +1215,7 @@ audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) static const orc_uint8 bc[] = { 1, 9, 24, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, 99, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, 1, 1, 14, 1, - 128, 0, 0, 0, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, + 128, 0, 0, 0, 14, 2, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, 68, 33, 0, 16, 174, 32, 33, 24, 94, 32, 32, 17, 159, 33, 32, 68, 0, 33, 16, 2, 0, }; @@ -1241,7 +1227,7 @@ audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) orc_program_set_backup_function (p, _backup_audiomixer_orc_volume_u8); orc_program_add_destination (p, 1, "d1"); orc_program_add_constant (p, 1, 0x00000080, "c1"); - orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_constant (p, 2, 0x00000003, "c2"); orc_program_add_parameter (p, 1, "p1"); orc_program_add_temporary (p, 2, "t1"); orc_program_add_temporary (p, 1, "t2"); @@ -1294,18 +1280,13 @@ audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, orc_int8 var35; #endif orc_int8 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_int8 var37; -#else orc_int8 var37; -#endif orc_int8 var38; orc_int8 var39; - orc_int8 var40; + orc_union16 var40; orc_union16 var41; - orc_union16 var42; + orc_int8 var42; orc_int8 var43; - orc_int8 var44; ptr0 = (orc_int8 *) d1; ptr4 = (orc_int8 *) s1; @@ -1314,28 +1295,26 @@ audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ /* 3: loadpb */ var36 = p1; - /* 7: loadpb */ - var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ var34 = ptr4[i]; /* 2: xorb */ - var40 = var34 ^ var35; + var39 = var34 ^ var35; /* 4: mulsbw */ - var41.i = var40 * var36; + var40.i = var39 * var36; /* 5: shrsw */ - var42.i = var41.i >> 3; + var41.i = var40.i >> 3; /* 6: convssswb */ - var43 = ORC_CLAMP_SB (var42.i); - /* 8: xorb */ - var44 = var43 ^ var37; - /* 9: loadb */ - var38 = ptr0[i]; - /* 10: addusb */ - var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); - /* 11: storeb */ - ptr0[i] = var39; + var42 = ORC_CLAMP_SB (var41.i); + /* 7: xorb */ + var43 = var42 ^ var35; + /* 8: loadb */ + var37 = ptr0[i]; + /* 9: addusb */ + var38 = ORC_CLAMP_UB ((orc_uint8) var37 + (orc_uint8) var43); + /* 10: storeb */ + ptr0[i] = var38; } } @@ -1355,18 +1334,13 @@ _backup_audiomixer_orc_add_volume_u8 (OrcExecutor * ORC_RESTRICT ex) orc_int8 var35; #endif orc_int8 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_int8 var37; -#else orc_int8 var37; -#endif orc_int8 var38; orc_int8 var39; - orc_int8 var40; + orc_union16 var40; orc_union16 var41; - orc_union16 var42; + orc_int8 var42; orc_int8 var43; - orc_int8 var44; ptr0 = (orc_int8 *) ex->arrays[0]; ptr4 = (orc_int8 *) ex->arrays[4]; @@ -1375,28 +1349,26 @@ _backup_audiomixer_orc_add_volume_u8 (OrcExecutor * ORC_RESTRICT ex) var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ /* 3: loadpb */ var36 = ex->params[24]; - /* 7: loadpb */ - var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ for (i = 0; i < n; i++) { /* 0: loadb */ var34 = ptr4[i]; /* 2: xorb */ - var40 = var34 ^ var35; + var39 = var34 ^ var35; /* 4: mulsbw */ - var41.i = var40 * var36; + var40.i = var39 * var36; /* 5: shrsw */ - var42.i = var41.i >> 3; + var41.i = var40.i >> 3; /* 6: convssswb */ - var43 = ORC_CLAMP_SB (var42.i); - /* 8: xorb */ - var44 = var43 ^ var37; - /* 9: loadb */ - var38 = ptr0[i]; - /* 10: addusb */ - var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); - /* 11: storeb */ - ptr0[i] = var39; + var42 = ORC_CLAMP_SB (var41.i); + /* 7: xorb */ + var43 = var42 ^ var35; + /* 8: loadb */ + var37 = ptr0[i]; + /* 9: addusb */ + var38 = ORC_CLAMP_UB ((orc_uint8) var37 + (orc_uint8) var43); + /* 10: storeb */ + ptr0[i] = var38; } } @@ -1419,7 +1391,7 @@ audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, static const orc_uint8 bc[] = { 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, - 1, 1, 12, 1, 1, 14, 1, 128, 0, 0, 0, 14, 4, 3, 0, 0, + 1, 1, 12, 1, 1, 14, 1, 128, 0, 0, 0, 14, 2, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, 68, 33, 4, 16, 174, 32, 33, 24, 94, 32, 32, 17, 159, 33, 32, 68, 33, 33, 16, 35, 0, 0, 33, 2, 0, @@ -1433,7 +1405,7 @@ audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, orc_program_add_destination (p, 1, "d1"); orc_program_add_source (p, 1, "s1"); orc_program_add_constant (p, 1, 0x00000080, "c1"); - orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_constant (p, 2, 0x00000003, "c2"); orc_program_add_parameter (p, 1, "p1"); orc_program_add_temporary (p, 2, "t1"); orc_program_add_temporary (p, 1, "t2"); @@ -1574,7 +1546,7 @@ audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, static const orc_uint8 bc[] = { 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 56, 11, - 1, 1, 12, 1, 1, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, + 1, 1, 12, 1, 1, 14, 2, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, 174, 32, 4, 24, 94, 32, 32, 16, 159, 33, 32, 34, 0, 0, 33, 2, 0, }; @@ -1586,7 +1558,7 @@ audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_s8); orc_program_add_destination (p, 1, "d1"); orc_program_add_source (p, 1, "s1"); - orc_program_add_constant (p, 4, 0x00000003, "c1"); + orc_program_add_constant (p, 2, 0x00000003, "c1"); orc_program_add_parameter (p, 1, "p1"); orc_program_add_temporary (p, 2, "t1"); orc_program_add_temporary (p, 1, "t2"); @@ -1638,18 +1610,13 @@ audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, orc_union16 var35; #endif orc_union16 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_union16 var37; -#else orc_union16 var37; -#endif orc_union16 var38; orc_union16 var39; - orc_union16 var40; + orc_union32 var40; orc_union32 var41; - orc_union32 var42; + orc_union16 var42; orc_union16 var43; - orc_union16 var44; ptr0 = (orc_union16 *) d1; ptr4 = (orc_union16 *) s1; @@ -1658,28 +1625,26 @@ audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ /* 3: loadpw */ var36.i = p1; - /* 7: loadpw */ - var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ for (i = 0; i < n; i++) { /* 0: loadw */ var34 = ptr4[i]; /* 2: xorw */ - var40.i = var34.i ^ var35.i; + var39.i = var34.i ^ var35.i; /* 4: mulswl */ - var41.i = var40.i * var36.i; + var40.i = var39.i * var36.i; /* 5: shrsl */ - var42.i = var41.i >> 11; + var41.i = var40.i >> 11; /* 6: convssslw */ - var43.i = ORC_CLAMP_SW (var42.i); - /* 8: xorw */ - var44.i = var43.i ^ var37.i; - /* 9: loadw */ - var38 = ptr0[i]; - /* 10: addusw */ - var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); - /* 11: storew */ - ptr0[i] = var39; + var42.i = ORC_CLAMP_SW (var41.i); + /* 7: xorw */ + var43.i = var42.i ^ var35.i; + /* 8: loadw */ + var37 = ptr0[i]; + /* 9: addusw */ + var38.i = ORC_CLAMP_UW ((orc_uint16) var37.i + (orc_uint16) var43.i); + /* 10: storew */ + ptr0[i] = var38; } } @@ -1699,18 +1664,13 @@ _backup_audiomixer_orc_add_volume_u16 (OrcExecutor * ORC_RESTRICT ex) orc_union16 var35; #endif orc_union16 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_union16 var37; -#else orc_union16 var37; -#endif orc_union16 var38; orc_union16 var39; - orc_union16 var40; + orc_union32 var40; orc_union32 var41; - orc_union32 var42; + orc_union16 var42; orc_union16 var43; - orc_union16 var44; ptr0 = (orc_union16 *) ex->arrays[0]; ptr4 = (orc_union16 *) ex->arrays[4]; @@ -1719,28 +1679,26 @@ _backup_audiomixer_orc_add_volume_u16 (OrcExecutor * ORC_RESTRICT ex) var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ /* 3: loadpw */ var36.i = ex->params[24]; - /* 7: loadpw */ - var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ for (i = 0; i < n; i++) { /* 0: loadw */ var34 = ptr4[i]; /* 2: xorw */ - var40.i = var34.i ^ var35.i; + var39.i = var34.i ^ var35.i; /* 4: mulswl */ - var41.i = var40.i * var36.i; + var40.i = var39.i * var36.i; /* 5: shrsl */ - var42.i = var41.i >> 11; + var41.i = var40.i >> 11; /* 6: convssslw */ - var43.i = ORC_CLAMP_SW (var42.i); - /* 8: xorw */ - var44.i = var43.i ^ var37.i; - /* 9: loadw */ - var38 = ptr0[i]; - /* 10: addusw */ - var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); - /* 11: storew */ - ptr0[i] = var39; + var42.i = ORC_CLAMP_SW (var41.i); + /* 7: xorw */ + var43.i = var42.i ^ var35.i; + /* 8: loadw */ + var37 = ptr0[i]; + /* 9: addusw */ + var38.i = ORC_CLAMP_UW ((orc_uint16) var37.i + (orc_uint16) var43.i); + /* 10: storew */ + ptr0[i] = var38; } } @@ -1986,18 +1944,13 @@ audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, orc_union32 var35; #endif orc_union32 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_union32 var37; -#else orc_union32 var37; -#endif orc_union32 var38; orc_union32 var39; - orc_union32 var40; + orc_union64 var40; orc_union64 var41; - orc_union64 var42; + orc_union32 var42; orc_union32 var43; - orc_union32 var44; ptr0 = (orc_union32 *) d1; ptr4 = (orc_union32 *) s1; @@ -2006,30 +1959,28 @@ audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ /* 3: loadpl */ var36.i = p1; - /* 7: loadpl */ - var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ for (i = 0; i < n; i++) { /* 0: loadl */ var34 = ptr4[i]; /* 2: xorl */ - var40.i = var34.i ^ var35.i; + var39.i = var34.i ^ var35.i; /* 4: mulslq */ - var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + var40.i = ((orc_int64) var39.i) * ((orc_int64) var36.i); /* 5: shrsq */ - var42.i = var41.i >> 27; + var41.i = var40.i >> 27; /* 6: convsssql */ - var43.i = ORC_CLAMP_SL (var42.i); - /* 8: xorl */ - var44.i = var43.i ^ var37.i; - /* 9: loadl */ - var38 = ptr0[i]; - /* 10: addusl */ - var39.i = - ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + - (orc_int64) (orc_uint32) var44.i); - /* 11: storel */ - ptr0[i] = var39; + var42.i = ORC_CLAMP_SL (var41.i); + /* 7: xorl */ + var43.i = var42.i ^ var35.i; + /* 8: loadl */ + var37 = ptr0[i]; + /* 9: addusl */ + var38.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var37.i + + (orc_int64) (orc_uint32) var43.i); + /* 10: storel */ + ptr0[i] = var38; } } @@ -2049,18 +2000,13 @@ _backup_audiomixer_orc_add_volume_u32 (OrcExecutor * ORC_RESTRICT ex) orc_union32 var35; #endif orc_union32 var36; -#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) - volatile orc_union32 var37; -#else orc_union32 var37; -#endif orc_union32 var38; orc_union32 var39; - orc_union32 var40; + orc_union64 var40; orc_union64 var41; - orc_union64 var42; + orc_union32 var42; orc_union32 var43; - orc_union32 var44; ptr0 = (orc_union32 *) ex->arrays[0]; ptr4 = (orc_union32 *) ex->arrays[4]; @@ -2069,30 +2015,28 @@ _backup_audiomixer_orc_add_volume_u32 (OrcExecutor * ORC_RESTRICT ex) var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ /* 3: loadpl */ var36.i = ex->params[24]; - /* 7: loadpl */ - var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ for (i = 0; i < n; i++) { /* 0: loadl */ var34 = ptr4[i]; /* 2: xorl */ - var40.i = var34.i ^ var35.i; + var39.i = var34.i ^ var35.i; /* 4: mulslq */ - var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + var40.i = ((orc_int64) var39.i) * ((orc_int64) var36.i); /* 5: shrsq */ - var42.i = var41.i >> 27; + var41.i = var40.i >> 27; /* 6: convsssql */ - var43.i = ORC_CLAMP_SL (var42.i); - /* 8: xorl */ - var44.i = var43.i ^ var37.i; - /* 9: loadl */ - var38 = ptr0[i]; - /* 10: addusl */ - var39.i = - ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + - (orc_int64) (orc_uint32) var44.i); - /* 11: storel */ - ptr0[i] = var39; + var42.i = ORC_CLAMP_SL (var41.i); + /* 7: xorl */ + var43.i = var42.i ^ var35.i; + /* 8: loadl */ + var37 = ptr0[i]; + /* 9: addusl */ + var38.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var37.i + + (orc_int64) (orc_uint32) var43.i); + /* 10: storel */ + ptr0[i] = var38; } } @@ -2115,10 +2059,10 @@ audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, static const orc_uint8 bc[] = { 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 51, 50, - 11, 4, 4, 12, 4, 4, 14, 4, 0, 0, 0, 128, 14, 4, 27, 0, - 0, 0, 16, 4, 20, 8, 20, 4, 132, 33, 4, 16, 178, 32, 33, 24, - 147, 32, 32, 17, 170, 33, 32, 132, 33, 33, 16, 105, 0, 0, 33, 2, - 0, + 11, 4, 4, 12, 4, 4, 14, 4, 0, 0, 0, 128, 15, 8, 27, 0, + 0, 0, 0, 0, 0, 0, 16, 4, 20, 8, 20, 4, 132, 33, 4, 16, + 178, 32, 33, 24, 147, 32, 32, 17, 170, 33, 32, 132, 33, 33, 16, 105, + 0, 0, 33, 2, 0, }; p = orc_program_new_from_static_bytecode (bc); orc_program_set_backup_function (p, @@ -2131,7 +2075,7 @@ audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, orc_program_add_destination (p, 4, "d1"); orc_program_add_source (p, 4, "s1"); orc_program_add_constant (p, 4, 0x80000000, "c1"); - orc_program_add_constant (p, 4, 0x0000001b, "c2"); + orc_program_add_constant_int64 (p, 8, 0x000000000000001bULL, "c2"); orc_program_add_parameter (p, 4, "p1"); orc_program_add_temporary (p, 8, "t1"); orc_program_add_temporary (p, 4, "t2"); @@ -2272,9 +2216,9 @@ audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, static const orc_uint8 bc[] = { 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 51, 50, - 11, 4, 4, 12, 4, 4, 14, 4, 27, 0, 0, 0, 16, 4, 20, 8, - 20, 4, 178, 32, 4, 24, 147, 32, 32, 16, 170, 33, 32, 104, 0, 0, - 33, 2, 0, + 11, 4, 4, 12, 4, 4, 15, 8, 27, 0, 0, 0, 0, 0, 0, 0, + 16, 4, 20, 8, 20, 4, 178, 32, 4, 24, 147, 32, 32, 16, 170, 33, + 32, 104, 0, 0, 33, 2, 0, }; p = orc_program_new_from_static_bytecode (bc); orc_program_set_backup_function (p, @@ -2286,7 +2230,7 @@ audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, _backup_audiomixer_orc_add_volume_s32); orc_program_add_destination (p, 4, "d1"); orc_program_add_source (p, 4, "s1"); - orc_program_add_constant (p, 4, 0x0000001b, "c1"); + orc_program_add_constant_int64 (p, 8, 0x000000000000001bULL, "c1"); orc_program_add_parameter (p, 4, "p1"); orc_program_add_temporary (p, 8, "t1"); orc_program_add_temporary (p, 4, "t2"); @@ -2651,8 +2595,8 @@ audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, { orc_union64 tmp; tmp.f = p1; - ex->params[ORC_VAR_P1] = tmp.x2[0]; - ex->params[ORC_VAR_T1] = tmp.x2[1]; + ex->params[ORC_VAR_P1] = ((orc_uint64) tmp.i) & 0xffffffff; + ex->params[ORC_VAR_T1] = ((orc_uint64) tmp.i) >> 32; } func = c->exec; From f64ebd1d21a08d588f2acd68333ff0501c9287af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 9 Jun 2015 14:37:36 +0100 Subject: [PATCH 079/153] audiomixer: fix misleading documentation copied from adder --- gst/audiomixer/gstaudiomixer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index f0e8f66ebc..3966a8fc27 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -27,8 +27,7 @@ * The audiomixer allows to mix several streams into one by adding the data. * Mixed data is clamped to the min/max values of the data format. * - * The audiomixer currently mixes all data received on the sinkpads as soon as - * possible without trying to synchronize the streams. + * Unlike the adder element audiomixer properly synchronises all input streams. * * The input pads are from a GstPad subclass and have additional * properties to mute each pad individually and set the volume: From a1cdede9404162162322cc3d77f03d4828f8da2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 21 Jul 2015 20:52:23 -0400 Subject: [PATCH 080/153] audioaggregator: Read output buffer duration with lock held --- gst/audiomixer/gstaudioaggregator.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index eb2fb1ebd7..01704f5330 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -1011,10 +1011,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) element = GST_ELEMENT (agg); aagg = GST_AUDIO_AGGREGATOR (agg); - blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, - GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); - blocksize = MAX (1, blocksize); - GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (agg); @@ -1062,6 +1058,9 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) rate = GST_AUDIO_INFO_RATE (&aagg->info); bpf = GST_AUDIO_INFO_BPF (&aagg->info); + blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, + GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); + blocksize = MAX (1, blocksize); /* for the next timestamp, use the sample counter, which will * never accumulate rounding errors */ From 74d7944cbb29c4f84404cb582669497dbde605ca Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 19 May 2015 16:08:08 +0530 Subject: [PATCH 081/153] audioaggregator: Sync pad values before aggregating We need to sync the pad values before taking the aggregator and pad locks otherwise the element will just deadlock if there's any property changes scheduled using GstController since that involves taking the aggregator and pad locks. Also add a test for this. https://bugzilla.gnome.org/show_bug.cgi?id=749574 --- gst/audiomixer/gstaudioaggregator.c | 37 ++++++++++---- tests/check/elements/audiomixer.c | 76 +++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 01704f5330..38e8709678 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -739,7 +739,6 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, GstClockTime start_time, end_time; gboolean discont = FALSE; guint64 start_offset, end_offset; - GstClockTime timestamp, stream_time = GST_CLOCK_TIME_NONE; gint rate, bpf; GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); @@ -762,15 +761,6 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, goto done; } - timestamp = GST_BUFFER_PTS (inbuf); - stream_time = gst_segment_to_stream_time (&aggpad->segment, GST_FORMAT_TIME, - timestamp); - - /* sync object properties on stream time */ - /* TODO: Ideally we would want to do that on every sample */ - if (GST_CLOCK_TIME_IS_VALID (stream_time)) - gst_object_sync_values (GST_OBJECT (pad), stream_time); - start_time = GST_BUFFER_PTS (inbuf); end_time = start_time + gst_util_uint64_scale_ceil (pad->priv->size, GST_SECOND, @@ -964,6 +954,29 @@ gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, return outbuf; } +static gboolean +sync_pad_values (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad) +{ + GstAggregatorPad *bpad = GST_AGGREGATOR_PAD (pad); + GstClockTime timestamp, stream_time; + + if (pad->priv->buffer == NULL) + return TRUE; + + timestamp = GST_BUFFER_PTS (pad->priv->buffer); + GST_OBJECT_LOCK (bpad); + stream_time = gst_segment_to_stream_time (&bpad->segment, GST_FORMAT_TIME, + timestamp); + GST_OBJECT_UNLOCK (bpad); + + /* sync object properties on stream time */ + /* TODO: Ideally we would want to do that on every sample */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (GST_OBJECT (pad), stream_time); + + return TRUE; +} + static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) { @@ -1011,6 +1024,10 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) element = GST_ELEMENT (agg); aagg = GST_AUDIO_AGGREGATOR (agg); + /* Sync pad properties to the stream time */ + gst_aggregator_iterate_sinkpads (agg, + (GstAggregatorPadForeachFunc) sync_pad_values, NULL); + GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (agg); diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index ddfdbe188e..8fe329d82f 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include static GMainLoop *main_loop; @@ -1835,6 +1837,79 @@ GST_START_TEST (test_segment_base_handling) GST_END_TEST; +static void +set_pad_volume_fade (GstPad * pad, GstClockTime start, gdouble start_value, + GstClockTime end, gdouble end_value) +{ + GstControlSource *cs; + GstTimedValueControlSource *tvcs; + + cs = gst_interpolation_control_source_new (); + fail_unless (gst_object_add_control_binding (GST_OBJECT_CAST (pad), + gst_direct_control_binding_new_absolute (GST_OBJECT_CAST (pad), + "volume", cs))); + + /* set volume interpolation mode */ + g_object_set (cs, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL); + + tvcs = (GstTimedValueControlSource *) cs; + fail_unless (gst_timed_value_control_source_set (tvcs, start, start_value)); + fail_unless (gst_timed_value_control_source_set (tvcs, end, end_value)); + gst_object_unref (cs); +} + +GST_START_TEST (test_sinkpad_property_controller) +{ + GstBus *bus; + GstMessage *msg; + GstElement *pipeline, *sink, *mix, *src1; + GstPad *srcpad, *sinkpad; + GError *error = NULL; + gchar *debug; + + pipeline = gst_pipeline_new ("pipeline"); + mix = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "num-buffers", 100, NULL); + gst_bin_add_many (GST_BIN (pipeline), src1, mix, sink, NULL); + fail_unless (gst_element_link (mix, sink)); + + srcpad = gst_element_get_static_pad (src1, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_0"); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + set_pad_volume_fade (sinkpad, 0, 0, 1.0, 2.0); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_EOS | GST_MESSAGE_ERROR); + switch (GST_MESSAGE_TYPE (msg)) { + case GST_MESSAGE_ERROR: + gst_message_parse_error (msg, &error, &debug); + g_printerr ("ERROR from element %s: %s\n", + GST_OBJECT_NAME (msg->src), error->message); + g_printerr ("Debug info: %s\n", debug); + g_error_free (error); + g_free (debug); + break; + case GST_MESSAGE_EOS: + break; + default: + g_assert_not_reached (); + } + gst_message_unref (msg); + g_object_unref (bus); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + static Suite * audiomixer_suite (void) { @@ -1859,6 +1934,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_sync_discont); tcase_add_test (tc_chain, test_sync_unaligned); tcase_add_test (tc_chain, test_segment_base_handling); + tcase_add_test (tc_chain, test_sinkpad_property_controller); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From ee1a50ef70bc887fdb4d9009004104670adef904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 22 Jul 2015 19:26:42 -0400 Subject: [PATCH 082/153] audioaggregator: Use 1.0 style buffer allocation --- gst/audiomixer/gstaudioaggregator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 38e8709678..cba57eed7b 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -943,8 +943,8 @@ static GstBuffer * gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, guint num_frames) { - GstBuffer *outbuf = gst_buffer_new_and_alloc (num_frames * - GST_AUDIO_INFO_BPF (&aagg->info)); + GstBuffer *outbuf = gst_buffer_new_allocate (NULL, num_frames * + GST_AUDIO_INFO_BPF (&aagg->info), NULL); GstMapInfo outmap; gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); From 9ab4b2e94ed22794b2c8211fd819002395e00061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 22 Jul 2015 19:27:19 -0400 Subject: [PATCH 083/153] audioaggregator: Register function name Otherwise, it sometimes segfaults with debugging enabled --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index cba57eed7b..b7e5cff674 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -1026,7 +1026,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) /* Sync pad properties to the stream time */ gst_aggregator_iterate_sinkpads (agg, - (GstAggregatorPadForeachFunc) sync_pad_values, NULL); + (GstAggregatorPadForeachFunc) GST_DEBUG_FUNCPTR (sync_pad_values), NULL); GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (agg); From 08df711c0cfbbf29dd4813ed94afde3fcbd1fb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Fri, 6 Mar 2015 19:50:08 -0500 Subject: [PATCH 084/153] aggregator: Queue "latency" buffers at each sink pad. In the case where you have a source giving the GstAggregator smaller buffers than it uses, when it reaches a timeout, it will consume the first buffer, then try to read another buffer for the pad. If the previous element is not fast enough, it may get the next buffer even though it may be queued just before. To prevent that race, the easiest solution is to move the queue inside the GstAggregatorPad itself. It also means that there is no need for strange code cause by increasing the min latency without increasing the max latency proportionally. This also means queuing the synchronized events and possibly acting on them on the src task. https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index b7e5cff674..351b1d70a4 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -723,7 +723,7 @@ gst_audio_aggregator_do_clip (GstAggregator * agg, bpf = GST_AUDIO_INFO_BPF (&pad->info); GST_OBJECT_LOCK (bpad); - *out = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); + *out = gst_audio_buffer_clip (buffer, &bpad->clip_segment, rate, bpf); GST_OBJECT_UNLOCK (bpad); return GST_FLOW_OK; From f6507af94616fd69ecb7ef2d4c3a60a9be76a499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 16 Mar 2015 17:06:46 -0400 Subject: [PATCH 085/153] audioaggregator: On timeout, resync pads with not enough data https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudioaggregator.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 351b1d70a4..ca06a66067 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -1271,6 +1271,20 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) aagg->priv->offset = next_offset; agg->segment.position = next_timestamp; + /* If there was a timeout and there was a gap in data in out of the streams, + * then it's a very good time to for a resync with the timestamps. + */ + if (timeout) { + for (iter = element->sinkpads; iter; iter = iter->next) { + GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (iter->data); + + GST_OBJECT_LOCK (pad); + if (pad->priv->output_offset < aagg->priv->offset) + pad->priv->output_offset = -1; + GST_OBJECT_UNLOCK (pad); + } + } + GST_OBJECT_UNLOCK (agg); /* send it out */ From c2794d1ad04dd5acf3beda79c995d21d851f0305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 2 Jul 2015 18:33:43 -0400 Subject: [PATCH 086/153] audiointerleave: Avoid caps processing if not yet negotiated https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- gst/audiomixer/gstaudiointerleave.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index 01ce19ccaf..39b86e87d6 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -514,6 +514,11 @@ gst_audio_interleave_aggregate (GstAggregator * aggregator, gboolean timeout) GstStructure *s; gboolean ret; + if (self->sinkcaps == NULL || self->channels == 0) { + /* In this case, let the base class handle it */ + goto not_negotiated; + } + srccaps = gst_caps_copy (self->sinkcaps); s = gst_caps_get_structure (srccaps, 0); @@ -535,6 +540,8 @@ gst_audio_interleave_aggregate (GstAggregator * aggregator, gboolean timeout) self->new_caps = FALSE; } + +not_negotiated: GST_OBJECT_UNLOCK (aggregator); return GST_AGGREGATOR_CLASS (parent_class)->aggregate (aggregator, timeout); From 47e374dbc88c9ba53898e0540783a0e6f3cb375c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 2 Jul 2015 18:37:28 -0400 Subject: [PATCH 087/153] tests: Add audiointerleave test to show that queuing works This tests fails without the queuing patch because incoming buffers are not delivered before they are needed. https://bugzilla.gnome.org/show_bug.cgi?id=745768 --- tests/check/elements/audiointerleave.c | 219 ++++++++++++++++++++++--- 1 file changed, 195 insertions(+), 24 deletions(-) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index 41788e1a6d..b4c504f292 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -34,6 +34,8 @@ #include #include +#include + static void gst_check_setup_events_audiointerleave (GstPad * srcpad, GstElement * element, GstCaps * caps, GstFormat format, const gchar * stream_id) @@ -436,11 +438,11 @@ src_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, { gint n = GPOINTER_TO_INT (user_data); gfloat *data; - gint i; - gsize size; + gint i, num_samples; GstCaps *caps; guint64 mask; GstAudioChannelPosition pos; + GstMapInfo map; fail_unless (gst_buffer_is_writable (buffer)); @@ -469,18 +471,18 @@ src_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, gst_pad_set_caps (pad, caps); gst_caps_unref (caps); - size = 48000 * sizeof (gfloat); - data = g_malloc (size); - for (i = 0; i < 48000; i++) + fail_unless (gst_buffer_map (buffer, &map, GST_MAP_WRITE)); + fail_unless (map.size % sizeof (gfloat) == 0); + + fail_unless (map.size > 480); + + num_samples = map.size / sizeof (gfloat); + data = (gfloat *) map.data; + + for (i = 0; i < num_samples; i++) data[i] = (n % 2 == 0) ? -1.0 : 1.0; - gst_buffer_append_memory (buffer, gst_memory_new_wrapped (0, data, - size, 0, size, data, g_free)); - - GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE; - GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE; - GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE; - GST_BUFFER_DURATION (buffer) = GST_SECOND; + gst_buffer_unmap (buffer, &map); } static void @@ -518,7 +520,7 @@ sink_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, gst_util_uint64_scale (map.size, GST_SECOND, 48000 * 2 * sizeof (gfloat)) >= GST_BUFFER_DURATION (buffer) - 1); - if (n == 0) { + if (n == 0 || n == 3) { GstAudioChannelPosition pos[2] = { GST_AUDIO_CHANNEL_POSITION_NONE, GST_AUDIO_CHANNEL_POSITION_NONE }; gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask); @@ -536,23 +538,25 @@ sink_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad, g_assert_not_reached (); } - caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, GST_AUDIO_NE (F32), - "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000, - "layout", G_TYPE_STRING, "interleaved", - "channel-mask", GST_TYPE_BITMASK, mask, NULL); - - ccaps = gst_pad_get_current_caps (pad); - fail_unless (gst_caps_is_equal (caps, ccaps)); - gst_caps_unref (ccaps); - gst_caps_unref (caps); + if (pad) { + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), + "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000, + "layout", G_TYPE_STRING, "interleaved", + "channel-mask", GST_TYPE_BITMASK, mask, NULL); + ccaps = gst_pad_get_current_caps (pad); + fail_unless (gst_caps_is_equal (caps, ccaps)); + gst_caps_unref (ccaps); + gst_caps_unref (caps); + } #ifdef HAVE_VALGRIND if (!(RUNNING_ON_VALGRIND)) #endif for (i = 0; i < map.size / sizeof (float); i += 2) { fail_unless_equals_float (data[i], -1.0); - fail_unless_equals_float (data[i + 1], 1.0); + if (n != 3) + fail_unless_equals_float (data[i + 1], 1.0); } have_data += map.size; @@ -578,6 +582,9 @@ test_audiointerleave_2ch_pipeline (gboolean interleaved) src1 = gst_element_factory_make ("fakesrc", "src1"); fail_unless (src1 != NULL); g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src1, "signal-handoffs", TRUE, NULL); g_object_set (src1, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src1, "handoff", G_CALLBACK (src_handoff_float32), @@ -587,6 +594,9 @@ test_audiointerleave_2ch_pipeline (gboolean interleaved) src2 = gst_element_factory_make ("fakesrc", "src2"); fail_unless (src2 != NULL); g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src2, "signal-handoffs", TRUE, NULL); g_object_set (src2, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src2, "handoff", G_CALLBACK (src_handoff_float32), @@ -675,6 +685,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_input_chanpos) src1 = gst_element_factory_make ("fakesrc", "src1"); fail_unless (src1 != NULL); g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src1, "signal-handoffs", TRUE, NULL); g_object_set (src1, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src1, "handoff", @@ -684,6 +697,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_input_chanpos) src2 = gst_element_factory_make ("fakesrc", "src2"); fail_unless (src2 != NULL); g_object_set (src2, "num-buffers", 4, NULL); + g_object_set (src2, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src2, "signal-handoffs", TRUE, NULL); g_object_set (src2, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src2, "handoff", @@ -764,6 +780,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_custom_chanpos) fail_unless (src1 != NULL); g_object_set (src1, "num-buffers", 4, NULL); g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src1, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src1, "handoff", G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (0)); @@ -773,6 +792,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_custom_chanpos) fail_unless (src2 != NULL); g_object_set (src2, "num-buffers", 4, NULL); g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src2, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src2, "handoff", G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (1)); @@ -862,6 +884,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_no_chanpos) fail_unless (src1 != NULL); g_object_set (src1, "num-buffers", 4, NULL); g_object_set (src1, "signal-handoffs", TRUE, NULL); + g_object_set (src1, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src1, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src1, "handoff", G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (0)); @@ -871,6 +896,9 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_no_chanpos) fail_unless (src2 != NULL); g_object_set (src2, "num-buffers", 4, NULL); g_object_set (src2, "signal-handoffs", TRUE, NULL); + g_object_set (src2, "sizetype", 2, + "sizemax", (int) 48000 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); g_object_set (src2, "format", GST_FORMAT_TIME, NULL); g_signal_connect (src2, "handoff", G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (1)); @@ -933,6 +961,148 @@ GST_START_TEST (test_audiointerleave_2ch_pipeline_no_chanpos) GST_END_TEST; +static void +forward_check_event (GstHarness * h, GstHarness * hsrc, GstEventType type) +{ + GstEvent *e; + + e = gst_harness_pull_event (hsrc); + fail_unless (GST_EVENT_TYPE (e) == type); + gst_harness_push_event (h, e); +} + +GST_START_TEST (test_audiointerleave_2ch_smallbuf) +{ + GstElement *audiointerleave; + GstHarness *hsrc; + GstHarness *h; + GstHarness *h2; + GstBuffer *buffer; + GstQuery *q; + gint i; + GstEvent *ev; + GstCaps *ecaps, *caps; + + audiointerleave = gst_element_factory_make ("audiointerleave", NULL); + + g_object_set (audiointerleave, "latency", GST_SECOND / 2, + "output-buffer-duration", GST_SECOND / 4, NULL); + + h = gst_harness_new_with_element (audiointerleave, "sink_0", "src"); + gst_harness_use_testclock (h); + + h2 = gst_harness_new_with_element (audiointerleave, "sink_1", NULL); + gst_harness_set_src_caps_str (h2, "audio/x-raw, " + "format=" GST_AUDIO_NE (F32) ", channels=(int)1," + " layout=interleaved, rate=48000, channel-mask=(bitmask)8"); + + hsrc = gst_harness_new ("fakesrc"); + gst_harness_use_testclock (hsrc); + g_object_set (hsrc->element, + "is-live", TRUE, + "sync", TRUE, + "signal-handoffs", TRUE, + "format", GST_FORMAT_TIME, + "sizetype", 2, + "sizemax", (int) 480 * sizeof (gfloat), + "datarate", (int) 48000 * sizeof (gfloat), NULL); + g_signal_connect (hsrc->element, "handoff", + G_CALLBACK (src_handoff_float32_audiointerleaved), GINT_TO_POINTER (2)); + gst_harness_play (hsrc); + + gst_harness_crank_single_clock_wait (hsrc); + forward_check_event (h, hsrc, GST_EVENT_STREAM_START); + forward_check_event (h, hsrc, GST_EVENT_CAPS); + forward_check_event (h, hsrc, GST_EVENT_SEGMENT); + gst_harness_push (h, gst_harness_pull (hsrc)); /* buffer */ + + for (i = 0; i < 24; i++) { + gst_harness_crank_single_clock_wait (hsrc); + forward_check_event (h, hsrc, GST_EVENT_CAPS); + gst_harness_push (h, gst_harness_pull (hsrc)); /* buffer */ + } + + gst_harness_crank_single_clock_wait (h); + + + gst_event_unref (gst_harness_pull_event (h)); /* stream-start */ + ev = gst_harness_pull_event (h); /* caps */ + fail_unless_equals_int (GST_EVENT_CAPS, GST_EVENT_TYPE (ev)); + + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), + "channels", G_TYPE_INT, 2, + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 48000, "channel-mask", GST_TYPE_BITMASK, 0x9, NULL); + + gst_event_parse_caps (ev, &ecaps); + gst_check_caps_equal (ecaps, caps); + gst_caps_unref (caps); + gst_event_unref (ev); + + for (i = 0; i < 24; i++) + gst_harness_crank_single_clock_wait (h); + fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK + (h->element)), 750 * GST_MSECOND); + + /* Check that the queue is really empty */ + q = gst_query_new_drain (); + gst_pad_peer_query (h->srcpad, q); + gst_query_unref (q); + + buffer = gst_harness_pull (h); + sink_handoff_float32 (NULL, buffer, NULL, GUINT_TO_POINTER (3)); + gst_buffer_unref (buffer); + fail_unless_equals_int (gst_harness_buffers_received (h), 1); + + for (i = 0; i < 50; i++) { + gst_harness_crank_single_clock_wait (hsrc); + forward_check_event (h, hsrc, GST_EVENT_CAPS); + gst_harness_push (h, gst_harness_pull (hsrc)); /* buffer */ + } + for (i = 0; i < 25; i++) + gst_harness_crank_single_clock_wait (h); + fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK + (h->element)), 1000 * GST_MSECOND); + buffer = gst_harness_pull (h); + sink_handoff_float32 (NULL, buffer, NULL, GUINT_TO_POINTER (3)); + gst_buffer_unref (buffer); + fail_unless_equals_int (gst_harness_buffers_received (h), 2); + + for (i = 0; i < 25; i++) { + gst_harness_crank_single_clock_wait (hsrc); + forward_check_event (h, hsrc, GST_EVENT_CAPS); + gst_harness_push (h, gst_harness_pull (hsrc)); /* buffer */ + } + for (i = 0; i < 25; i++) + gst_harness_crank_single_clock_wait (h); + fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK + (h->element)), 1250 * GST_MSECOND); + buffer = gst_harness_pull (h); + sink_handoff_float32 (NULL, buffer, NULL, GUINT_TO_POINTER (3)); + gst_buffer_unref (buffer); + fail_unless_equals_int (gst_harness_buffers_received (h), 3); + + gst_harness_push_event (h, gst_event_new_eos ()); + + for (i = 0; i < 25; i++) + gst_harness_crank_single_clock_wait (h); + fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK + (h->element)), 1500 * GST_MSECOND); + buffer = gst_harness_pull (h); + sink_handoff_float32 (NULL, buffer, NULL, GUINT_TO_POINTER (3)); + gst_buffer_unref (buffer); + + fail_unless_equals_int (gst_harness_buffers_received (h), 4); + + gst_harness_teardown (h2); + gst_harness_teardown (h); + gst_harness_teardown (hsrc); + gst_object_unref (audiointerleave); +} + +GST_END_TEST; + static Suite * audiointerleave_suite (void) { @@ -951,6 +1121,7 @@ audiointerleave_suite (void) tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_input_chanpos); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_custom_chanpos); tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_no_chanpos); + tcase_add_test (tc_chain, test_audiointerleave_2ch_smallbuf); return s; } From 1a4f037bc102ee0c7778d1bd3626f55e2b196d03 Mon Sep 17 00:00:00 2001 From: "hoonhee.lee" Date: Fri, 21 Aug 2015 16:44:43 +0900 Subject: [PATCH 088/153] tests: audiomixer: remove duplicated word in comment https://bugzilla.gnome.org/show_bug.cgi?id=753915 --- tests/check/elements/audiomixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 8fe329d82f..55ea92ef16 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -981,7 +981,7 @@ GST_START_TEST (test_clip) ck_assert_int_eq (ret, GST_FLOW_OK); /* The aggregation is done in a dedicated thread, so we can't - * not know when it is actually going to happen, so we use a DRAIN query + * know when it is actually going to happen, so we use a DRAIN query * to wait for it to complete. */ gst_pad_query (sinkpad, drain); From c91e32bbf7b2717256eceda6103b4c48f1ba9223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 11 Sep 2015 16:56:40 +0200 Subject: [PATCH 089/153] audioaggregator: Use stream time in the position query instead of segment position https://bugzilla.gnome.org/show_bug.cgi?id=753196 --- gst/audiomixer/gstaudioaggregator.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index ca06a66067..3b59a4295c 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -578,8 +578,9 @@ gst_audio_aggregator_src_query (GstAggregator * agg, GstQuery * query) switch (format) { case GST_FORMAT_TIME: - /* FIXME, bring to stream time, might be tricky */ - gst_query_set_position (query, format, agg->segment.position); + gst_query_set_position (query, format, + gst_segment_to_stream_time (&agg->segment, GST_FORMAT_TIME, + agg->segment.position)); res = TRUE; break; case GST_FORMAT_BYTES: From 41fae5fa5d92dce341c60610068a74578ff94e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 11 Sep 2015 21:37:08 +0200 Subject: [PATCH 090/153] audioaggregator: Fix mixup of running times and segment positions We have to queue buffers based on their running time, not based on the segment position. Also return running time from GstAggregator::get_next_time() instead of a segment position, as required by the API. Also only update the segment position after we pushed a buffer, otherwise we're going to push down a segment event with the next position already. https://bugzilla.gnome.org/show_bug.cgi?id=753196 --- gst/audiomixer/gstaudioaggregator.c | 138 ++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 38 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 3b59a4295c..c30bc64867 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -53,11 +53,12 @@ struct _GstAudioAggregatorPadPrivate cached values. */ guint position, size; - guint64 output_offset; /* Offset in output segment that - collect.pos refers to in the + guint64 output_offset; /* Sample offset in output segment relative to + segment.start that collect.pos refers to in the current buffer. */ - guint64 next_offset; /* Next expected offset in the input segment */ + guint64 next_offset; /* Next expected sample offset in the input segment + relative to segment.start */ /* Last time we noticed a discont */ GstClockTime discont_time; @@ -145,7 +146,8 @@ struct _GstAudioAggregatorPrivate /* counters to keep track of timestamps */ /* Readable with object lock, writable with both aag lock and object lock */ - gint64 offset; + + gint64 offset; /* Sample offset starting from 0 at segment.start */ }; #define GST_AUDIO_AGGREGATOR_LOCK(self) g_mutex_lock (&(self)->priv->mutex); @@ -195,10 +197,16 @@ gst_audio_aggregator_get_next_time (GstAggregator * agg) GstClockTime next_time; GST_OBJECT_LOCK (agg); - if (agg->segment.position == -1) + if (agg->segment.position == -1 || agg->segment.position < agg->segment.start) next_time = agg->segment.start; else next_time = agg->segment.position; + + if (agg->segment.stop != -1 && next_time > agg->segment.stop) + next_time = agg->segment.stop; + + next_time = + gst_segment_to_running_time (&agg->segment, GST_FORMAT_TIME, next_time); GST_OBJECT_UNLOCK (agg); return next_time; @@ -742,6 +750,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, guint64 start_offset, end_offset; gint rate, bpf; + GstAggregator *agg = GST_AGGREGATOR (aagg); GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); g_assert (pad->priv->buffer == NULL); @@ -767,7 +776,12 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, start_time + gst_util_uint64_scale_ceil (pad->priv->size, GST_SECOND, rate); - start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); + /* Clipping should've ensured this */ + g_assert (start_time >= aggpad->segment.start); + + start_offset = + gst_util_uint64_scale (start_time - aggpad->segment.start, rate, + GST_SECOND); end_offset = start_offset + pad->priv->size; if (GST_BUFFER_IS_DISCONT (inbuf) @@ -822,8 +836,8 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, if (pad->priv->output_offset == -1) { GstClockTime start_running_time; GstClockTime end_running_time; - guint64 start_running_time_offset; - guint64 end_running_time_offset; + guint64 start_output_offset; + guint64 end_output_offset; start_running_time = gst_segment_to_running_time (&aggpad->segment, @@ -831,12 +845,40 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, end_running_time = gst_segment_to_running_time (&aggpad->segment, GST_FORMAT_TIME, end_time); - start_running_time_offset = - gst_util_uint64_scale (start_running_time, rate, GST_SECOND); - end_running_time_offset = - gst_util_uint64_scale (end_running_time, rate, GST_SECOND); - if (end_running_time_offset < aagg->priv->offset) { + /* Convert to position in the output segment */ + start_output_offset = + gst_segment_to_position (&agg->segment, GST_FORMAT_TIME, + start_running_time); + if (start_output_offset != -1) + start_output_offset = + gst_util_uint64_scale (start_output_offset - agg->segment.start, rate, + GST_SECOND); + + end_output_offset = + gst_segment_to_position (&agg->segment, GST_FORMAT_TIME, + end_running_time); + if (end_output_offset != -1) + end_output_offset = + gst_util_uint64_scale (end_output_offset - agg->segment.start, rate, + GST_SECOND); + + if (start_output_offset == -1 && end_output_offset == -1) { + /* Outside output segment, drop */ + gst_buffer_unref (inbuf); + pad->priv->buffer = NULL; + pad->priv->position = 0; + pad->priv->size = 0; + pad->priv->output_offset = -1; + GST_DEBUG_OBJECT (pad, "Buffer outside output segment"); + return FALSE; + } + + /* Calculate end_output_offset if it was outside the output segment */ + if (end_output_offset == -1) + end_output_offset = start_output_offset + pad->priv->size; + + if (end_output_offset < aagg->priv->offset) { /* Before output segment, drop */ gst_buffer_unref (inbuf); pad->priv->buffer = NULL; @@ -845,12 +887,25 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, pad->priv->output_offset = -1; GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" - G_GUINT64_FORMAT, end_running_time_offset, aagg->priv->offset); + G_GUINT64_FORMAT, end_output_offset, aagg->priv->offset); return FALSE; } - if (start_running_time_offset < aagg->priv->offset) { - guint diff = aagg->priv->offset - start_running_time_offset; + if (start_output_offset == -1 || start_output_offset < aagg->priv->offset) { + guint diff; + + if (start_output_offset == -1 && end_output_offset < pad->priv->size) { + diff = pad->priv->size - end_output_offset + aagg->priv->offset; + } else if (start_output_offset == -1) { + start_output_offset = end_output_offset - pad->priv->size; + + if (start_output_offset < aagg->priv->offset) + diff = aagg->priv->offset - start_output_offset; + else + diff = 0; + } else { + diff = aagg->priv->offset - start_output_offset; + } pad->priv->position += diff; if (pad->priv->position >= pad->priv->size) { @@ -862,14 +917,16 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, pad->priv->output_offset = -1; GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT - " < %" G_GUINT64_FORMAT, end_running_time_offset, - aagg->priv->offset); + " < %" G_GUINT64_FORMAT, end_output_offset, aagg->priv->offset); return FALSE; } } - pad->priv->output_offset = - MAX (start_running_time_offset, aagg->priv->offset); + if (start_output_offset == -1 || start_output_offset < aagg->priv->offset) + pad->priv->output_offset = aagg->priv->offset; + else + pad->priv->output_offset = start_output_offset; + GST_DEBUG_OBJECT (pad, "Buffer resynced: Pad offset %" G_GUINT64_FORMAT ", current audio aggregator offset %" G_GUINT64_FORMAT, @@ -1066,13 +1123,10 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_OBJECT_UNLOCK (agg); gst_aggregator_set_src_caps (agg, aagg->current_caps); GST_OBJECT_LOCK (agg); - aagg->priv->offset = gst_util_uint64_scale (agg->segment.position, - GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); aagg->priv->send_caps = FALSE; } - rate = GST_AUDIO_INFO_RATE (&aagg->info); bpf = GST_AUDIO_INFO_BPF (&aagg->info); @@ -1090,7 +1144,9 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) next_offset = aagg->priv->offset - blocksize; } - next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + next_timestamp = + agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND, + rate); if (aagg->priv->current_buffer == NULL) { GST_OBJECT_UNLOCK (agg); @@ -1248,7 +1304,9 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" G_GUINT64_FORMAT, max_offset, next_offset); next_offset = max_offset; - next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + next_timestamp = + agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND, + rate); if (next_offset > aagg->priv->offset) gst_buffer_resize (outbuf, 0, (next_offset - aagg->priv->offset) * bpf); @@ -1269,6 +1327,23 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_BUFFER_DURATION (outbuf) = agg->segment.position - next_timestamp; } + GST_OBJECT_UNLOCK (agg); + + /* send it out */ + GST_LOG_OBJECT (aagg, + "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)), + GST_BUFFER_OFFSET (outbuf)); + + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + ret = gst_aggregator_finish_buffer (agg, aagg->priv->current_buffer); + aagg->priv->current_buffer = NULL; + + GST_LOG_OBJECT (aagg, "pushed outbuf, result = %s", gst_flow_get_name (ret)); + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (agg); aagg->priv->offset = next_offset; agg->segment.position = next_timestamp; @@ -1285,22 +1360,9 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_OBJECT_UNLOCK (pad); } } - GST_OBJECT_UNLOCK (agg); - - /* send it out */ - GST_LOG_OBJECT (aagg, - "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" - G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)), - GST_BUFFER_OFFSET (outbuf)); - GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - ret = gst_aggregator_finish_buffer (agg, aagg->priv->current_buffer); - aagg->priv->current_buffer = NULL; - - GST_LOG_OBJECT (aagg, "pushed outbuf, result = %s", gst_flow_get_name (ret)); - return ret; /* ERRORS */ not_negotiated: From e88ecc367bc8cce7568bca8e1ebb4a38f78fbed7 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 18 Sep 2015 00:20:13 +1000 Subject: [PATCH 091/153] Don't throw compiler warnings with G_DISABLE_ASSERT Disable code that warns about unused variables when G_DISABLE_ASSERT is defined, as it is in tarballs and pre-releases. --- gst/audiomixer/gstaudioaggregator.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index c30bc64867..1d3dcb56b4 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -625,13 +625,18 @@ void gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps) { +#ifndef G_DISABLE_ASSERT gboolean valid; GST_OBJECT_LOCK (pad); valid = gst_audio_info_from_caps (&pad->info, caps); - GST_OBJECT_UNLOCK (pad); - g_assert (valid); + +#else + GST_OBJECT_LOCK (pad); + (void) gst_audio_info_from_caps (&pad->info, caps); + GST_OBJECT_UNLOCK (pad); +#endif } From 3eb52b293e8b99c2a70d33d47ed40d7ac84f4ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 18 Sep 2015 18:00:05 +0200 Subject: [PATCH 092/153] audioaggregator: Only skip the remaining part of a GAP buffer We might've queued up a GAP buffer that is only partially inside the current output buffer (i.e. we received it too late!). In that case we should only skip the part of the GAP buffer that is inside the current output buffer, not also the remaining part. Otherwise we forward this pad too far into the future and break synchronization. --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 1d3dcb56b4..156fa93626 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -976,7 +976,7 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { /* skip gap buffer */ GST_LOG_OBJECT (pad, "skipping GAP buffer"); - pad->priv->output_offset += pad->priv->size; + pad->priv->output_offset += pad->priv->size - pad->priv->position; pad->priv->position = pad->priv->size; gst_buffer_replace (&pad->priv->buffer, NULL); From 94342ca11a3367898dad7d4321d28a5b3126f659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 26 Sep 2015 00:17:55 +0200 Subject: [PATCH 093/153] audioaggregator: Stop using deprecated gst_segment_to_position() --- gst/audiomixer/gstaudioaggregator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 156fa93626..d774694e8a 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -853,7 +853,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, /* Convert to position in the output segment */ start_output_offset = - gst_segment_to_position (&agg->segment, GST_FORMAT_TIME, + gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, start_running_time); if (start_output_offset != -1) start_output_offset = @@ -861,7 +861,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, GST_SECOND); end_output_offset = - gst_segment_to_position (&agg->segment, GST_FORMAT_TIME, + gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, end_running_time); if (end_output_offset != -1) end_output_offset = From ddbae168b8031cbda8d55fcc518ca5008db4431b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 26 Sep 2015 10:21:41 +0100 Subject: [PATCH 094/153] audiomixer: fix deadlock when G_DISABLE_ASSERT is not defined This makes the audiomixer unit test time out in master. Broke with 587e7c4 --- gst/audiomixer/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index d774694e8a..767a48d7c3 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -631,7 +631,7 @@ gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GST_OBJECT_LOCK (pad); valid = gst_audio_info_from_caps (&pad->info, caps); g_assert (valid); - + GST_OBJECT_UNLOCK (pad); #else GST_OBJECT_LOCK (pad); (void) gst_audio_info_from_caps (&pad->info, caps); From de9478bdc156b9f5efa5c65c881da36eb8da6cfb Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Wed, 30 Sep 2015 13:13:19 +0900 Subject: [PATCH 095/153] audiointerleave: typecast bit-mask to guint64 to fix segmentation fault While creating caps in audiointerleave tests, bitmask is being set as 0x9 This is resulting in segmentation fault. Fix the same by typecasting to guint64 https://bugzilla.gnome.org/show_bug.cgi?id=755840 --- tests/check/elements/audiointerleave.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index b4c504f292..222cd4df75 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -1033,7 +1033,8 @@ GST_START_TEST (test_audiointerleave_2ch_smallbuf) "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "channels", G_TYPE_INT, 2, "layout", G_TYPE_STRING, "interleaved", - "rate", G_TYPE_INT, 48000, "channel-mask", GST_TYPE_BITMASK, 0x9, NULL); + "rate", G_TYPE_INT, 48000, "channel-mask", GST_TYPE_BITMASK, + (guint64) 0x9, NULL); gst_event_parse_caps (ev, &ecaps); gst_check_caps_equal (ecaps, caps); From 52ea2667c610c373c0473d4cc8143cbaedc11f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 30 Sep 2015 19:01:21 +0200 Subject: [PATCH 096/153] audioaggregator: Select the initial offset based on the start segment position instead of always using 0. Otherwise we might output a lot of silence in the beginning instead of outputting from the relevant position. https://bugzilla.gnome.org/show_bug.cgi?id=755623 --- gst/audiomixer/gstaudioaggregator.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 767a48d7c3..e609dda47e 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -681,7 +681,7 @@ gst_audio_aggregator_reset (GstAudioAggregator * aagg) GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (aagg); agg->segment.position = -1; - aagg->priv->offset = 0; + aagg->priv->offset = -1; gst_audio_info_init (&aagg->info); gst_caps_replace (&aagg->current_caps, NULL); gst_buffer_replace (&aagg->priv->current_buffer, NULL); @@ -717,7 +717,7 @@ gst_audio_aggregator_flush (GstAggregator * agg) GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (aagg); agg->segment.position = -1; - aagg->priv->offset = 0; + aagg->priv->offset = -1; gst_buffer_replace (&aagg->priv->current_buffer, NULL); GST_OBJECT_UNLOCK (aagg); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); @@ -1135,8 +1135,15 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) rate = GST_AUDIO_INFO_RATE (&aagg->info); bpf = GST_AUDIO_INFO_BPF (&aagg->info); + if (aagg->priv->offset == -1) { + aagg->priv->offset = + gst_util_uint64_scale (agg->segment.position - agg->segment.start, rate, + GST_SECOND); + GST_DEBUG_OBJECT (aagg, "Starting at offset %lu", aagg->priv->offset); + } + blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, - GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); + rate, GST_SECOND); blocksize = MAX (1, blocksize); /* for the next timestamp, use the sample counter, which will From 11ab8b7965b92680f48cf4fc6fb986439e50692b Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Wed, 7 Oct 2015 08:48:15 +0900 Subject: [PATCH 097/153] audioaggregator: Fix build error Build error due to wrong argument type in debug message aagg->priv->offset and next_offset are of type int64, but uint64 formatter is being used in logs. Changing all those to int64 https://bugzilla.gnome.org/show_bug.cgi?id=756065 --- gst/audiomixer/gstaudioaggregator.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index e609dda47e..9155b1baa7 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -892,7 +892,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, pad->priv->output_offset = -1; GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" - G_GUINT64_FORMAT, end_output_offset, aagg->priv->offset); + G_GINT64_FORMAT, end_output_offset, aagg->priv->offset); return FALSE; } @@ -922,7 +922,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, pad->priv->output_offset = -1; GST_DEBUG_OBJECT (pad, "Buffer before segment or current position: %" G_GUINT64_FORMAT - " < %" G_GUINT64_FORMAT, end_output_offset, aagg->priv->offset); + " < %" G_GINT64_FORMAT, end_output_offset, aagg->priv->offset); return FALSE; } } @@ -934,7 +934,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, GST_DEBUG_OBJECT (pad, "Buffer resynced: Pad offset %" G_GUINT64_FORMAT - ", current audio aggregator offset %" G_GUINT64_FORMAT, + ", current audio aggregator offset %" G_GINT64_FORMAT, pad->priv->output_offset, aagg->priv->offset); } @@ -1139,7 +1139,8 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) aagg->priv->offset = gst_util_uint64_scale (agg->segment.position - agg->segment.start, rate, GST_SECOND); - GST_DEBUG_OBJECT (aagg, "Starting at offset %lu", aagg->priv->offset); + GST_DEBUG_OBJECT (aagg, "Starting at offset %" G_GINT64_FORMAT, + aagg->priv->offset); } blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, @@ -1172,7 +1173,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) outbuf = aagg->priv->current_buffer; GST_LOG_OBJECT (agg, - "Starting to mix %u samples for offset %" G_GUINT64_FORMAT + "Starting to mix %u samples for offset %" G_GINT64_FORMAT " with timestamp %" GST_TIME_FORMAT, blocksize, aagg->priv->offset, GST_TIME_ARGS (agg->segment.position)); @@ -1263,7 +1264,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (pad->priv->output_offset >= next_offset) { GST_DEBUG_OBJECT (pad, "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" - G_GUINT64_FORMAT, pad->priv->output_offset, next_offset); + G_GINT64_FORMAT, pad->priv->output_offset, next_offset); } else { is_done = FALSE; } @@ -1314,7 +1315,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (max_offset <= next_offset) { GST_DEBUG_OBJECT (aagg, "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" - G_GUINT64_FORMAT, max_offset, next_offset); + G_GINT64_FORMAT, max_offset, next_offset); next_offset = max_offset; next_timestamp = agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND, From 8cce2ccbf2865e910a40522550735e23fa5b6fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 5 Oct 2015 00:55:36 +0100 Subject: [PATCH 098/153] liveadder: Remove plugin, replace by compat subclass of audiomixer New subclass with a similar behaviour as the old liveadder, but a slightly different API as the latency is in nanoseconds, not milliseconds. Also, the new liveadder has a effective latency that is latency + output-buffer-duration. In practice, just setting a non-zero latency with the new audiomixer gives you the right behavior in 99% of the cases. --- gst/audiomixer/gstaudiomixer.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 3966a8fc27..d3ddcdf4f9 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -781,6 +781,28 @@ gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data) iface->get_children_count = gst_audiomixer_child_proxy_get_children_count; } +/* Empty liveadder alias with non-zero latency */ + +typedef GstAudioMixer GstLiveAdder; +typedef GstAudioMixerClass GstLiveAdderClass; + +static GType gst_live_adder_get_type (void); +#define GST_TYPE_LIVE_ADDER gst_live_adder_get_type () + +G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_AUDIO_MIXER); + +static void +gst_live_adder_init (GstLiveAdder * self) +{ + g_object_set (self, "latency", 30 * GST_MSECOND, NULL); +} + +static void +gst_live_adder_class_init (GstLiveAdderClass * klass) +{ +} + + static gboolean plugin_init (GstPlugin * plugin) { @@ -791,6 +813,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_AUDIO_MIXER)) return FALSE; + if (!gst_element_register (plugin, "liveadder", GST_RANK_NONE, + GST_TYPE_LIVE_ADDER)) + return FALSE; + if (!gst_element_register (plugin, "audiointerleave", GST_RANK_NONE, GST_TYPE_AUDIO_INTERLEAVE)) return FALSE; From 45081ef6f1124276155ba40b0d22a18605253c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 12 Oct 2015 09:36:29 +0100 Subject: [PATCH 099/153] liveadder: latency property is an uint64 in audiomixer --- gst/audiomixer/gstaudiomixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index d3ddcdf4f9..2ae06335aa 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -794,7 +794,8 @@ G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_AUDIO_MIXER); static void gst_live_adder_init (GstLiveAdder * self) { - g_object_set (self, "latency", 30 * GST_MSECOND, NULL); + /* FIXME: old live adder had latency as uint property */ + g_object_set (self, "latency", (guint64) 30 * GST_MSECOND, NULL); } static void From 7161795a4455806364788e615c35c4dde2ed25b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 26 Oct 2015 19:58:04 -0400 Subject: [PATCH 100/153] liveadder: Make latency property be a uint in millisecs This restores roughly the same behaviour as the old liveadder element. Except that the latency now also includes the output-buffer-duration. https://bugzilla.gnome.org/show_bug.cgi?id=757050 --- gst/audiomixer/gstaudiomixer.c | 74 ++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 2ae06335aa..dcf4c92ebc 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -791,18 +791,86 @@ static GType gst_live_adder_get_type (void); G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_AUDIO_MIXER); +enum +{ + LIVEADDER_PROP_LATENCY = 1 +}; + static void gst_live_adder_init (GstLiveAdder * self) { - /* FIXME: old live adder had latency as uint property */ - g_object_set (self, "latency", (guint64) 30 * GST_MSECOND, NULL); } +static void +gst_live_adder_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + case LIVEADDER_PROP_LATENCY: + { + GParamSpec *parent_spec = + g_object_class_find_property (G_OBJECT_CLASS + (gst_live_adder_parent_class), "latency"); + GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type); + GValue v = { 0 }; + + g_value_init (&v, G_TYPE_INT64); + + g_value_set_int64 (&v, g_value_get_uint (value) * GST_MSECOND); + + G_OBJECT_CLASS (pspec_class)->set_property (object, + parent_spec->param_id, &v, parent_spec); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_live_adder_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + switch (prop_id) { + case LIVEADDER_PROP_LATENCY: + { + GParamSpec *parent_spec = + g_object_class_find_property (G_OBJECT_CLASS + (gst_live_adder_parent_class), "latency"); + GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type); + GValue v = { 0 }; + + g_value_init (&v, G_TYPE_INT64); + + G_OBJECT_CLASS (pspec_class)->get_property (object, + parent_spec->param_id, &v, parent_spec); + + g_value_set_uint (value, g_value_get_int64 (&v) / GST_MSECOND); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + static void gst_live_adder_class_init (GstLiveAdderClass * klass) { -} + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->set_property = gst_live_adder_set_property; + gobject_class->get_property = gst_live_adder_get_property; + + g_object_class_install_property (gobject_class, LIVEADDER_PROP_LATENCY, + g_param_spec_uint ("latency", "Buffer latency", + "Additional latency in live mode to allow upstream " + "to take longer to produce buffers for the current " + "position (in milliseconds)", 0, G_MAXUINT, + 30, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); +} static gboolean plugin_init (GstPlugin * plugin) From 1369924fa0e7f5e1648939325253774bcc260efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 2 Nov 2015 19:40:28 -0500 Subject: [PATCH 101/153] audioaggregator: Improve log messages Make the level of log messages saner and improve some. --- gst/audiomixer/gstaudioaggregator.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 9155b1baa7..45353d9fe3 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -829,7 +829,7 @@ gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, if (discont) { /* Have discont, need resync */ if (pad->priv->next_offset != -1) - GST_INFO_OBJECT (pad, "Have discont. Expected %" + GST_DEBUG_OBJECT (pad, "Have discont. Expected %" G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, pad->priv->next_offset, start_offset); pad->priv->output_offset = -1; @@ -995,7 +995,7 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, if (pad->priv->position == pad->priv->size) { /* Buffer done, drop it */ gst_buffer_replace (&pad->priv->buffer, NULL); - GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); + GST_LOG_OBJECT (pad, "Finished mixing buffer, waiting for next"); return FALSE; } @@ -1194,8 +1194,8 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (timeout) { if (pad->priv->output_offset < next_offset) { gint64 diff = next_offset - pad->priv->output_offset; - GST_LOG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT " frames (%" - GST_TIME_FORMAT ")", diff, + GST_DEBUG_OBJECT (pad, "Timeout, missing %" G_GINT64_FORMAT + " frames (%" GST_TIME_FORMAT ")", diff, GST_TIME_ARGS (gst_util_uint64_scale (diff, GST_SECOND, GST_AUDIO_INFO_RATE (&aagg->info)))); } @@ -1241,7 +1241,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) pad->priv->output_offset += diff; if (pad->priv->position == pad->priv->size) { - GST_LOG_OBJECT (pad, "Buffer was late by %" GST_TIME_FORMAT + GST_DEBUG_OBJECT (pad, "Buffer was late by %" GST_TIME_FORMAT ", dropping %" GST_PTR_FORMAT, GST_TIME_ARGS (gst_util_uint64_scale (odiff, GST_SECOND, GST_AUDIO_INFO_RATE (&aagg->info))), pad->priv->buffer); @@ -1262,8 +1262,8 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer, outbuf); if (pad->priv->output_offset >= next_offset) { - GST_DEBUG_OBJECT (pad, - "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" + GST_LOG_OBJECT (pad, + "Pad is at or after current offset: %" G_GUINT64_FORMAT " >= %" G_GINT64_FORMAT, pad->priv->output_offset, next_offset); } else { is_done = FALSE; @@ -1279,15 +1279,15 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (dropped) { /* We dropped a buffer, retry */ - GST_INFO_OBJECT (aagg, "A pad dropped a buffer, wait for the next one"); + GST_LOG_OBJECT (aagg, "A pad dropped a buffer, wait for the next one"); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); return GST_FLOW_OK; } if (!is_done && !is_eos) { /* Get more buffers */ - GST_INFO_OBJECT (aagg, - "We're not done yet for the current offset," " waiting for more data"); + GST_LOG_OBJECT (aagg, + "We're not done yet for the current offset, waiting for more data"); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); return GST_FLOW_OK; } From 9f69e979357311df283a7436fe60a7342913ffed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 24 Nov 2015 00:20:36 +0000 Subject: [PATCH 102/153] audiomixer: register function name for debugging just once Not every time aggregate is called... --- gst/audiomixer/gstaudioaggregator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst/audiomixer/gstaudioaggregator.c index 45353d9fe3..5d7e8d634f 100644 --- a/gst/audiomixer/gstaudioaggregator.c +++ b/gst/audiomixer/gstaudioaggregator.c @@ -175,6 +175,8 @@ static GstFlowReturn gst_audio_aggregator_do_clip (GstAggregator * agg, GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout); +static gboolean sync_pad_values (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad); #define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) @@ -240,6 +242,8 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) klass->create_output_buffer = gst_audio_aggregator_create_output_buffer; + GST_DEBUG_REGISTER_FUNCPTR (sync_pad_values); + GST_DEBUG_CATEGORY_INIT (audio_aggregator_debug, "audioaggregator", GST_DEBUG_FG_MAGENTA, "GstAudioAggregator"); @@ -1089,7 +1093,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) /* Sync pad properties to the stream time */ gst_aggregator_iterate_sinkpads (agg, - (GstAggregatorPadForeachFunc) GST_DEBUG_FUNCPTR (sync_pad_values), NULL); + (GstAggregatorPadForeachFunc) sync_pad_values, NULL); GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (agg); From f6cd84c4e660e0c0d949495d9e73db15e17c9b58 Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Mon, 14 Dec 2015 11:09:46 +0900 Subject: [PATCH 103/153] plugins-bad: Fix example pipelines rename gst-launch --> gst-launch-1.0 replace old elements with new elements(ffmpegcolorspace -> videoconvert, ffenc_** -> avenc_**) fix caps in examples https://bugzilla.gnome.org/show_bug.cgi?id=759432 --- gst/audiomixer/gstaudiomixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index dcf4c92ebc..b211f7df56 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -44,7 +44,7 @@ * * Example launch line * |[ - * gst-launch audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. + * gst-launch-1.0 audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. * ]| This pipeline produces two sine waves mixed together. * * From a7b86878fb3f34a3d2e0b028535f019493b7087e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 16 Jan 2016 18:56:26 +0200 Subject: [PATCH 104/153] audio: Move audioaggregator base class to a library It's useful enough already to be used in other elements for audio aggregation, let's give people the opportunity to use it and give it some API testing. https://bugzilla.gnome.org/show_bug.cgi?id=760733 --- {gst/audiomixer => gst-libs/gst/audio}/gstaudioaggregator.c | 0 {gst/audiomixer => gst-libs/gst/audio}/gstaudioaggregator.h | 0 gst/audiomixer/Makefile.am | 5 +++-- gst/audiomixer/gstaudiointerleave.h | 2 +- gst/audiomixer/gstaudiomixer.h | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) rename {gst/audiomixer => gst-libs/gst/audio}/gstaudioaggregator.c (100%) rename {gst/audiomixer => gst-libs/gst/audio}/gstaudioaggregator.h (100%) diff --git a/gst/audiomixer/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c similarity index 100% rename from gst/audiomixer/gstaudioaggregator.c rename to gst-libs/gst/audio/gstaudioaggregator.c diff --git a/gst/audiomixer/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h similarity index 100% rename from gst/audiomixer/gstaudioaggregator.h rename to gst-libs/gst/audio/gstaudioaggregator.h diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index a126f90ef7..9104c1b806 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 gstaudiointerleave.c +libgstaudiomixer_la_SOURCES = gstaudiomixer.c gstaudiointerleave.c nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) libgstaudiomixer_la_CFLAGS = \ -I$(top_srcdir)/gst-libs \ @@ -14,9 +14,10 @@ libgstaudiomixer_la_CFLAGS = \ libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstaudiomixer_la_LIBADD = \ $(top_builddir)/gst-libs/gst/base/libgstbadbase-$(GST_API_VERSION).la \ + $(top_builddir)/gst-libs/gst/audio/libgstbadaudio-$(GST_API_VERSION).la \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) -noinst_HEADERS = gstaudiomixer.h gstaudioaggregator.h gstaudiointerleave.h +noinst_HEADERS = gstaudiomixer.h gstaudiointerleave.h diff --git a/gst/audiomixer/gstaudiointerleave.h b/gst/audiomixer/gstaudiointerleave.h index 0473b45e03..6dd82d383c 100644 --- a/gst/audiomixer/gstaudiointerleave.h +++ b/gst/audiomixer/gstaudiointerleave.h @@ -27,7 +27,7 @@ #include #include -#include "gstaudioaggregator.h" +#include G_BEGIN_DECLS diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index add6e32f47..0e4098debf 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -26,7 +26,7 @@ #include #include -#include "gstaudioaggregator.h" +#include G_BEGIN_DECLS From fe159a684732b83561e07d5e8d3c5609b7348405 Mon Sep 17 00:00:00 2001 From: Vineeth TM Date: Fri, 4 Mar 2016 15:50:26 +0900 Subject: [PATCH 105/153] bad: use new gst_element_class_add_static_pad_template() https://bugzilla.gnome.org/show_bug.cgi?id=763081 --- gst/audiomixer/gstaudiointerleave.c | 11 +++++------ gst/audiomixer/gstaudiomixer.c | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index 39b86e87d6..e91b6454b4 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -566,13 +566,12 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) 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_add_static_pad_template (gstelement_class, + &gst_audio_interleave_src_template); + gst_element_class_add_static_pad_template (gstelement_class, + &gst_audio_interleave_sink_template); gst_element_class_set_static_metadata (gstelement_class, "AudioInterleave", - "Generic/Audio", - "Mixes multiple audio streams", + "Generic/Audio", "Mixes multiple audio streams", "Olivier Crete "); gstelement_class->request_new_pad = diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index b211f7df56..de539db828 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -485,13 +485,12 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) "Setting this property takes a reference to the supplied GstCaps " "object", GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_audiomixer_src_template)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&gst_audiomixer_sink_template)); + gst_element_class_add_static_pad_template (gstelement_class, + &gst_audiomixer_src_template); + gst_element_class_add_static_pad_template (gstelement_class, + &gst_audiomixer_sink_template); gst_element_class_set_static_metadata (gstelement_class, "AudioMixer", - "Generic/Audio", - "Mixes multiple audio streams", + "Generic/Audio", "Mixes multiple audio streams", "Sebastian Dröge "); gstelement_class->request_new_pad = From 937a42247a22ff50ea1057c09273db93a8497109 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 19 May 2016 11:16:37 +0300 Subject: [PATCH 106/153] audioaggregator: fix buffer leak If the pad was still owning a buffer when being destroyed it was leaked. Fix a leak with the test_flush_start_flush_stop test. https://bugzilla.gnome.org/show_bug.cgi?id=766663 --- gst-libs/gst/audio/gstaudioaggregator.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 5d7e8d634f..a09b8a4446 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -78,13 +78,25 @@ static gboolean gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator); +static void +gst_audio_aggregator_pad_finalize (GObject * object) +{ + GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) object; + + gst_buffer_replace (&pad->priv->buffer, NULL); + + G_OBJECT_CLASS (gst_audio_aggregator_pad_parent_class)->finalize (object); +} + static void gst_audio_aggregator_pad_class_init (GstAudioAggregatorPadClass * klass) { + GObjectClass *gobject_class = (GObjectClass *) klass; GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass; g_type_class_add_private (klass, sizeof (GstAudioAggregatorPadPrivate)); + gobject_class->finalize = gst_audio_aggregator_pad_finalize; aggpadclass->flush = GST_DEBUG_FUNCPTR (gst_audio_aggregator_pad_flush_pad); } From ce53b648001700d7d036fff52950feac6ecdf747 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 19 May 2016 11:19:01 +0300 Subject: [PATCH 107/153] audiointerleave: fix message leaks by flushing the bus https://bugzilla.gnome.org/show_bug.cgi?id=766663 --- tests/check/elements/audiointerleave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index 222cd4df75..cb992f393a 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -269,6 +269,7 @@ GST_START_TEST (test_audiointerleave_2ch) g_cond_wait (&data_cond, &data_mutex); g_mutex_unlock (&data_mutex); + gst_bus_set_flushing (bus, TRUE); gst_element_set_state (interleave, GST_STATE_NULL); gst_element_set_state (queue, GST_STATE_NULL); @@ -410,6 +411,7 @@ GST_START_TEST (test_audiointerleave_2ch_1eos) g_cond_wait (&data_cond, &data_mutex); g_mutex_unlock (&data_mutex); + gst_bus_set_flushing (bus, TRUE); gst_element_set_state (interleave, GST_STATE_NULL); gst_element_set_state (queue, GST_STATE_NULL); From f1f13de8d7efaf8b9f372312342ff82f2bc35678 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 19 May 2016 11:19:20 +0300 Subject: [PATCH 108/153] fix event leaks in tests The events are supposed to be unreffed when finishing the test, not reffed. https://bugzilla.gnome.org/show_bug.cgi?id=766663 --- tests/check/elements/audiomixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 55ea92ef16..00be1c6eeb 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -431,7 +431,7 @@ GST_START_TEST (test_play_twice) /* cleanup */ g_main_loop_unref (main_loop); gst_consistency_checker_free (consist); - gst_event_ref (play_seek_event); + gst_event_unref (play_seek_event); gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); @@ -536,7 +536,7 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) /* cleanup */ g_main_loop_unref (main_loop); - gst_event_ref (play_seek_event); + gst_event_unref (play_seek_event); gst_consistency_checker_free (consist); gst_bus_remove_signal_watch (bus); gst_object_unref (bus); From 8d6d6bd2aa210517c866c9aa0b3bc0b4613859a4 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 15 Jul 2016 10:31:35 +0200 Subject: [PATCH 109/153] tests: fix bus leak gst_bus_remove_signal_watch() has to be called to release the ref taken by gst_bus_add_signal_watch(). https://bugzilla.gnome.org/show_bug.cgi?id=768843 --- tests/check/elements/audiomixer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 00be1c6eeb..9ef5c3c8ff 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -723,6 +723,7 @@ GST_START_TEST (test_live_seeking) g_main_loop_unref (main_loop); if (play_seek_event) gst_event_unref (play_seek_event); + gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); } From 64c5b610d98be2172e7a2f8ea4acabc2f357adfb Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 12 Aug 2016 21:21:45 +0530 Subject: [PATCH 110/153] Add support for Meson as alternative/parallel build system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/mesonbuild/meson With contributions from: Tim-Philipp Müller Matej Knopp Jussi Pakkanen (original port) Highlights of the features provided are: * Faster builds on Linux (~40-50% faster) * The ability to build with MSVC on Windows * Generate Visual Studio project files * Generate XCode project files * Much faster builds on Windows (on-par with Linux) * Seriously fast configure and building on embedded ... and many more. For more details see: http://blog.nirbheek.in/2016/05/gstreamer-and-meson-new-hope.html http://blog.nirbheek.in/2016/07/building-and-developing-gstreamer-using.html Building with Meson should work on both Linux and Windows, but may need a few more tweaks on other operating systems. --- gst/audiomixer/meson.build | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 gst/audiomixer/meson.build diff --git a/gst/audiomixer/meson.build b/gst/audiomixer/meson.build new file mode 100644 index 0000000000..e635ee62d7 --- /dev/null +++ b/gst/audiomixer/meson.build @@ -0,0 +1,33 @@ +audiomixer_sources = [ + 'gstaudiomixer.c', + 'gstaudiointerleave.c', +] + +orcsrc = 'gstaudiomixerorc' +if have_orcc + orc_h = custom_target(orcsrc + '.h', + input : orcsrc + '.orc', + output : orcsrc + '.h', + command : orcc_args + ['--header', '-o', '@OUTPUT@', '@INPUT@']) + orc_c = custom_target(orcsrc + '.c', + input : orcsrc + '.orc', + output : orcsrc + '.c', + command : orcc_args + ['--implementation', '-o', '@OUTPUT@', '@INPUT@']) +else + orc_h = configure_file(input : orcsrc + '-dist.h', + output : orcsrc + '.h', + configuration : configuration_data()) + orc_c = configure_file(input : orcsrc + '-dist.c', + output : orcsrc + '.c', + configuration : configuration_data()) +endif + +gstaudiomixer = library('gstaudiomixer', + audiomixer_sources, orc_c, orc_h, + c_args : gst_plugins_bad_args + [ '-DGST_USE_UNSTABLE_API' ], + include_directories : [configinc], + dependencies : [gstbadaudio_dep, gstbadbase_dep, gstaudio_dep, gstbase_dep, + orc_dep], + install : true, + install_dir : plugins_install_dir, +) From 20ced51df28f43640704ce122b129b3c6bfd29ca Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 27 Oct 2016 09:08:41 +0530 Subject: [PATCH 111/153] Fix incorrect return type in several functions All these should return GstFlowReturn, not gboolean --- gst-libs/gst/audio/gstaudioaggregator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index a09b8a4446..8d66225930 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -74,7 +74,7 @@ struct _GstAudioAggregatorPadPrivate G_DEFINE_TYPE (GstAudioAggregatorPad, gst_audio_aggregator_pad, GST_TYPE_AGGREGATOR_PAD); -static gboolean +static GstFlowReturn gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator); @@ -118,7 +118,7 @@ gst_audio_aggregator_pad_init (GstAudioAggregatorPad * pad) } -static gboolean +static GstFlowReturn gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator) { @@ -131,7 +131,7 @@ gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, gst_buffer_replace (&pad->priv->buffer, NULL); GST_OBJECT_UNLOCK (aggpad); - return TRUE; + return GST_FLOW_OK; } From 974cbfb3c49961eb3812fbfde663c7b5427f1ba5 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 31 Mar 2017 23:40:05 +1300 Subject: [PATCH 112/153] audiointerleave: don't overflow channel map with >64 channels When there are more than 64 channels, we don't want to exceed the bounds of the ordering_map buffer, and in these cases we don't want to remap at all. Here we avoid doing that. Based on a patch originally for plugins-good/interleave in https://bugzilla.gnome.org/show_bug.cgi?id=780331 --- gst/audiomixer/gstaudiointerleave.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index e91b6454b4..f4e9fa1073 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -330,7 +330,8 @@ gst_audio_interleave_get_channel_mask (GstAudioInterleave * self) { guint64 channel_mask = 0; - if (self->channel_positions != NULL && + if (self->channels <= 64 && + 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, @@ -338,7 +339,7 @@ gst_audio_interleave_get_channel_mask (GstAudioInterleave * self) GST_WARNING_OBJECT (self, "Invalid channel positions, using NONE"); channel_mask = 0; } - } else { + } else if (self->channels <= 64) { GST_WARNING_OBJECT (self, "Using NONE channel positions"); } @@ -826,7 +827,7 @@ gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (aaggpad); GstMapInfo inmap; GstMapInfo outmap; - gint out_width, in_bpf, out_bpf, out_channels; + gint out_width, in_bpf, out_bpf, out_channels, channel; guint8 *outdata; out_width = GST_AUDIO_INFO_WIDTH (&aagg->info) / 8; @@ -840,8 +841,13 @@ gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, " from offset %u", num_frames, pad->channel, out_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]); + if (self->channels > 64) { + channel = pad->channel; + } else { + channel = self->default_channels_ordering_map[pad->channel]; + } + + outdata = outmap.data + (out_offset * out_bpf) + (out_width * channel); self->func (outdata, inmap.data + (in_offset * in_bpf), out_channels, From 3a9a60327653808e9754419ee29bf429891123ac Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 8 Mar 2017 15:01:13 -0300 Subject: [PATCH 113/153] docs: Port all docstring to gtk-doc markdown --- gst/audiomixer/gstaudiointerleave.c | 6 +++--- gst/audiomixer/gstaudiomixer.c | 15 ++++----------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index f4e9fa1073..8d99b3ce13 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -27,7 +27,7 @@ */ /** * SECTION:element-audiointerleave - * + * @title: audiointerleave * */ @@ -593,7 +593,7 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) /** * 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 @@ -617,7 +617,7 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) /** * 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 diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index de539db828..ac2f49c04b 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -23,6 +23,7 @@ */ /** * SECTION:element-audiomixer + * @title: audiomixer * * The audiomixer allows to mix several streams into one by adding the data. * Mixed data is clamped to the min/max values of the data format. @@ -32,21 +33,13 @@ * The input pads are from a GstPad subclass and have additional * properties to mute each pad individually and set the volume: * - * - * - * "mute": Whether to mute the pad or not (#gboolean) - * - * - * "volume": The volume of the pad, between 0.0 and 10.0 (#gdouble) - * - * + * * "mute": Whether to mute the pad or not (#gboolean) + * * "volume": The volume of the pad, between 0.0 and 10.0 (#gdouble) * - * - * Example launch line + * ## Example launch line * |[ * gst-launch-1.0 audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. * ]| This pipeline produces two sine waves mixed together. - * * */ From 40f4fc3d848039ce63c5c1f304f2839b420c8ae4 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 16 May 2017 14:05:52 -0400 Subject: [PATCH 114/153] Remove plugin specific static build option Static and dynamic plugins now have the same interface. The standard --enable-static/--enable-shared toggle are sufficient. --- gst/audiomixer/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index 9104c1b806..72dc081cd0 100644 --- a/gst/audiomixer/Makefile.am +++ b/gst/audiomixer/Makefile.am @@ -17,7 +17,6 @@ libgstaudiomixer_la_LIBADD = \ $(top_builddir)/gst-libs/gst/audio/libgstbadaudio-$(GST_API_VERSION).la \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) -libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = gstaudiomixer.h gstaudiointerleave.h From 1e7aaf775f5b71c378d486113d02c16b3cf006c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 6 Jul 2016 16:41:44 -0400 Subject: [PATCH 115/153] aggregator: Simplify clip function The return value was ignored anyway https://bugzilla.gnome.org/show_bug.cgi?id=781673 --- gst-libs/gst/audio/gstaudioaggregator.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 8d66225930..5621fb7add 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -183,8 +183,8 @@ static GstFlowReturn gst_audio_aggregator_flush (GstAggregator * agg); static GstBuffer *gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, guint num_frames); -static GstFlowReturn gst_audio_aggregator_do_clip (GstAggregator * agg, - GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** outbuf); +static GstBuffer *gst_audio_aggregator_do_clip (GstAggregator * agg, + GstAggregatorPad * bpad, GstBuffer * buffer); static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout); static gboolean sync_pad_values (GstAudioAggregator * aagg, @@ -741,22 +741,21 @@ gst_audio_aggregator_flush (GstAggregator * agg) return GST_FLOW_OK; } -static GstFlowReturn +static GstBuffer * gst_audio_aggregator_do_clip (GstAggregator * agg, - GstAggregatorPad * bpad, GstBuffer * buffer, GstBuffer ** out) + GstAggregatorPad * bpad, GstBuffer * buffer) { GstAudioAggregatorPad *pad = GST_AUDIO_AGGREGATOR_PAD (bpad); gint rate, bpf; - rate = GST_AUDIO_INFO_RATE (&pad->info); bpf = GST_AUDIO_INFO_BPF (&pad->info); GST_OBJECT_LOCK (bpad); - *out = gst_audio_buffer_clip (buffer, &bpad->clip_segment, rate, bpf); + buffer = gst_audio_buffer_clip (buffer, &bpad->clip_segment, rate, bpf); GST_OBJECT_UNLOCK (bpad); - return GST_FLOW_OK; + return buffer; } /* Called with the object lock for both the element and pad held, From 26819ba296f59f0dac7e28da6d9296de250d1333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 6 Jul 2016 16:39:17 -0400 Subject: [PATCH 116/153] aggregator: Delay clipping to output thread This is required because the synchronized events like caps or segments may only be processed on the output thread. https://bugzilla.gnome.org/show_bug.cgi?id=781673 --- gst-libs/gst/audio/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 5621fb7add..2eb8a844ed 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -752,7 +752,7 @@ gst_audio_aggregator_do_clip (GstAggregator * agg, bpf = GST_AUDIO_INFO_BPF (&pad->info); GST_OBJECT_LOCK (bpad); - buffer = gst_audio_buffer_clip (buffer, &bpad->clip_segment, rate, bpf); + buffer = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); GST_OBJECT_UNLOCK (bpad); return buffer; From 57673e608e5b941be97a7ef88b7ce398b9622d40 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 20 May 2017 14:24:57 +0200 Subject: [PATCH 117/153] aggregator: add simple support for caps handling Modelled off the videoaggregator caps handling as that seems the most mature aggregtor-using implementation that has caps handling there is. https://bugzilla.gnome.org/show_bug.cgi?id=776931 --- gst-libs/gst/audio/gstaudioaggregator.c | 31 +++++------ gst-libs/gst/audio/gstaudioaggregator.h | 3 - gst/audiomixer/gstaudiointerleave.c | 74 ++++++++++--------------- gst/audiomixer/gstaudiointerleave.h | 1 - gst/audiomixer/gstaudiomixer.c | 30 +++++++--- tests/check/elements/audiointerleave.c | 4 +- 6 files changed, 69 insertions(+), 74 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 2eb8a844ed..f5c6c2be15 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -144,8 +144,6 @@ struct _GstAudioAggregatorPrivate { GMutex mutex; - gboolean send_caps; /* aagg lock */ - /* All three properties are unprotected, can't be modified while streaming */ /* Size in frames that is output per buffer */ GstClockTime output_buffer_duration; @@ -189,6 +187,8 @@ static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout); static gboolean sync_pad_values (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad); +static gboolean gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, + GstCaps * caps); #define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) @@ -251,6 +251,8 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) GST_DEBUG_FUNCPTR (gst_audio_aggregator_aggregate); gstaggregator_class->clip = GST_DEBUG_FUNCPTR (gst_audio_aggregator_do_clip); gstaggregator_class->get_next_time = gst_audio_aggregator_get_next_time; + gstaggregator_class->negotiated_src_caps = + gst_audio_aggregator_negotiated_src_caps; klass->create_output_buffer = gst_audio_aggregator_create_output_buffer; @@ -656,9 +658,10 @@ gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, } -gboolean -gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps) +static gboolean +gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps) { + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); GstAudioInfo info; if (!gst_audio_info_from_caps (&info, caps)) { @@ -674,8 +677,6 @@ gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps) gst_caps_replace (&aagg->current_caps, caps); memcpy (&aagg->info, &info, sizeof (info)); - aagg->priv->send_caps = TRUE; - } GST_OBJECT_UNLOCK (aagg); @@ -683,7 +684,9 @@ gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps) /* send caps event later, after stream-start event */ - return TRUE; + return + GST_AGGREGATOR_CLASS + (gst_audio_aggregator_parent_class)->negotiated_src_caps (agg, caps); } @@ -1132,21 +1135,13 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_OBJECT_UNLOCK (agg); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - return GST_FLOW_OK; + return GST_AGGREGATOR_FLOW_NEED_DATA; } else { GST_OBJECT_UNLOCK (agg); goto not_negotiated; } } - if (aagg->priv->send_caps) { - GST_OBJECT_UNLOCK (agg); - gst_aggregator_set_src_caps (agg, aagg->current_caps); - GST_OBJECT_LOCK (agg); - - aagg->priv->send_caps = FALSE; - } - rate = GST_AUDIO_INFO_RATE (&aagg->info); bpf = GST_AUDIO_INFO_BPF (&aagg->info); @@ -1296,7 +1291,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) /* We dropped a buffer, retry */ GST_LOG_OBJECT (aagg, "A pad dropped a buffer, wait for the next one"); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - return GST_FLOW_OK; + return GST_AGGREGATOR_FLOW_NEED_DATA; } if (!is_done && !is_eos) { @@ -1304,7 +1299,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_LOG_OBJECT (aagg, "We're not done yet for the current offset, waiting for more data"); GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - return GST_FLOW_OK; + return GST_AGGREGATOR_FLOW_NEED_DATA; } if (is_eos) { diff --git a/gst-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h index 304bad2871..be76349a43 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.h +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -162,9 +162,6 @@ void gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps); -gboolean -gst_audio_aggregator_set_src_caps (GstAudioAggregator * aagg, GstCaps * caps); - G_END_DECLS diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index 8d99b3ce13..dfae4a51ae 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -432,10 +432,10 @@ gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad, GST_DEBUG_OBJECT (self, "setting sinkcaps %" GST_PTR_FORMAT, sinkcaps); gst_caps_replace (&self->sinkcaps, sinkcaps); + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (aagg)); gst_caps_unref (sinkcaps); new = TRUE; - self->new_caps = TRUE; } if (self->channel_positions_from_input @@ -504,52 +504,40 @@ gst_audio_interleave_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, } static GstFlowReturn -gst_audio_interleave_aggregate (GstAggregator * aggregator, gboolean timeout) +gst_audio_interleave_update_src_caps (GstAggregator * agg, GstCaps * caps, + GstCaps ** ret) { - GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (aggregator); - GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (aggregator); + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + GstStructure *s; - GST_OBJECT_LOCK (aggregator); - if (self->new_caps) { - GstCaps *srccaps; - GstStructure *s; - gboolean ret; + /* This means that either no caps have been set on the sink pad (if + * sinkcaps is NULL) or that there is no sink pad (if channels == 0). + */ + if (self->sinkcaps == NULL || self->channels == 0) + return GST_FLOW_NOT_NEGOTIATED; - if (self->sinkcaps == NULL || self->channels == 0) { - /* In this case, let the base class handle it */ - goto not_negotiated; - } + *ret = gst_caps_copy (self->sinkcaps); + s = gst_caps_get_structure (*ret, 0); - srccaps = gst_caps_copy (self->sinkcaps); - s = gst_caps_get_structure (srccaps, 0); + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout", + G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK, + gst_audio_interleave_get_channel_mask (self), NULL); - gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout", - G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK, - gst_audio_interleave_get_channel_mask (self), NULL); + return GST_FLOW_OK; +} +static gboolean +gst_audio_interleave_negotiated_src_caps (GstAggregator * agg, GstCaps * caps) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); - GST_OBJECT_UNLOCK (aggregator); - ret = gst_audio_aggregator_set_src_caps (aagg, srccaps); - gst_caps_unref (srccaps); + if (!GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps)) + return FALSE; - if (!ret) - goto src_did_not_accept; + gst_audio_interleave_set_process_function (self, &aagg->info); - GST_OBJECT_LOCK (aggregator); - - gst_audio_interleave_set_process_function (self, &aagg->info); - - self->new_caps = FALSE; - } - -not_negotiated: - GST_OBJECT_UNLOCK (aggregator); - - return GST_AGGREGATOR_CLASS (parent_class)->aggregate (aggregator, timeout); - -src_did_not_accept: - GST_WARNING_OBJECT (self, "src did not accept setcaps()"); - return GST_FLOW_NOT_NEGOTIATED;; + return TRUE; } static void @@ -586,7 +574,8 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) 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; - agg_class->aggregate = gst_audio_interleave_aggregate; + agg_class->update_src_caps = gst_audio_interleave_update_src_caps; + agg_class->negotiated_src_caps = gst_audio_interleave_negotiated_src_caps; aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer; @@ -720,7 +709,6 @@ gst_audio_interleave_stop (GstAggregator * agg) if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) return FALSE; - self->new_caps = FALSE; gst_caps_replace (&self->sinkcaps, NULL); return TRUE; @@ -765,9 +753,7 @@ gst_audio_interleave_request_new_pad (GstElement * element, g_value_unset (&val); /* Update the src caps if we already have them */ - GST_OBJECT_LOCK (self); - self->new_caps = TRUE; - GST_OBJECT_UNLOCK (self); + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); return GST_PAD_CAST (newpad); @@ -804,7 +790,7 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad) ipad->channel--; } - self->new_caps = TRUE; + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); GST_OBJECT_UNLOCK (self); diff --git a/gst/audiomixer/gstaudiointerleave.h b/gst/audiomixer/gstaudiointerleave.h index 6dd82d383c..ef959ceef2 100644 --- a/gst/audiomixer/gstaudiointerleave.h +++ b/gst/audiomixer/gstaudiointerleave.h @@ -58,7 +58,6 @@ struct _GstAudioInterleave { gint padcounter; guint channels; - gboolean new_caps; GstCaps *sinkcaps; GValueArray *channel_positions; diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index ac2f49c04b..2233b82c1e 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -348,7 +348,6 @@ gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, GstAudioInfo info; GstStructure *s; gint channels = 0; - gboolean ret; caps = gst_caps_copy (orig_caps); @@ -405,20 +404,21 @@ gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, gst_caps_unref (caps); return FALSE; } + } else { + gst_caps_replace (&aagg->current_caps, caps); + aagg->info = info; + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (agg)); } GST_OBJECT_UNLOCK (audiomixer); - ret = gst_audio_aggregator_set_src_caps (aagg, caps); - - if (ret) - gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), - orig_caps); + gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), + orig_caps); GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); - return ret; + return TRUE; /* ERRORS */ invalid_format: @@ -429,6 +429,20 @@ invalid_format: } } +static GstFlowReturn +gst_audiomixer_update_src_caps (GstAggregator * agg, GstCaps * caps, + GstCaps ** ret) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + + if (aagg->current_caps == NULL) + return GST_AGGREGATOR_FLOW_NEED_DATA; + + *ret = gst_caps_ref (aagg->current_caps); + + return GST_FLOW_OK; +} + static gboolean gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, GstEvent * event) @@ -495,6 +509,8 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); + agg_class->update_src_caps = + GST_DEBUG_FUNCPTR (gst_audiomixer_update_src_caps); aagg_class->aggregate_one_buffer = gst_audiomixer_aggregate_one_buffer; } diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index cb992f393a..f2e820b526 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -1043,7 +1043,9 @@ GST_START_TEST (test_audiointerleave_2ch_smallbuf) gst_caps_unref (caps); gst_event_unref (ev); - for (i = 0; i < 24; i++) + /* eat the caps processing */ + gst_harness_crank_single_clock_wait (h); + for (i = 0; i < 23; i++) gst_harness_crank_single_clock_wait (h); fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK (h->element)), 750 * GST_MSECOND); From 8a302f18153552df73fcad1deed75af205023b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 20 May 2017 15:56:16 +0200 Subject: [PATCH 118/153] aggregator: Remove unused GST_FLOW_NOT_HANDLED --- gst-libs/gst/audio/gstaudioaggregator.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h index be76349a43..8f2e448faf 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.h +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -106,8 +106,6 @@ GType gst_audio_aggregator_pad_get_type (void); #define GST_IS_AUDIO_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_AGGREGATOR)) #define GST_IS_AUDIO_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_AGGREGATOR)) -#define GST_FLOW_CUSTOM_SUCCESS GST_FLOW_NOT_HANDLED - /** * GstAudioAggregator: * @parent: The parent #GstAggregator From 7b2e201bfa698fb1e1367cd509fb2743ad6cc577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 20 May 2017 17:47:04 +0200 Subject: [PATCH 119/153] audiointerleave: Take object lock while modifying channel count --- gst/audiomixer/gstaudiointerleave.c | 13 ++++++++++--- gst/audiomixer/gstaudiointerleave.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index dfae4a51ae..cc10e2145e 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -513,8 +513,11 @@ gst_audio_interleave_update_src_caps (GstAggregator * agg, GstCaps * caps, /* This means that either no caps have been set on the sink pad (if * sinkcaps is NULL) or that there is no sink pad (if channels == 0). */ - if (self->sinkcaps == NULL || self->channels == 0) + GST_OBJECT_LOCK (self); + if (self->sinkcaps == NULL || self->channels == 0) { + GST_OBJECT_UNLOCK (self); return GST_FLOW_NOT_NEGOTIATED; + } *ret = gst_caps_copy (self->sinkcaps); s = gst_caps_get_structure (*ret, 0); @@ -523,6 +526,8 @@ gst_audio_interleave_update_src_caps (GstAggregator * agg, GstCaps * caps, G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK, gst_audio_interleave_get_channel_mask (self), NULL); + GST_OBJECT_UNLOCK (self); + return GST_FLOW_OK; } @@ -726,10 +731,12 @@ gst_audio_interleave_request_new_pad (GstElement * element, /* FIXME: We ignore req_name, this is evil! */ + GST_OBJECT_LOCK (self); padnumber = g_atomic_int_add (&self->padcounter, 1); - channel = g_atomic_int_add (&self->channels, 1); + channel = self->channels++; if (!self->channel_positions_from_input) channel = padnumber; + GST_OBJECT_UNLOCK (self); pad_name = g_strdup_printf ("sink_%u", padnumber); newpad = (GstAudioInterleavePad *) @@ -776,7 +783,7 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad) /* Take lock to make sure we're not changing this when processing buffers */ GST_OBJECT_LOCK (self); - g_atomic_int_add (&self->channels, -1); + self->channels--; position = GST_AUDIO_INTERLEAVE_PAD (pad)->channel; g_value_array_remove (self->input_channel_positions, position); diff --git a/gst/audiomixer/gstaudiointerleave.h b/gst/audiomixer/gstaudiointerleave.h index ef959ceef2..bf46f4a505 100644 --- a/gst/audiomixer/gstaudiointerleave.h +++ b/gst/audiomixer/gstaudiointerleave.h @@ -56,7 +56,7 @@ struct _GstAudioInterleave { GstAudioAggregator parent; gint padcounter; - guint channels; + guint channels; /* object lock */ GstCaps *sinkcaps; From 55ab23cc30705f18071e9032957594eda35318aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 20 May 2017 17:59:19 +0200 Subject: [PATCH 120/153] audioaggregator: Use downstream allocator and params if available https://bugzilla.gnome.org/show_bug.cgi?id=746529 --- gst-libs/gst/audio/gstaudioaggregator.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index f5c6c2be15..d6833319e5 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1024,10 +1024,19 @@ static GstBuffer * gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, guint num_frames) { - GstBuffer *outbuf = gst_buffer_new_allocate (NULL, num_frames * - GST_AUDIO_INFO_BPF (&aagg->info), NULL); + GstAllocator *allocator; + GstAllocationParams params; + GstBuffer *outbuf; GstMapInfo outmap; + gst_aggregator_get_allocator (GST_AGGREGATOR (aagg), &allocator, ¶ms); + + outbuf = gst_buffer_new_allocate (allocator, num_frames * + GST_AUDIO_INFO_BPF (&aagg->info), ¶ms); + + if (allocator) + gst_object_unref (allocator); + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); gst_audio_format_fill_silence (aagg->info.finfo, outmap.data, outmap.size); gst_buffer_unmap (outbuf, &outmap); From 930c3cea405d43c6411d6dd05cb125057f78c014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Sat, 20 May 2017 19:00:23 +0200 Subject: [PATCH 121/153] audioaggregate: Don't hold object locks across calls to aggregate_one https://bugzilla.gnome.org/show_bug.cgi?id=782878 --- gst-libs/gst/audio/gstaudioaggregator.c | 18 +++++++++++++++++- gst/audiomixer/gstaudiointerleave.c | 6 ++++++ gst/audiomixer/gstaudiomixer.c | 9 ++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index d6833319e5..398e2cf738 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -976,6 +976,8 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, guint out_start; gboolean filled; guint blocksize; + guint in_offset; + gboolean pad_changed = FALSE; blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); @@ -1001,12 +1003,26 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, return FALSE; } + gst_buffer_ref (inbuf); + in_offset = pad->priv->position; + GST_OBJECT_UNLOCK (pad); + GST_OBJECT_UNLOCK (aagg); + filled = GST_AUDIO_AGGREGATOR_GET_CLASS (aagg)->aggregate_one_buffer (aagg, - pad, inbuf, pad->priv->position, outbuf, out_start, overlap); + pad, inbuf, in_offset, outbuf, out_start, overlap); + + GST_OBJECT_LOCK (aagg); + GST_OBJECT_LOCK (pad); + + pad_changed = (inbuf != pad->priv->buffer); + gst_buffer_unref (inbuf); if (filled) GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP); + if (pad_changed) + return FALSE; + pad->priv->position += overlap; pad->priv->output_offset += overlap; diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index cc10e2145e..fd58e789c6 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -823,6 +823,9 @@ gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, gint out_width, in_bpf, out_bpf, out_channels, channel; guint8 *outdata; + GST_OBJECT_LOCK (aagg); + GST_OBJECT_LOCK (aaggpad); + 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); @@ -850,6 +853,9 @@ gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg, gst_buffer_unmap (inbuf, &inmap); gst_buffer_unmap (outbuf, &outmap); + GST_OBJECT_UNLOCK (aaggpad); + GST_OBJECT_UNLOCK (aagg); + return TRUE; } diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 2233b82c1e..0ac0f4afc2 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -624,7 +624,6 @@ gst_audiomixer_release_pad (GstElement * element, GstPad * pad) } -/* Called with object lock and pad object lock held */ static gboolean gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, @@ -635,8 +634,13 @@ gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, GstMapInfo outmap; gint bpf; + GST_OBJECT_LOCK (aagg); + GST_OBJECT_LOCK (aaggpad); + if (pad->mute || pad->volume < G_MINDOUBLE) { GST_DEBUG_OBJECT (pad, "Skipping muted pad"); + GST_OBJECT_UNLOCK (aaggpad); + GST_OBJECT_UNLOCK (aagg); return FALSE; } @@ -744,6 +748,9 @@ gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, gst_buffer_unmap (inbuf, &inmap); gst_buffer_unmap (outbuf, &outmap); + GST_OBJECT_UNLOCK (aaggpad); + GST_OBJECT_UNLOCK (aagg); + return TRUE; } From e537f3576ada295d96a2ac24361e611f3fb9b14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 23 May 2017 00:52:27 +0200 Subject: [PATCH 122/153] tests: audiointerleave: Remove drain with manual clock Now that the queries go onto the queue, you may need to pull the crank in order for them to be processed, making this test difficult. --- tests/check/elements/audiointerleave.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c index f2e820b526..71348f4592 100644 --- a/tests/check/elements/audiointerleave.c +++ b/tests/check/elements/audiointerleave.c @@ -980,7 +980,6 @@ GST_START_TEST (test_audiointerleave_2ch_smallbuf) GstHarness *h; GstHarness *h2; GstBuffer *buffer; - GstQuery *q; gint i; GstEvent *ev; GstCaps *ecaps, *caps; @@ -1050,11 +1049,6 @@ GST_START_TEST (test_audiointerleave_2ch_smallbuf) fail_unless_equals_uint64 (gst_clock_get_time (GST_ELEMENT_CLOCK (h->element)), 750 * GST_MSECOND); - /* Check that the queue is really empty */ - q = gst_query_new_drain (); - gst_pad_peer_query (h->srcpad, q); - gst_query_unref (q); - buffer = gst_harness_pull (h); sink_handoff_float32 (NULL, buffer, NULL, GUINT_TO_POINTER (3)); gst_buffer_unref (buffer); From 7108cd2fc85b3048cd60b865773b84349463c8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Mon, 22 May 2017 23:06:01 +0200 Subject: [PATCH 123/153] tests: Make audiomixer test_clip verify the resulting timestamps too --- tests/check/elements/audiomixer.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 9ef5c3c8ff..05309557fb 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -901,8 +901,9 @@ handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, gpointer user_data) { GST_DEBUG ("got buffer -- SIZE: %" G_GSIZE_FORMAT - " -- %p DURATION is %" GST_TIME_FORMAT, + " -- %p PTS is %" GST_TIME_FORMAT " END is %" GST_TIME_FORMAT, gst_buffer_get_size (buffer), buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); gst_buffer_replace (&handoff_buffer, buffer); @@ -936,6 +937,7 @@ GST_START_TEST (test_clip) /* just an audiomixer and a fakesink */ audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + g_object_set (audiomixer, "output-buffer-duration", 50 * GST_MSECOND, NULL); sink = gst_element_factory_make ("fakesink", "sink"); g_object_set (sink, "signal-handoffs", TRUE, NULL); g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); @@ -992,6 +994,7 @@ GST_START_TEST (test_clip) buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); GST_DEBUG ("pushing buffer %p START %" GST_TIME_FORMAT " -- DURATION is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), @@ -1002,11 +1005,13 @@ GST_START_TEST (test_clip) gst_pad_query (sinkpad, drain); fail_unless (handoff_buffer != NULL); + ck_assert_int_eq (GST_BUFFER_PTS (handoff_buffer) + + GST_BUFFER_DURATION (handoff_buffer), 150 * GST_MSECOND); gst_buffer_replace (&handoff_buffer, NULL); /* should not be clipped */ buffer = gst_buffer_new_and_alloc (44100); - GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_TIMESTAMP (buffer) = 1150 * GST_MSECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, @@ -1016,6 +1021,8 @@ GST_START_TEST (test_clip) ck_assert_int_eq (ret, GST_FLOW_OK); gst_pad_query (sinkpad, drain); fail_unless (handoff_buffer != NULL); + ck_assert_int_eq (GST_BUFFER_PTS (handoff_buffer) + + GST_BUFFER_DURATION (handoff_buffer), 400 * GST_MSECOND); gst_buffer_replace (&handoff_buffer, NULL); fail_unless (handoff_buffer == NULL); @@ -1024,8 +1031,11 @@ GST_START_TEST (test_clip) buffer = gst_buffer_new_and_alloc (44100); GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + GST_DEBUG ("pushing buffer %p PTS is %" GST_TIME_FORMAT + " END is %" GST_TIME_FORMAT, buffer, + GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); From ecf38a3ade63d97c65cf7fb35e148cb0d07aad7a Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 10 Jul 2017 20:16:10 +0200 Subject: [PATCH 124/153] audiomixer: refactor test Apply cleanups from the adder tests. Use a fixture for common code. --- tests/check/elements/audiomixer.c | 35 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 05309557fb..dd2ae2ae14 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -40,6 +40,21 @@ static GMainLoop *main_loop; +/* fixtures */ + +static void +test_setup (void) +{ + main_loop = g_main_loop_new (NULL, FALSE); +} + +static void +test_teardown (void) +{ + g_main_loop_unref (main_loop); + main_loop = NULL; +} + /* make sure downstream gets a CAPS event before buffers are sent */ GST_START_TEST (test_caps) { @@ -261,7 +276,6 @@ GST_START_TEST (test_event) format = GST_FORMAT_UNDEFINED; position = -1; - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) test_event_message_received, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -293,7 +307,6 @@ GST_START_TEST (test_event) ck_assert_int_eq (position, 2 * GST_SECOND); /* cleanup */ - g_main_loop_unref (main_loop); gst_consistency_checker_free (chk_1); gst_consistency_checker_free (chk_2); gst_consistency_checker_free (chk_3); @@ -394,7 +407,6 @@ GST_START_TEST (test_play_twice) play_count = 0; - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) test_play_twice_message_received, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -429,7 +441,6 @@ GST_START_TEST (test_play_twice) ck_assert_int_eq (play_count, 2); /* cleanup */ - g_main_loop_unref (main_loop); gst_consistency_checker_free (consist); gst_event_unref (play_seek_event); gst_bus_remove_signal_watch (bus); @@ -480,7 +491,6 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_SEEK_TYPE_SET, (GstClockTime) 0, GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) test_play_twice_message_received, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -535,7 +545,6 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* cleanup */ - g_main_loop_unref (main_loop); gst_event_unref (play_seek_event); gst_consistency_checker_free (consist); gst_bus_remove_signal_watch (bus); @@ -606,7 +615,6 @@ GST_START_TEST (test_live_seeking) }; GST_INFO ("preparing test"); - main_loop = NULL; play_seek_event = NULL; /* build pipeline */ @@ -671,7 +679,6 @@ GST_START_TEST (test_live_seeking) GST_SEEK_TYPE_SET, (GstClockTime) 0, GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); g_signal_connect (bus, "message::eos", @@ -719,8 +726,6 @@ GST_START_TEST (test_live_seeking) /* cleanup */ GST_INFO ("cleaning up"); gst_consistency_checker_free (consist); - if (main_loop) - g_main_loop_unref (main_loop); if (play_seek_event) gst_event_unref (play_seek_event); gst_bus_remove_signal_watch (bus); @@ -765,7 +770,6 @@ GST_START_TEST (test_add_pad) srcpad = gst_element_get_static_pad (audiomixer, "src"); gst_object_unref (srcpad); - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) message_received, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -804,7 +808,6 @@ GST_START_TEST (test_add_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* cleanup */ - g_main_loop_unref (main_loop); gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); @@ -847,7 +850,6 @@ GST_START_TEST (test_remove_pad) srcpad = gst_element_get_static_pad (audiomixer, "src"); gst_object_unref (srcpad); - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) message_received, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -885,7 +887,6 @@ GST_START_TEST (test_remove_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* cleanup */ - g_main_loop_unref (main_loop); gst_bus_remove_signal_watch (bus); gst_object_unref (G_OBJECT (bus)); gst_object_unref (G_OBJECT (bin)); @@ -1228,7 +1229,6 @@ GST_START_TEST (test_loop) GST_SEEK_TYPE_SET, (GstClockTime) 0, GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); - main_loop = g_main_loop_new (NULL, FALSE); g_signal_connect (bus, "message::segment-done", (GCallback) loop_segment_done, bin); g_signal_connect (bus, "message::error", (GCallback) message_received, bin); @@ -1257,7 +1257,6 @@ GST_START_TEST (test_loop) state_res = gst_element_set_state (bin, GST_STATE_NULL); /* cleanup */ - g_main_loop_unref (main_loop); gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); @@ -1361,8 +1360,6 @@ run_sync_test (SendBuffersFunction send_buffers, GST_INFO ("preparing test"); - main_loop = g_main_loop_new (NULL, FALSE); - /* build pipeline */ bin = gst_pipeline_new ("pipeline"); bus = gst_element_get_bus (bin); @@ -1451,7 +1448,6 @@ run_sync_test (SendBuffersFunction send_buffers, gst_bus_remove_signal_watch (bus); gst_object_unref (bus); gst_object_unref (bin); - g_main_loop_unref (main_loop); } static void @@ -1946,6 +1942,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_sync_unaligned); tcase_add_test (tc_chain, test_segment_base_handling); tcase_add_test (tc_chain, test_sinkpad_property_controller); + tcase_add_checked_fixture (tc_chain, test_setup, test_teardown); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 6d53284450ad34cad0d978dc1bf6494c35c4cc9b Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 10 Jul 2017 21:08:09 +0200 Subject: [PATCH 125/153] audiomixer: more test cleanups Port over the test helpers from the adder tests. --- tests/check/elements/audiomixer.c | 413 +++++++++++------------------- 1 file changed, 145 insertions(+), 268 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index dd2ae2ae14..e8ad5e3d6f 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -55,97 +55,63 @@ test_teardown (void) main_loop = NULL; } -/* make sure downstream gets a CAPS event before buffers are sent */ -GST_START_TEST (test_caps) + +/* some test helpers */ + +static GstElement * +setup_pipeline (GstElement * audiomixer, gint num_srcs) { - GstElement *pipeline, *src, *audiomixer, *sink; - GstStateChangeReturn state_res; + GstElement *pipeline, *src, *sink; + gint i; + + pipeline = gst_pipeline_new ("pipeline"); + if (!audiomixer) { + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + } + + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), audiomixer, sink, NULL); + gst_element_link (audiomixer, sink); + + for (i = 0; i < num_srcs; i++) { + src = gst_element_factory_make ("audiotestsrc", NULL); + g_object_set (src, "wave", 4, NULL); /* silence */ + gst_bin_add (GST_BIN (pipeline), src); + gst_element_link (src, audiomixer); + } + return pipeline; +} + +static GstCaps * +get_element_sink_pad_caps (GstElement * pipeline, const gchar * element_name) +{ + GstElement *sink; GstCaps *caps; GstPad *pad; - /* build pipeline */ - pipeline = gst_pipeline_new ("pipeline"); - - src = gst_element_factory_make ("audiotestsrc", "src1"); - g_object_set (src, "wave", 4, NULL); /* silence */ - audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); - - fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); - - /* prepare playing */ - state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); - - /* wait for preroll */ - state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); - fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); - - /* check caps on fakesink */ + sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink"); pad = gst_element_get_static_pad (sink, "sink"); caps = gst_pad_get_current_caps (pad); - fail_unless (caps != NULL); - gst_caps_unref (caps); gst_object_unref (pad); + gst_object_unref (sink); - gst_element_set_state (pipeline, GST_STATE_NULL); - gst_object_unref (pipeline); + return caps; } -GST_END_TEST; - -/* check that caps set on the property are honoured */ -GST_START_TEST (test_filter_caps) +static void +set_state_and_wait (GstElement * pipeline, GstState state) { - GstElement *pipeline, *src, *audiomixer, *sink; GstStateChangeReturn state_res; - GstCaps *filter_caps, *caps; - GstPad *pad; - filter_caps = gst_caps_new_simple ("audio/x-raw", - "format", G_TYPE_STRING, GST_AUDIO_NE (F32), - "layout", G_TYPE_STRING, "interleaved", - "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, - "channel-mask", GST_TYPE_BITMASK, (guint64) 0x04, NULL); - - /* build pipeline */ - pipeline = gst_pipeline_new ("pipeline"); - - src = gst_element_factory_make ("audiotestsrc", NULL); - g_object_set (src, "wave", 4, NULL); /* silence */ - audiomixer = gst_element_factory_make ("audiomixer", NULL); - g_object_set (audiomixer, "caps", filter_caps, NULL); - sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); - - fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); - - /* prepare playing */ - state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + /* prepare paused/playing */ + state_res = gst_element_set_state (pipeline, state); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* wait for preroll */ state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); - fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); - - /* check caps on fakesink */ - pad = gst_element_get_static_pad (sink, "sink"); - caps = gst_pad_get_current_caps (pad); - fail_unless (caps != NULL); - GST_INFO_OBJECT (pipeline, "received caps: %" GST_PTR_FORMAT, caps); - fail_unless (gst_caps_is_equal_fixed (caps, filter_caps)); - gst_caps_unref (caps); - gst_object_unref (pad); - - gst_element_set_state (pipeline, GST_STATE_NULL); - gst_object_unref (pipeline); - - gst_caps_unref (filter_caps); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); } -GST_END_TEST; - static gboolean set_playing (GstElement * element) { @@ -157,6 +123,20 @@ set_playing (GstElement * element) return FALSE; } +static void +play_and_wait (GstElement * pipeline) +{ + GstStateChangeReturn state_res; + + g_idle_add ((GSourceFunc) set_playing, pipeline); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (pipeline, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); +} + static void message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) { @@ -194,6 +174,64 @@ message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) } +/* make sure downstream gets a CAPS event before buffers are sent */ +GST_START_TEST (test_caps) +{ + GstElement *pipeline; + GstCaps *caps; + + /* build pipeline */ + pipeline = setup_pipeline (NULL, 1); + + /* prepare playing */ + set_state_and_wait (pipeline, GST_STATE_PAUSED); + + /* check caps on fakesink */ + caps = get_element_sink_pad_caps (pipeline, "sink"); + fail_unless (caps != NULL); + gst_caps_unref (caps); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +/* check that caps set on the property are honoured */ +GST_START_TEST (test_filter_caps) +{ + GstElement *pipeline, *audiomixer; + GstCaps *filter_caps, *caps; + + filter_caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (F32), + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, + "channel-mask", GST_TYPE_BITMASK, (guint64) 0x04, NULL); + + /* build pipeline */ + audiomixer = gst_element_factory_make ("audiomixer", NULL); + g_object_set (audiomixer, "caps", filter_caps, NULL); + pipeline = setup_pipeline (audiomixer, 1); + + /* prepare playing */ + set_state_and_wait (pipeline, GST_STATE_PAUSED); + + /* check caps on fakesink */ + caps = get_element_sink_pad_caps (pipeline, "sink"); + fail_unless (caps != NULL); + GST_INFO_OBJECT (pipeline, "received caps: %" GST_PTR_FORMAT, caps); + fail_unless (gst_caps_is_equal_fixed (caps, filter_caps)); + gst_caps_unref (caps); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gst_caps_unref (filter_caps); +} + +GST_END_TEST; + static GstFormat format = GST_FORMAT_UNDEFINED; static gint64 position = -1; @@ -222,7 +260,6 @@ GST_START_TEST (test_event) GstElement *bin, *src1, *src2, *audiomixer, *sink; GstBus *bus; GstEvent *seek_event; - GstStateChangeReturn state_res; gboolean res; GstPad *srcpad, *sinkpad; GstStreamConsistency *chk_1, *chk_2, *chk_3; @@ -285,24 +322,13 @@ GST_START_TEST (test_event) GST_INFO ("starting test"); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); res = gst_element_send_event (bin, seek_event); fail_unless (res == TRUE, NULL); /* run pipeline */ - g_idle_add ((GSourceFunc) set_playing, bin); - - GST_INFO ("running main loop"); - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); ck_assert_int_eq (position, 2 * GST_SECOND); @@ -322,7 +348,7 @@ static GstEvent *play_seek_event = NULL; static void test_play_twice_message_received (GstBus * bus, GstMessage * message, - GstPipeline * bin) + GstElement * bin) { gboolean res; GstStateChangeReturn state_res; @@ -334,25 +360,16 @@ test_play_twice_message_received (GstBus * bus, GstMessage * message, case GST_MESSAGE_SEGMENT_DONE: play_count++; if (play_count == 1) { - state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); + state_res = gst_element_set_state (bin, GST_STATE_READY); ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* prepare playing again */ - state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - res = gst_element_send_event (GST_ELEMENT (bin), - gst_event_ref (play_seek_event)); + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); fail_unless (res == TRUE, NULL); - state_res = - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); } else { g_main_loop_quit (main_loop); @@ -367,35 +384,20 @@ test_play_twice_message_received (GstBus * bus, GstMessage * message, GST_START_TEST (test_play_twice) { - GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstElement *bin, *audiomixer; GstBus *bus; gboolean res; - GstStateChangeReturn state_res; GstPad *srcpad; GstStreamConsistency *consist; GST_INFO ("preparing test"); /* build pipeline */ - bin = gst_pipeline_new ("pipeline"); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + bin = setup_pipeline (audiomixer, 2); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); - src1 = gst_element_factory_make ("audiotestsrc", "src1"); - g_object_set (src1, "wave", 4, NULL); /* silence */ - src2 = gst_element_factory_make ("audiotestsrc", "src2"); - g_object_set (src2, "wave", 4, NULL); /* silence */ - audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); - - res = gst_element_link (src1, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (src2, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (audiomixer, sink); - fail_unless (res == TRUE, NULL); - srcpad = gst_element_get_static_pad (audiomixer, "src"); consist = gst_consistency_checker_new (srcpad); gst_object_unref (srcpad); @@ -416,14 +418,7 @@ GST_START_TEST (test_play_twice) GST_INFO ("starting test"); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); fail_unless (res == TRUE, NULL); @@ -431,12 +426,7 @@ GST_START_TEST (test_play_twice) GST_INFO ("seeked"); /* run pipeline */ - g_idle_add ((GSourceFunc) set_playing, bin); - - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); ck_assert_int_eq (play_count, 2); @@ -452,7 +442,7 @@ GST_END_TEST; GST_START_TEST (test_play_twice_then_add_and_play_again) { - GstElement *bin, *src1, *src2, *src3, *audiomixer, *sink; + GstElement *bin, *src, *audiomixer; GstBus *bus; gboolean res; GstStateChangeReturn state_res; @@ -463,29 +453,15 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_INFO ("preparing test"); /* build pipeline */ - bin = gst_pipeline_new ("pipeline"); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + bin = setup_pipeline (audiomixer, 2); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); - src1 = gst_element_factory_make ("audiotestsrc", "src1"); - g_object_set (src1, "wave", 4, NULL); /* silence */ - src2 = gst_element_factory_make ("audiotestsrc", "src2"); - g_object_set (src2, "wave", 4, NULL); /* silence */ - audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); - srcpad = gst_element_get_static_pad (audiomixer, "src"); consist = gst_consistency_checker_new (srcpad); gst_object_unref (srcpad); - res = gst_element_link (src1, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (src2, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (audiomixer, sink); - fail_unless (res == TRUE, NULL); - play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, (GstClockTime) 0, @@ -504,14 +480,7 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_INFO ("starting test-loop %d", i); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); fail_unless (res == TRUE, NULL); @@ -519,22 +488,17 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_INFO ("seeked"); /* run pipeline */ - g_idle_add ((GSourceFunc) set_playing, bin); - - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_READY); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); ck_assert_int_eq (play_count, 2); /* plug another source */ if (i == 0) { - src3 = gst_element_factory_make ("audiotestsrc", "src3"); - g_object_set (src3, "wave", 4, NULL); /* silence */ - gst_bin_add (GST_BIN (bin), src3); + src = gst_element_factory_make ("audiotestsrc", NULL); + g_object_set (src, "wave", 4, NULL); /* silence */ + gst_bin_add (GST_BIN (bin), src); - res = gst_element_link (src3, audiomixer); + res = gst_element_link (src, audiomixer); fail_unless (res == TRUE, NULL); } @@ -555,23 +519,6 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) GST_END_TEST; -static void -test_live_seeking_eos_message_received (GstBus * bus, GstMessage * message, - GstPipeline * bin) -{ - GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, - GST_MESSAGE_SRC (message), message); - - switch (message->type) { - case GST_MESSAGE_EOS: - g_main_loop_quit (main_loop); - break; - default: - g_assert_not_reached (); - break; - } -} - static GstElement * test_live_seeking_try_audiosrc (const gchar * factory_name) { @@ -681,8 +628,7 @@ GST_START_TEST (test_live_seeking) g_signal_connect (bus, "message::error", (GCallback) message_received, bin); g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); - g_signal_connect (bus, "message::eos", - (GCallback) test_live_seeking_eos_message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); srcpad = gst_element_get_static_pad (audiomixer, "src"); consist = gst_consistency_checker_new (srcpad); @@ -696,14 +642,7 @@ GST_START_TEST (test_live_seeking) GST_INFO ("starting test-loop %d", i); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); fail_unless (res == TRUE, NULL); @@ -711,14 +650,7 @@ GST_START_TEST (test_live_seeking) GST_INFO ("seeked"); /* run pipeline */ - g_idle_add ((GSourceFunc) set_playing, bin); - - GST_INFO ("playing"); - - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); gst_consistency_checker_reset (consist); } @@ -779,14 +711,7 @@ GST_START_TEST (test_add_pad) GST_INFO ("starting test"); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); /* add other element */ gst_bin_add_many (GST_BIN (bin), src2, NULL); @@ -800,12 +725,7 @@ GST_START_TEST (test_add_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* now play all */ - g_idle_add ((GSourceFunc) set_playing, bin); - - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); /* cleanup */ gst_bus_remove_signal_watch (bus); @@ -879,12 +799,7 @@ GST_START_TEST (test_remove_pad) ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); /* now play all */ - g_idle_add ((GSourceFunc) set_playing, bin); - - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + play_and_wait (bin); /* cleanup */ gst_bus_remove_signal_watch (bus); @@ -1091,14 +1006,7 @@ GST_START_TEST (test_duration_is_max) GST_BASE_SRC (src[2])->segment.duration = 2000; /* set to playing */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + set_state_and_wait (bin, GST_STATE_PLAYING); res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); fail_unless (res, NULL); @@ -1148,14 +1056,7 @@ GST_START_TEST (test_duration_unknown_overrides) GST_BASE_SRC (src[2])->segment.duration = 2000; /* set to playing */ - state_res = gst_element_set_state (bin, GST_STATE_PLAYING); - fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); - - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + set_state_and_wait (bin, GST_STATE_PLAYING); res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); fail_unless (res, NULL); @@ -1196,34 +1097,18 @@ loop_segment_done (GstBus * bus, GstMessage * message, GstElement * bin) GST_START_TEST (test_loop) { - GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstElement *bin; GstBus *bus; GstEvent *seek_event; - GstStateChangeReturn state_res; gboolean res; GST_INFO ("preparing test"); /* build pipeline */ - bin = gst_pipeline_new ("pipeline"); + bin = setup_pipeline (NULL, 2); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); - src1 = gst_element_factory_make ("audiotestsrc", "src1"); - g_object_set (src1, "wave", 4, NULL); /* silence */ - src2 = gst_element_factory_make ("audiotestsrc", "src2"); - g_object_set (src2, "wave", 4, NULL); /* silence */ - audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - sink = gst_element_factory_make ("fakesink", "sink"); - gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); - - res = gst_element_link (src1, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (src2, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (audiomixer, sink); - fail_unless (res == TRUE, NULL); - seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, (GstClockTime) 0, @@ -1238,23 +1123,15 @@ GST_START_TEST (test_loop) GST_INFO ("starting test"); /* prepare playing */ - state_res = gst_element_set_state (bin, GST_STATE_PAUSED); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); - - /* wait for completion */ - state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + set_state_and_wait (bin, GST_STATE_PAUSED); res = gst_element_send_event (bin, seek_event); fail_unless (res == TRUE, NULL); /* run pipeline */ - g_idle_add ((GSourceFunc) set_playing, bin); + play_and_wait (bin); - GST_INFO ("running main loop"); - g_main_loop_run (main_loop); - - state_res = gst_element_set_state (bin, GST_STATE_NULL); + fail_unless (looped); /* cleanup */ gst_bus_remove_signal_watch (bus); From 14a9ba2fa49df00a9f74d56cd7ea99a9dc1295f7 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 13 Jul 2017 21:54:55 +0200 Subject: [PATCH 126/153] audiomixer: use test helper once more --- tests/check/elements/audiomixer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index e8ad5e3d6f..858c58d84c 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1174,9 +1174,8 @@ GST_START_TEST (test_flush_start_flush_stop) gst_element_link (audiomixer, sink); - gst_element_set_state (pipeline, GST_STATE_PLAYING); - fail_unless (gst_element_get_state (pipeline, NULL, NULL, - GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS); + /* prepare playing */ + set_state_and_wait (pipeline, GST_STATE_PLAYING); audiomixer_src = gst_element_get_static_pad (audiomixer, "src"); fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); From 35a4149caf6a4864b2fb24fd497d66df994a0830 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 13 Jul 2017 21:55:55 +0200 Subject: [PATCH 127/153] aggregator: code cleanups Fix comment typos, some copy'n'paste in logging. Add more doc comments. --- gst-libs/gst/audio/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 398e2cf738..62174c6e8e 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -394,7 +394,7 @@ gst_audio_aggregator_src_event (GstAggregator * agg, GstEvent * event) gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, &start, &stop_type, &stop); - /* Check the seeking parametters before linking up */ + /* Check the seeking parameters before linking up */ if ((start_type != GST_SEEK_TYPE_NONE) && (start_type != GST_SEEK_TYPE_SET)) { result = FALSE; From 2ae763cc3458886c1c64114f70b34359237514da Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Fri, 14 Jul 2017 08:09:25 +0200 Subject: [PATCH 128/153] audiomixer: add a helper for buffer creation This makes the test setup easier to read. --- tests/check/elements/audiomixer.c | 119 ++++++++---------------------- 1 file changed, 31 insertions(+), 88 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 858c58d84c..8e7549aa2f 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1326,52 +1326,45 @@ run_sync_test (SendBuffersFunction send_buffers, gst_object_unref (bin); } +static GstBuffer * +new_buffer (gsize num_bytes, gint data, GstClockTime ts, GstClockTime dur, + GstBufferFlags flags) +{ + GstMapInfo map; + GstBuffer *buffer = gst_buffer_new_and_alloc (num_bytes); + + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, data, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = ts; + GST_BUFFER_DURATION (buffer) = dur; + if (flags) + GST_BUFFER_FLAG_SET (buffer, flags); + GST_DEBUG ("created buffer %p", buffer); + return buffer; +} + static void send_buffers_sync (GstPad * pad1, GstPad * pad2) { GstBuffer *buffer; - GstMapInfo map; GstFlowReturn ret; - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 1 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 2 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); gst_pad_send_event (pad1, gst_event_new_eos ()); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 2 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 3 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); @@ -1437,49 +1430,24 @@ static void send_buffers_sync_discont (GstPad * pad1, GstPad * pad2) { GstBuffer *buffer; - GstMapInfo map; GstFlowReturn ret; - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 1 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); - GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 3 * GST_SECOND, 1 * GST_SECOND, + GST_BUFFER_FLAG_DISCONT); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); gst_pad_send_event (pad1, gst_event_new_eos ()); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 2 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 3 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 3 * GST_SECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); @@ -1545,48 +1513,23 @@ static void send_buffers_sync_unaligned (GstPad * pad1, GstPad * pad2) { GstBuffer *buffer; - GstMapInfo map; GstFlowReturn ret; - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 750 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 750 * GST_MSECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 1, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 1750 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 1, 1750 * GST_MSECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad1, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); gst_pad_send_event (pad1, gst_event_new_eos ()); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 1750 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 1750 * GST_MSECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - buffer = gst_buffer_new_and_alloc (2000); - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, 2, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = 2750 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 1 * GST_SECOND; - GST_DEBUG ("pushing buffer %p", buffer); + buffer = new_buffer (2000, 2, 2750 * GST_MSECOND, 1 * GST_SECOND, 0); ret = gst_pad_chain (pad2, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); From 1692f910e7237a828ed7987ab30f08c793a6b695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 14 Jul 2017 13:54:31 +0100 Subject: [PATCH 129/153] audiomixer: document caps negotiation raciness Until we land conversion support in audioaggregator (#773762). https://bugzilla.gnome.org/show_bug.cgi?id=777915 --- gst/audiomixer/gstaudiomixer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 0ac0f4afc2..66ca3ec2e4 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -28,7 +28,15 @@ * The audiomixer allows to mix several streams into one by adding the data. * Mixed data is clamped to the min/max values of the data format. * - * Unlike the adder element audiomixer properly synchronises all input streams. + * Unlike the adder element audiomixer properly synchronises all input streams + * and also handles live inputs such as capture sources or RTP properly. + * + * Caps negotiation is inherently racy with the audiomixer element. You can set + * the "caps" property to force audiomixer to operate in a specific audio + * format, sample rate and channel count. In this case you may also need + * audioconvert and/or audioresample elements for each input stream before the + * audiomixer element to make sure the input branch can produce the forced + * format. * * The input pads are from a GstPad subclass and have additional * properties to mute each pad individually and set the volume: From db8482baaf38f255b7df399bece219ea2b6cefdd Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sat, 15 Jul 2017 19:33:21 +0200 Subject: [PATCH 130/153] tests: audiomixer: use the buffer helper for other tests too --- tests/check/elements/audiomixer.c | 60 ++++++++++++------------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 8e7549aa2f..909d615172 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -173,6 +173,23 @@ message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) } } +static GstBuffer * +new_buffer (gsize num_bytes, gint data, GstClockTime ts, GstClockTime dur, + GstBufferFlags flags) +{ + GstMapInfo map; + GstBuffer *buffer = gst_buffer_new_and_alloc (num_bytes); + + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + memset (map.data, data, map.size); + gst_buffer_unmap (buffer, &map); + GST_BUFFER_TIMESTAMP (buffer) = ts; + GST_BUFFER_DURATION (buffer) = dur; + if (flags) + GST_BUFFER_FLAG_SET (buffer, flags); + GST_DEBUG ("created buffer %p", buffer); + return buffer; +} /* make sure downstream gets a CAPS event before buffers are sent */ GST_START_TEST (test_caps) @@ -890,15 +907,12 @@ GST_START_TEST (test_clip) gst_pad_send_event (sinkpad, event); /* should be clipped and ok */ - buffer = gst_buffer_new_and_alloc (44100); - GST_BUFFER_TIMESTAMP (buffer) = 0; - GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + buffer = new_buffer (44100, 0, 0, 250 * GST_MSECOND, 0); GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); - /* The aggregation is done in a dedicated thread, so we can't * know when it is actually going to happen, so we use a DRAIN query * to wait for it to complete. @@ -907,15 +921,11 @@ GST_START_TEST (test_clip) fail_unless (handoff_buffer == NULL); /* should be partially clipped */ - buffer = gst_buffer_new_and_alloc (44100); - GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); - + buffer = new_buffer (44100, 0, 900 * GST_MSECOND, 250 * GST_MSECOND, + GST_BUFFER_FLAG_DISCONT); GST_DEBUG ("pushing buffer %p START %" GST_TIME_FORMAT " -- DURATION is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); - ret = gst_pad_chain (sinkpad, buffer); ck_assert_int_eq (ret, GST_FLOW_OK); gst_pad_query (sinkpad, drain); @@ -926,10 +936,7 @@ GST_START_TEST (test_clip) gst_buffer_replace (&handoff_buffer, NULL); /* should not be clipped */ - buffer = gst_buffer_new_and_alloc (44100); - GST_BUFFER_TIMESTAMP (buffer) = 1150 * GST_MSECOND; - GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - + buffer = new_buffer (44100, 0, 1150 * GST_MSECOND, 250 * GST_MSECOND, 0); GST_DEBUG ("pushing buffer %p END is %" GST_TIME_FORMAT, buffer, GST_TIME_ARGS (GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer))); @@ -943,11 +950,8 @@ GST_START_TEST (test_clip) fail_unless (handoff_buffer == NULL); /* should be clipped and ok */ - - buffer = gst_buffer_new_and_alloc (44100); - GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; - GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + buffer = new_buffer (44100, 0, 2 * GST_SECOND, 250 * GST_MSECOND, + GST_BUFFER_FLAG_DISCONT); GST_DEBUG ("pushing buffer %p PTS is %" GST_TIME_FORMAT " END is %" GST_TIME_FORMAT, buffer, @@ -1326,24 +1330,6 @@ run_sync_test (SendBuffersFunction send_buffers, gst_object_unref (bin); } -static GstBuffer * -new_buffer (gsize num_bytes, gint data, GstClockTime ts, GstClockTime dur, - GstBufferFlags flags) -{ - GstMapInfo map; - GstBuffer *buffer = gst_buffer_new_and_alloc (num_bytes); - - gst_buffer_map (buffer, &map, GST_MAP_WRITE); - memset (map.data, data, map.size); - gst_buffer_unmap (buffer, &map); - GST_BUFFER_TIMESTAMP (buffer) = ts; - GST_BUFFER_DURATION (buffer) = dur; - if (flags) - GST_BUFFER_FLAG_SET (buffer, flags); - GST_DEBUG ("created buffer %p", buffer); - return buffer; -} - static void send_buffers_sync (GstPad * pad1, GstPad * pad2) { From d176e8bd0115ffbdc881befdadece3035d37f257 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sat, 15 Jul 2017 21:28:38 +0200 Subject: [PATCH 131/153] tests: audiomixer: set all properties at once --- tests/check/elements/audiomixer.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 909d615172..f0f16f393c 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -701,12 +701,10 @@ GST_START_TEST (test_add_pad) gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); src1 = gst_element_factory_make ("audiotestsrc", "src1"); - g_object_set (src1, "num-buffers", 4, NULL); - g_object_set (src1, "wave", 4, NULL); /* silence */ + g_object_set (src1, "num-buffers", 4, "wave", /* silence */ 4, NULL); src2 = gst_element_factory_make ("audiotestsrc", "src2"); /* one buffer less, we connect with 1 buffer of delay */ - g_object_set (src2, "num-buffers", 3, NULL); - g_object_set (src2, "wave", 4, NULL); /* silence */ + g_object_set (src2, "num-buffers", 3, "wave", /* silence */ 4, NULL); audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); sink = gst_element_factory_make ("fakesink", "sink"); gst_bin_add_many (GST_BIN (bin), src1, audiomixer, sink, NULL); @@ -769,8 +767,7 @@ GST_START_TEST (test_remove_pad) gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); src = gst_element_factory_make ("audiotestsrc", "src"); - g_object_set (src, "num-buffers", 4, NULL); - g_object_set (src, "wave", 4, NULL); + g_object_set (src, "num-buffers", 4, "wave", 4, NULL); audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); sink = gst_element_factory_make ("fakesink", "sink"); gst_bin_add_many (GST_BIN (bin), src, audiomixer, sink, NULL); From d95e62be8211ac71a8469d5369374a7e8a8a2c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 18 Jul 2017 00:29:56 +0100 Subject: [PATCH 132/153] audio: mark symbols explicitly for export with GST_EXPORT --- gst-libs/gst/audio/gstaudioaggregator.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h index 8f2e448faf..7f910bf9e0 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.h +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -93,6 +93,7 @@ struct _GstAudioAggregatorPadClass gpointer _gst_reserved[GST_PADDING]; }; +GST_EXPORT GType gst_audio_aggregator_pad_get_type (void); /************************** @@ -154,12 +155,13 @@ struct _GstAudioAggregatorClass { * GstAggregator methods * ************************/ +GST_EXPORT GType gst_audio_aggregator_get_type(void); -void -gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, - GstAudioAggregatorPad * pad, GstCaps * caps); - +GST_EXPORT +void gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, + GstCaps * caps); G_END_DECLS From 4080b844c02622d504315aa662cc9935e616242e Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 23 Jul 2017 13:26:23 +0200 Subject: [PATCH 133/153] audioaggregator: use local var Instead of the self->priv-> deref use the local var we created already. --- gst-libs/gst/audio/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 62174c6e8e..2bcf11d3a5 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1385,7 +1385,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - ret = gst_aggregator_finish_buffer (agg, aagg->priv->current_buffer); + ret = gst_aggregator_finish_buffer (agg, outbuf); aagg->priv->current_buffer = NULL; GST_LOG_OBJECT (aagg, "pushed outbuf, result = %s", gst_flow_get_name (ret)); From 9c0a2a9ad486aab65112052fc4ccb44e82074e86 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 10 Sep 2017 20:55:07 +0200 Subject: [PATCH 134/153] tests: simplify audiomixer test Use _link_many() and reuse a helper to reduce the test code. --- tests/check/elements/audiomixer.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index f0f16f393c..4abddda386 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -570,7 +570,6 @@ GST_START_TEST (test_live_seeking) GstPad *srcpad; GstPad *sinkpad; gint i; - GstStateChangeReturn state_res; GstStreamConsistency *consist; /* don't use autoaudiosrc, as then we can't set anything here */ const gchar *audio_src_factories[] = { @@ -591,6 +590,7 @@ GST_START_TEST (test_live_seeking) } if (!src1) { /* normal audiosources behave differently than audiotestsrc */ + GST_WARNING ("no real audiosrc found, using audiotestsrc is-live"); src1 = gst_element_factory_make ("audiotestsrc", "src1"); g_object_set (src1, "wave", 4, "is-live", TRUE, NULL); /* silence */ } else { @@ -605,19 +605,11 @@ GST_START_TEST (test_live_seeking) sink = gst_element_factory_make ("fakesink", "sink"); gst_bin_add_many (GST_BIN (bin), src1, cf, audiomixer, sink, NULL); - res = gst_element_link (src1, cf); - fail_unless (res == TRUE, NULL); - res = gst_element_link (cf, audiomixer); - fail_unless (res == TRUE, NULL); - res = gst_element_link (audiomixer, sink); + res = gst_element_link_many (src1, cf, audiomixer, sink, NULL); fail_unless (res == TRUE, NULL); - gst_element_set_state (bin, GST_STATE_PLAYING); - /* wait for completion */ - state_res = - gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, - GST_CLOCK_TIME_NONE); - ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + /* get the caps for the livesrc, we'll reuse this for the non-live source */ + set_state_and_wait (bin, GST_STATE_PLAYING); sinkpad = gst_element_get_static_pad (sink, "sink"); fail_unless (sinkpad != NULL); From ceee4db9d0265ac8cf311ce2a04cbbf1ee1ca4ef Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 17 Sep 2017 21:05:03 -0700 Subject: [PATCH 135/153] audioaggregator: fix typo in comment --- gst-libs/gst/audio/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 2bcf11d3a5..20b486cd7a 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1264,7 +1264,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) g_assert (pad->priv->buffer); - /* This pad is lacking behind, we need to update the offset + /* This pad is lagging behind, we need to update the offset * and maybe drop the current buffer */ if (pad->priv->output_offset < aagg->priv->offset) { gint64 diff = aagg->priv->offset - pad->priv->output_offset; From 17848d7c0c7387a49e58abb994451d6ab38f8580 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 2 Oct 2017 09:40:50 +0200 Subject: [PATCH 136/153] audioaggregator: update comment for aggregate Replace collect-pads left-over. Remove first paragraph, we're not doing this. Remove 3), 4) since this is not per pad. --- gst-libs/gst/audio/gstaudioaggregator.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 20b486cd7a..b499d0ec1d 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1086,12 +1086,8 @@ sync_pad_values (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad) static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) { - /* Get all pads that have data for us and store them in a - * new list. - * - * Calculate the current output offset/timestamp and - * offset_end/timestamp_end. Allocate a silence buffer - * for this and store it. + /* Calculate the current output offset/timestamp and offset_end/timestamp_end. + * Allocate a silence buffer for this and store it. * * For all pads: * 1) Once per input buffer (cached) @@ -1108,11 +1104,11 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) * buffer and advance the pad's position. Remember if this pad needs * a new buffer to advance behind the output offset_end. * - * 3) If we had no pad with a buffer, go EOS. + * If we had no pad with a buffer, go EOS. * - * 4) If we had at least one pad that did not advance behind output - * offset_end, let collected be called again for the current - * output offset/offset_end. + * If we had at least one pad that did not advance behind output + * offset_end, let aggregate be called again for the current + * output offset/offset_end. */ GstElement *element; GstAudioAggregator *aagg; From 5fd4e71da3d4c1aa086c11b9c3c171b4ab3cb101 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 2 Oct 2017 16:57:21 +0200 Subject: [PATCH 137/153] audioaggregator: reduce variable scope This is a non-functional change that makes the code more alike to the previous check. I should be more obvious when we drop a buffer. --- gst-libs/gst/audio/gstaudioaggregator.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index b499d0ec1d..7d91f48368 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1212,7 +1212,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GstBuffer *inbuf; GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) iter->data; GstAggregatorPad *aggpad = (GstAggregatorPad *) iter->data; - gboolean drop_buf = FALSE; gboolean pad_eos = gst_aggregator_pad_is_eos (aggpad); if (!pad_eos) @@ -1289,6 +1288,8 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (pad->priv->output_offset >= aagg->priv->offset && pad->priv->output_offset < aagg->priv->offset + blocksize && pad->priv->buffer) { + gboolean drop_buf = FALSE; + GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer, outbuf); @@ -1299,12 +1300,14 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) } else { is_done = FALSE; } + if (drop_buf) { + GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); + continue; + } } GST_OBJECT_UNLOCK (pad); - if (drop_buf) - gst_aggregator_pad_drop_buffer (aggpad); - } GST_OBJECT_UNLOCK (agg); From 926fed92e992dbf5c8ed57cae45221d6eef04adf Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 5 Oct 2017 08:12:45 +0200 Subject: [PATCH 138/153] audioaggregator: rename _fill_buffer() to _queue_new_buffer() It does not fill a buffer. Rename it and add a short comment. --- gst-libs/gst/audio/gstaudioaggregator.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 7d91f48368..202474b845 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -763,9 +763,12 @@ gst_audio_aggregator_do_clip (GstAggregator * agg, /* Called with the object lock for both the element and pad held, * as well as the aagg lock + * + * Replace the current buffer with input and update GstAudioAggregatorPadPrivate + * values. */ static gboolean -gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, +gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstBuffer * inbuf) { GstClockTime start_time, end_time; @@ -1241,7 +1244,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) /* New buffer? */ if (!pad->priv->buffer) { /* Takes ownership of buffer */ - if (!gst_audio_aggregator_fill_buffer (aagg, pad, inbuf)) { + if (!gst_audio_aggregator_queue_new_buffer (aagg, pad, inbuf)) { dropped = TRUE; GST_OBJECT_UNLOCK (pad); gst_aggregator_pad_drop_buffer (aggpad); From 6ecfd599a5d0367ea2d6ff3ccd5cf405a964729d Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 5 Oct 2017 08:11:29 +0200 Subject: [PATCH 139/153] audioaggregator: pass blocksize to mix_buffer() No need to recalc the value twice per run. Establishes that it is the same value. --- gst-libs/gst/audio/gstaudioaggregator.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 202474b845..c7f7583348 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -973,19 +973,15 @@ done: static gboolean gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, - GstAudioAggregatorPad * pad, GstBuffer * inbuf, GstBuffer * outbuf) + GstAudioAggregatorPad * pad, GstBuffer * inbuf, GstBuffer * outbuf, + guint blocksize) { guint overlap; guint out_start; gboolean filled; - guint blocksize; guint in_offset; gboolean pad_changed = FALSE; - blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, - GST_AUDIO_INFO_RATE (&aagg->info), GST_SECOND); - blocksize = MAX (1, blocksize); - /* Overlap => mix */ if (aagg->priv->offset < pad->priv->output_offset) out_start = pad->priv->output_offset - aagg->priv->offset; @@ -1295,7 +1291,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer, - outbuf); + outbuf, blocksize); if (pad->priv->output_offset >= next_offset) { GST_LOG_OBJECT (pad, "Pad is at or after current offset: %" G_GUINT64_FORMAT " >= %" From f46d80f07dce7033eca5efa42276a590b047eb6e Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 5 Oct 2017 17:52:37 +0200 Subject: [PATCH 140/153] audioaggreator: update docs Remove wrote references to collectpads. Document the units. --- gst-libs/gst/audio/gstaudioaggregator.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index c7f7583348..fab852de53 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -47,18 +47,20 @@ struct _GstAudioAggregatorPadPrivate { /* All members are protected by the pad object lock */ - GstBuffer *buffer; /* current buffer we're mixing, - for comparison with collect.buffer - to see if we need to update our + GstBuffer *buffer; /* current input buffer we're mixing, for + comparison with a new input buffer from + aggregator to see if we need to update our cached values. */ - guint position, size; + + guint position, size; /* position in the input buffer and size of the + input buffer in number of samples */ guint64 output_offset; /* Sample offset in output segment relative to - segment.start that collect.pos refers to in the - current buffer. */ + pad.segment.start that position refers to + in the current buffer. */ - guint64 next_offset; /* Next expected sample offset in the input segment - relative to segment.start */ + guint64 next_offset; /* Next expected sample offset relative to + pad.segment.start */ /* Last time we noticed a discont */ GstClockTime discont_time; @@ -151,13 +153,15 @@ struct _GstAudioAggregatorPrivate GstClockTime discont_wait; /* Protected by srcpad stream clock */ - /* Buffer starting at offset containing block_size frames */ + /* Output buffer starting at offset containing blocksize frames (calculated + * from output_buffer_duration) */ GstBuffer *current_buffer; /* counters to keep track of timestamps */ /* Readable with object lock, writable with both aag lock and object lock */ - gint64 offset; /* Sample offset starting from 0 at segment.start */ + /* Sample offset starting from 0 at aggregator.segment.start */ + gint64 offset; }; #define GST_AUDIO_AGGREGATOR_LOCK(self) g_mutex_lock (&(self)->priv->mutex); From 1b842833967ac0c362baad6672f7425013c8c9d3 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 5 Oct 2017 17:54:34 +0200 Subject: [PATCH 141/153] audioaggregator: remove buffer!=NULL check Acording to the logic this cannot happen (we already check this before). So add a assert like we do above and remove the check. This make it clearer that we check for the offset range. Also remove a dead assignment since we reassign this a few lines below. --- gst-libs/gst/audio/gstaudioaggregator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index fab852de53..3447d0895d 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1287,11 +1287,11 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) } } + g_assert (pad->priv->buffer); if (pad->priv->output_offset >= aagg->priv->offset - && pad->priv->output_offset < - aagg->priv->offset + blocksize && pad->priv->buffer) { - gboolean drop_buf = FALSE; + && pad->priv->output_offset < aagg->priv->offset + blocksize) { + gboolean drop_buf; GST_LOG_OBJECT (aggpad, "Mixing buffer for current offset"); drop_buf = !gst_audio_aggregator_mix_buffer (aagg, pad, pad->priv->buffer, From bd342431779850a2c423bf3ee948ea18b299dbf0 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Thu, 5 Oct 2017 20:48:59 +0200 Subject: [PATCH 142/153] audioaggregator: move comment to the place it is meant to be This probably got shifted after some changes. --- gst-libs/gst/audio/gstaudioaggregator.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 3447d0895d..d126bb1835 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1181,9 +1181,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) rate, GST_SECOND); blocksize = MAX (1, blocksize); - /* for the next timestamp, use the sample counter, which will - * never accumulate rounding errors */ - /* FIXME: Reverse mixing does not work at all yet */ if (agg->segment.rate > 0.0) { next_offset = aagg->priv->offset + blocksize; @@ -1191,6 +1188,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) next_offset = aagg->priv->offset - blocksize; } + /* Use the sample counter, which will never accumulate rounding errors */ next_timestamp = agg->segment.start + gst_util_uint64_scale (next_offset, GST_SECOND, rate); From 023170e2f8428864b4ab7b03e6d61ec58239447d Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 15 Oct 2017 10:29:20 +0200 Subject: [PATCH 143/153] audioaggregator: improve readability in offset calculation Don't reuse the offset variables will contain a sample offset for an intermediate time value. Instead add a segment_pos variable of type GstClockTime for this. Use The clock-time macros to check if we got a valid time. --- gst-libs/gst/audio/gstaudioaggregator.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index d126bb1835..24b71b78df 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -866,8 +866,9 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, if (pad->priv->output_offset == -1) { GstClockTime start_running_time; GstClockTime end_running_time; - guint64 start_output_offset; - guint64 end_output_offset; + GstClockTime segment_pos; + guint64 start_output_offset = -1; + guint64 end_output_offset = -1; start_running_time = gst_segment_to_running_time (&aggpad->segment, @@ -877,20 +878,20 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, GST_FORMAT_TIME, end_time); /* Convert to position in the output segment */ - start_output_offset = + segment_pos = gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, start_running_time); - if (start_output_offset != -1) + if (GST_CLOCK_TIME_IS_VALID (segment_pos)) start_output_offset = - gst_util_uint64_scale (start_output_offset - agg->segment.start, rate, + gst_util_uint64_scale (segment_pos - agg->segment.start, rate, GST_SECOND); - end_output_offset = + segment_pos = gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, end_running_time); - if (end_output_offset != -1) + if (GST_CLOCK_TIME_IS_VALID (segment_pos)) end_output_offset = - gst_util_uint64_scale (end_output_offset - agg->segment.start, rate, + gst_util_uint64_scale (segment_pos - agg->segment.start, rate, GST_SECOND); if (start_output_offset == -1 && end_output_offset == -1) { From c2b462837b235c798e34bcd643642ed11c12dd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 13 Jul 2017 19:09:43 -0400 Subject: [PATCH 144/153] audioaggregator: Accept buffer with no data, but duration and gap flag These are produced from GAP events by the base class. https://bugzilla.gnome.org/show_bug.cgi?id=784846 --- gst-libs/gst/audio/gstaudioaggregator.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 24b71b78df..8e0d48544a 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -791,6 +791,18 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, pad->priv->position = 0; pad->priv->size = gst_buffer_get_size (inbuf) / bpf; + if (pad->priv->size == 0) { + if (!GST_BUFFER_DURATION_IS_VALID (inbuf) || + !GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + GST_WARNING_OBJECT (pad, "Dropping 0-sized buffer missing either a" + " duration or a GAP flag: %" GST_PTR_FORMAT, inbuf); + return FALSE; + } + + pad->priv->size = gst_util_uint64_scale (GST_BUFFER_DURATION (inbuf), rate, + GST_SECOND); + } + if (!GST_BUFFER_PTS_IS_VALID (inbuf)) { if (pad->priv->output_offset == -1) pad->priv->output_offset = aagg->priv->offset; From 8f4479b874507314fba835973c49abcbc24e4306 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Sun, 22 Oct 2017 19:43:17 +0200 Subject: [PATCH 145/153] aggregator: fix type for latency property (int64 -> GStClockTime) The value is used as GstClockTiem in the code. Adapt the hack^H^H^H^Hcode in live-adder. --- gst/audiomixer/gstaudiomixer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 66ca3ec2e4..faae1252fd 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -837,9 +837,9 @@ gst_live_adder_set_property (GObject * object, guint prop_id, GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type); GValue v = { 0 }; - g_value_init (&v, G_TYPE_INT64); + g_value_init (&v, G_TYPE_UINT64); - g_value_set_int64 (&v, g_value_get_uint (value) * GST_MSECOND); + g_value_set_uint64 (&v, g_value_get_uint (value) * GST_MSECOND); G_OBJECT_CLASS (pspec_class)->set_property (object, parent_spec->param_id, &v, parent_spec); @@ -864,12 +864,12 @@ gst_live_adder_get_property (GObject * object, guint prop_id, GValue * value, GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type); GValue v = { 0 }; - g_value_init (&v, G_TYPE_INT64); + g_value_init (&v, G_TYPE_UINT64); G_OBJECT_CLASS (pspec_class)->get_property (object, parent_spec->param_id, &v, parent_spec); - g_value_set_uint (value, g_value_get_int64 (&v) / GST_MSECOND); + g_value_set_uint (value, g_value_get_uint64 (&v) / GST_MSECOND); break; } default: From 73381c84750742cce78db54549ef8ef101984f70 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 23 Oct 2017 11:52:38 +0200 Subject: [PATCH 146/153] tests: comment and logging cleanups for audiomixer and aggregator Remove some references to 'collectpads'. Logs pads through the object variants. Add some more comments. Remove a left over comment. --- tests/check/elements/audiomixer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 4abddda386..4d04093a5d 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -1596,6 +1596,7 @@ GST_START_TEST (test_segment_base_handling) sink = gst_element_factory_make ("appsink", "sink"); g_object_set (sink, "caps", caps, "sync", FALSE, NULL); gst_caps_unref (caps); + /* 50 buffers of 1/10 sec = 5 sec */ src1 = gst_element_factory_make ("audiotestsrc", "src1"); g_object_set (src1, "samplesperbuffer", 4410, "num-buffers", 50, NULL); src2 = gst_element_factory_make ("audiotestsrc", "src2"); @@ -1612,6 +1613,7 @@ GST_START_TEST (test_segment_base_handling) srcpad = gst_element_get_static_pad (src2, "src"); sinkpad = gst_element_get_request_pad (mix, "sink_2"); fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + /* set a pad offset of another 5 seconds */ gst_pad_set_offset (sinkpad, 5 * GST_SECOND); gst_object_unref (sinkpad); gst_object_unref (srcpad); From 5d3795163b523a311f48624a10125546f4dddd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 23 Oct 2017 22:03:22 +0200 Subject: [PATCH 147/153] audioaggregator: make class padding larger --- gst-libs/gst/audio/gstaudioaggregator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h index 7f910bf9e0..41ce18b1c9 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.h +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -90,7 +90,7 @@ struct _GstAudioAggregatorPadClass GstAggregatorPadClass parent_class; /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING_LARGE]; }; GST_EXPORT @@ -148,7 +148,7 @@ struct _GstAudioAggregatorClass { GstBuffer * outbuf, guint out_offset, guint num_frames); /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING_LARGE]; }; /************************* From fc946277785cf39879cc26db9413bd87fe3c6cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 2 Nov 2017 12:46:26 +0000 Subject: [PATCH 148/153] audioaggregator: use new gst_element_foreach_sink_pad() Instead of gst_aggregator_iterate_sinkpads() which will soon be removed. https://bugzilla.gnome.org/show_bug.cgi?id=785679 --- gst-libs/gst/audio/gstaudioaggregator.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 8e0d48544a..f52a413243 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -189,8 +189,7 @@ static GstBuffer *gst_audio_aggregator_do_clip (GstAggregator * agg, GstAggregatorPad * bpad, GstBuffer * buffer); static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout); -static gboolean sync_pad_values (GstAudioAggregator * aagg, - GstAudioAggregatorPad * pad); +static gboolean sync_pad_values (GstElement * aagg, GstPad * pad, gpointer ud); static gboolean gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps); @@ -260,8 +259,6 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) klass->create_output_buffer = gst_audio_aggregator_create_output_buffer; - GST_DEBUG_REGISTER_FUNCPTR (sync_pad_values); - GST_DEBUG_CATEGORY_INIT (audio_aggregator_debug, "audioaggregator", GST_DEBUG_FG_MAGENTA, "GstAudioAggregator"); @@ -1077,15 +1074,16 @@ gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, } static gboolean -sync_pad_values (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad) +sync_pad_values (GstElement * aagg, GstPad * pad, gpointer user_data) { - GstAggregatorPad *bpad = GST_AGGREGATOR_PAD (pad); + GstAudioAggregatorPad *aapad = GST_AUDIO_AGGREGATOR_PAD (pad); + GstAggregatorPad *bpad = GST_AGGREGATOR_PAD_CAST (pad); GstClockTime timestamp, stream_time; - if (pad->priv->buffer == NULL) + if (aapad->priv->buffer == NULL) return TRUE; - timestamp = GST_BUFFER_PTS (pad->priv->buffer); + timestamp = GST_BUFFER_PTS (aapad->priv->buffer); GST_OBJECT_LOCK (bpad); stream_time = gst_segment_to_stream_time (&bpad->segment, GST_FORMAT_TIME, timestamp); @@ -1094,7 +1092,7 @@ sync_pad_values (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad) /* sync object properties on stream time */ /* TODO: Ideally we would want to do that on every sample */ if (GST_CLOCK_TIME_IS_VALID (stream_time)) - gst_object_sync_values (GST_OBJECT (pad), stream_time); + gst_object_sync_values (GST_OBJECT_CAST (pad), stream_time); return TRUE; } @@ -1143,8 +1141,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) aagg = GST_AUDIO_AGGREGATOR (agg); /* Sync pad properties to the stream time */ - gst_aggregator_iterate_sinkpads (agg, - (GstAggregatorPadForeachFunc) sync_pad_values, NULL); + gst_element_foreach_sink_pad (element, sync_pad_values, NULL); GST_AUDIO_AGGREGATOR_LOCK (aagg); GST_OBJECT_LOCK (agg); From 9e4b3fbd827977e04ea19dbbe0c0051771ad4c09 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Mon, 6 Nov 2017 21:07:51 +0100 Subject: [PATCH 149/153] aggregator: Remove klass->sinkpads_type This posed problems for the python bindings (and possibly others). Instead, subclasses now use add_pad_template_with_gtype. https://bugzilla.gnome.org/show_bug.cgi?id=789986 --- gst/audiomixer/gstaudiointerleave.c | 7 ++----- gst/audiomixer/gstaudiomixer.c | 6 ++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index fd58e789c6..6c7efdd2c4 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -562,8 +562,8 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) gst_element_class_add_static_pad_template (gstelement_class, &gst_audio_interleave_src_template); - gst_element_class_add_static_pad_template (gstelement_class, - &gst_audio_interleave_sink_template); + gst_element_class_add_static_pad_template_with_gtype (gstelement_class, + &gst_audio_interleave_sink_template, GST_TYPE_AUDIO_INTERLEAVE_PAD); gst_element_class_set_static_metadata (gstelement_class, "AudioInterleave", "Generic/Audio", "Mixes multiple audio streams", "Olivier Crete "); @@ -573,9 +573,6 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) 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; diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index faae1252fd..02737b1f02 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -502,8 +502,8 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) gst_element_class_add_static_pad_template (gstelement_class, &gst_audiomixer_src_template); - gst_element_class_add_static_pad_template (gstelement_class, - &gst_audiomixer_sink_template); + gst_element_class_add_static_pad_template_with_gtype (gstelement_class, + &gst_audiomixer_sink_template, GST_TYPE_AUDIO_MIXER_PAD); gst_element_class_set_static_metadata (gstelement_class, "AudioMixer", "Generic/Audio", "Mixes multiple audio streams", "Sebastian Dröge "); @@ -513,8 +513,6 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); - agg_class->sinkpads_type = GST_TYPE_AUDIO_MIXER_PAD; - agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); agg_class->update_src_caps = From 6c0744a5fc742c13a76807119e95d8c24989b42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 2 Dec 2017 16:01:25 +0000 Subject: [PATCH 150/153] Remove GstAggregator from -bad, moved to core https://bugzilla.gnome.org/show_bug.cgi?id=739010 --- gst/audiomixer/Makefile.am | 1 - gst/audiomixer/meson.build | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am index 72dc081cd0..f1a4d73953 100644 --- a/gst/audiomixer/Makefile.am +++ b/gst/audiomixer/Makefile.am @@ -13,7 +13,6 @@ libgstaudiomixer_la_CFLAGS = \ $(GST_CFLAGS) $(ORC_CFLAGS) libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstaudiomixer_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/base/libgstbadbase-$(GST_API_VERSION).la \ $(top_builddir)/gst-libs/gst/audio/libgstbadaudio-$(GST_API_VERSION).la \ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \ $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) diff --git a/gst/audiomixer/meson.build b/gst/audiomixer/meson.build index e635ee62d7..ccfe1b9d37 100644 --- a/gst/audiomixer/meson.build +++ b/gst/audiomixer/meson.build @@ -26,8 +26,7 @@ gstaudiomixer = library('gstaudiomixer', audiomixer_sources, orc_c, orc_h, c_args : gst_plugins_bad_args + [ '-DGST_USE_UNSTABLE_API' ], include_directories : [configinc], - dependencies : [gstbadaudio_dep, gstbadbase_dep, gstaudio_dep, gstbase_dep, - orc_dep], + dependencies : [gstbadaudio_dep, gstaudio_dep, gstbase_dep, orc_dep], install : true, install_dir : plugins_install_dir, ) From 164b5a7f94e9056d112ee18fefa0faac34a66791 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Sat, 22 Jul 2017 20:32:20 +0200 Subject: [PATCH 151/153] audioaggregator: implement input conversion https://bugzilla.gnome.org/show_bug.cgi?id=786344 --- gst-libs/gst/audio/gstaudioaggregator.c | 690 +++++++++++++++++++++--- gst-libs/gst/audio/gstaudioaggregator.h | 64 ++- gst/audiomixer/gstaudiointerleave.c | 2 +- gst/audiomixer/gstaudiomixer.c | 386 +------------ gst/audiomixer/gstaudiomixer.h | 7 +- tests/check/elements/audiomixer.c | 155 +++++- 6 files changed, 857 insertions(+), 447 deletions(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index f52a413243..8772ae86fc 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -29,6 +29,38 @@ * aggregating their buffers for raw audio * @see_also: #GstAggregator * + * #GstAudioAggregator will perform conversion on the data arriving + * on its sink pads, based on the format expected downstream. + * + * Subclasses can opt out of the conversion behaviour by setting + * #GstAudioAggregator.convert_buffer() to %NULL. + * + * Subclasses that wish to use the default conversion implementation + * should use a (subclass of) #GstAudioAggregatorConvertPad as their + * #GstAggregatorClass.sinkpads_type, as it will cache the created + * #GstAudioConverter and install a property allowing to configure it, + * #GstAudioAggregatorPadClass:converter-config. + * + * Subclasses that wish to perform custom conversion should override + * #GstAudioAggregator.convert_buffer(). + * + * When conversion is enabled, #GstAudioAggregator will accept + * any type of raw audio caps and perform conversion + * on the data arriving on its sink pads, with whatever downstream + * expects as the target format. + * + * In case downstream caps are not fully fixated, it will use + * the first configured sink pad to finish fixating its source pad + * caps. + * + * Additionally, handling audio conversion directly in the element + * means that this base class supports safely reconfiguring its + * source pad. + * + * A notable exception for now is the sample rate, sink pads must + * have the same sample rate as either the downstream requirement, + * or the first configured pad, or a combination of both (when + * downstream specifies a range or a set of acceptable rates). */ @@ -47,7 +79,7 @@ struct _GstAudioAggregatorPadPrivate { /* All members are protected by the pad object lock */ - GstBuffer *buffer; /* current input buffer we're mixing, for + GstBuffer *buffer; /* current buffer we're mixing, for comparison with a new input buffer from aggregator to see if we need to update our cached values. */ @@ -55,6 +87,8 @@ struct _GstAudioAggregatorPadPrivate guint position, size; /* position in the input buffer and size of the input buffer in number of samples */ + GstBuffer *input_buffer; + guint64 output_offset; /* Sample offset in output segment relative to pad.segment.start that position refers to in the current buffer. */ @@ -76,6 +110,12 @@ struct _GstAudioAggregatorPadPrivate G_DEFINE_TYPE (GstAudioAggregatorPad, gst_audio_aggregator_pad, GST_TYPE_AGGREGATOR_PAD); +enum +{ + PROP_PAD_0, + PROP_PAD_CONVERTER_CONFIG, +}; + static GstFlowReturn gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator); @@ -86,6 +126,7 @@ gst_audio_aggregator_pad_finalize (GObject * object) GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) object; gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); G_OBJECT_CLASS (gst_audio_aggregator_pad_parent_class)->finalize (object); } @@ -112,6 +153,7 @@ gst_audio_aggregator_pad_init (GstAudioAggregatorPad * pad) gst_audio_info_init (&pad->info); pad->priv->buffer = NULL; + pad->priv->input_buffer = NULL; pad->priv->position = 0; pad->priv->size = 0; pad->priv->output_offset = -1; @@ -131,13 +173,182 @@ gst_audio_aggregator_pad_flush_pad (GstAggregatorPad * aggpad, pad->priv->output_offset = pad->priv->next_offset = -1; pad->priv->discont_time = GST_CLOCK_TIME_NONE; gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); GST_OBJECT_UNLOCK (aggpad); return GST_FLOW_OK; } +struct _GstAudioAggregatorConvertPadPrivate +{ + /* All members are protected by the pad object lock */ + GstAudioConverter *converter; + GstStructure *converter_config; + gboolean converter_config_changed; +}; +G_DEFINE_TYPE (GstAudioAggregatorConvertPad, gst_audio_aggregator_convert_pad, + GST_TYPE_AUDIO_AGGREGATOR_PAD); + +static void +gst_audio_aggregator_convert_pad_update_converter (GstAudioAggregatorConvertPad + * aaggcpad, GstAudioInfo * in_info, GstAudioInfo * out_info) +{ + if (!aaggcpad->priv->converter_config_changed) + return; + + if (aaggcpad->priv->converter) { + gst_audio_converter_free (aaggcpad->priv->converter); + aaggcpad->priv->converter = NULL; + } + + if (gst_audio_info_is_equal (in_info, out_info) || + in_info->finfo->format == GST_AUDIO_FORMAT_UNKNOWN) { + if (aaggcpad->priv->converter) { + gst_audio_converter_free (aaggcpad->priv->converter); + aaggcpad->priv->converter = NULL; + } + } else { + /* If we haven't received caps yet, this pad should not have + * a buffer to convert anyway */ + aaggcpad->priv->converter = + gst_audio_converter_new (GST_AUDIO_CONVERTER_FLAG_NONE, + in_info, out_info, + aaggcpad->priv->converter_config ? gst_structure_copy (aaggcpad-> + priv->converter_config) : NULL); + } + + aaggcpad->priv->converter_config_changed = FALSE; +} + +static GstBuffer * +gst_audio_aggregator_convert_pad_convert_buffer (GstAudioAggregatorConvertPad * + aaggcpad, GstAudioInfo * in_info, GstAudioInfo * out_info, + GstBuffer * input_buffer) +{ + GstBuffer *res; + + gst_audio_aggregator_convert_pad_update_converter (aaggcpad, in_info, + out_info); + + if (aaggcpad->priv->converter) { + gint insize = gst_buffer_get_size (input_buffer); + gsize insamples = insize / in_info->bpf; + gsize outsamples = + gst_audio_converter_get_out_frames (aaggcpad->priv->converter, + insamples); + gint outsize = outsamples * out_info->bpf; + GstMapInfo inmap, outmap; + + res = gst_buffer_new_allocate (NULL, outsize, NULL); + + /* We create a perfectly similar buffer, except obviously for + * its converted contents */ + gst_buffer_copy_into (res, input_buffer, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_META, 0, -1); + + gst_buffer_map (input_buffer, &inmap, GST_MAP_READ); + gst_buffer_map (res, &outmap, GST_MAP_WRITE); + + gst_audio_converter_samples (aaggcpad->priv->converter, + GST_AUDIO_CONVERTER_FLAG_NONE, + (gpointer *) & inmap.data, insamples, + (gpointer *) & outmap.data, outsamples); + + gst_buffer_unmap (input_buffer, &inmap); + gst_buffer_unmap (res, &outmap); + } else { + res = gst_buffer_ref (input_buffer); + } + + return res; +} + +static void +gst_audio_aggregator_convert_pad_finalize (GObject * object) +{ + GstAudioAggregatorConvertPad *pad = (GstAudioAggregatorConvertPad *) object; + + if (pad->priv->converter) + gst_audio_converter_free (pad->priv->converter); + + if (pad->priv->converter_config) + gst_structure_free (pad->priv->converter_config); + + G_OBJECT_CLASS (gst_audio_aggregator_convert_pad_parent_class)->finalize + (object); +} + +static void +gst_audio_aggregator_convert_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioAggregatorConvertPad *pad = GST_AUDIO_AGGREGATOR_CONVERT_PAD (object); + + switch (prop_id) { + case PROP_PAD_CONVERTER_CONFIG: + GST_OBJECT_LOCK (pad); + if (pad->priv->converter_config) + g_value_set_boxed (value, pad->priv->converter_config); + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_aggregator_convert_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioAggregatorConvertPad *pad = GST_AUDIO_AGGREGATOR_CONVERT_PAD (object); + + switch (prop_id) { + case PROP_PAD_CONVERTER_CONFIG: + GST_OBJECT_LOCK (pad); + if (pad->priv->converter_config) + gst_structure_free (pad->priv->converter_config); + pad->priv->converter_config = g_value_dup_boxed (value); + pad->priv->converter_config_changed = TRUE; + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_aggregator_convert_pad_class_init (GstAudioAggregatorConvertPadClass * + klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + g_type_class_add_private (klass, + sizeof (GstAudioAggregatorConvertPadPrivate)); + + gobject_class->set_property = gst_audio_aggregator_convert_pad_set_property; + gobject_class->get_property = gst_audio_aggregator_convert_pad_get_property; + + g_object_class_install_property (gobject_class, PROP_PAD_CONVERTER_CONFIG, + g_param_spec_boxed ("converter-config", "Converter configuration", + "A GstStructure describing the configuration that should be used " + "when converting this pad's audio buffers", + GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gobject_class->finalize = gst_audio_aggregator_convert_pad_finalize; +} + +static void +gst_audio_aggregator_convert_pad_init (GstAudioAggregatorConvertPad * pad) +{ + pad->priv = + G_TYPE_INSTANCE_GET_PRIVATE (pad, GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD, + GstAudioAggregatorConvertPadPrivate); +} + /************************************** * GstAudioAggregator implementation * **************************************/ @@ -179,6 +390,9 @@ static gboolean gst_audio_aggregator_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, GstEvent * event); static gboolean gst_audio_aggregator_src_query (GstAggregator * agg, GstQuery * query); +static gboolean +gst_audio_aggregator_sink_query (GstAggregator * agg, GstAggregatorPad * aggpad, + GstQuery * query); static gboolean gst_audio_aggregator_start (GstAggregator * agg); static gboolean gst_audio_aggregator_stop (GstAggregator * agg); static GstFlowReturn gst_audio_aggregator_flush (GstAggregator * agg); @@ -192,6 +406,11 @@ static GstFlowReturn gst_audio_aggregator_aggregate (GstAggregator * agg, static gboolean sync_pad_values (GstElement * aagg, GstPad * pad, gpointer ud); static gboolean gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps); +static GstFlowReturn +gst_audio_aggregator_update_src_caps (GstAggregator * agg, + GstCaps * caps, GstCaps ** ret); +static GstCaps *gst_audio_aggregator_fixate_src_caps (GstAggregator * agg, + GstCaps * caps); #define DEFAULT_OUTPUT_BUFFER_DURATION (10 * GST_MSECOND) #define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) @@ -229,6 +448,66 @@ gst_audio_aggregator_get_next_time (GstAggregator * agg) return next_time; } +static GstBuffer * +gst_audio_aggregator_convert_once (GstAudioAggregator * aagg, GstPad * pad, + GstAudioInfo * in_info, GstAudioInfo * out_info, GstBuffer * buffer) +{ + GstAudioConverter *converter = + gst_audio_converter_new (GST_AUDIO_CONVERTER_FLAG_NONE, + in_info, out_info, NULL); + gint insize = gst_buffer_get_size (buffer); + gsize insamples = insize / in_info->bpf; + gsize outsamples = gst_audio_converter_get_out_frames (converter, + insamples); + gint outsize = outsamples * out_info->bpf; + GstMapInfo inmap, outmap; + GstBuffer *converted = gst_buffer_new_allocate (NULL, outsize, NULL); + + gst_buffer_copy_into (converted, buffer, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_META, 0, -1); + + gst_buffer_map (buffer, &inmap, GST_MAP_READ); + gst_buffer_map (converted, &outmap, GST_MAP_WRITE); + + gst_audio_converter_samples (converter, + GST_AUDIO_CONVERTER_FLAG_NONE, + (gpointer *) & inmap.data, insamples, + (gpointer *) & outmap.data, outsamples); + + gst_buffer_unmap (buffer, &inmap); + gst_buffer_unmap (converted, &outmap); + gst_audio_converter_free (converter); + + return converted; +} + +static GstBuffer * +gst_audio_aggregator_default_convert_buffer (GstAudioAggregator * aagg, + GstPad * pad, GstAudioInfo * in_info, GstAudioInfo * out_info, + GstBuffer * buffer) +{ + if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (pad)) + return + gst_audio_aggregator_convert_pad_convert_buffer + (GST_AUDIO_AGGREGATOR_CONVERT_PAD (pad), + &GST_AUDIO_AGGREGATOR_PAD (pad)->info, out_info, buffer); + else + return gst_audio_aggregator_convert_once (aagg, pad, in_info, out_info, + buffer); +} + +static GstBuffer * +gst_audio_aggregator_convert_buffer (GstAudioAggregator * aagg, GstPad * pad, + GstAudioInfo * in_info, GstAudioInfo * out_info, GstBuffer * buffer) +{ + GstAudioAggregatorClass *klass = GST_AUDIO_AGGREGATOR_GET_CLASS (aagg); + + g_assert (klass->convert_buffer); + + return klass->convert_buffer (aagg, pad, in_info, out_info, buffer); +} + static void gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) { @@ -247,6 +526,7 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) GST_DEBUG_FUNCPTR (gst_audio_aggregator_sink_event); gstaggregator_class->src_query = GST_DEBUG_FUNCPTR (gst_audio_aggregator_src_query); + gstaggregator_class->sink_query = gst_audio_aggregator_sink_query; gstaggregator_class->start = gst_audio_aggregator_start; gstaggregator_class->stop = gst_audio_aggregator_stop; gstaggregator_class->flush = gst_audio_aggregator_flush; @@ -254,10 +534,14 @@ gst_audio_aggregator_class_init (GstAudioAggregatorClass * klass) GST_DEBUG_FUNCPTR (gst_audio_aggregator_aggregate); gstaggregator_class->clip = GST_DEBUG_FUNCPTR (gst_audio_aggregator_do_clip); gstaggregator_class->get_next_time = gst_audio_aggregator_get_next_time; + gstaggregator_class->update_src_caps = + GST_DEBUG_FUNCPTR (gst_audio_aggregator_update_src_caps); + gstaggregator_class->fixate_src_caps = gst_audio_aggregator_fixate_src_caps; gstaggregator_class->negotiated_src_caps = gst_audio_aggregator_negotiated_src_caps; klass->create_output_buffer = gst_audio_aggregator_create_output_buffer; + klass->convert_buffer = gst_audio_aggregator_default_convert_buffer; GST_DEBUG_CATEGORY_INIT (audio_aggregator_debug, "audioaggregator", GST_DEBUG_FG_MAGENTA, "GstAudioAggregator"); @@ -361,6 +645,263 @@ gst_audio_aggregator_get_property (GObject * object, guint prop_id, } } +/* Caps negotiation */ + +/* Unref after usage */ +static GstAudioAggregatorPad * +gst_audio_aggregator_get_first_configured_pad (GstAggregator * agg) +{ + GstAudioAggregatorPad *res = NULL; + GList *l; + + GST_OBJECT_LOCK (agg); + for (l = GST_ELEMENT (agg)->sinkpads; l; l = l->next) { + GstAudioAggregatorPad *aaggpad = l->data; + + if (GST_AUDIO_INFO_FORMAT (&aaggpad->info) != GST_AUDIO_FORMAT_UNKNOWN) { + res = gst_object_ref (aaggpad); + break; + } + } + GST_OBJECT_UNLOCK (agg); + + return res; +} + +static GstCaps * +gst_audio_aggregator_sink_getcaps (GstPad * pad, GstAggregator * agg, + GstCaps * filter) +{ + GstAudioAggregatorPad *first_configured_pad = + gst_audio_aggregator_get_first_configured_pad (agg); + GstCaps *sink_template_caps = gst_pad_get_pad_template_caps (pad); + GstCaps *downstream_caps = gst_pad_get_allowed_caps (agg->srcpad); + GstCaps *sink_caps; + GstStructure *s, *s2; + gint downstream_rate; + + sink_template_caps = gst_caps_make_writable (sink_template_caps); + s = gst_caps_get_structure (sink_template_caps, 0); + + if (downstream_caps && !gst_caps_is_empty (downstream_caps)) + s2 = gst_caps_get_structure (downstream_caps, 0); + else + s2 = NULL; + + if (s2 && gst_structure_get_int (s2, "rate", &downstream_rate)) { + gst_structure_fixate_field_nearest_int (s, "rate", downstream_rate); + } else if (first_configured_pad) { + gst_structure_fixate_field_nearest_int (s, "rate", + first_configured_pad->info.rate); + } + + if (first_configured_pad) + gst_object_unref (first_configured_pad); + + sink_caps = filter ? gst_caps_intersect (sink_template_caps, + filter) : gst_caps_ref (sink_template_caps); + + GST_INFO_OBJECT (pad, "Getting caps with filter %" GST_PTR_FORMAT, filter); + GST_DEBUG_OBJECT (pad, "sink template caps : %" GST_PTR_FORMAT, + sink_template_caps); + GST_DEBUG_OBJECT (pad, "downstream caps %" GST_PTR_FORMAT, downstream_caps); + GST_INFO_OBJECT (pad, "returned sink caps : %" GST_PTR_FORMAT, sink_caps); + + gst_caps_unref (sink_template_caps); + + if (downstream_caps) + gst_caps_unref (downstream_caps); + + return sink_caps; +} + +static gboolean +gst_audio_aggregator_sink_setcaps (GstAudioAggregatorPad * aaggpad, + GstAggregator * agg, GstCaps * caps) +{ + GstAudioAggregatorPad *first_configured_pad = + gst_audio_aggregator_get_first_configured_pad (agg); + GstCaps *downstream_caps = gst_pad_get_allowed_caps (agg->srcpad); + GstAudioInfo info; + gboolean ret = TRUE; + gint downstream_rate; + GstStructure *s; + + if (!downstream_caps || gst_caps_is_empty (downstream_caps)) { + ret = FALSE; + goto done; + } + + gst_audio_info_from_caps (&info, caps); + s = gst_caps_get_structure (downstream_caps, 0); + + /* TODO: handle different rates on sinkpads, a bit complex + * because offsets will have to be updated, and audio resampling + * has a latency to take into account + */ + if ((gst_structure_get_int (s, "rate", &downstream_rate) + && info.rate != downstream_rate) || (first_configured_pad + && info.rate != first_configured_pad->info.rate)) { + gst_pad_push_event (GST_PAD (aaggpad), gst_event_new_reconfigure ()); + gst_object_unref (first_configured_pad); + ret = FALSE; + } else { + GST_OBJECT_LOCK (aaggpad); + gst_audio_info_from_caps (&aaggpad->info, caps); + if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)) + GST_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)-> + priv->converter_config_changed = TRUE; + GST_OBJECT_UNLOCK (aaggpad); + } + +done: + if (downstream_caps) + gst_caps_unref (downstream_caps); + + return ret; +} + +static GstFlowReturn +gst_audio_aggregator_update_src_caps (GstAggregator * agg, + GstCaps * caps, GstCaps ** ret) +{ + GstCaps *src_template_caps = gst_pad_get_pad_template_caps (agg->srcpad); + GstCaps *downstream_caps = + gst_pad_peer_query_caps (agg->srcpad, src_template_caps); + + gst_caps_unref (src_template_caps); + + *ret = gst_caps_intersect (caps, downstream_caps); + + GST_INFO ("Updated src caps to %" GST_PTR_FORMAT, *ret); + + if (downstream_caps) + gst_caps_unref (downstream_caps); + + return GST_FLOW_OK; +} + +/* At that point if the caps are not fixed, this means downstream + * didn't have fully specified requirements, we'll just go ahead + * and fixate raw audio fields using our first configured pad, we don't for + * now need a more complicated heuristic + */ +static GstCaps * +gst_audio_aggregator_fixate_src_caps (GstAggregator * agg, GstCaps * caps) +{ + GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (agg); + GstAudioAggregatorPad *first_configured_pad; + + if (!aaggclass->convert_buffer) + return + GST_AGGREGATOR_CLASS + (gst_audio_aggregator_parent_class)->fixate_src_caps (agg, caps); + + first_configured_pad = gst_audio_aggregator_get_first_configured_pad (agg); + + if (first_configured_pad) { + GstStructure *s, *s2; + GstCaps *first_configured_caps = + gst_audio_info_to_caps (&first_configured_pad->info); + gint first_configured_rate, first_configured_channels; + + caps = gst_caps_make_writable (caps); + s = gst_caps_get_structure (caps, 0); + s2 = gst_caps_get_structure (first_configured_caps, 0); + + gst_structure_get_int (s2, "rate", &first_configured_rate); + gst_structure_get_int (s2, "channels", &first_configured_channels); + + gst_structure_fixate_field_string (s, "format", + gst_structure_get_string (s2, "format")); + gst_structure_fixate_field_string (s, "layout", + gst_structure_get_string (s2, "layout")); + gst_structure_fixate_field_nearest_int (s, "rate", first_configured_rate); + gst_structure_fixate_field_nearest_int (s, "channels", + first_configured_channels); + + gst_caps_unref (first_configured_caps); + gst_object_unref (first_configured_pad); + } + + if (!gst_caps_is_fixed (caps)) + caps = gst_caps_fixate (caps); + + GST_INFO_OBJECT (agg, "Fixated src caps to %" GST_PTR_FORMAT, caps); + + return caps; +} + +/* Must be called with OBJECT_LOCK taken */ +static void +gst_audio_aggregator_update_converters (GstAudioAggregator * aagg, + GstAudioInfo * new_info) +{ + GList *l; + + for (l = GST_ELEMENT (aagg)->sinkpads; l; l = l->next) { + GstAudioAggregatorPad *aaggpad = l->data; + + if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)) + GST_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)-> + priv->converter_config_changed = TRUE; + + /* If we currently were mixing a buffer, we need to convert it to the new + * format */ + if (aaggpad->priv->buffer) { + GstBuffer *new_converted_buffer = + gst_audio_aggregator_convert_buffer (aagg, GST_PAD (aaggpad), + &aaggpad->info, new_info, aaggpad->priv->input_buffer); + gst_buffer_replace (&aaggpad->priv->buffer, new_converted_buffer); + } + } +} + +/* We now have our final output caps, we can create the required converters */ +static gboolean +gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps) +{ + GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); + GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (agg); + GstAudioInfo info; + + GST_INFO_OBJECT (agg, "src caps negotiated %" GST_PTR_FORMAT, caps); + + if (!gst_audio_info_from_caps (&info, caps)) { + GST_WARNING_OBJECT (aagg, "Rejecting invalid caps: %" GST_PTR_FORMAT, caps); + return FALSE; + } + + GST_AUDIO_AGGREGATOR_LOCK (aagg); + GST_OBJECT_LOCK (aagg); + + if (aaggclass->convert_buffer) { + gst_audio_aggregator_update_converters (aagg, &info); + + if (aagg->priv->current_buffer + && !gst_audio_info_is_equal (&aagg->info, &info)) { + GstBuffer *converted = + gst_audio_aggregator_convert_buffer (aagg, agg->srcpad, &aagg->info, + &info, aagg->priv->current_buffer); + gst_buffer_unref (aagg->priv->current_buffer); + aagg->priv->current_buffer = converted; + } + } + + if (!gst_audio_info_is_equal (&info, &aagg->info)) { + GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps); + gst_caps_replace (&aagg->current_caps, caps); + + memcpy (&aagg->info, &info, sizeof (info)); + } + + GST_OBJECT_UNLOCK (aagg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + return + GST_AGGREGATOR_CLASS + (gst_audio_aggregator_parent_class)->negotiated_src_caps (agg, caps); +} /* event handling */ @@ -439,6 +980,7 @@ static gboolean gst_audio_aggregator_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, GstEvent * event) { + GstAudioAggregatorPad *aaggpad = GST_AUDIO_AGGREGATOR_PAD (aggpad); gboolean res = TRUE; GST_DEBUG_OBJECT (aggpad, "Got %s event on sink pad", @@ -484,6 +1026,17 @@ gst_audio_aggregator_sink_event (GstAggregator * agg, break; } + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + GST_INFO_OBJECT (aggpad, "Got caps %" GST_PTR_FORMAT, caps); + res = gst_audio_aggregator_sink_setcaps (aaggpad, agg, caps); + gst_event_unref (event); + event = NULL; + break; + } default: break; } @@ -496,6 +1049,35 @@ gst_audio_aggregator_sink_event (GstAggregator * agg, return res; } +static gboolean +gst_audio_aggregator_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_aggregator_sink_getcaps (GST_PAD (aggpad), agg, filter); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + res = TRUE; + break; + } + default: + res = + GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->sink_query + (agg, aggpad, query); + break; + } + + return res; +} + + /* FIXME, the duration query should reflect how long you will produce * data, that is the amount of stream time until you will emit EOS. * @@ -658,39 +1240,6 @@ gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, #endif } - -static gboolean -gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps) -{ - GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); - GstAudioInfo info; - - if (!gst_audio_info_from_caps (&info, caps)) { - GST_WARNING_OBJECT (aagg, "Rejecting invalid caps: %" GST_PTR_FORMAT, caps); - return FALSE; - } - - GST_AUDIO_AGGREGATOR_LOCK (aagg); - GST_OBJECT_LOCK (aagg); - - if (!gst_audio_info_is_equal (&info, &aagg->info)) { - GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps); - gst_caps_replace (&aagg->current_caps, caps); - - memcpy (&aagg->info, &info, sizeof (info)); - } - - GST_OBJECT_UNLOCK (aagg); - GST_AUDIO_AGGREGATOR_UNLOCK (aagg); - - /* send caps event later, after stream-start event */ - - return - GST_AGGREGATOR_CLASS - (gst_audio_aggregator_parent_class)->negotiated_src_caps (agg, caps); -} - - /* Must hold object lock and aagg lock to call */ static void @@ -769,9 +1318,10 @@ gst_audio_aggregator_do_clip (GstAggregator * agg, * values. */ static gboolean -gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, - GstAudioAggregatorPad * pad, GstBuffer * inbuf) +gst_audio_aggregator_fill_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad) { + GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (aagg); GstClockTime start_time, end_time; gboolean discont = FALSE; guint64 start_offset, end_offset; @@ -780,27 +1330,31 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, GstAggregator *agg = GST_AGGREGATOR (aagg); GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - g_assert (pad->priv->buffer == NULL); - - rate = GST_AUDIO_INFO_RATE (&pad->info); - bpf = GST_AUDIO_INFO_BPF (&pad->info); + if (aaggclass->convert_buffer) { + rate = GST_AUDIO_INFO_RATE (&aagg->info); + bpf = GST_AUDIO_INFO_BPF (&aagg->info); + } else { + rate = GST_AUDIO_INFO_RATE (&pad->info); + bpf = GST_AUDIO_INFO_BPF (&pad->info); + } pad->priv->position = 0; - pad->priv->size = gst_buffer_get_size (inbuf) / bpf; + pad->priv->size = gst_buffer_get_size (pad->priv->buffer) / bpf; if (pad->priv->size == 0) { - if (!GST_BUFFER_DURATION_IS_VALID (inbuf) || - !GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + if (!GST_BUFFER_DURATION_IS_VALID (pad->priv->buffer) || + !GST_BUFFER_FLAG_IS_SET (pad->priv->buffer, GST_BUFFER_FLAG_GAP)) { GST_WARNING_OBJECT (pad, "Dropping 0-sized buffer missing either a" - " duration or a GAP flag: %" GST_PTR_FORMAT, inbuf); + " duration or a GAP flag: %" GST_PTR_FORMAT, pad->priv->buffer); return FALSE; } - pad->priv->size = gst_util_uint64_scale (GST_BUFFER_DURATION (inbuf), rate, + pad->priv->size = + gst_util_uint64_scale (GST_BUFFER_DURATION (pad->priv->buffer), rate, GST_SECOND); } - if (!GST_BUFFER_PTS_IS_VALID (inbuf)) { + if (!GST_BUFFER_PTS_IS_VALID (pad->priv->buffer)) { if (pad->priv->output_offset == -1) pad->priv->output_offset = aagg->priv->offset; if (pad->priv->next_offset == -1) @@ -810,7 +1364,7 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, goto done; } - start_time = GST_BUFFER_PTS (inbuf); + start_time = GST_BUFFER_PTS (pad->priv->buffer); end_time = start_time + gst_util_uint64_scale_ceil (pad->priv->size, GST_SECOND, rate); @@ -823,8 +1377,8 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, GST_SECOND); end_offset = start_offset + pad->priv->size; - if (GST_BUFFER_IS_DISCONT (inbuf) - || GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_RESYNC) + if (GST_BUFFER_IS_DISCONT (pad->priv->buffer) + || GST_BUFFER_FLAG_IS_SET (pad->priv->buffer, GST_BUFFER_FLAG_RESYNC) || pad->priv->new_segment || pad->priv->next_offset == -1) { discont = TRUE; pad->priv->new_segment = FALSE; @@ -905,8 +1459,6 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, if (start_output_offset == -1 && end_output_offset == -1) { /* Outside output segment, drop */ - gst_buffer_unref (inbuf); - pad->priv->buffer = NULL; pad->priv->position = 0; pad->priv->size = 0; pad->priv->output_offset = -1; @@ -919,9 +1471,6 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, end_output_offset = start_output_offset + pad->priv->size; if (end_output_offset < aagg->priv->offset) { - /* Before output segment, drop */ - gst_buffer_unref (inbuf); - pad->priv->buffer = NULL; pad->priv->position = 0; pad->priv->size = 0; pad->priv->output_offset = -1; @@ -950,8 +1499,6 @@ gst_audio_aggregator_queue_new_buffer (GstAudioAggregator * aagg, pad->priv->position += diff; if (pad->priv->position >= pad->priv->size) { /* Empty buffer, drop */ - gst_buffer_unref (inbuf); - pad->priv->buffer = NULL; pad->priv->position = 0; pad->priv->size = 0; pad->priv->output_offset = -1; @@ -978,7 +1525,6 @@ done: GST_LOG_OBJECT (pad, "Queued new buffer at offset %" G_GUINT64_FORMAT, pad->priv->output_offset); - pad->priv->buffer = inbuf; return TRUE; } @@ -1013,6 +1559,7 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, pad->priv->position = pad->priv->size; gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); return FALSE; } @@ -1042,6 +1589,7 @@ gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, if (pad->priv->position == pad->priv->size) { /* Buffer done, drop it */ gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); GST_LOG_OBJECT (pad, "Finished mixing buffer, waiting for next"); return FALSE; } @@ -1060,6 +1608,9 @@ gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, gst_aggregator_get_allocator (GST_AGGREGATOR (aagg), &allocator, ¶ms); + GST_DEBUG ("Creating output buffer with size %d", + num_frames * GST_AUDIO_INFO_BPF (&aagg->info)); + outbuf = gst_buffer_new_allocate (allocator, num_frames * GST_AUDIO_INFO_BPF (&aagg->info), ¶ms); @@ -1220,7 +1771,6 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) aagg->priv->offset, GST_TIME_ARGS (agg->segment.position)); for (iter = element->sinkpads; iter; iter = iter->next) { - GstBuffer *inbuf; GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) iter->data; GstAggregatorPad *aggpad = (GstAggregatorPad *) iter->data; gboolean pad_eos = gst_aggregator_pad_is_eos (aggpad); @@ -1228,10 +1778,10 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (!pad_eos) is_eos = FALSE; - inbuf = gst_aggregator_pad_get_buffer (aggpad); + pad->priv->input_buffer = gst_aggregator_pad_get_buffer (aggpad); GST_OBJECT_LOCK (pad); - if (!inbuf) { + if (!pad->priv->input_buffer) { if (timeout) { if (pad->priv->output_offset < next_offset) { gint64 diff = next_offset - pad->priv->output_offset; @@ -1247,19 +1797,28 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) continue; } - g_assert (!pad->priv->buffer || pad->priv->buffer == inbuf); - /* New buffer? */ if (!pad->priv->buffer) { - /* Takes ownership of buffer */ - if (!gst_audio_aggregator_queue_new_buffer (aagg, pad, inbuf)) { + if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (pad)) + pad->priv->buffer = + gst_audio_aggregator_convert_buffer + (aagg, GST_PAD (pad), &pad->info, &aagg->info, + pad->priv->input_buffer); + else + pad->priv->buffer = gst_buffer_ref (pad->priv->input_buffer); + + if (!gst_audio_aggregator_fill_buffer (aagg, pad)) { + gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); + pad->priv->buffer = NULL; dropped = TRUE; GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); continue; } } else { - gst_buffer_unref (inbuf); + gst_buffer_unref (pad->priv->input_buffer); } if (!pad->priv->buffer && !dropped && pad_eos) { @@ -1288,6 +1847,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) GST_AUDIO_INFO_RATE (&aagg->info))), pad->priv->buffer); /* Buffer done, drop it */ gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); dropped = TRUE; GST_OBJECT_UNLOCK (pad); gst_aggregator_pad_drop_buffer (aggpad); diff --git a/gst-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h index 41ce18b1c9..b32630ee67 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.h +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -67,7 +67,7 @@ typedef struct _GstAudioAggregatorPadPrivate GstAudioAggregatorPadPrivate; * @parent: The parent #GstAggregatorPad * @info: The audio info for this pad set from the incoming caps * - * The implementation the GstPad to use with #GstAudioAggregator + * The default implementation of GstPad used with #GstAudioAggregator */ struct _GstAudioAggregatorPad { @@ -86,7 +86,7 @@ struct _GstAudioAggregatorPad * */ struct _GstAudioAggregatorPadClass -{ + { GstAggregatorPadClass parent_class; /*< private >*/ @@ -96,6 +96,54 @@ struct _GstAudioAggregatorPadClass GST_EXPORT GType gst_audio_aggregator_pad_get_type (void); +#define GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD (gst_audio_aggregator_convert_pad_get_type()) +#define GST_AUDIO_AGGREGATOR_CONVERT_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD, GstAudioAggregatorConvertPad)) +#define GST_AUDIO_AGGREGATOR_CONVERT_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD, GstAudioAggregatorConvertPadClass)) +#define GST_AUDIO_AGGREGATOR_CONVERT_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD, GstAudioAggregatorConvertPadClass)) +#define GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD)) +#define GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD)) + +/**************************** + * GstAudioAggregatorPad Structs * + ***************************/ + +typedef struct _GstAudioAggregatorConvertPad GstAudioAggregatorConvertPad; +typedef struct _GstAudioAggregatorConvertPadClass GstAudioAggregatorConvertPadClass; +typedef struct _GstAudioAggregatorConvertPadPrivate GstAudioAggregatorConvertPadPrivate; + +/** + * GstAudioAggregatorConvertPad: + * @parent: The parent #GstAudioAggregatorPad + * + * An implementation of GstPad that can be used with #GstAudioAggregator. + * + * See #GstAudioAggregator for more details. + */ +struct _GstAudioAggregatorConvertPad +{ + GstAudioAggregatorPad parent; + + /*< private >*/ + GstAudioAggregatorConvertPadPrivate * priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstAudioAggregatorConvertPadClass: + * + */ +struct _GstAudioAggregatorConvertPadClass +{ + GstAudioAggregatorPadClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GST_EXPORT +GType gst_audio_aggregator_convert_pad_get_type (void); + /************************** * GstAudioAggregator API * **************************/ @@ -137,6 +185,10 @@ struct _GstAudioAggregator * buffer. The in_offset and out_offset are in "frames", which is * the size of a sample times the number of channels. Returns TRUE if * any non-silence was added to the buffer + * @convert_buffer: Convert a buffer from one format to another. The pad + * is either a sinkpad, when converting an input buffer, or the source pad, + * when converting the output buffer after a downstream format change is + * requested. */ struct _GstAudioAggregatorClass { GstAggregatorClass parent_class; @@ -146,6 +198,11 @@ struct _GstAudioAggregatorClass { gboolean (* aggregate_one_buffer) (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstBuffer * inbuf, guint in_offset, GstBuffer * outbuf, guint out_offset, guint num_frames); + GstBuffer * (* convert_buffer) (GstAudioAggregator *aagg, + GstPad * pad, + GstAudioInfo *in_info, + GstAudioInfo *out_info, + GstBuffer * buffer); /*< private >*/ gpointer _gst_reserved[GST_PADDING_LARGE]; @@ -163,6 +220,9 @@ void gst_audio_aggregator_set_sink_caps (GstAudioAggregator * aagg, GstAudioAggregatorPad * pad, GstCaps * caps); +GST_EXPORT +void gst_audio_aggregator_class_perform_conversion (GstAudioAggregatorClass * klass); + G_END_DECLS #endif /* __GST_AUDIO_AGGREGATOR_H__ */ diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c index 6c7efdd2c4..90ec363ea9 100644 --- a/gst/audiomixer/gstaudiointerleave.c +++ b/gst/audiomixer/gstaudiointerleave.c @@ -580,7 +580,7 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) agg_class->negotiated_src_caps = gst_audio_interleave_negotiated_src_caps; aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer; - + aagg_class->convert_buffer = NULL; /** * GstInterleave:channel-positions diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c index 02737b1f02..a0f5690101 100644 --- a/gst/audiomixer/gstaudiomixer.c +++ b/gst/audiomixer/gstaudiomixer.c @@ -31,12 +31,17 @@ * Unlike the adder element audiomixer properly synchronises all input streams * and also handles live inputs such as capture sources or RTP properly. * - * Caps negotiation is inherently racy with the audiomixer element. You can set - * the "caps" property to force audiomixer to operate in a specific audio - * format, sample rate and channel count. In this case you may also need - * audioconvert and/or audioresample elements for each input stream before the - * audiomixer element to make sure the input branch can produce the forced - * format. + * The audiomixer element can accept any sort of raw audio data, it will + * be converted to the target format if necessary, with the exception + * of the sample rate, which has to be identical to either what downstream + * expects, or the sample rate of the first configured pad. Use a capsfilter + * after the audiomixer element if you want to precisely control the format + * that comes out of the audiomixer, which supports changing the format of + * its output while playing. + * + * If you want to control the manner in which incoming data gets converted, + * see the #GstAudioAggregatorPad:converter-config property, which will let + * you for example change the way in which channels may get remapped. * * The input pads are from a GstPad subclass and have additional * properties to mute each pad individually and set the volume: @@ -89,7 +94,7 @@ enum }; G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, - GST_TYPE_AUDIO_AGGREGATOR_PAD); + GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD); static void gst_audiomixer_pad_get_property (GObject * object, guint prop_id, @@ -163,20 +168,19 @@ gst_audiomixer_pad_init (GstAudioMixerPad * pad) enum { - PROP_0, - PROP_FILTER_CAPS + PROP_0 }; -/* elementfactory information */ +/* These are the formats we can mix natively */ #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 }" + ", layout = interleaved" #else #define CAPS \ GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \ - ", layout = (string) { interleaved, non-interleaved }" + ", layout = interleaved" #endif static GstStaticPadTemplate gst_audiomixer_src_template = @@ -186,12 +190,15 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (CAPS) ); +#define SINK_CAPS \ + GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \ + ", layout=interleaved") + static GstStaticPadTemplate gst_audiomixer_sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, - GST_STATIC_CAPS (CAPS) - ); + SINK_CAPS); static void gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data); @@ -201,14 +208,6 @@ G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, gst_audiomixer_child_proxy_init)); -static void gst_audiomixer_dispose (GObject * object); -static void gst_audiomixer_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_audiomixer_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, - GstPad * pad, GstCaps * caps); static GstPad *gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); @@ -219,287 +218,12 @@ gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, GstBuffer * outbuf, guint out_offset, guint num_samples); -/* 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_audiomixer_sink_getcaps (GstAggregator * agg, GstPad * pad, - GstCaps * filter) -{ - GstAudioAggregator *aagg; - GstAudioMixer *audiomixer; - GstCaps *result, *peercaps, *current_caps, *filter_caps; - GstStructure *s; - gint i, n; - - audiomixer = GST_AUDIO_MIXER (agg); - aagg = GST_AUDIO_AGGREGATOR (agg); - - GST_OBJECT_LOCK (audiomixer); - /* take filter */ - if ((filter_caps = audiomixer->filter_caps)) { - if (filter) - filter_caps = - gst_caps_intersect_full (filter, filter_caps, - GST_CAPS_INTERSECT_FIRST); - else - gst_caps_ref (filter_caps); - } else { - filter_caps = filter ? gst_caps_ref (filter) : NULL; - } - GST_OBJECT_UNLOCK (audiomixer); - - if (filter_caps && gst_caps_is_empty (filter_caps)) { - GST_WARNING_OBJECT (pad, "Empty filter caps"); - return filter_caps; - } - - /* get the downstream possible caps */ - peercaps = gst_pad_peer_query_caps (agg->srcpad, filter_caps); - - /* get the allowed caps on this sinkpad */ - GST_OBJECT_LOCK (audiomixer); - current_caps = aagg->current_caps ? gst_caps_ref (aagg->current_caps) : NULL; - if (current_caps == NULL) { - current_caps = gst_pad_get_pad_template_caps (pad); - if (!current_caps) - current_caps = gst_caps_new_any (); - } - GST_OBJECT_UNLOCK (audiomixer); - - if (peercaps) { - /* if the peer has caps, intersect */ - GST_DEBUG_OBJECT (audiomixer, "intersecting peer and our caps"); - result = - gst_caps_intersect_full (peercaps, current_caps, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (peercaps); - gst_caps_unref (current_caps); - } else { - /* the peer has no caps (or there is no peer), just use the allowed caps - * of this sinkpad. */ - /* restrict with filter-caps if any */ - if (filter_caps) { - GST_DEBUG_OBJECT (audiomixer, "no peer caps, using filtered caps"); - result = - gst_caps_intersect_full (filter_caps, current_caps, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (current_caps); - } else { - GST_DEBUG_OBJECT (audiomixer, "no peer caps, using our caps"); - result = current_caps; - } - } - - result = gst_caps_make_writable (result); - - n = gst_caps_get_size (result); - for (i = 0; i < n; i++) { - GstStructure *sref; - - s = gst_caps_get_structure (result, i); - sref = gst_structure_copy (s); - gst_structure_set (sref, "channels", GST_TYPE_INT_RANGE, 0, 2, NULL); - if (gst_structure_is_subset (s, sref)) { - /* This field is irrelevant when in mono or stereo */ - gst_structure_remove_field (s, "channel-mask"); - } - gst_structure_free (sref); - } - - if (filter_caps) - gst_caps_unref (filter_caps); - - GST_LOG_OBJECT (audiomixer, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, - pad, GST_PAD_NAME (pad), result); - - return result; -} - -static gboolean -gst_audiomixer_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_audiomixer_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; -} - -/* 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_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, - GstCaps * orig_caps) -{ - GstAggregator *agg = GST_AGGREGATOR (audiomixer); - GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (audiomixer); - GstCaps *caps; - GstAudioInfo info; - GstStructure *s; - gint channels = 0; - - caps = gst_caps_copy (orig_caps); - - s = gst_caps_get_structure (caps, 0); - if (gst_structure_get_int (s, "channels", &channels)) - if (channels <= 2) - gst_structure_remove_field (s, "channel-mask"); - - if (!gst_audio_info_from_caps (&info, caps)) - goto invalid_format; - - if (channels == 1) { - GstCaps *filter; - GstCaps *downstream_caps; - - if (audiomixer->filter_caps) - filter = gst_caps_intersect_full (caps, audiomixer->filter_caps, - GST_CAPS_INTERSECT_FIRST); - else - filter = gst_caps_ref (caps); - - downstream_caps = gst_pad_peer_query_caps (agg->srcpad, filter); - gst_caps_unref (filter); - - if (downstream_caps) { - gst_caps_unref (caps); - caps = downstream_caps; - - if (gst_caps_is_empty (caps)) { - gst_caps_unref (caps); - return FALSE; - } - caps = gst_caps_fixate (caps); - } - } - - GST_OBJECT_LOCK (audiomixer); - /* don't allow reconfiguration for now; there's still a race between the - * different upstream threads doing query_caps + accept_caps + sending - * (possibly different) CAPS events, but there's not much we can do about - * that, upstream needs to deal with it. */ - if (aagg->current_caps != NULL) { - if (gst_audio_info_is_equal (&info, &aagg->info)) { - GST_OBJECT_UNLOCK (audiomixer); - gst_caps_unref (caps); - gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), - orig_caps); - return TRUE; - } else { - GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but " - "current caps are %" GST_PTR_FORMAT, caps, aagg->current_caps); - GST_OBJECT_UNLOCK (audiomixer); - gst_pad_push_event (pad, gst_event_new_reconfigure ()); - gst_caps_unref (caps); - return FALSE; - } - } else { - gst_caps_replace (&aagg->current_caps, caps); - aagg->info = info; - gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (agg)); - } - GST_OBJECT_UNLOCK (audiomixer); - - gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad), - orig_caps); - - GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); - - gst_caps_unref (caps); - - return TRUE; - - /* ERRORS */ -invalid_format: - { - gst_caps_unref (caps); - GST_WARNING_OBJECT (audiomixer, "invalid format set as caps"); - return FALSE; - } -} - -static GstFlowReturn -gst_audiomixer_update_src_caps (GstAggregator * agg, GstCaps * caps, - GstCaps ** ret) -{ - GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg); - - if (aagg->current_caps == NULL) - return GST_AGGREGATOR_FLOW_NEED_DATA; - - *ret = gst_caps_ref (aagg->current_caps); - - return GST_FLOW_OK; -} - -static gboolean -gst_audiomixer_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad, - GstEvent * event) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (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_audiomixer_setcaps (audiomixer, 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_audiomixer_class_init (GstAudioMixerClass * klass) { - GObjectClass *gobject_class = (GObjectClass *) klass; GstElementClass *gstelement_class = (GstElementClass *) klass; - GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass; - gobject_class->set_property = gst_audiomixer_set_property; - gobject_class->get_property = gst_audiomixer_get_property; - gobject_class->dispose = gst_audiomixer_dispose; - - g_object_class_install_property (gobject_class, PROP_FILTER_CAPS, - g_param_spec_boxed ("caps", "Target caps", - "Set target format for mixing (NULL means ANY). " - "Setting this property takes a reference to the supplied GstCaps " - "object", GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - gst_element_class_add_static_pad_template (gstelement_class, &gst_audiomixer_src_template); gst_element_class_add_static_pad_template_with_gtype (gstelement_class, @@ -513,80 +237,12 @@ gst_audiomixer_class_init (GstAudioMixerClass * klass) gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); - agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query); - agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event); - agg_class->update_src_caps = - GST_DEBUG_FUNCPTR (gst_audiomixer_update_src_caps); - aagg_class->aggregate_one_buffer = gst_audiomixer_aggregate_one_buffer; } static void gst_audiomixer_init (GstAudioMixer * audiomixer) { - audiomixer->filter_caps = NULL; -} - -static void -gst_audiomixer_dispose (GObject * object) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); - - gst_caps_replace (&audiomixer->filter_caps, NULL); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_audiomixer_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); - - switch (prop_id) { - case PROP_FILTER_CAPS:{ - GstCaps *new_caps = NULL; - GstCaps *old_caps; - const GstCaps *new_caps_val = gst_value_get_caps (value); - - if (new_caps_val != NULL) { - new_caps = (GstCaps *) new_caps_val; - gst_caps_ref (new_caps); - } - - GST_OBJECT_LOCK (audiomixer); - old_caps = audiomixer->filter_caps; - audiomixer->filter_caps = new_caps; - GST_OBJECT_UNLOCK (audiomixer); - - if (old_caps) - gst_caps_unref (old_caps); - - GST_DEBUG_OBJECT (audiomixer, "set new caps %" GST_PTR_FORMAT, new_caps); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); - - switch (prop_id) { - case PROP_FILTER_CAPS: - GST_OBJECT_LOCK (audiomixer); - gst_value_set_caps (value, audiomixer->filter_caps); - GST_OBJECT_UNLOCK (audiomixer); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } } static GstPad * diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h index 0e4098debf..67ccb27e6d 100644 --- a/gst/audiomixer/gstaudiomixer.h +++ b/gst/audiomixer/gstaudiomixer.h @@ -50,9 +50,6 @@ typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; */ struct _GstAudioMixer { GstAudioAggregator element; - - /* target caps (set via property) */ - GstCaps *filter_caps; }; struct _GstAudioMixerClass { @@ -69,7 +66,7 @@ GType gst_audiomixer_get_type (void); #define GST_AUDIO_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) struct _GstAudioMixerPad { - GstAudioAggregatorPad parent; + GstAudioAggregatorConvertPad parent; gdouble volume; gint volume_i32; @@ -79,7 +76,7 @@ struct _GstAudioMixerPad { }; struct _GstAudioMixerPadClass { - GstAudioAggregatorPadClass parent_class; + GstAudioAggregatorConvertPadClass parent_class; }; GType gst_audiomixer_pad_get_type (void); diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c index 4d04093a5d..4a8a8233be 100644 --- a/tests/check/elements/audiomixer.c +++ b/tests/check/elements/audiomixer.c @@ -59,7 +59,7 @@ test_teardown (void) /* some test helpers */ static GstElement * -setup_pipeline (GstElement * audiomixer, gint num_srcs) +setup_pipeline (GstElement * audiomixer, gint num_srcs, GstElement * capsfilter) { GstElement *pipeline, *src, *sink; gint i; @@ -71,7 +71,13 @@ setup_pipeline (GstElement * audiomixer, gint num_srcs) sink = gst_element_factory_make ("fakesink", "sink"); gst_bin_add_many (GST_BIN (pipeline), audiomixer, sink, NULL); - gst_element_link (audiomixer, sink); + + if (capsfilter) { + gst_bin_add (GST_BIN (pipeline), capsfilter); + gst_element_link_many (audiomixer, capsfilter, sink, NULL); + } else { + gst_element_link (audiomixer, sink); + } for (i = 0; i < num_srcs; i++) { src = gst_element_factory_make ("audiotestsrc", NULL); @@ -198,7 +204,7 @@ GST_START_TEST (test_caps) GstCaps *caps; /* build pipeline */ - pipeline = setup_pipeline (NULL, 1); + pipeline = setup_pipeline (NULL, 1, NULL); /* prepare playing */ set_state_and_wait (pipeline, GST_STATE_PAUSED); @@ -217,7 +223,7 @@ GST_END_TEST; /* check that caps set on the property are honoured */ GST_START_TEST (test_filter_caps) { - GstElement *pipeline, *audiomixer; + GstElement *pipeline, *audiomixer, *capsfilter; GstCaps *filter_caps, *caps; filter_caps = gst_caps_new_simple ("audio/x-raw", @@ -226,10 +232,12 @@ GST_START_TEST (test_filter_caps) "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, "channel-mask", GST_TYPE_BITMASK, (guint64) 0x04, NULL); + capsfilter = gst_element_factory_make ("capsfilter", NULL); + /* build pipeline */ audiomixer = gst_element_factory_make ("audiomixer", NULL); - g_object_set (audiomixer, "caps", filter_caps, NULL); - pipeline = setup_pipeline (audiomixer, 1); + g_object_set (capsfilter, "caps", filter_caps, NULL); + pipeline = setup_pipeline (audiomixer, 1, capsfilter); /* prepare playing */ set_state_and_wait (pipeline, GST_STATE_PAUSED); @@ -411,7 +419,7 @@ GST_START_TEST (test_play_twice) /* build pipeline */ audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - bin = setup_pipeline (audiomixer, 2); + bin = setup_pipeline (audiomixer, 2, NULL); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); @@ -471,7 +479,7 @@ GST_START_TEST (test_play_twice_then_add_and_play_again) /* build pipeline */ audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); - bin = setup_pipeline (audiomixer, 2); + bin = setup_pipeline (audiomixer, 2, NULL); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); @@ -1098,7 +1106,7 @@ GST_START_TEST (test_loop) GST_INFO ("preparing test"); /* build pipeline */ - bin = setup_pipeline (NULL, 2); + bin = setup_pipeline (NULL, 2, NULL); bus = gst_element_get_bus (bin); gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); @@ -1713,6 +1721,134 @@ GST_START_TEST (test_sinkpad_property_controller) GST_END_TEST; +static void +change_src_caps (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + GstElement * capsfilter) +{ + GstCaps *caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, GST_AUDIO_NE (S32), + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 10, "channels", G_TYPE_INT, 1, NULL); + + g_object_set (capsfilter, "caps", caps, NULL); + g_signal_connect (fakesink, "handoff", (GCallback) handoff_buffer_cb, NULL); + g_signal_handlers_disconnect_by_func (fakesink, change_src_caps, capsfilter); +} + +/* In this test, we create an input buffer with a duration of 2 seconds, + * and require the audiomixer to output 1 second long buffers. + * The input buffer will thus be mixed twice, and the audiomixer will + * output two buffers. + * + * After audiomixer has output a first buffer, we change its output format + * from S8 to S32. As our sample rate stays the same at 10 fps, and we use + * mono, the first buffer should be 10 bytes long, and the second 40. + * + * The input buffer is made up of 15 0-valued bytes, and 5 1-valued bytes. + * We verify that the second buffer contains 5 0-valued integers, and + * 5 1 << 24 valued integers. + */ +GST_START_TEST (test_change_output_caps) +{ + GstSegment segment; + GstElement *bin, *audiomixer, *capsfilter, *sink; + GstBus *bus; + GstPad *sinkpad; + gboolean res; + GstStateChangeReturn state_res; + GstFlowReturn ret; + GstEvent *event; + GstBuffer *buffer; + GstCaps *caps; + GstQuery *drain = gst_query_new_drain (); + GstMapInfo inmap; + GstMapInfo outmap; + gsize i; + + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + g_object_set (audiomixer, "output-buffer-duration", GST_SECOND, NULL); + capsfilter = gst_element_factory_make ("capsfilter", NULL); + sink = gst_element_factory_make ("fakesink", "sink"); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", (GCallback) change_src_caps, capsfilter); + gst_bin_add_many (GST_BIN (bin), audiomixer, capsfilter, sink, NULL); + + res = gst_element_link_many (audiomixer, capsfilter, sink, NULL); + fail_unless (res == TRUE, NULL); + + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + sinkpad = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (sinkpad == NULL, NULL); + + gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test")); + + caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "S8", + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 10, "channels", G_TYPE_INT, 1, NULL); + + gst_pad_set_caps (sinkpad, caps); + g_object_set (capsfilter, "caps", caps, NULL); + gst_caps_unref (caps); + + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = 0; + segment.stop = 2 * GST_SECOND; + segment.time = 0; + event = gst_event_new_segment (&segment); + gst_pad_send_event (sinkpad, event); + + gst_buffer_replace (&handoff_buffer, NULL); + + buffer = new_buffer (20, 0, 0, 2 * GST_SECOND, 0); + gst_buffer_map (buffer, &inmap, GST_MAP_WRITE); + memset (inmap.data + 15, 1, 5); + gst_buffer_unmap (buffer, &inmap); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + gst_pad_query (sinkpad, drain); + fail_unless (handoff_buffer != NULL); + fail_unless_equals_int (gst_buffer_get_size (handoff_buffer), 40); + + gst_buffer_map (handoff_buffer, &outmap, GST_MAP_READ); + for (i = 0; i < 10; i++) { + guint32 sample; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + sample = GUINT32_FROM_LE (((guint32 *) outmap.data)[i]); +#else + sample = GUINT32_FROM_BE (((guint32 *) outmap.data)[i]); +#endif + + if (i < 5) { + fail_unless_equals_int (sample, 0); + } else { + fail_unless_equals_int (sample, 1 << 24); + } + } + gst_buffer_unmap (handoff_buffer, &outmap); + + gst_element_release_request_pad (audiomixer, sinkpad); + gst_object_unref (sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); + gst_query_unref (drain); +} + +GST_END_TEST; + static Suite * audiomixer_suite (void) { @@ -1739,6 +1875,7 @@ audiomixer_suite (void) tcase_add_test (tc_chain, test_segment_base_handling); tcase_add_test (tc_chain, test_sinkpad_property_controller); tcase_add_checked_fixture (tc_chain, test_setup, test_teardown); + tcase_add_test (tc_chain, test_change_output_caps); /* Use a longer timeout */ #ifdef HAVE_VALGRIND From 558b37d889db6c734b14695d00ac31500d8056e9 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Wed, 20 Dec 2017 15:02:30 +0100 Subject: [PATCH 152/153] audioaggregator: Don't leak pads all audioaggregator subclasses were leaking the first sink pad :) --- gst-libs/gst/audio/gstaudioaggregator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index 8772ae86fc..d749b9b7cf 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -743,7 +743,6 @@ gst_audio_aggregator_sink_setcaps (GstAudioAggregatorPad * aaggpad, && info.rate != downstream_rate) || (first_configured_pad && info.rate != first_configured_pad->info.rate)) { gst_pad_push_event (GST_PAD (aaggpad), gst_event_new_reconfigure ()); - gst_object_unref (first_configured_pad); ret = FALSE; } else { GST_OBJECT_LOCK (aaggpad); @@ -755,6 +754,9 @@ gst_audio_aggregator_sink_setcaps (GstAudioAggregatorPad * aaggpad, } done: + if (first_configured_pad) + gst_object_unref (first_configured_pad); + if (downstream_caps) gst_caps_unref (downstream_caps); From 29534c3829a2b11dc7ad9424cc26cbbda20e3397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 23 Jan 2018 09:01:00 +0000 Subject: [PATCH 153/153] Update for renamed aggregator pad API https://bugzilla.gnome.org/show_bug.cgi?id=791204 --- gst-libs/gst/audio/gstaudioaggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c index d749b9b7cf..fa9911b31f 100644 --- a/gst-libs/gst/audio/gstaudioaggregator.c +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -1780,7 +1780,7 @@ gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) if (!pad_eos) is_eos = FALSE; - pad->priv->input_buffer = gst_aggregator_pad_get_buffer (aggpad); + pad->priv->input_buffer = gst_aggregator_pad_peek_buffer (aggpad); GST_OBJECT_LOCK (pad); if (!pad->priv->input_buffer) {