From 14656be9b563f292038f02707d9c66ce8593df95 Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Sat, 8 Jan 2005 18:31:22 +0000 Subject: [PATCH] gst/playback/: Adding stream selection support plus required properties for applications to use this. Fully fixes #10... Original commit message from CVS: * gst/playback/Makefile.am: * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), (group_destroy), (group_commit), (group_is_muted), (gen_preroll_element), (add_stream), (unknown_type), (probe_triggered), (preroll_unlinked), (mute_stream), (silence_stream), (new_decoded_pad), (setup_substreams), (setup_source), (get_active_source), (mute_group_type), (muted_group_change_state), (set_active_source), (gst_play_base_bin_set_property), (gst_play_base_bin_get_property), (play_base_eos), (gst_play_base_bin_change_state): * gst/playback/gstplaybasebin.h: * gst/playback/gstplaybin.c: (add_sink), (setup_sinks): * gst/playback/gststreaminfo.c: (gst_stream_info_class_init), (gst_stream_info_dispose), (stream_info_mute_pad), (stream_info_change_state), (gst_stream_info_set_mute): * gst/playback/gststreamselector.c: (gst_stream_selector_get_type), (gst_stream_selector_base_init), (gst_stream_selector_class_init), (gst_stream_selector_init), (gst_stream_selector_dispose), (gst_stream_selector_get_linked_pad), (gst_stream_selector_get_caps), (gst_stream_selector_link), (gst_stream_selector_get_linked_pads), (gst_stream_selector_request_new_pad), (gst_stream_selector_chain): * gst/playback/gststreamselector.h: Adding stream selection support plus required properties for applications to use this. Fully fixes #100931. --- ChangeLog | 28 +++ gst/playback/Makefile.am | 9 +- gst/playback/gstplaybasebin.c | 461 ++++++++++++++++++++++++++++++--------- gst/playback/gstplaybasebin.h | 15 +- gst/playback/gstplaybin.c | 202 +++++++---------- gst/playback/gststreaminfo.c | 50 ++++- gst/playback/gststreamselector.c | 278 +++++++++++++++++++++++ gst/playback/gststreamselector.h | 58 +++++ 8 files changed, 860 insertions(+), 241 deletions(-) create mode 100644 gst/playback/gststreamselector.c create mode 100644 gst/playback/gststreamselector.h diff --git a/ChangeLog b/ChangeLog index a11d790..71e9f7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2005-01-08 Ronald S. Bultje + + * gst/playback/Makefile.am: + * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), + (group_destroy), (group_commit), (group_is_muted), + (gen_preroll_element), (add_stream), (unknown_type), + (probe_triggered), (preroll_unlinked), (mute_stream), + (silence_stream), (new_decoded_pad), (setup_substreams), + (setup_source), (get_active_source), (mute_group_type), + (muted_group_change_state), (set_active_source), + (gst_play_base_bin_set_property), (gst_play_base_bin_get_property), + (play_base_eos), (gst_play_base_bin_change_state): + * gst/playback/gstplaybasebin.h: + * gst/playback/gstplaybin.c: (add_sink), (setup_sinks): + * gst/playback/gststreaminfo.c: (gst_stream_info_class_init), + (gst_stream_info_dispose), (stream_info_mute_pad), + (stream_info_change_state), (gst_stream_info_set_mute): + * gst/playback/gststreamselector.c: (gst_stream_selector_get_type), + (gst_stream_selector_base_init), (gst_stream_selector_class_init), + (gst_stream_selector_init), (gst_stream_selector_dispose), + (gst_stream_selector_get_linked_pad), + (gst_stream_selector_get_caps), (gst_stream_selector_link), + (gst_stream_selector_get_linked_pads), + (gst_stream_selector_request_new_pad), (gst_stream_selector_chain): + * gst/playback/gststreamselector.h: + Adding stream selection support plus required properties for + applications to use this. Fully fixes #100931. + 2005-01-08 Benjamin Otte * gst/games/gstpuzzle.c: (nav_event_handler): diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index cf94e4b..b7d0bd0 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -12,7 +12,9 @@ plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstplaybin_la_SOURCES = \ gstplaybin.c \ gstplaybasebin.c \ - gststreaminfo.c + gststreaminfo.c \ + gststreamselector.c + nodist_libgstplaybin_la_SOURCES = $(built_sources) libgstplaybin_la_CFLAGS = $(GST_CFLAGS) libgstplaybin_la_LIBADD = @@ -24,7 +26,10 @@ libgstdecodebin_la_CFLAGS = $(GST_CFLAGS) libgstdecodebin_la_LIBADD = libgstdecodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstplaybasebin.h gststreaminfo.h +noinst_HEADERS = \ + gstplaybasebin.h \ + gststreaminfo.h \ + gststreamselector.h noinst_PROGRAMS = test decodetest test2 test3 test4 diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c index 76fe31a..45df610 100644 --- a/gst/playback/gstplaybasebin.c +++ b/gst/playback/gstplaybasebin.c @@ -23,6 +23,7 @@ #include #include "gstplaybasebin.h" +#include "gststreamselector.h" #include "gstplay-marshal.h" GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); @@ -39,7 +40,10 @@ enum ARG_NSTREAMS, ARG_QUEUE_SIZE, ARG_STREAMINFO, - ARG_SOURCE + ARG_SOURCE, + ARG_VIDEO, + ARG_AUDIO, + ARG_TEXT }; /* signals */ @@ -76,6 +80,12 @@ static void gst_play_base_bin_error (GstElement * element, static void gst_play_base_bin_found_tag (GstElement * element, GstElement * source, const GstTagList * taglist, gpointer data); +static void set_active_source (GstPlayBaseBin * play_base_bin, + GstStreamType type, gint source_num); +static gboolean probe_triggered (GstProbe * probe, GstData ** data, + gpointer user_data); +static void setup_substreams (GstPlayBaseBin * play_base_bin); + static GstElementClass *element_class; static GstElementClass *parent_class; static guint gst_play_base_bin_signals[LAST_SIGNAL] = { 0 }; @@ -140,6 +150,19 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) g_param_spec_object ("source", "Source", "Source element", GST_TYPE_ELEMENT, G_PARAM_READABLE)); + g_object_class_install_property (gobject_klass, ARG_VIDEO, + g_param_spec_int ("current-video", "Current video", + "Currently playing video stream (-1 = none)", + -1, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_klass, ARG_AUDIO, + g_param_spec_int ("current-audio", "Current audio", + "Currently playing audio stream (-1 = none)", + -1, G_MAXINT, -1, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_klass, ARG_TEXT, + g_param_spec_int ("current-text", "Current text", + "Currently playing text stream (-1 = none)", + -1, G_MAXINT, -1, G_PARAM_READWRITE)); + GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0, "playbasebin"); @@ -263,17 +286,21 @@ static void group_destroy (GstPlayBaseGroup * group) { GstPlayBaseBin *play_base_bin = group->bin; - GList *prerolls, *infos; + GList *infos; + gint n; GST_LOG ("removing group %p", group); /* remove the preroll queues */ - for (prerolls = group->preroll_elems; prerolls; - prerolls = g_list_next (prerolls)) { - GstElement *element = GST_ELEMENT (prerolls->data); + for (n = 0; n < NUM_TYPES; n++) { + GstElement *element = group->type[n].preroll; GstPad *pad; guint sig_id; GstElement *fakesrc; + const GList *item; + + if (!element) + continue; /* have to unlink the unlink handler first because else we * are going to link an element in the finalize handler */ @@ -288,10 +315,20 @@ group_destroy (GstPlayBaseGroup * group) } /* remove any fakesrc elements for this preroll element */ - fakesrc = (GstElement *) g_object_get_data (G_OBJECT (element), "fakesrc"); - if (fakesrc != NULL) { - GST_LOG ("removing fakesrc"); - gst_bin_remove (GST_BIN (play_base_bin->thread), fakesrc); + for (item = gst_element_get_pad_list (group->type[n].selector); + item != NULL; item = item->next) { + GstPad *pad = item->data; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SINK) + continue; + + fakesrc = (GstElement *) g_object_get_data (G_OBJECT (pad), "fakesrc"); + if (fakesrc != NULL) { + GST_LOG ("removing fakesrc from %s:%s", + gst_pad_get_name (pad), + GST_ELEMENT_NAME (gst_pad_get_parent (pad))); + gst_bin_remove (GST_BIN (play_base_bin->thread), fakesrc); + } } /* if the group is currently being played, we have to remove the element @@ -299,12 +336,15 @@ group_destroy (GstPlayBaseGroup * group) if (get_active_group (play_base_bin) == group) { GST_LOG ("removing preroll element %s", gst_element_get_name (element)); gst_bin_remove (GST_BIN (play_base_bin->thread), element); + gst_bin_remove (GST_BIN (play_base_bin->thread), group->type[n].selector); } else { /* else we can just unref it */ gst_object_unref (GST_OBJECT (element)); + gst_object_unref (GST_OBJECT (group->type[n].selector)); } + + group->type[n].preroll = NULL; } - g_list_free (group->preroll_elems); /* free the streaminfo too */ for (infos = group->streaminfo; infos; infos = g_list_next (infos)) { @@ -323,7 +363,6 @@ static void group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal) { GstPlayBaseGroup *group = play_base_bin->building_group; - GList *prerolls; /* if an element signalled a no-more-pads after we stopped due * to preroll, the group is NULL. This is not an error */ @@ -334,6 +373,8 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal) GST_DEBUG ("Group loading failed, bailing out"); } } else { + gint n; + GST_DEBUG ("group %p done", group); play_base_bin->queued_groups = g_list_append (play_base_bin->queued_groups, @@ -343,11 +384,13 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal) /* remove signals. We don't want anymore signals from the preroll * elements at this stage. */ - for (prerolls = group->preroll_elems; prerolls; - prerolls = g_list_next (prerolls)) { - GstElement *element = GST_ELEMENT (prerolls->data); + for (n = 0; n < NUM_TYPES; n++) { + GstElement *element = group->type[n].preroll; guint sig_id; + if (!element) + continue; + sig_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (element), "signal_id")); @@ -367,15 +410,13 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal) static gboolean group_is_muted (GstPlayBaseGroup * group) { - GList *infos; - - for (infos = group->streaminfo; infos; infos = g_list_next (infos)) { - GstStreamInfo *info = GST_STREAM_INFO (infos->data); + gint n; - /* if we find a non muded group we can return FALSE */ - if (!info->mute) + for (n = 0; n < NUM_TYPES; n++) { + if (group->type[n].preroll && !group->type[n].done) return FALSE; } + return TRUE; } @@ -394,26 +435,62 @@ queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) * of the queues to fill. The assumption is that all the dynamic * streams will be detected by that time. */ -static GstElement * -gen_preroll_element (GstPlayBaseBin * play_base_bin, GstPad * pad) +static void +gen_preroll_element (GstPlayBaseBin * play_base_bin, + GstPlayBaseGroup * group, GstStreamType type, GstPad * pad, + GstStreamInfo * info) { - GstElement *element; + GstElement *selector, *preroll; gchar *name; + const gchar *prename; guint sig; + GstPad *preroll_pad; + GstProbe *probe; + + if (type == GST_STREAM_TYPE_VIDEO) + prename = "video"; + else if (type == GST_STREAM_TYPE_TEXT) + prename = "text"; + else if (type == GST_STREAM_TYPE_AUDIO) + prename = "audio"; + else + g_return_if_reached (); + + /* create stream selector */ + name = g_strdup_printf ("selector_%s_%s", prename, gst_pad_get_name (pad)); + selector = g_object_new (GST_TYPE_STREAM_SELECTOR, NULL); + gst_object_set_name (GST_OBJECT (selector), name); + g_free (name); - name = g_strdup_printf ("preroll_%s", gst_pad_get_name (pad)); - element = gst_element_factory_make ("queue", name); - g_object_set (G_OBJECT (element), + /* create preroll queue */ + name = g_strdup_printf ("preroll_%s_%s", prename, gst_pad_get_name (pad)); + preroll = gst_element_factory_make ("queue", name); + g_object_set (G_OBJECT (preroll), "max-size-buffers", 0, "max-size-bytes", 10 * 1024 * 1024, "max-size-time", play_base_bin->queue_size, NULL); - sig = g_signal_connect (G_OBJECT (element), "overrun", + sig = g_signal_connect (G_OBJECT (preroll), "overrun", G_CALLBACK (queue_overrun), play_base_bin); /* keep a ref to the signal id so that we can disconnect the signal callback * when we are done with the preroll */ - g_object_set_data (G_OBJECT (element), "signal_id", GINT_TO_POINTER (sig)); + g_object_set_data (G_OBJECT (preroll), "signal_id", GINT_TO_POINTER (sig)); g_free (name); - return element; + /* listen for EOS */ + preroll_pad = gst_element_get_pad (preroll, "src"); + probe = gst_probe_new (FALSE, probe_triggered, info); + /* have to REALIZE the pad as we cannot attach a padprobe to a ghostpad */ + gst_pad_add_probe (preroll_pad, probe); + + /* add to group list */ + gst_element_link (selector, preroll); + group->type[type - 1].selector = selector; + group->type[type - 1].preroll = preroll; + gst_bin_add (GST_BIN (play_base_bin->thread), selector); + gst_element_set_state (selector, GST_STATE_PLAYING); + gst_bin_add (GST_BIN (play_base_bin->thread), preroll); + gst_element_set_state (preroll, GST_STATE_PAUSED); + + play_base_bin->threaded = TRUE; } static void @@ -456,19 +533,8 @@ add_stream (GstPlayBaseGroup * group, GstStreamInfo * info) g_object_set_data (G_OBJECT (info), "group", group); group->streaminfo = g_list_append (group->streaminfo, info); - switch (info->type) { - case GST_STREAM_TYPE_AUDIO: - group->naudiopads++; - break; - case GST_STREAM_TYPE_VIDEO: - group->nvideopads++; - break; - case GST_STREAM_TYPE_TEXT: - group->ntextpads++; - break; - default: - group->nunknownpads++; - break; + if (info->type > 0 && info->type <= NUM_TYPES) { + group->type[info->type - 1].npads++; } } @@ -484,7 +550,7 @@ unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, GstPlayBaseGroup *group = get_building_group (play_base_bin); capsstr = gst_caps_to_string (caps); - g_warning ("don't know how to handle %s", capsstr); + g_message /*warning */ ("don't know how to handle %s", capsstr); /* add the stream to the list */ info = gst_stream_info_new (GST_OBJECT (pad), GST_STREAM_TYPE_UNKNOWN, @@ -540,15 +606,21 @@ probe_triggered (GstProbe * probe, GstData ** data, gpointer user_data) GstEvent *event = GST_EVENT (*data); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + gint num_groups = 0; gboolean have_left; GST_DEBUG ("probe got EOS in group %p", group); /* mute this stream */ - g_object_set (G_OBJECT (info), "mute", TRUE, NULL); + //g_object_set (G_OBJECT (info), "mute", TRUE, NULL); + if (info->type > 0 && info->type <= NUM_TYPES) + group->type[info->type - 1].done = TRUE; /* see if we have some more groups left to play */ - have_left = (g_list_length (play_base_bin->queued_groups) > 1); + num_groups = g_list_length (play_base_bin->queued_groups); + if (play_base_bin->building_group) + num_groups++; + have_left = (num_groups > 1); /* see if the complete group is muted */ if (!group_is_muted (group)) { @@ -561,14 +633,15 @@ probe_triggered (GstProbe * probe, GstData ** data, gpointer user_data) } if (have_left) { + GST_DEBUG ("changing state for group change"); gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); /* ok, get rid of the current group then */ group_destroy (group); /* removing the current group brings the next group * active */ play_base_bin->queued_groups = - g_list_delete_link (play_base_bin->queued_groups, - play_base_bin->queued_groups); + g_list_remove (play_base_bin->queued_groups, group); + setup_substreams (play_base_bin); GST_DEBUG ("switching to next group %p", play_base_bin->queued_groups->data); /* and signal the new group */ @@ -578,9 +651,12 @@ probe_triggered (GstProbe * probe, GstData ** data, gpointer user_data) GST_DEBUG ("Syncing state from %d", GST_STATE (play_base_bin->thread)); gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING); + GST_DEBUG ("done"); /* get rid of the EOS event */ return FALSE; + } else { + GST_LOG ("Last group done, EOS"); } } else if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { GstTagList *taglist; @@ -616,7 +692,7 @@ static void preroll_unlinked (GstPad * pad, GstPad * peerpad, GstPlayBaseBin * play_base_bin) { - GstElement *fakesrc, *queue; + GstElement *fakesrc; guint sig_id; /* make a fakesrc that will just emit one EOS */ @@ -629,8 +705,7 @@ preroll_unlinked (GstPad * pad, GstPad * peerpad, gst_bin_add (GST_BIN (play_base_bin->thread), fakesrc); /* keep track of these patch elements */ - queue = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (pad))); - g_object_set_data (G_OBJECT (queue), "fakesrc", fakesrc); + g_object_set_data (G_OBJECT (pad), "fakesrc", fakesrc); /* now unlink the unlinked signal so that it is not called again when * we destroy the queue */ @@ -641,6 +716,30 @@ preroll_unlinked (GstPad * pad, GstPad * peerpad, } } +/* Mute stream on first data - for header-is-in-stream-stuff + * (vorbis, ogmtext). */ +static gboolean +mute_stream (GstProbe * probe, GstData ** d, gpointer data) +{ + GstStreamInfo *info = GST_STREAM_INFO (data); + + if (GST_IS_BUFFER (*d)) { + g_object_set (G_OBJECT (info), "mute", TRUE, NULL); + gst_pad_remove_probe ((GstPad *) GST_PAD_REALIZE (info->object), probe); + gst_probe_destroy (probe); + } + + /* no data */ + return FALSE; +} + +/* Eat data. */ +static gboolean +silence_stream (GstProbe * probe, GstData ** d, gpointer data) +{ + /* no data */ + return FALSE; +} /* signal fired when decodebin has found a new raw pad. We create * a preroll element if needed and the appropriate streaminfo. @@ -652,13 +751,12 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, GstStructure *structure; const gchar *mimetype; GstCaps *caps; - GstElement *new_element = NULL; GstStreamInfo *info; GstStreamType type; - GstPad *srcpad; - gboolean need_preroll; + GstPad *sinkpad; GstPlayBaseGroup *group; GstProbe *probe; + guint sig; GST_DEBUG ("play base: new decoded pad %d", last); @@ -679,69 +777,46 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, group->nstreams++; - need_preroll = FALSE; - if (g_str_has_prefix (mimetype, "audio/")) { type = GST_STREAM_TYPE_AUDIO; - /* first audio pad gets a preroll element */ - if (group->naudiopads == 0) { - need_preroll = TRUE; - } } else if (g_str_has_prefix (mimetype, "video/")) { type = GST_STREAM_TYPE_VIDEO; - /* first video pad gets a preroll element */ - if (group->nvideopads == 0) { - need_preroll = TRUE; - } } else if (g_str_has_prefix (mimetype, "text/")) { type = GST_STREAM_TYPE_TEXT; - /* first text pad gets a preroll element */ - if (group->ntextpads == 0) { - need_preroll = TRUE; - } } else { type = GST_STREAM_TYPE_UNKNOWN; } - if (last || !need_preroll) { - GST_DEBUG ("play base: pad does not need preroll"); - srcpad = pad; - } else { - guint sig; - GstPad *sinkpad; - - GST_DEBUG ("play base: pad needs preroll"); - - new_element = gen_preroll_element (play_base_bin, pad); - srcpad = gst_element_get_pad (new_element, "src"); - gst_bin_add (GST_BIN (play_base_bin->thread), new_element); - play_base_bin->threaded = TRUE; - - group->preroll_elems = g_list_prepend (group->preroll_elems, new_element); - - gst_element_set_state (new_element, GST_STATE_READY); + info = gst_stream_info_new (GST_OBJECT (pad), type, NULL, caps); + if (type > 0 && type <= NUM_TYPES) { + /* first pad of each type gets a selector + preroll queue */ + if (group->type[type - 1].npads == 0) { + GST_DEBUG ("play base: pad needs new preroll"); + gen_preroll_element (play_base_bin, group, type, pad, info); + } + } - sinkpad = gst_element_get_pad (new_element, "sink"); - gst_pad_link (pad, sinkpad); - /* make sure we catch unlink signals */ - sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked", - G_CALLBACK (preroll_unlinked), play_base_bin); - /* keep a ref to the signal id so that we can disconnect the signal callback */ - g_object_set_data (G_OBJECT (sinkpad), "unlinked_id", - GINT_TO_POINTER (sig)); + /* add to stream selector */ + sinkpad = gst_element_get_pad (group->type[type - 1].selector, "sink%d"); + /* make sure we catch unlink signals */ + sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked", + G_CALLBACK (preroll_unlinked), play_base_bin); + /* keep a ref to the signal id so that we can disconnect the signal callback */ + g_object_set_data (G_OBJECT (sinkpad), "unlinked_id", GINT_TO_POINTER (sig)); + gst_pad_link (pad, sinkpad); - gst_element_set_state (new_element, GST_STATE_PAUSED); - } /* add the stream to the list */ - info = gst_stream_info_new (GST_OBJECT (srcpad), type, NULL, caps); gst_caps_free (caps); info->origin = GST_OBJECT (pad); - add_stream (group, info); - /* install a probe so that we know when this group has ended */ - probe = gst_probe_new (FALSE, probe_triggered, info); - /* have to REALIZE the pad as we cannot attach a padprobe to a ghostpad */ - gst_pad_add_probe (GST_PAD_REALIZE (srcpad), probe); + /* select 1st for now - we'll select a preferred one after preroll */ + if (type == GST_STREAM_TYPE_UNKNOWN || group->type[type - 1].npads > 0) { + probe = gst_probe_new (FALSE, silence_stream, info); + gst_pad_add_probe (GST_PAD_REALIZE (pad), probe); + g_object_set_data (G_OBJECT (pad), "eat_probe", probe); + } + + add_stream (group, info); /* signal the no more pads after adding the stream */ if (last) @@ -942,6 +1017,48 @@ gen_source_element (GstPlayBaseBin * play_base_bin, GList ** subbins) return bin; } +/* Setup the substreams (to be called right after group_commit ()) */ +static void +setup_substreams (GstPlayBaseBin * play_base_bin) +{ + GstPlayBaseGroup *group = get_active_group (play_base_bin); + GstProbe *probe; + gint n; + const GList *item; + + /* Remove the eat probes */ + for (item = group->streaminfo; item; item = item->next) { + GstStreamInfo *info = item->data; + + probe = g_object_get_data (G_OBJECT (info->object), "eat_probe"); + if (probe) { + gst_pad_remove_probe (GST_PAD_REALIZE (info->object), probe); + gst_probe_destroy (probe); + } + + /* now remove unknown pads */ + if (info->type == GST_STREAM_TYPE_UNKNOWN) { + GstProbe *probe; + + probe = gst_probe_new (FALSE, mute_stream, info); + gst_pad_add_probe (GST_PAD_REALIZE (info->object), probe); + } + } + + /* now check if the requested current streams exist */ + for (n = 0; n < NUM_TYPES; n++) { + if (play_base_bin->current[n] >= group->type[n].npads) { + play_base_bin->current[n] = group->type[n].npads > 0 ? 0 : -1; + } + } + + /* now acticate the right sources. Don't forget that during preroll, + * we set the first source to forwarding and ignored the rest. */ + for (n = 0; n < NUM_TYPES; n++) { + set_active_source (play_base_bin, n + 1, play_base_bin->current[n]); + } +} + /* construct and run the source and decoder elements until we found * all the streams or until a preroll queue has been filled. */ @@ -1133,14 +1250,125 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) play_base_bin->need_rebuild = FALSE; } - /* make subs iterate from now on */ - for (item = play_base_bin->subtitles; item; item = item->next) { - gst_bin_add (GST_BIN (play_base_bin->thread), item->data); + if (get_active_group (play_base_bin) != NULL) { + /* make subs iterate from now on */ + for (item = play_base_bin->subtitles; item; item = item->next) { + gst_bin_add (GST_BIN (play_base_bin->thread), item->data); + } + setup_substreams (play_base_bin); } return TRUE; } +/* + * Multi-stream management. -1 = none. + */ +static gint +get_active_source (GstPlayBaseBin * play_base_bin, GstStreamType type) +{ + GstPlayBaseGroup *group; + GList *s; + gint num = 0; + + group = get_active_group (play_base_bin); + if (!group) + return -1; + + for (s = group->streaminfo; s; s = s->next) { + GstStreamInfo *info = s->data; + + if (info->type == type) { + if (!info->mute) { + return num; + } else { + num++; + } + } + } + + return -1; +} + +/* Kill pad reactivation on state change. */ + +static void muted_group_change_state (GstElement * element, + gint old_state, gint new_state, gpointer data); + +static void +mute_group_type (GstPlayBaseGroup * group, GstStreamType type, gboolean mute) +{ + gboolean active = !mute; + + gst_pad_set_active (gst_element_get_pad (group->type[type - 1].preroll, + "src"), active); + gst_pad_set_active (gst_element_get_pad (group->type[type - 1].preroll, + "sink"), active); + gst_pad_set_active (gst_element_get_pad (group->type[type - 1].selector, + "src"), active); + + if (mute) { + g_signal_connect (group->type[type - 1].preroll, "state-change", + G_CALLBACK (muted_group_change_state), group); + } else { + g_signal_handlers_disconnect_by_func (group->type[type - 1].preroll, + G_CALLBACK (muted_group_change_state), group); + } +} + +static void +muted_group_change_state (GstElement * element, + gint old_state, gint new_state, gpointer data) +{ + GstPlayBaseGroup *group = data; + + if (new_state == GST_STATE_PLAYING) { + gint n; + + for (n = 0; n < NUM_TYPES; n++) { + if (group->type[n].selector == element) { + mute_group_type (group, n + 1, TRUE); + } + } + } +} + +static void +set_active_source (GstPlayBaseBin * play_base_bin, + GstStreamType type, gint source_num) +{ + GstPlayBaseGroup *group; + GList *s; + gint num = 0; + gboolean have_active = FALSE; + + GST_LOG ("Changing active source of type %d to %d", type, source_num); + play_base_bin->current[type - 1] = source_num; + + group = get_active_group (play_base_bin); + if (!group || !group->type[type - 1].preroll) + return; + + for (s = group->streaminfo; s; s = s->next) { + GstStreamInfo *info = s->data; + + if (info->type == type) { + if (num == source_num) { + g_object_set (s->data, "mute", FALSE, NULL); + have_active = TRUE; + } else { + GstProbe *probe; + + probe = gst_probe_new (FALSE, mute_stream, info); + gst_pad_add_probe (GST_PAD_REALIZE (info->object), probe); + } + num++; + } + } + + mute_group_type (group, type, !have_active); +} + static void gst_play_base_bin_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -1175,6 +1403,18 @@ gst_play_base_bin_set_property (GObject * object, guint prop_id, case ARG_QUEUE_SIZE: play_base_bin->queue_size = g_value_get_uint64 (value); break; + case ARG_VIDEO: + set_active_source (play_base_bin, + GST_STREAM_TYPE_VIDEO, g_value_get_int (value)); + break; + case ARG_AUDIO: + set_active_source (play_base_bin, + GST_STREAM_TYPE_AUDIO, g_value_get_int (value)); + break; + case ARG_TEXT: + set_active_source (play_base_bin, + GST_STREAM_TYPE_TEXT, g_value_get_int (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1222,6 +1462,18 @@ gst_play_base_bin_get_property (GObject * object, guint prop_id, GValue * value, } else g_value_set_object (value, play_base_bin->source); break; + case ARG_VIDEO: + g_value_set_int (value, get_active_source (play_base_bin, + GST_STREAM_TYPE_VIDEO)); + break; + case ARG_AUDIO: + g_value_set_int (value, get_active_source (play_base_bin, + GST_STREAM_TYPE_AUDIO)); + break; + case ARG_TEXT: + g_value_set_int (value, get_active_source (play_base_bin, + GST_STREAM_TYPE_TEXT)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1233,6 +1485,8 @@ play_base_eos (GstBin * bin, GstPlayBaseBin * play_base_bin) { no_more_pads (GST_ELEMENT (bin), play_base_bin); + GST_LOG ("forwarding EOS"); + gst_element_set_eos (GST_ELEMENT (play_base_bin)); } @@ -1349,6 +1603,7 @@ gst_play_base_bin_change_state (GstElement * element) GST_DEBUG ("emit signal"); g_signal_emit (play_base_bin, gst_play_base_bin_signals[SETUP_OUTPUT_PADS_SIGNAL], 0); + GST_DEBUG ("done"); } else { /* clean up leftover groups */ remove_groups (play_base_bin); diff --git a/gst/playback/gstplaybasebin.h b/gst/playback/gstplaybasebin.h index 7c7fb4e..9a4ed1b 100644 --- a/gst/playback/gstplaybasebin.h +++ b/gst/playback/gstplaybasebin.h @@ -52,12 +52,13 @@ typedef struct gint nstreams; GList *streaminfo; - gint naudiopads; - gint nvideopads; - gint ntextpads; - gint nunknownpads; - - GList *preroll_elems; + struct { + gint npads; + GstElement *preroll; + GstElement *selector; + gboolean done; +#define NUM_TYPES 3 + } type[NUM_TYPES]; /* AUDIO, VIDEO, TEXT */ } GstPlayBaseGroup; struct _GstPlayBaseBin { @@ -67,6 +68,8 @@ struct _GstPlayBaseBin { gboolean threaded; guint64 queue_size; + gint current[NUM_TYPES]; + /* internal thread */ GstElement *thread; gchar *uri; diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c index 7c2e61d..26ebb1d 100644 --- a/gst/playback/gstplaybin.c +++ b/gst/playback/gstplaybin.c @@ -671,74 +671,72 @@ remove_sinks (GstPlayBin * play_bin) * this should eventually be handled with a tuner interface so that * one can switch the streams. */ +static gboolean +add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad) +{ + GstPad *sinkpad; + gboolean res; + + /* we found a sink for this stream, now try to install it */ + gst_bin_add (GST_BIN (play_bin), sink); + GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n", + GST_STATE (sink), GST_STATE (play_bin), + GST_STATE (gst_pad_get_parent (srcpad))); + sinkpad = gst_element_get_pad (sink, "sink"); + + /* try to link the pad of the sink to the stream */ + res = gst_pad_link (srcpad, sinkpad); + if (!res) { + gchar *capsstr; + + /* could not link this stream */ + capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad)); + g_warning ("could not link %s", capsstr); + g_free (capsstr); + GST_LOG ("removing sink %p", sink); + gst_bin_remove (GST_BIN (play_bin), sink); + } else { + /* we got the sink succesfully linked, now keep the sink + * in out internal list */ + play_bin->sinks = g_list_prepend (play_bin->sinks, sink); + } + + return res; +} + static void setup_sinks (GstPlayBaseBin * play_base_bin) { GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin); - GList *streaminfo; - GList *s; - gint num_audio = 0; - gint num_video = 0; - gint num_text = 0; + GstPlayBaseGroup *group; + GList *streaminfo = NULL, *s; gboolean need_vis = FALSE; gboolean need_text = FALSE; GstPad *textsrcpad = NULL, *textsinkpad = NULL; + GstElement *sink; - /* FIXME: do this nicer, like taking a look at the installed - * bins and figuring out if we can simply reconnect them, remove - * or add them. */ - if (GST_STATE (play_base_bin) == GST_STATE_PLAYING) { + /* get rid of existing sinks */ + if (play_bin->sinks) { remove_sinks (play_bin); } GST_DEBUG ("setupsinks"); - /* get info about the stream */ - g_object_get (G_OBJECT (play_bin), "stream-info", &streaminfo, NULL); - /* first examine the streams we have */ - for (s = streaminfo; s; s = g_list_next (s)) { - GObject *obj = G_OBJECT (s->data); - gint type; - GstPad *srcpad; - - g_object_get (obj, "type", &type, NULL); - /* we don't care about streams with their own sink */ - if (type == 4) - continue; - - /* else we need to get the pad */ - g_object_get (obj, "object", &srcpad, NULL); - - /* hmm.. pad is allready linked */ - if (gst_pad_is_linked (srcpad)) - continue; - - if (type == 1) { - num_audio++; - } else if (type == 2) { - num_video++; - } else if (type == 3) { - num_text++; - } - } - /* no video, use vis */ - if (num_video == 0 && num_audio > 0 && play_bin->visualisation) { - need_vis = TRUE; - } else if (num_video > 0 && num_text > 0) { + /* find out what to do */ + group = play_base_bin->queued_groups->data; + if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 && + group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) { need_text = TRUE; + } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 && + group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 && + play_bin->visualisation != NULL) { + need_vis = TRUE; } - num_audio = 0; - num_video = 0; - num_text = 0; - /* now actually connect everything */ + g_object_get (G_OBJECT (play_base_bin), "stream-info", &streaminfo, NULL); for (s = streaminfo; s; s = g_list_next (s)) { GObject *obj = G_OBJECT (s->data); gint type; - GstPad *srcpad, *sinkpad; - GstElement *sink = NULL; - gboolean res; - gboolean mute = FALSE; GstObject *object; g_object_get (obj, "type", &type, NULL); @@ -747,91 +745,41 @@ setup_sinks (GstPlayBaseBin * play_base_bin) /* use the sink elements as seek entry point */ if (type == 4) { play_bin->seekables = g_list_prepend (play_bin->seekables, object); - continue; } + } - srcpad = GST_PAD (object); - - /* pas is allready linked, go to the next pad */ - if (gst_pad_is_linked (srcpad)) - continue; - - if (type == 1) { - if (num_audio > 0) { - g_warning ("two audio streams found, playing first one"); - mute = TRUE; - } else { - if (need_vis) { - sink = gen_vis_element (play_bin); - } else { - sink = gen_audio_element (play_bin); - } - num_audio++; - } - } else if (type == 2) { - if (num_video > 0) { - g_warning ("two video streams found, playing first one"); - mute = TRUE; - } else { - if (need_text) { - sink = gen_text_element (play_bin); - textsinkpad = gst_element_get_pad (sink, "text_sink"); - } else { - sink = gen_video_element (play_bin); - } - num_video++; - } - } else if (type == 3) { - if (num_text > 0) { - g_warning ("two subtitle streams found, playing first one"); - mute = TRUE; - } else { - textsrcpad = srcpad; - num_text++; - } - } else if (type == 4) { - /* we can ignore these streams here */ + /* link audio */ + if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) { + if (need_vis) { + sink = gen_vis_element (play_bin); } else { - g_warning ("unknown stream found"); - mute = TRUE; + sink = gen_audio_element (play_bin); } + //gst_element_link (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll, sink); + add_sink (play_bin, sink, + gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll, + "src")); + } - /* we found a sink for this stream, now try to install it */ - if (sink != NULL) { - gst_bin_add (GST_BIN (play_bin), sink); - GST_DEBUG ("Adding sink with state %d (parent: %d, peer: %d)\n", - GST_STATE (sink), GST_STATE (play_bin), - GST_STATE (gst_pad_get_parent (srcpad))); - sinkpad = gst_element_get_pad (sink, "sink"); - - /* try to link the pad of the sink to the stream */ - res = gst_pad_link (srcpad, sinkpad); - if (!res) { - gchar *capsstr; - - /* could not link this stream */ - capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad)); - g_warning ("could not link %s", capsstr); - g_free (capsstr); - GST_LOG ("removing sink %p", sink); - gst_bin_remove (GST_BIN (play_bin), sink); - mute = TRUE; - } else { - /* we got the sink succesfully linked, now keep the sink - * in out internal list */ - play_bin->sinks = g_list_prepend (play_bin->sinks, sink); + /* link video */ + if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) { + if (need_text) { + sink = gen_text_element (play_bin); + + textsinkpad = gst_element_get_pad (sink, "text_sink"); + textsrcpad = + gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll, + "src"); + if (textsinkpad && textsrcpad) { + gst_pad_link (textsrcpad, textsinkpad); } + } else { + sink = gen_video_element (play_bin); } - if (mute) { - /* no sink found/needed for this stream. We mute the stream - * so that it does not take any resources */ - g_object_set (G_OBJECT (obj), "mute", TRUE, NULL); - } - } - - /* if subtitles, link */ - if (textsrcpad && num_video > 0) { - gst_pad_link (textsrcpad, textsinkpad); + //gst_element_link (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll, sink); + add_sink (play_bin, sink, + gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll, + "src")); } } diff --git a/gst/playback/gststreaminfo.c b/gst/playback/gststreaminfo.c index 80362b1..3792cb4 100644 --- a/gst/playback/gststreaminfo.c +++ b/gst/playback/gststreaminfo.c @@ -25,6 +25,9 @@ #include #include "gststreaminfo.h" +GST_DEBUG_CATEGORY_STATIC (gst_streaminfo_debug); +#define GST_CAT_DEFAULT gst_streaminfo_debug + /* props */ enum { @@ -70,6 +73,9 @@ static void gst_stream_info_class_init (GstStreamInfoClass * klass); static void gst_stream_info_init (GstStreamInfo * stream_info); static void gst_stream_info_dispose (GObject * object); +static void stream_info_change_state (GstElement * element, + gint old_state, gint new_state, gpointer data); + static void gst_stream_info_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * spec); static void gst_stream_info_get_property (GObject * object, guint prop_id, @@ -140,6 +146,9 @@ gst_stream_info_class_init (GstStreamInfoClass * klass) gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_stream_info_dispose); + + GST_DEBUG_CATEGORY_INIT (gst_streaminfo_debug, "streaminfo", 0, + "Playbin Stream Info"); } @@ -181,8 +190,16 @@ gst_stream_info_dispose (GObject * object) stream_info = GST_STREAM_INFO (object); - gst_object_unref (stream_info->object); - stream_info->object = NULL; + if (stream_info->object) { + if (GST_PAD_REALIZE (stream_info->object)) { + g_signal_handlers_disconnect_by_func (gst_pad_get_parent ((GstPad *) + GST_PAD_REALIZE (stream_info->object)), + G_CALLBACK (stream_info_change_state), stream_info); + } + + gst_object_unref (stream_info->object); + stream_info->object = NULL; + } stream_info->origin = NULL; stream_info->type = GST_STREAM_TYPE_UNKNOWN; g_free (stream_info->decoder); @@ -207,6 +224,8 @@ stream_info_mute_pad (GstStreamInfo * stream_info, GstPad * pad, gboolean mute) GST_DEBUG_OBJECT (stream_info, "%s %s:%s", debug_str, GST_DEBUG_PAD_NAME (pad)); gst_pad_set_active (pad, activate); + if (gst_pad_get_parent (pad)->numsrcpads > 1) + return; for (int_links = gst_pad_get_internal_links (pad); int_links; int_links = g_list_next (int_links)) { @@ -231,6 +250,20 @@ stream_info_mute_pad (GstStreamInfo * stream_info, GstPad * pad, gboolean mute) } } +static void +stream_info_change_state (GstElement * element, + gint old_state, gint new_state, gpointer data) +{ + GstStreamInfo *stream_info = data; + + if (new_state == GST_STATE_PLAYING) { + /* state change will annoy us */ + g_return_if_fail (stream_info->mute == TRUE); + GST_DEBUG_OBJECT (stream_info, "Re-muting pads after state-change"); + stream_info_mute_pad (stream_info, GST_PAD (stream_info->object), TRUE); + } +} + gboolean gst_stream_info_set_mute (GstStreamInfo * stream_info, gboolean mute) { @@ -243,7 +276,18 @@ gst_stream_info_set_mute (GstStreamInfo * stream_info, gboolean mute) if (mute != stream_info->mute) { stream_info->mute = mute; - stream_info_mute_pad (stream_info, GST_PAD (stream_info->object), mute); + stream_info_mute_pad (stream_info, + (GstPad *) GST_PAD_REALIZE (stream_info->object), mute); + + if (mute) { + g_signal_connect (gst_pad_get_parent ((GstPad *) + GST_PAD_REALIZE (stream_info->object)), "state-change", + G_CALLBACK (stream_info_change_state), stream_info); + } else { + g_signal_handlers_disconnect_by_func (gst_pad_get_parent ((GstPad *) + GST_PAD_REALIZE (stream_info->object)), + G_CALLBACK (stream_info_change_state), stream_info); + } } return TRUE; } diff --git a/gst/playback/gststreamselector.c b/gst/playback/gststreamselector.c new file mode 100644 index 0000000..4a766ce --- /dev/null +++ b/gst/playback/gststreamselector.c @@ -0,0 +1,278 @@ +/* GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005 Ronald S. Bultje + * + * 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. + */ + +/* + * !!!!!!!!!!!!!!!!! Big phat warning. !!!!!!!!!!!!!!!!!!!!!! + * + * The pads on the sinkside can be filled and the application is + * supposed to enable/disable them. The plugin will receive input + * data over the currently active pad and take care of data + * forwarding and negotiation. This plugin does nothing fancy. It + * exists to be light-weight and simple. + * + * This is not a generic switch element. This is not to be used for + * any such purpose. Patches to make it do that will be rejected. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gststreamselector.h" + +GST_DEBUG_CATEGORY_STATIC (stream_selector_debug); +#define GST_CAT_DEFAULT stream_selector_debug + +static GstStaticPadTemplate gst_stream_selector_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_stream_selector_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static void gst_stream_selector_dispose (GObject * object); +static void gst_stream_selector_init (GstStreamSelector * sel); +static void gst_stream_selector_base_init (GstStreamSelectorClass * klass); +static void gst_stream_selector_class_init (GstStreamSelectorClass * klass); + +static GstCaps *gst_stream_selector_get_caps (GstPad * pad); +static GstPadLinkReturn gst_stream_selector_link (GstPad * pad, + const GstCaps * caps); +static GList *gst_stream_selector_get_linked_pads (GstPad * pad); +static GstPad *gst_stream_selector_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * unused); +static void gst_stream_selector_chain (GstPad * pad, GstData * data); + +static GstElementClass *parent_class = NULL; + +GType +gst_stream_selector_get_type (void) +{ + static GType stream_selector_type = 0; + + if (!stream_selector_type) { + static const GTypeInfo stream_selector_info = { + sizeof (GstStreamSelectorClass), + (GBaseInitFunc) gst_stream_selector_base_init, + NULL, + (GClassInitFunc) gst_stream_selector_class_init, + NULL, + NULL, + sizeof (GstStreamSelector), + 0, + (GInstanceInitFunc) gst_stream_selector_init, + }; + + stream_selector_type = + g_type_register_static (GST_TYPE_ELEMENT, + "GstStreamSelector", &stream_selector_info, 0); + + GST_DEBUG_CATEGORY_INIT (stream_selector_debug, + "streamselector", 0, "A stream-selector element"); + } + + return stream_selector_type; +} + +static void +gst_stream_selector_base_init (GstStreamSelectorClass * klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + static GstElementDetails gst_stream_selector_details = + GST_ELEMENT_DETAILS ("StreamSelector", + "Generic", + "N-to-1 input stream_selectoring", + "Julien Moutte \n" + "Ronald S. Bultje "); + + gst_element_class_set_details (element_class, &gst_stream_selector_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_stream_selector_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_stream_selector_src_factory)); +} + +static void +gst_stream_selector_class_init (GstStreamSelectorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + + gobject_class->dispose = gst_stream_selector_dispose; + + gstelement_class->request_new_pad = gst_stream_selector_request_new_pad; +} + +static void +gst_stream_selector_init (GstStreamSelector * sel) +{ + sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); + gst_pad_set_internal_link_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); + gst_pad_set_link_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_link)); + gst_pad_set_getcaps_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); + gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); + + /* sinkpad management */ + sel->last_active_sinkpad = NULL; + sel->nb_sinkpads = 0; +} + +static void +gst_stream_selector_dispose (GObject * object) +{ + GstStreamSelector *sel = GST_STREAM_SELECTOR (object); + + sel->last_active_sinkpad = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static GstPad * +gst_stream_selector_get_linked_pad (GstPad * pad) +{ + GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad)); + GstPad *otherpad = NULL; + + if (pad == sel->srcpad) + otherpad = sel->last_active_sinkpad; + else if (pad == sel->last_active_sinkpad) + otherpad = sel->srcpad; + + return otherpad; +} + +static GstCaps * +gst_stream_selector_get_caps (GstPad * pad) +{ + GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); + + if (!otherpad) { + GST_DEBUG_OBJECT (gst_pad_get_parent (pad), + "Pad %s not linked, returning ANY", gst_pad_get_name (pad)); + + return gst_caps_new_any (); + } + + GST_DEBUG_OBJECT (gst_pad_get_parent (pad), + "Pad %s is linked (to %s), returning allowed-caps", + gst_pad_get_name (pad), gst_pad_get_name (otherpad)); + + return gst_pad_get_allowed_caps (otherpad); +} + +static GstPadLinkReturn +gst_stream_selector_link (GstPad * pad, const GstCaps * caps) +{ + GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); + + if (!otherpad) { + GST_DEBUG_OBJECT (gst_pad_get_parent (pad), + "Pad %s not linked, returning %s", + gst_pad_get_name (pad), GST_PAD_IS_SINK (pad) ? "ok" : "delayed"); + + return GST_PAD_IS_SINK (pad) ? GST_PAD_LINK_OK : GST_PAD_LINK_DELAYED; + } + + GST_DEBUG_OBJECT (gst_pad_get_parent (pad), + "Pad %s is linked (to %s), returning other-trysetcaps", + gst_pad_get_name (pad), gst_pad_get_name (otherpad)); + + return gst_pad_try_set_caps (otherpad, caps); +} + +static GList * +gst_stream_selector_get_linked_pads (GstPad * pad) +{ + GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); + + if (!otherpad) + return NULL; + + return g_list_append (NULL, otherpad); +} + +static GstPad * +gst_stream_selector_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * unused) +{ + GstStreamSelector *sel = GST_STREAM_SELECTOR (element); + gchar *name = NULL; + GstPad *sinkpad = NULL; + + g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL); + + GST_LOG_OBJECT (sel, "Creating new pad %d", sel->nb_sinkpads); + + name = g_strdup_printf ("sink%d", sel->nb_sinkpads++); + sinkpad = gst_pad_new_from_template (templ, name); + g_free (name); + + gst_pad_set_link_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_link)); + gst_pad_set_getcaps_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); + gst_pad_set_chain_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_chain)); + gst_pad_set_internal_link_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); + gst_element_add_pad (GST_ELEMENT (sel), sinkpad); + + return sinkpad; +} + +static void +gst_stream_selector_chain (GstPad * pad, GstData * data) +{ + GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad)); + + /* first, check if the active pad changed. If so, redo + * negotiation and fail if that fails. */ + if (pad != sel->last_active_sinkpad) { + GstPadLinkReturn ret; + + GST_LOG_OBJECT (sel, "stream change detected, switching from %s to %s", + sel->last_active_sinkpad ? + gst_pad_get_name (sel->last_active_sinkpad) : "none", + gst_pad_get_name (pad)); + sel->last_active_sinkpad = pad; + ret = gst_pad_renegotiate (pad); + if (GST_PAD_LINK_FAILED (ret)) { + GST_ELEMENT_ERROR (sel, CORE, NEGOTIATION, (NULL), (NULL)); + sel->last_active_sinkpad = NULL; + return; + } + } + + /* forward */ + GST_DEBUG_OBJECT (sel, "Forwarding %s %p from pad %s", + GST_IS_EVENT (data) ? "event" : "buffer", data, gst_pad_get_name (pad)); + gst_pad_push (sel->srcpad, data); +} diff --git a/gst/playback/gststreamselector.h b/gst/playback/gststreamselector.h new file mode 100644 index 0000000..deef265 --- /dev/null +++ b/gst/playback/gststreamselector.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005 Ronald S. Bultje + * + * 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_STREAM_SELECTOR_H__ +#define __GST_STREAM_SELECTOR_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_STREAM_SELECTOR \ + (gst_stream_selector_get_type()) +#define GST_STREAM_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_STREAM_SELECTOR, GstStreamSelector)) +#define GST_STREAM_SELECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_STREAM_SELECTOR, GstStreamSelector)) +#define GST_IS_STREAM_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_STREAM_SELECTOR)) +#define GST_IS_STREAM_SELECTOR_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_STREAM_SELECTOR)) + +typedef struct _GstStreamSelector GstStreamSelector; +typedef struct _GstStreamSelectorClass GstStreamSelectorClass; + +struct _GstStreamSelector { + GstElement element; + + GstPad *last_active_sinkpad; + GstPad *srcpad; + guint nb_sinkpads; +}; + +struct _GstStreamSelectorClass { + GstElementClass parent_class; +}; + +GType gst_stream_selector_get_type (void); + +G_END_DECLS + +#endif /* __GST_STREAM_SELECTOR_H__ */ -- 2.7.4