From cffa20bf51568e7f27b4c94bf782e2d6b92fe6e1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 18 Oct 2005 17:13:56 +0000 Subject: [PATCH] gst/playback/: Make playbin async, it'll commit state to paused when all streams are detected. Original commit message from CVS: * gst/playback/Makefile.am: * gst/playback/gstdecodebin.c: (gst_decode_bin_init), (gst_decode_bin_dispose), (dynamic_create), (dynamic_free), (free_dynamics), (pad_unblocked), (pad_blocked), (close_pad_link), (try_to_link_1), (new_pad), (no_more_pads), (type_found), (gst_decode_bin_change_state): * gst/playback/gstplaybin.c: (gst_play_bin_class_init), (gst_play_bin_send_event_to_sink): * gst/playback/test5.c: (new_pad), (no_more_pads), (start_finding), (dump_element_stats), (main): * gst/playback/test6.c: (main): Make playbin async, it'll commit state to paused when all streams are detected. Remove ugly hack. Added test6.c to show async behaviour. --- ChangeLog | 18 +++++ gst/playback/Makefile.am | 5 +- gst/playback/gstdecodebin.c | 157 ++++++++++++++++++++++++++---------- gst/playback/test5.c | 4 +- gst/playback/test6.c | 92 +++++++++++++++++++++ 5 files changed, 230 insertions(+), 46 deletions(-) create mode 100644 gst/playback/test6.c diff --git a/ChangeLog b/ChangeLog index d17ec5301c..ccd3853a76 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2005-10-18 Wim Taymans + + * gst/playback/Makefile.am: + * gst/playback/gstdecodebin.c: (gst_decode_bin_init), + (gst_decode_bin_dispose), (dynamic_create), (dynamic_free), + (free_dynamics), (pad_unblocked), (pad_blocked), (close_pad_link), + (try_to_link_1), (new_pad), (no_more_pads), (type_found), + (gst_decode_bin_change_state): + * gst/playback/gstplaybin.c: (gst_play_bin_class_init), + (gst_play_bin_send_event_to_sink): + * gst/playback/test5.c: (new_pad), (no_more_pads), (start_finding), + (dump_element_stats), (main): + * gst/playback/test6.c: (main): + Make playbin async, it'll commit state to paused when all streams + are detected. + Remove ugly hack. + Added test6.c to show async behaviour. + 2005-10-18 Wim Taymans * ext/ogg/gstoggdemux.c: (gst_ogg_demux_chain_peer), diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 28cdc0fb4d..ac518b2e09 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -31,7 +31,7 @@ noinst_HEADERS = \ gststreaminfo.h \ gststreamselector.h -noinst_PROGRAMS = test decodetest test2 test3 test4 test5 +noinst_PROGRAMS = test decodetest test2 test3 test4 test5 test6 test_LDADD = $(GST_LIBS) test_CFLAGS = $(GST_CFLAGS) @@ -48,6 +48,9 @@ test4_CFLAGS = $(GST_CFLAGS) test5_LDADD = $(GST_LIBS) test5_CFLAGS = $(GST_CFLAGS) +test6_LDADD = $(GST_LIBS) +test6_CFLAGS = $(GST_CFLAGS) + decodetest_LDADD = $(GST_LIBS) decodetest_CFLAGS = $(GST_CFLAGS) diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c index 0143a3a72e..e35080fce6 100644 --- a/gst/playback/gstdecodebin.c +++ b/gst/playback/gstdecodebin.c @@ -56,16 +56,20 @@ struct _GstDecodeBin GstBin bin; /* we extend GstBin */ GstElement *typefind; /* this holds the typefind object */ + GstElement *fakesink; gboolean threaded; /* indicating threaded execution is desired */ GList *dynamics; /* list of dynamic connections */ GList *factories; /* factories we can use for selecting elements */ gint numpads; + gint numwaiting; GList *elements; /* elements we added in autoplugging */ guint have_type_id; /* signal id for the typefind element */ + + gboolean shutting_down; /* stop pluggin if we're shutting down */ }; struct _GstDecodeBinClass @@ -122,6 +126,7 @@ static void gst_decode_bin_get_property (GObject * object, guint prop_id, static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, GstStateChange transition); +static void free_dynamics (GstDecodeBin * decode_bin); static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin); static GstElement *try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, @@ -334,6 +339,16 @@ gst_decode_bin_init (GstDecodeBin * decode_bin) g_signal_connect (G_OBJECT (decode_bin->typefind), "have_type", G_CALLBACK (type_found), decode_bin); } + decode_bin->fakesink = gst_element_factory_make ("fakesink", "fakesink"); + if (!decode_bin->fakesink) { + g_warning ("can't find fakesink element, decodebin will not work"); + } else { + if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->fakesink)) { + g_warning ("Could not add fakesink element, decodebin will not work"); + gst_object_unref (decode_bin->fakesink); + decode_bin->fakesink = NULL; + } + } decode_bin->threaded = DEFAULT_THREADED; decode_bin->dynamics = NULL; @@ -353,6 +368,11 @@ gst_decode_bin_dispose (GObject * object) decode_bin->factories = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); + + /* our parent dispose might trigger new signals when pads are unlinked + * etc. clean up the mess here. */ + /* FIXME do proper cleanup when going to NULL */ + free_dynamics (decode_bin); } static GstDynamic * @@ -360,6 +380,8 @@ dynamic_create (GstElement * element, GstDecodeBin * decode_bin) { GstDynamic *dyn; + GST_DEBUG_OBJECT (element, "dynamic create"); + /* take refs */ gst_object_ref (element); gst_object_ref (decode_bin); @@ -378,6 +400,8 @@ dynamic_create (GstElement * element, GstDecodeBin * decode_bin) static void dynamic_free (GstDynamic * dyn) { + GST_DEBUG_OBJECT (dyn->decode_bin, "dynamic free"); + /* disconnect signals */ g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->np_sig_id); g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->nmp_sig_id); @@ -389,6 +413,20 @@ dynamic_free (GstDynamic * dyn) g_free (dyn); } +static void +free_dynamics (GstDecodeBin * decode_bin) +{ + GList *dyns; + + for (dyns = decode_bin->dynamics; dyns; dyns = g_list_next (dyns)) { + GstDynamic *dynamic = (GstDynamic *) dyns->data; + + dynamic_free (dynamic); + } + g_list_free (decode_bin->dynamics); + decode_bin->dynamics = NULL; +} + /* this function runs through the element factories and returns a list * of all elements that are able to sink the given caps */ @@ -441,6 +479,33 @@ mimetype_is_raw (const gchar * mimetype) g_str_has_prefix (mimetype, "text/plain"); } +static void +pad_unblocked (GstPad * pad, gboolean blocked, GstDecodeBin * decode_bin) +{ +} + +static void +pad_blocked (GstPad * pad, gboolean blocked, GstDecodeBin * decode_bin) +{ + decode_bin->numwaiting--; + if (decode_bin->numwaiting == 0) { + gst_object_ref (decode_bin->fakesink); + gst_bin_remove (GST_BIN (decode_bin), decode_bin->fakesink); + + gst_element_set_state (decode_bin->fakesink, GST_STATE_NULL); + gst_element_get_state (decode_bin->fakesink, NULL, NULL, + GST_CLOCK_TIME_NONE); + + gst_object_unref (decode_bin->fakesink); + decode_bin->fakesink = NULL; + + gst_element_post_message (GST_ELEMENT_CAST (decode_bin), + gst_message_new_state_dirty (GST_OBJECT_CAST (decode_bin))); + } + gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback) pad_unblocked, + NULL); +} + /* given a pad and a caps from an element, find the list of elements * that could connect to the pad * @@ -494,11 +559,15 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, /* make a unique name for this new pad */ padname = g_strdup_printf ("src%d", decode_bin->numpads); decode_bin->numpads++; + decode_bin->numwaiting++; /* make it a ghostpad */ ghost = gst_ghost_pad_new (padname, pad); gst_element_add_pad (GST_ELEMENT (decode_bin), ghost); + gst_pad_set_blocked_async (pad, TRUE, (GstPadBlockCallback) pad_blocked, + decode_bin); + GST_LOG_OBJECT (element, "closed pad %s", padname); /* our own signal with an extra flag that this is the only pad */ @@ -546,37 +615,6 @@ many_types: } } -/* crude crude crude crude hack to avoid deadlocks in core while wim figures out - * what's going on */ -/* not mt-safe */ -static GstStateChangeReturn -gst_element_set_state_like_a_crazy_man (GstElement * element, GstState state) -{ - GstElement *e; - GList *chain = NULL, *walk; - GstStateChangeReturn ret; - - for (e = gst_object_ref (element); e; - e = (GstElement *) gst_element_get_parent (e)) - chain = g_list_prepend (chain, e); - - for (walk = chain; walk; walk = walk->next) - GST_STATE_LOCK (walk->data); - - ret = gst_element_set_state (element, state); - - chain = g_list_reverse (chain); - - for (walk = chain; walk; walk = walk->next) { - GST_STATE_UNLOCK (walk->data); - gst_object_unref (walk->data); - } - - g_list_free (chain); - - return ret; -} - /* * given a list of element factories, try to link one of the factories * to the given pad. @@ -622,7 +660,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) gst_bin_add (GST_BIN (decode_bin), element); /* set to ready first so it is ready */ - gst_element_set_state_like_a_crazy_man (element, GST_STATE_READY); + gst_element_set_state (element, GST_STATE_READY); /* keep our own list of elements */ decode_bin->elements = g_list_prepend (decode_bin->elements, element); @@ -634,7 +672,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) gst_object_unref (sinkpad); /* this element did not work, remove it again and continue trying * other elements, the element will be disposed. */ - gst_element_set_state_like_a_crazy_man (element, GST_STATE_NULL); + gst_element_set_state (element, GST_STATE_NULL); gst_bin_remove (GST_BIN (decode_bin), element); } else { const gchar *klass; @@ -669,7 +707,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) * on it until we have a raw type */ close_link (element, decode_bin); /* change the state of the element to that of the parent */ - gst_element_set_state_like_a_crazy_man (element, GST_STATE_PAUSED); + gst_element_set_state (element, GST_STATE_PAUSED); result = element; @@ -811,6 +849,15 @@ new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic) GstDecodeBin *decode_bin = dynamic->decode_bin; GstCaps *caps; + GST_LOCK (decode_bin); + if (decode_bin->shutting_down) + goto shutting_down1; + GST_UNLOCK (decode_bin); + + GST_STATE_LOCK (decode_bin); + if (decode_bin->shutting_down) + goto shutting_down2; + /* see if any more pending dynamic connections exist */ gboolean more = gst_decode_bin_is_dynamic (decode_bin); @@ -818,6 +865,17 @@ new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic) close_pad_link (element, pad, caps, decode_bin, more); if (caps) gst_caps_unref (caps); + GST_STATE_UNLOCK (decode_bin); + + return; + +shutting_down1: + GST_UNLOCK (decode_bin); + return; + +shutting_down2: + GST_STATE_UNLOCK (decode_bin); + return; } /* this signal is fired when an element signals the no_more_pads signal. @@ -843,6 +901,7 @@ no_more_pads (GstElement * element, GstDynamic * dynamic) GST_DEBUG_OBJECT (decode_bin, "no more dynamic elements, signaling no_more_pads"); gst_element_no_more_pads (GST_ELEMENT (decode_bin)); + } else { GST_DEBUG_OBJECT (decode_bin, "we have more dynamic elements"); } @@ -1021,6 +1080,10 @@ type_found (GstElement * typefind, guint probability, GstCaps * caps, gboolean dynamic; GstPad *pad; + GST_STATE_LOCK (decode_bin); + if (decode_bin->shutting_down) + goto shutting_down; + GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); /* autoplug the new pad with the caps that the signal gave us. */ @@ -1038,6 +1101,10 @@ type_found (GstElement * typefind, guint probability, GstCaps * caps, /* more dynamic elements exist that could create new pads */ GST_DEBUG_OBJECT (decode_bin, "we have more dynamic elements"); } + +shutting_down: + GST_STATE_UNLOCK (decode_bin); + return; } static void @@ -1085,18 +1152,28 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; GstDecodeBin *decode_bin; - GList *dyns; decode_bin = GST_DECODE_BIN (element); - switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: decode_bin->numpads = 0; + decode_bin->numwaiting = 0; decode_bin->dynamics = NULL; break; case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_LOCK (decode_bin); + decode_bin->shutting_down = FALSE; + GST_UNLOCK (decode_bin); + break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_LOCK (decode_bin); + decode_bin->shutting_down = TRUE; + GST_UNLOCK (decode_bin); + break; default: break; } @@ -1108,13 +1185,7 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PAUSED_TO_READY: break; case GST_STATE_CHANGE_READY_TO_NULL: - for (dyns = decode_bin->dynamics; dyns; dyns = g_list_next (dyns)) { - GstDynamic *dynamic = (GstDynamic *) dyns->data; - - dynamic_free (dynamic); - } - g_list_free (decode_bin->dynamics); - decode_bin->dynamics = NULL; + free_dynamics (decode_bin); break; default: break; diff --git a/gst/playback/test5.c b/gst/playback/test5.c index b7c05dd609..a2c945a6c2 100644 --- a/gst/playback/test5.c +++ b/gst/playback/test5.c @@ -24,13 +24,13 @@ static GMainLoop *loop; static void new_pad (GstElement * element, GstPad * pad, gboolean last, GstElement * sink) { - g_print ("New pad..."); + g_print ("New pad...\n"); } static void no_more_pads (GstElement * element) { - g_print ("No more pads..."); + g_print ("No more pads...\n"); g_main_loop_quit (loop); } diff --git a/gst/playback/test6.c b/gst/playback/test6.c new file mode 100644 index 0000000000..6ca4c598d2 --- /dev/null +++ b/gst/playback/test6.c @@ -0,0 +1,92 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * 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. + */ +#include +#include + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *pipeline, *filesrc, *decodebin; + GstStateChangeReturn res; + GstIterator *it; + gpointer data; + + gst_init (&argc, &argv); + + pipeline = gst_pipeline_new ("pipeline"); + + filesrc = gst_element_factory_make ("filesrc", "filesrc"); + g_assert (filesrc); + + decodebin = gst_element_factory_make ("decodebin", "decodebin"); + g_assert (decodebin); + + gst_bin_add_many (GST_BIN (pipeline), filesrc, decodebin, NULL); + gst_element_link (filesrc, decodebin); + + if (argc < 2) { + g_print ("usage: %s \n", argv[0]); + exit (-1); + } + g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); + + g_print ("pause..\n"); + res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + if (res == GST_STATE_CHANGE_FAILURE) { + g_print ("could not pause\n"); + exit (-1); + } + g_print ("waiting..\n"); + res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + if (res != GST_STATE_CHANGE_SUCCESS) { + g_print ("could not complete pause\n"); + exit (-1); + } + g_print ("stats..\n"); + + it = gst_element_iterate_src_pads (decodebin); + while (gst_iterator_next (it, &data) == GST_ITERATOR_OK) { + GstPad *pad = GST_PAD (data); + GstCaps *caps; + gchar *str; + GstQuery *query; + + g_print ("stream %s:\n", GST_OBJECT_NAME (pad)); + + caps = gst_pad_get_caps (pad); + str = gst_caps_to_string (caps); + g_print (" caps: %s\n", str); + g_free (str); + + query = gst_query_new_position (GST_FORMAT_TIME); + if (gst_pad_query (pad, query)) { + gint64 duration; + + gst_query_parse_position (query, NULL, NULL, &duration); + + g_print (" duration: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (duration)); + } + gst_query_unref (query); + + gst_object_unref (pad); + } + gst_iterator_free (it); + + return 0; +}