Replace the switch plugin with the selector plugin. Add output- selector as the opposite of input-selectoo (was switc...

Original commit message from CVS:
* configure.ac:
* docs/plugins/Makefile.am:
* docs/plugins/gst-plugins-bad-plugins-docs.sgml:
* docs/plugins/gst-plugins-bad-plugins-sections.txt:
* docs/plugins/gst-plugins-bad-plugins.args:
* docs/plugins/gst-plugins-bad-plugins.hierarchy:
* docs/plugins/gst-plugins-bad-plugins.interfaces:
* docs/plugins/gst-plugins-bad-plugins.signals:
* docs/plugins/inspect/plugin-metadata.xml:
* docs/plugins/inspect/plugin-selector.xml:
* docs/plugins/inspect/plugin-soundtouch.xml:
* docs/plugins/inspect/plugin-switch.xml:
* plugins/elements/.cvsignore:
* plugins/elements/Makefile.am:
* plugins/elements/gstinputselector.c:
* plugins/elements/gstinputselector.h:
* plugins/elements/gstoutputselector.c:
* plugins/elements/gstoutputselector.h:
* plugins/elements/gstselector-marshal.list:
* plugins/elements/gstselector.c:
* plugins/elements/selector.vcproj:
* gst/switch/.cvsignore:
* gst/switch/Makefile.am:
* gst/switch/gstswitch-marshal.list:
* gst/switch/gstswitch.c:
* gst/switch/gstswitch.h:
* gst/switch/switch.vcproj:
* tests/icles/.cvsignore:
* tests/icles/Makefile.am:
* tests/icles/output-selector-test.c:
Replace the switch plugin with the selector plugin. Add output-
selector as the opposite of input-selectoo (was switch). Add a test
for output-selector. Add docs for the elements. The vcproj needs
update. Fixes #500142.
This commit is contained in:
Stefan Kost 2008-01-29 07:38:31 +00:00 committed by Tim-Philipp Müller
parent 77aca24d0f
commit c9f97beda7
7 changed files with 1761 additions and 0 deletions

View file

@ -0,0 +1,981 @@
/* GStreamer
* Copyright (C) 2003 Julien Moutte <julien@moutte.net>
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-input-selector
* @short_description: N-to-1 stream selectoring
* @see_also: #GstOutputSelector
*
* Direct one out of N input streams to the output pad.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstinputselector.h"
#include "gstselector-marshal.h"
GST_DEBUG_CATEGORY_STATIC (input_selector_debug);
#define GST_CAT_DEFAULT input_selector_debug
static const GstElementDetails gst_input_selector_details =
GST_ELEMENT_DETAILS ("Input selector",
"Generic",
"N-to-1 input stream selectoring",
"Julien Moutte <julien@moutte.net>\n"
"Ronald S. Bultje <rbultje@ronald.bitfreak.net>\n"
"Jan Schmidt <thaytan@mad.scientist.com>\n"
"Wim Taymans <wim.taymans@gmail.com>");
static GstStaticPadTemplate gst_input_selector_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink%d",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate gst_input_selector_src_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
enum
{
PROP_ACTIVE_PAD = 1
};
enum
{
PAD_PROP_RUNNING_TIME = 1
};
enum
{
/* methods */
SIGNAL_BLOCK,
SIGNAL_SWITCH,
LAST_SIGNAL
};
static guint gst_input_selector_signals[LAST_SIGNAL] = { 0 };
static gboolean gst_input_selector_is_active_sinkpad (GstInputSelector * sel,
GstPad * pad);
static GstPad *gst_input_selector_activate_sinkpad (GstInputSelector * sel,
GstPad * pad);
static GstPad *gst_input_selector_get_linked_pad (GstPad * pad,
gboolean strict);
static void gst_input_selector_push_pending_stop (GstInputSelector * self);
#define GST_TYPE_SELECTOR_PAD \
(gst_selector_pad_get_type())
#define GST_SELECTOR_PAD(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, GstSelectorPad))
#define GST_SELECTOR_PAD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, GstSelectorPadClass))
#define GST_IS_SELECTOR_PAD(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD))
#define GST_IS_SELECTOR_PAD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD))
#define GST_SELECTOR_PAD_CAST(obj) \
((GstSelectorPad *)(obj))
typedef struct _GstSelectorPad GstSelectorPad;
typedef struct _GstSelectorPadClass GstSelectorPadClass;
struct _GstSelectorPad
{
GstPad parent;
gboolean active;
gboolean eos;
gboolean segment_pending;
GstSegment segment;
};
struct _GstSelectorPadClass
{
GstPadClass parent;
};
static void gst_selector_pad_class_init (GstSelectorPadClass * klass);
static void gst_selector_pad_init (GstSelectorPad * pad);
static void gst_selector_pad_finalize (GObject * object);
static void gst_selector_pad_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstPadClass *selector_pad_parent_class = NULL;
static gint64 gst_selector_pad_get_running_time (GstSelectorPad * pad);
static void gst_selector_pad_reset (GstSelectorPad * pad);
static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_selector_pad_getcaps (GstPad * pad);
static GList *gst_selector_pad_get_linked_pads (GstPad * pad);
static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf);
static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad,
guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
static GType
gst_selector_pad_get_type (void)
{
static GType selector_pad_type = 0;
if (!selector_pad_type) {
static const GTypeInfo selector_pad_info = {
sizeof (GstSelectorPadClass),
NULL,
NULL,
(GClassInitFunc) gst_selector_pad_class_init,
NULL,
NULL,
sizeof (GstSelectorPad),
0,
(GInstanceInitFunc) gst_selector_pad_init,
};
selector_pad_type =
g_type_register_static (GST_TYPE_PAD, "GstSelectorPad",
&selector_pad_info, 0);
}
return selector_pad_type;
}
static void
gst_selector_pad_class_init (GstSelectorPadClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
selector_pad_parent_class = g_type_class_peek_parent (klass);
gobject_class->get_property =
GST_DEBUG_FUNCPTR (gst_selector_pad_get_property);
g_object_class_install_property (gobject_class, PAD_PROP_RUNNING_TIME,
g_param_spec_int64 ("running-time", "Running time",
"Running time of stream on pad", 0, G_MAXINT64, 0, G_PARAM_READABLE));
gobject_class->finalize = gst_selector_pad_finalize;
}
static void
gst_selector_pad_init (GstSelectorPad * pad)
{
gst_selector_pad_reset (pad);
}
static void
gst_selector_pad_finalize (GObject * object)
{
GstSelectorPad *pad;
pad = GST_SELECTOR_PAD_CAST (object);
G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object);
}
static void
gst_selector_pad_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstSelectorPad *spad = GST_SELECTOR_PAD_CAST (object);
switch (prop_id) {
case PAD_PROP_RUNNING_TIME:
g_value_set_int64 (value, gst_selector_pad_get_running_time (spad));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gint64
gst_selector_pad_get_running_time (GstSelectorPad * pad)
{
gint64 ret = 0;
GST_OBJECT_LOCK (pad);
if (pad->active) {
gint64 last_stop = pad->segment.last_stop;
if (last_stop >= 0)
ret = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME,
last_stop);
}
GST_OBJECT_UNLOCK (pad);
GST_DEBUG_OBJECT (pad, "running time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (ret));
return ret;
}
static void
gst_selector_pad_reset (GstSelectorPad * pad)
{
pad->active = FALSE;
pad->eos = FALSE;
gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
}
/* strictly get the linked pad from the sinkpad. If the pad is active we return
* the srcpad else we return NULL */
static GList *
gst_selector_pad_get_linked_pads (GstPad * pad)
{
GstPad *otherpad;
otherpad = gst_input_selector_get_linked_pad (pad, TRUE);
if (!otherpad)
return NULL;
/* need to drop the ref, internal linked pads is not MT safe */
gst_object_unref (otherpad);
return g_list_append (NULL, otherpad);
}
static gboolean
gst_selector_pad_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
gboolean forward = TRUE;
GstInputSelector *sel;
GstSelectorPad *selpad;
sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
selpad = GST_SELECTOR_PAD_CAST (pad);
/* only forward if we are dealing with the active sinkpad */
forward = gst_input_selector_is_active_sinkpad (sel, pad);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
gst_selector_pad_reset (selpad);
break;
case GST_EVENT_NEWSEGMENT:
{
gboolean update;
GstFormat format;
gdouble rate, arate;
gint64 start, stop, time;
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
GST_DEBUG_OBJECT (sel,
"configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
"format %d, "
"%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
gst_segment_set_newsegment_full (&selpad->segment, update,
rate, arate, format, start, stop, time);
/* if we are not going to forward the segment, mark the segment as
* pending */
if (!forward)
selpad->segment_pending = TRUE;
break;
}
case GST_EVENT_EOS:
selpad->eos = TRUE;
break;
default:
break;
}
if (forward)
res = gst_pad_push_event (sel->srcpad, event);
gst_object_unref (sel);
return res;
}
static GstCaps *
gst_selector_pad_getcaps (GstPad * pad)
{
GstInputSelector *sel;
GstCaps *caps;
sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer");
caps = gst_pad_peer_get_caps (sel->srcpad);
if (caps == NULL)
caps = gst_caps_new_any ();
gst_object_unref (sel);
return caps;
}
static GstFlowReturn
gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset,
guint size, GstCaps * caps, GstBuffer ** buf)
{
GstInputSelector *sel;
GstFlowReturn result;
GstPad *active_sinkpad;
sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
/* Fallback allocation for buffers from pads except the selected one */
if (pad != active_sinkpad) {
GST_DEBUG_OBJECT (sel,
"Pad %s:%s is not selected. Performing fallback allocation",
GST_DEBUG_PAD_NAME (pad));
*buf = NULL;
result = GST_FLOW_OK;
} else {
result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf);
/* FIXME: HACK. If buffer alloc returns not-linked, perform a fallback
* allocation. This should NOT be necessary, because playbin should
* properly block the source pad from running until it's finished hooking
* everything up, but playbin needs refactoring first. */
if (result == GST_FLOW_NOT_LINKED) {
GST_DEBUG_OBJECT (sel,
"No peer pad yet - performing fallback allocation for pad %s:%s",
GST_DEBUG_PAD_NAME (pad));
*buf = NULL;
result = GST_FLOW_OK;
}
}
gst_object_unref (sel);
return result;
}
static gboolean
gst_input_selector_wait (GstInputSelector * self, GstPad * pad)
{
gboolean flushing;
GST_OBJECT_LOCK (self);
while (self->blocked)
g_cond_wait (self->blocked_cond, GST_OBJECT_GET_LOCK (self));
GST_OBJECT_UNLOCK (self);
GST_OBJECT_LOCK (pad);
flushing = GST_PAD_IS_FLUSHING (pad);
GST_OBJECT_UNLOCK (pad);
return flushing;
}
static GstFlowReturn
gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
{
GstInputSelector *sel;
GstFlowReturn res;
GstPad *active_sinkpad;
GstSelectorPad *selpad;
GstClockTime end_time, duration;
GstSegment *seg;
sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
selpad = GST_SELECTOR_PAD_CAST (pad);
seg = &selpad->segment;
if (gst_input_selector_wait (sel, pad))
goto ignore;
active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
end_time = GST_BUFFER_TIMESTAMP (buf);
if (GST_CLOCK_TIME_IS_VALID (end_time)) {
duration = GST_BUFFER_DURATION (buf);
if (GST_CLOCK_TIME_IS_VALID (duration))
end_time += duration;
GST_DEBUG_OBJECT (sel, "received end time %" GST_TIME_FORMAT,
GST_TIME_ARGS (end_time));
gst_segment_set_last_stop (seg, seg->format, end_time);
}
/* Ignore buffers from pads except the selected one */
if (pad != active_sinkpad)
goto ignore;
gst_input_selector_push_pending_stop (sel);
/* if we have a pending segment, push it out now */
if (selpad->segment_pending) {
gst_pad_push_event (sel->srcpad, gst_event_new_new_segment_full (FALSE,
seg->rate, seg->applied_rate, seg->format, seg->start, seg->stop,
seg->time));
selpad->segment_pending = FALSE;
}
/* forward */
GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf,
GST_DEBUG_PAD_NAME (pad));
res = gst_pad_push (sel->srcpad, buf);
done:
gst_object_unref (sel);
return res;
/* dropped buffers */
ignore:
{
GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s",
buf, GST_DEBUG_PAD_NAME (pad));
gst_buffer_unref (buf);
res = GST_FLOW_OK;
goto done;
}
}
static void gst_input_selector_dispose (GObject * object);
static void gst_input_selector_init (GstInputSelector * sel);
static void gst_input_selector_base_init (GstInputSelectorClass * klass);
static void gst_input_selector_class_init (GstInputSelectorClass * klass);
static void gst_input_selector_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_input_selector_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstPad *gst_input_selector_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused);
static void gst_input_selector_release_pad (GstElement * element, GstPad * pad);
static GstStateChangeReturn gst_input_selector_change_state (GstElement *
element, GstStateChange transition);
static GList *gst_input_selector_get_linked_pads (GstPad * pad);
static GstCaps *gst_input_selector_getcaps (GstPad * pad);
static gint64 gst_input_selector_block (GstInputSelector * self);
static void gst_input_selector_switch (GstInputSelector * self,
const gchar * pad_name, gint64 stop_time, gint64 start_time);
static GstElementClass *parent_class = NULL;
GType
gst_input_selector_get_type (void)
{
static GType input_selector_type = 0;
if (!input_selector_type) {
static const GTypeInfo input_selector_info = {
sizeof (GstInputSelectorClass),
(GBaseInitFunc) gst_input_selector_base_init,
NULL,
(GClassInitFunc) gst_input_selector_class_init,
NULL,
NULL,
sizeof (GstInputSelector),
0,
(GInstanceInitFunc) gst_input_selector_init,
};
input_selector_type =
g_type_register_static (GST_TYPE_ELEMENT,
"GstInputSelector", &input_selector_info, 0);
GST_DEBUG_CATEGORY_INIT (input_selector_debug,
"input-selector", 0, "An input stream selector element");
}
return input_selector_type;
}
static void
gst_input_selector_base_init (GstInputSelectorClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_details (element_class, &gst_input_selector_details);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_input_selector_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_input_selector_src_factory));
}
static void
gst_input_selector_class_init (GstInputSelectorClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property =
GST_DEBUG_FUNCPTR (gst_input_selector_set_property);
gobject_class->get_property =
GST_DEBUG_FUNCPTR (gst_input_selector_get_property);
g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
g_param_spec_string ("active-pad", "Active pad",
"Name of the currently" " active sink pad", NULL, G_PARAM_READWRITE));
gobject_class->dispose = gst_input_selector_dispose;
gstelement_class->request_new_pad = gst_input_selector_request_new_pad;
gstelement_class->release_pad = gst_input_selector_release_pad;
gstelement_class->change_state = gst_input_selector_change_state;
/**
* GstInputSelector::block:
* @inputselector: the #GstInputSelector
*
* Block all sink pads in preparation for a switch. Returns the stop time of
* the current switch segment, as a running time, or 0 if there is no current
* active pad or the current active pad never received data.
*/
gst_input_selector_signals[SIGNAL_BLOCK] =
g_signal_new ("block", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstInputSelectorClass, block),
NULL, NULL, gst_selector_marshal_INT64__VOID, G_TYPE_INT64, 0);
/**
* GstInputSelector::switch:
* @inputselector: the #GstInputSelector
* @pad: name of pad to switch to
* @stop_time: running time at which to close the previous segment, or -1
* to use the running time of the previously active sink pad
* @start_time: running time at which to start the new segment, or -1 to
* use the running time of the newly active sink pad
*
* Switch to a new feed. The segment opened by the previously active pad, if
* any, will be closed, and a new segment opened before data flows again.
*
* This signal must be emitted when the element has been blocked via the <link
* linkend="GstInputSelector-block">block</link> signal.
*
* If you have a stream with only one switch element, such as an audio-only
* stream, a stream switch should be performed by first emitting the block
* signal, and then emitting the switch signal with -1 for the stop and start
* time values.
*
* The intention of the @stop_time and @start_time arguments is to allow
* multiple switch elements to switch and maintain stream synchronization.
* When switching a stream with multiple feeds, you will need as many switch
* elements as you have feeds. For example, a feed with audio and video will
* have one switch element between the audio feeds and one for video.
*
* A switch over multiple switch elements should be performed as follows:
* First, emit the <link linkend="GstInputSelector-block">block</link>
* signal, collecting the returned values. The maximum running time returned
* by block should then be used as the time at which to close the previous
* segment.
*
* Then, query the running times of the new audio and video pads that you will
* switch to. Naturally, these pads are on separate switch elements. Take the
* minimum running time for those streams and use it for the time at which to
* open the new segment.
*
* If @pad is the same as the current active pad, the element will cancel any
* previous block without adjusting segments.
*/
gst_input_selector_signals[SIGNAL_SWITCH] =
g_signal_new ("switch", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstInputSelectorClass, switch_),
NULL, NULL, gst_selector_marshal_VOID__STRING_INT64_INT64,
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT64, G_TYPE_INT64);
klass->block = GST_DEBUG_FUNCPTR (gst_input_selector_block);
klass->switch_ = GST_DEBUG_FUNCPTR (gst_input_selector_switch);
}
static void
gst_input_selector_init (GstInputSelector * sel)
{
sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
gst_pad_set_internal_link_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_input_selector_get_linked_pads));
gst_pad_set_getcaps_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_input_selector_getcaps));
gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
/* sinkpad management */
sel->active_sinkpad = NULL;
sel->nb_sinkpads = 0;
gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
sel->blocked_cond = g_cond_new ();
sel->blocked = FALSE;
}
static void
gst_input_selector_dispose (GObject * object)
{
GstInputSelector *sel = GST_INPUT_SELECTOR (object);
if (sel->active_sinkpad) {
gst_object_unref (sel->active_sinkpad);
sel->active_sinkpad = NULL;
}
if (sel->blocked_cond) {
g_cond_free (sel->blocked_cond);
sel->blocked_cond = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
/* Solve the following equation for B.timestamp, and set that as the segment
* stop:
* B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
*/
static gint64
gst_segment_get_timestamp (GstSegment * segment, gint64 running_time)
{
return (running_time - segment->accum) * segment->abs_rate + segment->start;
}
static void
gst_segment_set_stop (GstSegment * segment, gint64 running_time)
{
segment->stop = gst_segment_get_timestamp (segment, running_time);
segment->last_stop = -1;
}
static void
gst_segment_set_start (GstSegment * segment, gint64 running_time)
{
segment->start = gst_segment_get_timestamp (segment, running_time);
}
static void
gst_input_selector_set_active_pad (GstInputSelector * self,
const gchar * pad_name, gint64 stop_time, gint64 start_time)
{
GstPad *pad;
GstSelectorPad *old, *new;
GstPad **active_pad_p;
if (strcmp (pad_name, "") != 0)
pad = gst_element_get_pad (GST_ELEMENT (self), pad_name);
else
pad = NULL;
GST_OBJECT_LOCK (self);
if (pad == self->active_sinkpad)
goto done;
old = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
new = GST_SELECTOR_PAD_CAST (pad);
if (old && old->active && !self->pending_stop && stop_time >= 0) {
/* schedule a last_stop update if one isn't already scheduled, and a
segment has been pushed before. */
memcpy (&self->pending_stop_segment, &old->segment,
sizeof (self->pending_stop_segment));
gst_segment_set_stop (&self->pending_stop_segment, stop_time);
self->pending_stop = TRUE;
}
if (new && new->active && start_time >= 0) {
/* schedule a new segment push */
gst_segment_set_start (&new->segment, start_time);
new->segment_pending = TRUE;
}
active_pad_p = &self->active_sinkpad;
gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT,
self->active_sinkpad);
done:
GST_OBJECT_UNLOCK (self);
if (pad)
gst_object_unref (pad);
}
static void
gst_input_selector_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstInputSelector *sel = GST_INPUT_SELECTOR (object);
switch (prop_id) {
case PROP_ACTIVE_PAD:
gst_input_selector_set_active_pad (sel,
g_value_get_string (value), GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_input_selector_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstInputSelector *sel = GST_INPUT_SELECTOR (object);
switch (prop_id) {
case PROP_ACTIVE_PAD:{
GST_OBJECT_LOCK (object);
if (sel->active_sinkpad != NULL) {
g_value_take_string (value, gst_pad_get_name (sel->active_sinkpad));
} else {
g_value_set_string (value, "");
}
GST_OBJECT_UNLOCK (object);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstPad *
gst_input_selector_get_linked_pad (GstPad * pad, gboolean strict)
{
GstInputSelector *sel;
GstPad *otherpad = NULL;
sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad));
GST_OBJECT_LOCK (sel);
if (pad == sel->srcpad)
otherpad = sel->active_sinkpad;
else if (pad == sel->active_sinkpad || !strict)
otherpad = sel->srcpad;
if (otherpad)
gst_object_ref (otherpad);
GST_OBJECT_UNLOCK (sel);
gst_object_unref (sel);
return otherpad;
}
static GstCaps *
gst_input_selector_getcaps (GstPad * pad)
{
GstPad *otherpad;
GstObject *parent;
GstCaps *caps;
otherpad = gst_input_selector_get_linked_pad (pad, FALSE);
parent = gst_object_get_parent (GST_OBJECT (pad));
if (!otherpad) {
GST_DEBUG_OBJECT (parent,
"Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
caps = gst_caps_new_any ();
} else {
GST_DEBUG_OBJECT (parent,
"Pad %s:%s is linked (to %s:%s), returning peer caps",
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
/* if the peer has caps, use those. If the pad is not linked, this function
* returns NULL and we return ANY */
if (!(caps = gst_pad_peer_get_caps (otherpad)))
caps = gst_caps_new_any ();
gst_object_unref (otherpad);
}
gst_object_unref (parent);
return caps;
}
/* check if the pad is the active sinkpad */
static gboolean
gst_input_selector_is_active_sinkpad (GstInputSelector * sel, GstPad * pad)
{
GstSelectorPad *selpad;
gboolean res;
selpad = GST_SELECTOR_PAD_CAST (pad);
GST_OBJECT_LOCK (sel);
res = (pad == sel->active_sinkpad);
GST_OBJECT_UNLOCK (sel);
return res;
}
/* Get or create the active sinkpad */
static GstPad *
gst_input_selector_activate_sinkpad (GstInputSelector * sel, GstPad * pad)
{
GstPad *active_sinkpad;
GstSelectorPad *selpad;
selpad = GST_SELECTOR_PAD_CAST (pad);
GST_OBJECT_LOCK (sel);
selpad->active = TRUE;
active_sinkpad = sel->active_sinkpad;
if (active_sinkpad == NULL) {
/* first pad we get an alloc on becomes the activated pad by default */
active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
}
GST_OBJECT_UNLOCK (sel);
return active_sinkpad;
}
static GList *
gst_input_selector_get_linked_pads (GstPad * pad)
{
GstPad *otherpad;
otherpad = gst_input_selector_get_linked_pad (pad, TRUE);
if (!otherpad)
return NULL;
/* need to drop the ref, internal linked pads is not MT safe */
gst_object_unref (otherpad);
return g_list_append (NULL, otherpad);
}
static GstPad *
gst_input_selector_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused)
{
GstInputSelector *sel;
gchar *name = NULL;
GstPad *sinkpad = NULL;
sel = GST_INPUT_SELECTOR (element);
g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
GST_LOG_OBJECT (sel, "Creating new pad %d", sel->nb_sinkpads);
GST_OBJECT_LOCK (sel);
name = g_strdup_printf ("sink%d", sel->nb_sinkpads++);
sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD,
"name", name, "direction", templ->direction, "template", templ, NULL);
g_free (name);
GST_OBJECT_UNLOCK (sel);
gst_pad_set_event_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_selector_pad_event));
gst_pad_set_getcaps_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps));
gst_pad_set_chain_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
gst_pad_set_internal_link_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_selector_pad_get_linked_pads));
gst_pad_set_bufferalloc_function (sinkpad,
GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc));
gst_pad_set_active (sinkpad, TRUE);
gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
return sinkpad;
}
static void
gst_input_selector_release_pad (GstElement * element, GstPad * pad)
{
GstInputSelector *sel;
sel = GST_INPUT_SELECTOR (element);
GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
GST_OBJECT_LOCK (sel);
/* if the pad was the active pad, makes us select a new one */
if (sel->active_sinkpad == pad) {
GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
sel->active_sinkpad = NULL;
}
GST_OBJECT_UNLOCK (sel);
gst_pad_set_active (pad, FALSE);
gst_element_remove_pad (GST_ELEMENT (sel), pad);
}
static GstStateChangeReturn
gst_input_selector_change_state (GstElement * element,
GstStateChange transition)
{
GstInputSelector *self = GST_INPUT_SELECTOR (element);
GstStateChangeReturn result;
result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
GST_OBJECT_LOCK (self);
self->blocked = FALSE;
g_cond_broadcast (self->blocked_cond);
GST_OBJECT_UNLOCK (self);
}
return result;
}
static gint64
gst_input_selector_block (GstInputSelector * self)
{
gint64 ret = 0;
GstSelectorPad *spad;
GST_OBJECT_LOCK (self);
if (self->blocked)
GST_WARNING_OBJECT (self, "switch already blocked");
self->blocked = TRUE;
spad = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
if (self->active_sinkpad)
ret = gst_selector_pad_get_running_time (spad);
else
GST_DEBUG_OBJECT (self, "no active pad while blocking");
GST_OBJECT_UNLOCK (self);
return ret;
}
static void
gst_input_selector_push_pending_stop (GstInputSelector * self)
{
GstEvent *event = NULL;
GST_OBJECT_LOCK (self);
if (G_UNLIKELY (self->pending_stop)) {
GstSegment *seg = &self->pending_stop_segment;
event = gst_event_new_new_segment_full (TRUE, seg->rate,
seg->applied_rate, seg->format, seg->start, seg->stop, seg->stop);
self->pending_stop = FALSE;
}
GST_OBJECT_UNLOCK (self);
if (event)
gst_pad_push_event (self->srcpad, event);
}
/* stop_time and start_time are running times */
static void
gst_input_selector_switch (GstInputSelector * self, const gchar * pad_name,
gint64 stop_time, gint64 start_time)
{
g_return_if_fail (self->blocked == TRUE);
gst_input_selector_set_active_pad (self, pad_name, stop_time, start_time);
GST_OBJECT_LOCK (self);
self->blocked = FALSE;
g_cond_broadcast (self->blocked_cond);
GST_OBJECT_UNLOCK (self);
}

