renatofilho@787: /* GStreamer renatofilho@787: * Copyright (C) <2006> Edward Hervey renatofilho@787: * renatofilho@787: * This library is free software; you can redistribute it and/or renatofilho@787: * modify it under the terms of the GNU Library General Public renatofilho@787: * License as published by the Free Software Foundation; either renatofilho@787: * version 2 of the License, or (at your option) any later version. renatofilho@787: * renatofilho@787: * This library is distributed in the hope that it will be useful, renatofilho@787: * but WITHOUT ANY WARRANTY; without even the implied warranty of renatofilho@787: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU renatofilho@787: * Library General Public License for more details. renatofilho@787: * renatofilho@787: * You should have received a copy of the GNU Library General Public renatofilho@787: * License along with this library; if not, write to the renatofilho@787: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, renatofilho@787: * Boston, MA 02111-1307, USA. renatofilho@787: */ renatofilho@787: renatofilho@787: /** renatofilho@787: * SECTION:element-decodebin2 renatofilho@787: * @short_description: Next-generation automatic decoding bin renatofilho@787: * renatofilho@787: * #GstBin that auto-magically constructs a decoding pipeline using available renatofilho@787: * decoders and demuxers via auto-plugging. renatofilho@787: * renatofilho@787: * At this stage, decodebin2 is considered UNSTABLE. The API provided in the renatofilho@787: * signals is expected to change in the near future. renatofilho@787: * renatofilho@787: * To try out decodebin2, you can set the USE_DECODEBIN2 environment renatofilho@787: * variable (USE_DECODEBIN2=1 for example). This will cause playbin to use renatofilho@787: * decodebin2 instead of the older decodebin for its internal auto-plugging. renatofilho@787: */ renatofilho@787: renatofilho@787: #ifdef HAVE_CONFIG_H renatofilho@787: #include "config.h" renatofilho@787: #endif renatofilho@787: renatofilho@787: #include renatofilho@787: #include renatofilho@787: renatofilho@787: #include "gstplay-marshal.h" renatofilho@787: renatofilho@787: /* generic templates */ renatofilho@787: static GstStaticPadTemplate decoder_bin_sink_template = renatofilho@787: GST_STATIC_PAD_TEMPLATE ("sink", renatofilho@787: GST_PAD_SINK, renatofilho@787: GST_PAD_ALWAYS, renatofilho@787: GST_STATIC_CAPS_ANY); renatofilho@787: renatofilho@787: static GstStaticPadTemplate decoder_bin_src_template = renatofilho@787: GST_STATIC_PAD_TEMPLATE ("src%d", renatofilho@787: GST_PAD_SRC, renatofilho@787: GST_PAD_SOMETIMES, renatofilho@787: GST_STATIC_CAPS_ANY); renatofilho@787: renatofilho@787: GST_DEBUG_CATEGORY_STATIC (gst_decode_bin_debug); renatofilho@787: #define GST_CAT_DEFAULT gst_decode_bin_debug renatofilho@787: renatofilho@787: typedef struct _GstDecodeGroup GstDecodeGroup; renatofilho@787: typedef struct _GstDecodePad GstDecodePad; renatofilho@787: typedef struct _GstDecodeBin GstDecodeBin; renatofilho@787: typedef struct _GstDecodeBin GstDecodeBin2; renatofilho@787: typedef struct _GstDecodeBinClass GstDecodeBinClass; renatofilho@787: renatofilho@787: #define GST_TYPE_DECODE_BIN (gst_decode_bin_get_type()) renatofilho@787: #define GST_DECODE_BIN_CAST(obj) ((GstDecodeBin*)(obj)) renatofilho@787: #define GST_DECODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_BIN,GstDecodeBin)) renatofilho@787: #define GST_DECODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DECODE_BIN,GstDecodeBinClass)) renatofilho@787: #define GST_IS_DECODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DECODE_BIN)) renatofilho@787: #define GST_IS_DECODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DECODE_BIN)) renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2: renatofilho@787: * renatofilho@787: * The opaque #DecodeBin2 data structure renatofilho@787: */ renatofilho@787: struct _GstDecodeBin renatofilho@787: { renatofilho@787: GstBin bin; /* we extend GstBin */ renatofilho@787: renatofilho@787: GstElement *typefind; /* this holds the typefind object */ renatofilho@787: GstElement *fakesink; renatofilho@787: renatofilho@787: GMutex *lock; /* Protects activegroup and groups */ renatofilho@787: GstDecodeGroup *activegroup; /* group currently active */ renatofilho@787: GList *groups; /* List of non-active GstDecodeGroups, sorted in renatofilho@787: * order of creation. */ renatofilho@787: GList *oldgroups; /* List of no-longer-used GstDecodeGroups. renatofilho@787: * Should be freed in dispose */ renatofilho@787: gint nbpads; /* unique identifier for source pads */ renatofilho@787: GstCaps *caps; /* caps on which to stop decoding */ renatofilho@787: renatofilho@787: GList *factories; /* factories we can use for selecting elements */ renatofilho@787: }; renatofilho@787: renatofilho@787: struct _GstDecodeBinClass renatofilho@787: { renatofilho@787: GstBinClass parent_class; renatofilho@787: renatofilho@787: /* signal we fire when a new pad has been decoded into raw audio/video */ renatofilho@787: void (*new_decoded_pad) (GstElement * element, GstPad * pad, gboolean last); renatofilho@787: /* signal we fire when a pad has been removed */ renatofilho@787: void (*removed_decoded_pad) (GstElement * element, GstPad * pad); renatofilho@787: /* signal fired when we found a pad that we cannot decode */ renatofilho@787: void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); renatofilho@787: /* signal fired to know if we continue trying to decode the given caps */ renatofilho@787: gboolean (*autoplug_continue) (GstElement * element, GstCaps * caps); renatofilho@787: /* signal fired to reorder the proposed list of factories */ renatofilho@787: gboolean (*autoplug_sort) (GstElement * element, GstCaps * caps, renatofilho@787: GList ** list); renatofilho@787: }; renatofilho@787: renatofilho@787: /* signals */ renatofilho@787: enum renatofilho@787: { renatofilho@787: SIGNAL_NEW_DECODED_PAD, renatofilho@787: SIGNAL_REMOVED_DECODED_PAD, renatofilho@787: SIGNAL_UNKNOWN_TYPE, renatofilho@787: SIGNAL_AUTOPLUG_CONTINUE, renatofilho@787: SIGNAL_AUTOPLUG_SORT, renatofilho@787: LAST_SIGNAL renatofilho@787: }; renatofilho@787: renatofilho@787: /* Properties */ renatofilho@787: enum renatofilho@787: { renatofilho@787: PROP_0, renatofilho@787: PROP_CAPS, renatofilho@787: }; renatofilho@787: renatofilho@787: static GstBinClass *parent_class; renatofilho@787: static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; renatofilho@787: renatofilho@787: static const GstElementDetails gst_decode_bin_details = renatofilho@787: GST_ELEMENT_DETAILS ("Decoder Bin", renatofilho@787: "Generic/Bin/Decoder", renatofilho@787: "Autoplug and decode to raw media", renatofilho@787: "Edward Hervey "); renatofilho@787: renatofilho@787: renatofilho@787: static gboolean add_fakesink (GstDecodeBin * decode_bin); renatofilho@787: static void remove_fakesink (GstDecodeBin * decode_bin); renatofilho@787: renatofilho@787: static void type_found (GstElement * typefind, guint probability, renatofilho@787: GstCaps * caps, GstDecodeBin * decode_bin); renatofilho@787: renatofilho@787: static gboolean gst_decode_bin_autoplug_continue (GstElement * element, renatofilho@787: GstCaps * caps); renatofilho@787: static gboolean gst_decode_bin_autoplug_sort (GstElement * element, renatofilho@787: GstCaps * caps, GList ** list); renatofilho@787: static void gst_decode_bin_set_property (GObject * object, guint prop_id, renatofilho@787: const GValue * value, GParamSpec * pspec); renatofilho@787: static void gst_decode_bin_get_property (GObject * object, guint prop_id, renatofilho@787: GValue * value, GParamSpec * pspec); renatofilho@787: static void gst_decode_bin_set_caps (GstDecodeBin * dbin, GstCaps * caps); renatofilho@787: static GstCaps *gst_decode_bin_get_caps (GstDecodeBin * dbin); renatofilho@787: renatofilho@787: static GstPad *find_sink_pad (GstElement * element); renatofilho@787: static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, renatofilho@787: GstStateChange transition); renatofilho@787: renatofilho@787: #define DECODE_BIN_LOCK(dbin) G_STMT_START { \ renatofilho@787: GST_LOG_OBJECT (dbin, \ renatofilho@787: "locking from thread %p", \ renatofilho@787: g_thread_self ()); \ renatofilho@787: g_mutex_lock (GST_DECODE_BIN_CAST(dbin)->lock); \ renatofilho@787: GST_LOG_OBJECT (dbin, \ renatofilho@787: "locked from thread %p", \ renatofilho@787: g_thread_self ()); \ renatofilho@787: } G_STMT_END renatofilho@787: renatofilho@787: #define DECODE_BIN_UNLOCK(dbin) G_STMT_START { \ renatofilho@787: GST_LOG_OBJECT (dbin, \ renatofilho@787: "unlocking from thread %p", \ renatofilho@787: g_thread_self ()); \ renatofilho@787: g_mutex_unlock (GST_DECODE_BIN_CAST(dbin)->lock); \ renatofilho@787: } G_STMT_END renatofilho@787: renatofilho@787: /* GstDecodeGroup renatofilho@787: * renatofilho@787: * Streams belonging to the same group/chain of a media file renatofilho@787: * renatofilho@787: */ renatofilho@787: renatofilho@787: struct _GstDecodeGroup renatofilho@787: { renatofilho@787: GstDecodeBin *dbin; renatofilho@787: GMutex *lock; renatofilho@787: GstElement *multiqueue; renatofilho@787: gboolean exposed; /* TRUE if this group is exposed */ renatofilho@787: gboolean drained; /* TRUE if EOS went throug all endpads */ renatofilho@787: gboolean blocked; /* TRUE if all endpads are blocked */ renatofilho@787: gboolean complete; /* TRUE if we are not expecting anymore streams renatofilho@787: * on this group */ renatofilho@787: gulong overrunsig; renatofilho@787: gulong underrunsig; renatofilho@787: guint nbdynamic; /* number of dynamic pads in the group. */ renatofilho@787: renatofilho@787: GList *endpads; /* List of GstDecodePad of source pads to be exposed */ renatofilho@787: GList *ghosts; /* List of GstGhostPad for the endpads */ renatofilho@787: }; renatofilho@787: renatofilho@787: #define GROUP_MUTEX_LOCK(group) G_STMT_START { \ renatofilho@787: GST_LOG_OBJECT (group->dbin, \ renatofilho@787: "locking group %p from thread %p", \ renatofilho@787: group, g_thread_self ()); \ renatofilho@787: g_mutex_lock (group->lock); \ renatofilho@787: GST_LOG_OBJECT (group->dbin, \ renatofilho@787: "locked group %p from thread %p", \ renatofilho@787: group, g_thread_self ()); \ renatofilho@787: } G_STMT_END renatofilho@787: renatofilho@787: #define GROUP_MUTEX_UNLOCK(group) G_STMT_START { \ renatofilho@787: GST_LOG_OBJECT (group->dbin, \ renatofilho@787: "unlocking group %p from thread %p", \ renatofilho@787: group, g_thread_self ()); \ renatofilho@787: g_mutex_unlock (group->lock); \ renatofilho@787: } G_STMT_END renatofilho@787: renatofilho@787: renatofilho@787: static GstDecodeGroup *gst_decode_group_new (GstDecodeBin * decode_bin); renatofilho@787: static GstPad *gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, renatofilho@787: GstPad * pad); renatofilho@787: static gboolean gst_decode_group_control_source_pad (GstDecodeGroup * group, renatofilho@787: GstPad * pad); renatofilho@787: static gboolean gst_decode_group_expose (GstDecodeGroup * group); renatofilho@787: static void gst_decode_group_check_if_blocked (GstDecodeGroup * group); renatofilho@787: static void gst_decode_group_set_complete (GstDecodeGroup * group); renatofilho@787: static void gst_decode_group_hide (GstDecodeGroup * group); renatofilho@787: static void gst_decode_group_free (GstDecodeGroup * group); renatofilho@787: renatofilho@787: /* GstDecodePad renatofilho@787: * renatofilho@787: * GstPad private used for source pads of groups renatofilho@787: */ renatofilho@787: renatofilho@787: struct _GstDecodePad renatofilho@787: { renatofilho@787: GstPad *pad; renatofilho@787: GstDecodeGroup *group; renatofilho@787: gboolean blocked; renatofilho@787: gboolean drained; renatofilho@787: }; renatofilho@787: renatofilho@787: static GstDecodePad *gst_decode_pad_new (GstDecodeGroup * group, GstPad * pad, renatofilho@787: gboolean block); renatofilho@787: static void source_pad_blocked_cb (GstPad * pad, gboolean blocked, renatofilho@787: GstDecodePad * dpad); renatofilho@787: renatofilho@787: /* TempPadStruct renatofilho@787: * Internal structure used for pads which have more than one structure. renatofilho@787: */ renatofilho@787: typedef struct _TempPadStruct renatofilho@787: { renatofilho@787: GstDecodeBin *dbin; renatofilho@787: GstDecodeGroup *group; renatofilho@787: } TempPadStruct; renatofilho@787: renatofilho@787: /******************************** renatofilho@787: * Standard GObject boilerplate * renatofilho@787: ********************************/ renatofilho@787: renatofilho@787: static void gst_decode_bin_class_init (GstDecodeBinClass * klass); renatofilho@787: static void gst_decode_bin_init (GstDecodeBin * decode_bin); renatofilho@787: static void gst_decode_bin_dispose (GObject * object); renatofilho@787: static void gst_decode_bin_finalize (GObject * object); renatofilho@787: renatofilho@787: static GType renatofilho@787: gst_decode_bin_get_type (void) renatofilho@787: { renatofilho@787: static GType gst_decode_bin_type = 0; renatofilho@787: renatofilho@787: if (!gst_decode_bin_type) { renatofilho@787: static const GTypeInfo gst_decode_bin_info = { renatofilho@787: sizeof (GstDecodeBinClass), renatofilho@787: NULL, renatofilho@787: NULL, renatofilho@787: (GClassInitFunc) gst_decode_bin_class_init, renatofilho@787: NULL, renatofilho@787: NULL, renatofilho@787: sizeof (GstDecodeBin), renatofilho@787: 0, renatofilho@787: (GInstanceInitFunc) gst_decode_bin_init, renatofilho@787: NULL renatofilho@787: }; renatofilho@787: renatofilho@787: gst_decode_bin_type = renatofilho@787: g_type_register_static (GST_TYPE_BIN, "GstDecodeBin2", renatofilho@787: &gst_decode_bin_info, 0); renatofilho@787: } renatofilho@787: renatofilho@787: return gst_decode_bin_type; renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: _gst_boolean_accumulator (GSignalInvocationHint * ihint, renatofilho@787: GValue * return_accu, const GValue * handler_return, gpointer dummy) renatofilho@787: { renatofilho@787: gboolean myboolean; renatofilho@787: renatofilho@787: myboolean = g_value_get_boolean (handler_return); renatofilho@787: if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) renatofilho@787: g_value_set_boolean (return_accu, myboolean); renatofilho@787: renatofilho@787: /* stop emission if FALSE */ renatofilho@787: return myboolean; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_class_init (GstDecodeBinClass * klass) renatofilho@787: { renatofilho@787: GObjectClass *gobject_klass; renatofilho@787: GstElementClass *gstelement_klass; renatofilho@787: GstBinClass *gstbin_klass; renatofilho@787: renatofilho@787: gobject_klass = (GObjectClass *) klass; renatofilho@787: gstelement_klass = (GstElementClass *) klass; renatofilho@787: gstbin_klass = (GstBinClass *) klass; renatofilho@787: renatofilho@787: parent_class = g_type_class_peek_parent (klass); renatofilho@787: renatofilho@787: gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose); renatofilho@787: gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_decode_bin_finalize); renatofilho@787: gobject_klass->set_property = GST_DEBUG_FUNCPTR (gst_decode_bin_set_property); renatofilho@787: gobject_klass->get_property = GST_DEBUG_FUNCPTR (gst_decode_bin_get_property); renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2::new-decoded-pad: renatofilho@787: * @pad: the newly created pad renatofilho@787: * @islast: #TRUE if this is the last pad to be added. Deprecated. renatofilho@787: * renatofilho@787: * This signal gets emitted as soon as a new pad of the same type as one of renatofilho@787: * the valid 'raw' types is added. renatofilho@787: */ renatofilho@787: renatofilho@787: gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] = renatofilho@787: g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass), renatofilho@787: G_SIGNAL_RUN_LAST, renatofilho@787: G_STRUCT_OFFSET (GstDecodeBinClass, new_decoded_pad), NULL, NULL, renatofilho@787: gst_play_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, GST_TYPE_PAD, renatofilho@787: G_TYPE_BOOLEAN); renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2::removed-decoded-pad: renatofilho@787: * @pad: the pad that was removed renatofilho@787: * renatofilho@787: * This signal is emitted when a 'final' caps pad has been removed. renatofilho@787: */ renatofilho@787: renatofilho@787: gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD] = renatofilho@787: g_signal_new ("removed-decoded-pad", G_TYPE_FROM_CLASS (klass), renatofilho@787: G_SIGNAL_RUN_LAST, renatofilho@787: G_STRUCT_OFFSET (GstDecodeBinClass, removed_decoded_pad), NULL, NULL, renatofilho@787: gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2::unknown-type: renatofilho@787: * @pad: the new pad containing caps that cannot be resolved to a 'final' stream type. renatofilho@787: * @caps: the #GstCaps of the pad that cannot be resolved. renatofilho@787: * renatofilho@787: * This signal is emitted when a pad for which there is no further possible renatofilho@787: * decoding is added to the decodebin. renatofilho@787: */ renatofilho@787: renatofilho@787: gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE] = renatofilho@787: g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), renatofilho@787: G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, unknown_type), renatofilho@787: NULL, NULL, gst_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, renatofilho@787: GST_TYPE_PAD, GST_TYPE_CAPS); renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2::autoplug-continue: renatofilho@787: * @caps: The #GstCaps found. renatofilho@787: * renatofilho@787: * This signal is emitted whenever decodebin2 finds a new stream. It is renatofilho@787: * emitted before looking for any elements that can handle that stream. renatofilho@787: * renatofilho@787: * Returns: #TRUE if you wish decodebin2 to look for elements that can renatofilho@787: * handle the given @caps. If #FALSE, those caps will be considered as renatofilho@787: * final and the pad will be exposed as such (see 'new-decoded-pad' renatofilho@787: * signal). renatofilho@787: */ renatofilho@787: renatofilho@787: gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] = renatofilho@787: g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass), renatofilho@787: G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_continue), renatofilho@787: _gst_boolean_accumulator, NULL, gst_play_marshal_BOOLEAN__OBJECT, renatofilho@787: G_TYPE_BOOLEAN, 1, GST_TYPE_CAPS); renatofilho@787: renatofilho@787: /** renatofilho@787: * GstDecodeBin2::autoplug-sort: renatofilho@787: * @caps: The #GstCaps. renatofilho@787: * @factories: A #GList of possible #GstElementFactory to use. renatofilho@787: * renatofilho@787: * This signal is emitted once decodebin2 has found all the possible renatofilho@787: * #GstElementFactory that can be used to handle the given @caps. renatofilho@787: * renatofilho@787: * UNSTABLE API. Will change soon. renatofilho@787: * renatofilho@787: * Returns: #TRUE if you wish decodebin2 to start trying to decode renatofilho@787: * the given @caps with the list of factories. #FALSE if you do not want renatofilho@787: * these #GstCaps, if so the pad will be exposed as unknown (see renatofilho@787: * 'unknown-type' signal). renatofilho@787: */ renatofilho@787: renatofilho@787: gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT] = renatofilho@787: g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass), renatofilho@787: G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, autoplug_sort), renatofilho@787: _gst_boolean_accumulator, NULL, gst_play_marshal_BOOLEAN__OBJECT_POINTER, renatofilho@787: G_TYPE_BOOLEAN, 2, GST_TYPE_CAPS, G_TYPE_POINTER); renatofilho@787: renatofilho@787: g_object_class_install_property (gobject_klass, PROP_CAPS, renatofilho@787: g_param_spec_boxed ("caps", "Caps", "The caps on which to stop decoding.", renatofilho@787: GST_TYPE_CAPS, G_PARAM_READWRITE)); renatofilho@787: renatofilho@787: klass->autoplug_continue = renatofilho@787: GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_continue); renatofilho@787: klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_decode_bin_autoplug_sort); renatofilho@787: renatofilho@787: gst_element_class_add_pad_template (gstelement_klass, renatofilho@787: gst_static_pad_template_get (&decoder_bin_sink_template)); renatofilho@787: gst_element_class_add_pad_template (gstelement_klass, renatofilho@787: gst_static_pad_template_get (&decoder_bin_src_template)); renatofilho@787: renatofilho@787: gst_element_class_set_details (gstelement_klass, &gst_decode_bin_details); renatofilho@787: renatofilho@787: gstelement_klass->change_state = renatofilho@787: GST_DEBUG_FUNCPTR (gst_decode_bin_change_state); renatofilho@787: } renatofilho@787: renatofilho@787: /* the filter function for selecting the elements we can use in renatofilho@787: * autoplugging */ renatofilho@787: static gboolean renatofilho@787: gst_decode_bin_factory_filter (GstPluginFeature * feature, renatofilho@787: GstDecodeBin * decode_bin) renatofilho@787: { renatofilho@787: guint rank; renatofilho@787: const gchar *klass; renatofilho@787: renatofilho@787: /* we only care about element factories */ renatofilho@787: if (!GST_IS_ELEMENT_FACTORY (feature)) renatofilho@787: return FALSE; renatofilho@787: renatofilho@787: klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); renatofilho@787: /* only demuxers, decoders and parsers can play */ renatofilho@787: if (strstr (klass, "Demux") == NULL && renatofilho@787: strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) { renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: /* only select elements with autoplugging rank */ renatofilho@787: rank = gst_plugin_feature_get_rank (feature); renatofilho@787: if (rank < GST_RANK_MARGINAL) renatofilho@787: return FALSE; renatofilho@787: renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: /* function used to sort element features */ renatofilho@787: static gint renatofilho@787: compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) renatofilho@787: { renatofilho@787: gint diff; renatofilho@787: const gchar *rname1, *rname2; renatofilho@787: renatofilho@787: diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); renatofilho@787: if (diff != 0) renatofilho@787: return diff; renatofilho@787: renatofilho@787: rname1 = gst_plugin_feature_get_name (f1); renatofilho@787: rname2 = gst_plugin_feature_get_name (f2); renatofilho@787: renatofilho@787: diff = strcmp (rname2, rname1); renatofilho@787: renatofilho@787: return diff; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: print_feature (GstPluginFeature * feature) renatofilho@787: { renatofilho@787: const gchar *rname; renatofilho@787: renatofilho@787: rname = gst_plugin_feature_get_name (feature); renatofilho@787: renatofilho@787: GST_DEBUG ("%s", rname); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_init (GstDecodeBin * decode_bin) renatofilho@787: { renatofilho@787: GList *factories; renatofilho@787: renatofilho@787: /* first filter out the interesting element factories */ renatofilho@787: factories = gst_default_registry_feature_filter ( renatofilho@787: (GstPluginFeatureFilter) gst_decode_bin_factory_filter, renatofilho@787: FALSE, decode_bin); renatofilho@787: renatofilho@787: /* sort them according to their ranks */ renatofilho@787: decode_bin->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); renatofilho@787: /* do some debugging */ renatofilho@787: g_list_foreach (decode_bin->factories, (GFunc) print_feature, NULL); renatofilho@787: renatofilho@787: renatofilho@787: /* we create the typefind element only once */ renatofilho@787: decode_bin->typefind = gst_element_factory_make ("typefind", "typefind"); renatofilho@787: if (!decode_bin->typefind) { renatofilho@787: g_warning ("can't find typefind element, decodebin will not work"); renatofilho@787: } else { renatofilho@787: GstPad *pad; renatofilho@787: GstPad *gpad; renatofilho@787: renatofilho@787: /* add the typefind element */ renatofilho@787: if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->typefind)) { renatofilho@787: g_warning ("Could not add typefind element, decodebin will not work"); renatofilho@787: gst_object_unref (decode_bin->typefind); renatofilho@787: decode_bin->typefind = NULL; renatofilho@787: } renatofilho@787: renatofilho@787: /* get the sinkpad */ renatofilho@787: pad = gst_element_get_pad (decode_bin->typefind, "sink"); renatofilho@787: renatofilho@787: /* ghost the sink pad to ourself */ renatofilho@787: gpad = gst_ghost_pad_new ("sink", pad); renatofilho@787: gst_pad_set_active (gpad, TRUE); renatofilho@787: gst_element_add_pad (GST_ELEMENT (decode_bin), gpad); renatofilho@787: renatofilho@787: gst_object_unref (pad); renatofilho@787: renatofilho@787: /* connect a signal to find out when the typefind element found renatofilho@787: * a type */ renatofilho@787: g_signal_connect (G_OBJECT (decode_bin->typefind), "have_type", renatofilho@787: G_CALLBACK (type_found), decode_bin); renatofilho@787: } renatofilho@787: renatofilho@787: decode_bin->lock = g_mutex_new (); renatofilho@787: decode_bin->activegroup = NULL; renatofilho@787: decode_bin->groups = NULL; renatofilho@787: renatofilho@787: decode_bin->caps = renatofilho@787: gst_caps_from_string ("video/x-raw-yuv;video/x-raw-rgb;video/x-raw-gray;" renatofilho@787: "audio/x-raw-int;audio/x-raw-float;" "text/plain;text/x-pango-markup"); renatofilho@787: renatofilho@787: add_fakesink (decode_bin); renatofilho@787: renatofilho@787: /* FILLME */ renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_dispose (GObject * object) renatofilho@787: { renatofilho@787: GstDecodeBin *decode_bin; renatofilho@787: GList *tmp; renatofilho@787: renatofilho@787: decode_bin = GST_DECODE_BIN (object); renatofilho@787: renatofilho@787: if (decode_bin->factories) renatofilho@787: gst_plugin_feature_list_free (decode_bin->factories); renatofilho@787: decode_bin->factories = NULL; renatofilho@787: renatofilho@787: if (decode_bin->activegroup) { renatofilho@787: gst_decode_group_free (decode_bin->activegroup); renatofilho@787: decode_bin->activegroup = NULL; renatofilho@787: } renatofilho@787: renatofilho@787: /* remove groups */ renatofilho@787: for (tmp = decode_bin->groups; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; renatofilho@787: renatofilho@787: gst_decode_group_free (group); renatofilho@787: } renatofilho@787: g_list_free (decode_bin->groups); renatofilho@787: decode_bin->groups = NULL; renatofilho@787: renatofilho@787: for (tmp = decode_bin->oldgroups; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodeGroup *group = (GstDecodeGroup *) tmp->data; renatofilho@787: renatofilho@787: gst_decode_group_free (group); renatofilho@787: } renatofilho@787: g_list_free (decode_bin->oldgroups); renatofilho@787: decode_bin->oldgroups = NULL; renatofilho@787: renatofilho@787: if (decode_bin->caps) renatofilho@787: gst_caps_unref (decode_bin->caps); renatofilho@787: decode_bin->caps = NULL; renatofilho@787: remove_fakesink (decode_bin); renatofilho@787: renatofilho@787: G_OBJECT_CLASS (parent_class)->dispose (object); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_finalize (GObject * object) renatofilho@787: { renatofilho@787: GstDecodeBin *decode_bin; renatofilho@787: renatofilho@787: decode_bin = GST_DECODE_BIN (object); renatofilho@787: renatofilho@787: if (decode_bin->lock) { renatofilho@787: g_mutex_free (decode_bin->lock); renatofilho@787: decode_bin->lock = NULL; renatofilho@787: } renatofilho@787: renatofilho@787: G_OBJECT_CLASS (parent_class)->finalize (object); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_set_property (GObject * object, guint prop_id, renatofilho@787: const GValue * value, GParamSpec * pspec) renatofilho@787: { renatofilho@787: GstDecodeBin *dbin; renatofilho@787: renatofilho@787: dbin = GST_DECODE_BIN (object); renatofilho@787: renatofilho@787: switch (prop_id) { renatofilho@787: case PROP_CAPS: renatofilho@787: gst_decode_bin_set_caps (dbin, (GstCaps *) g_value_dup_boxed (value)); renatofilho@787: break; renatofilho@787: default: renatofilho@787: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_get_property (GObject * object, guint prop_id, renatofilho@787: GValue * value, GParamSpec * pspec) renatofilho@787: { renatofilho@787: GstDecodeBin *dbin; renatofilho@787: renatofilho@787: dbin = GST_DECODE_BIN (object); renatofilho@787: switch (prop_id) { renatofilho@787: case PROP_CAPS:{ renatofilho@787: g_value_take_boxed (value, gst_decode_bin_get_caps (dbin)); renatofilho@787: break; renatofilho@787: } renatofilho@787: default: renatofilho@787: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); renatofilho@787: break; renatofilho@787: } renatofilho@787: renatofilho@787: } renatofilho@787: renatofilho@787: /* _set_caps renatofilho@787: * Changes the caps on which decodebin will stop decoding. renatofilho@787: * Will unref the previously set one. The refcount of the given caps will be renatofilho@787: * taken. renatofilho@787: * @caps can be NULL. renatofilho@787: * renatofilho@787: * MT-safe renatofilho@787: */ renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_bin_set_caps (GstDecodeBin * dbin, GstCaps * caps) renatofilho@787: { renatofilho@787: GST_DEBUG_OBJECT (dbin, "Setting new caps: %" GST_PTR_FORMAT, caps); renatofilho@787: renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: if (dbin->caps) renatofilho@787: gst_caps_unref (dbin->caps); renatofilho@787: dbin->caps = caps; renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: } renatofilho@787: renatofilho@787: /* _get_caps renatofilho@787: * Returns the currently configured caps on which decodebin will stop decoding. renatofilho@787: * The returned caps (if not NULL), will have its refcount incremented. renatofilho@787: * renatofilho@787: * MT-safe renatofilho@787: */ renatofilho@787: renatofilho@787: static GstCaps * renatofilho@787: gst_decode_bin_get_caps (GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GstCaps *caps; renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (dbin, "Getting currently set caps"); renatofilho@787: renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: caps = dbin->caps; renatofilho@787: if (caps) renatofilho@787: gst_caps_ref (caps); renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: renatofilho@787: return caps; renatofilho@787: } renatofilho@787: renatofilho@787: /***** renatofilho@787: * Default autoplug signal handlers renatofilho@787: *****/ renatofilho@787: renatofilho@787: static gboolean renatofilho@787: gst_decode_bin_autoplug_continue (GstElement * element, GstCaps * caps) renatofilho@787: { renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: gst_decode_bin_autoplug_sort (GstElement * element, GstCaps * caps, renatofilho@787: GList ** list) renatofilho@787: { renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: renatofilho@787: /******** renatofilho@787: * Discovery methods renatofilho@787: *****/ renatofilho@787: renatofilho@787: static gboolean are_raw_caps (GstDecodeBin * dbin, GstCaps * caps); renatofilho@787: static gboolean is_demuxer_element (GstElement * srcelement); renatofilho@787: static GList *find_compatibles (GstDecodeBin * decode_bin, renatofilho@787: const GstCaps * caps); renatofilho@787: renatofilho@787: static gboolean connect_pad (GstDecodeBin * dbin, GstElement * src, renatofilho@787: GstPad * pad, GList * factories, GstDecodeGroup * group); renatofilho@787: static gboolean connect_element (GstDecodeBin * dbin, GstElement * element, renatofilho@787: GstDecodeGroup * group); renatofilho@787: static void expose_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, renatofilho@787: GstDecodeGroup * group); renatofilho@787: renatofilho@787: static void pad_added_group_cb (GstElement * element, GstPad * pad, renatofilho@787: GstDecodeGroup * group); renatofilho@787: static void pad_removed_group_cb (GstElement * element, GstPad * pad, renatofilho@787: GstDecodeGroup * group); renatofilho@787: static void no_more_pads_group_cb (GstElement * element, renatofilho@787: GstDecodeGroup * group); renatofilho@787: static void pad_added_cb (GstElement * element, GstPad * pad, renatofilho@787: GstDecodeBin * dbin); renatofilho@787: static void pad_removed_cb (GstElement * element, GstPad * pad, renatofilho@787: GstDecodeBin * dbin); renatofilho@787: static void no_more_pads_cb (GstElement * element, GstDecodeBin * dbin); renatofilho@787: renatofilho@787: static GstDecodeGroup *get_current_group (GstDecodeBin * dbin); renatofilho@787: renatofilho@787: static void renatofilho@787: analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, renatofilho@787: GstCaps * caps, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: gboolean apcontinue = TRUE; renatofilho@787: GList *factories = NULL; renatofilho@787: gboolean apsort = TRUE; renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (dbin, "Pad %s:%s caps:%" GST_PTR_FORMAT, renatofilho@787: GST_DEBUG_PAD_NAME (pad), caps); renatofilho@787: renatofilho@787: if ((caps == NULL) || gst_caps_is_empty (caps)) renatofilho@787: goto unknown_type; renatofilho@787: renatofilho@787: if (gst_caps_is_any (caps)) renatofilho@787: goto any_caps; renatofilho@787: renatofilho@787: /* 1. Emit 'autoplug-continue' */ renatofilho@787: g_signal_emit (G_OBJECT (dbin), renatofilho@787: gst_decode_bin_signals[SIGNAL_AUTOPLUG_CONTINUE], 0, caps, &apcontinue); renatofilho@787: renatofilho@787: /* 1.a if autoplug-continue is FALSE or caps is a raw format, goto pad_is_final */ renatofilho@787: if ((!apcontinue) || are_raw_caps (dbin, caps)) renatofilho@787: goto expose_pad; renatofilho@787: renatofilho@787: /* 1.b else if there's no compatible factory or 'autoplug-sort' returned FALSE, goto pad_not_used */ renatofilho@787: if ((factories = find_compatibles (dbin, caps))) { renatofilho@787: /* emit autoplug-sort */ renatofilho@787: g_signal_emit (G_OBJECT (dbin), renatofilho@787: gst_decode_bin_signals[SIGNAL_AUTOPLUG_SORT], renatofilho@787: 0, caps, &factories, &apsort); renatofilho@787: if (!apsort) { renatofilho@787: g_list_free (factories); renatofilho@787: /* User doesn't want that pad */ renatofilho@787: goto pad_not_wanted; renatofilho@787: } renatofilho@787: } else { renatofilho@787: /* no compatible factories */ renatofilho@787: goto unknown_type; renatofilho@787: } renatofilho@787: renatofilho@787: /* 1.c else goto pad_is_valid */ renatofilho@787: GST_LOG_OBJECT (pad, "Let's continue discovery on this pad"); renatofilho@787: renatofilho@787: connect_pad (dbin, src, pad, factories, group); renatofilho@787: g_list_free (factories); renatofilho@787: renatofilho@787: return; renatofilho@787: renatofilho@787: expose_pad: renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (dbin, "Pad is final. autoplug-continue:%d", apcontinue); renatofilho@787: expose_pad (dbin, src, pad, group); renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: pad_not_wanted: renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "User doesn't want this pad, stopping discovery"); renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: unknown_type: renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "Unknown type, firing signal"); renatofilho@787: g_signal_emit (G_OBJECT (dbin), renatofilho@787: gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); renatofilho@787: renatofilho@787: /* Check if there are no pending groups, if so, remove fakesink */ renatofilho@787: if (dbin->groups == NULL) renatofilho@787: remove_fakesink (dbin); renatofilho@787: renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: any_caps: renatofilho@787: { renatofilho@787: GST_WARNING_OBJECT (pad, renatofilho@787: "pad has ANY caps, not able to autoplug to anything"); renatofilho@787: /* FIXME : connect to caps notification */ renatofilho@787: return; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: /* connect_pad: renatofilho@787: * renatofilho@787: * Try to connect the given pad to an element created from one of the factories, renatofilho@787: * and recursively. renatofilho@787: * renatofilho@787: * Returns TRUE if an element was properly created and linked renatofilho@787: */ renatofilho@787: renatofilho@787: static gboolean renatofilho@787: connect_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, renatofilho@787: GList * factories, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: gboolean res = FALSE; renatofilho@787: GList *tmp; renatofilho@787: renatofilho@787: g_return_val_if_fail (factories != NULL, FALSE); renatofilho@787: GST_DEBUG_OBJECT (dbin, "pad %s:%s , group:%p", renatofilho@787: GST_DEBUG_PAD_NAME (pad), group); renatofilho@787: renatofilho@787: /* 1. is element demuxer or parser */ renatofilho@787: if (is_demuxer_element (src)) { renatofilho@787: GstPad *mqpad; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (src, "is a demuxer, connecting the pad through multiqueue"); renatofilho@787: renatofilho@787: if (!group) renatofilho@787: if (!(group = get_current_group (dbin))) { renatofilho@787: group = gst_decode_group_new (dbin); renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: dbin->groups = g_list_append (dbin->groups, group); renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: } renatofilho@787: renatofilho@787: if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad))) renatofilho@787: goto beach; renatofilho@787: pad = mqpad; renatofilho@787: } renatofilho@787: renatofilho@787: /* 2. Try to create an element and link to it */ renatofilho@787: for (tmp = factories; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstElementFactory *factory = (GstElementFactory *) tmp->data; renatofilho@787: GstElement *element; renatofilho@787: GstPad *sinkpad; renatofilho@787: renatofilho@787: /* 2.1. Try to create an element */ renatofilho@787: if ((element = gst_element_factory_create (factory, NULL)) == NULL) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Could not create an element from %s", renatofilho@787: gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: /* 2.3. Find its sink pad */ renatofilho@787: if (!(sinkpad = find_sink_pad (element))) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Element %s doesn't have a sink pad", renatofilho@787: GST_ELEMENT_NAME (element)); renatofilho@787: gst_object_unref (element); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: /* 2.4 add it ... */ renatofilho@787: if (!(gst_bin_add (GST_BIN (dbin), element))) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Couldn't add %s to the bin", renatofilho@787: GST_ELEMENT_NAME (element)); renatofilho@787: gst_object_unref (sinkpad); renatofilho@787: gst_object_unref (element); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: /* ... activate it ... */ renatofilho@787: if ((gst_element_set_state (element, renatofilho@787: GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Couldn't set %s to READY", renatofilho@787: GST_ELEMENT_NAME (element)); renatofilho@787: gst_object_unref (sinkpad); renatofilho@787: gst_bin_remove (GST_BIN (dbin), element); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: /* 2.5 ...and try to link */ renatofilho@787: if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Link failed on pad %s:%s", renatofilho@787: GST_DEBUG_PAD_NAME (sinkpad)); renatofilho@787: gst_element_set_state (element, GST_STATE_NULL); renatofilho@787: gst_object_unref (sinkpad); renatofilho@787: gst_bin_remove (GST_BIN (dbin), element); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "linked on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); renatofilho@787: renatofilho@787: /* link this element further */ renatofilho@787: connect_element (dbin, element, group); renatofilho@787: renatofilho@787: /* Bring the element to the state of the parent */ renatofilho@787: if ((gst_element_set_state (element, renatofilho@787: GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) { renatofilho@787: GST_WARNING_OBJECT (dbin, "Couldn't set %s to PAUSED", renatofilho@787: GST_ELEMENT_NAME (element)); renatofilho@787: gst_element_set_state (element, GST_STATE_NULL); renatofilho@787: gst_object_unref (sinkpad); renatofilho@787: gst_bin_remove (GST_BIN (dbin), element); renatofilho@787: continue; renatofilho@787: } renatofilho@787: renatofilho@787: res = TRUE; renatofilho@787: break; renatofilho@787: } renatofilho@787: renatofilho@787: beach: renatofilho@787: return res; renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: connect_element (GstDecodeBin * dbin, GstElement * element, renatofilho@787: GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *pads; renatofilho@787: gboolean res = TRUE; renatofilho@787: gboolean dynamic = FALSE; renatofilho@787: GList *to_connect = NULL; renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (dbin, "Attempting to connect element %s [group:%p] further", renatofilho@787: GST_ELEMENT_NAME (element), group); renatofilho@787: renatofilho@787: /* 1. Loop over pad templates, grabbing existing pads along the way */ renatofilho@787: for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads; renatofilho@787: pads = g_list_next (pads)) { renatofilho@787: GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data); renatofilho@787: const gchar *templ_name; renatofilho@787: renatofilho@787: /* we are only interested in source pads */ renatofilho@787: if (GST_PAD_TEMPLATE_DIRECTION (templ) != GST_PAD_SRC) renatofilho@787: continue; renatofilho@787: renatofilho@787: templ_name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ); renatofilho@787: GST_DEBUG_OBJECT (dbin, "got a source pad template %s", templ_name); renatofilho@787: renatofilho@787: /* figure out what kind of pad this is */ renatofilho@787: switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { renatofilho@787: case GST_PAD_ALWAYS: renatofilho@787: { renatofilho@787: /* get the pad that we need to autoplug */ renatofilho@787: GstPad *pad = gst_element_get_pad (element, templ_name); renatofilho@787: renatofilho@787: if (pad) { renatofilho@787: GST_DEBUG_OBJECT (dbin, "got the pad for always template %s", renatofilho@787: templ_name); renatofilho@787: /* here is the pad, we need to autoplug it */ renatofilho@787: to_connect = g_list_prepend (to_connect, pad); renatofilho@787: } else { renatofilho@787: /* strange, pad is marked as always but it's not renatofilho@787: * there. Fix the element */ renatofilho@787: GST_WARNING_OBJECT (dbin, renatofilho@787: "could not get the pad for always template %s", templ_name); renatofilho@787: } renatofilho@787: break; renatofilho@787: } renatofilho@787: case GST_PAD_SOMETIMES: renatofilho@787: { renatofilho@787: /* try to get the pad to see if it is already created or renatofilho@787: * not */ renatofilho@787: GstPad *pad = gst_element_get_pad (element, templ_name); renatofilho@787: renatofilho@787: if (pad) { renatofilho@787: GST_DEBUG_OBJECT (dbin, "got the pad for sometimes template %s", renatofilho@787: templ_name); renatofilho@787: /* the pad is created, we need to autoplug it */ renatofilho@787: to_connect = g_list_prepend (to_connect, pad); renatofilho@787: } else { renatofilho@787: GST_DEBUG_OBJECT (dbin, renatofilho@787: "did not get the sometimes pad of template %s", templ_name); renatofilho@787: /* we have an element that will create dynamic pads */ renatofilho@787: dynamic = TRUE; renatofilho@787: } renatofilho@787: break; renatofilho@787: } renatofilho@787: case GST_PAD_REQUEST: renatofilho@787: /* ignore request pads */ renatofilho@787: GST_DEBUG_OBJECT (dbin, "ignoring request padtemplate %s", templ_name); renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: /* 2. if there are more potential pads, connect to relevent signals */ renatofilho@787: if (dynamic) { renatofilho@787: if (group) { renatofilho@787: GST_LOG ("Adding signals to element %s in group %p", renatofilho@787: GST_ELEMENT_NAME (element), group); renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: group->nbdynamic++; renatofilho@787: GST_LOG ("Group %p has now %d dynamic elements", group, group->nbdynamic); renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: g_signal_connect (G_OBJECT (element), "pad-added", renatofilho@787: G_CALLBACK (pad_added_group_cb), group); renatofilho@787: g_signal_connect (G_OBJECT (element), "pad-removed", renatofilho@787: G_CALLBACK (pad_removed_group_cb), group); renatofilho@787: g_signal_connect (G_OBJECT (element), "no-more-pads", renatofilho@787: G_CALLBACK (no_more_pads_group_cb), group); renatofilho@787: } else { renatofilho@787: /* This is a non-grouped element, the handlers are different */ renatofilho@787: g_signal_connect (G_OBJECT (element), "pad-added", renatofilho@787: G_CALLBACK (pad_added_cb), dbin); renatofilho@787: g_signal_connect (G_OBJECT (element), "pad-removed", renatofilho@787: G_CALLBACK (pad_removed_cb), dbin); renatofilho@787: g_signal_connect (G_OBJECT (element), "no-more-pads", renatofilho@787: G_CALLBACK (no_more_pads_cb), dbin); renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: /* 3. for every available pad, connect it */ renatofilho@787: for (pads = to_connect; pads; pads = g_list_next (pads)) { renatofilho@787: GstPad *pad = GST_PAD_CAST (pads->data); renatofilho@787: GstCaps *caps; renatofilho@787: renatofilho@787: caps = gst_pad_get_caps (pad); renatofilho@787: analyze_new_pad (dbin, element, pad, caps, group); renatofilho@787: if (caps) renatofilho@787: gst_caps_unref (caps); renatofilho@787: renatofilho@787: gst_object_unref (pad); renatofilho@787: } renatofilho@787: g_list_free (to_connect); renatofilho@787: renatofilho@787: return res; renatofilho@787: } renatofilho@787: renatofilho@787: /* expose_pad: renatofilho@787: * renatofilho@787: * Expose the given pad on the group as a decoded pad. renatofilho@787: * If group is NULL, a GstDecodeGroup will be created and setup properly. renatofilho@787: */ renatofilho@787: static void renatofilho@787: expose_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad, renatofilho@787: GstDecodeGroup * group) renatofilho@787: { renatofilho@787: gboolean newgroup = FALSE; renatofilho@787: gboolean isdemux; renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (dbin, "pad %s:%s, group:%p", renatofilho@787: GST_DEBUG_PAD_NAME (pad), group); renatofilho@787: renatofilho@787: if (!group) renatofilho@787: if (!(group = get_current_group (dbin))) { renatofilho@787: group = gst_decode_group_new (dbin); renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: dbin->groups = g_list_append (dbin->groups, group); renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: newgroup = TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: isdemux = is_demuxer_element (src); renatofilho@787: renatofilho@787: if (isdemux || newgroup) { renatofilho@787: GstPad *mqpad; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (src, "is a demuxer, connecting the pad through multiqueue"); renatofilho@787: renatofilho@787: if (!(mqpad = gst_decode_group_control_demuxer_pad (group, pad))) renatofilho@787: goto beach; renatofilho@787: pad = mqpad; renatofilho@787: } renatofilho@787: renatofilho@787: gst_decode_group_control_source_pad (group, pad); renatofilho@787: renatofilho@787: if (newgroup && !isdemux) { renatofilho@787: /* If we have discovered a raw pad and it doesn't belong to any group, renatofilho@787: * that means there wasn't any demuxer. In that case, we consider the renatofilho@787: * group as being complete. */ renatofilho@787: gst_decode_group_set_complete (group); renatofilho@787: } renatofilho@787: beach: renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: type_found (GstElement * typefind, guint probability, renatofilho@787: GstCaps * caps, GstDecodeBin * decode_bin) renatofilho@787: { renatofilho@787: GstPad *pad; renatofilho@787: renatofilho@787: GST_STATE_LOCK (decode_bin); renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); renatofilho@787: renatofilho@787: pad = gst_element_get_pad (typefind, "src"); renatofilho@787: renatofilho@787: analyze_new_pad (decode_bin, typefind, pad, caps, NULL); renatofilho@787: renatofilho@787: gst_object_unref (pad); renatofilho@787: renatofilho@787: GST_STATE_UNLOCK (decode_bin); renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: pad_added_group_cb (GstElement * element, GstPad * pad, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GstCaps *caps; renatofilho@787: gboolean expose = FALSE; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (pad, "pad added, group:%p", group); renatofilho@787: renatofilho@787: caps = gst_pad_get_caps (pad); renatofilho@787: analyze_new_pad (group->dbin, element, pad, caps, group); renatofilho@787: if (caps) renatofilho@787: gst_caps_unref (caps); renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: group->nbdynamic--; renatofilho@787: GST_LOG ("Group %p has now %d dynamic objects", group, group->nbdynamic); renatofilho@787: if (group->nbdynamic == 0) renatofilho@787: expose = TRUE; renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: if (expose) { renatofilho@787: GST_LOG renatofilho@787: ("That was the last dynamic object, now attempting to expose the group"); renatofilho@787: DECODE_BIN_LOCK (group->dbin); renatofilho@787: gst_decode_group_expose (group); renatofilho@787: DECODE_BIN_UNLOCK (group->dbin); renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: pad_removed_group_cb (GstElement * element, GstPad * pad, renatofilho@787: GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "pad removed, group:%p", group); renatofilho@787: renatofilho@787: /* In fact, we don't have to do anything here, the active group will be renatofilho@787: * removed when the group's multiqueue is drained */ renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: no_more_pads_group_cb (GstElement * element, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (element, "no more pads, setting group %p to complete", group); renatofilho@787: renatofilho@787: /* FIXME : FILLME */ renatofilho@787: gst_decode_group_set_complete (group); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: pad_added_cb (GstElement * element, GstPad * pad, GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GstCaps *caps; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (pad, "Pad added to non-grouped element"); renatofilho@787: renatofilho@787: caps = gst_pad_get_caps (pad); renatofilho@787: analyze_new_pad (dbin, element, pad, caps, NULL); renatofilho@787: if (caps) renatofilho@787: gst_caps_unref (caps); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: pad_removed_cb (GstElement * element, GstPad * pad, GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "Pad removed from non-grouped element"); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: no_more_pads_cb (GstElement * element, GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GstDecodeGroup *group; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (element, "No more pads, setting current group to complete"); renatofilho@787: renatofilho@787: /* Find the non-complete group, there should only be one */ renatofilho@787: if (!(group = get_current_group (dbin))) renatofilho@787: goto no_group; renatofilho@787: renatofilho@787: gst_decode_group_set_complete (group); renatofilho@787: return; renatofilho@787: renatofilho@787: no_group: renatofilho@787: { renatofilho@787: GST_WARNING_OBJECT (dbin, "We couldn't find a non-completed group !!"); renatofilho@787: return; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: /* this function runs through the element factories and returns a list renatofilho@787: * of all elements that are able to sink the given caps renatofilho@787: */ renatofilho@787: static GList * renatofilho@787: find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) renatofilho@787: { renatofilho@787: GList *factories; renatofilho@787: GList *to_try = NULL; renatofilho@787: renatofilho@787: /* loop over all the factories */ renatofilho@787: for (factories = decode_bin->factories; factories; renatofilho@787: factories = g_list_next (factories)) { renatofilho@787: GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data); renatofilho@787: const GList *templates; renatofilho@787: GList *walk; renatofilho@787: renatofilho@787: /* get the templates from the element factory */ renatofilho@787: templates = gst_element_factory_get_static_pad_templates (factory); renatofilho@787: for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { renatofilho@787: GstStaticPadTemplate *templ = walk->data; renatofilho@787: renatofilho@787: /* we only care about the sink templates */ renatofilho@787: if (templ->direction == GST_PAD_SINK) { renatofilho@787: GstCaps *intersect; renatofilho@787: GstCaps *tmpl_caps; renatofilho@787: renatofilho@787: /* try to intersect the caps with the caps of the template */ renatofilho@787: tmpl_caps = gst_static_caps_get (&templ->static_caps); renatofilho@787: renatofilho@787: intersect = gst_caps_intersect (caps, tmpl_caps); renatofilho@787: gst_caps_unref (tmpl_caps); renatofilho@787: renatofilho@787: /* check if the intersection is empty */ renatofilho@787: if (!gst_caps_is_empty (intersect)) { renatofilho@787: /* non empty intersection, we can use this element */ renatofilho@787: to_try = g_list_prepend (to_try, factory); renatofilho@787: gst_caps_unref (intersect); renatofilho@787: break; renatofilho@787: } renatofilho@787: gst_caps_unref (intersect); renatofilho@787: } renatofilho@787: } renatofilho@787: } renatofilho@787: to_try = g_list_reverse (to_try); renatofilho@787: renatofilho@787: return to_try; renatofilho@787: } renatofilho@787: renatofilho@787: /* Decide whether an element is a demuxer based on the renatofilho@787: * klass and number/type of src pad templates it has */ renatofilho@787: static gboolean renatofilho@787: is_demuxer_element (GstElement * srcelement) renatofilho@787: { renatofilho@787: GstElementFactory *srcfactory; renatofilho@787: GstElementClass *elemclass; renatofilho@787: GList *templates, *walk; renatofilho@787: const gchar *klass; renatofilho@787: gint potential_src_pads = 0; renatofilho@787: renatofilho@787: srcfactory = gst_element_get_factory (srcelement); renatofilho@787: klass = gst_element_factory_get_klass (srcfactory); renatofilho@787: renatofilho@787: /* Can't be a demuxer unless it has Demux in the klass name */ renatofilho@787: if (!strstr (klass, "Demux")) renatofilho@787: return FALSE; renatofilho@787: renatofilho@787: /* Walk the src pad templates and count how many the element renatofilho@787: * might produce */ renatofilho@787: elemclass = GST_ELEMENT_GET_CLASS (srcelement); renatofilho@787: renatofilho@787: walk = templates = gst_element_class_get_pad_template_list (elemclass); renatofilho@787: while (walk != NULL) { renatofilho@787: GstPadTemplate *templ; renatofilho@787: renatofilho@787: templ = (GstPadTemplate *) walk->data; renatofilho@787: if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) { renatofilho@787: switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { renatofilho@787: case GST_PAD_ALWAYS: renatofilho@787: case GST_PAD_SOMETIMES: renatofilho@787: if (strstr (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), "%")) renatofilho@787: potential_src_pads += 2; /* Might make multiple pads */ renatofilho@787: else renatofilho@787: potential_src_pads += 1; renatofilho@787: break; renatofilho@787: case GST_PAD_REQUEST: renatofilho@787: potential_src_pads += 2; renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: walk = g_list_next (walk); renatofilho@787: } renatofilho@787: renatofilho@787: if (potential_src_pads < 2) renatofilho@787: return FALSE; renatofilho@787: renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: /* Returns TRUE if the caps are raw, or if they are compatible with the caps renatofilho@787: * specified in the 'caps' property renatofilho@787: * renatofilho@787: * The decodebin_lock should be taken ! renatofilho@787: */ renatofilho@787: static gboolean renatofilho@787: are_raw_caps (GstDecodeBin * dbin, GstCaps * caps) renatofilho@787: { renatofilho@787: GstCaps *intersection; renatofilho@787: gboolean res; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "Checking with caps %" GST_PTR_FORMAT, caps); renatofilho@787: renatofilho@787: intersection = gst_caps_intersect (dbin->caps, caps); renatofilho@787: renatofilho@787: res = (!(gst_caps_is_empty (intersection))); renatofilho@787: renatofilho@787: gst_caps_unref (intersection); renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "Caps are %sfinal caps", res ? "" : "not "); renatofilho@787: renatofilho@787: return res; renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: /**** renatofilho@787: * GstDecodeGroup functions renatofilho@787: ****/ renatofilho@787: renatofilho@787: static void renatofilho@787: multi_queue_overrun_cb (GstElement * queue, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (group->dbin, "multiqueue is full"); renatofilho@787: renatofilho@787: /* if we haven't exposed the group, do it */ renatofilho@787: DECODE_BIN_LOCK (group->dbin); renatofilho@787: gst_decode_group_expose (group); renatofilho@787: DECODE_BIN_UNLOCK (group->dbin); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: multi_queue_underrun_cb (GstElement * queue, GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GstDecodeBin *dbin = group->dbin; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "multiqueue is empty for group %p", group); renatofilho@787: renatofilho@787: /* Check if we need to activate another group */ renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: if ((group == dbin->activegroup) && dbin->groups) { renatofilho@787: GST_DEBUG_OBJECT (dbin, "Switching to new group"); renatofilho@787: /* unexpose current active */ renatofilho@787: gst_decode_group_hide (group); renatofilho@787: renatofilho@787: /* expose first group of groups */ renatofilho@787: gst_decode_group_expose ((GstDecodeGroup *) dbin->groups->data); renatofilho@787: } renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: } renatofilho@787: renatofilho@787: /* gst_decode_group_new renatofilho@787: * renatofilho@787: * Creates a new GstDecodeGroup. It is up to the caller to add it to the list renatofilho@787: * of groups. renatofilho@787: */ renatofilho@787: static GstDecodeGroup * renatofilho@787: gst_decode_group_new (GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GstDecodeGroup *group; renatofilho@787: GstElement *mq; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "Creating new group"); renatofilho@787: renatofilho@787: if (!(mq = gst_element_factory_make ("multiqueue", NULL))) { renatofilho@787: GST_WARNING ("Couldn't create multiqueue element"); renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: renatofilho@787: g_object_set (G_OBJECT (mq), renatofilho@787: "max-size-bytes", 2 * 1024 * 1024, renatofilho@787: "max-size-time", 5 * GST_SECOND, "max-size-buffers", 0, NULL); renatofilho@787: renatofilho@787: group = g_new0 (GstDecodeGroup, 1); renatofilho@787: group->lock = g_mutex_new (); renatofilho@787: group->dbin = dbin; renatofilho@787: group->multiqueue = mq; renatofilho@787: group->exposed = FALSE; renatofilho@787: group->drained = FALSE; renatofilho@787: group->blocked = FALSE; renatofilho@787: group->complete = FALSE; renatofilho@787: group->endpads = NULL; renatofilho@787: renatofilho@787: group->overrunsig = g_signal_connect (G_OBJECT (mq), "overrun", renatofilho@787: G_CALLBACK (multi_queue_overrun_cb), group); renatofilho@787: group->underrunsig = g_signal_connect (G_OBJECT (mq), "underrun", renatofilho@787: G_CALLBACK (multi_queue_underrun_cb), group); renatofilho@787: renatofilho@787: gst_bin_add (GST_BIN (dbin), group->multiqueue); renatofilho@787: gst_element_set_state (group->multiqueue, GST_STATE_PAUSED); renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "Returning new group %p", group); renatofilho@787: renatofilho@787: return group; renatofilho@787: } renatofilho@787: renatofilho@787: /** get_current_group: renatofilho@787: * renatofilho@787: * Returns the current non-completed group. renatofilho@787: * renatofilho@787: * Returns NULL if no groups are available, or all groups are completed. renatofilho@787: */ renatofilho@787: static GstDecodeGroup * renatofilho@787: get_current_group (GstDecodeBin * dbin) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: GstDecodeGroup *group = NULL; renatofilho@787: renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: for (tmp = dbin->groups; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodeGroup *this = (GstDecodeGroup *) tmp->data; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "group %p, complete:%d", this, this->complete); renatofilho@787: renatofilho@787: if (!this->complete) { renatofilho@787: group = this; renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: renatofilho@787: GST_LOG_OBJECT (dbin, "Returning group %p", group); renatofilho@787: renatofilho@787: return group; renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: group_demuxer_event_probe (GstPad * pad, GstEvent * event, renatofilho@787: GstDecodeGroup * group) renatofilho@787: { renatofilho@787: if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { renatofilho@787: GST_DEBUG_OBJECT (group->dbin, renatofilho@787: "Got EOS on group input pads, exposing group if it wasn't before"); renatofilho@787: DECODE_BIN_LOCK (group->dbin); renatofilho@787: gst_decode_group_expose (group); renatofilho@787: DECODE_BIN_UNLOCK (group->dbin); renatofilho@787: } renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: /* gst_decode_group_control_demuxer_pad renatofilho@787: * renatofilho@787: * Adds a new demuxer srcpad to the given group. renatofilho@787: * renatofilho@787: * Returns the srcpad of the multiqueue corresponding the given pad. renatofilho@787: * Returns NULL if there was an error. renatofilho@787: */ renatofilho@787: static GstPad * renatofilho@787: gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad) renatofilho@787: { renatofilho@787: GstPad *srcpad, *sinkpad; renatofilho@787: gchar *nb, *sinkname, *srcname; renatofilho@787: renatofilho@787: GST_LOG ("group:%p pad %s:%s", group, GST_DEBUG_PAD_NAME (pad)); renatofilho@787: renatofilho@787: srcpad = NULL; renatofilho@787: renatofilho@787: if (!(sinkpad = gst_element_get_pad (group->multiqueue, "sink%d"))) { renatofilho@787: GST_ERROR ("Couldn't get sinkpad from multiqueue"); renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: renatofilho@787: if ((gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) { renatofilho@787: GST_ERROR ("Couldn't link demuxer and multiqueue"); renatofilho@787: goto beach; renatofilho@787: } renatofilho@787: renatofilho@787: sinkname = gst_pad_get_name (sinkpad); renatofilho@787: nb = sinkname + 4; renatofilho@787: srcname = g_strdup_printf ("src%s", nb); renatofilho@787: g_free (sinkname); renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: renatofilho@787: if (!(srcpad = gst_element_get_pad (group->multiqueue, srcname))) { renatofilho@787: GST_ERROR ("Couldn't get srcpad %s from multiqueue", srcname); renatofilho@787: goto chiringuito; renatofilho@787: } renatofilho@787: renatofilho@787: /* connect event handler on pad to intercept EOS events */ renatofilho@787: gst_pad_add_event_probe (pad, G_CALLBACK (group_demuxer_event_probe), group); renatofilho@787: renatofilho@787: chiringuito: renatofilho@787: g_free (srcname); renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: renatofilho@787: beach: renatofilho@787: gst_object_unref (sinkpad); renatofilho@787: return srcpad; renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: gst_decode_group_control_source_pad (GstDecodeGroup * group, GstPad * pad) renatofilho@787: { renatofilho@787: GstDecodePad *dpad; renatofilho@787: renatofilho@787: g_return_val_if_fail (group != NULL, FALSE); renatofilho@787: renatofilho@787: GST_LOG ("group:%p , pad %s:%s", group, GST_DEBUG_PAD_NAME (pad)); renatofilho@787: renatofilho@787: /* FIXME : check if pad is already controlled */ renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: renatofilho@787: /* Create GstDecodePad for the pad */ renatofilho@787: dpad = gst_decode_pad_new (group, pad, TRUE); renatofilho@787: renatofilho@787: group->endpads = g_list_append (group->endpads, dpad); renatofilho@787: renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: /* gst_decode_group_check_if_blocked: renatofilho@787: * renatofilho@787: * Call this when one of the pads blocked status has changed. renatofilho@787: * If the group is complete and blocked, the group will be marked as blocked renatofilho@787: * and will ghost/expose all pads on decodebin if the group is the current one. renatofilho@787: * renatofilho@787: * Call with the group lock taken ! MT safe renatofilho@787: */ renatofilho@787: static void renatofilho@787: gst_decode_group_check_if_blocked (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: gboolean blocked = TRUE; renatofilho@787: renatofilho@787: GST_LOG ("group : %p , ->complete:%d , ->nbdynamic:%d", renatofilho@787: group, group->complete, group->nbdynamic); renatofilho@787: renatofilho@787: /* 1. don't do anything if group is not complete */ renatofilho@787: if (!group->complete || group->nbdynamic) { renatofilho@787: GST_DEBUG_OBJECT (group->dbin, "Group isn't complete yet"); renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodePad *dpad = (GstDecodePad *) tmp->data; renatofilho@787: renatofilho@787: if (!dpad->blocked) { renatofilho@787: blocked = FALSE; renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: /* 2. Update status of group */ renatofilho@787: group->blocked = blocked; renatofilho@787: GST_LOG ("group is blocked:%d", blocked); renatofilho@787: renatofilho@787: /* 3. don't do anything if not blocked completely */ renatofilho@787: if (!blocked) renatofilho@787: return; renatofilho@787: renatofilho@787: /* 4. if we're the current group, expose pads */ renatofilho@787: DECODE_BIN_LOCK (group->dbin); renatofilho@787: if (!gst_decode_group_expose (group)) renatofilho@787: GST_WARNING_OBJECT (group->dbin, "Couldn't expose group"); renatofilho@787: DECODE_BIN_UNLOCK (group->dbin); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_group_check_if_drained (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: GstDecodeBin *dbin = group->dbin; renatofilho@787: gboolean drained = TRUE; renatofilho@787: renatofilho@787: GST_LOG ("group : %p", group); renatofilho@787: renatofilho@787: for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodePad *dpad = (GstDecodePad *) tmp->data; renatofilho@787: renatofilho@787: GST_LOG ("testing dpad %p", dpad); renatofilho@787: renatofilho@787: if (!dpad->drained) { renatofilho@787: drained = FALSE; renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: group->drained = drained; renatofilho@787: GST_LOG ("group is drained"); renatofilho@787: renatofilho@787: if (!drained) renatofilho@787: return; renatofilho@787: renatofilho@787: DECODE_BIN_LOCK (dbin); renatofilho@787: if ((group == dbin->activegroup) && dbin->groups) { renatofilho@787: GST_DEBUG_OBJECT (dbin, "Switching to new group"); renatofilho@787: renatofilho@787: gst_decode_group_hide (group); renatofilho@787: renatofilho@787: gst_decode_group_expose ((GstDecodeGroup *) dbin->groups->data); renatofilho@787: } renatofilho@787: DECODE_BIN_UNLOCK (dbin); renatofilho@787: } renatofilho@787: renatofilho@787: /* sort_end_pads: renatofilho@787: * GCompareFunc to use with lists of GstPad. renatofilho@787: * Sorts pads by mime type. renatofilho@787: * First video (raw, then non-raw), then audio (raw, then non-raw), renatofilho@787: * then others. renatofilho@787: * renatofilho@787: * Return: negative if ab renatofilho@787: */ renatofilho@787: renatofilho@787: static gint renatofilho@787: sort_end_pads (GstDecodePad * da, GstDecodePad * db) renatofilho@787: { renatofilho@787: GstPad *a, *b; renatofilho@787: gint va, vb; renatofilho@787: GstCaps *capsa, *capsb; renatofilho@787: GstStructure *sa, *sb; renatofilho@787: const gchar *namea, *nameb; renatofilho@787: renatofilho@787: a = da->pad; renatofilho@787: b = db->pad; renatofilho@787: renatofilho@787: capsa = gst_pad_get_caps (a); renatofilho@787: capsb = gst_pad_get_caps (b); renatofilho@787: renatofilho@787: sa = gst_caps_get_structure ((const GstCaps *) capsa, 0); renatofilho@787: sb = gst_caps_get_structure ((const GstCaps *) capsb, 0); renatofilho@787: renatofilho@787: namea = gst_structure_get_name (sa); renatofilho@787: nameb = gst_structure_get_name (sb); renatofilho@787: renatofilho@787: if (g_strrstr (namea, "video/x-raw-")) renatofilho@787: va = 0; renatofilho@787: else if (g_strrstr (namea, "video/")) renatofilho@787: va = 1; renatofilho@787: else if (g_strrstr (namea, "audio/x-raw")) renatofilho@787: va = 2; renatofilho@787: else if (g_strrstr (namea, "audio/")) renatofilho@787: va = 3; renatofilho@787: else renatofilho@787: va = 4; renatofilho@787: renatofilho@787: if (g_strrstr (nameb, "video/x-raw-")) renatofilho@787: vb = 0; renatofilho@787: else if (g_strrstr (nameb, "video/")) renatofilho@787: vb = 1; renatofilho@787: else if (g_strrstr (nameb, "audio/x-raw")) renatofilho@787: vb = 2; renatofilho@787: else if (g_strrstr (nameb, "audio/")) renatofilho@787: vb = 3; renatofilho@787: else renatofilho@787: vb = 4; renatofilho@787: renatofilho@787: gst_caps_unref (capsa); renatofilho@787: gst_caps_unref (capsb); renatofilho@787: renatofilho@787: return va - vb; renatofilho@787: } renatofilho@787: renatofilho@787: /* gst_decode_group_expose: renatofilho@787: * renatofilho@787: * Expose this group's pads. renatofilho@787: * renatofilho@787: * Not MT safe, please take the group lock renatofilho@787: */ renatofilho@787: renatofilho@787: static gboolean renatofilho@787: gst_decode_group_expose (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: GList *next = NULL; renatofilho@787: renatofilho@787: if (group->dbin->activegroup) { renatofilho@787: GST_DEBUG_OBJECT (group->dbin, "A group is already active and exposed"); renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: if (group->dbin->activegroup == group) { renatofilho@787: GST_WARNING ("Group %p is already exposed", group); renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: if (!group->dbin->groups renatofilho@787: || (group != (GstDecodeGroup *) group->dbin->groups->data)) { renatofilho@787: GST_WARNING ("Group %p is not the first group to expose", group); renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: if (group->nbdynamic) { renatofilho@787: GST_WARNING ("Group %p still has %d dynamic objects, not exposing yet", renatofilho@787: group, group->nbdynamic); renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: GST_LOG ("Exposing group %p", group); renatofilho@787: renatofilho@787: /* re-order pads : video, then audio, then others */ renatofilho@787: group->endpads = g_list_sort (group->endpads, (GCompareFunc) sort_end_pads); renatofilho@787: renatofilho@787: /* Expose pads */ renatofilho@787: renatofilho@787: for (tmp = group->endpads; tmp; tmp = next) { renatofilho@787: GstDecodePad *dpad = (GstDecodePad *) tmp->data; renatofilho@787: gchar *padname; renatofilho@787: GstPad *ghost; renatofilho@787: renatofilho@787: next = g_list_next (tmp); renatofilho@787: renatofilho@787: /* 1. ghost pad */ renatofilho@787: padname = g_strdup_printf ("src%d", group->dbin->nbpads); renatofilho@787: group->dbin->nbpads++; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (group->dbin, "About to expose pad %s:%s", renatofilho@787: GST_DEBUG_PAD_NAME (dpad->pad)); renatofilho@787: renatofilho@787: ghost = gst_ghost_pad_new (padname, dpad->pad); renatofilho@787: gst_pad_set_active (ghost, TRUE); renatofilho@787: gst_element_add_pad (GST_ELEMENT (group->dbin), ghost); renatofilho@787: group->ghosts = g_list_append (group->ghosts, ghost); renatofilho@787: renatofilho@787: g_free (padname); renatofilho@787: renatofilho@787: /* 2. emit signal */ renatofilho@787: GST_DEBUG_OBJECT (group->dbin, "emitting new-decoded-pad"); renatofilho@787: g_signal_emit (G_OBJECT (group->dbin), renatofilho@787: gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, renatofilho@787: (next == NULL)); renatofilho@787: GST_DEBUG_OBJECT (group->dbin, "emitted new-decoded-pad"); renatofilho@787: renatofilho@787: /* 3. Unblock internal pad */ renatofilho@787: GST_DEBUG_OBJECT (dpad->pad, "unblocking"); renatofilho@787: gst_pad_set_blocked_async (dpad->pad, FALSE, renatofilho@787: (GstPadBlockCallback) source_pad_blocked_cb, dpad); renatofilho@787: GST_DEBUG_OBJECT (dpad->pad, "unblocked"); renatofilho@787: renatofilho@787: } renatofilho@787: renatofilho@787: group->dbin->activegroup = group; renatofilho@787: renatofilho@787: /* pop off the first group */ renatofilho@787: group->dbin->groups = renatofilho@787: g_list_delete_link (group->dbin->groups, group->dbin->groups); renatofilho@787: renatofilho@787: remove_fakesink (group->dbin); renatofilho@787: renatofilho@787: group->exposed = TRUE; renatofilho@787: renatofilho@787: GST_LOG_OBJECT (group->dbin, "signalling no-more-pads"); renatofilho@787: gst_element_no_more_pads (GST_ELEMENT (group->dbin)); renatofilho@787: renatofilho@787: GST_LOG_OBJECT (group->dbin, "Group %p exposed", group); renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_group_hide (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: renatofilho@787: GST_LOG ("Hiding group %p", group); renatofilho@787: renatofilho@787: if (group != group->dbin->activegroup) { renatofilho@787: GST_WARNING ("This group is not the active one, aborting"); renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: renatofilho@787: /* Remove ghost pads */ renatofilho@787: for (tmp = group->ghosts; tmp; tmp = g_list_next (tmp)) renatofilho@787: gst_element_remove_pad (GST_ELEMENT (group->dbin), (GstPad *) tmp->data); renatofilho@787: renatofilho@787: g_list_free (group->ghosts); renatofilho@787: group->ghosts = NULL; renatofilho@787: renatofilho@787: group->exposed = FALSE; renatofilho@787: renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: renatofilho@787: group->dbin->activegroup = NULL; renatofilho@787: group->dbin->oldgroups = g_list_append (group->dbin->oldgroups, group); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: deactivate_free_recursive (GstDecodeGroup * group, GstElement * element) renatofilho@787: { renatofilho@787: GstIterator *it; renatofilho@787: GstIteratorResult res; renatofilho@787: gpointer point; renatofilho@787: renatofilho@787: GST_LOG ("element:%s", GST_ELEMENT_NAME (element)); renatofilho@787: renatofilho@787: /* call on downstream elements */ renatofilho@787: it = gst_element_iterate_src_pads (element); renatofilho@787: renatofilho@787: restart: renatofilho@787: renatofilho@787: while (1) { renatofilho@787: res = gst_iterator_next (it, &point); renatofilho@787: switch (res) { renatofilho@787: case GST_ITERATOR_DONE: renatofilho@787: goto done; renatofilho@787: case GST_ITERATOR_RESYNC: renatofilho@787: gst_iterator_resync (it); renatofilho@787: goto restart; renatofilho@787: case GST_ITERATOR_ERROR: renatofilho@787: { renatofilho@787: GST_WARNING ("Had an error while iterating source pads of element: %s", renatofilho@787: GST_ELEMENT_NAME (element)); renatofilho@787: goto beach; renatofilho@787: } renatofilho@787: case GST_ITERATOR_OK: renatofilho@787: { renatofilho@787: GstPad *pad = GST_PAD (point); renatofilho@787: GstPad *peerpad = NULL; renatofilho@787: renatofilho@787: if ((peerpad = gst_pad_get_peer (pad))) { renatofilho@787: GstObject *parent = gst_pad_get_parent (peerpad); renatofilho@787: renatofilho@787: if (parent && GST_IS_ELEMENT (parent)) renatofilho@787: deactivate_free_recursive (group, GST_ELEMENT (parent)); renatofilho@787: if (parent) renatofilho@787: gst_object_unref (parent); renatofilho@787: } renatofilho@787: } renatofilho@787: break; renatofilho@787: default: renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: done: renatofilho@787: gst_element_set_state (element, GST_STATE_NULL); renatofilho@787: gst_bin_remove (GST_BIN (group->dbin), element); renatofilho@787: renatofilho@787: beach: renatofilho@787: gst_iterator_free (it); renatofilho@787: renatofilho@787: return; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_decode_group_free (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GList *tmp; renatofilho@787: renatofilho@787: GST_LOG ("group %p", group); renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: /* Clear all GstDecodePad */ renatofilho@787: for (tmp = group->endpads; tmp; tmp = g_list_next (tmp)) { renatofilho@787: GstDecodePad *dpad = (GstDecodePad *) tmp->data; renatofilho@787: renatofilho@787: g_free (dpad); renatofilho@787: } renatofilho@787: g_list_free (group->endpads); renatofilho@787: group->endpads = NULL; renatofilho@787: renatofilho@787: /* disconnect signal handlers on multiqueue */ renatofilho@787: g_signal_handler_disconnect (group->multiqueue, group->underrunsig); renatofilho@787: g_signal_handler_disconnect (group->multiqueue, group->overrunsig); renatofilho@787: renatofilho@787: /* remove all elements */ renatofilho@787: deactivate_free_recursive (group, group->multiqueue); renatofilho@787: renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: renatofilho@787: g_mutex_free (group->lock); renatofilho@787: g_free (group); renatofilho@787: } renatofilho@787: renatofilho@787: /* gst_decode_group_set_complete: renatofilho@787: * renatofilho@787: * Mark the group as complete. This means no more streams will be controlled renatofilho@787: * through this group. renatofilho@787: * renatofilho@787: * MT safe renatofilho@787: */ renatofilho@787: static void renatofilho@787: gst_decode_group_set_complete (GstDecodeGroup * group) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (group->dbin, "Setting group %p to COMPLETE", group); renatofilho@787: renatofilho@787: GROUP_MUTEX_LOCK (group); renatofilho@787: group->complete = TRUE; renatofilho@787: gst_decode_group_check_if_blocked (group); renatofilho@787: GROUP_MUTEX_UNLOCK (group); renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: renatofilho@787: /************************* renatofilho@787: * GstDecodePad functions renatofilho@787: *************************/ renatofilho@787: renatofilho@787: static void renatofilho@787: source_pad_blocked_cb (GstPad * pad, gboolean blocked, GstDecodePad * dpad) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "blocked:%d , dpad:%p, dpad->group:%p", renatofilho@787: blocked, dpad, dpad->group); renatofilho@787: renatofilho@787: /* Update this GstDecodePad status */ renatofilho@787: dpad->blocked = blocked; renatofilho@787: renatofilho@787: if (blocked) { renatofilho@787: GROUP_MUTEX_LOCK (dpad->group); renatofilho@787: gst_decode_group_check_if_blocked (dpad->group); renatofilho@787: GROUP_MUTEX_UNLOCK (dpad->group); renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: source_pad_event_probe (GstPad * pad, GstEvent * event, GstDecodePad * dpad) renatofilho@787: { renatofilho@787: GST_LOG_OBJECT (pad, "%s dpad:%p", GST_EVENT_TYPE_NAME (event), dpad); renatofilho@787: renatofilho@787: if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { renatofilho@787: /* Set our pad as drained */ renatofilho@787: dpad->drained = TRUE; renatofilho@787: renatofilho@787: /* Check if all pads are drained */ renatofilho@787: gst_decode_group_check_if_drained (dpad->group); renatofilho@787: } renatofilho@787: renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: /*gst_decode_pad_new: renatofilho@787: * renatofilho@787: * Creates a new GstDecodePad for the given pad. renatofilho@787: * If block is TRUE, Sets the pad blocking asynchronously renatofilho@787: */ renatofilho@787: renatofilho@787: static GstDecodePad * renatofilho@787: gst_decode_pad_new (GstDecodeGroup * group, GstPad * pad, gboolean block) renatofilho@787: { renatofilho@787: GstDecodePad *dpad; renatofilho@787: renatofilho@787: dpad = g_new0 (GstDecodePad, 1); renatofilho@787: dpad->pad = pad; renatofilho@787: dpad->group = group; renatofilho@787: dpad->blocked = FALSE; renatofilho@787: dpad->drained = TRUE; renatofilho@787: renatofilho@787: if (block) renatofilho@787: gst_pad_set_blocked_async (pad, TRUE, renatofilho@787: (GstPadBlockCallback) source_pad_blocked_cb, dpad); renatofilho@787: gst_pad_add_event_probe (pad, G_CALLBACK (source_pad_event_probe), dpad); renatofilho@787: return dpad; renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: /***** renatofilho@787: * Element add/remove renatofilho@787: *****/ renatofilho@787: renatofilho@787: /* renatofilho@787: * add_fakesink / remove_fakesink renatofilho@787: * renatofilho@787: * We use a sink so that the parent ::change_state returns GST_STATE_CHANGE_ASYNC renatofilho@787: * when that sink is present (since it's not connected to anything it will renatofilho@787: * always return GST_STATE_CHANGE_ASYNC). renatofilho@787: * renatofilho@787: * But this is an ugly way of achieving this goal. renatofilho@787: * Ideally, we shouldn't use a sink and just return GST_STATE_CHANGE_ASYNC in renatofilho@787: * our ::change_state if we have not exposed the active group. renatofilho@787: * We also need to override ::get_state to fake the asynchronous behaviour. renatofilho@787: * Once the active group is exposed, we would then post a renatofilho@787: * GST_MESSAGE_STATE_DIRTY and return GST_STATE_CHANGE_SUCCESS (which will call renatofilho@787: * ::get_state . renatofilho@787: */ renatofilho@787: renatofilho@787: static gboolean renatofilho@787: add_fakesink (GstDecodeBin * decode_bin) renatofilho@787: { renatofilho@787: GST_DEBUG_OBJECT (decode_bin, "Adding the fakesink"); renatofilho@787: renatofilho@787: if (decode_bin->fakesink) renatofilho@787: return TRUE; renatofilho@787: renatofilho@787: decode_bin->fakesink = renatofilho@787: gst_element_factory_make ("fakesink", "async-fakesink"); renatofilho@787: if (!decode_bin->fakesink) renatofilho@787: goto no_fakesink; renatofilho@787: renatofilho@787: /* hacky, remove sink flag, we don't want our decodebin to become a sink renatofilho@787: * just because we add a fakesink element to make us ASYNC */ renatofilho@787: GST_OBJECT_FLAG_UNSET (decode_bin->fakesink, GST_ELEMENT_IS_SINK); renatofilho@787: renatofilho@787: if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->fakesink)) renatofilho@787: goto could_not_add; renatofilho@787: renatofilho@787: return TRUE; renatofilho@787: renatofilho@787: /* ERRORS */ renatofilho@787: no_fakesink: renatofilho@787: { renatofilho@787: g_warning ("can't find fakesink element, decodebin will not work"); renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: could_not_add: renatofilho@787: { renatofilho@787: g_warning ("Could not add fakesink to decodebin, decodebin will not work"); renatofilho@787: gst_object_unref (decode_bin->fakesink); renatofilho@787: decode_bin->fakesink = NULL; renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: remove_fakesink (GstDecodeBin * decode_bin) renatofilho@787: { renatofilho@787: if (decode_bin->fakesink == NULL) renatofilho@787: return; renatofilho@787: renatofilho@787: GST_DEBUG_OBJECT (decode_bin, "Removing the fakesink"); renatofilho@787: renatofilho@787: gst_element_set_state (decode_bin->fakesink, GST_STATE_NULL); renatofilho@787: gst_bin_remove (GST_BIN (decode_bin), decode_bin->fakesink); renatofilho@787: decode_bin->fakesink = NULL; renatofilho@787: renatofilho@787: gst_element_post_message (GST_ELEMENT_CAST (decode_bin), renatofilho@787: gst_message_new_state_dirty (GST_OBJECT_CAST (decode_bin))); renatofilho@787: } renatofilho@787: renatofilho@787: /***** renatofilho@787: * convenience functions renatofilho@787: *****/ renatofilho@787: renatofilho@787: /* find_sink_pad renatofilho@787: * renatofilho@787: * Returns the first sink pad of the given element, or NULL if it doesn't have renatofilho@787: * any. renatofilho@787: */ renatofilho@787: renatofilho@787: static GstPad * renatofilho@787: find_sink_pad (GstElement * element) renatofilho@787: { renatofilho@787: GstIterator *it; renatofilho@787: GstPad *pad = NULL; renatofilho@787: gpointer point; renatofilho@787: renatofilho@787: it = gst_element_iterate_sink_pads (element); renatofilho@787: renatofilho@787: if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK) renatofilho@787: pad = (GstPad *) point; renatofilho@787: renatofilho@787: gst_iterator_free (it); renatofilho@787: renatofilho@787: return pad; renatofilho@787: } renatofilho@787: renatofilho@787: static GstStateChangeReturn renatofilho@787: gst_decode_bin_change_state (GstElement * element, GstStateChange transition) renatofilho@787: { renatofilho@787: GstStateChangeReturn ret; renatofilho@787: GstDecodeBin *dbin = GST_DECODE_BIN (element); renatofilho@787: renatofilho@787: switch (transition) { renatofilho@787: case GST_STATE_CHANGE_NULL_TO_READY: renatofilho@787: if (dbin->typefind == NULL) renatofilho@787: goto missing_typefind; renatofilho@787: break; renatofilho@787: case GST_STATE_CHANGE_READY_TO_PAUSED:{ renatofilho@787: if (!add_fakesink (dbin)) renatofilho@787: goto missing_fakesink; renatofilho@787: break; renatofilho@787: } renatofilho@787: default: renatofilho@787: break; renatofilho@787: } renatofilho@787: renatofilho@787: ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); renatofilho@787: renatofilho@787: /* FIXME : put some cleanup functions here.. if needed */ renatofilho@787: renatofilho@787: return ret; renatofilho@787: renatofilho@787: /* ERRORS */ renatofilho@787: missing_typefind: renatofilho@787: { renatofilho@787: GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL), ("no typefind!")); renatofilho@787: return GST_STATE_CHANGE_FAILURE; renatofilho@787: } renatofilho@787: missing_fakesink: renatofilho@787: { renatofilho@787: GST_ELEMENT_ERROR (dbin, CORE, MISSING_PLUGIN, (NULL), ("no fakesink!")); renatofilho@787: return GST_STATE_CHANGE_FAILURE; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: plugin_init (GstPlugin * plugin) renatofilho@787: { renatofilho@787: GST_DEBUG_CATEGORY_INIT (gst_decode_bin_debug, "decodebin2", 0, renatofilho@787: "decoder bin"); renatofilho@787: renatofilho@787: return gst_element_register (plugin, "decodebin2", GST_RANK_NONE, renatofilho@787: GST_TYPE_DECODE_BIN); renatofilho@787: } renatofilho@787: renatofilho@787: GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, renatofilho@787: GST_VERSION_MINOR, renatofilho@787: "decodebin2", renatofilho@787: "decoder bin newer version", plugin_init, VERSION, GST_LICENSE, renatofilho@787: GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)