diff --git a/ChangeLog b/ChangeLog index 734c8ba69e..1bdd0e5918 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2004-11-02 Wim Taymans + + * gst/playback/gstdecodebin.c: (gst_decode_bin_class_init), + (try_to_link_1), (get_our_ghost_pad), (remove_element_chain), + (unlinked), (no_more_pads), (close_link): + * gst/playback/gstplaybasebin.c: (gst_play_base_bin_init), + (unknown_type), (add_element_stream), (new_decoded_pad), + (removed_decoded_pad), (setup_source): + * gst/playback/gststreaminfo.c: (gst_stream_info_get_type), + (gst_stream_info_class_init), (gst_stream_info_init), + (gst_stream_info_new), (gst_stream_info_dispose), + (stream_info_mute_pad), (gst_stream_info_set_property), + (gst_stream_info_get_property): + * gst/playback/gststreaminfo.h: + Fix playback of multiple files. + a slightly different approach to handling dynamic pad removals. + This one only looks at pads that we have linked. + 2004-11-01 Christophe Fergeau * ext/ogg/gstoggdemux.c: (gst_ogg_demux_finalize): fix an "invalid diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c index 8ceb96b228..382a2fa64d 100644 --- a/gst/playback/gstdecodebin.c +++ b/gst/playback/gstdecodebin.c @@ -74,10 +74,10 @@ struct _GstDecodeBinClass /* signal we fire when a new pad has been decoded into raw audio/video */ void (*new_decoded_pad) (GstElement * element, GstPad * pad, gboolean last); + /* signal we fire when a pad has been removed */ + void (*removed_decoded_pad) (GstElement * element, GstPad * pad); /* signal fired when we found a pad that we cannot decode */ void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); - /* called on dynamic pad removal */ - void (*removed_decoded_pad) (GstElement * element, GstPad * pad); }; /* props */ @@ -91,8 +91,8 @@ enum enum { SIGNAL_NEW_DECODED_PAD, - SIGNAL_UNKNOWN_TYPE, SIGNAL_REMOVED_DECODED_PAD, + SIGNAL_UNKNOWN_TYPE, LAST_SIGNAL }; @@ -101,6 +101,7 @@ enum typedef struct { gint np_sig_id; /* signal id of new_pad */ + gint unlink_sig_id; /* signal id of unlinked */ gint nmp_sig_id; /* signal id of no_more_pads */ GstElement *element; /* the element sending the signal */ GstDecodeBin *decode_bin; /* pointer to ourself */ @@ -124,6 +125,8 @@ static GstElement *try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, static void close_link (GstElement * element, GstDecodeBin * decode_bin); static void close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, GstDecodeBin * decode_bin, gboolean more); +static void unlinked (GstPad * pad, GstPad * peerpad, + GstDecodeBin * decode_bin); static GstElementClass *parent_class; static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; @@ -189,16 +192,16 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) G_STRUCT_OFFSET (GstDecodeBinClass, new_decoded_pad), NULL, NULL, gst_play_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_BOOLEAN); + gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD] = + g_signal_new ("removed-decoded-pad", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstDecodeBinClass, removed_decoded_pad), NULL, NULL, + gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE] = g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, unknown_type), NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_CAPS); - gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD] = - g_signal_new ("removed-decoded-pad", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstDecodeBinClass, removed_decoded_pad), NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose); @@ -464,7 +467,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) /* make an element from the factory first */ element = gst_element_factory_create (factory, NULL); if (element == NULL) { - /* hmm, strange */ + /* hmm, strange. Like with all things in live, let's move on.. */ GST_WARNING_OBJECT (decode_bin, "could not create an element from %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); continue; @@ -474,6 +477,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) GST_DEBUG_OBJECT (decode_bin, "adding %s", gst_element_get_name (element)); gst_bin_add (GST_BIN (decode_bin), element); + /* set to ready first so it can do negotiation */ gst_element_set_state (element, GST_STATE_READY); /* keep out own list of elements */ @@ -486,6 +490,10 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) if (ret) { const gchar *klass; GstElementFactory *factory; + guint sig; + + GST_DEBUG_OBJECT (decode_bin, "linked on pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); /* The link worked, now figure out what it was that we connected */ factory = gst_element_get_factory (element); @@ -496,6 +504,11 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) /* FIXME, do something with threads here */ } } + /* make sure we catch unlink signals */ + sig = g_signal_connect (G_OBJECT (pad), "unlinked", + G_CALLBACK (unlinked), decode_bin); + /* keep a ref to the signal id so that we can disconnect the signal callback */ + g_object_set_data (G_OBJECT (pad), "unlinked_id", GINT_TO_POINTER (sig)); /* now that we added the element we can try to continue autoplugging * on it until we have a raw type */ @@ -504,6 +517,8 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) gst_element_sync_state_with_parent (element); return element; } else { + GST_DEBUG_OBJECT (decode_bin, "link failed on pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); /* this element did not work, remove it again and continue trying * other elements */ gst_bin_remove (GST_BIN (decode_bin), element); @@ -512,6 +527,115 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) return NULL; } +static GstPad * +get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad) +{ + GList *ghostpads; + + if (pad == NULL || !GST_PAD_IS_SRC (pad)) { + GST_DEBUG_OBJECT (decode_bin, "pad NULL or not SRC pad"); + return NULL; + } + + if (GST_IS_GHOST_PAD (pad)) { + GstElement *parent = gst_pad_get_parent (pad); + + GST_DEBUG_OBJECT (decode_bin, "pad parent %s", + gst_element_get_name (parent)); + if (parent == GST_ELEMENT (decode_bin)) { + GST_DEBUG_OBJECT (decode_bin, "pad is our ghostpad"); + return pad; + } else { + GST_DEBUG_OBJECT (decode_bin, "pad is ghostpad but not ours"); + return NULL; + } + } + + GST_DEBUG_OBJECT (decode_bin, "looping over ghostpads"); + ghostpads = gst_pad_get_ghost_pad_list (pad); + while (ghostpads) { + GstPad *ghostpad; + + ghostpad = get_our_ghost_pad (decode_bin, GST_PAD (ghostpads->data)); + if (ghostpad) + return ghostpad; + + ghostpads = g_list_next (ghostpads); + } + GST_DEBUG_OBJECT (decode_bin, "done looping over ghostpads, nothing found"); + + return NULL; +} + +/* remove all downstream elements starting from the given pad. + * Also make sure to remove the ghostpad we created for the raw + * decoded stream. + */ +static void +remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad) +{ + GList *int_links; + GstElement *elem = gst_pad_get_parent (pad); + + GST_DEBUG_OBJECT (decode_bin, "%s:%s", GST_DEBUG_PAD_NAME (pad)); + + /* remove all elements linked to this pad up to the ghostpad + * that we created for this stream */ + for (int_links = gst_pad_get_internal_links (pad); + int_links; int_links = g_list_next (int_links)) { + GstPad *pad; + GstPad *ghostpad; + GstPad *peer; + + pad = GST_PAD (int_links->data); + GST_DEBUG_OBJECT (decode_bin, "inspecting internal pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); + + ghostpad = get_our_ghost_pad (decode_bin, pad); + if (ghostpad) { + GST_DEBUG_OBJECT (decode_bin, "found our ghost pad %s:%s for %s:%s", + GST_DEBUG_PAD_NAME (ghostpad), GST_DEBUG_PAD_NAME (pad)); + + g_signal_emit (G_OBJECT (decode_bin), + gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD], 0, ghostpad); + + gst_element_remove_pad (GST_ELEMENT (decode_bin), ghostpad); + continue; + } else { + GST_DEBUG_OBJECT (decode_bin, "not one of our ghostpads"); + } + + peer = gst_pad_get_peer (pad); + if (peer == NULL) + continue; + + GST_DEBUG_OBJECT (decode_bin, "internal pad %s:%s linked to pad %s:%s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer)); + + if (gst_pad_get_real_parent (peer) != GST_ELEMENT (decode_bin)) { + GST_DEBUG_OBJECT (decode_bin, "dead end pad %s:%s", + GST_DEBUG_PAD_NAME (peer)); + } else { + GST_DEBUG_OBJECT (decode_bin, "recursing element %s on pad %s:%s", + gst_element_get_name (elem), GST_DEBUG_PAD_NAME (pad)); + remove_element_chain (decode_bin, peer); + } + } + GST_DEBUG_OBJECT (decode_bin, "removing %s", gst_element_get_name (elem)); + gst_bin_remove (GST_BIN (decode_bin), elem); +} + +/* This function will be called when a pad is disconnected for some reason */ +static void +unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin) +{ + /* inactivate pad */ + gst_pad_set_active (pad, FALSE); + + /* remove all elements linked to the peerpad */ + remove_element_chain (decode_bin, peerpad); +} + /* This function will be called when a dynamic pad is created on an element. * We try to continue autoplugging on this new pad. */ static void @@ -547,7 +671,7 @@ no_more_pads (GstElement * element, GstDynamic * dynamic) decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic); g_free (dynamic); - /* if we have no more dynamic elements, we have no change of creating + /* if we have no more dynamic elements, we have no chance of creating * more pads, so we fire the no_more_pads signal */ if (decode_bin->dynamics == NULL) { GST_DEBUG_OBJECT (decode_bin, @@ -558,110 +682,6 @@ no_more_pads (GstElement * element, GstDynamic * dynamic) } } -/* get unconnected element (after unlink/pad-remove). */ -static GstElement * -get_unconnected_element (GstDecodeBin * decode_bin) -{ - const GList *walk; - - for (walk = gst_bin_get_list (GST_BIN (decode_bin)); - walk != NULL; walk = walk->next) { - GstElement *element = walk->data; - const GList *pwalk; - - for (pwalk = gst_element_get_pad_list (GST_ELEMENT (element)); - pwalk != NULL; pwalk = pwalk->next) { - GstPad *pad = pwalk->data; - - if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) == NULL) - return element; - } - } - - return NULL; -} - -/* remove all elements linked forward from this element on from the bin */ -static void -remove_starting_from (GstDecodeBin * decode_bin, GstElement * start) -{ - const GList *pwalk; - - for (pwalk = gst_element_get_pad_list (start); - pwalk != NULL; pwalk = pwalk->next) { - GstPad *pad = pwalk->data; - - if (GST_PAD_IS_SRC (pad)) { - GstPad *peer = GST_PAD_PEER (pad); - GstElement *peer_parent = NULL; - - if (peer) - peer_parent = gst_pad_get_parent (peer); - - /* be recursive */ - if (peer_parent != NULL) { - if (gst_object_get_parent (GST_OBJECT (peer_parent)) - == GST_OBJECT (decode_bin)) { - remove_starting_from (decode_bin, peer_parent); - } else { - /* linked outside us - signal pad removal */ - g_signal_emit (decode_bin, - gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD], 0, pad); - } - } - } - } - - /* now remove ourselves */ - gst_bin_remove (GST_BIN (decode_bin), start); -} - -/* This function will be called for elements with dynamic elements when - * they remove pads. This might just be because they're disposed, in - * which case we don't care. It might also be because they're changing - * chain, in which case we want to re-plug afterwards. */ - -static void -pad_removed (GstElement * element, GstPad * pad, GstDecodeBin * decode_bin) -{ - GList *walk; - GstElement *peer; - GstDynamic *dyn; - - /* don't care about disposal - it's really only for replugging. - * We only need to check for pending is ready because if pending - * is NULL, then state is READY. */ - if (GST_STATE (element) <= GST_STATE_READY || - GST_STATE_PENDING (element) == GST_STATE_READY) - return; - - /* clean up. FIXME: getting peer of removed pad is dirty. */ - peer = get_unconnected_element (decode_bin); - g_assert (peer); - remove_starting_from (decode_bin, peer); - - /* if an element removes two pads, then we don't want this twice */ - for (walk = decode_bin->dynamics; walk != NULL; walk = walk->next) { - dyn = walk->data; - if (dyn->element == element) - return; - } - - GST_DEBUG_OBJECT (decode_bin, "pad removal while alive - chained?"); - - /* re-setup dynamic plugging */ - dyn = g_new0 (GstDynamic, 1); - dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad", - G_CALLBACK (new_pad), dyn); - dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads", - G_CALLBACK (no_more_pads), dyn); - dyn->element = element; - dyn->decode_bin = decode_bin; - - /* and add this element to the dynamic elements */ - decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); -} - /* this function inspects the given element and tries to connect something * on the srcpads. If there are dynamic pads, it sets up a signal handler to * continue autoplugging when they become available */ @@ -751,10 +771,6 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) /* and add this element to the dynamic elements */ decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); - - /* let's keep this one around longer than the lifetime of dynamicity */ - g_signal_connect (G_OBJECT (element), "pad-removed", - G_CALLBACK (pad_removed), decode_bin); } /* Check if this is an element with more than 1 pad. If this element diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c index 92ef040e92..256f05b3c9 100644 --- a/gst/playback/gstplaybasebin.c +++ b/gst/playback/gstplaybasebin.c @@ -166,7 +166,6 @@ static void gst_play_base_bin_init (GstPlayBaseBin * play_base_bin) { play_base_bin->uri = NULL; - play_base_bin->threaded = FALSE; play_base_bin->need_rebuild = TRUE; play_base_bin->source = NULL; play_base_bin->decoder = NULL; @@ -282,6 +281,7 @@ unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, /* add the stream to the list */ info = gst_stream_info_new (GST_OBJECT (pad), GST_STREAM_TYPE_UNKNOWN, NULL, caps); + info->origin = GST_OBJECT (pad); play_base_bin->streaminfo = g_list_append (play_base_bin->streaminfo, info); g_free (capsstr); @@ -301,6 +301,7 @@ add_element_stream (GstElement * element, GstPlayBaseBin * play_base_bin) info = gst_stream_info_new (GST_OBJECT (element), GST_STREAM_TYPE_ELEMENT, NULL, NULL); + info->origin = GST_OBJECT (element); play_base_bin->streaminfo = g_list_append (play_base_bin->streaminfo, info); } @@ -393,6 +394,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, /* add the stream to the list */ info = gst_stream_info_new (GST_OBJECT (srcpad), type, NULL, caps); + info->origin = GST_OBJECT (pad); play_base_bin->streaminfo = g_list_append (play_base_bin->streaminfo, info); /* signal the no more pads after adding the stream */ @@ -400,6 +402,35 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, no_more_pads (NULL, play_base_bin); } +/* signal fired when decodebin has removed a raw pad. We remove + * the preroll element if needed and the appropriate streaminfo. + */ +static void +removed_decoded_pad (GstElement * element, GstPad * pad, + GstPlayBaseBin * play_base_bin) +{ + GList *streams; + + GST_DEBUG ("removing decoded pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + /* first find the stream to decode this pad */ + streams = play_base_bin->streaminfo; + while (streams) { + GstStreamInfo *info = GST_STREAM_INFO (streams->data); + + if (info->origin == GST_OBJECT (pad)) { + GST_DEBUG ("removing stream %p", info); + play_base_bin->streaminfo = + g_list_remove (play_base_bin->streaminfo, info); + g_object_unref (info); + return; + } else { + GST_DEBUG ("skipping stream %p", info); + } + streams = g_list_next (streams); + } +} + /* * Cache errors... */ @@ -448,6 +479,8 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) if (!play_base_bin->need_rebuild) return TRUE; + play_base_bin->threaded = FALSE; + /* keep ref to old souce in case creating the new source fails */ old_src = play_base_bin->source; @@ -537,7 +570,7 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) { gboolean res; - gint sig1, sig2, sig3, sig4, sig5; + gint sig1, sig2, sig3, sig4, sig5, sig6; /* now create the decoder element */ play_base_bin->decoder = gst_element_factory_make ("decodebin", "decoder"); @@ -559,11 +592,13 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) } sig1 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "new-decoded-pad", G_CALLBACK (new_decoded_pad), play_base_bin); - sig2 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads", + sig2 = g_signal_connect (G_OBJECT (play_base_bin->decoder), + "removed-decoded-pad", G_CALLBACK (removed_decoded_pad), play_base_bin); + sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads", G_CALLBACK (no_more_pads), play_base_bin); - sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "unknown-type", + sig4 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "unknown-type", G_CALLBACK (unknown_type), play_base_bin); - sig4 = g_signal_connect (G_OBJECT (play_base_bin->thread), "error", + sig5 = g_signal_connect (G_OBJECT (play_base_bin->thread), "error", G_CALLBACK (thread_error), error); /* either when the queues are filled or when the decoder element has no more @@ -575,7 +610,7 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) GList *prerolls; GST_DEBUG ("waiting for preroll..."); - sig5 = g_signal_connect (G_OBJECT (play_base_bin->thread), + sig6 = g_signal_connect (G_OBJECT (play_base_bin->thread), "state-change", G_CALLBACK (state_change), play_base_bin); g_cond_wait (play_base_bin->preroll_cond, play_base_bin->preroll_lock); GST_DEBUG ("preroll done !"); @@ -595,16 +630,18 @@ setup_source (GstPlayBaseBin * play_base_bin, GError ** error) } } else { GST_DEBUG ("state change failed, media cannot be loaded"); - sig5 = 0; + sig6 = 0; } g_mutex_unlock (play_base_bin->preroll_lock); - if (sig5 != 0) - g_signal_handler_disconnect (G_OBJECT (play_base_bin->thread), sig5); - g_signal_handler_disconnect (G_OBJECT (play_base_bin->thread), sig4); + if (sig6 != 0) + g_signal_handler_disconnect (G_OBJECT (play_base_bin->thread), sig6); + + g_signal_handler_disconnect (G_OBJECT (play_base_bin->thread), sig5); + g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig4); g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig3); - g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig2); - g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig1); + //g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig2); + //g_signal_handler_disconnect (G_OBJECT (play_base_bin->decoder), sig1); play_base_bin->need_rebuild = FALSE; } diff --git a/gst/playback/gststreaminfo.c b/gst/playback/gststreaminfo.c index 4c1b22f9da..4716f5cd83 100644 --- a/gst/playback/gststreaminfo.c +++ b/gst/playback/gststreaminfo.c @@ -147,6 +147,7 @@ static void gst_stream_info_init (GstStreamInfo * stream_info) { stream_info->object = NULL; + stream_info->origin = NULL; stream_info->type = GST_STREAM_TYPE_UNKNOWN; stream_info->decoder = NULL; stream_info->mute = FALSE; @@ -165,6 +166,7 @@ gst_stream_info_new (GstObject * object, info->object = object; info->type = type; info->decoder = g_strdup (decoder); + info->origin = object; if (caps) { info->caps = gst_caps_copy (caps); } @@ -181,6 +183,7 @@ gst_stream_info_dispose (GObject * object) 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); stream_info->decoder = NULL; diff --git a/gst/playback/gststreaminfo.h b/gst/playback/gststreaminfo.h index 41f09a125e..b5381ed0e3 100644 --- a/gst/playback/gststreaminfo.h +++ b/gst/playback/gststreaminfo.h @@ -49,6 +49,7 @@ struct _GstStreamInfo { GstStreamType type; gchar *decoder; gboolean mute; + GstObject *origin; GstCaps *caps; };