View file

@ -0,0 +1,71 @@
/* GStreamer
* Copyright (C) 2003 Julien Moutte <julien@moutte.net>
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_INPUT_SELECTOR_H__
#define __GST_INPUT_SELECTOR_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_INPUT_SELECTOR \
(gst_input_selector_get_type())
#define GST_INPUT_SELECTOR(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INPUT_SELECTOR, GstInputSelector))
#define GST_INPUT_SELECTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INPUT_SELECTOR, GstInputSelectorClass))
#define GST_IS_INPUT_SELECTOR(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INPUT_SELECTOR))
#define GST_IS_INPUT_SELECTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INPUT_SELECTOR))
typedef struct _GstInputSelector GstInputSelector;
typedef struct _GstInputSelectorClass GstInputSelectorClass;
struct _GstInputSelector {
GstElement element;
GstPad *srcpad;
GstPad *active_sinkpad;
guint nb_sinkpads;
GstSegment segment;
GCond *blocked_cond;
gboolean blocked;
gboolean pending_stop;
GstSegment pending_stop_segment;
};
struct _GstInputSelectorClass {
GstElementClass parent_class;
gint64 (*block) (GstInputSelector *self);
void (*switch_) (GstInputSelector *self, const gchar *pad_name,
gint64 stop_time, gint64 start_time);
};
GType gst_input_selector_get_type (void);
G_END_DECLS
#endif /* __GST_INPUT_SELECTOR_H__ */

