summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier CrĂȘte <olivier.crete@collabora.com>2015-10-05 00:55:36 +0100
committerOlivier CrĂȘte <olivier.crete@collabora.com>2015-10-11 11:04:38 +0100
commit305e5c7ac3c6cebc0280de25b9736bcf058f973a (patch)
tree2ff9149a2f20d833e24dab60435d1cbf3f778fe2
parent657f1913c6c4b3b47424e33ef26d1b277792cd21 (diff)
downloadgstreamer-plugins-bad-305e5c7ac3c6cebc0280de25b9736bcf058f973a.tar.gz
liveadder: Remove plugin, replace by compat subclass of audiomixer
New subclass with a similar behaviour as the old liveadder, but a slightly different API as the latency is in nanoseconds, not milliseconds. Also, the new liveadder has a effective latency that is latency + output-buffer-duration. In practice, just setting a non-zero latency with the new audiomixer gives you the right behavior in 99% of the cases.
-rw-r--r--configure.ac2
-rw-r--r--docs/plugins/Makefile.am1
-rw-r--r--docs/plugins/gst-plugins-bad-plugins-docs.sgml1
-rw-r--r--docs/plugins/gst-plugins-bad-plugins-sections.txt9
-rw-r--r--docs/plugins/inspect/plugin-liveadder.xml34
-rw-r--r--gst/audiomixer/gstaudiomixer.c26
-rw-r--r--gst/liveadder/Makefile.am11
-rw-r--r--gst/liveadder/liveadder.c1538
-rw-r--r--gst/liveadder/liveadder.h91
9 files changed, 27 insertions, 1686 deletions
diff --git a/configure.ac b/configure.ac
index 0adb3b8a0..618f9c60c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -466,7 +466,6 @@ AG_GST_CHECK_PLUGIN(ivtc)
AG_GST_CHECK_PLUGIN(jp2kdecimator)
AG_GST_CHECK_PLUGIN(jpegformat)
AG_GST_CHECK_PLUGIN(librfb)
-AG_GST_CHECK_PLUGIN(liveadder)
AG_GST_CHECK_PLUGIN(midi)
AG_GST_CHECK_PLUGIN(mpegdemux)
AG_GST_CHECK_PLUGIN(mpegtsdemux)
@@ -3412,7 +3411,6 @@ gst/ivtc/Makefile
gst/jp2kdecimator/Makefile
gst/jpegformat/Makefile
gst/librfb/Makefile
-gst/liveadder/Makefile
gst/midi/Makefile
gst/mpegdemux/Makefile
gst/mpegtsdemux/Makefile
diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am
index b94cf9e96..7607b558f 100644
--- a/docs/plugins/Makefile.am
+++ b/docs/plugins/Makefile.am
@@ -150,7 +150,6 @@ EXTRA_HFILES = \
$(top_srcdir)/gst/geometrictransform/gstwaterripple.h \
$(top_srcdir)/gst/ivfparse/gstivfparse.h \
$(top_srcdir)/gst/jpegformat/gstjpegparse.h \
- $(top_srcdir)/gst/liveadder/liveadder.h \
$(top_srcdir)/gst/mxf/mxfdemux.h \
$(top_srcdir)/gst/mxf/mxfmux.h \
$(top_srcdir)/gst/nuvdemux/gstnuvdemux.h \
diff --git a/docs/plugins/gst-plugins-bad-plugins-docs.sgml b/docs/plugins/gst-plugins-bad-plugins-docs.sgml
index 51278e9f5..bd25df380 100644
--- a/docs/plugins/gst-plugins-bad-plugins-docs.sgml
+++ b/docs/plugins/gst-plugins-bad-plugins-docs.sgml
@@ -132,7 +132,6 @@
<xi:include href="xml/plugin-geometrictransform.xml" />
<xi:include href="xml/plugin-gsm.xml" />
<xi:include href="xml/plugin-jpegformat.xml" />
- <xi:include href="xml/plugin-liveadder.xml" />
<xi:include href="xml/plugin-mimic.xml" />
<xi:include href="xml/plugin-mms.xml" />
<xi:include href="xml/plugin-modplug.xml" />
diff --git a/docs/plugins/gst-plugins-bad-plugins-sections.txt b/docs/plugins/gst-plugins-bad-plugins-sections.txt
index 5e869c820..a289e226d 100644
--- a/docs/plugins/gst-plugins-bad-plugins-sections.txt
+++ b/docs/plugins/gst-plugins-bad-plugins-sections.txt
@@ -720,15 +720,7 @@ gst_jpeg_parse_get_type
GstLiveAdder
<SUBSECTION Standard>
GstLiveAdderClass
-GST_IS_LIVE_ADDER
-GST_IS_LIVE_ADDER_CLASS
-GST_IS_MXF_DEMUX_PAD
-GST_LIVE_ADDER
-GST_LIVE_ADDER_CLASS
-GST_LIVE_ADDER_GET_CLASS
GST_TYPE_LIVE_ADDER
-GstLiveAdderFormat
-GstLiveAdderFunction
gst_live_adder_get_type
</SECTION>
@@ -1008,6 +1000,7 @@ GST_MXF_DEMUX
GST_MXF_DEMUX_CLASS
GST_TYPE_MXF_DEMUX
gst_mxf_demux_get_type
+GST_IS_MXF_DEMUX_PAD
</SECTION>
<SECTION>
diff --git a/docs/plugins/inspect/plugin-liveadder.xml b/docs/plugins/inspect/plugin-liveadder.xml
deleted file mode 100644
index b48d0602d..000000000
--- a/docs/plugins/inspect/plugin-liveadder.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<plugin>
- <name>liveadder</name>
- <description>Adds multiple live discontinuous streams</description>
- <filename>../../gst/liveadder/.libs/libgstliveadder.so</filename>
- <basename>libgstliveadder.so</basename>
- <version>1.6.0</version>
- <license>LGPL</license>
- <source>gst-plugins-bad</source>
- <package>GStreamer Bad Plug-ins source release</package>
- <origin>Unknown package origin</origin>
- <elements>
- <element>
- <name>liveadder</name>
- <longname>Live Adder element</longname>
- <class>Generic/Audio</class>
- <description>Mixes live/discontinuous audio streams</description>
- <author>Olivier Crete &lt;olivier.crete@collabora.co.uk&gt;</author>
- <pads>
- <caps>
- <name>sink_%u</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>audio/x-raw, format=(string){ S8, U8, S16LE, U16LE, S32LE, U32LE, F32LE, F64LE }, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>audio/x-raw, format=(string){ S8, U8, S16LE, U16LE, S32LE, U32LE, F32LE, F64LE }, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]</details>
- </caps>
- </pads>
- </element>
- </elements>
-</plugin> \ No newline at end of file
diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c
index 3966a8fc2..d3ddcdf4f 100644
--- a/gst/audiomixer/gstaudiomixer.c
+++ b/gst/audiomixer/gstaudiomixer.c
@@ -781,6 +781,28 @@ gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data)
iface->get_children_count = gst_audiomixer_child_proxy_get_children_count;
}
+/* Empty liveadder alias with non-zero latency */
+
+typedef GstAudioMixer GstLiveAdder;
+typedef GstAudioMixerClass GstLiveAdderClass;
+
+static GType gst_live_adder_get_type (void);
+#define GST_TYPE_LIVE_ADDER gst_live_adder_get_type ()
+
+G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_AUDIO_MIXER);
+
+static void
+gst_live_adder_init (GstLiveAdder * self)
+{
+ g_object_set (self, "latency", 30 * GST_MSECOND, NULL);
+}
+
+static void
+gst_live_adder_class_init (GstLiveAdderClass * klass)
+{
+}
+
+
static gboolean
plugin_init (GstPlugin * plugin)
{
@@ -791,6 +813,10 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_AUDIO_MIXER))
return FALSE;
+ if (!gst_element_register (plugin, "liveadder", GST_RANK_NONE,
+ GST_TYPE_LIVE_ADDER))
+ return FALSE;
+
if (!gst_element_register (plugin, "audiointerleave", GST_RANK_NONE,
GST_TYPE_AUDIO_INTERLEAVE))
return FALSE;
diff --git a/gst/liveadder/Makefile.am b/gst/liveadder/Makefile.am
deleted file mode 100644
index e6f5d44fe..000000000
--- a/gst/liveadder/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-plugin_LTLIBRARIES = libgstliveadder.la
-
-libgstliveadder_la_SOURCES = liveadder.c
-libgstliveadder_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
-libgstliveadder_la_LIBADD = \
- $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \
- $(GST_BASE_LIBS) $(GST_LIBS)
-libgstliveadder_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-libgstliveadder_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
-
-noinst_HEADERS = liveadder.h
diff --git a/gst/liveadder/liveadder.c b/gst/liveadder/liveadder.c
deleted file mode 100644
index a66041980..000000000
--- a/gst/liveadder/liveadder.c
+++ /dev/null
@@ -1,1538 +0,0 @@
-/*
- * GStreamer
- *
- * Copyright 2012 Collabora Ltd
- * Copyright 2008 Nokia Corporation
- * @author: Olivier Crete <olivier.crete@collabora.co.uk>
- *
- * With parts copied from the adder plugin which is
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2001 Thomas <thomas@apestaart.org>
- * 2005,2006 Wim Taymans <wim@fluendo.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-/**
- * SECTION:element-liveadder
- * @see_also: adder
- *
- * The live 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.
- *
- * Unlike the adder, the liveadder mixes the streams according the their
- * timestamps and waits for some milli-seconds before trying doing the mixing.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "liveadder.h"
-
-#include <gst/audio/audio.h>
-
-#include <string.h>
-
-#define DEFAULT_LATENCY_MS 60
-
-GST_DEBUG_CATEGORY_STATIC (live_adder_debug);
-#define GST_CAT_DEFAULT (live_adder_debug)
-
-static GstStaticPadTemplate gst_live_adder_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink_%u",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE ("{ S8, U8, "
- GST_AUDIO_NE (S16) "," GST_AUDIO_NE (U16) ","
- GST_AUDIO_NE (S32) "," GST_AUDIO_NE (U32) ","
- GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "}"))
- );
-
-static GstStaticPadTemplate gst_live_adder_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE ("{ S8, U8, "
- GST_AUDIO_NE (S16) "," GST_AUDIO_NE (U16) ","
- GST_AUDIO_NE (S32) "," GST_AUDIO_NE (U32) ","
- GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "}"))
- );
-
-/* Valve signals and args */
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-enum
-{
- PROP_0,
- PROP_LATENCY,
-};
-
-typedef struct _GstLiveAdderPadPrivate
-{
- GstSegment segment;
- gboolean eos;
-
- GstClockTime expected_timestamp;
-
-} GstLiveAdderPadPrivate;
-
-G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_ELEMENT);
-
-static void gst_live_adder_finalize (GObject * object);
-static void
-gst_live_adder_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void
-gst_live_adder_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstPad *gst_live_adder_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
-static void gst_live_adder_release_pad (GstElement * element, GstPad * pad);
-static GstStateChangeReturn
-gst_live_adder_change_state (GstElement * element, GstStateChange transition);
-
-static gboolean gst_live_adder_setcaps (GstLiveAdder * adder, GstPad * pad,
- GstCaps * caps);
-static GstCaps *gst_live_adder_sink_getcaps (GstLiveAdder * adder, GstPad * pad,
- GstCaps * filter);
-static gboolean gst_live_adder_src_activate_mode (GstPad * pad,
- GstObject * parent, GstPadMode mode, gboolean active);
-static gboolean gst_live_adder_src_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
-
-static void gst_live_adder_loop (gpointer data);
-static gboolean gst_live_adder_src_query (GstPad * pad, GstObject * parent,
- GstQuery * query);
-static gboolean gst_live_adder_sink_query (GstPad * pad, GstObject * parent,
- GstQuery * query);
-static gboolean gst_live_adder_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
-
-
-static void reset_pad_private (GstPad * pad);
-
-/* clipping versions */
-#define MAKE_FUNC(name,type,ttype,min,max) \
-static void name (type *out, type *in, gint bytes) { \
- gint i; \
- for (i = 0; i < bytes / sizeof (type); i++) \
- out[i] = CLAMP ((ttype)out[i] + (ttype)in[i], min, max); \
-}
-
-/* non-clipping versions (for float) */
-#define MAKE_FUNC_NC(name,type,ttype) \
-static void name (type *out, type *in, gint bytes) { \
- gint i; \
- for (i = 0; i < bytes / sizeof (type); i++) \
- out[i] = (ttype)out[i] + (ttype)in[i]; \
-}
-
-/* *INDENT-OFF* */
-MAKE_FUNC (add_int32, gint32, gint64, G_MININT32, G_MAXINT32)
-MAKE_FUNC (add_int16, gint16, gint32, G_MININT16, G_MAXINT16)
-MAKE_FUNC (add_int8, gint8, gint16, G_MININT8, G_MAXINT8)
-MAKE_FUNC (add_uint32, guint32, guint64, 0, G_MAXUINT32)
-MAKE_FUNC (add_uint16, guint16, guint32, 0, G_MAXUINT16)
-MAKE_FUNC (add_uint8, guint8, guint16, 0, G_MAXUINT8)
-MAKE_FUNC_NC (add_float64, gdouble, gdouble)
-MAKE_FUNC_NC (add_float32, gfloat, gfloat)
-/* *INDENT-ON* */
-
-
-static void
-gst_live_adder_class_init (GstLiveAdderClass * klass)
-{
- GObjectClass *gobject_class = (GObjectClass *) klass;
- GstElementClass *gstelement_class = (GstElementClass *) klass;
-
- GST_DEBUG_CATEGORY_INIT (live_adder_debug, "liveadder", 0, "Live Adder");
-
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_live_adder_src_template));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_live_adder_sink_template));
- gst_element_class_set_static_metadata (gstelement_class, "Live Adder element",
- "Generic/Audio",
- "Mixes live/discontinuous audio streams",
- "Olivier Crete <olivier.crete@collabora.co.uk>");
-
- gobject_class->finalize = gst_live_adder_finalize;
- gobject_class->set_property = gst_live_adder_set_property;
- gobject_class->get_property = gst_live_adder_get_property;
-
- gstelement_class->request_new_pad = gst_live_adder_request_new_pad;
- gstelement_class->release_pad = gst_live_adder_release_pad;
- gstelement_class->change_state = gst_live_adder_change_state;
-
- g_object_class_install_property (gobject_class, PROP_LATENCY,
- g_param_spec_uint ("latency", "Buffering latency",
- "Amount of data to buffer (in milliseconds)",
- 0, G_MAXUINT, DEFAULT_LATENCY_MS,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-}
-
-static void
-gst_live_adder_init (GstLiveAdder * adder)
-{
- adder->srcpad =
- gst_pad_new_from_static_template (&gst_live_adder_src_template, "src");
- gst_pad_set_query_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_live_adder_src_query));
- gst_pad_set_event_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_live_adder_src_event));
- gst_pad_set_activatemode_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_live_adder_src_activate_mode));
- gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
-
- adder->padcount = 0;
- adder->func = NULL;
- g_cond_init (&adder->not_empty_cond);
-
- adder->next_timestamp = GST_CLOCK_TIME_NONE;
-
- adder->latency_ms = DEFAULT_LATENCY_MS;
-
- adder->buffers = g_queue_new ();
-}
-
-
-static void
-gst_live_adder_finalize (GObject * object)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (object);
-
- g_cond_clear (&adder->not_empty_cond);
-
- g_queue_foreach (adder->buffers, (GFunc) gst_mini_object_unref, NULL);
- while (g_queue_pop_head (adder->buffers)) {
- }
- g_queue_free (adder->buffers);
-
- g_list_free (adder->sinkpads);
-
- G_OBJECT_CLASS (gst_live_adder_parent_class)->finalize (object);
-}
-
-
-static void
-gst_live_adder_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (object);
-
- switch (prop_id) {
- case PROP_LATENCY:
- {
- guint64 new_latency, old_latency;
-
- new_latency = g_value_get_uint (value);
-
- GST_OBJECT_LOCK (adder);
- old_latency = adder->latency_ms;
- adder->latency_ms = new_latency;
- GST_OBJECT_UNLOCK (adder);
-
- /* post message if latency changed, this will inform the parent pipeline
- * that a latency reconfiguration is possible/needed. */
- if (new_latency != old_latency) {
- GST_DEBUG_OBJECT (adder, "latency changed to: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (new_latency));
-
- gst_element_post_message (GST_ELEMENT_CAST (adder),
- gst_message_new_latency (GST_OBJECT_CAST (adder)));
- }
- 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)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (object);
-
- switch (prop_id) {
- case PROP_LATENCY:
- GST_OBJECT_LOCK (adder);
- g_value_set_uint (value, adder->latency_ms);
- GST_OBJECT_UNLOCK (adder);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-
-/* we can only accept caps that we and downstream can handle. */
-static GstCaps *
-gst_live_adder_sink_getcaps (GstLiveAdder * adder, GstPad * pad,
- GstCaps * filter)
-{
- GstCaps *result, *peercaps, *sinkcaps;
-
- /* get the downstream possible caps */
- peercaps = gst_pad_peer_query_caps (adder->srcpad, filter);
- /* 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_current_caps (pad);
- if (!sinkcaps)
- sinkcaps = gst_pad_get_pad_template_caps (pad);
- if (peercaps) {
- /* if the peer has caps, intersect */
- GST_DEBUG_OBJECT (adder, "intersecting peer and template caps");
- result = gst_caps_intersect (peercaps, sinkcaps);
- gst_caps_unref (sinkcaps);
- gst_caps_unref (peercaps);
- } else {
- /* the peer has no caps (or there is no peer), just use the allowed caps
- * of this sinkpad. */
- GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
- result = sinkcaps;
- }
-
- return result;
-}
-
-struct SetCapsIterCtx
-{
- GstPad *pad;
- GstCaps *caps;
- gboolean all_valid;
-};
-
-static void
-check_other_caps (const GValue * item, gpointer user_data)
-{
- GstPad *otherpad = GST_PAD (g_value_get_object (item));
- struct SetCapsIterCtx *ctx = user_data;
-
- if (otherpad == ctx->pad)
- return;
-
- if (!gst_pad_peer_query_accept_caps (otherpad, ctx->caps))
- ctx->all_valid = FALSE;
-}
-
-static void
-set_other_caps (const GValue * item, gpointer user_data)
-{
- GstPad *otherpad = GST_PAD (g_value_get_object (item));
- struct SetCapsIterCtx *ctx = user_data;
-
- if (otherpad == ctx->pad)
- return;
-
- if (!gst_pad_set_caps (otherpad, ctx->caps))
- ctx->all_valid = FALSE;
-}
-
-/* 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_live_adder_setcaps (GstLiveAdder * adder, GstPad * pad, GstCaps * caps)
-{
- GstIterator *iter;
- struct SetCapsIterCtx ctx;
-
- 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. */
- iter = gst_element_iterate_sink_pads (GST_ELEMENT (adder));
- ctx.pad = pad;
- ctx.caps = caps;
- ctx.all_valid = TRUE;
- while (gst_iterator_foreach (iter, check_other_caps, &ctx) ==
- GST_ITERATOR_RESYNC) {
- ctx.all_valid = TRUE;
- gst_iterator_resync (iter);
- }
- if (!ctx.all_valid) {
- GST_WARNING_OBJECT (adder, "Caps are not acceptable by other sinkpads");
- gst_iterator_free (iter);
- return FALSE;
- }
-
- while (gst_iterator_foreach (iter, set_other_caps, &ctx) ==
- GST_ITERATOR_RESYNC) {
- ctx.all_valid = TRUE;
- gst_iterator_resync (iter);
- }
- gst_iterator_free (iter);
-
- if (!ctx.all_valid) {
- GST_WARNING_OBJECT (adder, "Could not set caps on the other sink pads");
- return FALSE;
- }
-
- if (!gst_pad_set_caps (adder->srcpad, caps)) {
- GST_WARNING_OBJECT (adder, "Could not set caps downstream");
- return FALSE;
- }
-
- GST_OBJECT_LOCK (adder);
- /* parse caps now */
- if (!gst_audio_info_from_caps (&adder->info, caps))
- goto not_supported;
-
- if (GST_AUDIO_INFO_IS_INTEGER (&adder->info)) {
- switch (GST_AUDIO_INFO_WIDTH (&adder->info)) {
- case 8:
- adder->func = GST_AUDIO_INFO_IS_SIGNED (&adder->info) ?
- (GstLiveAdderFunction) add_int8 : (GstLiveAdderFunction) add_uint8;
- break;
- case 16:
- adder->func = GST_AUDIO_INFO_IS_SIGNED (&adder->info) ?
- (GstLiveAdderFunction) add_int16 : (GstLiveAdderFunction)
- add_uint16;
- break;
- case 32:
- adder->func = GST_AUDIO_INFO_IS_SIGNED (&adder->info) ?
- (GstLiveAdderFunction) add_int32 : (GstLiveAdderFunction)
- add_uint32;
- break;
- default:
- goto not_supported;
- }
- } else if (GST_AUDIO_INFO_IS_FLOAT (&adder->info)) {
- switch (GST_AUDIO_INFO_WIDTH (&adder->info)) {
- case 32:
- adder->func = (GstLiveAdderFunction) add_float32;
- break;
- case 64:
- adder->func = (GstLiveAdderFunction) add_float64;
- break;
- default:
- goto not_supported;
- }
- } else {
- goto not_supported;
- }
-
- GST_OBJECT_UNLOCK (adder);
- return TRUE;
-
- /* ERRORS */
-not_supported:
- {
- GST_OBJECT_UNLOCK (adder);
- GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
- return FALSE;
- }
-}
-
-static void
-gst_live_adder_flush_start (GstLiveAdder * adder)
-{
- GST_DEBUG_OBJECT (adder, "Disabling pop on queue");
-
- GST_OBJECT_LOCK (adder);
- /* mark ourselves as flushing */
- adder->srcresult = GST_FLOW_FLUSHING;
-
- /* Empty the queue */
- g_queue_foreach (adder->buffers, (GFunc) gst_mini_object_unref, NULL);
- while (g_queue_pop_head (adder->buffers));
-
- /* unlock clock, we just unschedule, the entry will be released by the
- * locking streaming thread. */
- if (adder->clock_id)
- gst_clock_id_unschedule (adder->clock_id);
-
- g_cond_broadcast (&adder->not_empty_cond);
- GST_OBJECT_UNLOCK (adder);
-}
-
-static gboolean
-gst_live_adder_src_activate_mode (GstPad * pad, GstObject * parent,
- GstPadMode mode, gboolean active)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- gboolean result = TRUE;
-
- if (mode == GST_PAD_MODE_PULL)
- return FALSE;
-
- if (active) {
- /* Mark as non flushing */
- GST_OBJECT_LOCK (adder);
- adder->srcresult = GST_FLOW_OK;
- GST_OBJECT_UNLOCK (adder);
-
- /* start pushing out buffers */
- GST_DEBUG_OBJECT (adder, "Starting task on srcpad");
- gst_pad_start_task (adder->srcpad,
- (GstTaskFunction) gst_live_adder_loop, adder, NULL);
- } else {
- /* make sure all data processing stops ASAP */
- gst_live_adder_flush_start (adder);
-
- /* NOTE this will hardlock if the state change is called from the src pad
- * task thread because we will _join() the thread. */
- GST_DEBUG_OBJECT (adder, "Stopping task on srcpad");
- result = gst_pad_stop_task (pad);
- }
-
- return result;
-}
-
-static gboolean
-gst_live_adder_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- GstLiveAdderPadPrivate *padprivate = NULL;
- gboolean ret = TRUE;
-
- padprivate = gst_pad_get_element_private (pad);
-
- if (!padprivate)
- return FALSE;
-
- GST_LOG_OBJECT (adder, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_CAPS:
- {
- GstCaps *caps;
-
- gst_event_parse_caps (event, &caps);
- ret = gst_live_adder_setcaps (adder, pad, caps);
- gst_event_unref (event);
- break;
- }
- case GST_EVENT_SEGMENT:
- {
- const GstSegment *segment;
- GstSegment livesegment;
-
- gst_event_parse_segment (event, &segment);
-
- /* we need time for now */
- if (segment->format != GST_FORMAT_TIME)
- goto newseg_wrong_format;
-
- /* now configure the values, we need these to time the release of the
- * buffers on the srcpad. */
- GST_OBJECT_LOCK (adder);
- gst_segment_copy_into (segment, &padprivate->segment);
- GST_OBJECT_UNLOCK (adder);
- gst_event_unref (event);
-
- gst_segment_init (&livesegment, GST_FORMAT_TIME);
- gst_pad_push_event (adder->srcpad, gst_event_new_segment (&livesegment));
- break;
- }
- case GST_EVENT_FLUSH_START:
- gst_live_adder_flush_start (adder);
- ret = gst_pad_push_event (adder->srcpad, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- GST_OBJECT_LOCK (adder);
- adder->next_timestamp = GST_CLOCK_TIME_NONE;
- reset_pad_private (pad);
- GST_OBJECT_UNLOCK (adder);
- ret = gst_pad_push_event (adder->srcpad, event);
- ret &=
- gst_live_adder_src_activate_mode (adder->srcpad, GST_OBJECT (adder),
- GST_PAD_MODE_PUSH, TRUE);
- break;
- case GST_EVENT_EOS:
- {
- GST_OBJECT_LOCK (adder);
-
- ret = adder->srcresult == GST_FLOW_OK;
- if (ret && !padprivate->eos) {
- GST_DEBUG_OBJECT (adder, "queuing EOS");
- padprivate->eos = TRUE;
- g_cond_broadcast (&adder->not_empty_cond);
- } else if (padprivate->eos) {
- GST_DEBUG_OBJECT (adder, "dropping EOS, we are already EOS");
- } else {
- GST_DEBUG_OBJECT (adder, "dropping EOS, reason %s",
- gst_flow_get_name (adder->srcresult));
- }
-
- GST_OBJECT_UNLOCK (adder);
-
- gst_event_unref (event);
- break;
- }
- default:
- ret = gst_pad_push_event (adder->srcpad, event);
- break;
- }
-
-done:
-
- return ret;
-
- /* ERRORS */
-newseg_wrong_format:
- {
- GST_DEBUG_OBJECT (adder, "received non TIME segment");
- ret = FALSE;
- goto done;
- }
-}
-
-static gboolean
-gst_live_adder_query_pos_dur (GstLiveAdder * adder, GstFormat format,
- gboolean position, gint64 * outvalue)
-{
- GValue item = { 0 };
- gint64 max = G_MININT64;
- gboolean res = TRUE;
- GstIterator *it;
- gboolean done = FALSE;
-
- it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
- while (!done) {
- switch (gst_iterator_next (it, &item)) {
- case GST_ITERATOR_DONE:
- done = TRUE;
- break;
- case GST_ITERATOR_OK:
- {
- GstPad *pad = GST_PAD_CAST (g_value_get_object (&item));
- gint64 value;
- gboolean curres;
-
- /* ask sink peer for duration */
- if (position)
- curres = gst_pad_peer_query_position (pad, format, &value);
- else
- curres = gst_pad_peer_query_duration (pad, format, &value);
-
- /* take max from all valid return values */
- /* Only if the format is the one we requested, otherwise ignore it ?
- */
-
- if (curres) {
- res &= curres;
-
- /* valid unknown length, stop searching */
- if (value == -1) {
- max = value;
- done = TRUE;
- } else if (value > max) {
- max = value;
- }
- }
- g_value_reset (&item);
- break;
- }
- case GST_ITERATOR_RESYNC:
- max = -1;
- res = TRUE;
- break;
- default:
- res = FALSE;
- done = TRUE;
- break;
- }
- }
-
- g_value_unset (&item);
- gst_iterator_free (it);
-
- if (res)
- *outvalue = max;
-
- return res;
-}
-
-/* FIXME:
- *
- * 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_live_adder_query_duration (GstLiveAdder * adder, GstQuery * query)
-{
- GstFormat format;
- gint64 max;
- gboolean res;
-
- /* parse format */
- gst_query_parse_duration (query, &format, NULL);
-
- res = gst_live_adder_query_pos_dur (adder, format, FALSE, &max);
-
- if (res) {
- /* and store the max */
- gst_query_set_duration (query, format, max);
- }
-
- return res;
-}
-
-static gboolean
-gst_live_adder_query_position (GstLiveAdder * adder, GstQuery * query)
-{
- GstFormat format;
- gint64 max;
- gboolean res;
-
- /* parse format */
- gst_query_parse_position (query, &format, NULL);
-
- res = gst_live_adder_query_pos_dur (adder, format, TRUE, &max);
-
- if (res) {
- /* and store the max */
- gst_query_set_position (query, format, max);
- }
-
- return res;
-}
-
-
-
-static gboolean
-gst_live_adder_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- gboolean res = FALSE;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- {
- /* We need to send the query upstream and add the returned latency to our
- * own */
- res = gst_pad_query_default (pad, parent, query);
-
- if (res) {
- GstClockTime my_latency = adder->latency_ms * GST_MSECOND;
- GstClockTime min_latency, max_latency;
- gboolean live;
-
- gst_query_parse_latency (query, &live, &min_latency, &max_latency);
-
- GST_OBJECT_LOCK (adder);
- adder->peer_latency = min_latency;
- min_latency += my_latency;
- GST_OBJECT_UNLOCK (adder);
-
- /* Make sure we don't risk an overflow */
- if (max_latency < G_MAXUINT64 - my_latency)
- max_latency += my_latency;
- else
- max_latency = G_MAXUINT64;
- gst_query_set_latency (query, TRUE, min_latency, max_latency);
- GST_DEBUG_OBJECT (adder, "Calculated total latency : min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
- }
- break;
- }
- case GST_QUERY_DURATION:
- res = gst_live_adder_query_duration (adder, query);
- break;
- case GST_QUERY_POSITION:
- res = gst_live_adder_query_position (adder, query);
- break;
- default:
- res = gst_pad_query_default (pad, parent, query);
- break;
- }
-
- return res;
-}
-
-static gboolean
-gst_live_adder_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- gboolean res;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_CAPS:
- {
- GstCaps *filter;
- GstCaps *result;
-
- gst_query_parse_caps (query, &filter);
- result = gst_live_adder_sink_getcaps (adder, pad, filter);
- gst_query_set_caps_result (query, result);
- gst_caps_unref (result);
- res = TRUE;
- break;
- }
- default:
- res = gst_pad_query_default (pad, parent, query);
- break;
- }
-
- return res;
-}
-
-static gboolean
-forward_event_func (const GValue * item, GValue * ret, gpointer user_data)
-{
- GstPad *pad = GST_PAD (g_value_get_object (item));
- GstEvent *event = user_data;
-
- 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)) {
- g_value_set_boolean (ret, FALSE);
- GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
- event, GST_EVENT_TYPE_NAME (event));
- } else {
- GST_LOG_OBJECT (pad, "Sent event %p (%s).",
- event, GST_EVENT_TYPE_NAME (event));
- }
- 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 (GstLiveAdder * adder, GstEvent * event)
-{
- GstIterator *it;
- GValue vret = { 0 };
-
- GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
- GST_EVENT_TYPE_NAME (event));
-
- g_value_init (&vret, G_TYPE_BOOLEAN);
- g_value_set_boolean (&vret, TRUE);
- it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
- gst_iterator_fold (it, forward_event_func, &vret, event);
- gst_iterator_free (it);
-
- return g_value_get_boolean (&vret);
-}
-
-
-static gboolean
-gst_live_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- gboolean result;
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_QOS:
- /* TODO : QoS might be tricky */
- result = FALSE;
- break;
- case GST_EVENT_NAVIGATION:
- /* TODO : navigation is rather pointless. */
- result = FALSE;
- break;
- default:
- /* just forward the rest for now */
- result = forward_event (adder, event);
- break;
- }
-
- gst_event_unref (event);
-
- return result;
-}
-
-static guint
-gst_live_adder_length_from_duration (GstLiveAdder * adder,
- GstClockTime duration)
-{
- guint64 ret = GST_AUDIO_INFO_BPF (&adder->info) *
- gst_util_uint64_scale_int_round (duration,
- GST_AUDIO_INFO_RATE (&adder->info), GST_SECOND);
-
- return (guint) ret;
-}
-
-static GstClockTime
-gst_live_adder_length_to_duration (GstLiveAdder * adder, guint size)
-{
- return GST_FRAMES_TO_CLOCK_TIME ((size /
- GST_AUDIO_INFO_BPF (&adder->info)),
- GST_AUDIO_INFO_RATE (&adder->info));
-}
-
-static GstFlowReturn
-gst_live_adder_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (parent);
- GstLiveAdderPadPrivate *padprivate = NULL;
- GstFlowReturn ret = GST_FLOW_OK;
- GList *item = NULL;
- GstClockTime skip = 0;
- gint64 drift = 0; /* Positive if new buffer after old buffer */
- gsize buffer_size = 0;
- gsize subbuffer_size = 0;
- gsize offset = 0;
-
- GST_OBJECT_LOCK (adder);
-
- ret = adder->srcresult;
-
- GST_DEBUG ("Incoming buffer time:%" GST_TIME_FORMAT " duration:%"
- GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
-
- if (ret != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (adder, "Passing non-ok result from src: %s",
- gst_flow_get_name (ret));
- gst_buffer_unref (buffer);
- goto out;
- }
-
- padprivate = gst_pad_get_element_private (pad);
-
- if (!padprivate) {
- ret = GST_FLOW_NOT_LINKED;
- gst_buffer_unref (buffer);
- goto out;
- }
-
- if (padprivate->eos) {
- GST_DEBUG_OBJECT (adder, "Received buffer after EOS");
- ret = GST_FLOW_EOS;
- gst_buffer_unref (buffer);
- goto out;
- }
-
- if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
- goto invalid_timestamp;
-
- if (padprivate->segment.format == GST_FORMAT_UNDEFINED) {
- GST_WARNING_OBJECT (adder, "No new-segment received,"
- " initializing segment with time 0..-1");
- gst_segment_init (&padprivate->segment, GST_FORMAT_TIME);
- }
-
- buffer = gst_buffer_make_writable (buffer);
-
- drift = GST_BUFFER_TIMESTAMP (buffer) - padprivate->expected_timestamp;
-
- /* Just see if we receive invalid timestamp/durations */
- if (GST_CLOCK_TIME_IS_VALID (padprivate->expected_timestamp) &&
- !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT) &&
- (drift != 0)) {
- GST_LOG_OBJECT (adder,
- "Timestamp discontinuity without the DISCONT flag set"
- " (expected %" GST_TIME_FORMAT ", got %" GST_TIME_FORMAT
- " drift:%" G_GINT64_FORMAT "ms)",
- GST_TIME_ARGS (padprivate->expected_timestamp),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), drift / GST_MSECOND);
-
- /* We accept drifts of 10ms */
- if (ABS (drift) < (10 * GST_MSECOND)) {
- GST_DEBUG ("Correcting minor drift");
- GST_BUFFER_TIMESTAMP (buffer) = padprivate->expected_timestamp;
- }
- }
-
-
- /* If there is no duration, lets set one */
- if (!GST_BUFFER_DURATION_IS_VALID (buffer)) {
- GST_BUFFER_DURATION (buffer) = (gst_buffer_get_size (buffer) * GST_SECOND) /
- (GST_AUDIO_INFO_BPF (&adder->info) *
- GST_AUDIO_INFO_RATE (&adder->info));
- padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
- } else {
- padprivate->expected_timestamp = GST_BUFFER_TIMESTAMP (buffer) +
- GST_BUFFER_DURATION (buffer);
- }
-
-
- /*
- * Lets clip the buffer to the segment (so we don't have to worry about
- * cliping afterwards).
- * This should also guarantee us that we'll have valid timestamps and
- * durations afterwards
- */
-
- buffer = gst_audio_buffer_clip (buffer, &padprivate->segment,
- GST_AUDIO_INFO_RATE (&adder->info), GST_AUDIO_INFO_BPF (&adder->info));
-
- /* buffer can be NULL if it's completely outside of the segment */
- if (!buffer) {
- GST_DEBUG ("Buffer completely outside of configured segment, dropping it");
- goto out;
- }
-
- /*
- * Make sure all incoming buffers share the same timestamping
- */
- GST_BUFFER_TIMESTAMP (buffer) =
- gst_segment_to_running_time (&padprivate->segment,
- padprivate->segment.format, GST_BUFFER_TIMESTAMP (buffer));
-
-
- if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) &&
- GST_BUFFER_TIMESTAMP (buffer) < adder->next_timestamp) {
- if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
- adder->next_timestamp) {
- GST_DEBUG_OBJECT (adder, "Buffer is late, dropping (ts: %" GST_TIME_FORMAT
- " duration: %" GST_TIME_FORMAT ")",
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
- gst_buffer_unref (buffer);
- goto out;
- } else {
- skip = adder->next_timestamp - GST_BUFFER_TIMESTAMP (buffer);
- GST_DEBUG_OBJECT (adder, "Buffer is partially late, skipping %"
- GST_TIME_FORMAT, GST_TIME_ARGS (skip));
- }
- }
-
- /* If our new buffer's head is higher than the queue's head, lets wake up,
- * we may not have to wait for as long
- */
- if (adder->clock_id &&
- g_queue_peek_head (adder->buffers) != NULL &&
- GST_BUFFER_TIMESTAMP (buffer) + skip <
- GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers)))
- gst_clock_id_unschedule (adder->clock_id);
-
- for (item = g_queue_peek_head_link (adder->buffers);
- item; item = g_list_next (item)) {
- GstBuffer *oldbuffer = item->data;
- GstClockTime old_skip = 0;
- GstClockTime mix_duration = 0;
- GstClockTime mix_start = 0;
- GstClockTime mix_end = 0;
- GstMapInfo oldmap, map;
-
- /* We haven't reached our place yet */
- if (GST_BUFFER_TIMESTAMP (buffer) + skip >=
- GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
- continue;
-
- /* We're past our place, lets insert ouselves here */
- if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <=
- GST_BUFFER_TIMESTAMP (oldbuffer))
- break;
-
- /* if we reach this spot, we have overlap, so we must mix */
-
- /* First make a subbuffer with the non-overlapping part */
- if (GST_BUFFER_TIMESTAMP (buffer) + skip < GST_BUFFER_TIMESTAMP (oldbuffer)) {
- GstBuffer *subbuffer = NULL;
- GstClockTime subbuffer_duration = GST_BUFFER_TIMESTAMP (oldbuffer) -
- (GST_BUFFER_TIMESTAMP (buffer) + skip);
-
- buffer_size = gst_buffer_get_size (buffer);
- offset = gst_live_adder_length_from_duration (adder, skip);
- subbuffer_size = gst_live_adder_length_from_duration (adder,
- subbuffer_duration);
-
- if (offset + subbuffer_size > buffer_size) {
- subbuffer_size = buffer_size - offset;
- subbuffer_duration = gst_live_adder_length_to_duration (adder,
- subbuffer_size);
- }
-
- subbuffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
- offset, subbuffer_size);
-
- GST_BUFFER_TIMESTAMP (subbuffer) = GST_BUFFER_TIMESTAMP (buffer) + skip;
- GST_BUFFER_DURATION (subbuffer) = subbuffer_duration;
-
- skip += subbuffer_duration;
-
- g_queue_insert_before (adder->buffers, item, subbuffer);
- }
-
- /* Now we are on the overlapping part */
- oldbuffer = gst_buffer_make_writable (oldbuffer);
- item->data = oldbuffer;
-
- old_skip = GST_BUFFER_TIMESTAMP (buffer) + skip -
- GST_BUFFER_TIMESTAMP (oldbuffer);
-
- mix_start = GST_BUFFER_TIMESTAMP (oldbuffer) + old_skip;
-
- if (GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) <
- GST_BUFFER_TIMESTAMP (oldbuffer) + GST_BUFFER_DURATION (oldbuffer))
- mix_end = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
- else
- mix_end = GST_BUFFER_TIMESTAMP (oldbuffer) +
- GST_BUFFER_DURATION (oldbuffer);
-
- mix_duration = mix_end - mix_start;
-
- if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) {
- GST_BUFFER_FLAG_UNSET (oldbuffer, GST_BUFFER_FLAG_GAP);
- gst_buffer_map (oldbuffer, &oldmap, GST_MAP_WRITE);
- gst_buffer_map (buffer, &map, GST_MAP_READ);
- adder->func (oldmap.data +
- gst_live_adder_length_from_duration (adder, old_skip),
- map.data +
- gst_live_adder_length_from_duration (adder, skip),
- gst_live_adder_length_from_duration (adder, mix_duration));
- gst_buffer_unmap (oldbuffer, &oldmap);
- gst_buffer_unmap (buffer, &map);
- }
- skip += mix_duration;
- }
-
- g_cond_broadcast (&adder->not_empty_cond);
-
- if (skip == GST_BUFFER_DURATION (buffer)) {
- gst_buffer_unref (buffer);
- } else {
- if (skip) {
- GstClockTime subbuffer_duration = GST_BUFFER_DURATION (buffer) - skip;
- GstClockTime subbuffer_ts = GST_BUFFER_TIMESTAMP (buffer) + skip;
- GstBuffer *new_buffer;
-
- buffer_size = gst_buffer_get_size (buffer);
- offset = gst_live_adder_length_from_duration (adder, skip);
- subbuffer_size = gst_live_adder_length_from_duration (adder,
- subbuffer_duration);
-
- if (offset + subbuffer_size > buffer_size) {
- subbuffer_size = buffer_size - offset;
- subbuffer_duration = gst_live_adder_length_to_duration (adder,
- subbuffer_size);
- }
-
- new_buffer = gst_buffer_copy_region (buffer,
- GST_BUFFER_COPY_ALL, offset, subbuffer_size);
-
- gst_buffer_unref (buffer);
- buffer = new_buffer;
- GST_BUFFER_PTS (buffer) = subbuffer_ts;
- GST_BUFFER_DURATION (buffer) = subbuffer_duration;
- }
-
- if (item)
- g_queue_insert_before (adder->buffers, item, buffer);
- else
- g_queue_push_tail (adder->buffers, buffer);
- }
-
-out:
-
- GST_OBJECT_UNLOCK (adder);
-
- return ret;
-
-invalid_timestamp:
-
- GST_OBJECT_UNLOCK (adder);
- gst_buffer_unref (buffer);
- GST_ELEMENT_ERROR (adder, STREAM, FAILED,
- ("Buffer without a valid timestamp received"),
- ("Invalid timestamp received on buffer"));
-
- return GST_FLOW_ERROR;
-}
-
-/*
- * This only works because the GstObject lock is taken
- *
- * It checks if all sink pads are EOS
- */
-static gboolean
-check_eos_locked (GstLiveAdder * adder)
-{
- GList *item;
-
- /* We can't be EOS if we have no sinkpads */
- if (adder->sinkpads == NULL)
- return FALSE;
-
- for (item = adder->sinkpads; item; item = g_list_next (item)) {
- GstPad *pad = item->data;
- GstLiveAdderPadPrivate *padprivate = gst_pad_get_element_private (pad);
-
- if (padprivate && padprivate->eos != TRUE)
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-gst_live_adder_loop (gpointer data)
-{
- GstLiveAdder *adder = GST_LIVE_ADDER (data);
- GstClockTime buffer_timestamp = 0;
- GstClockTime sync_time = 0;
- GstClock *clock = NULL;
- GstClockID id = NULL;
- GstClockReturn ret;
- GstBuffer *buffer = NULL;
- GstFlowReturn result;
-
- GST_OBJECT_LOCK (adder);
-
-again:
-
- for (;;) {
- if (adder->srcresult != GST_FLOW_OK)
- goto flushing;
- if (!g_queue_is_empty (adder->buffers))
- break;
- if (check_eos_locked (adder))
- goto eos;
- g_cond_wait (&adder->not_empty_cond, GST_OBJECT_GET_LOCK (adder));
- }
-
- buffer_timestamp = GST_BUFFER_TIMESTAMP (g_queue_peek_head (adder->buffers));
-
- clock = GST_ELEMENT_CLOCK (adder);
-
- /* If we have no clock, then we can't do anything.. error */
- if (!clock) {
- if (adder->playing)
- goto no_clock;
- else
- goto push_buffer;
- }
-
- GST_DEBUG_OBJECT (adder, "sync to timestamp %" GST_TIME_FORMAT,
- GST_TIME_ARGS (buffer_timestamp));
-
- sync_time = buffer_timestamp + GST_ELEMENT_CAST (adder)->base_time;
- /* add latency, this includes our own latency and the peer latency. */
- sync_time += adder->latency_ms * GST_MSECOND;
- sync_time += adder->peer_latency;
-
- /* create an entry for the clock */
- id = adder->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
- GST_OBJECT_UNLOCK (adder);
-
- ret = gst_clock_id_wait (id, NULL);
-
- GST_OBJECT_LOCK (adder);
-
- /* and free the entry */
- gst_clock_id_unref (id);
- adder->clock_id = NULL;
-
- /* at this point, the clock could have been unlocked by a timeout, a new
- * head element was added to the queue or because we are shutting down. Check
- * for shutdown first. */
-
- if (adder->srcresult != GST_FLOW_OK)
- goto flushing;
-
- if (ret == GST_CLOCK_UNSCHEDULED) {
- GST_DEBUG_OBJECT (adder,
- "Wait got unscheduled, will retry to push with new buffer");
- goto again;
- }
-
- if (ret != GST_CLOCK_OK && ret != GST_CLOCK_EARLY)
- goto clock_error;
-
-push_buffer:
-
- buffer = g_queue_pop_head (adder->buffers);
-
- if (!buffer)
- goto again;
-
- /*
- * We make sure the timestamps are exactly contiguous
- * If its only small skew (due to rounding errors), we correct it
- * silently. Otherwise we put the discont flag
- */
- if (GST_CLOCK_TIME_IS_VALID (adder->next_timestamp) &&
- GST_BUFFER_TIMESTAMP (buffer) != adder->next_timestamp) {
- GstClockTimeDiff diff = GST_CLOCK_DIFF (GST_BUFFER_TIMESTAMP (buffer),
- adder->next_timestamp);
- if (diff < 0)
- diff = -diff;
-
- if (diff < GST_SECOND / GST_AUDIO_INFO_RATE (&adder->info)) {
- GST_BUFFER_TIMESTAMP (buffer) = adder->next_timestamp;
- GST_DEBUG_OBJECT (adder, "Correcting slight skew");
- GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
- } else {
- GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
- GST_DEBUG_OBJECT (adder, "Expected buffer at %" GST_TIME_FORMAT
- ", but is at %" GST_TIME_FORMAT ", setting discont",
- GST_TIME_ARGS (adder->next_timestamp),
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
- }
- } else {
- GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
- }
-
- GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
- GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
-
- if (GST_BUFFER_DURATION_IS_VALID (buffer))
- adder->next_timestamp = GST_BUFFER_TIMESTAMP (buffer) +
- GST_BUFFER_DURATION (buffer);
- else
- adder->next_timestamp = GST_CLOCK_TIME_NONE;
- GST_OBJECT_UNLOCK (adder);
-
- GST_LOG_OBJECT (adder, "About to push buffer time:%" GST_TIME_FORMAT
- " duration:%" GST_TIME_FORMAT,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
-
- result = gst_pad_push (adder->srcpad, buffer);
- if (result != GST_FLOW_OK)
- goto pause;
-
- return;
-
-flushing:
- {
- GST_DEBUG_OBJECT (adder, "we are flushing");
- gst_pad_pause_task (adder->srcpad);
- GST_OBJECT_UNLOCK (adder);
- return;
- }
-
-clock_error:
- {
- gst_pad_pause_task (adder->srcpad);
- GST_OBJECT_UNLOCK (adder);
- GST_ELEMENT_ERROR (adder, STREAM, MUX, ("Error with the clock"),
- ("Error with the clock: %d", ret));
- GST_ERROR_OBJECT (adder, "Error with the clock: %d", ret);
- return;
- }
-
-no_clock:
- {
- gst_pad_pause_task (adder->srcpad);
- GST_OBJECT_UNLOCK (adder);
- GST_ELEMENT_ERROR (adder, STREAM, MUX, ("No available clock"),
- ("No available clock"));
- GST_ERROR_OBJECT (adder, "No available clock");
- return;
- }
-
-pause:
- {
- GST_DEBUG_OBJECT (adder, "pausing task, reason %s",
- gst_flow_get_name (result));
-
- GST_OBJECT_LOCK (adder);
-
- /* store result */
- adder->srcresult = result;
- /* we don't post errors or anything because upstream will do that for us
- * when we pass the return value upstream. */
- gst_pad_pause_task (adder->srcpad);
- GST_OBJECT_UNLOCK (adder);
- return;
- }
-
-eos:
- {
- /* store result, we are flushing now */
- GST_DEBUG_OBJECT (adder, "We are EOS, pushing EOS downstream");
- adder->srcresult = GST_FLOW_EOS;
- gst_pad_pause_task (adder->srcpad);
- GST_OBJECT_UNLOCK (adder);
- gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
- return;
- }
-}
-
-static GstPad *
-gst_live_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
- const gchar * ignored_name, const GstCaps * caps)
-{
- gchar *name;
- GstLiveAdder *adder;
- GstPad *newpad;
- gint padcount;
- GstLiveAdderPadPrivate *padprivate = NULL;
-
- if (templ->direction != GST_PAD_SINK)
- goto not_sink;
-
- adder = GST_LIVE_ADDER (element);
-
- /* increment pad counter */
- padcount = g_atomic_int_add (&adder->padcount, 1);
-
- name = g_strdup_printf ("sink_%u", padcount);
- newpad = gst_pad_new_from_template (templ, name);
- GST_DEBUG_OBJECT (adder, "request new pad %s", name);
- g_free (name);
-
- gst_pad_set_event_function (newpad,
- GST_DEBUG_FUNCPTR (gst_live_adder_sink_event));
- gst_pad_set_query_function (newpad,
- GST_DEBUG_FUNCPTR (gst_live_adder_sink_query));
-
- padprivate = g_new0 (GstLiveAdderPadPrivate, 1);
-
- gst_segment_init (&padprivate->segment, GST_FORMAT_UNDEFINED);
- padprivate->eos = FALSE;
- padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
-
- gst_pad_set_element_private (newpad, padprivate);
-
- gst_pad_set_chain_function (newpad, gst_live_adder_chain);
-
-
- if (!gst_pad_set_active (newpad, TRUE))
- goto could_not_activate;
-
- /* takes ownership of the pad */
- if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
- goto could_not_add;
-
- GST_OBJECT_LOCK (adder);
- adder->sinkpads = g_list_prepend (adder->sinkpads, newpad);
- GST_OBJECT_UNLOCK (adder);
-
- 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");
- g_free (padprivate);
- gst_object_unref (newpad);
- return NULL;
- }
-could_not_activate:
- {
- GST_DEBUG_OBJECT (adder, "could not activate new pad");
- g_free (padprivate);
- gst_object_unref (newpad);
- return NULL;
- }
-}
-
-static void
-gst_live_adder_release_pad (GstElement * element, GstPad * pad)
-{
- GstLiveAdder *adder;
- GstLiveAdderPadPrivate *padprivate;
-
- adder = GST_LIVE_ADDER (element);
-
- GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
- GST_OBJECT_LOCK (element);
- padprivate = gst_pad_get_element_private (pad);
- gst_pad_set_element_private (pad, NULL);
- adder->sinkpads = g_list_remove_all (adder->sinkpads, pad);
- GST_OBJECT_UNLOCK (element);
-
- g_free (padprivate);
-
- gst_element_remove_pad (element, pad);
-}
-
-static void
-reset_pad_private (GstPad * pad)
-{
- GstLiveAdderPadPrivate *padprivate;
-
- padprivate = gst_pad_get_element_private (pad);
-
- if (!padprivate)
- return;
-
- gst_segment_init (&padprivate->segment, GST_FORMAT_UNDEFINED);
-
- padprivate->expected_timestamp = GST_CLOCK_TIME_NONE;
- padprivate->eos = FALSE;
-}
-
-static GstStateChangeReturn
-gst_live_adder_change_state (GstElement * element, GstStateChange transition)
-{
- GstLiveAdder *adder;
- GstStateChangeReturn ret;
-
- adder = GST_LIVE_ADDER (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_OBJECT_LOCK (adder);
- adder->segment_pending = TRUE;
- adder->peer_latency = 0;
- adder->next_timestamp = GST_CLOCK_TIME_NONE;
- g_list_foreach (adder->sinkpads, (GFunc) reset_pad_private, NULL);
- GST_OBJECT_UNLOCK (adder);
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- GST_OBJECT_LOCK (adder);
- adder->playing = FALSE;
- GST_OBJECT_UNLOCK (adder);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (gst_live_adder_parent_class)->change_state (element,
- transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- GST_OBJECT_LOCK (adder);
- adder->playing = TRUE;
- GST_OBJECT_UNLOCK (adder);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- if (!gst_element_register (plugin, "liveadder", GST_RANK_NONE,
- GST_TYPE_LIVE_ADDER)) {
- return FALSE;
- }
-
- return TRUE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- liveadder,
- "Adds multiple live discontinuous streams",
- plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/liveadder/liveadder.h b/gst/liveadder/liveadder.h
deleted file mode 100644
index 3755edf4d..000000000
--- a/gst/liveadder/liveadder.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * GStreamer
- *
- * Copyright 2008 Collabora Ltd
- * Copyright 2008 Nokia Corporation
- * @author: Olivier Crete <olivier.crete@collabora.co.uk>
- *
- * 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_LIVE_ADDER_H__
-#define __GST_LIVE_ADDER_H__
-
-#include <gst/gst.h>
-#include <gst/audio/audio.h>
-
-G_BEGIN_DECLS
-#define GST_TYPE_LIVE_ADDER (gst_live_adder_get_type())
-#define GST_LIVE_ADDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LIVE_ADDER,GstLiveAdder))
-#define GST_IS_LIVE_ADDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LIVE_ADDER))
-#define GST_LIVE_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_LIVE_ADDER,GstLiveAdderClass))
-#define GST_IS_LIVE_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_LIVE_ADDER))
-#define GST_LIVE_ADDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_LIVE_ADDER,GstLiveAdderClass))
-typedef struct _GstLiveAdder GstLiveAdder;
-typedef struct _GstLiveAdderClass GstLiveAdderClass;
-
-typedef void (*GstLiveAdderFunction) (gpointer out, gpointer in, guint size);
-
-/**
- * GstLiveAdder:
- *
- * The adder object structure.
- */
-struct _GstLiveAdder
-{
- /*< private >*/
- GstElement element;
-
- GstPad *srcpad;
- /* pad counter, used for creating unique request pads */
- gint padcount;
- GList *sinkpads;
-
- GstFlowReturn srcresult;
- GstClockID clock_id;
-
- /* the queue is ordered head to tail */
- GQueue *buffers;
- GCond not_empty_cond;
-
- GstClockTime next_timestamp;
-
- /* the next are valid for both int and float */
- GstAudioInfo info;
-
- /* function to add samples */
- GstLiveAdderFunction func;
-
- GstClockTime latency_ms;
- GstClockTime peer_latency;
-
- gboolean segment_pending;
-
- gboolean playing;
-};
-
-struct _GstLiveAdderClass
-{
- GstElementClass parent_class;
-};
-
-GType gst_live_adder_get_type (void);
-
-G_END_DECLS
-#endif /* __GST_LIVE_ADDER_H__ */