diff -r 71e347f905f2 -r 4a7fac7dd34a gst_plugins_base/gst/playback/gstdecodebin2.c --- a/gst_plugins_base/gst/playback/gstdecodebin2.c Fri Mar 19 09:35:09 2010 +0200 +++ b/gst_plugins_base/gst/playback/gstdecodebin2.c Fri Apr 16 15:15:52 2010 +0300 @@ -19,17 +19,16 @@ /** * SECTION:element-decodebin2 - * @short_description: Next-generation automatic decoding bin * * #GstBin that auto-magically constructs a decoding pipeline using available * decoders and demuxers via auto-plugging. * * At this stage, decodebin2 is considered UNSTABLE. The API provided in the - * signals is expected to change in the near future. + * signals is expected to change in the near future. * - * To try out decodebin2, you can set the USE_DECODEBIN2 environment + * To try out decodebin2, you can set the USE_DECODEBIN2 environment * variable (USE_DECODEBIN2=1 for example). This will cause playbin to use - * decodebin2 instead of the older decodebin for its internal auto-plugging. + * decodebin2 instead of the older #GstDecodeBin for its internal auto-plugging. */ #ifdef HAVE_CONFIG_H @@ -46,9 +45,6 @@ #include "gstplay-enum.h" #include "gstfactorylists.h" -#ifdef __SYMBIAN32__ -#include -#endif /* generic templates */ static GstStaticPadTemplate decoder_bin_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", @@ -67,6 +63,7 @@ typedef struct _GstDecodeGroup GstDecodeGroup; typedef struct _GstDecodePad GstDecodePad; +typedef GstGhostPadClass GstDecodePadClass; typedef struct _GstDecodeBin GstDecodeBin; typedef struct _GstDecodeBin GstDecodeBin2; typedef struct _GstDecodeBinClass GstDecodeBinClass; @@ -92,7 +89,6 @@ gchar *encoding; /* encoding of subtitles */ GstElement *typefind; /* this holds the typefind object */ - GstElement *fakesink; GMutex *lock; /* Protects activegroup and groups */ GstDecodeGroup *activegroup; /* group currently active */ @@ -103,9 +99,16 @@ gint nbpads; /* unique identifier for source pads */ GValueArray *factories; /* factories we can use for selecting elements */ + GList *subtitles; /* List of elements with subtitle-encoding */ gboolean have_type; /* if we received the have_type signal */ guint have_type_id; /* signal id for have-type from typefind */ + + gboolean async_pending; /* async-start has been emited */ + + GMutex *dyn_lock; /* lock protecting pad blocking */ + gboolean shutdown; /* if we are shutting down */ + GList *blocked_pads; /* pads that have set to block */ }; struct _GstDecodeBinClass @@ -155,7 +158,9 @@ { PROP_0, PROP_CAPS, - PROP_SUBTITLE_ENCODING + PROP_SUBTITLE_ENCODING, + PROP_SINK_CAPS, + PROP_LAST }; static GstBinClass *parent_class; @@ -168,8 +173,8 @@ "Edward Hervey "); -static gboolean add_fakesink (GstDecodeBin * decode_bin); -static void remove_fakesink (GstDecodeBin * decode_bin); +static void do_async_start (GstDecodeBin * dbin); +static void do_async_done (GstDecodeBin * dbin); static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin); @@ -215,6 +220,23 @@ g_mutex_unlock (GST_DECODE_BIN_CAST(dbin)->lock); \ } G_STMT_END +#define DECODE_BIN_DYN_LOCK(dbin) G_STMT_START { \ + GST_LOG_OBJECT (dbin, \ + "dynlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_lock (GST_DECODE_BIN_CAST(dbin)->dyn_lock); \ + GST_LOG_OBJECT (dbin, \ + "dynlocked from thread %p", \ + g_thread_self ()); \ +} G_STMT_END + +#define DECODE_BIN_DYN_UNLOCK(dbin) G_STMT_START { \ + GST_LOG_OBJECT (dbin, \ + "dynunlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_unlock (GST_DECODE_BIN_CAST(dbin)->dyn_lock); \ +} G_STMT_END + /* GstDecodeGroup * * Streams belonging to the same group/chain of a media file @@ -225,17 +247,16 @@ GstDecodeBin *dbin; GMutex *lock; GstElement *multiqueue; + gboolean exposed; /* TRUE if this group is exposed */ - gboolean drained; /* TRUE if EOS went throug all endpads */ + gboolean drained; /* TRUE if EOS went through all endpads */ gboolean blocked; /* TRUE if all endpads are blocked */ gboolean complete; /* TRUE if we are not expecting anymore streams * on this group */ - gulong overrunsig; - gulong underrunsig; + gulong overrunsig; /* the overrun signal for multiqueue */ guint nbdynamic; /* number of dynamic pads in the group. */ GList *endpads; /* List of GstDecodePad of source pads to be exposed */ - GList *ghosts; /* List of GstGhostPad for the endpads */ GList *reqpads; /* List of RequestPads for multiqueue. */ }; @@ -262,9 +283,9 @@ static GstPad *gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad); static gboolean gst_decode_group_control_source_pad (GstDecodeGroup * group, - GstPad * pad); + GstDecodePad * pad); static gboolean gst_decode_group_expose (GstDecodeGroup * group); -static void gst_decode_group_check_if_blocked (GstDecodeGroup * group); +static gboolean gst_decode_group_check_if_blocked (GstDecodeGroup * group); static void gst_decode_group_set_complete (GstDecodeGroup * group); static void gst_decode_group_hide (GstDecodeGroup * group); static void gst_decode_group_free (GstDecodeGroup * group); @@ -273,28 +294,26 @@ * * GstPad private used for source pads of groups */ - struct _GstDecodePad { - GstPad *pad; - GstDecodeGroup *group; - gboolean blocked; - gboolean drained; -}; - -static GstDecodePad *gst_decode_pad_new (GstDecodeGroup * group, GstPad * pad, - gboolean block); -static void source_pad_blocked_cb (GstPad * pad, gboolean blocked, - GstDecodePad * dpad); - -/* TempPadStruct - * Internal structure used for pads which have more than one structure. - */ -typedef struct _TempPadStruct -{ + GstGhostPad parent; GstDecodeBin *dbin; GstDecodeGroup *group; -} TempPadStruct; + + gboolean blocked; /* the pad is blocked */ + gboolean drained; /* an EOS has been seen on the pad */ + gboolean added; /* the pad is added to decodebin */ +}; + +G_DEFINE_TYPE (GstDecodePad, gst_decode_pad, GST_TYPE_GHOST_PAD); +#define GST_TYPE_DECODE_PAD (gst_decode_pad_get_type ()) +#define GST_DECODE_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_PAD,GstDecodePad)) + +static GstDecodePad *gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, + GstDecodeGroup * group); +static void gst_decode_pad_activate (GstDecodePad * dpad, + GstDecodeGroup * group); +static void gst_decode_pad_unblock (GstDecodePad * dpad); /******************************** * Standard GObject boilerplate * @@ -393,7 +412,8 @@ /** * GstDecodeBin2::new-decoded-pad: - * @pad: the newly created pad + * @bin: The decodebin + * @pad: The newly created pad * @islast: #TRUE if this is the last pad to be added. Deprecated. * * This signal gets emitted as soon as a new pad of the same type as one of @@ -408,7 +428,8 @@ /** * GstDecodeBin2::removed-decoded-pad: - * @pad: the pad that was removed + * @bin: The decodebin + * @pad: The pad that was removed * * This signal is emitted when a 'final' caps pad has been removed. */ @@ -420,8 +441,10 @@ /** * GstDecodeBin2::unknown-type: - * @pad: the new pad containing caps that cannot be resolved to a 'final' stream type. - * @caps: the #GstCaps of the pad that cannot be resolved. + * @bin: The decodebin + * @pad: The new pad containing caps that cannot be resolved to a 'final' + * stream type. + * @caps: The #GstCaps of the pad that cannot be resolved. * * This signal is emitted when a pad for which there is no further possible * decoding is added to the decodebin. @@ -429,11 +452,12 @@ 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_OBJECT, G_TYPE_NONE, 2, + NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_CAPS); /** * GstDecodeBin2::autoplug-continue: + * @bin: The decodebin * @pad: The #GstPad. * @caps: The #GstCaps found. * @@ -448,11 +472,12 @@ gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] = g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_continue), - _gst_boolean_accumulator, NULL, gst_play_marshal_BOOLEAN__OBJECT_OBJECT, + _gst_boolean_accumulator, NULL, gst_play_marshal_BOOLEAN__OBJECT_BOXED, G_TYPE_BOOLEAN, 2, GST_TYPE_PAD, GST_TYPE_CAPS); /** * GstDecodeBin2::autoplug-factories: + * @bin: The decodebin * @pad: The #GstPad. * @caps: The #GstCaps found. * @@ -473,11 +498,12 @@ g_signal_new ("autoplug-factories", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_factories), _gst_array_accumulator, NULL, - gst_play_marshal_BOXED__OBJECT_OBJECT, G_TYPE_VALUE_ARRAY, 2, + gst_play_marshal_BOXED__OBJECT_BOXED, G_TYPE_VALUE_ARRAY, 2, GST_TYPE_PAD, GST_TYPE_CAPS); /** * GstDecodeBin2::autoplug-sort: + * @bin: The decodebin * @pad: The #GstPad. * @caps: The #GstCaps. * @factories: A #GValueArray of possible #GstElementFactory to use. @@ -494,34 +520,47 @@ gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT] = g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_sort), - NULL, NULL, gst_play_marshal_BOXED__OBJECT_OBJECT_BOXED, + NULL, NULL, gst_play_marshal_BOXED__OBJECT_BOXED_BOXED, G_TYPE_VALUE_ARRAY, 3, GST_TYPE_PAD, GST_TYPE_CAPS, G_TYPE_VALUE_ARRAY); /** * GstDecodeBin2::autoplug-select: + * @bin: The decodebin * @pad: The #GstPad. * @caps: The #GstCaps. - * @factories: A #GValueArray of possible #GstElementFactory to use, sorted by - * rank (higher ranks come first). + * @factory: A #GstElementFactory to use * * This signal is emitted once decodebin2 has found all the possible - * #GstElementFactory that can be used to handle the given @caps. + * #GstElementFactory that can be used to handle the given @caps. For each of + * those factories, this signal is emited. + * + * The signal handler should return a #GST_TYPE_AUTOPLUG_SELECT_RESULT enum + * value indicating what decodebin2 should do next. + * + * A value of #GST_AUTOPLUG_SELECT_TRY will try to autoplug an element from + * @factory. * - * Returns: A #gint indicating what factory index from the @factories array - * that you wish decodebin2 to use for trying to decode the given @caps. - * Return -1 to stop selection of a factory and expose the pad as a raw type. - * The default handler always returns the first possible factory (index 0). + * A value of #GST_AUTOPLUG_SELECT_EXPOSE will expose @pad without plugging + * any element to it. + * + * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the + * next factory. + * + * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required + * operation. the default handler will always return + * #GST_AUTOPLUG_SELECT_TRY. */ gst_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT] = g_signal_new ("autoplug-select", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_select), _gst_select_accumulator, NULL, - gst_play_marshal_ENUM__OBJECT_OBJECT_OBJECT, + gst_play_marshal_ENUM__OBJECT_BOXED_OBJECT, GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS, GST_TYPE_ELEMENT_FACTORY); /** * GstDecodeBin2::drained + * @bin: The decodebin * * This signal is emitted once decodebin2 has finished decoding all the data. * @@ -534,14 +573,20 @@ g_object_class_install_property (gobject_klass, PROP_CAPS, g_param_spec_boxed ("caps", "Caps", "The caps on which to stop decoding.", - GST_TYPE_CAPS, G_PARAM_READWRITE)); + GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING, g_param_spec_string ("subtitle-encoding", "subtitle encoding", "Encoding to assume if input subtitles are not in UTF-8 encoding. " "If not set, the GST_SUBTITLE_ENCODING environment variable will " "be checked for an encoding to use. If that is not set either, " - "ISO-8859-15 will be assumed.", NULL, G_PARAM_READWRITE)); + "ISO-8859-15 will be assumed.", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_SINK_CAPS, + g_param_spec_boxed ("sink-caps", "Sink Caps", + "The caps of the input data. (NULL = use typefind element)", + GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); klass->autoplug_continue = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_continue); @@ -584,7 +629,7 @@ } /* get the sinkpad */ - pad = gst_element_get_pad (decode_bin->typefind, "sink"); + pad = gst_element_get_static_pad (decode_bin->typefind, "sink"); /* ghost the sink pad to ourself */ gpad = gst_ghost_pad_new ("sink", pad); @@ -604,20 +649,96 @@ decode_bin->activegroup = NULL; decode_bin->groups = NULL; + decode_bin->dyn_lock = g_mutex_new (); + decode_bin->shutdown = FALSE; + decode_bin->blocked_pads = NULL; + decode_bin->caps = gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb;video/x-raw-gray;" - "audio/x-raw-int;audio/x-raw-float;" "text/plain;text/x-pango-markup"); - - add_fakesink (decode_bin); - - /* FILLME */ + "audio/x-raw-int;audio/x-raw-float;" "text/plain;text/x-pango-markup;" + "video/x-dvd-subpicture; subpicture/x-pgs"); +} + +static void +gst_decode_bin_remove_groups (GstDecodeBin * dbin) +{ + GList *tmp; + GstIterator *it; + gpointer point; + gboolean done; + GstIteratorResult res; + + GST_DEBUG_OBJECT (dbin, "cleaning up"); + + if (dbin->activegroup) { + GST_DEBUG_OBJECT (dbin, "free active group %p", dbin->activegroup); + gst_decode_group_free (dbin->activegroup); + dbin->activegroup = NULL; + } + + /* remove groups */ + for (tmp = dbin->groups; tmp; tmp = g_list_next (tmp)) { + GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; + + GST_DEBUG_OBJECT (dbin, "free group %p", group); + gst_decode_group_free (group); + } + g_list_free (dbin->groups); + dbin->groups = NULL; + + for (tmp = dbin->oldgroups; tmp; tmp = g_list_next (tmp)) { + GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; + + GST_DEBUG_OBJECT (dbin, "free old group %p", group); + gst_decode_group_free (group); + } + g_list_free (dbin->oldgroups); + dbin->oldgroups = NULL; + + GST_DEBUG_OBJECT (dbin, "removing last elements"); + + /* remove all remaining elements */ + it = gst_bin_iterate_elements (GST_BIN_CAST (dbin)); +restart: + done = FALSE; + while (!done) { + res = gst_iterator_next (it, &point); + switch (res) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (it); + goto restart; + case GST_ITERATOR_ERROR: + GST_WARNING_OBJECT (dbin, + "Had an error while iterating bin %s", GST_ELEMENT_NAME (dbin)); + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstElement *elem = GST_ELEMENT_CAST (point); + + /* don't remove the typefind element */ + if (elem != dbin->typefind) { + GST_DEBUG_OBJECT (dbin, "remove element %s", GST_ELEMENT_NAME (elem)); + gst_bin_remove (GST_BIN_CAST (dbin), elem); + gst_element_set_state (elem, GST_STATE_NULL); + } + gst_object_unref (elem); + break; + } + default: + break; + } + } + gst_iterator_free (it); } static void gst_decode_bin_dispose (GObject * object) { GstDecodeBin *decode_bin; - GList *tmp; decode_bin = GST_DECODE_BIN (object); @@ -625,27 +746,7 @@ g_value_array_free (decode_bin->factories); decode_bin->factories = NULL; - if (decode_bin->activegroup) { - gst_decode_group_free (decode_bin->activegroup); - decode_bin->activegroup = NULL; - } - - /* remove groups */ - for (tmp = decode_bin->groups; tmp; tmp = g_list_next (tmp)) { - GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; - - gst_decode_group_free (group); - } - g_list_free (decode_bin->groups); - decode_bin->groups = NULL; - - for (tmp = decode_bin->oldgroups; tmp; tmp = g_list_next (tmp)) { - GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; - - gst_decode_group_free (group); - } - g_list_free (decode_bin->oldgroups); - decode_bin->oldgroups = NULL; + gst_decode_bin_remove_groups (decode_bin); if (decode_bin->caps) gst_caps_unref (decode_bin->caps); @@ -654,7 +755,8 @@ g_free (decode_bin->encoding); decode_bin->encoding = NULL; - remove_fakesink (decode_bin); + g_list_free (decode_bin->subtitles); + decode_bin->subtitles = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -671,6 +773,11 @@ decode_bin->lock = NULL; } + if (decode_bin->dyn_lock) { + g_mutex_free (decode_bin->dyn_lock); + decode_bin->dyn_lock = NULL; + } + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -685,15 +792,22 @@ static void gst_decode_bin_set_caps (GstDecodeBin * dbin, GstCaps * caps) { + GstCaps *old; + GST_DEBUG_OBJECT (dbin, "Setting new caps: %" GST_PTR_FORMAT, caps); - DECODE_BIN_LOCK (dbin); - if (dbin->caps) - gst_caps_unref (dbin->caps); - if (caps) - gst_caps_ref (caps); - dbin->caps = caps; - DECODE_BIN_UNLOCK (dbin); + GST_OBJECT_LOCK (dbin); + old = dbin->caps; + if (old != caps) { + if (caps) + gst_caps_ref (caps); + + dbin->caps = caps; + + if (old) + gst_caps_unref (old); + } + GST_OBJECT_UNLOCK (dbin); } /* _get_caps @@ -702,7 +816,6 @@ * * MT-safe */ - static GstCaps * gst_decode_bin_get_caps (GstDecodeBin * dbin) { @@ -710,11 +823,31 @@ GST_DEBUG_OBJECT (dbin, "Getting currently set caps"); - DECODE_BIN_LOCK (dbin); + GST_OBJECT_LOCK (dbin); caps = dbin->caps; if (caps) gst_caps_ref (caps); - DECODE_BIN_UNLOCK (dbin); + GST_OBJECT_UNLOCK (dbin); + + return caps; +} + +static void +gst_decode_bin_set_sink_caps (GstDecodeBin * dbin, GstCaps * caps) +{ + GST_DEBUG_OBJECT (dbin, "Setting new caps: %" GST_PTR_FORMAT, caps); + + g_object_set (dbin->typefind, "force-caps", caps, NULL); +} + +static GstCaps * +gst_decode_bin_get_sink_caps (GstDecodeBin * dbin) +{ + GstCaps *caps; + + GST_DEBUG_OBJECT (dbin, "Getting currently set caps"); + + g_object_get (dbin->typefind, "force-caps", &caps, NULL); return caps; } @@ -722,11 +855,21 @@ static void gst_decode_bin_set_subs_encoding (GstDecodeBin * dbin, const gchar * encoding) { + GList *walk; + GST_DEBUG_OBJECT (dbin, "Setting new encoding: %s", GST_STR_NULL (encoding)); DECODE_BIN_LOCK (dbin); + GST_OBJECT_LOCK (dbin); g_free (dbin->encoding); dbin->encoding = g_strdup (encoding); + GST_OBJECT_UNLOCK (dbin); + + /* set the subtitle encoding on all added elements */ + for (walk = dbin->subtitles; walk; walk = g_list_next (walk)) { + g_object_set (G_OBJECT (walk->data), "subtitle-encoding", dbin->encoding, + NULL); + } DECODE_BIN_UNLOCK (dbin); } @@ -737,9 +880,9 @@ GST_DEBUG_OBJECT (dbin, "Getting currently set encoding"); - DECODE_BIN_LOCK (dbin); + GST_OBJECT_LOCK (dbin); encoding = g_strdup (dbin->encoding); - DECODE_BIN_UNLOCK (dbin); + GST_OBJECT_UNLOCK (dbin); return encoding; } @@ -759,6 +902,9 @@ case PROP_SUBTITLE_ENCODING: gst_decode_bin_set_subs_encoding (dbin, g_value_get_string (value)); break; + case PROP_SINK_CAPS: + gst_decode_bin_set_sink_caps (dbin, g_value_get_boxed (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -779,6 +925,9 @@ case PROP_SUBTITLE_ENCODING: g_value_take_string (value, gst_decode_bin_get_subs_encoding (dbin)); break; + case PROP_SINK_CAPS: + g_value_take_boxed (value, gst_decode_bin_get_sink_caps (dbin)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -786,9 +935,6 @@ } -static GValueArray *find_compatibles (GstDecodeBin * decode_bin, - GstPad * pad, const GstCaps * caps); - /***** * Default autoplug signal handlers *****/ @@ -808,8 +954,11 @@ { GValueArray *result; + GST_DEBUG_OBJECT (element, "finding factories"); + /* return all compatible factories for caps */ - result = find_compatibles (GST_DECODE_BIN (element), pad, caps); + result = + gst_factory_list_filter (GST_DECODE_BIN_CAST (element)->factories, caps); GST_DEBUG_OBJECT (element, "autoplug-factories returns %p", result); @@ -848,12 +997,12 @@ static gboolean is_demuxer_element (GstElement * srcelement); static gboolean connect_pad (GstDecodeBin * dbin, GstElement * src, - GstPad * pad, GstCaps * caps, GValueArray * factories, + GstDecodePad * dpad, GstPad * pad, GstCaps * caps, GValueArray * factories, GstDecodeGroup * group); static gboolean connect_element (GstDecodeBin * dbin, GstElement * element, GstDecodeGroup * group); -static void expose_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, - GstDecodeGroup * group); +static void expose_pad (GstDecodeBin * dbin, GstElement * src, + GstDecodePad * dpad, GstPad * pad, GstDecodeGroup * group); static void pad_added_group_cb (GstElement * element, GstPad * pad, GstDecodeGroup * group); @@ -867,7 +1016,8 @@ GstDecodeBin * dbin); static void no_more_pads_cb (GstElement * element, GstDecodeBin * dbin); -static GstDecodeGroup *get_current_group (GstDecodeBin * dbin); +static GstDecodeGroup *get_current_group (GstDecodeBin * dbin, + gboolean create, gboolean demux, gboolean * created); /* called when a new pad is discovered. It will perform some basic actions * before trying to link something to it. @@ -887,6 +1037,7 @@ { gboolean apcontinue = TRUE; GValueArray *factories = NULL, *result = NULL; + GstDecodePad *dpad; GST_DEBUG_OBJECT (dbin, "Pad %s:%s caps:%" GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (pad), caps); @@ -897,10 +1048,12 @@ if (gst_caps_is_any (caps)) goto any_caps; + dpad = gst_decode_pad_new (dbin, pad, group); + /* 1. Emit 'autoplug-continue' the result will tell us if this pads needs * further autoplugging. */ g_signal_emit (G_OBJECT (dbin), - gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, pad, caps, + gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, dpad, caps, &apcontinue); /* 1.a if autoplug-continue is FALSE or caps is a raw format, goto pad_is_final */ @@ -915,7 +1068,7 @@ /* 1.c else get the factories and if there's no compatible factory goto * unknown_type */ g_signal_emit (G_OBJECT (dbin), - gst_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES], 0, pad, caps, + gst_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES], 0, dpad, caps, &factories); /* NULL means that we can expose the pad */ @@ -926,20 +1079,22 @@ if (factories->n_values == 0) { /* no compatible factories */ g_value_array_free (factories); + gst_object_unref (dpad); goto unknown_type; } /* 1.d sort some more. */ g_signal_emit (G_OBJECT (dbin), - gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT], 0, pad, caps, factories, + gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT], 0, dpad, caps, factories, &result); g_value_array_free (factories); factories = result; /* 1.e else continue autoplugging something from the list. */ GST_LOG_OBJECT (pad, "Let's continue discovery on this pad"); - connect_pad (dbin, src, pad, caps, factories, group); - + connect_pad (dbin, src, dpad, pad, caps, factories, group); + + gst_object_unref (dpad); g_value_array_free (factories); return; @@ -947,7 +1102,8 @@ expose_pad: { GST_LOG_OBJECT (dbin, "Pad is final. autoplug-continue:%d", apcontinue); - expose_pad (dbin, src, pad, group); + expose_pad (dbin, src, dpad, pad, group); + gst_object_unref (dpad); return; } unknown_type: @@ -956,9 +1112,10 @@ g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); - /* Check if there are no pending groups, if so, remove fakesink */ - if (dbin->groups == NULL) - remove_fakesink (dbin); + /* Check if there are no pending groups, if so, commit our state */ + if (dbin->groups == NULL) { + do_async_done (dbin); + } if (src == dbin->typefind) { gchar *desc; @@ -979,6 +1136,7 @@ non_fixed: { GST_DEBUG_OBJECT (pad, "pad has non-fixed caps delay autoplugging"); + gst_object_unref (dpad); goto setup_caps_delay; } any_caps: @@ -993,7 +1151,8 @@ if (group) { GROUP_MUTEX_LOCK (group); group->nbdynamic++; - GST_LOG ("Group %p has now %d dynamic elements", group, group->nbdynamic); + GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic elements", group, + group->nbdynamic); GROUP_MUTEX_UNLOCK (group); g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (caps_notify_group_cb), group); @@ -1010,11 +1169,15 @@ * Try to connect the given pad to an element created from one of the factories, * and recursively. * + * Note that dpad is ghosting pad, and so pad is linked; be sure to unset dpad's + * target before trying to link pad. + * * Returns TRUE if an element was properly created and linked */ static gboolean -connect_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, - GstCaps * caps, GValueArray * factories, GstDecodeGroup * group) +connect_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad, + GstPad * pad, GstCaps * caps, GValueArray * factories, + GstDecodeGroup * group) { gboolean res = FALSE; GstPad *mqpad = NULL; @@ -1030,17 +1193,14 @@ GST_LOG_OBJECT (src, "is a demuxer, connecting the pad through multiqueue"); if (!group) - if (!(group = get_current_group (dbin))) { - group = gst_decode_group_new (dbin, TRUE); - DECODE_BIN_LOCK (dbin); - dbin->groups = g_list_append (dbin->groups, group); - DECODE_BIN_UNLOCK (dbin); - } - + group = get_current_group (dbin, TRUE, TRUE, NULL); + + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), NULL); if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad))) goto beach; src = group->multiqueue; pad = mqpad; + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad); } /* 2. Try to create an element and link to it */ @@ -1049,6 +1209,7 @@ GstElementFactory *factory; GstElement *element; GstPad *sinkpad; + gboolean subtitle; /* take first factory */ factory = g_value_get_object (g_value_array_get_nth (factories, 0)); @@ -1058,7 +1219,7 @@ /* emit autoplug-select to see what we should do with it. */ g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_AUTOPLUG_SELECT], - 0, pad, caps, factory, &ret); + 0, dpad, caps, factory, &ret); switch (ret) { case GST_AUTOPLUG_SELECT_TRY: @@ -1067,7 +1228,7 @@ case GST_AUTOPLUG_SELECT_EXPOSE: GST_DEBUG_OBJECT (dbin, "autoplug select requested expose"); /* expose the pad, we don't have the source element */ - expose_pad (dbin, src, pad, group); + expose_pad (dbin, src, dpad, pad, group); res = TRUE; goto beach; case GST_AUTOPLUG_SELECT_SKIP: @@ -1078,6 +1239,9 @@ break; } + /* 2.0. Unlink pad */ + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), NULL); + /* 2.1. Try to create an element */ if ((element = gst_element_factory_create (factory, NULL)) == NULL) { GST_WARNING_OBJECT (dbin, "Could not create an element from %s", @@ -1100,6 +1264,7 @@ if (!(sinkpad = find_sink_pad (element))) { GST_WARNING_OBJECT (dbin, "Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element)); + gst_element_set_state (element, GST_STATE_NULL); gst_object_unref (element); continue; } @@ -1109,6 +1274,7 @@ GST_WARNING_OBJECT (dbin, "Couldn't add %s to the bin", GST_ELEMENT_NAME (element)); gst_object_unref (sinkpad); + gst_element_set_state (element, GST_STATE_NULL); gst_object_unref (element); continue; } @@ -1128,6 +1294,17 @@ /* link this element further */ connect_element (dbin, element, group); + /* try to configure the subtitle encoding property when we can */ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), + "subtitle-encoding")) { + GST_DEBUG_OBJECT (dbin, + "setting subtitle-encoding=%s to element", dbin->encoding); + g_object_set (G_OBJECT (element), "subtitle-encoding", dbin->encoding, + NULL); + subtitle = TRUE; + } else + subtitle = FALSE; + /* Bring the element to the state of the parent */ if ((gst_element_set_state (element, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) { @@ -1137,6 +1314,13 @@ gst_bin_remove (GST_BIN (dbin), element); continue; } + if (subtitle) { + DECODE_BIN_LOCK (dbin); + /* we added the element now, add it to the list of subtitle-encoding + * elements when we can set the property */ + dbin->subtitles = g_list_prepend (dbin->subtitles, element); + DECODE_BIN_UNLOCK (dbin); + } res = TRUE; break; @@ -1179,7 +1363,7 @@ case GST_PAD_ALWAYS: { /* get the pad that we need to autoplug */ - GstPad *pad = gst_element_get_pad (element, templ_name); + GstPad *pad = gst_element_get_static_pad (element, templ_name); if (pad) { GST_DEBUG_OBJECT (dbin, "got the pad for always template %s", @@ -1198,7 +1382,7 @@ { /* try to get the pad to see if it is already created or * not */ - GstPad *pad = gst_element_get_pad (element, templ_name); + GstPad *pad = gst_element_get_static_pad (element, templ_name); if (pad) { GST_DEBUG_OBJECT (dbin, "got the pad for sometimes template %s", @@ -1223,11 +1407,12 @@ /* 2. if there are more potential pads, connect to relevent signals */ if (dynamic) { if (group) { - GST_LOG ("Adding signals to element %s in group %p", + GST_LOG_OBJECT (dbin, "Adding signals to element %s in group %p", GST_ELEMENT_NAME (element), group); GROUP_MUTEX_LOCK (group); group->nbdynamic++; - GST_LOG ("Group %p has now %d dynamic elements", group, group->nbdynamic); + GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic elements", group, + group->nbdynamic); GROUP_MUTEX_UNLOCK (group); g_signal_connect (G_OBJECT (element), "pad-added", G_CALLBACK (pad_added_group_cb), group); @@ -1269,8 +1454,8 @@ * If group is NULL, a GstDecodeGroup will be created and setup properly. */ static void -expose_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, - GstDecodeGroup * group) +expose_pad (GstDecodeBin * dbin, GstElement * src, GstDecodePad * dpad, + GstPad * pad, GstDecodeGroup * group) { gboolean newgroup = FALSE; gboolean isdemux; @@ -1282,23 +1467,19 @@ isdemux = is_demuxer_element (src); if (!group) - if (!(group = get_current_group (dbin))) { - group = gst_decode_group_new (dbin, isdemux); - DECODE_BIN_LOCK (dbin); - dbin->groups = g_list_append (dbin->groups, group); - DECODE_BIN_UNLOCK (dbin); - newgroup = TRUE; - } + group = get_current_group (dbin, TRUE, isdemux, &newgroup); if (isdemux) { GST_LOG_OBJECT (src, "connecting the pad through multiqueue"); + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), NULL); if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad))) goto beach; pad = mqpad; + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad); } - gst_decode_group_control_source_pad (group, pad); + gst_decode_group_control_source_pad (group, dpad); if (newgroup && !isdemux) { /* If we have discovered a raw pad and it doesn't belong to any group, @@ -1321,6 +1502,16 @@ GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); + /* If the typefinder (but not something else) finds text/plain - i.e. that's + * the top-level type of the file - then error out. + */ + if (gst_structure_has_name (gst_caps_get_structure (caps, 0), "text/plain")) { + GST_ELEMENT_ERROR (decode_bin, STREAM, WRONG_TYPE, + (_("This appears to be a text file")), + ("decodebin2 cannot decode plain text files")); + goto exit; + } + /* we can only deal with one type, we don't yet support dynamically changing * caps from the typefind element */ if (decode_bin->have_type) @@ -1343,27 +1534,33 @@ { GstCaps *caps; gboolean expose = FALSE; + GstDecodeBin *dbin; + + dbin = group->dbin; GST_DEBUG_OBJECT (pad, "pad added, group:%p", group); caps = gst_pad_get_caps (pad); - analyze_new_pad (group->dbin, element, pad, caps, group); + analyze_new_pad (dbin, element, pad, caps, group); if (caps) gst_caps_unref (caps); GROUP_MUTEX_LOCK (group); - group->nbdynamic--; - GST_LOG ("Group %p has now %d dynamic objects", group, group->nbdynamic); + if (group->nbdynamic > 0) + group->nbdynamic--; + GST_LOG_OBJECT (dbin, "Group %p has now %d dynamic objects", group, + group->nbdynamic); if (group->nbdynamic == 0) expose = TRUE; GROUP_MUTEX_UNLOCK (group); if (expose) { - GST_LOG - ("That was the last dynamic object, now attempting to expose the group"); - DECODE_BIN_LOCK (group->dbin); - gst_decode_group_expose (group); - DECODE_BIN_UNLOCK (group->dbin); + GST_LOG_OBJECT (dbin, + "That was the last dynamic object, now attempting to expose the group"); + DECODE_BIN_LOCK (dbin); + if (!gst_decode_group_expose (group)) + GST_WARNING_OBJECT (dbin, "Couldn't expose group"); + DECODE_BIN_UNLOCK (dbin); } } @@ -1382,7 +1579,7 @@ { GST_LOG_OBJECT (element, "no more pads, setting group %p to complete", group); - /* FIXME : FILLME */ + /* when we received no_more_pads, we can complete the pads of the group */ gst_decode_group_set_complete (group); } @@ -1413,15 +1610,16 @@ GST_LOG_OBJECT (element, "No more pads, setting current group to complete"); /* Find the non-complete group, there should only be one */ - if (!(group = get_current_group (dbin))) + if (!(group = get_current_group (dbin, FALSE, FALSE, NULL))) goto no_group; gst_decode_group_set_complete (group); + return; no_group: { - GST_WARNING_OBJECT (dbin, "We couldn't find a non-completed group !!"); + GST_DEBUG_OBJECT (dbin, "We couldn't find a non-completed group"); return; } } @@ -1434,6 +1632,10 @@ GST_LOG_OBJECT (dbin, "Notified caps for pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + /* Disconnect this; if we still need it, we'll reconnect to this in + * analyze_new_pad */ + g_signal_handlers_disconnect_by_func (pad, (gpointer*)caps_notify_cb, dbin); + element = GST_ELEMENT_CAST (gst_pad_get_parent (pad)); pad_added_cb (element, pad, dbin); @@ -1448,6 +1650,10 @@ GST_LOG_OBJECT (pad, "Notified caps for pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + /* Disconnect this; if we still need it, we'll reconnect to this in + * analyze_new_pad */ + g_signal_handlers_disconnect_by_func (pad, (gpointer*)caps_notify_group_cb, group); + element = GST_ELEMENT_CAST (gst_pad_get_parent (pad)); pad_added_group_cb (element, pad, group); @@ -1455,21 +1661,6 @@ gst_object_unref (element); } -/* this function runs through the element factories and returns a value array of - * all elements that are able to sink the given caps - */ -static GValueArray * -find_compatibles (GstDecodeBin * decode_bin, GstPad * pad, const GstCaps * caps) -{ - GValueArray *result; - - GST_DEBUG_OBJECT (decode_bin, "finding factories"); - - result = gst_factory_list_filter (decode_bin->factories, caps); - - return result; -} - /* Decide whether an element is a demuxer based on the * klass and number/type of src pad templates it has */ static gboolean @@ -1533,7 +1724,10 @@ GST_LOG_OBJECT (dbin, "Checking with caps %" GST_PTR_FORMAT, caps); + /* lock for getting the caps */ + GST_OBJECT_LOCK (dbin); intersection = gst_caps_intersect (dbin->caps, caps); + GST_OBJECT_UNLOCK (dbin); res = (!(gst_caps_is_empty (intersection))); @@ -1549,38 +1743,45 @@ * GstDecodeGroup functions ****/ +/* The overrun callback is used to expose groups that have not yet had their + * no_more_pads called while the (large) multiqueue overflowed. When this + * happens we must assume that the no_more_pads will not arrive anymore and we + * must expose the pads that we have. + */ static void multi_queue_overrun_cb (GstElement * queue, GstDecodeGroup * group) { - GST_LOG_OBJECT (group->dbin, "multiqueue is full"); - - /* if we haven't exposed the group, do it */ - DECODE_BIN_LOCK (group->dbin); - gst_decode_group_expose (group); - DECODE_BIN_UNLOCK (group->dbin); + GstDecodeBin *dbin; + gboolean expose; + + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "multiqueue %p is full", queue); + + GROUP_MUTEX_LOCK (group); + if (group->complete) { + /* the group was already complete (had the no_more_pads called), we + * can ignore the overrun signal, the last remaining dynamic element + * will expose the group eventually. */ + GST_LOG_OBJECT (dbin, "group %p was already complete", group); + expose = FALSE; + } else { + /* set number of dynamic element to 0, we don't expect anything anymore + * and we need the groups to be 0 for the expose to work */ + group->nbdynamic = 0; + expose = TRUE; + } + GROUP_MUTEX_UNLOCK (group); + + if (expose) { + DECODE_BIN_LOCK (dbin); + if (!gst_decode_group_expose (group)) + GST_WARNING_OBJECT (dbin, "Couldn't expose group"); + DECODE_BIN_UNLOCK (group->dbin); + } } -static void -multi_queue_underrun_cb (GstElement * queue, GstDecodeGroup * group) -{ - GstDecodeBin *dbin = group->dbin; - - GST_LOG_OBJECT (dbin, "multiqueue is empty for group %p", group); - - /* Check if we need to activate another group */ - DECODE_BIN_LOCK (dbin); - if ((group == dbin->activegroup) && dbin->groups) { - GST_DEBUG_OBJECT (dbin, "Switching to new group"); - /* unexpose current active */ - gst_decode_group_hide (group); - - /* expose first group of groups */ - gst_decode_group_expose ((GstDecodeGroup *) dbin->groups->data); - } - DECODE_BIN_UNLOCK (dbin); -} - -/* gst_decode_group_new +/* gst_decode_group_new: * * Creates a new GstDecodeGroup. It is up to the caller to add it to the list * of groups. @@ -1595,7 +1796,7 @@ if (use_queue) { if (!(mq = gst_element_factory_make ("multiqueue", NULL))) { - GST_WARNING ("Couldn't create multiqueue element"); + GST_ERROR_OBJECT (dbin, "Couldn't create multiqueue element"); return NULL; } } else { @@ -1619,15 +1820,11 @@ * memory. When this queue overruns, we assume the group is complete and can * be exposed. */ g_object_set (G_OBJECT (mq), - "max-size-bytes", 2 * 1024 * 1024, - "max-size-time", 5 * GST_SECOND, "max-size-buffers", 0, NULL); + "max-size-bytes", (guint) 2 * 1024 * 1024, + "max-size-time", (guint64) 0, "max-size-buffers", (guint) 0, NULL); /* will expose the group */ group->overrunsig = g_signal_connect (G_OBJECT (mq), "overrun", G_CALLBACK (multi_queue_overrun_cb), group); - /* will hide the group again, this is usually called when the multiqueue is - * drained because of EOS. */ - group->underrunsig = g_signal_connect (G_OBJECT (mq), "underrun", - G_CALLBACK (multi_queue_underrun_cb), group); gst_bin_add (GST_BIN (dbin), mq); gst_element_set_state (mq, GST_STATE_PAUSED); @@ -1638,14 +1835,20 @@ return group; } -/** get_current_group: +/* get_current_group: + * @dbin: the decodebin + * @create: create the group when not present + * @as_demux: create the group as a demuxer + * @created: result when the group was created * - * Returns the current non-completed group. + * Returns the current non-completed group. The dynamic refcount of the group is + * increased when dealing with a demuxer. * - * Returns NULL if no groups are available, or all groups are completed. + * Returns: %NULL if no groups are available, or all groups are completed. */ static GstDecodeGroup * -get_current_group (GstDecodeBin * dbin) +get_current_group (GstDecodeBin * dbin, gboolean create, gboolean as_demux, + gboolean * created) { GList *tmp; GstDecodeGroup *group = NULL; @@ -1654,13 +1857,27 @@ for (tmp = dbin->groups; tmp; tmp = g_list_next (tmp)) { GstDecodeGroup *this = (GstDecodeGroup *) tmp->data; + GROUP_MUTEX_LOCK (this); GST_LOG_OBJECT (dbin, "group %p, complete:%d", this, this->complete); if (!this->complete) { group = this; + GROUP_MUTEX_UNLOCK (this); break; + } else { + GROUP_MUTEX_UNLOCK (this); } } + if (group == NULL && create) { + group = gst_decode_group_new (dbin, as_demux); + GST_LOG_OBJECT (dbin, "added group %p, demux %d", group, as_demux); + dbin->groups = g_list_prepend (dbin->groups, group); + if (created) + *created = TRUE; + /* demuxers are dynamic, we need no-more-pads or overrun now */ + if (as_demux) + group->nbdynamic++; + } DECODE_BIN_UNLOCK (dbin); GST_LOG_OBJECT (dbin, "Returning group %p", group); @@ -1668,20 +1885,6 @@ return group; } -static gboolean -group_demuxer_event_probe (GstPad * pad, GstEvent * event, - GstDecodeGroup * group) -{ - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - GST_DEBUG_OBJECT (group->dbin, - "Got EOS on group input pads, exposing group if it wasn't before"); - DECODE_BIN_LOCK (group->dbin); - gst_decode_group_expose (group); - DECODE_BIN_UNLOCK (group->dbin); - } - return TRUE; -} - /* gst_decode_group_control_demuxer_pad * * Adds a new demuxer srcpad to the given group. @@ -1692,24 +1895,27 @@ static GstPad * gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad) { + GstDecodeBin *dbin; GstPad *srcpad, *sinkpad; gchar *nb, *sinkname, *srcname; - GST_LOG ("group:%p pad %s:%s", group, GST_DEBUG_PAD_NAME (pad)); + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "group:%p pad %s:%s", group, GST_DEBUG_PAD_NAME (pad)); srcpad = NULL; if (!(sinkpad = gst_element_get_request_pad (group->multiqueue, "sink%d"))) { - GST_ERROR ("Couldn't get sinkpad from multiqueue"); + GST_ERROR_OBJECT (dbin, "Couldn't get sinkpad from multiqueue"); return NULL; } if ((gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) { - GST_ERROR ("Couldn't link demuxer and multiqueue"); + GST_ERROR_OBJECT (dbin, "Couldn't link demuxer and multiqueue"); goto beach; } - group->reqpads = g_list_append (group->reqpads, sinkpad); + group->reqpads = g_list_prepend (group->reqpads, sinkpad); sinkname = gst_pad_get_name (sinkpad); nb = sinkname + 4; @@ -1718,14 +1924,11 @@ GROUP_MUTEX_LOCK (group); - if (!(srcpad = gst_element_get_pad (group->multiqueue, srcname))) { - GST_ERROR ("Couldn't get srcpad %s from multiqueue", srcname); + if (!(srcpad = gst_element_get_static_pad (group->multiqueue, srcname))) { + GST_ERROR_OBJECT (dbin, "Couldn't get srcpad %s from multiqueue", srcname); goto chiringuito; } - /* connect event handler on pad to intercept EOS events */ - gst_pad_add_event_probe (pad, G_CALLBACK (group_demuxer_event_probe), group); - chiringuito: g_free (srcname); GROUP_MUTEX_UNLOCK (group); @@ -1736,23 +1939,18 @@ } static gboolean -gst_decode_group_control_source_pad (GstDecodeGroup * group, GstPad * pad) +gst_decode_group_control_source_pad (GstDecodeGroup * group, + GstDecodePad * dpad) { - GstDecodePad *dpad; - g_return_val_if_fail (group != NULL, FALSE); - GST_LOG ("group:%p , pad %s:%s", group, GST_DEBUG_PAD_NAME (pad)); + GST_DEBUG_OBJECT (dpad, "adding decode pad to group %p", group); /* FIXME : check if pad is already controlled */ + gst_decode_pad_activate (dpad, group); GROUP_MUTEX_LOCK (group); - - /* Create GstDecodePad for the pad */ - dpad = gst_decode_pad_new (group, pad, TRUE); - - group->endpads = g_list_append (group->endpads, dpad); - + group->endpads = g_list_prepend (group->endpads, gst_object_ref (dpad)); GROUP_MUTEX_UNLOCK (group); return TRUE; @@ -1765,20 +1963,25 @@ * and will ghost/expose all pads on decodebin if the group is the current one. * * Call with the group lock taken ! MT safe + * + * Returns: TRUE when the group is completely blocked and ready to be exposed. */ -static void +static gboolean gst_decode_group_check_if_blocked (GstDecodeGroup * group) { + GstDecodeBin *dbin; GList *tmp; gboolean blocked = TRUE; - GST_LOG ("group : %p , ->complete:%d , ->nbdynamic:%d", + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "group : %p , ->complete:%d , ->nbdynamic:%d", group, group->complete, group->nbdynamic); - /* 1. don't do anything if group is not complete */ + /* don't do anything if group is not complete */ if (!group->complete || group->nbdynamic) { GST_DEBUG_OBJECT (group->dbin, "Group isn't complete yet"); - return; + return FALSE; } for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { @@ -1790,64 +1993,100 @@ } } - /* 2. Update status of group */ + /* Update status of group */ group->blocked = blocked; - GST_LOG ("group is blocked:%d", blocked); - - /* 3. don't do anything if not blocked completely */ - if (!blocked) - return; - - /* 4. if we're the current group, expose pads */ - DECODE_BIN_LOCK (group->dbin); - if (!gst_decode_group_expose (group)) - GST_WARNING_OBJECT (group->dbin, "Couldn't expose group"); - DECODE_BIN_UNLOCK (group->dbin); + GST_LOG_OBJECT (dbin, "group is blocked:%d", blocked); + + return blocked; } +/* activate the next group when there is one + * + * Returns: TRUE when group was the active group and there was a + * next group to activate. + */ +static gboolean +gst_decode_bin_activate_next_group (GstDecodeBin * dbin, GstDecodeGroup * group) +{ + gboolean have_next = FALSE; + + DECODE_BIN_LOCK (dbin); + /* Check if there is a next group to activate */ + if ((group == dbin->activegroup) && dbin->groups) { + GstDecodeGroup *newgroup; + + /* get the next group */ + newgroup = (GstDecodeGroup *) dbin->groups->data; + + GST_DEBUG_OBJECT (dbin, "Switching to new group"); + + /* hide current group */ + gst_decode_group_hide (group); + /* expose next group */ + gst_decode_group_expose (newgroup); + + /* we have a next group */ + have_next = TRUE; + } + DECODE_BIN_UNLOCK (dbin); + + return have_next; +} + +/* check if the group is drained, meaning all pads have seen an EOS + * event. */ static void -gst_decode_group_check_if_drained (GstDecodeGroup * group) +gst_decode_pad_handle_eos (GstDecodePad * pad) { GList *tmp; - GstDecodeBin *dbin = group->dbin; + GstDecodeBin *dbin; + GstDecodeGroup *group; gboolean drained = TRUE; - GST_LOG ("group : %p", group); + group = pad->group; + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "group : %p, pad %p", group, pad); + + GROUP_MUTEX_LOCK (group); + /* mark pad as drained */ + pad->drained = TRUE; + + /* Ensure we only emit the drained signal once, for this group */ + if (group->drained) + goto was_drained; for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { GstDecodePad *dpad = (GstDecodePad *) tmp->data; - GST_LOG ("testing dpad %p", dpad); + GST_LOG_OBJECT (dbin, "testing dpad %p %d", dpad, dpad->drained); if (!dpad->drained) { drained = FALSE; break; } } - group->drained = drained; - if (!drained) - return; - - /* we are drained. Check if there is a next group to activate */ - DECODE_BIN_LOCK (dbin); - if ((group == dbin->activegroup) && dbin->groups) { - GST_DEBUG_OBJECT (dbin, "Switching to new group"); - - /* hide current group */ - gst_decode_group_hide (group); - /* expose next group */ - gst_decode_group_expose ((GstDecodeGroup *) dbin->groups->data); - /* we're not yet drained now */ - drained = FALSE; - } - DECODE_BIN_UNLOCK (dbin); + GROUP_MUTEX_UNLOCK (group); if (drained) { - /* no more groups to activate, we're completely drained now */ - GST_LOG ("all groups drained, fire signal"); - g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_DRAINED], 0, - NULL); + /* the current group is completely drained, try to activate the next + * group. this function returns FALSE if there was no next group activated + * and so we are really drained. */ + if (!gst_decode_bin_activate_next_group (dbin, group)) { + /* no more groups to activate, we're completely drained now */ + GST_LOG_OBJECT (dbin, "all groups drained, fire signal"); + g_signal_emit (G_OBJECT (dbin), gst_decode_bin_signals[SIGNAL_DRAINED], 0, + NULL); + } + } + return; + +was_drained: + { + GST_LOG_OBJECT (dbin, "group was already drained"); + GROUP_MUTEX_UNLOCK (group); + return; } } @@ -1863,17 +2102,13 @@ static gint sort_end_pads (GstDecodePad * da, GstDecodePad * db) { - GstPad *a, *b; gint va, vb; GstCaps *capsa, *capsb; GstStructure *sa, *sb; const gchar *namea, *nameb; - a = da->pad; - b = db->pad; - - capsa = gst_pad_get_caps (a); - capsb = gst_pad_get_caps (b); + capsa = gst_pad_get_caps (GST_PAD (da)); + capsb = gst_pad_get_caps (GST_PAD (db)); sa = gst_caps_get_structure ((const GstCaps *) capsa, 0); sb = gst_caps_get_structure ((const GstCaps *) capsb, 0); @@ -1913,146 +2148,159 @@ * * Expose this group's pads. * - * Not MT safe, please take the group lock + * Not MT safe, please take the decodebin lock */ static gboolean gst_decode_group_expose (GstDecodeGroup * group) { GList *tmp; GList *next = NULL; - - if (group->dbin->activegroup) { - GST_DEBUG_OBJECT (group->dbin, "A group is already active and exposed"); - return TRUE; + GstDecodeBin *dbin; + + dbin = group->dbin; + + GST_DEBUG_OBJECT (dbin, "going to expose group %p", group); + + if (group->nbdynamic) { + GST_DEBUG_OBJECT (dbin, + "Group %p still has %d dynamic objects, not exposing yet", group, + group->nbdynamic); + return FALSE; } - if (group->dbin->activegroup == group) { - GST_WARNING ("Group %p is already exposed", group); + if (dbin->activegroup == group) { + GST_DEBUG_OBJECT (dbin, "Group %p is already exposed, all is fine", group); return TRUE; } - if (!group->dbin->groups - || (group != (GstDecodeGroup *) group->dbin->groups->data)) { - GST_WARNING ("Group %p is not the first group to expose", group); - return FALSE; - } - - if (group->nbdynamic) { - GST_WARNING ("Group %p still has %d dynamic objects, not exposing yet", - group, group->nbdynamic); - return FALSE; - } - - GST_LOG ("Exposing group %p", group); - if (group->multiqueue) { /* update runtime limits. At runtime, we try to keep the amount of buffers * in the queues as low as possible (but at least 5 buffers). */ g_object_set (G_OBJECT (group->multiqueue), - "max-size-bytes", 2 * 1024 * 1024, - "max-size-time", 2 * GST_SECOND, "max-size-buffers", 5, NULL); + "max-size-bytes", 2 * 1024 * 1024, "max-size-buffers", 5, NULL); /* we can now disconnect any overrun signal, which is used to expose the * group. */ if (group->overrunsig) { - GST_LOG ("Disconnecting overrun"); + GST_LOG_OBJECT (dbin, "Disconnecting overrun"); g_signal_handler_disconnect (group->multiqueue, group->overrunsig); group->overrunsig = 0; } } + if (dbin->activegroup) { + GST_DEBUG_OBJECT (dbin, + "another group %p is already exposed, waiting for EOS", + dbin->activegroup); + return TRUE; + } + + if (!dbin->groups || (group != (GstDecodeGroup *) dbin->groups->data)) { + GST_WARNING_OBJECT (dbin, "Group %p is not the first group to expose", + group); + return FALSE; + } + + GST_LOG_OBJECT (dbin, "Exposing group %p", group); + /* re-order pads : video, then audio, then others */ group->endpads = g_list_sort (group->endpads, (GCompareFunc) sort_end_pads); /* Expose pads */ - for (tmp = group->endpads; tmp; tmp = next) { GstDecodePad *dpad = (GstDecodePad *) tmp->data; gchar *padname; - GstPad *ghost; next = g_list_next (tmp); - /* 1. ghost pad */ - padname = g_strdup_printf ("src%d", group->dbin->nbpads); - group->dbin->nbpads++; - - GST_LOG_OBJECT (group->dbin, "About to expose pad %s:%s", - GST_DEBUG_PAD_NAME (dpad->pad)); - - ghost = gst_ghost_pad_new (padname, dpad->pad); - gst_pad_set_active (ghost, TRUE); - gst_element_add_pad (GST_ELEMENT (group->dbin), ghost); - group->ghosts = g_list_append (group->ghosts, ghost); - + /* 1. rewrite name */ + padname = g_strdup_printf ("src%d", dbin->nbpads); + dbin->nbpads++; + GST_DEBUG_OBJECT (dbin, "About to expose dpad %s as %s", + GST_OBJECT_NAME (dpad), padname); + gst_object_set_name (GST_OBJECT (dpad), padname); g_free (padname); - /* 2. emit signal */ - GST_DEBUG_OBJECT (group->dbin, "emitting new-decoded-pad"); - g_signal_emit (G_OBJECT (group->dbin), - gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, + /* 2. activate and add */ + if (!gst_element_add_pad (GST_ELEMENT (dbin), GST_PAD (dpad))) { + /* not really fatal, we can try to add the other pads */ + g_warning ("error adding pad to decodebin2"); + continue; + } + dpad->added = TRUE; + + /* 3. emit signal */ + GST_DEBUG_OBJECT (dbin, "emitting new-decoded-pad"); + g_signal_emit (G_OBJECT (dbin), + gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, dpad, (next == NULL)); - GST_DEBUG_OBJECT (group->dbin, "emitted new-decoded-pad"); + GST_DEBUG_OBJECT (dbin, "emitted new-decoded-pad"); } /* signal no-more-pads. This allows the application to hook stuff to the * exposed pads */ - GST_LOG_OBJECT (group->dbin, "signalling no-more-pads"); - gst_element_no_more_pads (GST_ELEMENT (group->dbin)); - - /* 3. Unblock internal pads. The application should have connected stuff now + GST_LOG_OBJECT (dbin, "signalling no-more-pads"); + gst_element_no_more_pads (GST_ELEMENT (dbin)); + + /* 4. Unblock internal pads. The application should have connected stuff now * so that streaming can continue. */ for (tmp = group->endpads; tmp; tmp = next) { GstDecodePad *dpad = (GstDecodePad *) tmp->data; next = g_list_next (tmp); - GST_DEBUG_OBJECT (dpad->pad, "unblocking"); - gst_pad_set_blocked_async (dpad->pad, FALSE, - (GstPadBlockCallback) source_pad_blocked_cb, dpad); - GST_DEBUG_OBJECT (dpad->pad, "unblocked"); + GST_DEBUG_OBJECT (dpad, "unblocking"); + gst_decode_pad_unblock (dpad); + GST_DEBUG_OBJECT (dpad, "unblocked"); } - group->dbin->activegroup = group; + dbin->activegroup = group; /* pop off the first group */ - group->dbin->groups = - g_list_delete_link (group->dbin->groups, group->dbin->groups); - - remove_fakesink (group->dbin); + if (dbin->groups && dbin->groups->data) { + GST_LOG_OBJECT (dbin, "removed group %p", dbin->groups->data); + dbin->groups = g_list_delete_link (dbin->groups, dbin->groups); + } else { + GST_LOG_OBJECT (dbin, "no more groups"); + } + + do_async_done (dbin); group->exposed = TRUE; - GST_LOG_OBJECT (group->dbin, "Group %p exposed", group); + GST_LOG_OBJECT (dbin, "Group %p exposed", group); return TRUE; } +/* must be called with the decodebin lock */ static void gst_decode_group_hide (GstDecodeGroup * group) { GList *tmp; - - GST_LOG ("Hiding group %p", group); - - if (group != group->dbin->activegroup) { - GST_WARNING ("This group is not the active one, aborting"); + GstDecodeBin *dbin; + + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "Hiding group %p", group); + + if (group != dbin->activegroup) { + GST_WARNING_OBJECT (dbin, "This group is not the active one, ignoring"); return; } GROUP_MUTEX_LOCK (group); - /* Remove ghost pads */ - for (tmp = group->ghosts; tmp; tmp = g_list_next (tmp)) - gst_element_remove_pad (GST_ELEMENT (group->dbin), (GstPad *) tmp->data); - - g_list_free (group->ghosts); - group->ghosts = NULL; - + for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { + GstDecodePad *dpad = (GstDecodePad *) tmp->data; + + if (dpad->added) + gst_element_remove_pad (GST_ELEMENT (group->dbin), GST_PAD (dpad)); + dpad->added = FALSE; + } group->exposed = FALSE; - GROUP_MUTEX_UNLOCK (group); group->dbin->activegroup = NULL; - group->dbin->oldgroups = g_list_append (group->dbin->oldgroups, group); + group->dbin->oldgroups = g_list_prepend (group->dbin->oldgroups, group); } static void @@ -2061,8 +2309,11 @@ GstIterator *it; GstIteratorResult res; gpointer point; - - GST_LOG ("element:%s", GST_ELEMENT_NAME (element)); + GstDecodeBin *dbin; + + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "element:%s", GST_ELEMENT_NAME (element)); /* call on downstream elements */ it = gst_element_iterate_src_pads (element); @@ -2079,7 +2330,8 @@ goto restart; case GST_ITERATOR_ERROR: { - GST_WARNING ("Had an error while iterating source pads of element: %s", + GST_WARNING_OBJECT (dbin, + "Had an error while iterating source pads of element: %s", GST_ELEMENT_NAME (element)); goto beach; } @@ -2108,7 +2360,11 @@ done: gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (group->dbin), element); + DECODE_BIN_LOCK (dbin); + /* remove possible subtitle element */ + dbin->subtitles = g_list_remove (dbin->subtitles, element); + DECODE_BIN_UNLOCK (dbin); + gst_bin_remove (GST_BIN (dbin), element); beach: gst_iterator_free (it); @@ -2119,27 +2375,29 @@ static void gst_decode_group_free (GstDecodeGroup * group) { + GstDecodeBin *dbin; GList *tmp; - GST_LOG ("group %p", group); + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "group %p", group); GROUP_MUTEX_LOCK (group); - /* free ghost pads */ - if (group == group->dbin->activegroup) { - for (tmp = group->ghosts; tmp; tmp = g_list_next (tmp)) - gst_element_remove_pad (GST_ELEMENT (group->dbin), (GstPad *) tmp->data); - - g_list_free (group->ghosts); - group->ghosts = NULL; + /* remove exposed pads */ + if (group == dbin->activegroup) { + for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { + GstDecodePad *dpad = (GstDecodePad *) tmp->data; + + if (dpad->added) + gst_element_remove_pad (GST_ELEMENT (dbin), GST_PAD (dpad)); + dpad->added = FALSE; + } } /* Clear all GstDecodePad */ - for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { - GstDecodePad *dpad = (GstDecodePad *) tmp->data; - - g_free (dpad); - } + for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) + gst_object_unref (tmp->data); g_list_free (group->endpads); group->endpads = NULL; @@ -2152,8 +2410,6 @@ /* disconnect signal handlers on multiqueue */ if (group->multiqueue) { - if (group->underrunsig) - g_signal_handler_disconnect (group->multiqueue, group->underrunsig); if (group->overrunsig) g_signal_handler_disconnect (group->multiqueue, group->overrunsig); deactivate_free_recursive (group, group->multiqueue); @@ -2170,40 +2426,85 @@ /* gst_decode_group_set_complete: * * Mark the group as complete. This means no more streams will be controlled - * through this group. + * through this group. This method is usually called when we got no_more_pads or + * when we added the last pad not from a demuxer. + * + * When this method is called, it is possible that some dynamic plugging is + * going on in streaming threads. We decrement the dynamic counter and when it + * reaches zero, we check if all of our pads are blocked before we finally + * expose the group. * * MT safe */ static void gst_decode_group_set_complete (GstDecodeGroup * group) { - GST_LOG_OBJECT (group->dbin, "Setting group %p to COMPLETE", group); + gboolean expose = FALSE; + GstDecodeBin *dbin; + + dbin = group->dbin; + + GST_LOG_OBJECT (dbin, "Setting group %p to COMPLETE", group); GROUP_MUTEX_LOCK (group); group->complete = TRUE; - gst_decode_group_check_if_blocked (group); + if (group->nbdynamic > 0) + group->nbdynamic--; + expose = gst_decode_group_check_if_blocked (group); GROUP_MUTEX_UNLOCK (group); + + /* don't do anything if not blocked completely */ + if (expose) { + DECODE_BIN_LOCK (dbin); + if (!gst_decode_group_expose (group)) + GST_WARNING_OBJECT (dbin, "Couldn't expose group"); + DECODE_BIN_UNLOCK (dbin); + } } - - /************************* * GstDecodePad functions *************************/ static void -source_pad_blocked_cb (GstPad * pad, gboolean blocked, GstDecodePad * dpad) +gst_decode_pad_class_init (GstDecodePadClass * klass) +{ +} + +static void +gst_decode_pad_init (GstDecodePad * pad) { - GST_LOG_OBJECT (pad, "blocked:%d , dpad:%p, dpad->group:%p", - blocked, dpad, dpad->group); - + pad->group = NULL; + pad->blocked = FALSE; + pad->drained = FALSE; + gst_object_ref (pad); + gst_object_sink (pad); +} + +static void +source_pad_blocked_cb (GstDecodePad * dpad, gboolean blocked, gpointer unused) +{ + GstDecodeGroup *group; + GstDecodeBin *dbin; + gboolean expose = FALSE; + + group = dpad->group; + dbin = group->dbin; + + GST_LOG_OBJECT (dpad, "blocked:%d, dpad->group:%p", blocked, group); + + GROUP_MUTEX_LOCK (group); /* Update this GstDecodePad status */ dpad->blocked = blocked; - - if (blocked) { - GROUP_MUTEX_LOCK (dpad->group); - gst_decode_group_check_if_blocked (dpad->group); - GROUP_MUTEX_UNLOCK (dpad->group); + if (blocked) + expose = gst_decode_group_check_if_blocked (group); + GROUP_MUTEX_UNLOCK (group); + + if (expose) { + DECODE_BIN_LOCK (dbin); + if (!gst_decode_group_expose (group)) + GST_WARNING_OBJECT (dbin, "Couldn't expose group"); + DECODE_BIN_UNLOCK (dbin); } } @@ -2213,40 +2514,82 @@ GST_LOG_OBJECT (pad, "%s dpad:%p", GST_EVENT_TYPE_NAME (event), dpad); if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - /* Set our pad as drained */ - dpad->drained = TRUE; - GST_DEBUG_OBJECT (pad, "we received EOS"); /* Check if all pads are drained. If there is a next group to expose, we * will remove the ghostpad of the current group first, which unlinks the * peer and so drops the EOS. */ - gst_decode_group_check_if_drained (dpad->group); + gst_decode_pad_handle_eos (dpad); } /* never drop events */ return TRUE; } +static void +gst_decode_pad_set_blocked (GstDecodePad * dpad, gboolean blocked) +{ + GstDecodeBin *dbin = dpad->dbin; + + DECODE_BIN_DYN_LOCK (dbin); + gst_pad_set_blocked_async (GST_PAD (dpad), blocked, + (GstPadBlockCallback) source_pad_blocked_cb, NULL); + if (blocked) { + if (dbin->shutdown) { + /* deactivate to force flushing state to prevent NOT_LINKED errors */ + gst_pad_set_active (GST_PAD (dpad), FALSE); + } else { + gst_object_ref (dpad); + dbin->blocked_pads = g_list_prepend (dbin->blocked_pads, dpad); + } + } else { + if (g_list_find (dbin->blocked_pads, dpad)) + gst_object_unref (dpad); + dbin->blocked_pads = g_list_remove (dbin->blocked_pads, dpad); + } + DECODE_BIN_DYN_UNLOCK (dbin); +} + +static void +gst_decode_pad_add_drained_check (GstDecodePad * dpad) +{ + gst_pad_add_event_probe (GST_PAD (dpad), + G_CALLBACK (source_pad_event_probe), dpad); +} + +static void +gst_decode_pad_activate (GstDecodePad * dpad, GstDecodeGroup * group) +{ + g_return_if_fail (group != NULL); + + dpad->group = group; + gst_pad_set_active (GST_PAD (dpad), TRUE); + gst_decode_pad_set_blocked (dpad, TRUE); + gst_decode_pad_add_drained_check (dpad); +} + +static void +gst_decode_pad_unblock (GstDecodePad * dpad) +{ + gst_decode_pad_set_blocked (dpad, FALSE); +} + /*gst_decode_pad_new: * * Creates a new GstDecodePad for the given pad. - * If block is TRUE, Sets the pad blocking asynchronously */ static GstDecodePad * -gst_decode_pad_new (GstDecodeGroup * group, GstPad * pad, gboolean block) +gst_decode_pad_new (GstDecodeBin * dbin, GstPad * pad, GstDecodeGroup * group) { GstDecodePad *dpad; - dpad = g_new0 (GstDecodePad, 1); - dpad->pad = pad; + dpad = + g_object_new (GST_TYPE_DECODE_PAD, "direction", GST_PAD_DIRECTION (pad), + NULL); + gst_ghost_pad_construct (GST_GHOST_PAD (dpad)); + gst_ghost_pad_set_target (GST_GHOST_PAD (dpad), pad); dpad->group = group; - dpad->blocked = FALSE; - dpad->drained = TRUE; - - if (block) - gst_pad_set_blocked_async (pad, TRUE, - (GstPadBlockCallback) source_pad_blocked_cb, dpad); - gst_pad_add_event_probe (pad, G_CALLBACK (source_pad_event_probe), dpad); + dpad->dbin = dbin; + return dpad; } @@ -2255,73 +2598,28 @@ * Element add/remove *****/ -/* - * add_fakesink / remove_fakesink - * - * We use a sink so that the parent ::change_state returns GST_STATE_CHANGE_ASYNC - * when that sink is present (since it's not connected to anything it will - * always return GST_STATE_CHANGE_ASYNC). - * - * But this is an ugly way of achieving this goal. - * Ideally, we shouldn't use a sink and just return GST_STATE_CHANGE_ASYNC in - * our ::change_state if we have not exposed the active group. - * We also need to override ::get_state to fake the asynchronous behaviour. - * Once the active group is exposed, we would then post a - * GST_MESSAGE_STATE_DIRTY and return GST_STATE_CHANGE_SUCCESS (which will call - * ::get_state . - */ - -static gboolean -add_fakesink (GstDecodeBin * decode_bin) +static void +do_async_start (GstDecodeBin * dbin) { - GST_DEBUG_OBJECT (decode_bin, "Adding the fakesink"); - - if (decode_bin->fakesink) - return TRUE; - - decode_bin->fakesink = - gst_element_factory_make ("fakesink", "async-fakesink"); - if (!decode_bin->fakesink) - goto no_fakesink; - - /* enable sync so that we force ASYNC preroll */ - g_object_set (G_OBJECT (decode_bin->fakesink), "sync", TRUE, NULL); - - /* hacky, remove sink flag, we don't want our decodebin to become a sink - * just because we add a fakesink element to make us ASYNC */ - GST_OBJECT_FLAG_UNSET (decode_bin->fakesink, GST_ELEMENT_IS_SINK); - - if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->fakesink)) - goto could_not_add; - - return TRUE; - - /* ERRORS */ -no_fakesink: - { - g_warning ("can't find fakesink element, decodebin will not work"); - return FALSE; - } -could_not_add: - { - g_warning ("Could not add fakesink to decodebin, decodebin will not work"); - gst_object_unref (decode_bin->fakesink); - decode_bin->fakesink = NULL; - return FALSE; - } + GstMessage *message; + + dbin->async_pending = TRUE; + + message = gst_message_new_async_start (GST_OBJECT_CAST (dbin), FALSE); + parent_class->handle_message (GST_BIN_CAST (dbin), message); } static void -remove_fakesink (GstDecodeBin * decode_bin) +do_async_done (GstDecodeBin * dbin) { - if (decode_bin->fakesink == NULL) - return; - - GST_DEBUG_OBJECT (decode_bin, "Removing the fakesink"); - - gst_element_set_state (decode_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), decode_bin->fakesink); - decode_bin->fakesink = NULL; + GstMessage *message; + + if (dbin->async_pending) { + message = gst_message_new_async_done (GST_OBJECT_CAST (dbin)); + parent_class->handle_message (GST_BIN_CAST (dbin), message); + + dbin->async_pending = FALSE; + } } /***** @@ -2351,10 +2649,35 @@ return pad; } +/* call with dyn_lock held */ +static void +unblock_pads (GstDecodeBin * dbin) +{ + GList *tmp, *next; + + for (tmp = dbin->blocked_pads; tmp; tmp = next) { + GstDecodePad *dpad = (GstDecodePad *) tmp->data; + + next = g_list_next (tmp); + + GST_DEBUG_OBJECT (dpad, "unblocking"); + gst_pad_set_blocked_async (GST_PAD (dpad), FALSE, + (GstPadBlockCallback) source_pad_blocked_cb, NULL); + /* make flushing, prevent NOT_LINKED */ + GST_PAD_SET_FLUSHING (GST_PAD (dpad)); + gst_object_unref (dpad); + GST_DEBUG_OBJECT (dpad, "unblocked"); + } + + /* clear, no more blocked pads */ + g_list_free (dbin->blocked_pads); + dbin->blocked_pads = NULL; +} + static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, GstStateChange transition) { - GstStateChangeReturn ret; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstDecodeBin *dbin = GST_DECODE_BIN (element); switch (transition) { @@ -2362,19 +2685,47 @@ if (dbin->typefind == NULL) goto missing_typefind; break; - case GST_STATE_CHANGE_READY_TO_PAUSED:{ + case GST_STATE_CHANGE_READY_TO_PAUSED: + DECODE_BIN_DYN_LOCK (dbin); + GST_LOG_OBJECT (dbin, "clearing shutdown flag"); + dbin->shutdown = FALSE; + DECODE_BIN_DYN_UNLOCK (dbin); dbin->have_type = FALSE; - if (!add_fakesink (dbin)) - goto missing_fakesink; + ret = GST_STATE_CHANGE_ASYNC; + do_async_start (dbin); break; - } + case GST_STATE_CHANGE_PAUSED_TO_READY: + DECODE_BIN_DYN_LOCK (dbin); + GST_LOG_OBJECT (dbin, "setting shutdown flag"); + dbin->shutdown = TRUE; + unblock_pads (dbin); + DECODE_BIN_DYN_UNLOCK (dbin); default: break; } - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - /* FIXME : put some cleanup functions here.. if needed */ + { + GstStateChangeReturn bret; + + bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE)) + goto activate_failed; + else if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) { + do_async_done (dbin); + ret = bret; + } + } + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + do_async_done (dbin); + gst_decode_bin_remove_groups (dbin); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_decode_bin_remove_groups (dbin); + break; + default: + break; + } return ret; @@ -2386,16 +2737,19 @@ GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL), ("no typefind!")); return GST_STATE_CHANGE_FAILURE; } -missing_fakesink: +activate_failed: { - gst_element_post_message (element, - gst_missing_element_message_new (element, "fakesink")); - GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL), ("no fakesink!")); + GST_DEBUG_OBJECT (element, + "element failed to change states -- activation problem?"); return GST_STATE_CHANGE_FAILURE; } } - -static gboolean +#ifdef __SYMBIAN32__ +EXPORT_C +#endif + + +gboolean gst_decode_bin_plugin_init (GstPlugin * plugin) { GST_DEBUG_CATEGORY_INIT (gst_decode_bin_debug, "decodebin2", 0, @@ -2405,6 +2759,7 @@ GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, LOCALEDIR); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); #endif /* ENABLE_NLS */ return gst_element_register (plugin, "decodebin2", GST_RANK_NONE,