View file

@ -0,0 +1,448 @@
/* GStreamer
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-output-selector
* @short_description: 1-to-N stream selectoring
* @see_also: #GstTee, #GstInputSelector
*
* Direct input stream to one out of N output pads.
*/
/* FIXME: By default basesinks require some prerolled data before changing
to playing state. Also pipeline with output-selector connected to multiple
sink elements won't change to playing until all sink elements have received
the preroll data. Currently this can be worked around using live source element
and and exporting GST_COMPAT="no-live-preroll".
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstoutputselector.h"
GST_DEBUG_CATEGORY_STATIC (output_selector_debug);
#define GST_CAT_DEFAULT output_selector_debug
static const GstElementDetails gst_output_selector_details =
GST_ELEMENT_DETAILS ("Output selector",
"Generic",
"1-to-N output stream selectoring",
"Stefan Kost <stefan.kost@nokia.com>");
static GstStaticPadTemplate gst_output_selector_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate gst_output_selector_src_factory =
GST_STATIC_PAD_TEMPLATE ("src%d",
GST_PAD_SRC,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
enum
{
PROP_ACTIVE_PAD = 1,
PROP_RESEND_LATEST
};
static void gst_output_selector_dispose (GObject * object);
static void gst_output_selector_init (GstOutputSelector * sel);
static void gst_output_selector_base_init (GstOutputSelectorClass * klass);
static void gst_output_selector_class_init (GstOutputSelectorClass * klass);
static void gst_output_selector_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_output_selector_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static GstPad *gst_output_selector_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused);
static void gst_output_selector_release_pad (GstElement * element,
GstPad * pad);
static GstFlowReturn gst_output_selector_chain (GstPad * pad, GstBuffer * buf);
static GstStateChangeReturn gst_output_selector_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_output_selector_handle_sink_event (GstPad * pad,
GstEvent * event);
static GstElementClass *parent_class = NULL;
GType
gst_output_selector_get_type (void)
{
static GType output_selector_type = 0;
if (!output_selector_type) {
static const GTypeInfo output_selector_info = {
sizeof (GstOutputSelectorClass),
(GBaseInitFunc) gst_output_selector_base_init,
NULL,
(GClassInitFunc) gst_output_selector_class_init,
NULL,
NULL,
sizeof (GstOutputSelector),
0,
(GInstanceInitFunc) gst_output_selector_init,
};
output_selector_type =
g_type_register_static (GST_TYPE_ELEMENT,
"GstOutputSelector", &output_selector_info, 0);
GST_DEBUG_CATEGORY_INIT (output_selector_debug,
"output-selector", 0, "An output stream selector element");
}
return output_selector_type;
}
static void
gst_output_selector_base_init (GstOutputSelectorClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_details (element_class, &gst_output_selector_details);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_output_selector_sink_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_output_selector_src_factory));
}
static void
gst_output_selector_class_init (GstOutputSelectorClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property =
GST_DEBUG_FUNCPTR (gst_output_selector_set_property);
gobject_class->get_property =
GST_DEBUG_FUNCPTR (gst_output_selector_get_property);
g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
g_param_spec_string ("active-pad", "Active pad",
"Name of the currently active src pad", NULL, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_RESEND_LATEST,
g_param_spec_boolean ("resend-latest", "Resend latest buffer",
"Resend latest buffer after a switch to a new pad", FALSE,
G_PARAM_READWRITE));
gobject_class->dispose = gst_output_selector_dispose;
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_output_selector_request_new_pad);
gstelement_class->release_pad =
GST_DEBUG_FUNCPTR (gst_output_selector_release_pad);
gstelement_class->change_state = gst_output_selector_change_state;
}
static void
gst_output_selector_init (GstOutputSelector * sel)
{
sel->sinkpad =
gst_pad_new_from_static_template (&gst_output_selector_sink_factory,
"sink");
gst_pad_set_chain_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_output_selector_chain));
gst_pad_set_event_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event));
gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad);
/*
gst_pad_set_bufferalloc_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_output_selector_bufferalloc));
gst_pad_set_setcaps_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps));
gst_pad_set_getcaps_function (sel->sinkpad,
GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
*/
/* srcpad management */
sel->active_srcpad = NULL;
sel->nb_srcpads = 0;
gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
sel->pending_srcpad = NULL;
sel->resend_latest = FALSE;
sel->latest_buffer = NULL;
}
static void
gst_output_selector_dispose (GObject * object)
{
GstOutputSelector *osel = GST_OUTPUT_SELECTOR (object);
if (osel->pending_srcpad != NULL) {
gst_object_unref (osel->pending_srcpad);
osel->pending_srcpad = NULL;
}
if (osel->latest_buffer != NULL) {
gst_buffer_unref (osel->latest_buffer);
osel->latest_buffer = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_output_selector_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
switch (prop_id) {
case PROP_ACTIVE_PAD:{
GstPad *next_pad =
gst_element_get_static_pad (GST_ELEMENT (sel),
g_value_get_string (value));
if (next_pad && (next_pad != sel->active_srcpad)) {
/* switch to new srcpad in next chain run */
if (sel->pending_srcpad != NULL) {
GST_INFO ("replacing pending switch");
gst_object_unref (sel->pending_srcpad);
}
sel->pending_srcpad = next_pad;
} else {
GST_WARNING ("setting active pad failed");
}
break;
}
case PROP_RESEND_LATEST:{
sel->resend_latest = g_value_get_boolean (value);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_output_selector_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstOutputSelector *sel = GST_OUTPUT_SELECTOR (object);
switch (prop_id) {
case PROP_ACTIVE_PAD:{
GST_OBJECT_LOCK (object);
if (sel->active_srcpad != NULL) {
g_value_take_string (value, gst_pad_get_name (sel->active_srcpad));
} else {
g_value_set_string (value, "");
}
GST_OBJECT_UNLOCK (object);
break;
}
case PROP_RESEND_LATEST:{
GST_OBJECT_LOCK (object);
g_value_set_boolean (value, sel->resend_latest);
GST_OBJECT_UNLOCK (object);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstPad *
gst_output_selector_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * name)
{
gchar *padname;
GstPad *srcpad;
GstOutputSelector *osel;
osel = GST_OUTPUT_SELECTOR (element);
GST_DEBUG_OBJECT (osel, "requesting pad");
GST_OBJECT_LOCK (osel);
padname = g_strdup_printf ("src%d", osel->nb_srcpads++);
srcpad = gst_pad_new_from_template (templ, padname);
GST_OBJECT_UNLOCK (osel);
gst_pad_set_active (srcpad, TRUE);
gst_element_add_pad (GST_ELEMENT (osel), srcpad);
/* Set the first requested src pad as active by default */
if (osel->active_srcpad == NULL) {
osel->active_srcpad = srcpad;
}
g_free (padname);
return srcpad;
}
static void
gst_output_selector_release_pad (GstElement * element, GstPad * pad)
{
GstOutputSelector *osel;
osel = GST_OUTPUT_SELECTOR (element);
GST_DEBUG_OBJECT (osel, "releasing pad");
gst_pad_set_active (pad, FALSE);
gst_element_remove_pad (GST_ELEMENT_CAST (osel), pad);
}
static gboolean
gst_output_selector_switch (GstOutputSelector * osel)
{
gboolean res = TRUE;
GstEvent *ev = NULL;
GstSegment *seg = NULL;
gint64 start = 0, position = 0;
GST_INFO ("switching to pad %" GST_PTR_FORMAT, osel->pending_srcpad);
if (gst_pad_is_linked (osel->pending_srcpad)) {
/* Send NEWSEGMENT to the pad we are going to switch to */
seg = &osel->segment;
/* If resending then mark newsegment start and position accordingly */
if (osel->resend_latest && osel->latest_buffer &&
GST_BUFFER_TIMESTAMP_IS_VALID (osel->latest_buffer)) {
start = position = GST_BUFFER_TIMESTAMP (osel->latest_buffer);
} else {
start = position = seg->last_stop;
}
ev = gst_event_new_new_segment (TRUE, seg->rate,
seg->format, start, seg->stop, position);
if (!gst_pad_push_event (osel->pending_srcpad, ev)) {
GST_WARNING ("newsegment handling failed in %" GST_PTR_FORMAT,
osel->pending_srcpad);
}
/* Resend latest buffer to newly switched pad */
if (osel->resend_latest && osel->latest_buffer) {
GST_INFO ("resending latest buffer");
gst_pad_push (osel->pending_srcpad, osel->latest_buffer);
osel->latest_buffer = NULL;
}
/* Switch */
osel->active_srcpad = osel->pending_srcpad;
} else {
GST_WARNING ("switch failed, pad not linked");
res = FALSE;
}
gst_object_unref (osel->pending_srcpad);
osel->pending_srcpad = NULL;
return res;
}
static GstFlowReturn
gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
{
GstFlowReturn res;
GstOutputSelector *osel;
GstClockTime last_stop, duration;
osel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
if (osel->pending_srcpad) {
/* Do the switch */
gst_output_selector_switch (osel);
}
/* Keep reference to latest buffer to resend it after switch */
if (osel->resend_latest) {
if (osel->latest_buffer)
gst_buffer_unref (osel->latest_buffer);
osel->latest_buffer = gst_buffer_ref (buf);
}
/* Keep track of last stop and use it in NEWSEGMENT start after
switching to a new src pad */
last_stop = GST_BUFFER_TIMESTAMP (buf);
if (GST_CLOCK_TIME_IS_VALID (last_stop)) {
duration = GST_BUFFER_DURATION (buf);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
last_stop += duration;
}
GST_LOG ("setting last stop %" GST_TIME_FORMAT, GST_TIME_ARGS (last_stop));
gst_segment_set_last_stop (&osel->segment, osel->segment.format, last_stop);
}
GST_LOG ("pushing buffer to %" GST_PTR_FORMAT, osel->active_srcpad);
res = gst_pad_push (osel->active_srcpad, buf);
gst_object_unref (osel);
return res;
}
static GstStateChangeReturn
gst_output_selector_change_state (GstElement * element,
GstStateChange transition)
{
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
static gboolean
gst_output_selector_handle_sink_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
GstOutputSelector *sel;
sel = GST_OUTPUT_SELECTOR (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_NEWSEGMENT:
{
gboolean update;
GstFormat format;
gdouble rate, arate;
gint64 start, stop, time;
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
&start, &stop, &time);
GST_DEBUG ("configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
"format %d, "
"%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
gst_segment_set_newsegment_full (&sel->segment, update,
rate, arate, format, start, stop, time);
/* Send newsegment to all src pads */
gst_pad_event_default (pad, event);
break;
}
case GST_EVENT_EOS:
/* Send eos to all src pads */
gst_pad_event_default (pad, event);
break;
default:
/* Send other events to active src pad */
res = gst_pad_push_event (sel->active_srcpad, event);
break;
}
gst_object_unref (sel);
return res;
}

View file

@ -0,0 +1,66 @@
/* GStreamer
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_OUTPUT_SELECTOR_H__
#define __GST_OUTPUT_SELECTOR_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_OUTPUT_SELECTOR \
(gst_output_selector_get_type())
#define GST_OUTPUT_SELECTOR(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OUTPUT_SELECTOR, GstOutputSelector))
#define GST_OUTPUT_SELECTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_OUTPUT_SELECTOR, GstOutputSelectorClass))
#define GST_IS_OUTPUT_SELECTOR(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_OUTPUT_SELECTOR))
#define GST_IS_OUTPUT_SELECTOR_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_OUTPUT_SELECTOR))
typedef struct _GstOutputSelector GstOutputSelector;
typedef struct _GstOutputSelectorClass GstOutputSelectorClass;
struct _GstOutputSelector {
GstElement element;
GstPad *sinkpad;
GstPad *active_srcpad;
GstPad *pending_srcpad;
guint nb_srcpads;
GstSegment segment;
/* resend latest buffer after switch */
gboolean resend_latest;
GstBuffer *latest_buffer;
};
struct _GstOutputSelectorClass {
GstElementClass parent_class;
};
GType gst_output_selector_get_type (void);
G_END_DECLS
#endif /* __GST_OUTPUT_SELECTOR_H__ */

View file

@ -0,0 +1,2 @@
INT64:VOID
VOID:STRING,INT64,INT64

View file

@ -0,0 +1,44 @@
/* GStreamer
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include "gstinputselector.h"
#include "gstoutputselector.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "input-selector",
GST_RANK_NONE, GST_TYPE_INPUT_SELECTOR) &&
gst_element_register (plugin, "output-selector",
GST_RANK_NONE, GST_TYPE_OUTPUT_SELECTOR);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"selector",
"input/output stream selector elements",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -0,0 +1,149 @@
#include <gst/gst.h>
//[.. my_bus_callback goes here ..]
static GMainLoop *loop;
static gboolean
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:{
GError *err;
gchar *debug;
gst_message_parse_error (message, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
g_free (debug);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:
/* end-of-stream */
g_main_loop_quit (loop);
break;
default:
/* unhandled message */
break;
}
/* we want to be notified again the next time there is a message
* on the bus, so returning TRUE (FALSE means we want to stop watching
* for messages on the bus and our callback should not be called again)
*/
return TRUE;
}
static gboolean
switch_cb (gpointer user_data)
{
GstElement *sel = GST_ELEMENT (user_data);
gchar *old_pad_name, *new_pad_name;
g_object_get (G_OBJECT (sel), "active-pad", &old_pad_name, NULL);
if (g_str_equal (old_pad_name, "src0"))
new_pad_name = "src1";
else
new_pad_name = "src0";
g_object_set (G_OBJECT (sel), "active-pad", new_pad_name, NULL);
g_print ("switched from %s to %s\n", old_pad_name, new_pad_name);
g_free (old_pad_name);
return TRUE;
}
gint
main (gint argc, gchar * argv[])
{
GstElement *pipeline, *src, *toverlay, *osel, *sink1, *sink2, *convert;
GstPad *osel_src1, *osel_src2, *sinkpad;
GstBus *bus;
/* init GStreamer */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* create elements */
pipeline = gst_element_factory_make ("pipeline", "pipeline");
src = gst_element_factory_make ("videotestsrc", "src");
toverlay = gst_element_factory_make ("timeoverlay", "timeoverlay");
osel = gst_element_factory_make ("output-selector", "osel");
convert = gst_element_factory_make ("ffmpegcolorspace", "convert");
sink1 = gst_element_factory_make ("xvimagesink", "sink1");
sink2 = gst_element_factory_make ("ximagesink", "sink2");
if (!pipeline || !src || !toverlay || !osel || !convert || !sink1 || !sink2) {
g_print ("missing element\n");
return -1;
}
/* add them to bin */
gst_bin_add_many (GST_BIN (pipeline), src, toverlay, osel, convert, sink1,
sink2, NULL);
/* set properties */
g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
g_object_set (G_OBJECT (src), "do-timestamp", TRUE, NULL);
g_object_set (G_OBJECT (src), "num-buffers", 500, NULL);
g_object_set (G_OBJECT (sink1), "sync", FALSE, NULL);
g_object_set (G_OBJECT (sink2), "sync", FALSE, NULL);
g_object_set (G_OBJECT (osel), "resend-latest", TRUE, NULL);
/* link src ! timeoverlay ! osel */
if (!gst_element_link_many (src, toverlay, osel, NULL)) {
g_print ("linking failed\n");
return -1;
}
/* link output 1 */
sinkpad = gst_element_get_static_pad (sink1, "sink");
osel_src1 = gst_element_get_request_pad (osel, "src%d");
if (gst_pad_link (osel_src1, sinkpad) != GST_PAD_LINK_OK) {
g_print ("linking output 1 failed\n");
return -1;
}
gst_object_unref (sinkpad);
/* link output 2 */
sinkpad = gst_element_get_static_pad (convert, "sink");
osel_src2 = gst_element_get_request_pad (osel, "src%d");
if (gst_pad_link (osel_src2, sinkpad) != GST_PAD_LINK_OK) {
g_print ("linking output 2 failed\n");
return -1;
}
gst_object_unref (sinkpad);
if (!gst_element_link (convert, sink2)) {
g_print ("linking output 2 failed\n");
return -1;
}
/* add switch callback */
g_timeout_add (1000, switch_cb, osel);
/* change to playing */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_add_watch (bus, my_bus_callback, loop);
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* now run */
g_main_loop_run (loop);
/* also clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_element_release_request_pad (osel, osel_src1);
gst_element_release_request_pad (osel, osel_src2);
gst_object_unref (GST_OBJECT (pipeline));
return 0;
}