diff options
author | Tim-Philipp Müller <tim@centricular.com> | 2018-02-13 15:56:49 +0000 |
---|---|---|
committer | Tim-Philipp Müller <tim@centricular.com> | 2018-02-13 15:56:49 +0000 |
commit | ab758a9a39f4bc6dc9f32d03541c122b9618afec (patch) | |
tree | 1c4f1c6c78f7e35d14602effd8bdb68155bd83b8 | |
parent | aab5cccc340c5562429869612f2e788cfcd579f7 (diff) | |
parent | 29534c3829a2b11dc7ad9424cc26cbbda20e3397 (diff) |
audioaggregator, audiomixer, audiointerleave: move from -bad to -base
https://bugzilla.gnome.org/show_bug.cgi?id=791218
-rw-r--r-- | gst-libs/gst/audio/gstaudioaggregator.c | 1995 | ||||
-rw-r--r-- | gst-libs/gst/audio/gstaudioaggregator.h | 228 | ||||
-rw-r--r-- | gst/audiomixer/Makefile.am | 21 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiointerleave.c | 902 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiointerleave.h | 100 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiomixer.c | 577 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiomixer.h | 87 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiomixerorc-dist.c | 2605 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiomixerorc-dist.h | 106 | ||||
-rw-r--r-- | gst/audiomixer/gstaudiomixerorc.orc | 176 | ||||
-rw-r--r-- | gst/audiomixer/meson.build | 32 | ||||
-rw-r--r-- | tests/check/elements/audiointerleave.c | 1128 | ||||
-rw-r--r-- | tests/check/elements/audiomixer.c | 1894 |
13 files changed, 9851 insertions, 0 deletions
diff --git a/gst-libs/gst/audio/gstaudioaggregator.c b/gst-libs/gst/audio/gstaudioaggregator.c new file mode 100644 index 000000000..fa9911b31 --- /dev/null +++ b/gst-libs/gst/audio/gstaudioaggregator.c @@ -0,0 +1,1995 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2001 Thomas <thomas@apestaart.org> + * 2005,2006 Wim Taymans <wim@fluendo.com> + * 2013 Sebastian Dröge <sebastian@centricular.com> + * 2014 Collabora + * Olivier Crete <olivier.crete@collabora.com> + * + * 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 + * + * #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). + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstaudioaggregator.h" + +#include <string.h> + +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 a new input buffer from + aggregator to see if we need to update our + cached values. */ + + 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. */ + + guint64 next_offset; /* Next expected sample offset relative to + pad.segment.start */ + + /* 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); + +enum +{ + PROP_PAD_0, + PROP_PAD_CONVERTER_CONFIG, +}; + +static GstFlowReturn +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); + gst_buffer_replace (&pad->priv->input_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); +} + +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->input_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 GstFlowReturn +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_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 * + **************************************/ + +struct _GstAudioAggregatorPrivate +{ + GMutex mutex; + + /* 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 */ + /* 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 */ + + /* Sample offset starting from 0 at aggregator.segment.start */ + 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_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); + +static GstBuffer *gst_audio_aggregator_create_output_buffer (GstAudioAggregator + * aagg, guint num_frames); +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 (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) +#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 || 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; +} + +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) +{ + 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->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; + 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; + 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"); + + 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; + } +} + +/* 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 ()); + 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 (first_configured_pad) + gst_object_unref (first_configured_pad); + + 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 */ + +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 parameters 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) +{ + GstAudioAggregatorPad *aaggpad = GST_AUDIO_AGGREGATOR_PAD (aggpad); + 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; + } + 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; + } + + if (event != NULL) + return + GST_AGGREGATOR_CLASS (gst_audio_aggregator_parent_class)->sink_event + (agg, aggpad, event); + + 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. + * + * 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: + 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: + 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) +{ +#ifndef G_DISABLE_ASSERT + gboolean valid; + + 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); + GST_OBJECT_UNLOCK (pad); +#endif +} + +/* 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 = -1; + 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 = -1; + gst_buffer_replace (&aagg->priv->current_buffer, NULL); + GST_OBJECT_UNLOCK (aagg); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + return GST_FLOW_OK; +} + +static GstBuffer * +gst_audio_aggregator_do_clip (GstAggregator * agg, + 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); + buffer = gst_audio_buffer_clip (buffer, &bpad->segment, rate, bpf); + GST_OBJECT_UNLOCK (bpad); + + return buffer; +} + +/* 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, + GstAudioAggregatorPad * pad) +{ + GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (aagg); + GstClockTime start_time, end_time; + gboolean discont = FALSE; + guint64 start_offset, end_offset; + gint rate, bpf; + + GstAggregator *agg = GST_AGGREGATOR (aagg); + GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); + + 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 (pad->priv->buffer) / bpf; + + if (pad->priv->size == 0) { + 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, pad->priv->buffer); + return FALSE; + } + + pad->priv->size = + gst_util_uint64_scale (GST_BUFFER_DURATION (pad->priv->buffer), rate, + GST_SECOND); + } + + 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) + pad->priv->next_offset = pad->priv->size; + else + pad->priv->next_offset += pad->priv->size; + goto done; + } + + start_time = GST_BUFFER_PTS (pad->priv->buffer); + end_time = + start_time + gst_util_uint64_scale_ceil (pad->priv->size, GST_SECOND, + rate); + + /* 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 (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; + } 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_DEBUG_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; + GstClockTime segment_pos; + guint64 start_output_offset = -1; + guint64 end_output_offset = -1; + + 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); + + /* Convert to position in the output segment */ + segment_pos = + gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, + start_running_time); + if (GST_CLOCK_TIME_IS_VALID (segment_pos)) + start_output_offset = + gst_util_uint64_scale (segment_pos - agg->segment.start, rate, + GST_SECOND); + + segment_pos = + gst_segment_position_from_running_time (&agg->segment, GST_FORMAT_TIME, + end_running_time); + if (GST_CLOCK_TIME_IS_VALID (segment_pos)) + end_output_offset = + gst_util_uint64_scale (segment_pos - agg->segment.start, rate, + GST_SECOND); + + if (start_output_offset == -1 && end_output_offset == -1) { + /* Outside output segment, drop */ + 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) { + 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_GINT64_FORMAT, end_output_offset, aagg->priv->offset); + return FALSE; + } + + 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) { + /* Empty buffer, drop */ + 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_GINT64_FORMAT, end_output_offset, aagg->priv->offset); + return FALSE; + } + } + + 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_GINT64_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); + + return TRUE; +} + +/* Called with pad object lock held */ + +static gboolean +gst_audio_aggregator_mix_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * pad, GstBuffer * inbuf, GstBuffer * outbuf, + guint blocksize) +{ + guint overlap; + guint out_start; + gboolean filled; + guint in_offset; + gboolean pad_changed = FALSE; + + /* 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->position = pad->priv->size; + + gst_buffer_replace (&pad->priv->buffer, NULL); + gst_buffer_replace (&pad->priv->input_buffer, NULL); + 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, 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; + + 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; + } + + return TRUE; +} + +static GstBuffer * +gst_audio_aggregator_create_output_buffer (GstAudioAggregator * aagg, + guint num_frames) +{ + GstAllocator *allocator; + GstAllocationParams params; + GstBuffer *outbuf; + GstMapInfo outmap; + + 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); + + 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); + + return outbuf; +} + +static gboolean +sync_pad_values (GstElement * aagg, GstPad * pad, gpointer user_data) +{ + GstAudioAggregatorPad *aapad = GST_AUDIO_AGGREGATOR_PAD (pad); + GstAggregatorPad *bpad = GST_AGGREGATOR_PAD_CAST (pad); + GstClockTime timestamp, stream_time; + + if (aapad->priv->buffer == NULL) + return TRUE; + + timestamp = GST_BUFFER_PTS (aapad->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_CAST (pad), stream_time); + + return TRUE; +} + +static GstFlowReturn +gst_audio_aggregator_aggregate (GstAggregator * agg, gboolean timeout) +{ + /* 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. + * + * If we had no pad with a buffer, go EOS. + * + * 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; + 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); + + /* Sync pad properties to the stream time */ + gst_element_foreach_sink_pad (element, sync_pad_values, NULL); + + 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_AGGREGATOR_FLOW_NEED_DATA; + } else { + GST_OBJECT_UNLOCK (agg); + goto not_negotiated; + } + } + + 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 %" G_GINT64_FORMAT, + aagg->priv->offset); + } + + blocksize = gst_util_uint64_scale (aagg->priv->output_buffer_duration, + rate, GST_SECOND); + blocksize = MAX (1, blocksize); + + /* 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; + } + + /* Use the sample counter, which will never accumulate rounding errors */ + next_timestamp = + agg->segment.start + 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_GINT64_FORMAT + " with timestamp %" GST_TIME_FORMAT, blocksize, + aagg->priv->offset, GST_TIME_ARGS (agg->segment.position)); + + for (iter = element->sinkpads; iter; iter = iter->next) { + GstAudioAggregatorPad *pad = (GstAudioAggregatorPad *) iter->data; + GstAggregatorPad *aggpad = (GstAggregatorPad *) iter->data; + gboolean pad_eos = gst_aggregator_pad_is_eos (aggpad); + + if (!pad_eos) + is_eos = FALSE; + + pad->priv->input_buffer = gst_aggregator_pad_peek_buffer (aggpad); + + GST_OBJECT_LOCK (pad); + if (!pad->priv->input_buffer) { + if (timeout) { + if (pad->priv->output_offset < next_offset) { + gint64 diff = next_offset - pad->priv->output_offset; + 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)))); + } + } else if (!pad_eos) { + is_done = FALSE; + } + GST_OBJECT_UNLOCK (pad); + continue; + } + + /* New buffer? */ + if (!pad->priv->buffer) { + 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 (pad->priv->input_buffer); + } + + 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 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; + gint64 odiff = diff; + + 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) { + 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); + /* 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); + continue; + } + } + + g_assert (pad->priv->buffer); + + if (pad->priv->output_offset >= aagg->priv->offset + && 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, + outbuf, blocksize); + if (pad->priv->output_offset >= next_offset) { + 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; + } + if (drop_buf) { + GST_OBJECT_UNLOCK (pad); + gst_aggregator_pad_drop_buffer (aggpad); + continue; + } + } + + GST_OBJECT_UNLOCK (pad); + } + GST_OBJECT_UNLOCK (agg); + + if (dropped) { + /* 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_AGGREGATOR_FLOW_NEED_DATA; + } + + if (!is_done && !is_eos) { + /* Get more buffers */ + GST_LOG_OBJECT (aagg, + "We're not done yet for the current offset, waiting for more data"); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + return GST_AGGREGATOR_FLOW_NEED_DATA; + } + + 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_GINT64_FORMAT, max_offset, next_offset); + next_offset = max_offset; + 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); + } + } + + /* 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; + } + + 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, outbuf); + 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; + + /* 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); + GST_AUDIO_AGGREGATOR_UNLOCK (aagg); + + 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-libs/gst/audio/gstaudioaggregator.h b/gst-libs/gst/audio/gstaudioaggregator.h new file mode 100644 index 000000000..b32630ee6 --- /dev/null +++ b/gst-libs/gst/audio/gstaudioaggregator.h @@ -0,0 +1,228 @@ +/* GStreamer + * Copyright (C) 2014 Collabora + * Author: Olivier Crete <olivier.crete@collabora.com> + * + * 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 <gst/gst.h> +#include <gst/base/gstaggregator.h> +#include <gst/audio/audio.h> + +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 default implementation of GstPad used 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_LARGE]; +}; + +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 * + **************************/ + +#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)) + +/** + * 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 + * @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; + + 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); + GstBuffer * (* convert_buffer) (GstAudioAggregator *aagg, + GstPad * pad, + GstAudioInfo *in_info, + GstAudioInfo *out_info, + GstBuffer * buffer); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +/************************* + * GstAggregator methods * + ************************/ + +GST_EXPORT +GType gst_audio_aggregator_get_type(void); + +GST_EXPORT +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/Makefile.am b/gst/audiomixer/Makefile.am new file mode 100644 index 000000000..f1a4d7395 --- /dev/null +++ b/gst/audiomixer/Makefile.am @@ -0,0 +1,21 @@ +plugin_LTLIBRARIES = libgstaudiomixer.la + +ORC_SOURCE=gstaudiomixerorc +include $(top_srcdir)/common/orc.mak + + +libgstaudiomixer_la_SOURCES = gstaudiomixer.c gstaudiointerleave.c +nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstaudiomixer_la_CFLAGS = \ + -I$(top_srcdir)/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 = \ + $(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) + +noinst_HEADERS = gstaudiomixer.h gstaudiointerleave.h + diff --git a/gst/audiomixer/gstaudiointerleave.c b/gst/audiomixer/gstaudiointerleave.c new file mode 100644 index 000000000..90ec363ea --- /dev/null +++ b/gst/audiomixer/gstaudiointerleave.c @@ -0,0 +1,902 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * 2005 Wim Taymans <wim@fluendo.com> + * 2007 Andy Wingo <wingo at pobox.com> + * 2008 Sebastian Dröge <slomo@circular-chaos.org> + * 2014 Collabora + * Olivier Crete <olivier.crete@collabora.com> + * + * 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 + * @title: 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 <gst/audio/audio.h> + +#include <string.h> + +#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; +} + + +/* Must be called with the object lock held */ + +static guint64 +gst_audio_interleave_get_channel_mask (GstAudioInterleave * self) +{ + guint64 channel_mask = 0; + + 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, + &channel_mask)) { + GST_WARNING_OBJECT (self, "Invalid channel positions, using NONE"); + channel_mask = 0; + } + } else if (self->channels <= 64) { + GST_WARNING_OBJECT (self, "Using NONE channel positions"); + } + + return channel_mask; +} + + +#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, + GstAudioInfo * info) +{ + switch (GST_AUDIO_INFO_WIDTH (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); + GstAudioInfo info; + GValue *val; + guint channel; + gboolean new = FALSE; + + if (!gst_audio_info_from_caps (&info, caps)) + goto invalid_format; + + 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); + 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_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (aagg)); + + gst_caps_unref (sinkcaps); + new = 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); + + return TRUE; + + /* ERRORS */ +invalid_format: + { + GST_WARNING_OBJECT (self, "invalid format set as caps: %" GST_PTR_FORMAT, + caps); + return FALSE; + } +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; + } +} + +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 GstFlowReturn +gst_audio_interleave_update_src_caps (GstAggregator * agg, GstCaps * caps, + GstCaps ** ret) +{ + GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg); + GstStructure *s; + + /* 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). + */ + 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); + + 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 (self); + + 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); + + if (!GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps)) + return FALSE; + + gst_audio_interleave_set_process_function (self, &aagg->info); + + return TRUE; +} + +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_static_pad_template (gstelement_class, + &gst_audio_interleave_src_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 <olivier.crete@collabora.com>"); + + 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->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->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; + aagg_class->convert_buffer = NULL; + + /** + * 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; + 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 channel, padnumber; + GValue val = { 0, }; + + /* FIXME: We ignore req_name, this is evil! */ + + GST_OBJECT_LOCK (self); + padnumber = g_atomic_int_add (&self->padcounter, 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 *) + 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 = channel; + 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 */ + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); + + 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); + + self->channels--; + + 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--; + } + + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); + 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, 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); + 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, out_channels, + out_offset * out_bpf, in_offset * in_bpf); + + 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, + num_frames); + + + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unmap (outbuf, &outmap); + + GST_OBJECT_UNLOCK (aaggpad); + GST_OBJECT_UNLOCK (aagg); + + 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 000000000..bf46f4a50 --- /dev/null +++ b/gst/audiomixer/gstaudiointerleave.h @@ -0,0 +1,100 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * Copyright (C) 2013 Sebastian Dröge <slomo@circular-chaos.org> + * + * 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 <gst/gst.h> +#include <gst/audio/audio.h> + +#include <gst/audio/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; /* object lock */ + + GstCaps *sinkcaps; + + 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 new file mode 100644 index 000000000..a0f569010 --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.c @@ -0,0 +1,577 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2001 Thomas <thomas@apestaart.org> + * 2005,2006 Wim Taymans <wim@fluendo.com> + * 2013 Sebastian Dröge <sebastian@centricular.com> + * + * 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 + * @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. + * + * Unlike the adder element audiomixer properly synchronises all input streams + * and also handles live inputs such as capture sources or RTP properly. + * + * 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: + * + * * "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 + * |[ + * gst-launch-1.0 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 <gst/audio/audio.h> +#include <string.h> /* strcmp */ +#include "gstaudiomixerorc.h" + +#include "gstaudiointerleave.h" + +#define GST_CAT_DEFAULT gst_audiomixer_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#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_AUDIO_AGGREGATOR_CONVERT_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; +} + +enum +{ + PROP_0 +}; + +/* 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 = interleaved" +#else +#define CAPS \ + GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \ + ", layout = interleaved" +#endif + +static GstStaticPadTemplate gst_audiomixer_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + 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, + SINK_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_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_audiomixer_child_proxy_init)); + +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 gboolean +gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_samples); + + +static void +gst_audiomixer_class_init (GstAudioMixerClass * klass) +{ + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass; + + gst_element_class_add_static_pad_template (gstelement_class, + &gst_audiomixer_src_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 <sebastian@centricular.com>"); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); + + aagg_class->aggregate_one_buffer = gst_audiomixer_aggregate_one_buffer; +} + +static void +gst_audiomixer_init (GstAudioMixer * audiomixer) +{ +} + +static GstPad * +gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * req_name, const GstCaps * caps) +{ + GstAudioMixerPad *newpad; + + newpad = (GstAudioMixerPad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, req_name, caps); + + if (newpad == NULL) + goto could_not_create; + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return GST_PAD_CAST (newpad); + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + 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)); + + GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); +} + + +static gboolean +gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg, + GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset, + GstBuffer * outbuf, guint out_offset, guint num_frames) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aaggpad); + GstMapInfo inmap; + 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; + } + + 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", + num_frames * bpf, out_offset * bpf, in_offset * bpf); + + /* further buffers, need to add them */ + if (pad->volume == 1.0) { + switch (aagg->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + 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_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_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_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_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_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_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_offset * bpf), + (gpointer) (inmap.data + in_offset * bpf), + num_frames * aagg->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } else { + switch (aagg->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + 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_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_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_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_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_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_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_offset * bpf), (gpointer) (inmap.data + in_offset * bpf), + pad->volume, num_frames * aagg->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unmap (outbuf, &outmap); + + GST_OBJECT_UNLOCK (aaggpad); + GST_OBJECT_UNLOCK (aagg); + + return TRUE; +} + + +/* 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; +} + +/* 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); + +enum +{ + LIVEADDER_PROP_LATENCY = 1 +}; + +static void +gst_live_adder_init (GstLiveAdder * self) +{ +} + +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_UINT64); + + 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); + 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_UINT64); + + G_OBJECT_CLASS (pspec_class)->get_property (object, + parent_spec->param_id, &v, parent_spec); + + g_value_set_uint (value, g_value_get_uint64 (&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) +{ + 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; + + 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; + + 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 000000000..67ccb27e6 --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.h @@ -0,0 +1,87 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * Copyright (C) 2013 Sebastian Dröge <slomo@circular-chaos.org> + * + * 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 <gst/gst.h> +#include <gst/audio/audio.h> +#include <gst/audio/gstaudioaggregator.h> + +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 { + GstAudioAggregator element; +}; + +struct _GstAudioMixerClass { + GstAudioAggregatorClass 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 { + GstAudioAggregatorConvertPad parent; + + gdouble volume; + gint volume_i32; + gint volume_i16; + gint volume_i8; + gboolean mute; +}; + +struct _GstAudioMixerPadClass { + GstAudioAggregatorConvertPadClass 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 000000000..be377f705 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.c @@ -0,0 +1,2605 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <glib.h> + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include <stdint.h> +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 <limits.h> +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 <orc/orc.h> +#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)&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)) +#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; + orc_int8 var37; + orc_int8 var38; + orc_union16 var39; + orc_union16 var40; + orc_int8 var41; + + ptr0 = (orc_int8 *) d1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var38 = var34 ^ var35; + /* 4: mulsbw */ + var39.i = var38 * var36; + /* 5: shrsw */ + var40.i = var39.i >> 3; + /* 6: convssswb */ + var41 = ORC_CLAMP_SB (var40.i); + /* 7: xorb */ + var37 = var41 ^ var35; + /* 8: storeb */ + ptr0[i] = var37; + } + +} + +#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; + orc_int8 var37; + orc_int8 var38; + orc_union16 var39; + orc_union16 var40; + orc_int8 var41; + + ptr0 = (orc_int8 *) ex->arrays[0]; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var38 = var34 ^ var35; + /* 4: mulsbw */ + var39.i = var38 * var36; + /* 5: shrsw */ + var40.i = var39.i >> 3; + /* 6: convssswb */ + var41 = ORC_CLAMP_SB (var40.i); + /* 7: xorb */ + var37 = var41 ^ var35; + /* 8: storeb */ + ptr0[i] = var37; + } + +} + +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, 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, + }; + 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, 2, 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; + orc_int8 var37; + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + orc_int8 var43; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[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); + /* 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; + } + +} + +#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; + orc_int8 var37; + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + orc_int8 var43; + + 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]; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[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); + /* 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; + } + +} + +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, 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, + + }; + 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, 2, 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, 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, + }; + 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, 2, 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; + orc_union16 var37; + orc_union16 var38; + orc_union16 var39; + orc_union32 var40; + orc_union32 var41; + orc_union16 var42; + orc_union16 var43; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + /* 1: loadpw */ + var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + /* 3: loadpw */ + var36.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var39.i = var34.i ^ var35.i; + /* 4: mulswl */ + var40.i = var39.i * var36.i; + /* 5: shrsl */ + var41.i = var40.i >> 11; + /* 6: convssslw */ + 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; + } + +} + +#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; + orc_union16 var37; + orc_union16 var38; + orc_union16 var39; + orc_union32 var40; + orc_union32 var41; + orc_union16 var42; + orc_union16 var43; + + 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]; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var39.i = var34.i ^ var35.i; + /* 4: mulswl */ + var40.i = var39.i * var36.i; + /* 5: shrsl */ + var41.i = var40.i >> 11; + /* 6: convssslw */ + 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; + } + +} + +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; + orc_union32 var37; + orc_union32 var38; + orc_union32 var39; + orc_union64 var40; + orc_union64 var41; + orc_union32 var42; + orc_union32 var43; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + /* 3: loadpl */ + var36.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var39.i = var34.i ^ var35.i; + /* 4: mulslq */ + var40.i = ((orc_int64) var39.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var41.i = var40.i >> 27; + /* 6: convsssql */ + 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; + } + +} + +#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; + orc_union32 var37; + orc_union32 var38; + orc_union32 var39; + orc_union64 var40; + orc_union64 var41; + orc_union32 var42; + orc_union32 var43; + + 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]; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var39.i = var34.i ^ var35.i; + /* 4: mulslq */ + var40.i = ((orc_int64) var39.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var41.i = var40.i >> 27; + /* 6: convsssql */ + 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; + } + +} + +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, 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, + _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_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"); + + 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, 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, + _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_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"); + + 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] = ((orc_uint64) tmp.i) & 0xffffffff; + ex->params[ORC_VAR_T1] = ((orc_uint64) tmp.i) >> 32; + } + + 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 000000000..af0de0139 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.h @@ -0,0 +1,106 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifndef _GSTAUDIOMIXERORC_H_ +#define _GSTAUDIOMIXERORC_H_ + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include <stdint.h> +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 <limits.h> +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 000000000..5eaff2395 --- /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/gst/audiomixer/meson.build b/gst/audiomixer/meson.build new file mode 100644 index 000000000..ccfe1b9d3 --- /dev/null +++ b/gst/audiomixer/meson.build @@ -0,0 +1,32 @@ +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, gstaudio_dep, gstbase_dep, orc_dep], + install : true, + install_dir : plugins_install_dir, +) diff --git a/tests/check/elements/audiointerleave.c b/tests/check/elements/audiointerleave.c new file mode 100644 index 000000000..71348f459 --- /dev/null +++ b/tests/check/elements/audiointerleave.c @@ -0,0 +1,1128 @@ +/* GStreamer unit tests for the audiointerleave element + * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.org> + * + * 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 <valgrind/valgrind.h> +#endif + +#include <gst/check/gstcheck.h> +#include <gst/audio/audio.h> +#include <gst/audio/audio-enumtypes.h> + +#include <gst/check/gstharness.h> + +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_bus_set_flushing (bus, TRUE); + 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_bus_set_flushing (bus, TRUE); + 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, num_samples; + GstCaps *caps; + guint64 mask; + GstAudioChannelPosition pos; + GstMapInfo map; + + 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); + + 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_unmap (buffer, &map); +} + +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 || 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); + } 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 (); + } + + 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); + if (n != 3) + 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, "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), + 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, "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), + 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, "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_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, "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_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, "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)); + 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, "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)); + 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; + +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, "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)); + 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, "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)); + 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 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; + 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, + (guint64) 0x9, NULL); + + gst_event_parse_caps (ev, &ecaps); + gst_check_caps_equal (ecaps, caps); + gst_caps_unref (caps); + gst_event_unref (ev); + + /* 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); + + 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) +{ + 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); + tcase_add_test (tc_chain, test_audiointerleave_2ch_pipeline_no_chanpos); + tcase_add_test (tc_chain, test_audiointerleave_2ch_smallbuf); + + return s; +} + +GST_CHECK_MAIN (audiointerleave); diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c new file mode 100644 index 000000000..4a8a8233b --- /dev/null +++ b/tests/check/elements/audiomixer.c @@ -0,0 +1,1894 @@ +/* GStreamer + * + * unit test for audiomixer + * + * Copyright (C) 2005 Thomas Vander Stichele <thomas at apestaart dot org> + * Copyright (C) 2013 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_VALGRIND +# include <valgrind/valgrind.h> +#endif + +#include <unistd.h> + +#include <gst/check/gstcheck.h> +#include <gst/check/gstconsistencychecker.h> +#include <gst/audio/audio.h> +#include <gst/base/gstbasesrc.h> +#include <gst/controller/gstdirectcontrolbinding.h> +#include <gst/controller/gstinterpolationcontrolsource.h> + +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; +} + + +/* some test helpers */ + +static GstElement * +setup_pipeline (GstElement * audiomixer, gint num_srcs, GstElement * capsfilter) +{ + 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); + + 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); + 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; + + 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); + gst_object_unref (pad); + gst_object_unref (sink); + + return caps; +} + +static void +set_state_and_wait (GstElement * pipeline, GstState state) +{ + GstStateChangeReturn state_res; + + /* 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); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); +} + +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 +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) +{ + 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 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) +{ + GstElement *pipeline; + GstCaps *caps; + + /* build pipeline */ + pipeline = setup_pipeline (NULL, 1, NULL); + + /* 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, *capsfilter; + 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); + + capsfilter = gst_element_factory_make ("capsfilter", NULL); + + /* build pipeline */ + audiomixer = gst_element_factory_make ("audiomixer", NULL); + g_object_set (capsfilter, "caps", filter_caps, NULL); + pipeline = setup_pipeline (audiomixer, 1, capsfilter); + + /* 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; + +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; + 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; + + 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 */ + set_state_and_wait (bin, GST_STATE_PAUSED); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + play_and_wait (bin); + + ck_assert_int_eq (position, 2 * GST_SECOND); + + /* cleanup */ + 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, + GstElement * 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 (bin, GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* prepare playing again */ + 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); + + 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); + } + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_play_twice) +{ + GstElement *bin, *audiomixer; + GstBus *bus; + gboolean res; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + bin = setup_pipeline (audiomixer, 2, NULL); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + 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; + + 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 */ + 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); + + GST_INFO ("seeked"); + + /* run pipeline */ + play_and_wait (bin); + + ck_assert_int_eq (play_count, 2); + + /* cleanup */ + gst_consistency_checker_free (consist); + gst_event_unref (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, *src, *audiomixer; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + gint i; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + bin = setup_pipeline (audiomixer, 2, NULL); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + 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); + + 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 */ + 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); + + GST_INFO ("seeked"); + + /* run pipeline */ + play_and_wait (bin); + + ck_assert_int_eq (play_count, 2); + + /* plug another source */ + if (i == 0) { + 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 (src, 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 */ + gst_event_unref (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 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, *cf, *src2, *audiomixer, *sink; + GstCaps *caps; + GstBus *bus; + gboolean res; + GstPad *srcpad; + GstPad *sinkpad; + gint i; + 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"); + 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 */ + 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 { + /* live sources ignore seeks, force eos after 2 sec (4 buffers half second + * each) + */ + g_object_set (src1, "num-buffers", 4, "blocksize", 44100, NULL); + } + + 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, cf, audiomixer, sink, NULL); + res = gst_element_link_many (src1, cf, audiomixer, sink, NULL); + fail_unless (res == TRUE, NULL); + + /* 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); + 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, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + 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); + + 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 */ + 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); + + GST_INFO ("seeked"); + + /* run pipeline */ + play_and_wait (bin); + + gst_consistency_checker_reset (consist); + } + + /* cleanup */ + GST_INFO ("cleaning up"); + gst_consistency_checker_free (consist); + if (play_seek_event) + gst_event_unref (play_seek_event); + gst_bus_remove_signal_watch (bus); + 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, "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, "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); + + 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); + + 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 */ + set_state_and_wait (bin, GST_STATE_PAUSED); + + /* 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 */ + play_and_wait (bin); + + /* cleanup */ + 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, "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); + + 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 */ + play_and_wait (bin); + + /* cleanup */ + 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 -- SIZE: %" G_GSIZE_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); +} + +/* 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; + GstQuery *drain = gst_query_new_drain (); + + 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"); + 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); + 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", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "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 = 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. + */ + gst_pad_query (sinkpad, drain); + fail_unless (handoff_buffer == NULL); + + /* should be partially clipped */ + 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); + + 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 = 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))); + ret = gst_pad_chain (sinkpad, buffer); + 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); + + /* should be clipped and ok */ + 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, + 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); + gst_pad_query (sinkpad, drain); + 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_query_unref (drain); +} + +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 */ + set_state_and_wait (bin, GST_STATE_PLAYING); + + 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 */ + set_state_and_wait (bin, GST_STATE_PLAYING); + + 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; + GstBus *bus; + GstEvent *seek_event; + gboolean res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = setup_pipeline (NULL, 2, NULL); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + 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); + + 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 */ + set_state_and_wait (bin, GST_STATE_PAUSED); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + play_and_wait (bin); + + fail_unless (looped); + + /* cleanup */ + 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, *srcpad1, *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); + 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"); + gst_pad_link (tmppad, sinkpad2); + gst_object_unref (tmppad); + + gst_element_link (audiomixer, sink); + + /* 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)); + 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); + + gst_element_release_request_pad (audiomixer, sinkpad1); + 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); + gst_object_unref (pipeline); +} + +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)); +} + +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; + GstBus *bus; + GstPad *sinkpad1, *sinkpad2; + GstPad *queue1_sinkpad, *queue2_sinkpad; + GstPad *pad; + gboolean res; + GstStateChangeReturn state_res; + GstEvent *event; + GstCaps *caps; + GList *received_buffers = NULL; + + 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 */ + 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, "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, + &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 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 + * 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", + "format", G_TYPE_STRING, GST_AUDIO_NE (S16), + "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 */ + 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); +} + +static void +send_buffers_sync (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstFlowReturn ret; + + 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 = 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 = 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 = 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); + + gst_pad_send_event (pad2, gst_event_new_eos ()); +} + +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); + 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] == 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 (); + } + + gst_buffer_unmap (buffer, &map); + + } +} + +GST_START_TEST (test_sync) +{ + run_sync_test (send_buffers_sync, check_buffers_sync); +} + +GST_END_TEST; + +static void +send_buffers_sync_discont (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstFlowReturn ret; + + 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 = 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 = 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 = 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); + + 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 void +send_buffers_sync_unaligned (GstPad * pad1, GstPad * pad2) +{ + GstBuffer *buffer; + GstFlowReturn ret; + + 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 = 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 = 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 = 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); + + 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; + +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); + /* 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"); + 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); + /* set a pad offset of another 5 seconds */ + 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; + +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 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) +{ + 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); + tcase_add_test (tc_chain, test_sync); + 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); + 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 + 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); |