summaryrefslogtreecommitdiff
path: root/gst/adder/gstadder.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/adder/gstadder.c')
-rw-r--r--gst/adder/gstadder.c1327
1 files changed, 0 insertions, 1327 deletions
diff --git a/gst/adder/gstadder.c b/gst/adder/gstadder.c
deleted file mode 100644
index 8d0b300f..00000000
--- a/gst/adder/gstadder.c
+++ /dev/null
@@ -1,1327 +0,0 @@
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2001 Thomas <thomas@apestaart.org>
- * 2005,2006 Wim Taymans <wim@fluendo.com>
- *
- * adder.c: Adder 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-/**
- * SECTION:element-adder
- *
- * The adder allows to mix several streams into one by adding the data.
- * Mixed data is clamped to the min/max values of the data format.
- *
- * The adder currently mixes all data received on the sinkpads as soon as
- * possible without trying to synchronize the streams.
- *
- * <refsect2>
- * <title>Example launch line</title>
- * |[
- * gst-launch audiotestsrc freq=100 ! adder name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix.
- * ]| This pipeline produces two sine waves mixed together.
- * </refsect2>
- *
- * Last reviewed on 2006-05-09 (0.10.7)
- */
-/* Element-Checklist-Version: 5 */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include "gstadder.h"
-#include <gst/audio/audio.h>
-#include <string.h> /* strcmp */
-/*#include <liboil/liboil.h>*/
-
-/* highest positive/lowest negative x-bit value we can use for clamping */
-#define MAX_INT_32 ((gint32) (0x7fffffff))
-#define MAX_INT_16 ((gint16) (0x7fff))
-#define MAX_INT_8 ((gint8) (0x7f))
-#define MAX_UINT_32 ((guint32)(0xffffffff))
-#define MAX_UINT_16 ((guint16)(0xffff))
-#define MAX_UINT_8 ((guint8) (0xff))
-
-#define MIN_INT_32 ((gint32) (0x80000000))
-#define MIN_INT_16 ((gint16) (0x8000))
-#define MIN_INT_8 ((gint8) (0x80))
-#define MIN_UINT_32 ((guint32)(0x00000000))
-#define MIN_UINT_16 ((guint16)(0x0000))
-#define MIN_UINT_8 ((guint8) (0x00))
-
-enum
-{
- PROP_0,
- PROP_FILTER_CAPS
-};
-
-#define GST_CAT_DEFAULT gst_adder_debug
-GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
-
-/* elementfactory information */
-
-#define CAPS \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
- "channels = (int) [ 1, MAX ], " \
- "endianness = (int) BYTE_ORDER, " \
- "width = (int) 32, " \
- "depth = (int) 32, " \
- "signed = (boolean) { true, false } ;" \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
- "channels = (int) [ 1, MAX ], " \
- "endianness = (int) BYTE_ORDER, " \
- "width = (int) 16, " \
- "depth = (int) 16, " \
- "signed = (boolean) { true, false } ;" \
- "audio/x-raw-int, " \
- "rate = (int) [ 1, MAX ], " \
- "channels = (int) [ 1, MAX ], " \
- "endianness = (int) BYTE_ORDER, " \
- "width = (int) 8, " \
- "depth = (int) 8, " \
- "signed = (boolean) { true, false } ;" \
- "audio/x-raw-float, " \
- "rate = (int) [ 1, MAX ], " \
- "channels = (int) [ 1, MAX ], " \
- "endianness = (int) BYTE_ORDER, " \
- "width = (int) { 32, 64 }"
-
-static GstStaticPadTemplate gst_adder_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (CAPS)
- );
-
-static GstStaticPadTemplate gst_adder_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS (CAPS)
- );
-
-static void gst_adder_class_init (GstAdderClass * klass);
-static void gst_adder_init (GstAdder * adder);
-static void gst_adder_dispose (GObject * object);
-static void gst_adder_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_adder_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean gst_adder_query (GstPad * pad, GstQuery * query);
-static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event);
-static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
-
-static GstPad *gst_adder_request_new_pad (GstElement * element,
- GstPadTemplate * temp, const gchar * unused);
-static void gst_adder_release_pad (GstElement * element, GstPad * pad);
-
-static GstStateChangeReturn gst_adder_change_state (GstElement * element,
- GstStateChange transition);
-
-static GstBuffer *gst_adder_do_clip (GstCollectPads * pads,
- GstCollectData * data, GstBuffer * buffer, gpointer user_data);
-static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
- gpointer user_data);
-
-static GstElementClass *parent_class = NULL;
-
-GType
-gst_adder_get_type (void)
-{
- static GType adder_type = 0;
-
- if (G_UNLIKELY (adder_type == 0)) {
- static const GTypeInfo adder_info = {
- sizeof (GstAdderClass), NULL, NULL,
- (GClassInitFunc) gst_adder_class_init, NULL, NULL,
- sizeof (GstAdder), 0,
- (GInstanceInitFunc) gst_adder_init,
- };
-
- adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder",
- &adder_info, 0);
- GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
- "audio channel mixing element");
- }
- return adder_type;
-}
-
-/* clipping versions (for int)
- * FIXME: what about: oil_add_s16 (out, out, in, bytes / sizeof (type))
- */
-#define MAKE_FUNC(name,type,ttype,min,max) \
-static void name (type *out, type *in, gint bytes) { \
- gint i; \
- ttype add; \
- for (i = 0; i < bytes / sizeof (type); i++) { \
- add = (ttype)out[i] + (ttype)in[i]; \
- out[i] = CLAMP (add, min, max); \
- } \
-}
-
-/* unsigned versions (for int) */
-#define MAKE_FUNC_US(name,type,ttype,max) \
-static void name (type *out, type *in, gint bytes) { \
- gint i; \
- ttype add; \
- for (i = 0; i < bytes / sizeof (type); i++) { \
- add = (ttype)out[i] + (ttype)in[i]; \
- out[i] = ((add <= max) ? add : max); \
- } \
-}
-
-/* non-clipping versions (for float) */
-#define MAKE_FUNC_NC(name,type) \
-static void name (type *out, type *in, gint bytes) { \
- gint i; \
- for (i = 0; i < bytes / sizeof (type); i++) \
- out[i] += in[i]; \
-}
-
-#if 0
-/* right now, the liboil function don't seems to be faster on x86
- * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float ! m.
- * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! m.
- */
-static void
-add_float32 (gfloat * out, gfloat * in, gint bytes)
-{
- oil_add_f32 (out, out, in, bytes / sizeof (gfloat));
-}
-
-static void
-add_float64 (gdouble * out, gdouble * in, gint bytes)
-{
- oil_add_f64 (out, out, in, bytes / sizeof (gdouble));
-}
-#endif
-
-/* *INDENT-OFF* */
-MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32)
-MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16)
-MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8)
-MAKE_FUNC_US (add_uint32, guint32, guint64, MAX_UINT_32)
-MAKE_FUNC_US (add_uint16, guint16, guint32, MAX_UINT_16)
-MAKE_FUNC_US (add_uint8, guint8, guint16, MAX_UINT_8)
-MAKE_FUNC_NC (add_float64, gdouble)
-MAKE_FUNC_NC (add_float32, gfloat)
-/* *INDENT-ON* */
-
-/* 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_adder_sink_getcaps (GstPad * pad)
-{
- GstAdder *adder;
- GstCaps *result, *peercaps, *sinkcaps, *filter_caps;
-
- adder = GST_ADDER (GST_PAD_PARENT (pad));
-
- GST_OBJECT_LOCK (adder);
- /* take filter */
- if ((filter_caps = adder->filter_caps))
- gst_caps_ref (filter_caps);
- GST_OBJECT_UNLOCK (adder);
-
- /* get the downstream possible caps */
- peercaps = gst_pad_peer_get_caps (adder->srcpad);
-
- /* get the allowed caps on this sinkpad, we use the fixed caps function so
- * that it does not call recursively in this function. */
- sinkcaps = gst_pad_get_fixed_caps_func (pad);
- if (peercaps) {
- /* restrict with filter-caps if any */
- if (filter_caps) {
- GST_DEBUG_OBJECT (adder, "filtering peer caps");
- result = gst_caps_intersect (peercaps, filter_caps);
- gst_caps_unref (peercaps);
- peercaps = result;
- }
- /* if the peer has caps, intersect */
- GST_DEBUG_OBJECT (adder, "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. */
- /* restrict with filter-caps if any */
- if (filter_caps) {
- GST_DEBUG_OBJECT (adder, "no peer caps, using filtered sinkcaps");
- result = gst_caps_intersect (sinkcaps, filter_caps);
- gst_caps_unref (sinkcaps);
- } else {
- GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
- result = sinkcaps;
- }
- }
-
- if (filter_caps)
- gst_caps_unref (filter_caps);
-
- GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
- GST_PAD_NAME (pad), result);
-
- return result;
-}
-
-/* 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_adder_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstAdder *adder;
- GList *pads;
- GstStructure *structure;
- const char *media_type;
-
- adder = GST_ADDER (GST_PAD_PARENT (pad));
-
- GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
- GST_PAD_NAME (pad), caps);
-
- /* FIXME, see if the other pads can accept the format. Also lock the
- * format on the other pads to this new format. */
- GST_OBJECT_LOCK (adder);
- pads = GST_ELEMENT (adder)->pads;
- while (pads) {
- GstPad *otherpad = GST_PAD (pads->data);
-
- if (otherpad != pad) {
- gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
- }
- pads = g_list_next (pads);
- }
- GST_OBJECT_UNLOCK (adder);
-
- /* parse caps now */
- structure = gst_caps_get_structure (caps, 0);
- media_type = gst_structure_get_name (structure);
- if (strcmp (media_type, "audio/x-raw-int") == 0) {
- adder->format = GST_ADDER_FORMAT_INT;
- gst_structure_get_int (structure, "width", &adder->width);
- gst_structure_get_int (structure, "depth", &adder->depth);
- gst_structure_get_int (structure, "endianness", &adder->endianness);
- gst_structure_get_boolean (structure, "signed", &adder->is_signed);
-
- GST_INFO_OBJECT (pad, "parse_caps sets adder to format int, %d bit",
- adder->width);
-
- if (adder->endianness != G_BYTE_ORDER)
- goto not_supported;
-
- switch (adder->width) {
- case 8:
- adder->func = (adder->is_signed ?
- (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8);
- break;
- case 16:
- adder->func = (adder->is_signed ?
- (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
- break;
- case 32:
- adder->func = (adder->is_signed ?
- (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
- break;
- default:
- goto not_supported;
- }
- } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
- adder->format = GST_ADDER_FORMAT_FLOAT;
- gst_structure_get_int (structure, "width", &adder->width);
- gst_structure_get_int (structure, "endianness", &adder->endianness);
-
- GST_INFO_OBJECT (pad, "parse_caps sets adder to format float, %d bit",
- adder->width);
-
- if (adder->endianness != G_BYTE_ORDER)
- goto not_supported;
-
- switch (adder->width) {
- case 32:
- adder->func = (GstAdderFunction) add_float32;
- break;
- case 64:
- adder->func = (GstAdderFunction) add_float64;
- break;
- default:
- goto not_supported;
- }
- } else {
- goto not_supported;
- }
-
- gst_structure_get_int (structure, "channels", &adder->channels);
- gst_structure_get_int (structure, "rate", &adder->rate);
- /* precalc bps */
- adder->bps = (adder->width / 8) * adder->channels;
-
- return TRUE;
-
- /* ERRORS */
-not_supported:
- {
- GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
- return FALSE;
- }
-}
-
-/* FIXME, the duration query should reflect how long you will produce
- * data, that is the amount of stream time until you will emit EOS.
- *
- * For synchronized mixing this is always the max of all the durations
- * of upstream since we emit EOS when all of them finished.
- *
- * We don't do synchronized mixing so this really depends on where the
- * streams where punched in and what their relative offsets are against
- * eachother which we can get from the first timestamps we see.
- *
- * When we add a new stream (or remove a stream) the duration might
- * also become invalid again and we need to post a new DURATION
- * message to notify this fact to the parent.
- * For now we take the max of all the upstream elements so the simple
- * cases work at least somewhat.
- */
-static gboolean
-gst_adder_query_duration (GstAdder * adder, GstQuery * query)
-{
- gint64 max;
- gboolean res;
- GstFormat format;
- GstIterator *it;
- gboolean done;
-
- /* parse format */
- gst_query_parse_duration (query, &format, NULL);
-
- max = -1;
- res = TRUE;
- done = FALSE;
-
- it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
- while (!done) {
- GstIteratorResult ires;
-
- gpointer item;
-
- ires = gst_iterator_next (it, &item);
- switch (ires) {
- case GST_ITERATOR_DONE:
- done = TRUE;
- break;
- case GST_ITERATOR_OK:
- {
- GstPad *pad = GST_PAD_CAST (item);
-
- gint64 duration;
-
- /* ask sink peer for duration */
- res &= gst_pad_query_peer_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;
- }
- gst_object_unref (pad);
- break;
- }
- case GST_ITERATOR_RESYNC:
- max = -1;
- res = TRUE;
- gst_iterator_resync (it);
- break;
- default:
- res = FALSE;
- done = TRUE;
- break;
- }
- }
- gst_iterator_free (it);
-
- if (res) {
- /* and store the max */
- GST_DEBUG_OBJECT (adder, "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_adder_query_latency (GstAdder * adder, GstQuery * query)
-{
- GstClockTime min, max;
- gboolean live;
- gboolean res;
- GstIterator *it;
- gboolean done;
-
- res = TRUE;
- done = FALSE;
-
- live = FALSE;
- min = 0;
- max = GST_CLOCK_TIME_NONE;
-
- /* Take maximum of all latency values */
- it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
- while (!done) {
- GstIteratorResult ires;
-
- gpointer item;
-
- ires = gst_iterator_next (it, &item);
- switch (ires) {
- case GST_ITERATOR_DONE:
- done = TRUE;
- break;
- case GST_ITERATOR_OK:
- {
- GstPad *pad = GST_PAD_CAST (item);
- GstQuery *peerquery;
- GstClockTime min_cur, max_cur;
- gboolean live_cur;
-
- peerquery = gst_query_new_latency ();
-
- /* Ask peer for latency */
- res &= gst_pad_peer_query (pad, peerquery);
-
- /* take max from all valid return values */
- if (res) {
- gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
-
- if (min_cur > min)
- min = min_cur;
-
- if (max_cur != GST_CLOCK_TIME_NONE &&
- ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
- (max == GST_CLOCK_TIME_NONE)))
- max = max_cur;
-
- live = live || live_cur;
- }
-
- gst_query_unref (peerquery);
- gst_object_unref (pad);
- break;
- }
- case GST_ITERATOR_RESYNC:
- live = FALSE;
- min = 0;
- max = GST_CLOCK_TIME_NONE;
- res = TRUE;
- gst_iterator_resync (it);
- break;
- default:
- res = FALSE;
- done = TRUE;
- break;
- }
- }
- gst_iterator_free (it);
-
- if (res) {
- /* store the results */
- GST_DEBUG_OBJECT (adder, "Calculated total latency: live %s, min %"
- GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
- (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
- gst_query_set_latency (query, live, min, max);
- }
-
- return res;
-}
-
-static gboolean
-gst_adder_query (GstPad * pad, GstQuery * query)
-{
- GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
- gboolean res = FALSE;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_POSITION:
- {
- GstFormat format;
-
- gst_query_parse_position (query, &format, NULL);
-
- switch (format) {
- case GST_FORMAT_TIME:
- /* FIXME, bring to stream time, might be tricky */
- gst_query_set_position (query, format, adder->timestamp);
- res = TRUE;
- break;
- case GST_FORMAT_DEFAULT:
- gst_query_set_position (query, format, adder->offset);
- res = TRUE;
- break;
- default:
- break;
- }
- break;
- }
- case GST_QUERY_DURATION:
- res = gst_adder_query_duration (adder, query);
- break;
- case GST_QUERY_LATENCY:
- res = gst_adder_query_latency (adder, query);
- break;
- default:
- /* FIXME, needs a custom query handler because we have multiple
- * sinkpads */
- res = gst_pad_query_default (pad, query);
- break;
- }
-
- gst_object_unref (adder);
- return res;
-}
-
-typedef struct
-{
- GstEvent *event;
- gboolean flush;
-} EventData;
-
-static gboolean
-forward_event_func (GstPad * pad, GValue * ret, EventData * data)
-{
- GstEvent *event = data->event;
-
- gst_event_ref (event);
- GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
- if (!gst_pad_push_event (pad, event)) {
- GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
- event, GST_EVENT_TYPE_NAME (event));
- /* quick hack to unflush the pads, ideally we need a way to just unflush
- * this single collect pad */
- if (data->flush)
- gst_pad_send_event (pad, gst_event_new_flush_stop ());
- } else {
- g_value_set_boolean (ret, TRUE);
- GST_LOG_OBJECT (pad, "Sent event %p (%s).",
- event, GST_EVENT_TYPE_NAME (event));
- }
- gst_object_unref (pad);
-
- /* continue on other pads, even if one failed */
- return TRUE;
-}
-
-/* forwards the event to all sinkpads, takes ownership of the
- * event
- *
- * Returns: TRUE if the event could be forwarded on all
- * sinkpads.
- */
-static gboolean
-forward_event (GstAdder * adder, GstEvent * event, gboolean flush)
-{
- gboolean ret;
- GstIterator *it;
- GstIteratorResult ires;
- GValue vret = { 0 };
- EventData data;
-
- GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
- GST_EVENT_TYPE_NAME (event));
-
- data.event = event;
- data.flush = flush;
-
- g_value_init (&vret, G_TYPE_BOOLEAN);
- g_value_set_boolean (&vret, FALSE);
- it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
- while (TRUE) {
- ires = gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
- &vret, &data);
- switch (ires) {
- case GST_ITERATOR_RESYNC:
- GST_WARNING ("resync");
- gst_iterator_resync (it);
- g_value_set_boolean (&vret, TRUE);
- break;
- case GST_ITERATOR_OK:
- case GST_ITERATOR_DONE:
- ret = g_value_get_boolean (&vret);
- goto done;
- default:
- ret = FALSE;
- goto done;
- }
- }
-done:
- gst_iterator_free (it);
- GST_LOG_OBJECT (adder, "Forwarded event %p (%s), ret=%d", event,
- GST_EVENT_TYPE_NAME (event), ret);
- gst_event_unref (event);
-
- return ret;
-}
-
-static gboolean
-gst_adder_src_event (GstPad * pad, GstEvent * event)
-{
- GstAdder *adder;
- gboolean result;
-
- adder = GST_ADDER (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- {
- GstSeekFlags flags;
- GstSeekType curtype;
- gint64 cur;
- gboolean flush;
-
- /* parse the seek parameters */
- gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
- &cur, NULL, NULL);
-
- flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
-
- /* check if we are flushing */
- if (flush) {
- /* make sure we accept nothing anymore and return WRONG_STATE */
- gst_collect_pads_set_flushing (adder->collect, TRUE);
-
- /* flushing seek, start flush downstream, the flush will be done
- * when all pads received a FLUSH_STOP. */
- gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
- }
- GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
-
- /* now wait for the collected to be finished and mark a new
- * segment. After we have the lock, no collect function is running and no
- * new collect function will be called for as long as we're flushing. */
- GST_OBJECT_LOCK (adder->collect);
- if (curtype == GST_SEEK_TYPE_SET)
- adder->segment_position = cur;
- else
- adder->segment_position = 0;
- /* make sure we push a new segment, to inform about new basetime
- * see FIXME in gst_adder_collected() */
- adder->segment_pending = TRUE;
- if (flush) {
- /* Yes, we need to call _set_flushing again *WHEN* the streaming threads
- * have stopped so that the cookie gets properly updated. */
- gst_collect_pads_set_flushing (adder->collect, TRUE);
- }
- /* we might have a pending flush_stop event now. This event will either be
- * sent by an upstream element when it completes the seek or we will push
- * one in the collected callback ourself */
- adder->flush_stop_pending = flush;
- GST_OBJECT_UNLOCK (adder->collect);
- GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
- event);
-
- result = forward_event (adder, event, flush);
- if (!result) {
- /* seek failed. maybe source is a live source. */
- GST_DEBUG_OBJECT (adder, "seeking failed");
- }
- /* FIXME: ideally we would like to send a flush-stop event from here but
- * collectpads does not have a method that allows us to do that. Instead
- * we forward all flush-stop events we receive on the sinkpads. We might
- * be sending too many flush-stop events. */
- break;
- }
- case GST_EVENT_QOS:
- /* QoS might be tricky */
- result = FALSE;
- break;
- case GST_EVENT_NAVIGATION:
- /* navigation is rather pointless. */
- result = FALSE;
- break;
- default:
- /* just forward the rest for now */
- GST_DEBUG_OBJECT (adder, "forward unhandled event: %s",
- GST_EVENT_TYPE_NAME (event));
- result = forward_event (adder, event, FALSE);
- break;
- }
- gst_object_unref (adder);
-
- return result;
-}
-
-static gboolean
-gst_adder_sink_event (GstPad * pad, GstEvent * event)
-{
- GstAdder *adder;
- gboolean ret = TRUE;
-
- adder = GST_ADDER (gst_pad_get_parent (pad));
-
- GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
- GST_DEBUG_PAD_NAME (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_STOP:
- /* we received a flush-stop. The collect_event function will push the
- * event past our element. We simply forward all flush-stop events, even
- * when no flush-stop was pending, this is required because collectpads
- * does not provide an API to handle-but-not-forward the flush-stop.
- * We unset the pending flush-stop flag so that we don't send anymore
- * flush-stop from the collect function later.
- */
- GST_OBJECT_LOCK (adder->collect);
- adder->segment_pending = TRUE;
- adder->flush_stop_pending = FALSE;
- /* Clear pending tags */
- if (adder->pending_events) {
- g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
- g_list_free (adder->pending_events);
- adder->pending_events = NULL;
- }
- GST_OBJECT_UNLOCK (adder->collect);
- break;
- case GST_EVENT_TAG:
- GST_OBJECT_LOCK (adder->collect);
- /* collect tags here so we can push them out when we collect data */
- adder->pending_events = g_list_append (adder->pending_events, event);
- GST_OBJECT_UNLOCK (adder->collect);
- goto beach;
- default:
- break;
- }
-
- /* now GstCollectPads can take care of the rest, e.g. EOS */
- ret = adder->collect_event (pad, event);
-
-beach:
- gst_object_unref (adder);
- return ret;
-}
-
-static void
-gst_adder_class_init (GstAdderClass * klass)
-{
- GObjectClass *gobject_class = (GObjectClass *) klass;
- GstElementClass *gstelement_class = (GstElementClass *) klass;
-
- gobject_class->set_property = gst_adder_set_property;
- gobject_class->get_property = gst_adder_get_property;
- gobject_class->dispose = gst_adder_dispose;
-
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_adder_src_template));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_adder_sink_template));
- gst_element_class_set_details_simple (gstelement_class, "Adder",
- "Generic/Audio",
- "Add N audio channels together",
- "Thomas Vander Stichele <thomas at apestaart dot org>");
-
- parent_class = g_type_class_peek_parent (klass);
-
- /**
- * GstAdder:caps:
- *
- * Since: 0.10.24
- */
- g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
- g_param_spec_boxed ("caps", "Target caps",
- "Set target format for mixing (NULL means ANY). "
- "Setting this property takes a reference to the supplied GstCaps "
- "object.", GST_TYPE_CAPS,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_adder_request_new_pad);
- gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_adder_release_pad);
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_adder_change_state);
-}
-
-static void
-gst_adder_init (GstAdder * adder)
-{
- GstPadTemplate *template;
-
- template = gst_static_pad_template_get (&gst_adder_src_template);
- adder->srcpad = gst_pad_new_from_template (template, "src");
- gst_object_unref (template);
-
- gst_pad_set_getcaps_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
- gst_pad_set_setcaps_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_adder_setcaps));
- gst_pad_set_query_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_adder_query));
- gst_pad_set_event_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_adder_src_event));
- gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
-
- adder->format = GST_ADDER_FORMAT_UNSET;
- adder->padcount = 0;
- adder->func = NULL;
-
- adder->filter_caps = NULL;
-
- /* keep track of the sinkpads requested */
- adder->collect = gst_collect_pads_new ();
- gst_collect_pads_set_function (adder->collect,
- GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
- gst_collect_pads_set_clip_function (adder->collect,
- GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
-}
-
-static void
-gst_adder_dispose (GObject * object)
-{
- GstAdder *adder = GST_ADDER (object);
-
- if (adder->collect) {
- gst_object_unref (adder->collect);
- adder->collect = NULL;
- }
- gst_caps_replace (&adder->filter_caps, NULL);
- if (adder->pending_events) {
- g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
- g_list_free (adder->pending_events);
- adder->pending_events = NULL;
- }
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_adder_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstAdder *adder = GST_ADDER (object);
-
- switch (prop_id) {
- case PROP_FILTER_CAPS:{
- GstCaps *new_caps = NULL;
- GstCaps *old_caps;
- const GstCaps *new_caps_val = gst_value_get_caps (value);
-
- if (new_caps_val != NULL) {
- new_caps = (GstCaps *) new_caps_val;
- gst_caps_ref (new_caps);
- }
-
- GST_OBJECT_LOCK (adder);
- old_caps = adder->filter_caps;
- adder->filter_caps = new_caps;
- GST_OBJECT_UNLOCK (adder);
-
- if (old_caps)
- gst_caps_unref (old_caps);
-
- GST_DEBUG_OBJECT (adder, "set new caps %" GST_PTR_FORMAT, new_caps);
- break;
- }
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_adder_get_property (GObject * object, guint prop_id, GValue * value,
- GParamSpec * pspec)
-{
- GstAdder *adder = GST_ADDER (object);
-
- switch (prop_id) {
- case PROP_FILTER_CAPS:
- GST_OBJECT_LOCK (adder);
- gst_value_set_caps (value, adder->filter_caps);
- GST_OBJECT_UNLOCK (adder);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-
-static GstPad *
-gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
- const gchar * unused)
-{
- gchar *name;
- GstAdder *adder;
- GstPad *newpad;
- gint padcount;
-
- if (templ->direction != GST_PAD_SINK)
- goto not_sink;
-
- adder = GST_ADDER (element);
-
- /* increment pad counter */
- padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);
-
- name = g_strdup_printf ("sink%d", padcount);
- newpad = gst_pad_new_from_template (templ, name);
- GST_DEBUG_OBJECT (adder, "request new pad %s", name);
- g_free (name);
-
- gst_pad_set_getcaps_function (newpad,
- GST_DEBUG_FUNCPTR (gst_adder_sink_getcaps));
- gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_setcaps));
- gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
-
- /* FIXME: hacked way to override/extend the event function of
- * GstCollectPads; because it sets its own event function giving the
- * element no access to events */
- adder->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
- gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_event));
-
- /* takes ownership of the pad */
- if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
- goto could_not_add;
-
- return newpad;
-
- /* errors */
-not_sink:
- {
- g_warning ("gstadder: request new pad that is not a SINK pad\n");
- return NULL;
- }
-could_not_add:
- {
- GST_DEBUG_OBJECT (adder, "could not add pad");
- gst_collect_pads_remove_pad (adder->collect, newpad);
- gst_object_unref (newpad);
- return NULL;
- }
-}
-
-static void
-gst_adder_release_pad (GstElement * element, GstPad * pad)
-{
- GstAdder *adder;
-
- adder = GST_ADDER (element);
-
- GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
- gst_collect_pads_remove_pad (adder->collect, pad);
- gst_element_remove_pad (element, pad);
-}
-
-static GstBuffer *
-gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
- GstBuffer * buffer, gpointer user_data)
-{
- GstAdder *adder = GST_ADDER (user_data);
-
- buffer = gst_audio_buffer_clip (buffer, &data->segment, adder->rate,
- adder->bps);
-
- return buffer;
-}
-
-static GstFlowReturn
-gst_adder_collected (GstCollectPads * pads, gpointer user_data)
-{
- /*
- * combine streams by adding data values
- * basic algorithm :
- * - this function is called when all pads have a buffer
- * - get available bytes on all pads.
- * - repeat for each input pad :
- * - read available bytes, copy or add to target buffer
- * - if there's an EOS event, remove the input channel
- * - push out the output buffer
- *
- * todo:
- * - would be nice to have a mixing mode, where instead of adding we mix
- * - for float we could downscale after collect loop
- * - for int we need to downscale each input to avoid clipping or
- * mix into a temp (float) buffer and scale afterwards as well
- */
- GstAdder *adder;
- GSList *collected, *next = NULL;
- GstFlowReturn ret;
- GstBuffer *outbuf = NULL, *gapbuf = NULL;
- gpointer outdata = NULL;
- guint outsize;
-
- adder = GST_ADDER (user_data);
-
- /* this is fatal */
- if (G_UNLIKELY (adder->func == NULL))
- goto not_negotiated;
-
- if (adder->flush_stop_pending) {
- gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
- adder->flush_stop_pending = FALSE;
- }
-
- /* get available bytes for reading, this can be 0 which could mean empty
- * buffers or EOS, which we will catch when we loop over the pads. */
- outsize = gst_collect_pads_available (pads);
- /* can only happen when no pads to collect or all EOS */
- if (outsize == 0)
- goto eos;
-
- GST_LOG_OBJECT (adder,
- "starting to cycle through channels, %d bytes available (bps = %d)",
- outsize, adder->bps);
-
- for (collected = pads->data; collected; collected = next) {
- GstCollectData *collect_data;
- GstBuffer *inbuf;
- gboolean is_gap;
-
- /* take next to see if this is the last collectdata */
- next = g_slist_next (collected);
-
- collect_data = (GstCollectData *) collected->data;
-
- /* get a buffer of size bytes, if we get a buffer, it is at least outsize
- * bytes big. */
- inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize);
- /* NULL means EOS or an empty buffer so we still need to flush in
- * case of an empty buffer. */
- if (inbuf == NULL) {
- GST_LOG_OBJECT (adder, "channel %p: no bytes available", collect_data);
- continue;
- }
-
- is_gap = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP);
-
- /* Try to make an output buffer */
- if (outbuf == NULL) {
- /* if this is a gap buffer but we have some more pads to check, skip it.
- * If we are at the last buffer, take it, regardless if it is a GAP
- * buffer or not. */
- if (is_gap && next) {
- GST_DEBUG_OBJECT (adder, "skipping, non-last GAP buffer");
- /* we keep the GAP buffer, if we don't have anymore buffers (all pads
- * EOS, we can use this one as the output buffer. */
- if (gapbuf == NULL)
- gapbuf = inbuf;
- else
- gst_buffer_unref (inbuf);
- continue;
- }
-
- GST_LOG_OBJECT (adder, "channel %p: preparing output buffer of %d bytes",
- collect_data, outsize);
- /* make data and metadata writable, can simply return the inbuf when we
- * are the only one referencing this buffer. If this is the last (and
- * only) GAP buffer, it will automatically copy the GAP flag. */
- outbuf = gst_buffer_make_writable (inbuf);
- outdata = GST_BUFFER_DATA (outbuf);
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad));
- } else {
- if (!is_gap) {
- /* we had a previous output buffer, mix this non-GAP buffer */
- guint8 *indata;
- guint insize;
-
- indata = GST_BUFFER_DATA (inbuf);
- insize = GST_BUFFER_SIZE (inbuf);
-
- /* all buffers should have outsize, there are no short buffers because we
- * asked for the max size above */
- g_assert (insize == outsize);
-
- GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p",
- collect_data, insize, indata);
-
- /* further buffers, need to add them */
- adder->func ((gpointer) outdata, (gpointer) indata, insize);
- } else {
- /* skip gap buffer */
- GST_LOG_OBJECT (adder, "channel %p: skipping GAP buffer", collect_data);
- }
- gst_buffer_unref (inbuf);
- }
- }
-
- if (outbuf == NULL) {
- /* no output buffer, reuse one of the GAP buffers then if we have one */
- if (gapbuf) {
- GST_LOG_OBJECT (adder, "reusing GAP buffer %p", gapbuf);
- outbuf = gapbuf;
- } else
- /* assume EOS otherwise, this should not happen, really */
- goto eos;
- } else if (gapbuf)
- /* we had an output buffer, unref the gapbuffer we kept */
- gst_buffer_unref (gapbuf);
-
- /* our timestamping is very simple, just an ever incrementing
- * counter, the new segment time will take care of their respective
- * stream time. */
- if (adder->segment_pending) {
- GstEvent *event;
-
- /* FIXME, use rate/applied_rate as set on all sinkpads.
- * - currently we just set rate as received from last seek-event
- * We could potentially figure out the duration as well using
- * the current segment positions and the stated stop positions.
- * Also we just start from stream time 0 which is rather
- * weird. For non-synchronized mixing, the time should be
- * the min of the stream times of all received segments,
- * rationale being that the duration is at least going to
- * be as long as the earliest stream we start mixing. This
- * would also be correct for synchronized mixing but then
- * the later streams would be delayed until the stream times
- * match.
- */
- event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
- 1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position);
-
- if (event) {
- if (!gst_pad_push_event (adder->srcpad, event)) {
- GST_WARNING_OBJECT (adder->srcpad, "Sending event %p (%s) failed.",
- event, GST_EVENT_TYPE_NAME (event));
- }
- adder->segment_pending = FALSE;
- adder->segment_position = 0;
- } else {
- GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
- "start:%" G_GINT64_FORMAT " pos:%" G_GINT64_FORMAT " failed",
- adder->timestamp, adder->segment_position);
- }
- }
-
- if (G_UNLIKELY (adder->pending_events)) {
- GList *tmp = adder->pending_events;
-
- while (tmp) {
- GstEvent *ev = (GstEvent *) tmp->data;
-
- gst_pad_push_event (adder->srcpad, ev);
- tmp = g_list_next (tmp);
- }
- g_list_free (adder->pending_events);
- adder->pending_events = NULL;
- }
-
- /* set timestamps on the output buffer */
- GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
- GST_BUFFER_OFFSET (outbuf) = adder->offset;
-
- /* for the next timestamp, use the sample counter, which will
- * never accumulate rounding errors */
- adder->offset += outsize / adder->bps;
- adder->timestamp = gst_util_uint64_scale_int (adder->offset,
- GST_SECOND, adder->rate);
-
- /* now we can set the duration of the buffer */
- GST_BUFFER_DURATION (outbuf) = adder->timestamp -
- GST_BUFFER_TIMESTAMP (outbuf);
-
- /* send it out */
- GST_LOG_OBJECT (adder, "pushing outbuf %p, timestamp %" GST_TIME_FORMAT,
- outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
- ret = gst_pad_push (adder->srcpad, outbuf);
-
- GST_LOG_OBJECT (adder, "pushed outbuf, result = %s", gst_flow_get_name (ret));
-
- return ret;
-
- /* ERRORS */
-not_negotiated:
- {
- GST_ELEMENT_ERROR (adder, STREAM, FORMAT, (NULL),
- ("Unknown data received, not negotiated"));
- return GST_FLOW_NOT_NEGOTIATED;
- }
-eos:
- {
- GST_DEBUG_OBJECT (adder, "no data available, must be EOS");
- gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
- return GST_FLOW_UNEXPECTED;
- }
-}
-
-static GstStateChangeReturn
-gst_adder_change_state (GstElement * element, GstStateChange transition)
-{
- GstAdder *adder;
- GstStateChangeReturn ret;
-
- adder = GST_ADDER (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- adder->timestamp = 0;
- adder->offset = 0;
- adder->flush_stop_pending = FALSE;
- adder->segment_pending = TRUE;
- adder->segment_position = 0;
- adder->segment_rate = 1.0;
- gst_segment_init (&adder->segment, GST_FORMAT_UNDEFINED);
- gst_collect_pads_start (adder->collect);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- /* need to unblock the collectpads before calling the
- * parent change_state so that streaming can finish */
- gst_collect_pads_stop (adder->collect);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- default:
- break;
- }
-
- return ret;
-}
-
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- /*oil_init (); */
-
- if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "adder",
- "Adds multiple streams",
- plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)