*
* 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.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-adder
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+
#include "gstadder.h"
+#include <gst/audio/audio.h>
#include <string.h> /* strcmp */
#include "gstadderorc.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,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define CAPS \
- GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }")
+ 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 }")
+ GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
+ ", layout = (string) { interleaved, non-interleaved }"
#endif
static GstStaticPadTemplate gst_adder_src_template =
);
static GstStaticPadTemplate gst_adder_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink%d",
+GST_STATIC_PAD_TEMPLATE ("sink_%u",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS (CAPS)
static gboolean gst_adder_setcaps (GstAdder * adder, 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 gboolean gst_adder_src_query (GstPad * pad, GstObject * parent,
+ GstQuery * query);
+static gboolean gst_adder_sink_query (GstCollectPads * pads,
+ GstCollectData * pad, GstQuery * query, gpointer user_data);
+static gboolean gst_adder_src_event (GstPad * pad, GstObject * parent,
+ GstEvent * event);
+static gboolean gst_adder_sink_event (GstCollectPads * pads,
+ GstCollectData * pad, GstEvent * event, gpointer user_data);
static GstPad *gst_adder_request_new_pad (GstElement * element,
GstPadTemplate * temp, const gchar * unused, const GstCaps * caps);
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_do_clip (GstCollectPads * pads,
+ GstCollectData * data, GstBuffer * buffer, GstBuffer ** out,
+ gpointer user_data);
static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
gpointer user_data);
}
/* *INDENT-OFF* */
-MAKE_FUNC_NC (add_float64, gdouble)
+MAKE_FUNC_NC (adder_orc_add_float64, gdouble)
/* *INDENT-ON* */
/* we can only accept caps that we and downstream can handle.
else
gst_caps_ref (filter_caps);
} else {
- filter_caps = gst_caps_ref (filter);
+ filter_caps = filter ? gst_caps_ref (filter) : NULL;
}
GST_OBJECT_UNLOCK (adder);
}
/* get the downstream possible caps */
- peercaps = gst_pad_peer_get_caps (adder->srcpad, filter_caps);
+ peercaps = gst_pad_peer_query_caps (adder->srcpad, filter_caps);
/* get the allowed caps on this sinkpad */
sinkcaps = gst_pad_get_current_caps (pad);
return result;
}
+static gboolean
+gst_adder_sink_query (GstCollectPads * pads, GstCollectData * pad,
+ GstQuery * query, gpointer user_data)
+{
+ gboolean res = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CAPS:
+ {
+ GstCaps *filter, *caps;
+
+ gst_query_parse_caps (query, &filter);
+ caps = gst_adder_sink_getcaps (pad->pad, filter);
+ gst_query_set_caps_result (query, caps);
+ gst_caps_unref (caps);
+ res = TRUE;
+ break;
+ }
+ default:
+ res = gst_collect_pads_query_default (pads, pad, query, FALSE);
+ break;
+ }
+
+ return res;
+}
+
typedef struct
{
GstPad *pad;
{
GstPad *otherpad = g_value_get_object (item);
- if (otherpad != data->pad)
+ if (otherpad != data->pad) {
+ GST_LOG_OBJECT (data->pad, "calling set_caps with %" GST_PTR_FORMAT,
+ data->caps);
gst_pad_set_caps (data->pad, data->caps);
+ gst_pad_use_fixed_caps (data->pad);
+ }
}
/* the first caps we receive on any of the sinkpads will define the caps for all
static gboolean
gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * caps)
{
- GstAudioInfo info;
GstIterator *it;
GstIteratorResult ires;
IterData idata;
gboolean done;
- GST_LOG_OBJECT (adder, "setting caps pad %p,%s to %" GST_PTR_FORMAT, pad,
- GST_PAD_NAME (pad), caps);
+ /* this gets called recursively due to gst_iterator_foreach calling
+ * gst_pad_set_caps() */
+ if (adder->in_setcaps)
+ return TRUE;
+
+ /* don't allow reconfiguration for now; there's still a race between the
+ * different upstream threads doing query_caps + accept_caps + sending
+ * (possibly different) CAPS events, but there's not much we can do about
+ * that, upstream needs to deal with it. */
+ if (adder->current_caps != NULL) {
+ /* FIXME: not quite right for optional fields such as channel-mask, which
+ * may or may not be present for mono/stereo */
+ if (gst_caps_is_equal (caps, adder->current_caps)) {
+ return TRUE;
+ } else {
+ GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but "
+ "current caps are %" GST_PTR_FORMAT, caps, adder->current_caps);
+ gst_pad_push_event (pad, gst_event_new_reconfigure ());
+ return FALSE;
+ }
+ }
+
+ GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps);
+
+ adder->current_caps = gst_caps_ref (caps);
+ /* send caps event later, after stream-start event */
it = gst_element_iterate_pads (GST_ELEMENT_CAST (adder));
idata.caps = caps;
idata.pad = pad;
+ adder->in_setcaps = TRUE;
done = FALSE;
while (!done) {
ires = gst_iterator_foreach (it, (GstIteratorForeachFunction) setcapsfunc,
break;
}
}
+ adder->in_setcaps = FALSE;
+ gst_iterator_free (it);
+
+ GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps);
- if (!gst_audio_info_from_caps (&info, caps))
+ if (!gst_audio_info_from_caps (&adder->info, caps))
goto invalid_format;
- switch (GST_AUDIO_INFO_FORMAT (&info)) {
+ switch (GST_AUDIO_INFO_FORMAT (&adder->info)) {
case GST_AUDIO_FORMAT_S8:
- adder->func = (GstAdderFunction) add_int8;
+ adder->func = (GstAdderFunction) adder_orc_add_int8;
break;
case GST_AUDIO_FORMAT_U8:
- adder->func = (GstAdderFunction) add_uint8;
+ adder->func = (GstAdderFunction) adder_orc_add_uint8;
break;
case GST_AUDIO_FORMAT_S16:
- adder->func = (GstAdderFunction) add_int16;
+ adder->func = (GstAdderFunction) adder_orc_add_int16;
break;
case GST_AUDIO_FORMAT_U16:
- adder->func = (GstAdderFunction) add_uint16;
+ adder->func = (GstAdderFunction) adder_orc_add_uint16;
break;
case GST_AUDIO_FORMAT_S32:
- adder->func = (GstAdderFunction) add_int32;
+ adder->func = (GstAdderFunction) adder_orc_add_int32;
break;
case GST_AUDIO_FORMAT_U32:
- adder->func = (GstAdderFunction) add_uint32;
+ adder->func = (GstAdderFunction) adder_orc_add_uint32;
break;
case GST_AUDIO_FORMAT_F32:
- adder->func = (GstAdderFunction) add_float32;
+ adder->func = (GstAdderFunction) adder_orc_add_float32;
break;
case GST_AUDIO_FORMAT_F64:
- adder->func = (GstAdderFunction) add_float64;
+ adder->func = (GstAdderFunction) adder_orc_add_float64;
break;
default:
goto invalid_format;
/* ERRORS */
invalid_format:
{
- GST_DEBUG_OBJECT (adder, "invalid format set as caps");
+ GST_WARNING_OBJECT (adder, "invalid format set as caps");
return FALSE;
}
}
gint64 duration;
/* ask sink peer for duration */
- res &= gst_pad_query_peer_duration (pad, format, &duration);
+ res &= gst_pad_peer_query_duration (pad, format, &duration);
/* take max from all valid return values */
if (res) {
/* valid unknown length, stop searching */
}
static gboolean
-gst_adder_query (GstPad * pad, GstQuery * query)
+gst_adder_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
- GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
+ GstAdder *adder = GST_ADDER (parent);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
default:
/* FIXME, needs a custom query handler because we have multiple
* sinkpads */
- res = gst_pad_query_default (pad, query);
+ res = gst_pad_query_default (pad, parent, query);
break;
}
- gst_object_unref (adder);
return res;
}
+/* event handling */
+
typedef struct
{
GstEvent *event;
{
GstPad *pad = g_value_get_object (val);
GstEvent *event = data->event;
+ GstPad *peer;
gst_event_ref (event);
GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
- if (!gst_pad_push_event (pad, event)) {
+ peer = gst_pad_get_peer (pad);
+ /* collect pad might have been set flushing,
+ * so bypass core checking that and send directly to peer */
+ if (!peer || !gst_pad_send_event (peer, event)) {
+ if (!peer)
+ gst_event_unref (event);
GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
event, GST_EVENT_TYPE_NAME (event));
/* quick hack to unflush the pads, ideally we need a way to just unflush
GST_LOG_OBJECT (pad, "Sent event %p (%s).",
event, GST_EVENT_TYPE_NAME (event));
}
+ if (peer)
+ gst_object_unref (peer);
/* continue on other pads, even if one failed */
return TRUE;
}
static gboolean
-gst_adder_src_event (GstPad * pad, GstEvent * event)
+gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstAdder *adder;
gboolean result;
- adder = GST_ADDER (gst_pad_get_parent (pad));
+ adder = GST_ADDER (parent);
GST_DEBUG_OBJECT (pad, "Got %s event on src pad",
GST_EVENT_TYPE_NAME (event));
{
GstSeekFlags flags;
gdouble rate;
- GstSeekType curtype, endtype;
- gint64 cur, end;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ GstFormat seek_format, dest_format;
gboolean flush;
/* parse the seek parameters */
- gst_event_parse_seek (event, &rate, NULL, &flags, &curtype,
- &cur, &endtype, &end);
+ gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type,
+ &start, &stop_type, &stop);
- if ((curtype != GST_SEEK_TYPE_NONE) && (curtype != GST_SEEK_TYPE_SET)) {
+ if ((start_type != GST_SEEK_TYPE_NONE)
+ && (start_type != GST_SEEK_TYPE_SET)) {
+ result = FALSE;
+ GST_DEBUG_OBJECT (adder,
+ "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 (adder,
- "seeking failed, unhandled seek type for start: %d", curtype);
+ "seeking failed, unhandled seek type for end: %d", stop_type);
goto done;
}
- if ((endtype != GST_SEEK_TYPE_NONE) && (endtype != GST_SEEK_TYPE_SET)) {
+
+ dest_format = adder->segment.format;
+ if (seek_format != dest_format) {
result = FALSE;
GST_DEBUG_OBJECT (adder,
- "seeking failed, unhandled seek type for end: %d", endtype);
+ "seeking failed, unhandled seek format: %d", seek_format);
goto done;
}
/* 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. */
+ * when all pads received a FLUSH_STOP.
+ * Make sure we accept nothing anymore and return WRONG_STATE.
+ * We send a flush-start before, to ensure no streaming is done
+ * as we need to take the stream lock.
+ */
gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
+ gst_collect_pads_set_flushing (adder->collect, TRUE);
/* We can't send FLUSH_STOP here since upstream could start pushing data
* after we unlock adder->collect.
* whichever happens first.
*/
g_atomic_int_set (&adder->flush_stop_pending, TRUE);
+ GST_DEBUG_OBJECT (adder, "mark pending flush stop event");
}
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);
- adder->segment.rate = rate;
- if (curtype == GST_SEEK_TYPE_SET)
- adder->segment.start = cur;
- else
- adder->segment.start = 0;
- if (endtype == GST_SEEK_TYPE_SET)
- adder->segment.stop = end;
- else
- adder->segment.stop = GST_CLOCK_TIME_NONE;
+ GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
+ /* clip position and update our segment */
+ if (adder->segment.stop != -1) {
+ adder->segment.position = adder->segment.stop;
+ }
+ gst_segment_do_seek (&adder->segment, rate, seek_format, flags,
+ start_type, start, stop_type, stop, NULL);
+
if (flush) {
/* Yes, we need to call _set_flushing again *WHEN* the streaming threads
* have stopped so that the cookie gets properly updated. */
gst_collect_pads_set_flushing (adder->collect, TRUE);
}
- GST_OBJECT_UNLOCK (adder->collect);
+ GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
event);
+ GST_DEBUG_OBJECT (adder, "updated segment: %" GST_SEGMENT_FORMAT,
+ &adder->segment);
/* we're forwarding seek to all upstream peers and wait for one to reply
* with a newsegment-event before we send a newsegment-event downstream */
if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
TRUE, FALSE)) {
GST_DEBUG_OBJECT (adder, "pending flush stop");
- gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop (TRUE));
+ if (!gst_pad_push_event (adder->srcpad,
+ gst_event_new_flush_stop (TRUE))) {
+ GST_WARNING_OBJECT (adder, "Sending flush stop event failed");
+ }
}
break;
}
case GST_EVENT_QOS:
/* QoS might be tricky */
result = FALSE;
+ gst_event_unref (event);
break;
case GST_EVENT_NAVIGATION:
/* navigation is rather pointless. */
result = FALSE;
+ gst_event_unref (event);
break;
default:
/* just forward the rest for now */
}
done:
- gst_object_unref (adder);
return result;
}
static gboolean
-gst_adder_sink_event (GstPad * pad, GstEvent * event)
+gst_adder_sink_event (GstCollectPads * pads, GstCollectData * pad,
+ GstEvent * event, gpointer user_data)
{
- GstAdder *adder;
- gboolean ret = TRUE;
-
- adder = GST_ADDER (gst_pad_get_parent (pad));
+ GstAdder *adder = GST_ADDER (user_data);
+ gboolean res = TRUE, discard = FALSE;
- GST_DEBUG_OBJECT (pad, "Got %s event on sink pad",
+ GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad",
GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
GstCaps *caps;
gst_event_parse_caps (event, &caps);
- ret = gst_adder_setcaps (adder, pad, caps);
+ res = gst_adder_setcaps (adder, pad->pad, caps);
gst_event_unref (event);
-
- goto beach;
+ event = NULL;
+ break;
}
+ case GST_EVENT_FLUSH_START:
+ /* ensure that we will send a flush stop */
+ g_atomic_int_set (&adder->need_flush_stop, TRUE);
+ break;
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.
+ /* we received a flush-stop. We will only forward it when
+ * flush_stop_pending is set, and we will unset it then.
*/
- GST_OBJECT_LOCK (adder->collect);
- g_atomic_int_set (&adder->new_segment_pending, TRUE);
- g_atomic_int_set (&adder->flush_stop_pending, FALSE);
+ if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
+ TRUE, FALSE)) {
+ g_atomic_int_set (&adder->new_segment_pending, TRUE);
+ GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop");
+ } else {
+ discard = TRUE;
+ GST_DEBUG_OBJECT (pad->pad, "eating flush stop");
+ }
/* 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;
+ event = NULL;
+ break;
case GST_EVENT_SEGMENT:
if (g_atomic_int_compare_and_exchange (&adder->wait_for_new_segment,
TRUE, FALSE)) {
* see FIXME in gst_adder_collected() */
g_atomic_int_set (&adder->new_segment_pending, TRUE);
}
+ if (g_atomic_int_compare_and_exchange (&adder->need_flush_stop,
+ TRUE, FALSE)) {
+ /* ensure that we'll eventually send a flush-stop
+ * (e.g. after a flushing seek directly sent to an upstream element) */
+ g_atomic_int_set (&adder->flush_stop_pending, TRUE);
+ GST_DEBUG_OBJECT (adder, "mark pending flush stop event");
+ }
+ discard = TRUE;
break;
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;
+ if (G_LIKELY (event))
+ return gst_collect_pads_event_default (pads, pad, event, discard);
+ else
+ return res;
}
static void
gobject_class->get_property = gst_adder_get_property;
gobject_class->dispose = gst_adder_dispose;
- /**
- * 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). "
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",
+ gst_element_class_set_static_metadata (gstelement_class, "Adder",
"Generic/Audio",
"Add N audio channels together",
"Thomas Vander Stichele <thomas at apestaart dot org>");
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_query_function (adder->srcpad,
- GST_DEBUG_FUNCPTR (gst_adder_query));
+ GST_DEBUG_FUNCPTR (gst_adder_src_query));
gst_pad_set_event_function (adder->srcpad,
GST_DEBUG_FUNCPTR (gst_adder_src_event));
+ GST_PAD_SET_PROXY_CAPS (adder->srcpad);
gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
+ adder->current_caps = NULL;
gst_audio_info_init (&adder->info);
adder->padcount = 0;
adder->func = NULL;
GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
gst_collect_pads_set_clip_function (adder->collect,
GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
+ gst_collect_pads_set_event_function (adder->collect,
+ GST_DEBUG_FUNCPTR (gst_adder_sink_event), adder);
+ gst_collect_pads_set_query_function (adder->collect,
+ GST_DEBUG_FUNCPTR (gst_adder_sink_query), adder);
}
static void
adder->collect = NULL;
}
gst_caps_replace (&adder->filter_caps, NULL);
+ gst_caps_replace (&adder->current_caps, NULL);
+
if (adder->pending_events) {
g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
g_list_free (adder->pending_events);
adder = GST_ADDER (element);
/* increment pad counter */
-#if GLIB_CHECK_VERSION(2,29,5)
padcount = g_atomic_int_add (&adder->padcount, 1);
-#else
- padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);
-#endif
- name = g_strdup_printf ("sink%d", padcount);
+ 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_getcaps_function (newpad,
- GST_DEBUG_FUNCPTR (gst_adder_sink_getcaps));
- 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));
+ gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData),
+ NULL, TRUE);
/* takes ownership of the pad */
if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
- gst_collect_pads_remove_pad (adder->collect, pad);
+ if (adder->collect)
+ gst_collect_pads_remove_pad (adder->collect, pad);
gst_element_remove_pad (element, pad);
}
-static GstBuffer *
+static GstFlowReturn
gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
- GstBuffer * buffer, gpointer user_data)
+ GstBuffer * buffer, GstBuffer ** out, gpointer user_data)
{
GstAdder *adder = GST_ADDER (user_data);
gint rate, bpf;
buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf);
- return buffer;
+ *out = buffer;
+ return GST_FLOW_OK;
}
static GstFlowReturn
GSList *collected, *next = NULL;
GstFlowReturn ret;
GstBuffer *outbuf = NULL, *gapbuf = NULL;
- gpointer outdata = NULL;
+ GstMapInfo outmap = { NULL };
guint outsize;
gint64 next_offset;
gint64 next_timestamp;
if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
TRUE, FALSE)) {
- GST_DEBUG_OBJECT (adder, "pending flush stop");
- gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop (TRUE));
+ GST_INFO_OBJECT (adder->srcpad, "send pending flush stop event");
+ if (!gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop (TRUE))) {
+ GST_WARNING_OBJECT (adder->srcpad, "Sending flush stop event failed");
+ }
+ }
+
+ if (adder->send_stream_start) {
+ gchar s_id[32];
+
+ GST_INFO_OBJECT (adder->srcpad, "send pending stream start event");
+ /* stream-start (FIXME: create id based on input ids) */
+ g_snprintf (s_id, sizeof (s_id), "adder-%08x", g_random_int ());
+ if (!gst_pad_push_event (adder->srcpad, gst_event_new_stream_start (s_id))) {
+ GST_WARNING_OBJECT (adder->srcpad, "Sending stream start event failed");
+ }
+ adder->send_stream_start = FALSE;
+ }
+
+ if (adder->send_caps) {
+ GstEvent *caps_event;
+
+ caps_event = gst_event_new_caps (adder->current_caps);
+ GST_INFO_OBJECT (adder->srcpad, "send pending caps event %" GST_PTR_FORMAT,
+ caps_event);
+ if (!gst_pad_push_event (adder->srcpad, caps_event)) {
+ GST_WARNING_OBJECT (adder->srcpad, "Sending caps event failed");
+ }
+ adder->send_caps = FALSE;
}
/* get available bytes for reading, this can be 0 which could mean empty
bpf = GST_AUDIO_INFO_BPF (&adder->info);
GST_LOG_OBJECT (adder,
- "starting to cycle through channels, %d bytes available (bps = %d)",
- outsize, bpf);
+ "starting to cycle through channels, %d bytes available (bps = %d, bpf = %d)",
+ outsize, bps, bpf);
for (collected = pads->data; collected; collected = next) {
GstCollectData *collect_data;
GST_LOG_OBJECT (adder, "channel %p: preparing output buffer of %d bytes",
collect_data, outsize);
- outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
+ /* 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);
+ gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
} else {
if (!is_gap) {
/* we had a previous output buffer, mix this non-GAP buffer */
- guint8 *indata;
- gsize insize;
+ GstMapInfo inmap;
- indata = gst_buffer_map (inbuf, &insize, NULL, GST_MAP_READ);
+ gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
/* all buffers should have outsize, there are no short buffers because we
* asked for the max size above */
- g_assert (insize == outsize);
+ g_assert (inmap.size == outmap.size);
GST_LOG_OBJECT (adder, "channel %p: mixing %" G_GSIZE_FORMAT " bytes"
- " from data %p", collect_data, insize, indata);
+ " from data %p", collect_data, inmap.size, inmap.data);
/* further buffers, need to add them */
- adder->func ((gpointer) outdata, (gpointer) indata, insize / bps);
- gst_buffer_unmap (inbuf, indata, insize);
+ adder->func ((gpointer) outmap.data, (gpointer) inmap.data,
+ inmap.size / bps);
+ gst_buffer_unmap (inbuf, &inmap);
} else {
/* skip gap buffer */
GST_LOG_OBJECT (adder, "channel %p: skipping GAP buffer", collect_data);
}
}
if (outbuf)
- gst_buffer_unmap (outbuf, outdata, outsize);
+ gst_buffer_unmap (outbuf, &outmap);
if (outbuf == NULL) {
/* no output buffer, reuse one of the GAP buffers then if we have one */
* - currently we just set rate as received from last seek-event
*
* When seeking we set the start and stop positions as given in the seek
- * event. We also adjust offset & timestamp acordingly.
+ * event. We also adjust offset & timestamp accordingly.
* This basically ignores all newsegments sent by upstream.
*/
event = gst_event_new_segment (&adder->segment);
-
if (adder->segment.rate > 0.0) {
adder->segment.position = adder->segment.start;
} else {
}
adder->offset = gst_util_uint64_scale (adder->segment.position,
rate, GST_SECOND);
- GST_INFO_OBJECT (adder, "seg_start %" G_GUINT64_FORMAT ", seg_end %"
- G_GUINT64_FORMAT, adder->segment.start, adder->segment.stop);
- GST_INFO_OBJECT (adder, "timestamp %" G_GINT64_FORMAT ",new offset %"
- G_GINT64_FORMAT, adder->segment.position, adder->offset);
+ GST_INFO_OBJECT (adder->srcpad, "sending pending new segment event %"
+ GST_SEGMENT_FORMAT, &adder->segment);
if (event) {
if (!gst_pad_push_event (adder->srcpad, event)) {
- GST_WARNING_OBJECT (adder->srcpad, "Sending event failed");
+ GST_WARNING_OBJECT (adder->srcpad, "Sending new segment event failed");
}
} else {
GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
{
GST_DEBUG_OBJECT (adder, "no data available, must be EOS");
gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
- return GST_FLOW_UNEXPECTED;
+ return GST_FLOW_EOS;
}
}
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- adder->segment.position = 0;
adder->offset = 0;
adder->flush_stop_pending = FALSE;
adder->new_segment_pending = TRUE;
adder->wait_for_new_segment = FALSE;
+ adder->send_stream_start = TRUE;
+ adder->send_caps = TRUE;
+ gst_caps_replace (&adder->current_caps, NULL);
gst_segment_init (&adder->segment, GST_FORMAT_TIME);
gst_collect_pads_start (adder->collect);
break;
return ret;
}
-
static gboolean
plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
"audio channel mixing element");
- gst_adder_orc_init ();
-
if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
return FALSE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
- "adder",
+ adder,
"Adds multiple streams",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)