From 24f28cfdb016782822094211bb809db6e1148212 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 14 Jun 2011 23:42:27 +0100 Subject: [PATCH] playback: remove old playbin and decodebin elements --- gst/playback/Makefile.am | 28 +- gst/playback/README | 91 -- gst/playback/gstdecodebin.c | 2072 ------------------------------ gst/playback/gstplayback.c | 5 +- gst/playback/gstplaybasebin.c | 2823 ----------------------------------------- gst/playback/gstplaybasebin.h | 126 -- gst/playback/gstplaybin.c | 1991 ----------------------------- gst/playback/gststreaminfo.c | 404 ------ gst/playback/gststreaminfo.h | 86 -- 9 files changed, 2 insertions(+), 7624 deletions(-) delete mode 100644 gst/playback/README delete mode 100644 gst/playback/gstdecodebin.c delete mode 100644 gst/playback/gstplaybasebin.c delete mode 100644 gst/playback/gstplaybasebin.h delete mode 100644 gst/playback/gstplaybin.c delete mode 100644 gst/playback/gststreaminfo.c delete mode 100644 gst/playback/gststreaminfo.h diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 2fe90e6..dd6ac2c 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -6,16 +6,13 @@ glib_gen_basename = gstplay built_sources = gstplay-marshal.c built_headers = gstplay-marshal.h -plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstdecodebin2.la +plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin2.la libgstplaybin_la_SOURCES = \ gstplayback.c \ - gstplaybin.c \ gstplaybin2.c \ gstplaysink.c \ - gstplaybasebin.c \ gstplay-enum.c \ - gststreaminfo.c \ gststreamselector.c \ gstsubtitleoverlay.c \ gstplaysinkvideoconvert.c \ @@ -32,15 +29,6 @@ libgstplaybin_la_LIBADD = \ $(GST_LIBS) libgstplaybin_la_LIBTOOLFLAGS = --tag=disable-static -libgstdecodebin_la_SOURCES = gstdecodebin.c -nodist_libgstdecodebin_la_SOURCES = $(built_sources) -libgstdecodebin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) -libgstdecodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstdecodebin_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \ - $(GST_LIBS) -libgstdecodebin_la_LIBTOOLFLAGS = --tag=disable-static - libgstdecodebin2_la_SOURCES = gstdecodebin2.c gsturidecodebin.c gstplay-enum.c nodist_libgstdecodebin2_la_SOURCES = $(built_sources) libgstdecodebin2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) @@ -52,9 +40,7 @@ libgstdecodebin2_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = \ gstplayback.h \ - gstplaybasebin.h \ gstplaysink.h \ - gststreaminfo.h \ gstplay-enum.h \ gststreamselector.h \ gstrawcaps.h \ @@ -73,18 +59,6 @@ include $(top_srcdir)/common/gst-glib-gen.mak Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ - -:PROJECT libgstdecodebin -:SHARED libgstdecodebin \ - -:TAGS eng debug \ - -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ - -:SOURCES $(libgstdecodebin_la_SOURCES) \ - $(nodist_libgstdecodebin_la_SOURCES) \ - -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdecodebin_la_CFLAGS) \ - -:LDFLAGS $(libgstdecodebin_la_LDFLAGS) \ - $(libgstdecodebin_la_LIBADD) \ - -ldl \ - -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ - LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ - \ -:PROJECT libgstdecodebin2 -:SHARED libgstdecodebin2 \ -:TAGS eng debug \ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ diff --git a/gst/playback/README b/gst/playback/README deleted file mode 100644 index 286e49f..0000000 --- a/gst/playback/README +++ /dev/null @@ -1,91 +0,0 @@ -decodebin: - - A bin with a sinkpad that decodes the data into raw formats. It works by sending - the input data through a typefind element and then recursively autoplugs elements - from the registry until a raw format is obtained. It will then create a new ghostpad - on itself to signal the app of the new pad. - - Decodebin will also remove pads when they are removed from the stream. - - TODO - - reuse of decoderbin, cleanup in READY state - - threading after demuxing? - - new_media events should be handled. - - caching of elements. - - abstract more elements, pads (typefind, ...); - - The autoplugging happens as follows: - - 1) typefind is added internally to the bin. - 2) the have_type signal is connected to typefind. - 3) in the have_type callback the close_pad_link function is called - 4) close_pad_link checks the type on the pad, if it is raw, a ghostpad - is created and autoplugging for that pad stops. - 5) if the type of the pad is not raw, a list of possible elements that - can connect to this type is generated in find_compatibles. - 6) try_to_link_1 with the element list is called. The function will loop - over the element list and will try to connect one of the elements to - the pad. If the link works, a call is made to close_link. - 7) close_link loops over all the source pads of the element and - recursively calls 4) for any ALWAYS pad. For elements with - a SOMETIMES pad, a structure is set up and is passed to the callback - of the new_pad signal. - 8) in the new_pad callback, 4) is called to try to autoplug the - new pad. - - -playbasebin: - - A bin with an uri property. It will find the right source element from the registry - and connect a decoderbin to it. When going to the PAUSED state, it will iterate the - decoderbin and listen for new pad signals from it. It will connect a queue to each - new pad and will iterate the decoderbin until one of the queues is filled. It is - assumed that by that time all the streams will be found so that when leaving the - PAUSED state, one can query the number of streams in the media file with the given - uri. - - Playbasebin internally groups related streams together in a GstPlayBaseGroup. This - is particulary important for chained oggs. Initially, a new group is created in - the 'building' state. All new streams will be added to the building group until - no-more-pads is signaled or one of the preroll queues overflows. When this happens, - the group is commited to a list of groups ready for playback. PlaybaseBin will then - attach a padprobe to each stream to figure out when it finished. It will remove - the current group and install the next playable group, then. - - Before going to the PLAYING state, it is possible to connect a custom element to - each of the streams. To do that, you have to add the element to the bin and then - connect the pad(s) from the stream(s). You do not have to add the elements in - a thread, the bin will take care of then when it's needed. You are allowed to use - threads inside the elements, of course. - The bin tries to be smart and doesn't add a queue when there is only one possible - stream. - - - TODO - - reuse, cleanup in ready state - - when the first pad is closed, it's possible that another dynamic element is - added somewhere so that we need a queue for the first pad as well. - - -playbin: - - Extends playbasebin, sets up default audiosink and videosink for first audio/video - stream detected. implements seeking and querying on the configured sinks. - - It also waits for new notifications from playbasebin about any new groups that are - becomming active. It then disconnects the sinks and reconnects them to the new - pads in the group. - - TODO - - reuse, refcounting, cleanup in READY state - - be smarter about replugging the sinks instead of removing them and readding them. - - Do not crap out when the audio device is in use. - - -general - - TODO - - playlist support. maybe use a playlist bin that streams the contents of the - playlist on a pad, interleaved with new_media events. Also add a tuner - interface while we're at it. - diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c deleted file mode 100644 index ef0571c..0000000 --- a/gst/playback/gstdecodebin.c +++ /dev/null @@ -1,2072 +0,0 @@ -/* GStreamer - * Copyright (C) <2004> Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:element-decodebin - * - * #GstBin that auto-magically constructs a decoding pipeline using available - * decoders and demuxers via auto-plugging. - * - * When using decodebin in your application, connect a signal handler to - * #GstDecodeBin::new-decoded-pad and connect your sinks from within the - * callback function. - * - * - * This element is deprecated and no longer supported. You should use the - * #uridecodebin or #decodebin2 element instead (or, even better: #playbin2). - * - * - * Deprecated: use uridecodebin or decodebin2 instead. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include -#include -#include - -#include "gstplay-marshal.h" - -/* generic templates */ -static GstStaticPadTemplate decoder_bin_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate decoder_bin_src_template = -GST_STATIC_PAD_TEMPLATE ("src%d", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC (gst_decode_bin_debug); -#define GST_CAT_DEFAULT gst_decode_bin_debug - -#define GST_TYPE_DECODE_BIN (gst_decode_bin_get_type()) -#define GST_DECODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECODE_BIN,GstDecodeBin)) -#define GST_DECODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DECODE_BIN,GstDecodeBinClass)) -#define GST_IS_DECODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DECODE_BIN)) -#define GST_IS_DECODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DECODE_BIN)) - -typedef struct _GstDecodeBin GstDecodeBin; -typedef struct _GstDecodeBinClass GstDecodeBinClass; - -/** - * GstDecodeBin: - * - * Auto-plugging decoder element structure - */ -struct _GstDecodeBin -{ - GstBin bin; /* we extend GstBin */ - - GstElement *typefind; /* this holds the typefind object */ - GstElement *fakesink; - - GList *dynamics; /* list of dynamic connections */ - - GList *queues; /* list of demuxer-decoder queues */ - - GList *probes; /* list of PadProbeData */ - - GList *factories; /* factories we can use for selecting elements */ - gint numpads; - gint numwaiting; - - gboolean have_type; - guint have_type_id; /* signal id for the typefind element */ - - gboolean shutting_down; /* stop pluggin if we're shutting down */ - - GType queue_type; /* store the GType of queues, to aid in recognising them */ - - GMutex *cb_mutex; /* Mutex for multi-threaded callbacks, such as removing the fakesink */ -}; - -struct _GstDecodeBinClass -{ - GstBinClass parent_class; - - /* signal we fire when a new pad has been decoded into raw audio/video */ - void (*new_decoded_pad) (GstElement * element, GstPad * pad, gboolean last); - /* signal we fire when a pad has been removed */ - void (*removed_decoded_pad) (GstElement * element, GstPad * pad); - /* signal fired when we found a pad that we cannot decode */ - void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); -}; - -/* signals */ -enum -{ - SIGNAL_NEW_DECODED_PAD, - SIGNAL_REMOVED_DECODED_PAD, - SIGNAL_UNKNOWN_TYPE, - SIGNAL_REDIRECT, - LAST_SIGNAL -}; - -/* Properties */ -enum -{ - PROP_0, - PROP_SINK_CAPS, -}; - - -typedef struct -{ - GstPad *pad; - gulong sigid; - gboolean done; -} PadProbeData; - -/* this structure is created for all dynamic pads that could get created - * at runtime */ -typedef struct -{ - GstDecodeBin *decode_bin; /* pointer to ourself */ - - GstElement *element; /* the element sending the signal */ - gint np_sig_id; /* signal id of new_pad */ - gint nmp_sig_id; /* signal id of no_more_pads */ - - GstPad *pad; /* the pad sending the signal */ - gint caps_sig_id; /* signal id of caps */ -} -GstDynamic; - -static void gst_decode_bin_class_init (GstDecodeBinClass * klass); -static void gst_decode_bin_init (GstDecodeBin * decode_bin); -static void gst_decode_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_decode_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_decode_bin_dispose (GObject * object); -static void gst_decode_bin_finalize (GObject * object); - -static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, - GstStateChange transition); - -static gboolean add_fakesink (GstDecodeBin * decode_bin); -static void remove_fakesink (GstDecodeBin * decode_bin); - -static void dynamic_free (GstDynamic * dyn); -static void free_dynamics (GstDecodeBin * decode_bin); -static void type_found (GstElement * typefind, guint probability, - GstCaps * caps, GstDecodeBin * decode_bin); -static GstElement *try_to_link_1 (GstDecodeBin * decode_bin, - GstElement * origelement, GstPad * pad, GList * factories); -static void close_link (GstElement * element, GstDecodeBin * decode_bin); -static void close_pad_link (GstElement * element, GstPad * pad, - GstCaps * caps, GstDecodeBin * decode_bin, gboolean more); -static void unlinked (GstPad * pad, GstPad * peerpad, - GstDecodeBin * decode_bin); -static void new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic); -static void no_more_pads (GstElement * element, GstDynamic * dynamic); -static void new_caps (GstPad * pad, GParamSpec * unused, GstDynamic * dynamic); - -static void queue_filled_cb (GstElement * queue, GstDecodeBin * decode_bin); -static void queue_underrun_cb (GstElement * queue, GstDecodeBin * decode_bin); - -static gboolean is_demuxer_element (GstElement * srcelement); - -static GstElementClass *parent_class; -static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; - - -static GType -gst_decode_bin_get_type (void) -{ - static GType gst_decode_bin_type = 0; - - if (!gst_decode_bin_type) { - static const GTypeInfo gst_decode_bin_info = { - sizeof (GstDecodeBinClass), - NULL, - NULL, - (GClassInitFunc) gst_decode_bin_class_init, - NULL, - NULL, - sizeof (GstDecodeBin), - 0, - (GInstanceInitFunc) gst_decode_bin_init, - NULL - }; - - gst_decode_bin_type = - g_type_register_static (GST_TYPE_BIN, "GstDecodeBin", - &gst_decode_bin_info, 0); - } - - return gst_decode_bin_type; -} - -static void -gst_decode_bin_class_init (GstDecodeBinClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_klass->set_property = gst_decode_bin_set_property; - gobject_klass->get_property = gst_decode_bin_get_property; - gobject_klass->dispose = gst_decode_bin_dispose; - gobject_klass->finalize = gst_decode_bin_finalize; - - /** - * GstDecodeBin::new-decoded-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 - * the valid 'raw' types is added. - */ - gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] = - g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstDecodeBinClass, new_decoded_pad), NULL, NULL, - gst_play_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, GST_TYPE_PAD, - G_TYPE_BOOLEAN); - /** - * GstDecodeBin::removed-decoded-pad: - * @bin: The decodebin - * @pad: The pad that was removed - * - * This signal is emitted when a 'final' caps pad has been removed. - */ - gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD] = - g_signal_new ("removed-decoded-pad", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstDecodeBinClass, removed_decoded_pad), NULL, NULL, - gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); - /** - * GstDecodeBin::unknown-type: - * @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. - */ - gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE] = - g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, unknown_type), - NULL, NULL, gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, - GST_TYPE_PAD, GST_TYPE_CAPS); - - 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)); - - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&decoder_bin_sink_template)); - gst_element_class_add_pad_template (gstelement_klass, - gst_static_pad_template_get (&decoder_bin_src_template)); - - gst_element_class_set_details_simple (gstelement_klass, - "Decoder Bin", "Generic/Bin/Decoder", - "Autoplug and decode to raw media", - "Wim Taymans "); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_decode_bin_change_state); -} - -/* check if the bin is dynamic. - * - * If there are no outstanding dynamic connections, the bin is - * considered to be non-dynamic. - */ -static gboolean -gst_decode_bin_is_dynamic (GstDecodeBin * decode_bin) -{ - return decode_bin->dynamics != NULL; -} - -/* the filter function for selecting the elements we can use in - * autoplugging */ -static gboolean -gst_decode_bin_factory_filter (GstPluginFeature * feature, - GstDecodeBin * decode_bin) -{ - guint rank; - const gchar *klass; - - /* we only care about element factories */ - if (!GST_IS_ELEMENT_FACTORY (feature)) - return FALSE; - - klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); - /* only demuxers, decoders and parsers can play */ - if (strstr (klass, "Demux") == NULL && - strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL && - strstr (klass, "Depayloader") == NULL) { - return FALSE; - } - - /* only select elements with autoplugging rank */ - rank = gst_plugin_feature_get_rank (feature); - if (rank < GST_RANK_MARGINAL) - return FALSE; - - return TRUE; -} - -/* function used to sort element features */ -static gint -compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) -{ - gint diff; - const gchar *rname1, *rname2; - - diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); - if (diff != 0) - return diff; - - rname1 = gst_plugin_feature_get_name (f1); - rname2 = gst_plugin_feature_get_name (f2); - - diff = strcmp (rname2, rname1); - - return diff; -} - -static void -print_feature (GstPluginFeature * feature) -{ - const gchar *rname; - - rname = gst_plugin_feature_get_name (feature); - - GST_DEBUG ("%s", rname); -} - -static void -gst_decode_bin_init (GstDecodeBin * decode_bin) -{ - GList *factories; - - decode_bin->cb_mutex = g_mutex_new (); - - /* first filter out the interesting element factories */ - factories = gst_default_registry_feature_filter ( - (GstPluginFeatureFilter) gst_decode_bin_factory_filter, - FALSE, decode_bin); - - /* sort them according to their ranks */ - decode_bin->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); - /* do some debugging */ - g_list_foreach (decode_bin->factories, (GFunc) print_feature, NULL); - - /* we create the typefind element only once */ - decode_bin->typefind = gst_element_factory_make ("typefind", "typefind"); - if (!decode_bin->typefind) { - g_warning ("can't find typefind element, decodebin will not work"); - } else { - GstPad *pad, *gpad; - GstPadTemplate *pad_tmpl; - - /* add the typefind element */ - if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->typefind)) { - g_warning ("Could not add typefind element, decodebin will not work"); - gst_object_unref (decode_bin->typefind); - decode_bin->typefind = NULL; - } - - /* get the sinkpad */ - pad = gst_element_get_static_pad (decode_bin->typefind, "sink"); - - /* get the pad template */ - pad_tmpl = gst_static_pad_template_get (&decoder_bin_sink_template); - - /* ghost the sink pad to ourself */ - gpad = gst_ghost_pad_new_from_template ("sink", pad, pad_tmpl); - gst_pad_set_active (gpad, TRUE); - gst_element_add_pad (GST_ELEMENT (decode_bin), gpad); - - gst_object_unref (pad_tmpl); - gst_object_unref (pad); - - /* connect a signal to find out when the typefind element found - * a type */ - decode_bin->have_type_id = - g_signal_connect (G_OBJECT (decode_bin->typefind), "have_type", - G_CALLBACK (type_found), decode_bin); - } - add_fakesink (decode_bin); - - decode_bin->dynamics = NULL; - decode_bin->queues = NULL; - decode_bin->probes = NULL; -} - -static void -gst_decode_bin_dispose (GObject * object) -{ - GstDecodeBin *decode_bin; - - decode_bin = GST_DECODE_BIN (object); - - if (decode_bin->factories) - gst_plugin_feature_list_free (decode_bin->factories); - decode_bin->factories = NULL; - - G_OBJECT_CLASS (parent_class)->dispose (object); - - /* our parent dispose might trigger new signals when pads are unlinked - * etc. clean up the mess here. */ - /* FIXME do proper cleanup when going to NULL */ - free_dynamics (decode_bin); -} - -static 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; -} - -static void -gst_decode_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDecodeBin *dbin; - - dbin = GST_DECODE_BIN (object); - - switch (prop_id) { - 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; - } -} - -static void -gst_decode_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDecodeBin *dbin; - - dbin = GST_DECODE_BIN (object); - switch (prop_id) { - 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; - } -} - -static void -gst_decode_bin_finalize (GObject * object) -{ - GstDecodeBin *decode_bin = GST_DECODE_BIN (object); - - g_mutex_free (decode_bin->cb_mutex); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -struct DynFind -{ - GstElement *elem; - GstPad *pad; -}; - -static gint -find_dynamic (GstDynamic * dyn, struct DynFind *info) -{ - if (dyn->element == info->elem && dyn->pad == info->pad) - return 0; - return 1; -} - -/* Add either an element (for dynamic pads/pad-added watching) or a - * pad (for delayed caps/notify::caps watching) to the dynamic list, - * taking care to ignore repeat entries so we don't end up handling a - * pad twice, for example */ -static void -dynamic_add (GstElement * element, GstPad * pad, GstDecodeBin * decode_bin) -{ - GstDynamic *dyn; - struct DynFind find_info; - GList *found; - - g_return_if_fail (element != NULL); - - /* do a search that this entry doesn't already exist */ - find_info.elem = element; - find_info.pad = pad; - found = g_list_find_custom (decode_bin->dynamics, &find_info, - (GCompareFunc) find_dynamic); - if (found != NULL) - goto exit; - - /* take refs */ - dyn = g_new0 (GstDynamic, 1); - dyn->element = gst_object_ref (element); - dyn->decode_bin = gst_object_ref (decode_bin); - if (pad) { - dyn->pad = gst_object_ref (pad); - GST_DEBUG_OBJECT (decode_bin, "dynamic create for pad %" GST_PTR_FORMAT, - pad); - dyn->caps_sig_id = g_signal_connect (G_OBJECT (pad), "notify::caps", - G_CALLBACK (new_caps), dyn); - } else { - GST_DEBUG_OBJECT (decode_bin, "dynamic create for element %" - GST_PTR_FORMAT, element); - dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "pad-added", - G_CALLBACK (new_pad), dyn); - dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads", - G_CALLBACK (no_more_pads), dyn); - } - - /* and add this element to the dynamic elements */ - decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); - - return; -exit: - if (element) { - GST_DEBUG_OBJECT (decode_bin, "Dynamic element already added: %" - GST_PTR_FORMAT, element); - } else { - GST_DEBUG_OBJECT (decode_bin, "Dynamic pad already added: %" - GST_PTR_FORMAT, pad); - } -} - -static void -dynamic_free (GstDynamic * dyn) -{ - GST_DEBUG_OBJECT (dyn->decode_bin, "dynamic free"); - - /* disconnect signals */ - if (dyn->np_sig_id) - g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->np_sig_id); - if (dyn->nmp_sig_id) - g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->nmp_sig_id); - if (dyn->caps_sig_id) - g_signal_handler_disconnect (G_OBJECT (dyn->pad), dyn->caps_sig_id); - - if (dyn->pad) - gst_object_unref (dyn->pad); - dyn->pad = NULL; - if (dyn->element) - gst_object_unref (dyn->element); - dyn->element = NULL; - - gst_object_unref (dyn->decode_bin); - dyn->decode_bin = NULL; - - g_free (dyn); -} - -static void -free_dynamics (GstDecodeBin * decode_bin) -{ - GList *dyns; - - for (dyns = decode_bin->dynamics; dyns; dyns = g_list_next (dyns)) { - GstDynamic *dynamic = (GstDynamic *) dyns->data; - - dynamic_free (dynamic); - } - g_list_free (decode_bin->dynamics); - decode_bin->dynamics = NULL; -} - -/* this function runs through the element factories and returns a list - * of all elements that are able to sink the given caps - */ -static GList * -find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) -{ - GList *factories; - GList *to_try = NULL; - - /* loop over all the factories */ - for (factories = decode_bin->factories; factories; - factories = g_list_next (factories)) { - GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data); - const GList *templates; - GList *walk; - - /* get the templates from the element factory */ - templates = gst_element_factory_get_static_pad_templates (factory); - for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { - GstStaticPadTemplate *templ = walk->data; - - /* we only care about the sink templates */ - if (templ->direction == GST_PAD_SINK) { - gboolean can_intersect; - GstCaps *tmpl_caps; - - /* try to intersect the caps with the caps of the template */ - tmpl_caps = gst_static_caps_get (&templ->static_caps); - - can_intersect = gst_caps_can_intersect (caps, tmpl_caps); - gst_caps_unref (tmpl_caps); - - /* check if the intersection is empty */ - if (can_intersect) { - /* non empty intersection, we can use this element */ - to_try = g_list_prepend (to_try, factory); - break; - } - } - } - } - to_try = g_list_reverse (to_try); - - return to_try; -} - -static gboolean -mimetype_is_raw (const gchar * mimetype) -{ - return g_str_has_prefix (mimetype, "video/x-raw") || - g_str_has_prefix (mimetype, "audio/x-raw") || - g_str_has_prefix (mimetype, "text/plain") || - g_str_has_prefix (mimetype, "text/x-pango-markup"); -} - -static void -free_pad_probes (GstDecodeBin * decode_bin) -{ - GList *tmp; - - /* Remove pad probes */ - for (tmp = decode_bin->probes; tmp; tmp = g_list_next (tmp)) { - PadProbeData *data = (PadProbeData *) tmp->data; - - gst_pad_remove_probe (data->pad, data->sigid); - g_free (data); - } - g_list_free (decode_bin->probes); - decode_bin->probes = NULL; -} - -/* used when we need to remove a probe because the decoder we plugged failed - * to activate */ -static void -free_pad_probe_for_element (GstDecodeBin * decode_bin, GstElement * element) -{ - GList *l; - - for (l = decode_bin->probes; l != NULL; l = g_list_next (l)) { - PadProbeData *data = (PadProbeData *) l->data; - - if (GST_ELEMENT_CAST (GST_PAD_PARENT (data->pad)) == element) { - gst_pad_remove_probe (data->pad, data->sigid); - decode_bin->probes = g_list_delete_link (decode_bin->probes, l); - g_free (data); - return; - } - } -} - -static gboolean -add_fakesink (GstDecodeBin * decode_bin) -{ - if (decode_bin->fakesink != NULL) - return TRUE; - - g_mutex_lock (decode_bin->cb_mutex); - - decode_bin->fakesink = gst_element_factory_make ("fakesink", "fakesink"); - if (!decode_bin->fakesink) - goto no_fakesink; - - /* 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); - - /* takes ownership */ - if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->fakesink)) { - g_warning ("Could not add fakesink element, decodebin will not work"); - gst_object_unref (decode_bin->fakesink); - decode_bin->fakesink = NULL; - } - g_mutex_unlock (decode_bin->cb_mutex); - return TRUE; - - /* ERRORS */ -no_fakesink: - { - g_warning ("can't find fakesink element, decodebin will not work"); - g_mutex_unlock (decode_bin->cb_mutex); - return FALSE; - } -} - -static void -remove_fakesink (GstDecodeBin * decode_bin) -{ - gboolean removed_fakesink = FALSE; - - if (decode_bin->fakesink == NULL) - return; - - g_mutex_lock (decode_bin->cb_mutex); - if (decode_bin->fakesink) { - GST_DEBUG_OBJECT (decode_bin, "Removing fakesink and marking state dirty"); - - /* Lock the state to prevent it from changing state to non-NULL - * before it's removed */ - gst_element_set_locked_state (decode_bin->fakesink, TRUE); - /* setting the state to NULL is never async */ - gst_element_set_state (decode_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), decode_bin->fakesink); - decode_bin->fakesink = NULL; - - removed_fakesink = TRUE; - } - g_mutex_unlock (decode_bin->cb_mutex); - - if (removed_fakesink) { - free_pad_probes (decode_bin); - } -} - -/* this should be implemented with _pad_block() */ -static GstProbeReturn -pad_probe (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GstMiniObject *data = type_data; - GstDecodeBin *decode_bin = user_data; - GList *tmp; - gboolean alldone = TRUE; - - for (tmp = decode_bin->probes; tmp; tmp = g_list_next (tmp)) { - PadProbeData *pdata = (PadProbeData *) tmp->data; - - if (pdata->pad == pad) { - if (GST_IS_BUFFER (data)) { - if (!pdata->done) - decode_bin->numwaiting--; - pdata->done = TRUE; - } else if (GST_IS_EVENT (data) && - ((GST_EVENT_TYPE (data) == GST_EVENT_EOS) || - (GST_EVENT_TYPE (data) == GST_EVENT_TAG) || - (GST_EVENT_TYPE (data) == GST_EVENT_FLUSH_START))) { - /* FIXME, what about NEWSEGMENT? really, use _pad_block()... */ - if (!pdata->done) - decode_bin->numwaiting--; - pdata->done = TRUE; - } - } - - if (!(pdata->done)) { - GST_LOG_OBJECT (decode_bin, "Pad probe on pad %" GST_PTR_FORMAT - " but pad %" GST_PTR_FORMAT " still needs data.", pad, pdata->pad); - alldone = FALSE; - } - } - if (alldone) - remove_fakesink (decode_bin); - return TRUE; -} - -/* FIXME: this should be somehow merged with the queue code in - * try_to_link_1() to reduce code duplication */ -static GstPad * -add_raw_queue (GstDecodeBin * decode_bin, GstPad * pad) -{ - GstElement *queue = NULL; - GstPad *queuesinkpad = NULL, *queuesrcpad = NULL; - - queue = gst_element_factory_make ("queue", NULL); - decode_bin->queue_type = G_OBJECT_TYPE (queue); - - g_object_set (G_OBJECT (queue), "max-size-buffers", 0, NULL); - g_object_set (G_OBJECT (queue), "max-size-time", G_GINT64_CONSTANT (0), NULL); - g_object_set (G_OBJECT (queue), "max-size-bytes", 8192, NULL); - gst_bin_add (GST_BIN (decode_bin), queue); - gst_element_set_state (queue, GST_STATE_READY); - queuesinkpad = gst_element_get_static_pad (queue, "sink"); - queuesrcpad = gst_element_get_static_pad (queue, "src"); - - if (gst_pad_link (pad, queuesinkpad) != GST_PAD_LINK_OK) { - GST_WARNING_OBJECT (decode_bin, - "Linking queue failed, trying without queue"); - gst_element_set_state (queue, GST_STATE_NULL); - gst_object_unref (queuesrcpad); - gst_object_unref (queuesinkpad); - gst_bin_remove (GST_BIN (decode_bin), queue); - return gst_object_ref (pad); - } - - decode_bin->queues = g_list_append (decode_bin->queues, queue); - g_signal_connect (G_OBJECT (queue), - "overrun", G_CALLBACK (queue_filled_cb), decode_bin); - g_signal_connect (G_OBJECT (queue), - "underrun", G_CALLBACK (queue_underrun_cb), decode_bin); - - gst_element_set_state (queue, GST_STATE_PAUSED); - gst_object_unref (queuesinkpad); - - return queuesrcpad; -} - -/* given a pad and a caps from an element, find the list of elements - * that could connect to the pad - * - * If the pad has a raw format, this function will create a ghostpad - * for the pad onto the decodebin. - * - * If no compatible elements could be found, this function will signal - * the unknown_type signal. - */ -static void -close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, - GstDecodeBin * decode_bin, gboolean more) -{ - GstStructure *structure; - const gchar *mimetype; - gchar *padname; - gint diff; - - padname = gst_pad_get_name (pad); - diff = strncmp (padname, "current_", 8); - g_free (padname); - - /* hack.. ignore current pads */ - if (!diff) - return; - - /* the caps is empty, this means the pad has no type, we can only - * decide to fire the unknown_type signal. */ - if (caps == NULL || gst_caps_is_empty (caps)) - goto unknown_type; - - /* the caps is any, this means the pad can be anything and - * we don't know yet */ - if (gst_caps_is_any (caps)) - goto dont_know_yet; - - GST_LOG_OBJECT (element, "trying to close %" GST_PTR_FORMAT, caps); - - /* FIXME, iterate over more structures? I guess it is possible that - * this pad has some encoded and some raw pads. This code will fail - * then if the first structure is not the raw type... */ - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - /* first see if this is raw. If the type is raw, we can - * create a ghostpad for this pad. It's possible that the caps are not - * fixed. */ - if (mimetype_is_raw (mimetype)) { - GstPadTemplate *tmpl; - gchar *padname; - GstPad *ghost; - PadProbeData *data; - - /* If we're at a demuxer element but have raw data already - * we have to add a queue here. For non-raw data this is done - * in try_to_link_1() */ - if (is_demuxer_element (element)) { - GST_DEBUG_OBJECT (decode_bin, - "Element %s is a demuxer, inserting a queue", - GST_OBJECT_NAME (element)); - - pad = add_raw_queue (decode_bin, pad); - } - - /* make a unique name for this new pad */ - padname = g_strdup_printf ("src%d", decode_bin->numpads); - decode_bin->numpads++; - - /* make it a ghostpad */ - tmpl = gst_static_pad_template_get (&decoder_bin_src_template); - ghost = gst_ghost_pad_new_from_template (padname, pad, tmpl); - gst_object_unref (tmpl); - - gst_pad_set_active (ghost, TRUE); - gst_element_add_pad (GST_ELEMENT (decode_bin), ghost); - - data = g_new0 (PadProbeData, 1); - data->pad = pad; - data->done = FALSE; - - /* FIXME, use pad blocking */ - data->sigid = gst_pad_add_probe (pad, GST_PROBE_TYPE_DATA, pad_probe, - decode_bin, NULL); - decode_bin->numwaiting++; - - decode_bin->probes = g_list_append (decode_bin->probes, data); - - GST_LOG_OBJECT (element, "closed pad %s", padname); - - /* our own signal with an extra flag that this is the only pad */ - GST_DEBUG_OBJECT (decode_bin, "emitting new-decoded-pad"); - g_signal_emit (G_OBJECT (decode_bin), - gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, !more); - GST_DEBUG_OBJECT (decode_bin, "emitted new-decoded-pad"); - - g_free (padname); - - /* If we're at a demuxer element pad was set to a queue's - * srcpad and must be unref'd here */ - if (is_demuxer_element (element)) - gst_object_unref (pad); - } else { - GList *to_try; - - /* if the caps has many types, we need to delay */ - if (!gst_caps_is_fixed (caps)) - goto many_types; - - /* continue plugging, first find all compatible elements */ - to_try = find_compatibles (decode_bin, caps); - if (to_try == NULL) - /* no compatible elements, we cannot go on */ - goto unknown_type; - - if (try_to_link_1 (decode_bin, element, pad, to_try) == NULL) { - g_list_free (to_try); - GST_LOG_OBJECT (pad, "none of the allegedly available elements usable"); - goto unknown_type; - } - - /* can free the list again now */ - g_list_free (to_try); - } - return; - - /* ERRORS */ -unknown_type: - { - GST_LOG_OBJECT (pad, "unknown type found, fire signal"); - g_signal_emit (G_OBJECT (decode_bin), - gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); - - gst_element_post_message (GST_ELEMENT_CAST (decode_bin), - gst_missing_decoder_message_new (GST_ELEMENT_CAST (decode_bin), caps)); - - if (element == decode_bin->typefind) { - gchar *desc; - - desc = gst_pb_utils_get_decoder_description (caps); - GST_ELEMENT_ERROR (decode_bin, STREAM, CODEC_NOT_FOUND, - (_("A %s plugin is required to play this stream, but not installed."), - desc), - ("No decoder to handle media type '%s'", - gst_structure_get_name (gst_caps_get_structure (caps, 0)))); - g_free (desc); - } - - return; - } -dont_know_yet: - { - GST_LOG_OBJECT (pad, "type is not known yet"); - goto setup_caps_delay; - } -many_types: - { - GST_LOG_OBJECT (pad, "many possible types"); - goto setup_caps_delay; - } -setup_caps_delay: - { - GST_LOG_OBJECT (pad, "setting up a delayed link"); - dynamic_add (element, pad, decode_bin); - return; - } -} - -/* Decide whether an element is a demuxer based on the - * klass and number/type of src pad templates it has */ -static gboolean -is_demuxer_element (GstElement * srcelement) -{ - GstElementFactory *srcfactory; - GstElementClass *elemclass; - GList *walk; - const gchar *klass; - gint potential_src_pads = 0; - - srcfactory = gst_element_get_factory (srcelement); - klass = gst_element_factory_get_klass (srcfactory); - - /* Can't be a demuxer unless it has Demux in the klass name */ - if (klass == NULL || !strstr (klass, "Demux")) - return FALSE; - - /* Walk the src pad templates and count how many the element - * might produce */ - elemclass = GST_ELEMENT_GET_CLASS (srcelement); - - walk = gst_element_class_get_pad_template_list (elemclass); - while (walk != NULL) { - GstPadTemplate *templ; - - templ = (GstPadTemplate *) walk->data; - if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) { - switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { - case GST_PAD_ALWAYS: - case GST_PAD_SOMETIMES: - if (strstr (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ), "%")) - potential_src_pads += 2; /* Might make multiple pads */ - else - potential_src_pads += 1; - break; - case GST_PAD_REQUEST: - potential_src_pads += 2; - break; - } - } - walk = g_list_next (walk); - } - - if (potential_src_pads < 2) - return FALSE; - - return TRUE; -} - -/* - * given a list of element factories, try to link one of the factories - * to the given pad. - * - * The function returns the element that was successfully linked to the - * pad. - */ -static GstElement * -try_to_link_1 (GstDecodeBin * decode_bin, GstElement * srcelement, GstPad * pad, - GList * factories) -{ - GList *walk; - GstElement *result = NULL; - gboolean isdemux = FALSE; - GstPad *queuesinkpad = NULL, *queuesrcpad = NULL; - GstElement *queue = NULL; - GstPad *usedsrcpad = pad; - - /* Check if the parent of the src pad is a demuxer */ - isdemux = is_demuxer_element (srcelement); - - if (isdemux && factories != NULL) { - GstPadLinkReturn dqlink; - - /* Insert a queue between demuxer and decoder */ - GST_DEBUG_OBJECT (decode_bin, - "Element %s is a demuxer, inserting a queue", - GST_OBJECT_NAME (srcelement)); - queue = gst_element_factory_make ("queue", NULL); - decode_bin->queue_type = G_OBJECT_TYPE (queue); - - g_object_set (G_OBJECT (queue), "max-size-buffers", 0, NULL); - g_object_set (G_OBJECT (queue), "max-size-time", G_GINT64_CONSTANT (0), - NULL); - g_object_set (G_OBJECT (queue), "max-size-bytes", 8192, NULL); - gst_bin_add (GST_BIN (decode_bin), queue); - gst_element_set_state (queue, GST_STATE_READY); - queuesinkpad = gst_element_get_static_pad (queue, "sink"); - usedsrcpad = queuesrcpad = gst_element_get_static_pad (queue, "src"); - - dqlink = gst_pad_link (pad, queuesinkpad); - g_return_val_if_fail (dqlink == GST_PAD_LINK_OK, NULL); - } - - /* loop over the factories */ - for (walk = factories; walk; walk = g_list_next (walk)) { - GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data); - GstElement *element; - GstPadLinkReturn ret; - GstPad *sinkpad; - - GST_DEBUG_OBJECT (decode_bin, "trying to link %s", - gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); - - /* make an element from the factory first */ - if ((element = gst_element_factory_create (factory, NULL)) == NULL) { - /* hmm, strange. Like with all things in life, let's move on.. */ - GST_WARNING_OBJECT (decode_bin, "could not create an element from %s", - gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); - continue; - } - - /* try to link the given pad to a sinkpad */ - /* FIXME, find the sinkpad by looping over the pads instead of - * looking it up by name */ - if ((sinkpad = gst_element_get_static_pad (element, "sink")) == NULL) { - /* if no pad is found we can't do anything */ - GST_WARNING_OBJECT (decode_bin, "could not find sinkpad in element"); - continue; - } - - /* now add the element to the bin first */ - GST_DEBUG_OBJECT (decode_bin, "adding %s", GST_OBJECT_NAME (element)); - gst_bin_add (GST_BIN (decode_bin), element); - - /* set to READY first so it is ready, duh. */ - if (gst_element_set_state (element, - GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) { - GST_WARNING_OBJECT (decode_bin, "Couldn't set %s to READY", - GST_ELEMENT_NAME (element)); - /* get rid of the sinkpad */ - gst_object_unref (sinkpad); - /* this element did not work, remove it again and continue trying - * other elements, the element will be disposed. */ - /* FIXME: shouldn't we do this before adding it to the bin so that no - * error messages get through to the app? (tpm) */ - gst_bin_remove (GST_BIN (decode_bin), element); - continue; - } - - if ((ret = gst_pad_link (usedsrcpad, sinkpad)) != GST_PAD_LINK_OK) { - GST_DEBUG_OBJECT (decode_bin, "link failed on pad %s:%s, reason %d", - GST_DEBUG_PAD_NAME (pad), ret); - /* get rid of the sinkpad */ - gst_object_unref (sinkpad); - /* this element did not work, remove it again and continue trying - * other elements, the element will be disposed. */ - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), element); - } else { - GST_DEBUG_OBJECT (decode_bin, "linked on pad %s:%s", - GST_DEBUG_PAD_NAME (usedsrcpad)); - - /* configure the queue some more */ - if (queue != NULL) { - decode_bin->queues = g_list_append (decode_bin->queues, queue); - g_signal_connect (G_OBJECT (queue), - "overrun", G_CALLBACK (queue_filled_cb), decode_bin); - g_signal_connect (G_OBJECT (queue), - "underrun", G_CALLBACK (queue_underrun_cb), decode_bin); - } - - /* The link worked, now figure out what it was that we connected */ - - /* make sure we catch unlink signals */ - g_signal_connect (G_OBJECT (pad), "unlinked", - G_CALLBACK (unlinked), decode_bin); - - /* now that we added the element we can try to continue autoplugging - * on it until we have a raw type */ - close_link (element, decode_bin); - - /* change the state of the element to that of the parent */ - if ((gst_element_set_state (element, - GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) { - GST_WARNING_OBJECT (decode_bin, "Couldn't set %s to PAUSED", - GST_ELEMENT_NAME (element)); - /* close_link -> close_pad_link -> might have set up a pad probe */ - free_pad_probe_for_element (decode_bin, element); - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), element); - continue; - } - - result = element; - - /* get rid of the sinkpad now */ - gst_object_unref (sinkpad); - - /* Set the queue to paused and set the pointer to NULL so we don't - * remove it below */ - if (queue != NULL) { - gst_element_set_state (queue, GST_STATE_PAUSED); - queue = NULL; - gst_object_unref (queuesrcpad); - gst_object_unref (queuesinkpad); - } - - /* and exit */ - goto done; - } - } -done: - if (queue != NULL) { - /* We didn't successfully connect to the queue */ - gst_pad_unlink (pad, queuesinkpad); - gst_element_set_state (queue, GST_STATE_NULL); - gst_object_unref (queuesrcpad); - gst_object_unref (queuesinkpad); - gst_bin_remove (GST_BIN (decode_bin), queue); - } - return result; -} - -static GstPad * -get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad) -{ - GstIterator *pad_it = NULL; - GValue item = { 0, }; - GstPad *db_pad = NULL; - gboolean done = FALSE; - - if (pad == NULL || !GST_PAD_IS_SRC (pad)) { - GST_DEBUG_OBJECT (decode_bin, "pad NULL or not SRC pad"); - return NULL; - } - - /* our ghostpads are the sourcepads */ - pad_it = gst_element_iterate_src_pads (GST_ELEMENT (decode_bin)); - while (!done) { - db_pad = NULL; - switch (gst_iterator_next (pad_it, &item)) { - case GST_ITERATOR_OK:{ - db_pad = g_value_get_object (&item); - GST_DEBUG_OBJECT (decode_bin, "looking at pad %s:%s", - GST_DEBUG_PAD_NAME (db_pad)); - if (GST_IS_GHOST_PAD (db_pad)) { - GstPad *target_pad = NULL; - - target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (db_pad)); - done = (target_pad == pad); - if (target_pad) - gst_object_unref (target_pad); - - if (done) { - /* Found our ghost pad */ - GST_DEBUG_OBJECT (decode_bin, "found ghostpad %s:%s for pad %s:%s", - GST_DEBUG_PAD_NAME (db_pad), GST_DEBUG_PAD_NAME (pad)); - break; - } - } - /* Not the right one */ - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pad_it); - break; - case GST_ITERATOR_ERROR: - done = TRUE; - break; - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (pad_it); - - return db_pad; -} - -/* remove all downstream elements starting from the given pad. - * Also make sure to remove the ghostpad we created for the raw - * decoded stream. - */ -static void -remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad) -{ - GstIterator *iter; - gboolean done = FALSE; - GValue item = { 0, }; - GstElement *elem = GST_ELEMENT (GST_OBJECT_PARENT (pad)); - - while (GST_OBJECT_PARENT (elem) && - GST_OBJECT_PARENT (elem) != GST_OBJECT (decode_bin)) - elem = GST_ELEMENT (GST_OBJECT_PARENT (elem)); - - if (G_OBJECT_TYPE (elem) == decode_bin->queue_type) { - GST_DEBUG_OBJECT (decode_bin, - "Encountered demuxer output queue while removing element chain"); - decode_bin->queues = g_list_remove (decode_bin->queues, elem); - } - - GST_DEBUG_OBJECT (decode_bin, "%s:%s", GST_DEBUG_PAD_NAME (pad)); - iter = gst_pad_iterate_internal_links (pad); - if (!iter) - goto no_iter; - - /* remove all elements linked to this pad up to the ghostpad - * that we created for this stream */ - while (!done) { - switch (gst_iterator_next (iter, &item)) { - case GST_ITERATOR_OK:{ - GstPad *pad; - GstPad *ghostpad; - GstPad *peer; - - pad = g_value_get_object (&item); - GST_DEBUG_OBJECT (decode_bin, "inspecting internal pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - - ghostpad = get_our_ghost_pad (decode_bin, pad); - if (ghostpad) { - GST_DEBUG_OBJECT (decode_bin, "found our ghost pad %s:%s for %s:%s", - GST_DEBUG_PAD_NAME (ghostpad), GST_DEBUG_PAD_NAME (pad)); - - g_signal_emit (G_OBJECT (decode_bin), - gst_decode_bin_signals[SIGNAL_REMOVED_DECODED_PAD], 0, ghostpad); - - gst_element_remove_pad (GST_ELEMENT (decode_bin), ghostpad); - gst_object_unref (ghostpad); - g_value_reset (&item); - continue; - } else { - GST_DEBUG_OBJECT (decode_bin, "not one of our ghostpads"); - } - - peer = gst_pad_get_peer (pad); - if (peer) { - GstObject *parent = gst_pad_get_parent (peer); - - GST_DEBUG_OBJECT (decode_bin, - "internal pad %s:%s linked to pad %s:%s", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer)); - - if (parent) { - GstObject *grandparent = gst_object_get_parent (parent); - - if (grandparent != NULL) { - if (GST_ELEMENT (grandparent) != GST_ELEMENT (decode_bin)) { - GST_DEBUG_OBJECT (decode_bin, "dead end pad %s:%s parent %s", - GST_DEBUG_PAD_NAME (peer), GST_OBJECT_NAME (grandparent)); - } else { - GST_DEBUG_OBJECT (decode_bin, - "recursing element %s on pad %s:%s", - GST_ELEMENT_NAME (elem), GST_DEBUG_PAD_NAME (pad)); - remove_element_chain (decode_bin, peer); - } - gst_object_unref (grandparent); - } - gst_object_unref (parent); - } - gst_object_unref (peer); - } - g_value_reset (&item); - } - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (iter); - break; - case GST_ITERATOR_ERROR: - GST_ERROR_OBJECT (pad, "Could not iterate over internally linked pads"); - done = TRUE; - break; - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - } - GST_DEBUG_OBJECT (decode_bin, "removing %s", GST_ELEMENT_NAME (elem)); - g_value_unset (&item); - gst_iterator_free (iter); - -no_iter: - gst_element_set_state (elem, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), elem); -} - -/* there are @bytes bytes in @queue, enlarge it - * - * Returns: new max number of bytes in @queue - */ -static guint -queue_enlarge (GstElement * queue, guint bytes, GstDecodeBin * decode_bin) -{ - /* Increase the queue size by 1Mbyte if it is over 1Mb, else double its current limit - */ - if (bytes > 1024 * 1024) - bytes += 1024 * 1024; - else - bytes *= 2; - - GST_DEBUG_OBJECT (decode_bin, - "increasing queue %s max-size-bytes to %d", GST_ELEMENT_NAME (queue), - bytes); - g_object_set (G_OBJECT (queue), "max-size-bytes", bytes, NULL); - - return bytes; -} - -/* this callback is called when our queues fills up or are empty - * We then check the status of all other queues to make sure we - * never have an empty and full queue at the same time since that - * would block dataflow. In the case of a filled queue, we make - * it larger. - */ -static void -queue_underrun_cb (GstElement * queue, GstDecodeBin * decode_bin) -{ - /* FIXME: we don't really do anything here for now. Ideally we should - * see if some of the queues are filled and increase their values - * in that case. - * Note: be very carefull with thread safety here as this underrun - * signal is done from the streaming thread of queue srcpad which - * is different from the pad_added (where we add the queue to the - * list) and the overrun signals that are signalled from the - * demuxer thread. - */ - GST_DEBUG_OBJECT (decode_bin, "got underrun"); -} - -/* Make sure we don't have a full queue and empty queue situation */ -static void -queue_filled_cb (GstElement * queue, GstDecodeBin * decode_bin) -{ - GList *tmp; - gboolean increase = FALSE; - guint bytes; - - /* get current byte level from the queue that is filled */ - g_object_get (G_OBJECT (queue), "current-level-bytes", &bytes, NULL); - GST_DEBUG_OBJECT (decode_bin, "One of the queues is full at %d bytes", bytes); - - /* we do not buffer more than 20Mb */ - if (bytes > (20 * 1024 * 1024)) - goto too_large; - - /* check all other queue to see if one is empty, in that case - * we need to enlarge @queue */ - for (tmp = decode_bin->queues; tmp; tmp = g_list_next (tmp)) { - GstElement *aqueue = GST_ELEMENT (tmp->data); - guint levelbytes = 0; - - if (aqueue != queue) { - g_object_get (G_OBJECT (aqueue), "current-level-bytes", &levelbytes, - NULL); - if (levelbytes == 0) { - /* yup, found an empty queue, we can stop the search and - * need to enlarge the queue */ - increase = TRUE; - break; - } - } - } - - if (increase) { - /* enlarge @queue */ - queue_enlarge (queue, bytes, decode_bin); - } else { - GST_DEBUG_OBJECT (decode_bin, - "Queue is full but other queues are not empty, not doing anything"); - } - return; - - /* errors */ -too_large: - { - GST_WARNING_OBJECT (decode_bin, - "Queue is bigger than 20Mbytes, something else is going wrong"); - return; - } -} - -/* This function will be called when a dynamic pad is created on an element. - * We try to continue autoplugging on this new pad. */ -static void -new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic) -{ - GstDecodeBin *decode_bin = dynamic->decode_bin; - GstCaps *caps; - gboolean more; - - GST_OBJECT_LOCK (decode_bin); - if (decode_bin->shutting_down) - goto shutting_down1; - GST_OBJECT_UNLOCK (decode_bin); - - GST_STATE_LOCK (decode_bin); - if (decode_bin->shutting_down) - goto shutting_down2; - - /* see if any more pending dynamic connections exist */ - more = gst_decode_bin_is_dynamic (decode_bin); - - caps = gst_pad_get_caps (pad, NULL); - close_pad_link (element, pad, caps, decode_bin, more); - if (caps) - gst_caps_unref (caps); - GST_STATE_UNLOCK (decode_bin); - - return; - -shutting_down1: - { - GST_DEBUG_OBJECT (decode_bin, "we are shutting down"); - GST_OBJECT_UNLOCK (decode_bin); - return; - } -shutting_down2: - { - GST_DEBUG_OBJECT (decode_bin, "we are shutting down"); - GST_STATE_UNLOCK (decode_bin); - return; - } -} - -static void -dynamic_remove (GstDynamic * dynamic) -{ - GstDecodeBin *decode_bin = dynamic->decode_bin; - - /* remove the dynamic from the list of dynamics */ - decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic); - dynamic_free (dynamic); - - /* if we have no more dynamic elements, we have no chance of creating - * more pads, so we fire the no_more_pads signal */ - if (decode_bin->dynamics == NULL) { - if (decode_bin->numwaiting == 0) { - GST_DEBUG_OBJECT (decode_bin, - "no more dynamic elements, removing fakesink"); - remove_fakesink (decode_bin); - } - GST_DEBUG_OBJECT (decode_bin, - "no more dynamic elements, signaling no_more_pads"); - gst_element_no_more_pads (GST_ELEMENT (decode_bin)); - } else { - GST_DEBUG_OBJECT (decode_bin, "we have more dynamic elements"); - } -} - -/* this signal is fired when an element signals the no_more_pads signal. - * This means that the element will not generate more dynamic pads and - * we can remove the element from the list of dynamic elements. When we - * have no more dynamic elements in the pipeline, we can fire a no_more_pads - * signal ourselves. */ -static void -no_more_pads (GstElement * element, GstDynamic * dynamic) -{ - GST_DEBUG_OBJECT (dynamic->decode_bin, "no more pads on element %s", - GST_ELEMENT_NAME (element)); - - dynamic_remove (dynamic); -} - -static void -new_caps (GstPad * pad, GParamSpec * unused, GstDynamic * dynamic) -{ - GST_DEBUG_OBJECT (dynamic->decode_bin, "delayed link triggered"); - - new_pad (dynamic->element, pad, dynamic); - - /* assume it worked and remove the dynamic */ - dynamic_remove (dynamic); - - return; -} - -static gboolean -is_our_kid (GstElement * e, GstDecodeBin * decode_bin) -{ - gboolean ret; - GstElement *parent; - - parent = (GstElement *) gst_object_get_parent ((GstObject *) e); - ret = (parent == (GstElement *) decode_bin); - - if (parent) - gst_object_unref ((GstObject *) parent); - - return ret; -} - -static gboolean -elem_is_dynamic (GstElement * element, GstDecodeBin * decode_bin) -{ - GList *pads; - - /* loop over all the padtemplates */ - for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads; - pads = g_list_next (pads)) { - GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data); - const gchar *templ_name; - - /* we are only interested in source pads */ - if (GST_PAD_TEMPLATE_DIRECTION (templ) != GST_PAD_SRC) - continue; - - templ_name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ); - GST_DEBUG_OBJECT (decode_bin, "got a source pad template %s", templ_name); - - /* figure out what kind of pad this is */ - switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { - case GST_PAD_SOMETIMES: - { - /* try to get the pad to see if it is already created or - * not */ - GstPad *pad = gst_element_get_static_pad (element, templ_name); - - if (pad) { - GST_DEBUG_OBJECT (decode_bin, "got the pad for sometimes template %s", - templ_name); - gst_object_unref (pad); - } else { - GST_DEBUG_OBJECT (decode_bin, - "did not get the sometimes pad of template %s", templ_name); - /* we have an element that will create dynamic pads */ - return TRUE; - } - break; - } - default: - /* Don't care about ALWAYS or REQUEST pads */ - break; - } - } - return FALSE; -} - -/* This function will be called when a pad is disconnected for some reason */ -static void -unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin) -{ - GstElement *element, *peer; - - /* inactivate pad */ - gst_pad_set_active (pad, GST_ACTIVATE_NONE); - - peer = gst_pad_get_parent_element (peerpad); - - if (!is_our_kid (peer, decode_bin)) - goto exit; - - GST_DEBUG_OBJECT (decode_bin, "pad %s:%s removal while alive - chained?", - GST_DEBUG_PAD_NAME (pad)); - - /* remove all elements linked to the peerpad */ - remove_element_chain (decode_bin, peerpad); - - /* Re-add as a dynamic element if needed, via close_link */ - element = gst_pad_get_parent_element (pad); - if (element) { - if (elem_is_dynamic (element, decode_bin)) - dynamic_add (element, NULL, decode_bin); - - gst_object_unref (element); - } - -exit: - gst_object_unref (peer); -} - -/* this function inspects the given element and tries to connect something - * on the srcpads. If there are dynamic pads, it sets up a signal handler to - * continue autoplugging when they become available */ -static void -close_link (GstElement * element, GstDecodeBin * decode_bin) -{ - GList *pads; - gboolean dynamic = FALSE; - GList *to_connect = NULL; - gboolean more; - - GST_DEBUG_OBJECT (decode_bin, "closing links with element %s", - GST_ELEMENT_NAME (element)); - - /* loop over all the padtemplates */ - for (pads = GST_ELEMENT_GET_CLASS (element)->padtemplates; pads; - pads = g_list_next (pads)) { - GstPadTemplate *templ = GST_PAD_TEMPLATE (pads->data); - const gchar *templ_name; - - /* we are only interested in source pads */ - if (GST_PAD_TEMPLATE_DIRECTION (templ) != GST_PAD_SRC) - continue; - - templ_name = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ); - GST_DEBUG_OBJECT (decode_bin, "got a source pad template %s", templ_name); - - /* figure out what kind of pad this is */ - switch (GST_PAD_TEMPLATE_PRESENCE (templ)) { - case GST_PAD_ALWAYS: - { - /* get the pad that we need to autoplug */ - GstPad *pad = gst_element_get_static_pad (element, templ_name); - - if (pad) { - GST_DEBUG_OBJECT (decode_bin, "got the pad for always template %s", - templ_name); - /* here is the pad, we need to autoplug it */ - to_connect = g_list_prepend (to_connect, pad); - } else { - /* strange, pad is marked as always but it's not - * there. Fix the element */ - GST_WARNING_OBJECT (decode_bin, - "could not get the pad for always template %s", templ_name); - } - break; - } - case GST_PAD_SOMETIMES: - { - /* try to get the pad to see if it is already created or - * not */ - GstPad *pad = gst_element_get_static_pad (element, templ_name); - - if (pad) { - GST_DEBUG_OBJECT (decode_bin, "got the pad for sometimes template %s", - templ_name); - /* the pad is created, we need to autoplug it */ - to_connect = g_list_prepend (to_connect, pad); - } else { - GST_DEBUG_OBJECT (decode_bin, - "did not get the sometimes pad of template %s", templ_name); - /* we have an element that will create dynamic pads */ - dynamic = TRUE; - } - break; - } - case GST_PAD_REQUEST: - /* ignore request pads */ - GST_DEBUG_OBJECT (decode_bin, "ignoring request padtemplate %s", - templ_name); - break; - } - } - if (dynamic) { - GST_DEBUG_OBJECT (decode_bin, "got a dynamic element here"); - /* ok, this element has dynamic pads, set up the signal handlers to be - * notified of them */ - - dynamic_add (element, NULL, decode_bin); - } - - /* Check if this is an element with more than 1 pad. If this element - * has more than 1 pad, we need to be carefull not to signal the - * no_more_pads signal after connecting the first pad. */ - more = g_list_length (to_connect) > 1; - - /* now loop over all the pads we need to connect */ - for (pads = to_connect; pads; pads = g_list_next (pads)) { - GstPad *pad = GST_PAD_CAST (pads->data); - GstCaps *caps; - - /* we have more pads if we have more than 1 pad to connect or - * dynamics. If we have only 1 pad and no dynamics, more will be - * set to FALSE and the no-more-pads signal will be fired. Note - * that this can change after the close_pad_link call. */ - more |= gst_decode_bin_is_dynamic (decode_bin); - - GST_DEBUG_OBJECT (decode_bin, "closing pad link for %s", - GST_OBJECT_NAME (pad)); - - /* continue autoplugging on the pads */ - caps = gst_pad_get_caps (pad, NULL); - close_pad_link (element, pad, caps, decode_bin, more); - if (caps) - gst_caps_unref (caps); - - gst_object_unref (pad); - } - g_list_free (to_connect); -} - -/* this is the signal handler for the typefind element have_type signal. - * It tries to continue autoplugging on the typefind src pad */ -static void -type_found (GstElement * typefind, guint probability, GstCaps * caps, - GstDecodeBin * decode_bin) -{ - gboolean dynamic; - GstPad *pad; - - GST_DEBUG_OBJECT (decode_bin, "typefind found caps %" GST_PTR_FORMAT, caps); - - GST_OBJECT_LOCK (decode_bin); - if (decode_bin->shutting_down) - goto shutting_down; - GST_OBJECT_UNLOCK (decode_bin); - - GST_STATE_LOCK (decode_bin); - if (decode_bin->shutting_down) - goto exit; - - /* don't need the typefind anymore if we already found a type, we're not going - * to be able to do anything with it anyway except for generating errors */ - if (decode_bin->have_type) - goto exit; - - decode_bin->have_type = TRUE; - - /* special-case text/plain: we only want to accept it as a raw type if it - * comes from a subtitle parser element or a demuxer, but not if it is the - * type of the entire stream, in which case we just want to error out */ - if (typefind == decode_bin->typefind && - gst_structure_has_name (gst_caps_get_structure (caps, 0), "text/plain")) { - gst_element_no_more_pads (GST_ELEMENT (decode_bin)); - /* we can't handle this type of stream */ - GST_ELEMENT_ERROR (decode_bin, STREAM, WRONG_TYPE, - (_("This appears to be a text file")), - ("decodebin cannot decode plain text files")); - goto exit; - } - - /* autoplug the new pad with the caps that the signal gave us. */ - pad = gst_element_get_static_pad (typefind, "src"); - close_pad_link (typefind, pad, caps, decode_bin, FALSE); - gst_object_unref (pad); - - dynamic = gst_decode_bin_is_dynamic (decode_bin); - if (dynamic == FALSE) { - GST_DEBUG_OBJECT (decode_bin, "we have no dynamic elements anymore"); - /* if we have no dynamic elements, we know that no new pads - * will be created and we can signal out no_more_pads signal */ - gst_element_no_more_pads (GST_ELEMENT (decode_bin)); - } else { - /* more dynamic elements exist that could create new pads */ - GST_DEBUG_OBJECT (decode_bin, "we have more dynamic elements"); - } - -exit: - GST_STATE_UNLOCK (decode_bin); - return; - -shutting_down: - { - GST_DEBUG_OBJECT (decode_bin, "we are shutting down"); - GST_OBJECT_UNLOCK (decode_bin); - return; - } -} - -static void -disconnect_unlinked_signals (GstDecodeBin * decode_bin, GstElement * element) -{ - GstIterator *pad_it = NULL; - gboolean done = FALSE; - GstPad *pad = NULL; - GValue item = { 0, }; - - pad_it = gst_element_iterate_src_pads (element); - while (!done) { - switch (gst_iterator_next (pad_it, &item)) { - case GST_ITERATOR_OK: - pad = g_value_get_object (&item); - g_signal_handlers_disconnect_by_func (pad, (gpointer) unlinked, - decode_bin); - g_value_reset (&item); - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pad_it); - break; - default: - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (pad_it); -} - - -static void -cleanup_decodebin (GstDecodeBin * decode_bin) -{ - GstIterator *elem_it = NULL, *gpad_it = NULL; - GstPad *typefind_pad = NULL; - gboolean done = FALSE; - GstElement *element = NULL; - GstPad *pad = NULL; - GValue item = { 0, }; - - g_return_if_fail (GST_IS_DECODE_BIN (decode_bin)); - - GST_DEBUG_OBJECT (decode_bin, "cleaning up decodebin"); - - typefind_pad = gst_element_get_static_pad (decode_bin->typefind, "src"); - if (GST_IS_PAD (typefind_pad)) { - g_signal_handlers_block_by_func (typefind_pad, (gpointer) unlinked, - decode_bin); - } - - elem_it = gst_bin_iterate_elements (GST_BIN (decode_bin)); - while (!done) { - switch (gst_iterator_next (elem_it, &item)) { - case GST_ITERATOR_OK: - element = g_value_get_object (&item); - if (element != decode_bin->typefind && element != decode_bin->fakesink) { - GST_DEBUG_OBJECT (element, "removing autoplugged element"); - disconnect_unlinked_signals (decode_bin, element); - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (decode_bin), element); - } - g_value_reset (&item); - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (elem_it); - break; - case GST_ITERATOR_ERROR: - done = TRUE; - break; - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (elem_it); - - done = FALSE; - gpad_it = gst_element_iterate_pads (GST_ELEMENT (decode_bin)); - while (!done) { - switch (gst_iterator_next (gpad_it, &item)) { - case GST_ITERATOR_OK: - pad = g_value_get_object (&item); - GST_DEBUG_OBJECT (pad, "inspecting pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - if (GST_IS_GHOST_PAD (pad) && GST_PAD_IS_SRC (pad)) { - GST_DEBUG_OBJECT (pad, "removing ghost pad"); - gst_element_remove_pad (GST_ELEMENT (decode_bin), pad); - } - g_value_reset (&item); - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (gpad_it); - break; - case GST_ITERATOR_ERROR: - done = TRUE; - break; - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - } - g_value_unset (&item); - gst_iterator_free (gpad_it); - - if (GST_IS_PAD (typefind_pad)) { - g_signal_handlers_unblock_by_func (typefind_pad, (gpointer) unlinked, - decode_bin); - g_signal_handlers_disconnect_by_func (typefind_pad, (gpointer) unlinked, - decode_bin); - gst_object_unref (typefind_pad); - } -} - -static GstStateChangeReturn -gst_decode_bin_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstDecodeBin *decode_bin; - - decode_bin = GST_DECODE_BIN (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - decode_bin->numpads = 0; - decode_bin->numwaiting = 0; - decode_bin->dynamics = NULL; - if (decode_bin->typefind == NULL) - goto missing_typefind; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_OBJECT_LOCK (decode_bin); - decode_bin->shutting_down = FALSE; - decode_bin->have_type = FALSE; - GST_OBJECT_UNLOCK (decode_bin); - - if (!add_fakesink (decode_bin)) - goto missing_fakesink; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_OBJECT_LOCK (decode_bin); - decode_bin->shutting_down = TRUE; - GST_OBJECT_UNLOCK (decode_bin); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: - free_dynamics (decode_bin); - free_pad_probes (decode_bin); - cleanup_decodebin (decode_bin); - break; - default: - break; - } - - return ret; -/* ERRORS */ -missing_typefind: - { - gst_element_post_message (element, - gst_missing_element_message_new (element, "typefind")); - GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, (NULL), ("no typefind!")); - return GST_STATE_CHANGE_FAILURE; - } -missing_fakesink: - { - gst_element_post_message (element, - gst_missing_element_message_new (element, "fakesink")); - GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, (NULL), ("no fakesink!")); - return GST_STATE_CHANGE_FAILURE; - } -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_decode_bin_debug, "decodebin", 0, "decoder bin"); - -#ifdef ENABLE_NLS - 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, "decodebin", GST_RANK_NONE, - GST_TYPE_DECODE_BIN); -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "decodebin", - "decoder bin", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN) diff --git a/gst/playback/gstplayback.c b/gst/playback/gstplayback.c index a83fee1..f7a0432 100644 --- a/gst/playback/gstplayback.c +++ b/gst/playback/gstplayback.c @@ -30,7 +30,6 @@ #include "gstplayback.h" #include "gstplaysink.h" #include "gststreamselector.h" -#include "gststreaminfo.h" #include "gstsubtitleoverlay.h" static gboolean @@ -49,11 +48,9 @@ plugin_init (GstPlugin * plugin) /* ref class from a thread-safe context to work around missing bit of * thread-safety in GObject */ - g_type_class_ref (GST_TYPE_STREAM_INFO); g_type_class_ref (GST_TYPE_STREAM_SELECTOR); - res = gst_play_bin_plugin_init (plugin); - res &= gst_play_bin2_plugin_init (plugin); + res = gst_play_bin2_plugin_init (plugin); res &= gst_play_sink_plugin_init (plugin); res &= gst_subtitle_overlay_plugin_init (plugin); diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c deleted file mode 100644 index 492977a..0000000 --- a/gst/playback/gstplaybasebin.c +++ /dev/null @@ -1,2823 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include "gstplaybasebin.h" -#include "gststreamselector.h" -#include "gstplay-marshal.h" - -#include - -GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); -#define GST_CAT_DEFAULT gst_play_base_bin_debug - -#define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) -#define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100) -#define DEFAULT_QUEUE_THRESHOLD ((DEFAULT_QUEUE_SIZE * 95) / 100) -#define DEFAULT_CONNECTION_SPEED 0 - -#define GROUP_LOCK(pbb) g_mutex_lock (pbb->group_lock) -#define GROUP_UNLOCK(pbb) g_mutex_unlock (pbb->group_lock) -#define GROUP_WAIT(pbb) g_cond_wait (pbb->group_cond, pbb->group_lock) -#define GROUP_SIGNAL(pbb) g_cond_signal (pbb->group_cond) - -/* props */ -enum -{ - ARG_0, - ARG_URI, - ARG_SUBURI, - ARG_QUEUE_SIZE, - ARG_QUEUE_THRESHOLD, - ARG_QUEUE_MIN_THRESHOLD, - ARG_NSTREAMS, - ARG_STREAMINFO, - ARG_STREAMINFO_VALUES, - ARG_SOURCE, - ARG_VIDEO, - ARG_AUDIO, - ARG_TEXT, - ARG_SUBTITLE_ENCODING, - ARG_CONNECTION_SPEED -}; - -static void gst_play_base_bin_class_init (GstPlayBaseBinClass * klass); -static void gst_play_base_bin_init (GstPlayBaseBin * play_base_bin); -static void gst_play_base_bin_dispose (GObject * object); -static void gst_play_base_bin_finalize (GObject * object); - -static void gst_play_base_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * spec); -static void gst_play_base_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * spec); -static void gst_play_base_bin_handle_message_func (GstBin * bin, - GstMessage * msg); - -static GstStateChangeReturn gst_play_base_bin_change_state (GstElement * - element, GstStateChange transition); - -static const GList *gst_play_base_bin_get_streaminfo (GstPlayBaseBin * bin); -static GValueArray *gst_play_base_bin_get_streaminfo_value_array (GstPlayBaseBin - * play_base_bin); -static void preroll_remove_overrun (GstElement * element, - GstPlayBaseBin * play_base_bin); -static void queue_remove_probe (GstElement * queue, GstPlayBaseBin - * play_base_bin); - -static GstElement *make_decoder (GstPlayBaseBin * play_base_bin); -static gboolean has_all_raw_caps (GstPad * pad, gboolean * all_raw); - -static gboolean prepare_output (GstPlayBaseBin * play_base_bin); -static void set_active_source (GstPlayBaseBin * play_base_bin, - GstStreamType type, gint source_num); -static GstProbeReturn probe_triggered (GstPad * pad, GstProbeType type, - gpointer type_data, gpointer user_data); -static void setup_substreams (GstPlayBaseBin * play_base_bin); - -static GstPipelineClass *parent_class; - -/* - * GObject playbasebin wrappers. - */ - -GType -gst_play_base_bin_get_type (void) -{ - static GType gst_play_base_bin_type = 0; - - if (!gst_play_base_bin_type) { - static const GTypeInfo gst_play_base_bin_info = { - sizeof (GstPlayBaseBinClass), - NULL, - NULL, - (GClassInitFunc) gst_play_base_bin_class_init, - NULL, - NULL, - sizeof (GstPlayBaseBin), - 0, - (GInstanceInitFunc) gst_play_base_bin_init, - NULL - }; - - gst_play_base_bin_type = g_type_register_static (GST_TYPE_PIPELINE, - "GstPlayBaseBin", &gst_play_base_bin_info, 0); - } - - return gst_play_base_bin_type; -} - -static void -gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - GstBinClass *gstbin_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - gstbin_klass = (GstBinClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_klass->set_property = gst_play_base_bin_set_property; - gobject_klass->get_property = gst_play_base_bin_get_property; - - g_object_class_install_property (gobject_klass, ARG_URI, - g_param_spec_string ("uri", "URI", "URI of the media to play", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_SUBURI, - g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE, - g_param_spec_uint64 ("queue-size", "Queue size", - "Size of internal queues in nanoseconds", 0, G_MAXINT64, - DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_QUEUE_THRESHOLD, - g_param_spec_uint64 ("queue-threshold", "Queue threshold", - "Buffering threshold of internal queues in nanoseconds", 0, - G_MAXINT64, DEFAULT_QUEUE_THRESHOLD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_QUEUE_MIN_THRESHOLD, - g_param_spec_uint64 ("queue-min-threshold", "Queue min threshold", - "Buffering low threshold of internal queues in nanoseconds", 0, - G_MAXINT64, DEFAULT_QUEUE_MIN_THRESHOLD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, ARG_NSTREAMS, - g_param_spec_int ("nstreams", "NStreams", "number of streams", - 0, G_MAXINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_STREAMINFO, - g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo", - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_STREAMINFO_VALUES, - g_param_spec_value_array ("stream-info-value-array", - "StreamInfo GValueArray", "value array of streaminfo", - g_param_spec_object ("streaminfo", "StreamInfo", "Streaminfo object", - GST_TYPE_STREAM_INFO, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS), - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_SOURCE, - g_param_spec_object ("source", "Source", "Source element", - GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, ARG_VIDEO, - g_param_spec_int ("current-video", "Current video", - "Currently playing video stream (-1 = none)", - -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_AUDIO, - g_param_spec_int ("current-audio", "Current audio", - "Currently playing audio stream (-1 = none)", - -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_TEXT, - g_param_spec_int ("current-text", "Current text", - "Currently playing text stream (-1 = none)", - -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_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 | G_PARAM_STATIC_STRINGS)); - /** - * GstPlayBaseBin:connection-speed - * - * Network connection speed in kbps (0 = unknown) - * - * Since version 0.10.10 in #GstPlayBin, at 0.10.15 moved to #GstPlayBaseBin - * - * - * Since: 0.10.10 - */ - g_object_class_install_property (gobject_klass, ARG_CONNECTION_SPEED, - g_param_spec_uint ("connection-speed", "Connection Speed", - "Network connection speed in kbps (0 = unknown)", - 0, G_MAXUINT, DEFAULT_CONNECTION_SPEED, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0, - "playbasebin"); - - gobject_klass->dispose = gst_play_base_bin_dispose; - gobject_klass->finalize = gst_play_base_bin_finalize; - - gstbin_klass->handle_message = - GST_DEBUG_FUNCPTR (gst_play_base_bin_handle_message_func); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); -} - -static void -gst_play_base_bin_init (GstPlayBaseBin * play_base_bin) -{ - play_base_bin->uri = NULL; - play_base_bin->suburi = NULL; - play_base_bin->need_rebuild = TRUE; - play_base_bin->is_stream = FALSE; - play_base_bin->source = NULL; - play_base_bin->decoders = NULL; - play_base_bin->subtitle = NULL; - play_base_bin->subencoding = NULL; - play_base_bin->subtitle_elements = NULL; - play_base_bin->sub_lock = g_mutex_new (); - - play_base_bin->group_lock = g_mutex_new (); - play_base_bin->group_cond = g_cond_new (); - - play_base_bin->building_group = NULL; - play_base_bin->queued_groups = NULL; - - play_base_bin->queue_size = DEFAULT_QUEUE_SIZE; - play_base_bin->queue_threshold = DEFAULT_QUEUE_THRESHOLD; - play_base_bin->queue_min_threshold = DEFAULT_QUEUE_MIN_THRESHOLD; - play_base_bin->connection_speed = DEFAULT_CONNECTION_SPEED; -} - -static void -gst_play_base_bin_dispose (GObject * object) -{ - GstPlayBaseBin *play_base_bin; - - play_base_bin = GST_PLAY_BASE_BIN (object); - g_free (play_base_bin->uri); - play_base_bin->uri = NULL; - g_free (play_base_bin->suburi); - play_base_bin->suburi = NULL; - g_free (play_base_bin->subencoding); - play_base_bin->subencoding = NULL; - if (play_base_bin->subtitle_elements) { - g_slist_free (play_base_bin->subtitle_elements); - play_base_bin->subtitle_elements = NULL; - } - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_play_base_bin_finalize (GObject * object) -{ - GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (object); - - g_mutex_free (play_base_bin->group_lock); - g_cond_free (play_base_bin->group_cond); - - g_mutex_free (play_base_bin->sub_lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/* - * playbasebingroup stuff. - */ - -static GstPlayBaseGroup * -group_create (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group; - - group = g_new0 (GstPlayBaseGroup, 1); - group->bin = play_base_bin; - group->streaminfo_value_array = g_value_array_new (0); - - GST_DEBUG_OBJECT (play_base_bin, "created new group %p", group); - - return group; -} - -/* - * Gets the currently playing group. - * - * Callers must have group-lock held when calling this. - */ - -static GstPlayBaseGroup * -get_active_group (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group = NULL; - - if (play_base_bin->queued_groups) - group = play_base_bin->queued_groups->data; - - return group; -} - -/* - * get the group used for discovering the different streams. - * This function creates a group is there is none. - * - * Callers must have group-lock held when calling this. - */ -static GstPlayBaseGroup * -get_building_group (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group; - - group = play_base_bin->building_group; - if (group == NULL) { - group = group_create (play_base_bin); - play_base_bin->building_group = group; - } - - return group; -} - -/* - * Callers must have lock held when calling this! - */ - -static void -group_destroy (GstPlayBaseGroup * group) -{ - GstPlayBaseBin *play_base_bin = group->bin; - gint n; - - GST_LOG ("removing group %p", group); - - /* remove the preroll queues */ - for (n = 0; n < NUM_TYPES; n++) { - GstElement *element = group->type[n].preroll; - GstElement *fakesrc; - GstElement *sel; - const GList *item; - - if (!element) - continue; - - sel = group->type[n].selector; - - /* remove any fakesrc elements for this preroll element */ - for (item = sel->pads; item != NULL; item = item->next) { - GstPad *pad = GST_PAD (item->data); - guint sig_id; - - if (GST_PAD_DIRECTION (pad) != GST_PAD_SINK) - continue; - - sig_id = - GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pad), "unlinked_id")); - - if (sig_id != 0) { - GST_LOG ("removing unlink signal %s:%s", GST_DEBUG_PAD_NAME (pad)); - g_signal_handler_disconnect (G_OBJECT (pad), sig_id); - g_object_set_data (G_OBJECT (pad), "unlinked_id", GINT_TO_POINTER (0)); - } - - fakesrc = (GstElement *) g_object_get_data (G_OBJECT (pad), "fakesrc"); - if (fakesrc != NULL) { - GST_LOG ("removing fakesrc from %s:%s", - GST_PAD_NAME (pad), GST_ELEMENT_NAME (GST_PAD_PARENT (pad))); - gst_element_set_state (fakesrc, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_base_bin), fakesrc); - } - } - - /* if the group is currently being played, we have to remove the element - * from the thread */ - gst_element_set_state (element, GST_STATE_NULL); - gst_element_set_state (group->type[n].selector, GST_STATE_NULL); - - GST_LOG ("removing preroll element %s", GST_ELEMENT_NAME (element)); - - gst_bin_remove (group->type[n].bin, element); - gst_bin_remove (group->type[n].bin, group->type[n].selector); - - group->type[n].preroll = NULL; - group->type[n].selector = NULL; - group->type[n].bin = NULL; - } - - /* free the streaminfo too */ - g_list_foreach (group->streaminfo, (GFunc) g_object_unref, NULL); - g_list_free (group->streaminfo); - g_value_array_free (group->streaminfo_value_array); - g_free (group); -} - -/* - * is called when the current building group is completely finished - * and ready for playback - * - * This function grabs lock, so take care when calling. - */ -static void -group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal, gboolean subtitle) -{ - GstPlayBaseGroup *group; - gboolean had_active_group; - - GROUP_LOCK (play_base_bin); - group = play_base_bin->building_group; - had_active_group = (get_active_group (play_base_bin) != NULL); - - GST_DEBUG_OBJECT (play_base_bin, "commit group %p, had active %d", - group, had_active_group); - - /* if an element signalled a no-more-pads after we stopped due - * to preroll, the group is NULL. This is not an error */ - if (group == NULL) { - if (!fatal) { - GROUP_UNLOCK (play_base_bin); - return; - } else { - GST_DEBUG_OBJECT (play_base_bin, "Group loading failed, bailing out"); - } - } else { - if (!subtitle) { - gint n; - - GST_DEBUG_OBJECT (play_base_bin, "group %p done", group); - - play_base_bin->queued_groups = - g_list_append (play_base_bin->queued_groups, group); - - play_base_bin->building_group = NULL; - - /* remove signals. We don't want anymore signals from the preroll - * elements at this stage. */ - for (n = 0; n < NUM_TYPES; n++) { - GstElement *element = group->type[n].preroll; - - if (!element) - continue; - - preroll_remove_overrun (element, play_base_bin); - /* if overrun is removed, probe alse has to be removed */ - queue_remove_probe (element, play_base_bin); - } - } else { - /* this is a special subtitle bin, we don't commit the group but - * mark the subtitles as detected before we signal. */ - GST_DEBUG_OBJECT (play_base_bin, "marking subtitle bin as complete"); - play_base_bin->subtitle_done = TRUE; - } - } - - GST_DEBUG_OBJECT (play_base_bin, "signal group done"); - GROUP_SIGNAL (play_base_bin); - GST_DEBUG_OBJECT (play_base_bin, "signaled group done"); - - if (!subtitle && !had_active_group) { - if (!prepare_output (play_base_bin)) { - GROUP_UNLOCK (play_base_bin); - return; - } - - setup_substreams (play_base_bin); - GST_DEBUG_OBJECT (play_base_bin, "Emitting signal"); - GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin)->setup_output_pads - (play_base_bin, group); - GST_DEBUG_OBJECT (play_base_bin, "done"); - - GROUP_UNLOCK (play_base_bin); - - g_object_notify (G_OBJECT (play_base_bin), "stream-info"); - } else { - GROUP_UNLOCK (play_base_bin); - } -} - -/* - * check if there are streams in the group that are not muted - * - * Callers must have group-lock held when calling this. - */ -static gboolean -group_is_muted (GstPlayBaseGroup * group) -{ - gint n; - - for (n = 0; n < NUM_TYPES; n++) { - if (group->type[n].preroll && !group->type[n].done) - return FALSE; - } - - return TRUE; -} - -/* - * Buffer/cache checking. - */ - -static inline void -fill_buffer (GstPlayBaseBin * play_base_bin, gint percent) -{ - GST_DEBUG_OBJECT (play_base_bin, "buffering %d", percent); - gst_element_post_message (GST_ELEMENT_CAST (play_base_bin), - gst_message_new_buffering (GST_OBJECT_CAST (play_base_bin), percent)); -} - -static GstProbeReturn -check_queue_event (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GstEvent *event = type_data; - GstElement *queue = GST_ELEMENT_CAST (user_data); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - GST_DEBUG ("EOS event, mark EOS"); - g_object_set_data (G_OBJECT (queue), "eos", GINT_TO_POINTER (1)); - break; - case GST_EVENT_FLUSH_STOP: - GST_DEBUG ("FLUSH_STOP event, remove EOS"); - g_object_set_data (G_OBJECT (queue), "eos", NULL); - break; - default: - GST_DEBUG ("uninteresting event %s", GST_EVENT_TYPE_NAME (event)); - break; - } - return GST_PROBE_OK; -} - -static GstProbeReturn -check_queue (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GstElement *queue = GST_ELEMENT_CAST (user_data); - GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb"); - guint64 level = 0; - - GST_DEBUG_OBJECT (queue, "check queue triggered"); - - g_object_get (G_OBJECT (queue), "current-level-time", &level, NULL); - GST_DEBUG_OBJECT (play_base_bin, "Queue size: %" GST_TIME_FORMAT, - GST_TIME_ARGS (level)); - - if (play_base_bin->queue_threshold > 0) { - level = level * 99 / play_base_bin->queue_threshold; - if (level > 99) - level = 99; - } else - level = 99; - - fill_buffer (play_base_bin, level); - - /* continue! */ - return GST_PROBE_OK; -} - -/* If a queue overruns and we are buffer in streaming mode (we have a min-time) - * we can potentially create a deadlock when: - * - * 1) the max-bytes is hit and - * 2) the min-time is not hit. - * - * We recover from this situation in this callback by - * setting the max-bytes to unlimited if we see that there is - * a current-time-level (which means some sort of timestamping is - * done). - */ -static void -queue_deadlock_check (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - guint64 time, min_time; - guint bytes; - - GST_DEBUG_OBJECT (play_base_bin, "overrun signal received from queue %s", - GST_ELEMENT_NAME (queue)); - - /* figure out where we are */ - g_object_get (G_OBJECT (queue), "current-level-time", &time, - "current-level-bytes", &bytes, "min-threshold-time", &min_time, NULL); - - GST_DEBUG_OBJECT (play_base_bin, "streaming mode, queue %s current %" - GST_TIME_FORMAT ", min %" GST_TIME_FORMAT - ", bytes %d", GST_ELEMENT_NAME (queue), - GST_TIME_ARGS (time), GST_TIME_ARGS (min_time), bytes); - - /* if the bytes in the queue represent time, we disable bytes - * overrun checking to not cause deadlocks. - */ - if (bytes && time != 0 && time < min_time) { - GST_DEBUG_OBJECT (play_base_bin, - "possible deadlock found, removing byte limit"); - - /* queue knows about time but is filled with bytes that do - * not represent min-threshold time, disable bytes checking so - * the queue can grow some more. */ - g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL); - - /* bytes limit is removed, we cannot deadlock anymore */ - g_signal_handlers_disconnect_by_func (queue, - (gpointer) queue_deadlock_check, play_base_bin); - } else { - GST_DEBUG_OBJECT (play_base_bin, "no deadlock"); - } -} - -static void -queue_remove_probe (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - gpointer data; - GstPad *sinkpad; - - data = g_object_get_data (G_OBJECT (queue), "probe"); - sinkpad = gst_element_get_static_pad (queue, "sink"); - - if (data) { - GST_DEBUG_OBJECT (play_base_bin, - "Removing buffer probe from pad %s:%s (%p)", - GST_DEBUG_PAD_NAME (sinkpad), sinkpad); - - g_object_set_data (G_OBJECT (queue), "probe", NULL); - gst_pad_remove_probe (sinkpad, GPOINTER_TO_INT (data)); - } else { - GST_DEBUG_OBJECT (play_base_bin, - "No buffer probe to remove from %s:%s (%p)", - GST_DEBUG_PAD_NAME (sinkpad), sinkpad); - } - gst_object_unref (sinkpad); -} - -/* Used for time-based buffering in streaming mode and is called when a queue - * emits the running signal. This means that the high watermark threshold is - * reached and the buffering is completed. */ -static void -queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group; - gint n; - - GST_DEBUG_OBJECT (play_base_bin, "running signal received from queue %s", - GST_ELEMENT_NAME (queue)); - - /* we disconnect the signal so that we don't get called for every buffer. */ - g_signal_handlers_disconnect_by_func (queue, - (gpointer) queue_threshold_reached, play_base_bin); - - if (g_object_get_data (G_OBJECT (queue), "eos")) { - GST_DEBUG_OBJECT (play_base_bin, "disable min threshold time, we are EOS"); - g_object_set (queue, "min-threshold-time", (guint64) 0, NULL); - } else { - /* now place the limits at the low threshold. When we hit this limit, the - * underrun signal will be called. The underrun signal is always connected. */ - GST_DEBUG_OBJECT (play_base_bin, - "setting min threshold time to %" G_GUINT64_FORMAT, - play_base_bin->queue_min_threshold); - g_object_set (queue, "min-threshold-time", - play_base_bin->queue_min_threshold, NULL); - } - - GROUP_LOCK (play_base_bin); - group = get_active_group (play_base_bin); - if (!group) { - GROUP_UNLOCK (play_base_bin); - return; - } - - /* we remove the probe now because we don't need it anymore to give progress - * about the buffering. */ - for (n = 0; n < NUM_TYPES; n++) { - GstElement *element = group->type[n].preroll; - - if (!element) - continue; - - queue_remove_probe (element, play_base_bin); - } - - GROUP_UNLOCK (play_base_bin); - - /* we post a 100% buffering message to notify the app that buffering is - * completed and playback can start/continue */ - if (play_base_bin->is_stream) - fill_buffer (play_base_bin, 100); -} - -/* this signal will be fired when one of the queues with raw - * data is filled. This means that the group building stage is over - * and playback of the new queued group should start. This is a rather unusual - * situation because normally the group is commited when the "no_more_pads" - * signal is fired. - */ -static void -queue_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GST_DEBUG_OBJECT (play_base_bin, "queue %s overrun", - GST_ELEMENT_NAME (queue)); - - preroll_remove_overrun (queue, play_base_bin); - - group_commit (play_base_bin, FALSE, - GST_OBJECT_PARENT (GST_OBJECT_CAST (queue)) == - GST_OBJECT_CAST (play_base_bin->subtitle)); - - /* notify end of buffering */ - queue_threshold_reached (queue, play_base_bin); -} - -/* this signal is only added when in streaming mode to catch underruns - */ -static void -queue_out_of_data (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GST_DEBUG_OBJECT (play_base_bin, "underrun signal received from queue %s", - GST_ELEMENT_NAME (queue)); - - /* On underrun, we want to temoprarily pause playback, set a "min-size" - * threshold and wait for the running signal and then play again. - * - * This signal could never be called because the queue max-size limits are set - * too low. We take care of this possible deadlock in the the overrun signal - * handler. */ - g_signal_connect (G_OBJECT (queue), "pushing", - G_CALLBACK (queue_threshold_reached), play_base_bin); - GST_DEBUG_OBJECT (play_base_bin, - "setting min threshold time to %" G_GUINT64_FORMAT, - (guint64) play_base_bin->queue_threshold); - g_object_set (queue, "min-threshold-time", - (guint64) play_base_bin->queue_threshold, NULL); - - /* re-connect probe, this will fire feedback about the percentage that we - * buffered and is posted in the BUFFERING message. */ - if (!g_object_get_data (G_OBJECT (queue), "probe")) { - GstPad *sinkpad; - guint id; - - sinkpad = gst_element_get_static_pad (queue, "sink"); - id = gst_pad_add_probe (sinkpad, GST_PROBE_TYPE_BUFFER, check_queue, queue, - NULL); - g_object_set_data (G_OBJECT (queue), "probe", GINT_TO_POINTER (id)); - GST_DEBUG_OBJECT (play_base_bin, - "Re-attaching buffering probe to pad %s:%s %p", - GST_DEBUG_PAD_NAME (sinkpad), sinkpad); - gst_object_unref (sinkpad); - - fill_buffer (play_base_bin, 0); - } -} - -/* - * generate a preroll element which is simply a queue. While there - * are still dynamic elements in the pipeline, we wait for one - * of the queues to fill. The assumption is that all the dynamic - * streams will be detected by that time. - * - * Callers must have the group-lock held when calling this. - */ -static void -gen_preroll_element (GstPlayBaseBin * play_base_bin, - GstPlayBaseGroup * group, GstStreamType type, GstPad * pad, - GstStreamInfo * info) -{ - GstElement *selector, *preroll; - gchar *name, *padname; - const gchar *prename; - guint overrun_sig; - GstPad *preroll_pad; - GstBin *target; - GstState state; - - if (type == GST_STREAM_TYPE_VIDEO) - prename = "video"; - else if (type == GST_STREAM_TYPE_TEXT) - prename = "text"; - else if (type == GST_STREAM_TYPE_AUDIO) - prename = "audio"; - else if (type == GST_STREAM_TYPE_SUBPICTURE) - prename = "subpicture"; - else - g_return_if_reached (); - - /* create stream selector */ - selector = g_object_new (GST_TYPE_STREAM_SELECTOR, NULL); - padname = gst_pad_get_name (pad); - name = g_strdup_printf ("selector_%s_%s", prename, padname); - gst_object_set_name (GST_OBJECT_CAST (selector), name); - g_free (name); - - /* create preroll queue */ - name = g_strdup_printf ("preroll_%s_%s", prename, padname); - preroll = gst_element_factory_make ("queue", name); - g_free (name); - g_free (padname); - - /* for buffering of raw data we ideally want to buffer a - * very small amount of buffers since the memory used by - * this raw data can be enormously huge. - * - * We use an upper limit of typically a few seconds here but - * cap in case no timestamps are set on the raw data (bad!). - * - * FIXME: we abuse this buffer to do network buffering since - * we can then easily do time-based buffering. The better - * solution would be to add a specific network queue right - * after the source that measures the datarate and scales this - * queue of encoded data instead. - */ - if (play_base_bin->raw_decoding_mode) { - if (type == GST_STREAM_TYPE_VIDEO) { - g_object_set (G_OBJECT (preroll), - "max-size-buffers", 2, "max-size-bytes", 0, - "max-size-time", (guint64) 0, NULL); - } else { - g_object_set (G_OBJECT (preroll), - "max-size-buffers", 0, "max-size-bytes", - 2 * 1024 * 1024, "max-size-time", play_base_bin->queue_size, NULL); - } - } else { - g_object_set (G_OBJECT (preroll), - "max-size-buffers", 0, "max-size-bytes", - ((type == GST_STREAM_TYPE_VIDEO) ? 25 : 2) * 1024 * 1024, - "max-size-time", play_base_bin->queue_size, NULL); - } - - /* the overrun signal is always attached and serves two purposes: - * - * 1) when we are building a group and the overrun is called, we commit the - * group. The reason being that if we fill the entire queue without a - * normal group commit (with _no_more_pads()) we can assume the - * audio/video is completely wacked or the element just does not know when - * it is ready with all the pads (mpeg). - * 2) When we are doing network buffering, we keep track of low/high - * watermarks in the queue. It is possible that we set the high watermark - * higher than the max-size limits to trigger an overrun. In this case we - * will never get a running signal but we can use the overrun signal to - * detect this deadlock and correct it. - */ - overrun_sig = g_signal_connect (G_OBJECT (preroll), "overrun", - G_CALLBACK (queue_overrun), play_base_bin); - - /* keep a ref to the signal id so that we can disconnect the signal callback - * when we are done with the preroll */ - g_object_set_data (G_OBJECT (preroll), "overrun_signal_id", - GINT_TO_POINTER (overrun_sig)); - - if (play_base_bin->is_stream && - ((type == GST_STREAM_TYPE_VIDEO && - group->type[GST_STREAM_TYPE_AUDIO - 1].npads == 0) || - (type == GST_STREAM_TYPE_AUDIO && - group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0))) { - GstPad *sinkpad; - guint id; - - /* catch deadlocks when we are network buffering in time but the max-limit - * in bytes is hit. */ - g_signal_connect (G_OBJECT (preroll), "overrun", - G_CALLBACK (queue_deadlock_check), play_base_bin); - - /* attach pointer to playbasebin */ - g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin); - - /* give updates on queue size */ - sinkpad = gst_element_get_static_pad (preroll, "sink"); - id = gst_pad_add_probe (sinkpad, GST_PROBE_TYPE_BUFFER, check_queue, - preroll, NULL); - GST_DEBUG_OBJECT (play_base_bin, "Attaching probe to pad %s:%s (%p)", - GST_DEBUG_PAD_NAME (sinkpad), sinkpad); - g_object_set_data (G_OBJECT (preroll), "probe", GINT_TO_POINTER (id)); - - /* catch eos and flush events so that we can ignore underruns */ - id = gst_pad_add_probe (sinkpad, GST_PROBE_TYPE_EVENT, check_queue_event, - preroll, NULL); - g_object_set_data (G_OBJECT (preroll), "eos_probe", GINT_TO_POINTER (id)); - - gst_object_unref (sinkpad); - - /* When we connect this queue, it will start running and immediatly - * fire an underrun. */ - g_signal_connect (G_OBJECT (preroll), "underrun", - G_CALLBACK (queue_out_of_data), play_base_bin); - /* configure threshold and callbacks */ - queue_out_of_data (preroll, play_base_bin); - } - - /* listen for EOS so we can switch groups when one ended. */ - preroll_pad = gst_element_get_static_pad (preroll, "src"); - gst_pad_add_probe (preroll_pad, GST_PROBE_TYPE_EVENT, probe_triggered, info, - NULL); - gst_object_unref (preroll_pad); - - /* add to group list */ - group->type[type - 1].selector = selector; - group->type[type - 1].preroll = preroll; - - /* figure out where the preroll element should go */ - if (type == GST_STREAM_TYPE_TEXT && play_base_bin->subtitle) - target = GST_BIN_CAST (play_base_bin->subtitle); - else - target = GST_BIN_CAST (play_base_bin); - - group->type[type - 1].bin = target; - gst_bin_add (target, selector); - gst_bin_add (target, preroll); - - gst_element_link (selector, preroll); - - /* figure out target state and set */ - state = (GST_STATE (play_base_bin) == GST_STATE_PLAYING ? - GST_STATE_PLAYING : GST_STATE_PAUSED); - - gst_element_set_state (selector, state); - gst_element_set_state (preroll, state); -} - -static void -preroll_remove_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) -{ - guint overrun_sig; - GObject *obj = G_OBJECT (element); - - overrun_sig = GPOINTER_TO_INT (g_object_get_data (obj, "overrun_signal_id")); - if (overrun_sig) { - GST_LOG_OBJECT (play_base_bin, "removing preroll signal %s", - GST_ELEMENT_NAME (element)); - g_signal_handler_disconnect (obj, overrun_sig); - /* We have disconnected this signal, remove the signal_id from the object - * data */ - g_object_set_data (obj, "overrun_signal_id", NULL); - } -} - -static void -remove_groups (GstPlayBaseBin * play_base_bin) -{ - GROUP_LOCK (play_base_bin); - - /* first destroy the group we were building if any */ - if (play_base_bin->building_group) { - group_destroy (play_base_bin->building_group); - play_base_bin->building_group = NULL; - } - - /* remove the queued groups */ - g_list_foreach (play_base_bin->queued_groups, (GFunc) group_destroy, NULL); - g_list_free (play_base_bin->queued_groups); - play_base_bin->queued_groups = NULL; - - /* clear subs */ - if (play_base_bin->subtitle) { - gst_element_set_state (play_base_bin->subtitle, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); - play_base_bin->subtitle = NULL; - } - - GROUP_UNLOCK (play_base_bin); -} - -/* - * Add/remove a single stream to current building group. - * - * Must be called with group-lock held. - */ -static void -add_stream (GstPlayBaseGroup * group, GstStreamInfo * info) -{ - GValue v = { 0, }; - GST_DEBUG ("add stream to group %p", group); - - /* keep ref to the group */ - g_object_set_data (G_OBJECT (info), "group", group); - - g_value_init (&v, G_TYPE_OBJECT); - g_value_set_object (&v, info); - g_value_array_append (group->streaminfo_value_array, &v); - g_value_unset (&v); - group->streaminfo = g_list_append (group->streaminfo, info); - if (info->type > 0 && info->type <= NUM_TYPES) { - group->type[info->type - 1].npads++; - } -} - -static gboolean -string_arr_has_str (const gchar * values[], const gchar * value) -{ - if (values && value) { - while (*values != NULL) { - if (strcmp (value, *values) == 0) - return TRUE; - ++values; - } - } - return FALSE; -} - -/* mime types we are not handling on purpose right now, don't post a - * missing-plugin message for these */ -static const gchar *blacklisted_mimes[] = { - "video/x-dvd-subpicture", NULL -}; - -#define IS_BLACKLISTED_MIME(type) (string_arr_has_str(blacklisted_mimes,type)) - -static void -gst_play_base_bin_handle_message_func (GstBin * bin, GstMessage * msg) -{ - if (gst_is_missing_plugin_message (msg)) { - gchar *detail; - guint i; - - detail = gst_missing_plugin_message_get_installer_detail (msg); - for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) { - if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) { - GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg); - gst_message_unref (msg); - g_free (detail); - return; - } - } - g_free (detail); - } - GST_BIN_CLASS (parent_class)->handle_message (bin, msg); -} - -/* - * signal fired when an unknown stream is found. We create a new - * UNKNOWN streaminfo object. - */ -static void -unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, - GstPlayBaseBin * play_base_bin) -{ - const gchar *type_name; - GstStreamInfo *info; - GstPlayBaseGroup *group; - - type_name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); - if (type_name && !IS_BLACKLISTED_MIME (type_name)) { - gchar *capsstr; - - capsstr = gst_caps_to_string (caps); - GST_DEBUG_OBJECT (play_base_bin, "don't know how to handle %s", capsstr); - /* FIXME, g_message() ? */ - g_message ("don't know how to handle %s", capsstr); - g_free (capsstr); - } else { - /* don't spew stuff to the terminal or send message if it's blacklisted */ - GST_DEBUG_OBJECT (play_base_bin, "media type %s not handled on purpose, " - "not posting a missing-plugin message on the bus", type_name); - } - - GROUP_LOCK (play_base_bin); - - group = get_building_group (play_base_bin); - - /* add the stream to the list */ - info = gst_stream_info_new (GST_OBJECT_CAST (pad), GST_STREAM_TYPE_UNKNOWN, - NULL, caps); - info->origin = GST_OBJECT_CAST (pad); - add_stream (group, info); - - GROUP_UNLOCK (play_base_bin); -} - -/* add a streaminfo that indicates that the stream is handled by the - * given element. This usually means that a stream without actual data is - * produced but one that is sunken by an element. Examples of this are: - * cdaudio, a hardware decoder/sink, dvd meta bins etc... - */ -static void -add_element_stream (GstElement * element, GstPlayBaseBin * play_base_bin) -{ - GstStreamInfo *info; - GstPlayBaseGroup *group; - - GROUP_LOCK (play_base_bin); - - group = get_building_group (play_base_bin); - - /* add the stream to the list */ - info = - gst_stream_info_new (GST_OBJECT_CAST (element), GST_STREAM_TYPE_ELEMENT, - NULL, NULL); - info->origin = GST_OBJECT_CAST (element); - add_stream (group, info); - - GROUP_UNLOCK (play_base_bin); -} - -/* when the decoder element signals that no more pads will be generated, we - * can commit the current group. - */ -static void -no_more_pads_full (GstElement * element, gboolean subs, - GstPlayBaseBin * play_base_bin) -{ - /* setup phase */ - GST_DEBUG_OBJECT (element, "no more pads, %d pending", - play_base_bin->pending); - - /* nothing pending, we can exit */ - if (play_base_bin->pending == 0) - return; - - /* the object has no pending no_more_pads */ - if (!g_object_get_data (G_OBJECT (element), "pending")) - return; - - g_object_set_data (G_OBJECT (element), "pending", NULL); - - play_base_bin->pending--; - - GST_DEBUG_OBJECT (element, "remove pending, now %d pending", - play_base_bin->pending); - - if (play_base_bin->pending == 0) { - /* we can commit this group for playback now */ - group_commit (play_base_bin, play_base_bin->is_stream, subs); - } -} - -static void -no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin) -{ - no_more_pads_full (element, FALSE, play_base_bin); -} - -static void -sub_no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin) -{ - no_more_pads_full (element, TRUE, play_base_bin); -} - -static void -source_no_more_pads (GstElement * element, GstPlayBaseBin * bin) -{ - GST_DEBUG_OBJECT (bin, "No more pads in source element %s.", - GST_ELEMENT_NAME (element)); - - g_signal_handler_disconnect (G_OBJECT (element), bin->src_np_sig_id); - bin->src_np_sig_id = 0; - g_signal_handler_disconnect (G_OBJECT (element), bin->src_nmp_sig_id); - bin->src_nmp_sig_id = 0; - - no_more_pads_full (element, FALSE, bin); -} - -static GstProbeReturn -probe_triggered (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GstEvent *event = type_data; - GstPlayBaseGroup *group; - GstPlayBaseBin *play_base_bin; - GstStreamInfo *info; - GstEventType etype; - - etype = GST_EVENT_TYPE (event); - - GST_LOG ("probe triggered, (%d) %s", type, gst_event_type_get_name (type)); - - /* we only care about EOS */ - if (etype != GST_EVENT_EOS) - return GST_PROBE_OK; - - info = GST_STREAM_INFO (user_data); - group = (GstPlayBaseGroup *) g_object_get_data (G_OBJECT (info), "group"); - play_base_bin = group->bin; - - if (etype == GST_EVENT_EOS) { - gint num_groups = 0; - gboolean have_left; - - GST_DEBUG_OBJECT (play_base_bin, "probe got EOS in group %p", group); - - GROUP_LOCK (play_base_bin); - - /* mute this stream */ - g_object_set (G_OBJECT (info), "mute", TRUE, NULL); - if (info->type > 0 && info->type <= NUM_TYPES) - group->type[info->type - 1].done = TRUE; - - /* see if we have some more groups left to play */ - num_groups = g_list_length (play_base_bin->queued_groups); - if (play_base_bin->building_group) - num_groups++; - have_left = (num_groups > 1); - - /* see if the complete group is muted */ - if (!group_is_muted (group)) { - /* group is not completely muted, we remove the EOS event - * and continue, eventually the other streams will be EOSed and - * we can switch out this group. */ - GST_DEBUG ("group %p not completely muted", group); - - GROUP_UNLOCK (play_base_bin); - - /* remove the EOS if we have something left */ - return (have_left ? GST_PROBE_DROP : GST_PROBE_OK); - } - - if (have_left) { - /* ok, get rid of the current group then */ - //group_destroy (group); - /* removing the current group brings the next group - * active */ - play_base_bin->queued_groups = - g_list_remove (play_base_bin->queued_groups, group); - /* and wait for the next one to be ready */ - while (!play_base_bin->queued_groups) { - GROUP_WAIT (play_base_bin); - } - group = play_base_bin->queued_groups->data; - - /* now activate the next one */ - setup_substreams (play_base_bin); - GST_DEBUG ("switching to next group %p - emitting signal", group); - /* and signal the new group */ - GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin)->setup_output_pads - (play_base_bin, group); - - GROUP_UNLOCK (play_base_bin); - - g_object_notify (G_OBJECT (play_base_bin), "stream-info"); - - /* get rid of the EOS event */ - return GST_PROBE_DROP; - } else { - GROUP_UNLOCK (play_base_bin); - GST_LOG ("Last group done, EOS"); - } - } - - return GST_PROBE_OK; -} - -/* This function will be called when the sinkpad of the preroll element - * is unlinked, we have to connect something to the sinkpad or else the - * state change will fail.. - */ -static void -preroll_unlinked (GstPad * pad, GstPad * peerpad, - GstPlayBaseBin * play_base_bin) -{ - GstElement *fakesrc; - guint sig_id; - GstPad *srcpad; - - /* make a fakesrc that will just emit one EOS */ - fakesrc = gst_element_factory_make ("fakesrc", NULL); - g_object_set (G_OBJECT (fakesrc), "num-buffers", 0, NULL); - - GST_DEBUG ("patching unlinked pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - srcpad = gst_element_get_static_pad (fakesrc, "src"); - gst_bin_add (GST_BIN_CAST (play_base_bin), fakesrc); - gst_pad_link (srcpad, pad); - gst_object_unref (srcpad); - - /* keep track of these patch elements */ - g_object_set_data (G_OBJECT (pad), "fakesrc", fakesrc); - - /* now unlink the unlinked signal so that it is not called again when - * we destroy the queue */ - sig_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pad), "unlinked_id")); - if (sig_id != 0) { - g_signal_handler_disconnect (G_OBJECT (pad), sig_id); - g_object_set_data (G_OBJECT (pad), "unlinked_id", GINT_TO_POINTER (0)); - } -} - -/* Mute stream on first data - for header-is-in-stream-stuff - * (vorbis, ogmtext). */ -static GstProbeReturn -mute_stream (GstPad * pad, GstProbeType type, gpointer type_data, gpointer data) -{ - GstStreamInfo *info = GST_STREAM_INFO (data); - guint id; - - GST_DEBUG ("mute stream triggered"); - - g_object_set (G_OBJECT (info), "mute", TRUE, NULL); - id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), "mute_probe")); - g_object_set_data (G_OBJECT (info), "mute_probe", NULL); - if (id > 0) - gst_pad_remove_probe (GST_PAD_CAST (info->object), id); - - /* no data */ - return GST_PROBE_DROP; -} - -/* Eat data. */ -static GstProbeReturn -silence_stream (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GST_DEBUG ("silence stream triggered"); - - /* no data */ - return GST_PROBE_DROP; -} - -/* Called by the signal handlers when a decodebin (main or subtitle) has - * found a new raw pad. We create a preroll element if needed and the - * appropriate streaminfo. Commits the group if there will be no more pads - * from decodebin */ -static void -new_decoded_pad_full (GstElement * element, GstPad * pad, gboolean last, - GstPlayBaseBin * play_base_bin, gboolean is_subs) -{ - GstStructure *structure; - const gchar *mimetype; - GstCaps *caps; - GstStreamInfo *info; - GstStreamType type = GST_STREAM_TYPE_UNKNOWN; - GstPad *sinkpad; - GstPlayBaseGroup *group; - guint sig; - GstObject *parent; - gboolean first_pad; - - GST_DEBUG ("play base: new decoded pad. Last: %d", last); - - /* first see if this pad has interesting caps */ - caps = gst_pad_get_caps (pad, NULL); - if (caps == NULL || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) - goto no_type; - - /* get the mime type */ - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - GROUP_LOCK (play_base_bin); - - group = get_building_group (play_base_bin); - - group->nstreams++; - - parent = gst_object_get_parent (GST_OBJECT_CAST (element)); - if (g_str_has_prefix (mimetype, "audio/") && - parent != GST_OBJECT_CAST (play_base_bin->subtitle)) { - type = GST_STREAM_TYPE_AUDIO; - } else if (g_str_has_prefix (mimetype, "video/x-dvd-subpicture") && - parent != GST_OBJECT_CAST (play_base_bin->subtitle)) { - type = GST_STREAM_TYPE_SUBPICTURE; - } else if (g_str_has_prefix (mimetype, "video/") && - parent != GST_OBJECT_CAST (play_base_bin->subtitle)) { - type = GST_STREAM_TYPE_VIDEO; - } else if (g_str_has_prefix (mimetype, "text/")) { - type = GST_STREAM_TYPE_TEXT; - } - gst_object_unref (parent); - - info = gst_stream_info_new (GST_OBJECT_CAST (pad), type, NULL, caps); - gst_caps_unref (caps); - - if (type == GST_STREAM_TYPE_UNKNOWN) { - /* Unknown streams get added to the group, but the data - * just gets ignored */ - add_stream (group, info); - - GROUP_UNLOCK (play_base_bin); - - /* signal the no more pads after adding the stream */ - if (last) - no_more_pads_full (element, is_subs, play_base_bin); - - return; - } - - /* first pad of each type gets a selector + preroll queue */ - first_pad = (group->type[type - 1].npads == 0); - - if (first_pad) { - GST_DEBUG ("play base: pad needs new preroll"); - gen_preroll_element (play_base_bin, group, type, pad, info); - } - - /* add to stream selector */ - sinkpad = - gst_element_get_request_pad (group->type[type - 1].selector, "sink%d"); - - /* make sure we catch unlink signals */ - sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked", - G_CALLBACK (preroll_unlinked), play_base_bin); - /* keep a ref to the signal id so that we can disconnect the signal callback */ - g_object_set_data (G_OBJECT (sinkpad), "unlinked_id", GINT_TO_POINTER (sig)); - /* Store a pointer to the stream selector pad for this stream */ - g_object_set_data (G_OBJECT (pad), "pb_sel_pad", sinkpad); - - gst_pad_link (pad, sinkpad); - gst_object_unref (sinkpad); - - /* select 1st for now - we'll select a preferred one after preroll */ - if (!first_pad) { - guint id; - - GST_DEBUG ("Adding silence_stream data probe on type %d (npads %d)", type, - group->type[type - 1].npads); - - id = gst_pad_add_probe (GST_PAD_CAST (pad), GST_PROBE_TYPE_DATA, - silence_stream, info, NULL); - g_object_set_data (G_OBJECT (pad), "eat_probe", GINT_TO_POINTER (id)); - } - - /* add the stream to the list */ - add_stream (group, info); - - GROUP_UNLOCK (play_base_bin); - - /* signal the no more pads after adding the stream */ - if (last) - no_more_pads_full (element, is_subs, play_base_bin); - - return; - - /* ERRORS */ -no_type: - { - g_warning ("no type on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (caps) - gst_caps_unref (caps); - return; - } -} - -static void -new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, - GstPlayBaseBin * play_base_bin) -{ - new_decoded_pad_full (element, pad, last, play_base_bin, FALSE); -} - -static void -subs_new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, - GstPlayBaseBin * play_base_bin) -{ - new_decoded_pad_full (element, pad, last, play_base_bin, TRUE); -} - -static void -set_encoding_element (GstElement * element, gchar * encoding) -{ - GST_DEBUG_OBJECT (element, "setting encoding to %s", GST_STR_NULL (encoding)); - g_object_set (G_OBJECT (element), "subtitle-encoding", encoding, NULL); -} - - -static void -decodebin_element_added_cb (GstBin * decodebin, GstElement * element, - gpointer data) -{ - GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (data); - gchar *encoding; - - if (!g_object_class_find_property (G_OBJECT_GET_CLASS (element), - "subtitle-encoding")) { - return; - } - - g_mutex_lock (play_base_bin->sub_lock); - play_base_bin->subtitle_elements = - g_slist_append (play_base_bin->subtitle_elements, element); - encoding = g_strdup (play_base_bin->subencoding); - g_mutex_unlock (play_base_bin->sub_lock); - - set_encoding_element (element, encoding); - g_free (encoding); -} - -static void -decodebin_element_removed_cb (GstBin * decodebin, GstElement * element, - gpointer data) -{ - GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (data); - - g_mutex_lock (play_base_bin->sub_lock); - play_base_bin->subtitle_elements = - g_slist_remove (play_base_bin->subtitle_elements, element); - g_mutex_unlock (play_base_bin->sub_lock); -} - - -/* - * Generate source ! subparse bins. - */ - -static GstElement * -setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri) -{ - GstElement *source, *subdecodebin, *subbin; - - if (!gst_uri_is_valid (sub_uri)) - goto invalid_uri; - - source = gst_element_make_from_uri (GST_URI_SRC, sub_uri, NULL); - if (!source) - goto unknown_uri; - - if (g_getenv ("USE_DECODEBIN2")) - subdecodebin = gst_element_factory_make ("decodebin2", "subtitle-decoder"); - else - subdecodebin = gst_element_factory_make ("decodebin", "subtitle-decoder"); - g_signal_connect (subdecodebin, "element-added", - G_CALLBACK (decodebin_element_added_cb), play_base_bin); - g_signal_connect (subdecodebin, "element-removed", - G_CALLBACK (decodebin_element_removed_cb), play_base_bin); - subbin = gst_bin_new ("subtitle-bin"); - gst_bin_add_many (GST_BIN_CAST (subbin), source, subdecodebin, NULL); - - gst_element_link (source, subdecodebin); - - /* return the subtitle GstElement object */ - return subbin; - - /* WARNINGS */ -invalid_uri: - { - GST_ELEMENT_WARNING (play_base_bin, RESOURCE, NOT_FOUND, - (_("Invalid subtitle URI \"%s\", subtitles disabled."), sub_uri), - (NULL)); - return NULL; - } -unknown_uri: - { - gchar *prot = gst_uri_get_protocol (sub_uri); - - if (prot) { - gchar *desc; - - gst_element_post_message (GST_ELEMENT (play_base_bin), - gst_missing_uri_source_message_new (GST_ELEMENT (play_base_bin), - prot)); - - desc = gst_pb_utils_get_source_description (prot); - GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, - (_("A %s plugin is required to play this stream, but not installed."), - desc), ("No URI handler to handle sub_uri: %s", sub_uri)); - g_free (desc); - g_free (prot); - } else - goto invalid_uri; - - return NULL; - } -} - -/* helper function to lookup stuff in lists */ -static gboolean -array_has_value (const gchar * values[], const gchar * value) -{ - gint i; - - for (i = 0; values[i]; i++) { - if (g_str_has_prefix (value, values[i])) - return TRUE; - } - return FALSE; -} - -/* list of URIs that we consider to be streams and that need buffering. - * We have no mechanism yet to figure this out with a query. */ -static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", - "mmsu://", "mmst://", "myth://", NULL -}; - -/* blacklisted URIs, we know they will always fail. */ -static const gchar *blacklisted_uris[] = { NULL }; - -/* mime types that we don't consider to be media types */ -static const gchar *no_media_mimes[] = { - "application/x-executable", "application/x-bzip", "application/x-gzip", - "application/zip", "application/x-compress", NULL -}; - -/* mime types we consider raw media */ -static const gchar *raw_mimes[] = { - "audio/x-raw", "video/x-raw", "video/x-dvd-subpicture", NULL -}; - -#define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri)) -#define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri)) -#define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime)) -#define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime)) - -/* - * Generate and configure a source element. - */ -static GstElement * -gen_source_element (GstPlayBaseBin * play_base_bin, GstElement ** subbin) -{ - GstElement *source; - - if (!play_base_bin->uri) - goto no_uri; - - if (!gst_uri_is_valid (play_base_bin->uri)) - goto invalid_uri; - - if (IS_BLACKLISTED_URI (play_base_bin->uri)) - goto uri_blacklisted; - - if (play_base_bin->suburi) { - GST_LOG_OBJECT (play_base_bin, "Creating decoder for subtitles URI %s", - play_base_bin->suburi); - /* subtitle specified */ - *subbin = setup_subtitle (play_base_bin, play_base_bin->suburi); - } else { - /* no subtitle specified */ - *subbin = NULL; - } - - source = gst_element_make_from_uri (GST_URI_SRC, play_base_bin->uri, - "source"); - if (!source) - goto no_source; - - play_base_bin->is_stream = IS_STREAM_URI (play_base_bin->uri); - - /* make HTTP sources send extra headers so we get icecast - * metadata in case the stream is an icecast stream */ - if (!strncmp (play_base_bin->uri, "http://", 7) && - g_object_class_find_property (G_OBJECT_GET_CLASS (source), - "iradio-mode")) { - g_object_set (source, "iradio-mode", TRUE, NULL); - } - - if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), - "connection-speed")) { - GST_DEBUG_OBJECT (play_base_bin, - "setting connection-speed=%d to source element", - play_base_bin->connection_speed / 1000); - g_object_set (source, "connection-speed", - play_base_bin->connection_speed / 1000, NULL); - } - - return source; - - /* ERRORS */ -no_uri: - { - GST_ELEMENT_ERROR (play_base_bin, RESOURCE, NOT_FOUND, - (_("No URI specified to play from.")), (NULL)); - return NULL; - } -invalid_uri: - { - GST_ELEMENT_ERROR (play_base_bin, RESOURCE, NOT_FOUND, - (_("Invalid URI \"%s\"."), play_base_bin->uri), (NULL)); - return NULL; - } -uri_blacklisted: - { - GST_ELEMENT_ERROR (play_base_bin, RESOURCE, FAILED, - (_("RTSP streams cannot be played yet.")), (NULL)); - return NULL; - } -no_source: - { - gchar *prot = gst_uri_get_protocol (play_base_bin->uri); - - /* whoops, could not create the source element, dig a little deeper to - * figure out what might be wrong. */ - if (prot) { - gchar *desc; - - gst_element_post_message (GST_ELEMENT (play_base_bin), - gst_missing_uri_source_message_new (GST_ELEMENT (play_base_bin), - prot)); - - desc = gst_pb_utils_get_source_description (prot); - GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, - (_("A %s plugin is required to play this stream, but not installed."), - desc), ("No URI handler for %s", prot)); - g_free (desc); - g_free (prot); - } else - goto invalid_uri; - - return NULL; - } -} - -/* is called when a dynamic source element created a new pad. */ -static void -source_new_pad (GstElement * element, GstPad * pad, GstPlayBaseBin * bin) -{ - GstElement *decoder; - gboolean is_raw; - - GST_DEBUG_OBJECT (bin, "Found new pad %s.%s in source element %s", - GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element)); - - /* if this is a pad with all raw caps, we can expose it */ - if (has_all_raw_caps (pad, &is_raw) && is_raw) { - bin->raw_decoding_mode = TRUE; - /* it's all raw, create output pads. */ - new_decoded_pad_full (element, pad, FALSE, bin, FALSE); - return; - } - - /* not raw, create decoder */ - decoder = make_decoder (bin); - if (!decoder) - goto no_decodebin; - - /* and link to decoder */ - if (!gst_element_link (bin->source, decoder)) - goto could_not_link; - - gst_element_set_state (decoder, GST_STATE_PAUSED); - - return; - - /* ERRORS */ -no_decodebin: - { - /* error was posted */ - return; - } -could_not_link: - { - GST_ELEMENT_ERROR (bin, CORE, NEGOTIATION, - (NULL), ("Can't link source to decoder element")); - return; - } -} - -/* - * Setup the substreams (is called right after group_commit () when - * loading a new group, or after switching groups). - * - * Should be called with group-lock held. - */ -static void -setup_substreams (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group; - gint n; - const GList *item; - - GST_DEBUG_OBJECT (play_base_bin, "setting up substreams"); - - /* Remove the eat probes */ - group = get_active_group (play_base_bin); - for (item = group->streaminfo; item; item = item->next) { - GstStreamInfo *info = item->data; - gpointer data; - - data = g_object_get_data (G_OBJECT (info->object), "eat_probe"); - if (data) { - gst_pad_remove_probe (GST_PAD_CAST (info->object), - GPOINTER_TO_INT (data)); - g_object_set_data (G_OBJECT (info->object), "eat_probe", NULL); - } - - /* now remove unknown pads */ - if (info->type == GST_STREAM_TYPE_UNKNOWN) { - guint id; - - id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), "mute_probe")); - if (id == 0) { - id = gst_pad_add_probe (GST_PAD_CAST (info->object), - GST_PROBE_TYPE_BUFFER, mute_stream, info, NULL); - g_object_set_data (G_OBJECT (info), "mute_probe", GINT_TO_POINTER (id)); - } - } - } - - /* now check if the requested current streams exist. If - * current >= num_streams, decrease current so at least - * we have output. Always keep it enabled. */ - for (n = 0; n < NUM_TYPES; n++) { - if (play_base_bin->current[n] >= group->type[n].npads) { - GST_DEBUG_OBJECT (play_base_bin, "reset type %d to current 0", n); - play_base_bin->current[n] = 0; - } - } - - /* now activate the right sources. Don't forget that during preroll, - * we set the first source to forwarding and ignored the rest. */ - for (n = 0; n < NUM_TYPES; n++) { - GST_DEBUG_OBJECT (play_base_bin, "setting type %d to current %d", n, - play_base_bin->current[n]); - set_active_source (play_base_bin, n + 1, play_base_bin->current[n]); - } -} - -/** - * has_all_raw_caps: - * @pad: a #GstPad - * @all_raw: pointer to hold the result - * - * check if the caps of the pad are all raw. The caps are all raw if - * all of its structures contain audio/x-raw or video/x-raw. - * - * Returns: %FALSE @pad has no caps. Else TRUE and @all_raw set t the result. - */ -static gboolean -has_all_raw_caps (GstPad * pad, gboolean * all_raw) -{ - GstCaps *caps; - gint capssize; - guint i, num_raw = 0; - gboolean res = FALSE; - - caps = gst_pad_get_caps (pad, NULL); - if (caps == NULL) - return FALSE; - - capssize = gst_caps_get_size (caps); - /* no caps, skip and move to the next pad */ - if (capssize == 0 || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) - goto done; - - /* count the number of raw formats in the caps */ - for (i = 0; i < capssize; ++i) { - GstStructure *s; - const gchar *mime_type; - - s = gst_caps_get_structure (caps, i); - mime_type = gst_structure_get_name (s); - - if (IS_RAW_MIME (mime_type)) - ++num_raw; - } - - *all_raw = (num_raw == capssize); - res = TRUE; - -done: - gst_caps_unref (caps); - return res; -} - -/** - * analyse_source: - * @play_base_bin: a #GstPlayBaseBin - * @is_raw: are all pads raw data - * @have_out: does the source have output - * @is_dynamic: is this a dynamic source - * - * Check the source of @play_base_bin and collect information about it. - * - * @is_raw will be set to TRUE if the source only produces raw pads. When this - * function returns, all of the raw pad of the source will be added - * to @play_base_bin. - * - * @have_out: will be set to TRUE if the source has output pads. - * - * @is_dynamic: TRUE if the element will create (more) pads dynamically later - * on. - * - * Returns: FALSE if a fatal error occured while scanning. - */ -static gboolean -analyse_source (GstPlayBaseBin * play_base_bin, gboolean * is_raw, - gboolean * have_out, gboolean * is_dynamic) -{ - GstIterator *pads_iter; - gboolean done = FALSE; - gboolean res = TRUE; - GValue item = { 0, }; - - *have_out = FALSE; - *is_raw = FALSE; - *is_dynamic = FALSE; - - pads_iter = gst_element_iterate_src_pads (play_base_bin->source); - while (!done) { - GstPad *pad = NULL; - - switch (gst_iterator_next (pads_iter, &item)) { - case GST_ITERATOR_ERROR: - res = FALSE; - /* FALLTROUGH */ - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_RESYNC: - /* reset results and resync */ - *have_out = FALSE; - *is_raw = FALSE; - *is_dynamic = FALSE; - gst_iterator_resync (pads_iter); - break; - case GST_ITERATOR_OK: - pad = g_value_get_object (&item); - /* we now officially have an ouput pad */ - *have_out = TRUE; - - /* if FALSE, this pad has no caps and we continue with the next pad. */ - if (!has_all_raw_caps (pad, is_raw)) { - g_value_reset (&item); - break; - } - - /* caps on source pad are all raw, we can add the pad */ - if (*is_raw) { - new_decoded_pad_full (play_base_bin->source, pad, FALSE, - play_base_bin, FALSE); - } - - g_value_reset (&item); - break; - } - } - g_value_unset (&item); - gst_iterator_free (pads_iter); - - if (!*have_out) { - GstElementClass *elemclass; - GList *walk; - - /* element has no output pads, check for padtemplates that list SOMETIMES - * pads. */ - elemclass = GST_ELEMENT_GET_CLASS (play_base_bin->source); - - walk = gst_element_class_get_pad_template_list (elemclass); - while (walk != NULL) { - GstPadTemplate *templ; - - templ = (GstPadTemplate *) walk->data; - if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) { - if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_SOMETIMES) { - *is_dynamic = TRUE; - break; /* only break out if we found a sometimes src pad - continue walking through if say a request src pad is found - elements such as mpegtsparse and dvbbasebin have request - and sometimes src pads */ - } - } - walk = g_list_next (walk); - } - } - - return res; -} - -static void -remove_decoders (GstPlayBaseBin * bin) -{ - GSList *walk; - - for (walk = bin->decoders; walk; walk = g_slist_next (walk)) { - GstElement *decoder = GST_ELEMENT_CAST (walk->data); - - GST_DEBUG_OBJECT (bin, "removing old decoder element"); - /* Disconnect all the signal handlers we attached to the decodebin before - * we dispose of it */ - g_signal_handlers_disconnect_by_func (decoder, - (gpointer) (decodebin_element_added_cb), bin); - g_signal_handlers_disconnect_by_func (decoder, - (gpointer) (decodebin_element_removed_cb), bin); - g_signal_handlers_disconnect_by_func (decoder, - (gpointer) (new_decoded_pad), bin); - g_signal_handlers_disconnect_by_func (decoder, - (gpointer) (no_more_pads), bin); - g_signal_handlers_disconnect_by_func (decoder, - (gpointer) (unknown_type), bin); - - gst_element_set_state (decoder, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (bin), decoder); - } - g_slist_free (bin->decoders); - bin->decoders = NULL; -} - -static GstElement * -make_decoder (GstPlayBaseBin * play_base_bin) -{ - GstElement *decoder; - - /* now create the decoder element */ - if (g_getenv ("USE_DECODEBIN2")) - decoder = gst_element_factory_make ("decodebin2", NULL); - else - decoder = gst_element_factory_make ("decodebin", NULL); - if (!decoder) - goto no_decodebin; - - g_signal_connect (decoder, "element-added", - G_CALLBACK (decodebin_element_added_cb), play_base_bin); - g_signal_connect (decoder, "element-removed", - G_CALLBACK (decodebin_element_removed_cb), play_base_bin); - - gst_bin_add (GST_BIN_CAST (play_base_bin), decoder); - - /* set up callbacks to create the links between decoded data - * and video/audio/subtitle rendering/output. */ - g_signal_connect (G_OBJECT (decoder), - "new-decoded-pad", G_CALLBACK (new_decoded_pad), play_base_bin); - g_signal_connect (G_OBJECT (decoder), "no-more-pads", - G_CALLBACK (no_more_pads), play_base_bin); - g_signal_connect (G_OBJECT (decoder), - "unknown-type", G_CALLBACK (unknown_type), play_base_bin); - g_object_set_data (G_OBJECT (decoder), "pending", GINT_TO_POINTER (1)); - play_base_bin->pending++; - - GST_DEBUG_OBJECT (play_base_bin, "created decodebin, %d pending", - play_base_bin->pending); - - play_base_bin->decoders = g_slist_prepend (play_base_bin->decoders, decoder); - - return decoder; - - /* ERRORS */ -no_decodebin: - { - GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, - (_("Could not create \"decodebin\" element.")), (NULL)); - return NULL; - } -} - -static void -remove_source (GstPlayBaseBin * bin) -{ - GstElement *source = bin->source; - - if (source) { - GST_DEBUG_OBJECT (bin, "removing old src element"); - gst_element_set_state (source, GST_STATE_NULL); - - if (bin->src_np_sig_id) { - g_signal_handler_disconnect (G_OBJECT (source), bin->src_np_sig_id); - bin->src_np_sig_id = 0; - } - if (bin->src_nmp_sig_id) { - g_signal_handler_disconnect (G_OBJECT (source), bin->src_nmp_sig_id); - bin->src_nmp_sig_id = 0; - } - gst_bin_remove (GST_BIN_CAST (bin), source); - bin->source = NULL; - } -} - -static GstBusSyncReply -subbin_startup_sync_msg (GstBus * bus, GstMessage * msg, gpointer user_data) -{ - if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { - GstPlayBaseBin *play_base_bin; - - play_base_bin = GST_PLAY_BASE_BIN (user_data); - if (!play_base_bin->subtitle_done) { - GST_WARNING_OBJECT (play_base_bin, "error starting up subtitle bin: %" - GST_PTR_FORMAT, msg); - play_base_bin->subtitle_done = TRUE; - GST_DEBUG_OBJECT (play_base_bin, "signal group done"); - GROUP_SIGNAL (play_base_bin); - GST_DEBUG_OBJECT (play_base_bin, "signaled group done"); - } - } - return GST_BUS_PASS; -} - -/* construct and run the source and decoder elements until we found - * all the streams or until a preroll queue has been filled. -*/ -static gboolean -setup_source (GstPlayBaseBin * play_base_bin) -{ - GstElement *subbin = NULL; - gboolean is_raw, have_out, is_dynamic; - - if (!play_base_bin->need_rebuild) - return TRUE; - play_base_bin->raw_decoding_mode = FALSE; - - GST_DEBUG_OBJECT (play_base_bin, "setup source"); - - /* delete old src */ - remove_source (play_base_bin); - - /* create and configure an element that can handle the uri */ - if (!(play_base_bin->source = gen_source_element (play_base_bin, &subbin))) - goto no_source; - - /* state will be merged later - if file is not found, error will be - * handled by the application right after. */ - gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->source); - g_object_notify (G_OBJECT (play_base_bin), "source"); - - /* remove the old decoders now, if any */ - remove_decoders (play_base_bin); - - /* remove our previous preroll queues */ - remove_groups (play_base_bin); - - /* clear pending dynamic elements */ - play_base_bin->pending = 0; - - /* do subs */ - if (subbin) { - GstElement *db; - GstBus *bus; - - play_base_bin->subtitle = subbin; - db = gst_bin_get_by_name (GST_BIN_CAST (subbin), "subtitle-decoder"); - - /* do type detection, without adding (so no preroll) */ - g_signal_connect (G_OBJECT (db), "new-decoded-pad", - G_CALLBACK (subs_new_decoded_pad), play_base_bin); - g_signal_connect (G_OBJECT (db), "no-more-pads", - G_CALLBACK (sub_no_more_pads), play_base_bin); - g_signal_connect (G_OBJECT (db), "unknown-type", - G_CALLBACK (unknown_type), play_base_bin); - g_object_set_data (G_OBJECT (db), "pending", GINT_TO_POINTER (1)); - play_base_bin->pending++; - - GST_DEBUG_OBJECT (play_base_bin, "we have subtitles, %d pending", - play_base_bin->pending); - - if (!play_base_bin->is_stream) { - GstStateChangeReturn sret; - - /* either when the queues are filled or when the decoder element - * has no more dynamic streams, the cond is unlocked. We can remove - * the signal handlers then - */ - GST_DEBUG_OBJECT (play_base_bin, "starting subtitle bin"); - - /* for subtitles in a separate bin we will not commit the - * current building group since we need to add the other - * audio/video streams to the group. We check if we managed - * to commit the subtitle group using an extra flag. */ - play_base_bin->subtitle_done = FALSE; - - /* since subbin is still a stand-alone bin, we need to add a custom bus - * to intercept error messages, so we can stop waiting and continue */ - bus = gst_bus_new (); - gst_element_set_bus (subbin, bus); - gst_bus_set_sync_handler (bus, subbin_startup_sync_msg, play_base_bin); - - sret = gst_element_set_state (subbin, GST_STATE_PAUSED); - if (sret != GST_STATE_CHANGE_FAILURE) { - GROUP_LOCK (play_base_bin); - GST_DEBUG ("waiting for subtitle to complete..."); - while (!play_base_bin->subtitle_done) - GROUP_WAIT (play_base_bin); - GST_DEBUG ("group done !"); - GROUP_UNLOCK (play_base_bin); - - if (!play_base_bin->building_group || - play_base_bin->building_group->type[GST_STREAM_TYPE_TEXT - - 1].npads == 0) { - - GST_DEBUG ("No subtitle found - ignoring"); - gst_element_set_state (subbin, GST_STATE_NULL); - gst_object_unref (play_base_bin->subtitle); - play_base_bin->subtitle = NULL; - } else { - GST_DEBUG_OBJECT (play_base_bin, "Subtitle set-up successful"); - } - } else { - GST_WARNING_OBJECT (play_base_bin, "Failed to start subtitle bin"); - gst_element_set_state (subbin, GST_STATE_NULL); - gst_object_unref (play_base_bin->subtitle); - play_base_bin->subtitle = NULL; - } - - gst_bus_set_sync_handler (bus, NULL, NULL); - gst_element_set_bus (subbin, NULL); - gst_object_unref (bus); - } - gst_object_unref (db); - } - /* see if the source element emits raw audio/video all by itself, - * if so, we can create streams for the pads and be done with it. - * Also check that is has source pads, if not, we assume it will - * do everything itself. */ - if (!analyse_source (play_base_bin, &is_raw, &have_out, &is_dynamic)) - goto invalid_source; - - if (is_raw) { - GST_DEBUG_OBJECT (play_base_bin, "Source provides all raw data"); - /* source provides raw data, we added the pads and we can now signal a - * no_more pads because we are done. */ - group_commit (play_base_bin, play_base_bin->is_stream, FALSE); - return TRUE; - } - if (!have_out && !is_dynamic) { - GST_DEBUG_OBJECT (play_base_bin, "Source has no output pads"); - /* create a stream to indicate that this uri is handled by a self - * contained element. We are now done. */ - add_element_stream (play_base_bin->source, play_base_bin); - group_commit (play_base_bin, play_base_bin->is_stream, FALSE); - return TRUE; - } - if (is_dynamic) { - /* connect a handler for the new-pad signal */ - play_base_bin->src_np_sig_id = - g_signal_connect (G_OBJECT (play_base_bin->source), "pad-added", - G_CALLBACK (source_new_pad), play_base_bin); - play_base_bin->src_nmp_sig_id = - g_signal_connect (G_OBJECT (play_base_bin->source), "no-more-pads", - G_CALLBACK (source_no_more_pads), play_base_bin); - g_object_set_data (G_OBJECT (play_base_bin->source), "pending", - GINT_TO_POINTER (1)); - play_base_bin->pending++; - GST_DEBUG_OBJECT (play_base_bin, - "Source has dynamic output pads, %d pending", play_base_bin->pending); - } else { - GstElement *decoder; - - /* no dynamic source, we can link now */ - decoder = make_decoder (play_base_bin); - if (!decoder) - goto no_decodebin; - - if (!gst_element_link (play_base_bin->source, decoder)) - goto could_not_link; - } - - if (play_base_bin->subtitle) - gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); - - play_base_bin->need_rebuild = FALSE; - - return TRUE; - - /* ERRORS */ -no_source: - { - /* error message was already posted */ - return FALSE; - } -invalid_source: - { - GST_ELEMENT_ERROR (play_base_bin, CORE, FAILED, - (_("Source element is invalid.")), (NULL)); - return FALSE; - } -no_decodebin: - { - /* message was posted */ - return FALSE; - } -could_not_link: - { - GST_ELEMENT_ERROR (play_base_bin, CORE, NEGOTIATION, - (NULL), ("Can't link source to decoder element")); - return FALSE; - } -} - -static void -finish_source (GstPlayBaseBin * play_base_bin) -{ - /* FIXME: no need to grab the group lock here? (tpm) */ - if (get_active_group (play_base_bin) != NULL) { - if (play_base_bin->subtitle) { - /* make subs iterate from now on */ - gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); - } - } -} - -/* - * Caller must have group-lock held. - * - * We iterate over all detected streams in the streaminfo and try to find - * impossible cases, like subtitles without video. - */ -static gboolean -prepare_output (GstPlayBaseBin * play_base_bin) -{ - const GList *item; - gboolean stream_found = FALSE, no_media = FALSE; - gboolean got_video = FALSE, got_subtitle = FALSE; - GstPlayBaseGroup *group; - - group = get_active_group (play_base_bin); - - /* check if we found any supported stream... if not, then - * we detected stream type (or the above would've failed), - * but linking/decoding failed - plugin probably missing. */ - for (item = group ? group->streaminfo : NULL; item != NULL; item = item->next) { - GstStreamInfo *info = GST_STREAM_INFO (item->data); - - if (info->type == GST_STREAM_TYPE_VIDEO) { - stream_found = TRUE; - got_video = TRUE; - break; - } else if (info->type == GST_STREAM_TYPE_ELEMENT) { - stream_found = TRUE; - } else if (info->type == GST_STREAM_TYPE_AUDIO) { - stream_found = TRUE; - } else if (info->type == GST_STREAM_TYPE_TEXT || - info->type == GST_STREAM_TYPE_SUBPICTURE) { - got_subtitle = TRUE; - } else if (!item->prev && !item->next) { - /* We're no audio/video and the only stream... We could - * be something not-media that's detected because then our - * typefind doesn't mess up with mp3 (bz2, gz, elf, ...) */ - if (info->caps && !gst_caps_is_empty (info->caps)) { - const gchar *mime = - gst_structure_get_name (gst_caps_get_structure (info->caps, 0)); - - no_media = IS_NO_MEDIA_MIME (mime); - } - } - } - - if (!stream_found) { - if (got_subtitle) { - GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, - (_("Only a subtitle stream was detected. " - "Either you are loading a subtitle file or some other type of " - "text file, or the media file was not recognized.")), (NULL)); - } else if (!no_media) { - GST_ELEMENT_ERROR (play_base_bin, STREAM, CODEC_NOT_FOUND, - (_("You do not have a decoder installed to handle this file. " - "You might need to install the necessary plugins.")), (NULL)); - } else { - GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, - (_("This is not a media file")), (NULL)); - } - return FALSE; - } else if (got_subtitle && !got_video) { - GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, - (_("A subtitle stream was detected, but no video stream.")), (NULL)); - return FALSE; - } - - return TRUE; -} - -/* - * Multi-stream management. -1 = none. - * - * Caller has group-lock held. - */ -static gint -get_active_source (GstPlayBaseBin * play_base_bin, GstStreamType type) -{ - GstPlayBaseGroup *group; - GList *s; - gint num = 0; - - group = get_active_group (play_base_bin); - if (!group) - return -1; - - for (s = group->streaminfo; s; s = s->next) { - GstStreamInfo *info = s->data; - - if (info->type == type) { - if (!info->mute && !g_object_get_data (G_OBJECT (info), "mute_probe")) { - return num; - } else { - num++; - } - } - } - - return -1; -} - -/* Kill pad reactivation on state change. */ - -#if 0 -static void muted_group_change_state (GstElement * element, - gint old_state, gint new_state, gpointer data); -#endif - -static void -mute_group_type (GstPlayBaseGroup * group, GstStreamType type, gboolean mute) -{ - gboolean active = !mute; - GstPad *pad; - - pad = gst_element_get_static_pad (group->type[type - 1].preroll, "src"); - gst_pad_set_active (pad, active); - gst_object_unref (pad); - pad = gst_element_get_static_pad (group->type[type - 1].preroll, "sink"); - gst_pad_set_active (pad, active); - gst_object_unref (pad); - pad = gst_element_get_static_pad (group->type[type - 1].selector, "src"); - gst_pad_set_active (pad, active); - gst_object_unref (pad); - -#if 0 - if (mute) { - g_signal_connect (group->type[type - 1].preroll, "state-changed", - G_CALLBACK (muted_group_change_state), group); - } else { - g_signal_handlers_disconnect_by_func (group->type[type - 1].preroll, - G_CALLBACK (muted_group_change_state), group); - } -#endif -} - -#if 0 -static void -muted_group_change_state (GstElement * element, - gint old_state, gint new_state, gpointer data) -{ - GstPlayBaseGroup *group = data; - - GROUP_LOCK (group->bin); - - if (new_state == GST_STATE_PLAYING) { - gint n; - - for (n = 0; n < NUM_TYPES; n++) { - if (group->type[n].selector == element) { - mute_group_type (group, n + 1, TRUE); - } - } - } - - GROUP_UNLOCK (group->bin); -} -#endif - -static void -set_subtitles_visible (GstPlayBaseBin * play_base_bin, gboolean visible) -{ - GstPlayBaseBinClass *klass = GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin); - - /* we use a vfunc for this since we don't have a reference to the - * textoverlay element, but playbin does */ - if (klass != NULL && klass->set_subtitles_visible != NULL) - klass->set_subtitles_visible (play_base_bin, visible); -} - -static void -set_audio_mute (GstPlayBaseBin * play_base_bin, gboolean mute) -{ - GstPlayBaseBinClass *klass = GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin); - - /* we use a vfunc for this since we don't have a reference to the - * textoverlay element, but playbin does */ - if (klass != NULL && klass->set_audio_mute != NULL) - klass->set_audio_mute (play_base_bin, mute); -} - -/* - * Caller has group-lock held. - */ - -static void -set_active_source (GstPlayBaseBin * play_base_bin, - GstStreamType type, gint source_num) -{ - GstPlayBaseGroup *group; - GList *s; - gint num = 0; - gboolean have_active = FALSE; - GstElement *sel; - - GST_LOG ("Changing active source of type %d to %d", type, source_num); - play_base_bin->current[type - 1] = source_num; - - group = get_active_group (play_base_bin); - if (!group || !group->type[type - 1].preroll) { - GST_LOG ("No active group, or group for type %d has no preroll", type); - return; - } - - /* HACK: instead of unlinking the subtitle input (= lots of hassle, - * especially if subtitles come from an external source), just tell - * textoverlay not to render them */ - if (type == GST_STREAM_TYPE_TEXT) { - gboolean visible = (source_num != -1); - - set_subtitles_visible (play_base_bin, visible); - if (!visible) - return; - } else if (type == GST_STREAM_TYPE_AUDIO) { - gboolean mute = (source_num == -1); - - set_audio_mute (play_base_bin, mute); - - if (mute) - return; - } - - sel = group->type[type - 1].selector; - - for (s = group->streaminfo; s; s = s->next) { - GstStreamInfo *info = s->data; - - if (info->type == type) { - if (num == source_num) { - GstPad *sel_pad; - - GST_LOG ("Unmuting (if already muted) source %d of type %d", source_num, - type); - g_object_set (info, "mute", FALSE, NULL); - - /* Tell the stream selector which pad to accept */ - sel_pad = GST_PAD_CAST (g_object_get_data (G_OBJECT (info->object), - "pb_sel_pad")); - - if (sel && sel_pad != NULL) { - g_object_set (G_OBJECT (sel), "active-pad", sel_pad, NULL); - } - - have_active = TRUE; - } else { - guint id; - - GST_LOG_OBJECT (info->object, "Muting source %d of type %d", num, type); - - id = gst_pad_add_probe (GST_PAD_CAST (info->object), - GST_PROBE_TYPE_BUFFER, mute_stream, info, NULL); - g_object_set_data (G_OBJECT (info), "mute_probe", GINT_TO_POINTER (id)); - } - num++; - } - } - - if (!have_active) { - GST_LOG ("Muting group type: %d", type); - g_object_set (sel, "active-pad", NULL, NULL); - } else { - GST_LOG ("Unmuting group type: %d", type); - } - mute_group_type (group, type, !have_active); -} - -static void -gst_play_base_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstPlayBaseBin *play_base_bin; - - g_return_if_fail (GST_IS_PLAY_BASE_BIN (object)); - - play_base_bin = GST_PLAY_BASE_BIN (object); - - switch (prop_id) { - case ARG_URI: - { - const gchar *uri = g_value_get_string (value); - - if (uri == NULL) { - g_warning ("cannot set NULL uri"); - return; - } - /* if we have no previous uri, or the new uri is different from the - * old one, replug */ - if (play_base_bin->uri == NULL || strcmp (play_base_bin->uri, uri) != 0) { - g_free (play_base_bin->uri); - play_base_bin->uri = g_strdup (uri); - - GST_DEBUG ("setting new uri to %s", uri); - - play_base_bin->need_rebuild = TRUE; - } - break; - } - case ARG_SUBURI:{ - const gchar *suburi = g_value_get_string (value); - - if ((!suburi && !play_base_bin->suburi) || - (suburi && play_base_bin->suburi && - !strcmp (play_base_bin->suburi, suburi))) - return; - g_free (play_base_bin->suburi); - play_base_bin->suburi = g_strdup (suburi); - GST_DEBUG ("setting new .sub uri to %s", suburi); - play_base_bin->need_rebuild = TRUE; - break; - } - case ARG_QUEUE_SIZE: - play_base_bin->queue_size = g_value_get_uint64 (value); - break; - case ARG_QUEUE_THRESHOLD: - play_base_bin->queue_threshold = g_value_get_uint64 (value); - break; - case ARG_QUEUE_MIN_THRESHOLD: - play_base_bin->queue_min_threshold = g_value_get_uint64 (value); - break; - case ARG_CONNECTION_SPEED: - play_base_bin->connection_speed = g_value_get_uint (value) * 1000; - break; - case ARG_VIDEO: - GROUP_LOCK (play_base_bin); - set_active_source (play_base_bin, - GST_STREAM_TYPE_VIDEO, g_value_get_int (value)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_AUDIO: - GROUP_LOCK (play_base_bin); - set_active_source (play_base_bin, - GST_STREAM_TYPE_AUDIO, g_value_get_int (value)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_TEXT: - GROUP_LOCK (play_base_bin); - set_active_source (play_base_bin, - GST_STREAM_TYPE_TEXT, g_value_get_int (value)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_SUBTITLE_ENCODING: - { - const gchar *encoding; - GSList *list; - - encoding = g_value_get_string (value); - if (encoding && play_base_bin->subencoding && - !strcmp (play_base_bin->subencoding, encoding)) { - return; - } - if (encoding == NULL && play_base_bin->subencoding == NULL) - return; - - g_mutex_lock (play_base_bin->sub_lock); - g_free (play_base_bin->subencoding); - play_base_bin->subencoding = g_strdup (encoding); - list = g_slist_copy (play_base_bin->subtitle_elements); - g_slist_foreach (list, (GFunc) gst_object_ref, NULL); - g_mutex_unlock (play_base_bin->sub_lock); - - /* we can't hold a lock when calling g_object_set() on a child, since - * the notify event will trigger GstObject to send a deep-notify event - * which will try to take the lock ... */ - g_slist_foreach (list, (GFunc) set_encoding_element, (gpointer) encoding); - g_slist_foreach (list, (GFunc) gst_object_unref, NULL); - g_slist_free (list); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_play_base_bin_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstPlayBaseBin *play_base_bin; - - g_return_if_fail (GST_IS_PLAY_BASE_BIN (object)); - - play_base_bin = GST_PLAY_BASE_BIN (object); - - switch (prop_id) { - case ARG_URI: - g_value_set_string (value, play_base_bin->uri); - break; - case ARG_SUBURI: - g_value_set_string (value, play_base_bin->suburi); - break; - case ARG_NSTREAMS: - { - GstPlayBaseGroup *group; - - GROUP_LOCK (play_base_bin); - group = get_active_group (play_base_bin); - if (group) { - g_value_set_int (value, group->nstreams); - } else { - g_value_set_int (value, 0); - } - GROUP_UNLOCK (play_base_bin); - break; - } - case ARG_QUEUE_SIZE: - g_value_set_uint64 (value, play_base_bin->queue_size); - break; - case ARG_QUEUE_THRESHOLD: - g_value_set_uint64 (value, play_base_bin->queue_threshold); - break; - case ARG_QUEUE_MIN_THRESHOLD: - g_value_set_uint64 (value, play_base_bin->queue_min_threshold); - break; - case ARG_CONNECTION_SPEED: - g_value_set_uint (value, play_base_bin->connection_speed / 1000); - break; - case ARG_STREAMINFO: - /* FIXME: hold some kind of lock here, use iterator */ - g_value_set_pointer (value, - (gpointer) gst_play_base_bin_get_streaminfo (play_base_bin)); - break; - case ARG_STREAMINFO_VALUES:{ - GValueArray *copy; - - copy = gst_play_base_bin_get_streaminfo_value_array (play_base_bin); - g_value_take_boxed (value, copy); - break; - } - case ARG_SOURCE: - g_value_set_object (value, play_base_bin->source); - break; - case ARG_VIDEO: - GROUP_LOCK (play_base_bin); - g_value_set_int (value, get_active_source (play_base_bin, - GST_STREAM_TYPE_VIDEO)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_AUDIO: - GROUP_LOCK (play_base_bin); - g_value_set_int (value, get_active_source (play_base_bin, - GST_STREAM_TYPE_AUDIO)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_TEXT: - GROUP_LOCK (play_base_bin); - g_value_set_int (value, get_active_source (play_base_bin, - GST_STREAM_TYPE_TEXT)); - GROUP_UNLOCK (play_base_bin); - break; - case ARG_SUBTITLE_ENCODING: - GST_OBJECT_LOCK (play_base_bin); - g_value_set_string (value, play_base_bin->subencoding); - GST_OBJECT_UNLOCK (play_base_bin); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstStateChangeReturn -gst_play_base_bin_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstPlayBaseBin *play_base_bin; - - play_base_bin = GST_PLAY_BASE_BIN (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - if (!setup_source (play_base_bin)) - goto source_failed; - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - if (ret == GST_STATE_CHANGE_FAILURE) - goto cleanup_groups; - - finish_source (play_base_bin); - break; - /* clean-up in both cases, READY=>NULL clean-up is if there was an error */ - case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: - play_base_bin->need_rebuild = TRUE; - remove_decoders (play_base_bin); - remove_groups (play_base_bin); - remove_source (play_base_bin); - break; - default: - break; - } - return ret; - - /* ERRORS */ -source_failed: - { - play_base_bin->need_rebuild = TRUE; - - return GST_STATE_CHANGE_FAILURE; - } -cleanup_groups: - { - /* clean up leftover groups */ - remove_groups (play_base_bin); - play_base_bin->need_rebuild = TRUE; - - return GST_STATE_CHANGE_FAILURE; - } -} - -static const GList * -gst_play_base_bin_get_streaminfo (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group = get_active_group (play_base_bin); - GList *info = NULL; - - if (group) { - info = group->streaminfo; - } - return info; -} - -static GValueArray * -gst_play_base_bin_get_streaminfo_value_array (GstPlayBaseBin * play_base_bin) -{ - GstPlayBaseGroup *group; - GValueArray *array = NULL; - - GROUP_LOCK (play_base_bin); - group = get_active_group (play_base_bin); - if (group) { - array = g_value_array_copy (group->streaminfo_value_array); - } else { - array = g_value_array_new (0); - } - GROUP_UNLOCK (play_base_bin); - - return array; -} diff --git a/gst/playback/gstplaybasebin.h b/gst/playback/gstplaybasebin.h deleted file mode 100644 index c8c8649..0000000 --- a/gst/playback/gstplaybasebin.h +++ /dev/null @@ -1,126 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * <2007> Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_PLAYBASEBIN_H__ -#define __GST_PLAYBASEBIN_H__ - -#include -#include "gststreaminfo.h" - -G_BEGIN_DECLS - -#define GST_TYPE_PLAY_BASE_BIN (gst_play_base_bin_get_type()) -#define GST_PLAY_BASE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BASE_BIN,GstPlayBaseBin)) -#define GST_PLAY_BASE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BASE_BIN,GstPlayBaseBinClass)) -#define GST_IS_PLAY_BASE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BASE_BIN)) -#define GST_IS_PLAY_BASE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BASE_BIN)) -#define GST_PLAY_BASE_BIN_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLAY_BASE_BIN, \ - GstPlayBaseBinClass)) - -typedef struct _GstPlayBaseBin GstPlayBaseBin; -typedef struct _GstPlayBaseBinClass GstPlayBaseBinClass; - -/* a GstPlayBaseGroup is a group of pads and streaminfo that together - * make up a playable stream. A new group is created from the current - * set of pads that are alive when the preroll elements are filled or - * when the no-more-pads signal is fired. - * - * We have to queue the groups as they can be created while the preroll - * queues are still playing the old group. We monitor the EOS signals - * on the preroll queues and when all the streams in the current group - * have EOSed, we switch to the next queued group. - */ -typedef struct -{ - GstPlayBaseBin *bin; /* ref to the owner */ - - gint nstreams; - GList *streaminfo; - GValueArray *streaminfo_value_array; - - /* contained decoded elementary streams */ - struct { - gint npads; - GstBin *bin; - GstElement *preroll; - GstElement *selector; - gboolean done; -#define NUM_TYPES 4 - } type[NUM_TYPES]; /* AUDIO, VIDEO, TEXT, SUBPIC */ -} GstPlayBaseGroup; - -struct _GstPlayBaseBin { - GstPipeline pipeline; - - /* properties */ - guint64 queue_size; - guint64 queue_threshold; - guint64 queue_min_threshold; - /* connection speed in bits/sec (0 = unknown) */ - guint connection_speed; - - - /* currently loaded media */ - gint current[NUM_TYPES]; - gchar *uri, *suburi; - gboolean is_stream; - GstElement *source; - GSList *decoders; - GstElement *subtitle; /* additional filesrc ! subparse bin */ - gboolean subtitle_done; - gboolean need_rebuild; - gboolean raw_decoding_mode; /* Use smaller queues when source outputs raw data */ - - GSList *subtitle_elements; /* subtitle elements that have 'subtitle-encoding' property */ - gchar *subencoding; /* encoding to propagate to the above subtitle elements */ - GMutex *sub_lock; /* protecting subtitle_elements and subencoding members */ - - /* group management - using own lock */ - GMutex *group_lock; /* lock and mutex to signal availability of new group */ - GCond *group_cond; - GstPlayBaseGroup *building_group; /* the group that we are constructing */ - GList *queued_groups; /* the constructed groups, head is the active one */ - - /* for dynamic sources */ - guint src_np_sig_id; /* new-pad signal id */ - guint src_nmp_sig_id; /* no-more-pads signal id */ - gint pending; -}; - -struct _GstPlayBaseBinClass { - GstPipelineClass parent_class; - - /* virtual fuctions */ - gboolean (*setup_output_pads) (GstPlayBaseBin *play_base_bin, - GstPlayBaseGroup *group); - - void (*set_subtitles_visible) (GstPlayBaseBin *play_base_bin, - gboolean visible); - void (*set_audio_mute) (GstPlayBaseBin *play_base_bin, - gboolean mute); -}; - -GType gst_play_base_bin_get_type (void); - -G_END_DECLS - -#endif /* __GST_PLAYBASEBIN_H__ */ - diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c deleted file mode 100644 index 8ff5e98..0000000 --- a/gst/playback/gstplaybin.c +++ /dev/null @@ -1,1991 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:element-playbin - * - * Playbin provides a stand-alone everything-in-one abstraction for an - * audio and/or video player. - * - * - * This element is deprecated and no longer supported. You should use - * the #playbin2 element instead. - * - * - * It can handle both audio and video files and features - * - * - * automatic file type recognition and based on that automatic - * selection and usage of the right audio/video/subtitle demuxers/decoders - * - * - * visualisations for audio files - * - * - * subtitle support for video files - * - * - * stream selection between different audio/subtitles streams - * - * - * meta info (tag) extraction - * - * - * easy access to the last video frame - * - * - * buffering when playing streams over a network - * - * - * volume control - * - * - * - * - * Usage - * - * A playbin element can be created just like any other element using - * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin:uri - * property. This must be an absolute URI, relative file paths are not allowed. - * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg - * - * Playbin is a #GstPipeline. It will notify the application of everything - * that's happening (errors, end of stream, tags found, state changes, etc.) - * by posting messages on its #GstBus. The application needs to watch the - * bus. - * - * Playback can be initiated by setting the element to PLAYING state using - * gst_element_set_state(). Note that the state change will take place in - * the background in a separate thread, when the function returns playback - * is probably not happening yet and any errors might not have occured yet. - * Applications using playbin should ideally be written to deal with things - * completely asynchroneous. - * - * When playback has finished (an EOS message has been received on the bus) - * or an error has occured (an ERROR message has been received on the bus) or - * the user wants to play a different track, playbin should be set back to - * READY or NULL state, then the #GstPlayBin:uri property should be set to the - * new location and then playbin be set to PLAYING state again. - * - * Seeking can be done using gst_element_seek_simple() or gst_element_seek() - * on the playbin element. Again, the seek will not be executed - * instantaneously, but will be done in a background thread. When the seek - * call returns the seek will most likely still be in process. An application - * may wait for the seek to finish (or fail) using gst_element_get_state() with - * -1 as the timeout, but this will block the user interface and is not - * recommended at all. - * - * Applications may query the current position and duration of the stream - * via gst_element_query_position() and gst_element_query_duration() and - * setting the format passed to GST_FORMAT_TIME. If the query was successful, - * the duration or position will have been returned in units of nanoseconds. - * - * - * - * Advanced Usage: specifying the audio and video sink - * - * By default, if no audio sink or video sink has been specified via the - * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property, playbin will use - * the autoaudiosink and autovideosink elements to find the first-best - * available output method. - * This should work in most cases, but is not always desirable. Often either - * the user or application might want to specify more explicitly what to use - * for audio and video output. - * - * If the application wants more control over how audio or video should be - * output, it may create the audio/video sink elements itself (for example - * using gst_element_factory_make()) and provide them to playbin using the - * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property. - * - * GNOME-based applications, for example, will usually want to create - * gconfaudiosink and gconfvideosink elements and make playbin use those, - * so that output happens to whatever the user has configured in the GNOME - * Multimedia System Selector confinguration dialog. - * - * The sink elements do not necessarily need to be ready-made sinks. It is - * possible to create container elements that look like a sink to playbin, - * but in reality contain a number of custom elements linked together. This - * can be achieved by creating a #GstBin and putting elements in there and - * linking them, and then creating a sink #GstGhostPad for the bin and pointing - * it to the sink pad of the first element within the bin. This can be used - * for a number of purposes, for example to force output to a particular - * format or to modify or observe the data before it is output. - * - * It is also possible to 'suppress' audio and/or video output by using - * 'fakesink' elements (or capture it from there using the fakesink element's - * "handoff" signal, which, nota bene, is fired from the streaming thread!). - * - * - * - * Retrieving Tags and Other Meta Data - * - * Most of the common meta data (artist, title, etc.) can be retrieved by - * watching for TAG messages on the pipeline's bus (see above). - * - * Other more specific meta information like width/height/framerate of video - * streams or samplerate/number of channels of audio streams can be obtained - * using the #GstPlayBaseBin:stream-info property, which will return a GList of - * stream info objects, one for each stream. These are opaque objects that can - * only be accessed via the standard GObject property interface, ie. g_object_get(). - * Each stream info object has the following properties: - * - * "object" (GstObject) (the decoder source pad usually) - * "type" (enum) (if this is an audio/video/subtitle stream) - * "decoder" (string) (name of decoder used to decode this stream) - * "mute" (boolean) (to mute or unmute this stream) - * "caps" (GstCaps) (caps of the decoded stream) - * "language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams) - * "codec" (string) (format this stream was encoded in) - * - * Stream information from the #GstPlayBaseBin:stream-info property is best queried once - * playbin has changed into PAUSED or PLAYING state (which can be detected - * via a state-changed message on the #GstBus where old_state=READY and - * new_state=PAUSED), since before that the list might not be complete yet or - * not contain all available information (like language-codes). - * - * - * - * Buffering - * Playbin handles buffering automatically for the most part, but applications - * need to handle parts of the buffering process as well. Whenever playbin is - * buffering, it will post BUFFERING messages on the bus with a percentage - * value that shows the progress of the buffering process. Applications need - * to set playbin to PLAYING or PAUSED state in response to these messages. - * They may also want to convey the buffering progress to the user in some - * way. Here is how to extract the percentage information from the message - * (requires GStreamer >= 0.10.11): - * |[ - * switch (GST_MESSAGE_TYPE (msg)) { - * case GST_MESSAGE_BUFFERING: { - * gint percent = 0; - * gst_message_parse_buffering (msg, &percent); - * g_print ("Buffering (%%u percent done)", percent); - * break; - * } - * ... - * } - * ]| - * Note that applications should keep/set the pipeline in the PAUSED state when - * a BUFFERING message is received with a buffer percent value < 100 and set - * the pipeline back to PLAYING state when a BUFFERING message with a value - * of 100 percent is received (if PLAYING is the desired state, that is). - * - * - * Embedding the video window in your application - * By default, playbin (or rather the video sinks used) will create their own - * window. Applications will usually want to force output to a window of their - * own, however. This can be done using the #GstXOverlay interface, which most - * video sinks implement. See the documentation there for more details. - * - * - * Specifying which CD/DVD device to use - * The device to use for CDs/DVDs needs to be set on the source element - * playbin creates before it is opened. The only way to do this at the moment - * is to connect to playbin's "notify::source" signal, which will be emitted - * by playbin when it has created the source element for a particular URI. - * In the signal callback you can check if the source element has a "device" - * property and set it appropriately. In future ways might be added to specify - * the device as part of the URI, but at the time of writing this is not - * possible yet. - * - * - * Examples - * |[ - * gst-launch -v playbin uri=file:///path/to/somefile.avi - * ]| This will play back the given AVI video file, given that the video and - * audio decoders required to decode the content are installed. Since no - * special audio sink or video sink is supplied (not possible via gst-launch), - * playbin will try to find a suitable audio and video sink automatically - * using the autoaudiosink and autovideosink elements. - * |[ - * gst-launch -v playbin uri=cdda://4 - * ]| This will play back track 4 on an audio CD in your disc drive (assuming - * the drive is detected automatically by the plugin). - * |[ - * gst-launch -v playbin uri=dvd://1 - * ]| This will play back title 1 of a DVD in your disc drive (assuming - * the drive is detected automatically by the plugin). - * - * - * Deprecated: use playbin2 instead - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include -#include - -#include "gstplaybasebin.h" -#include "gstplayback.h" - -GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug); -#define GST_CAT_DEFAULT gst_play_bin_debug - -#define GST_TYPE_PLAY_BIN (gst_play_bin_get_type()) -#define GST_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin)) -#define GST_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass)) -#define GST_IS_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN)) -#define GST_IS_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN)) - -#define VOLUME_MAX_DOUBLE 10.0 - -typedef struct _GstPlayBin GstPlayBin; -typedef struct _GstPlayBinClass GstPlayBinClass; - -/** - * GstPlayBin: - * - * High-level player element - */ -struct _GstPlayBin -{ - GstPlayBaseBin parent; - - /* the configurable elements */ - GstElement *fakesink; - GstElement *audio_sink; - GstElement *video_sink; - GstElement *visualisation; - GstElement *pending_visualisation; - GstElement *volume_element; - GstElement *textoverlay_element; - GstElement *spu_element; - gfloat volume; - - /* these are the currently active sinks */ - GList *sinks; - - /* the last captured frame for snapshots */ - GstBuffer *frame; - - /* our cache for the sinks */ - GHashTable *cache; - - /* font description */ - gchar *font_desc; - - /* indication if the pipeline is live */ - gboolean is_live; - - /* probes */ - gulong text_id; - gulong sub_id; -}; - -struct _GstPlayBinClass -{ - GstPlayBaseBinClass parent_class; -}; - -/* props */ -enum -{ - ARG_0, - ARG_AUDIO_SINK, - ARG_VIDEO_SINK, - ARG_VIS_PLUGIN, - ARG_VOLUME, - ARG_FRAME, - ARG_FONT_DESC -}; - -/* signals */ -enum -{ - LAST_SIGNAL -}; - -static void gst_play_bin_class_init (GstPlayBinClass * klass); -static void gst_play_bin_init (GstPlayBin * play_bin); -static void gst_play_bin_dispose (GObject * object); - -static gboolean setup_sinks (GstPlayBaseBin * play_base_bin, - GstPlayBaseGroup * group); -static void remove_sinks (GstPlayBin * play_bin); -static void playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin, - gboolean visible); -static void playbin_set_audio_mute (GstPlayBaseBin * play_base_bin, - gboolean mute); - -static void gst_play_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * spec); -static void gst_play_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * spec); - -static gboolean gst_play_bin_send_event (GstElement * element, - GstEvent * event); -static GstStateChangeReturn gst_play_bin_change_state (GstElement * element, - GstStateChange transition); - -static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message); - -static GstElementClass *parent_class; - -//static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 }; - -static GType -gst_play_bin_get_type (void) -{ - static GType gst_play_bin_type = 0; - - if (!gst_play_bin_type) { - static const GTypeInfo gst_play_bin_info = { - sizeof (GstPlayBinClass), - NULL, - NULL, - (GClassInitFunc) gst_play_bin_class_init, - NULL, - NULL, - sizeof (GstPlayBin), - 0, - (GInstanceInitFunc) gst_play_bin_init, - NULL - }; - - gst_play_bin_type = g_type_register_static (GST_TYPE_PLAY_BASE_BIN, - "GstPlayBin", &gst_play_bin_info, 0); - } - - return gst_play_bin_type; -} - -static void -gst_play_bin_class_init (GstPlayBinClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - GstBinClass *gstbin_klass; - GstPlayBaseBinClass *playbasebin_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - gstbin_klass = (GstBinClass *) klass; - playbasebin_klass = (GstPlayBaseBinClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_klass->set_property = gst_play_bin_set_property; - gobject_klass->get_property = gst_play_bin_get_property; - - g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK, - g_param_spec_object ("video-sink", "Video Sink", - "the video output element to use (NULL = default sink)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK, - g_param_spec_object ("audio-sink", "Audio Sink", - "the audio output element to use (NULL = default sink)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN, - g_param_spec_object ("vis-plugin", "Vis plugin", - "the visualization element to use (NULL = none)", - GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstPlayBin:volume: - * - * Get or set the current audio stream volume. 1.0 means 100%, - * 0.0 means mute. This uses a linear volume scale. - * - */ - g_object_class_install_property (gobject_klass, ARG_VOLUME, - g_param_spec_double ("volume", "volume", "volume", - 0.0, VOLUME_MAX_DOUBLE, 1.0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_FRAME, - g_param_spec_boxed ("frame", "Frame", - "The last frame (NULL = no video available)", GST_TYPE_BUFFER, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_FONT_DESC, - g_param_spec_string ("subtitle-font-desc", "Subtitle font description", - "Pango font description of font " "to be used for subtitle rendering", - NULL, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); - - gobject_klass->dispose = gst_play_bin_dispose; - - gst_element_class_set_details_simple (gstelement_klass, - "Player Bin", "Generic/Bin/Player", - "Autoplug and play media from an uri", - "Wim Taymans "); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_play_bin_change_state); - gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event); - - gstbin_klass->handle_message = - GST_DEBUG_FUNCPTR (gst_play_bin_handle_message); - - playbasebin_klass->setup_output_pads = setup_sinks; - playbasebin_klass->set_subtitles_visible = playbin_set_subtitles_visible; - playbasebin_klass->set_audio_mute = playbin_set_audio_mute; -} - -static void -gst_play_bin_init (GstPlayBin * play_bin) -{ - play_bin->video_sink = NULL; - play_bin->audio_sink = NULL; - play_bin->visualisation = NULL; - play_bin->pending_visualisation = NULL; - play_bin->volume_element = NULL; - play_bin->textoverlay_element = NULL; - play_bin->spu_element = NULL; - play_bin->volume = 1.0; - play_bin->sinks = NULL; - play_bin->frame = NULL; - play_bin->font_desc = NULL; - play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) gst_object_unref); -} - -static void -gst_play_bin_dispose (GObject * object) -{ - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (object); - - if (play_bin->cache != NULL) { - remove_sinks (play_bin); - g_hash_table_destroy (play_bin->cache); - play_bin->cache = NULL; - } - - if (play_bin->audio_sink != NULL) { - gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL); - gst_object_unref (play_bin->audio_sink); - play_bin->audio_sink = NULL; - } - if (play_bin->video_sink != NULL) { - gst_element_set_state (play_bin->video_sink, GST_STATE_NULL); - gst_object_unref (play_bin->video_sink); - play_bin->video_sink = NULL; - } - if (play_bin->visualisation != NULL) { - gst_element_set_state (play_bin->visualisation, GST_STATE_NULL); - gst_object_unref (play_bin->visualisation); - play_bin->visualisation = NULL; - } - if (play_bin->pending_visualisation != NULL) { - gst_element_set_state (play_bin->pending_visualisation, GST_STATE_NULL); - gst_object_unref (play_bin->pending_visualisation); - play_bin->pending_visualisation = NULL; - } - if (play_bin->textoverlay_element != NULL) { - gst_object_unref (play_bin->textoverlay_element); - play_bin->textoverlay_element = NULL; - } - if (play_bin->volume_element) { - gst_object_unref (play_bin->volume_element); - play_bin->volume_element = NULL; - } - if (play_bin->spu_element != NULL) { - gst_object_unref (play_bin->spu_element); - play_bin->spu_element = NULL; - } - g_free (play_bin->font_desc); - play_bin->font_desc = NULL; - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static GstProbeReturn -gst_play_bin_vis_blocked (GstPad * tee_pad, GstProbeType type, - gpointer type_data, gpointer user_data) -{ - GstPlayBin *play_bin = GST_PLAY_BIN (user_data); - GstBin *vis_bin = NULL; - GstPad *vis_sink_pad = NULL, *vis_src_pad = NULL, *vqueue_pad = NULL; - GstState bin_state; - GstElement *pending_visualisation; - - GST_OBJECT_LOCK (play_bin); - pending_visualisation = play_bin->pending_visualisation; - play_bin->pending_visualisation = NULL; - GST_OBJECT_UNLOCK (play_bin); - - /* We want to disable visualisation */ - if (!GST_IS_ELEMENT (pending_visualisation)) { - /* Set visualisation element to READY */ - gst_element_set_state (play_bin->visualisation, GST_STATE_READY); - goto beach; - } - - vis_bin = - GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST - (play_bin->visualisation))); - - if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) { - goto beach; - } - - vis_src_pad = gst_element_get_static_pad (play_bin->visualisation, "src"); - vis_sink_pad = gst_pad_get_peer (tee_pad); - - /* Can be fakesink */ - if (GST_IS_PAD (vis_src_pad)) { - vqueue_pad = gst_pad_get_peer (vis_src_pad); - } - - if (!GST_IS_PAD (vis_sink_pad)) { - goto beach; - } - - /* Check the bin's state */ - GST_OBJECT_LOCK (vis_bin); - bin_state = GST_STATE (vis_bin); - GST_OBJECT_UNLOCK (vis_bin); - - /* Unlink */ - gst_pad_unlink (tee_pad, vis_sink_pad); - gst_object_unref (vis_sink_pad); - vis_sink_pad = NULL; - - if (GST_IS_PAD (vqueue_pad)) { - gst_pad_unlink (vis_src_pad, vqueue_pad); - gst_object_unref (vis_src_pad); - vis_src_pad = NULL; - } - - /* Remove from vis_bin */ - gst_bin_remove (vis_bin, play_bin->visualisation); - /* Set state to NULL */ - gst_element_set_state (play_bin->visualisation, GST_STATE_NULL); - /* And loose our ref */ - gst_object_unref (play_bin->visualisation); - - if (pending_visualisation) { - /* Ref this new visualisation element before adding to the bin */ - gst_object_ref (pending_visualisation); - /* Add the new one */ - gst_bin_add (vis_bin, pending_visualisation); - /* Synchronizing state */ - gst_element_set_state (pending_visualisation, bin_state); - - vis_sink_pad = gst_element_get_static_pad (pending_visualisation, "sink"); - vis_src_pad = gst_element_get_static_pad (pending_visualisation, "src"); - - if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) { - goto beach; - } - - /* Link */ - gst_pad_link (tee_pad, vis_sink_pad); - gst_pad_link (vis_src_pad, vqueue_pad); - } - - /* We are done */ - gst_object_unref (play_bin->visualisation); - play_bin->visualisation = pending_visualisation; - -beach: - if (vis_sink_pad) { - gst_object_unref (vis_sink_pad); - } - if (vis_src_pad) { - gst_object_unref (vis_src_pad); - } - if (vqueue_pad) { - gst_object_unref (vqueue_pad); - } - if (vis_bin) { - gst_object_unref (vis_bin); - } - - /* unblock the pad and remove the probe */ - return GST_PROBE_REMOVE; -} - -static void -gst_play_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (object); - - switch (prop_id) { - case ARG_VIDEO_SINK: - if (play_bin->video_sink != NULL) { - gst_object_unref (play_bin->video_sink); - } - play_bin->video_sink = g_value_get_object (value); - if (play_bin->video_sink != NULL) { - gst_object_ref_sink (play_bin->video_sink); - } - /* when changing the videosink, we just remove the - * video pipeline from the cache so that it will be - * regenerated with the new sink element */ - g_hash_table_remove (play_bin->cache, "vbin"); - break; - case ARG_AUDIO_SINK: - if (play_bin->audio_sink != NULL) { - gst_object_unref (play_bin->audio_sink); - } - if (play_bin->volume_element != NULL) { - gst_object_unref (play_bin->volume_element); - play_bin->volume_element = NULL; - } - play_bin->audio_sink = g_value_get_object (value); - if (play_bin->audio_sink != NULL) { - gst_object_ref_sink (play_bin->audio_sink); - } - g_hash_table_remove (play_bin->cache, "abin"); - break; - case ARG_VIS_PLUGIN: - { - GstElement *pending_visualisation = - GST_ELEMENT_CAST (g_value_get_object (value)); - - /* Take ownership */ - if (pending_visualisation) { - gst_object_ref_sink (pending_visualisation); - } - - /* Do we already have a visualisation change pending ? */ - GST_OBJECT_LOCK (play_bin); - if (play_bin->pending_visualisation) { - gst_object_unref (play_bin->pending_visualisation); - play_bin->pending_visualisation = pending_visualisation; - GST_OBJECT_UNLOCK (play_bin); - } else { - GST_OBJECT_UNLOCK (play_bin); - /* Was there a visualisation already set ? */ - if (play_bin->visualisation != NULL) { - GstBin *vis_bin = NULL; - - vis_bin = - GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST - (play_bin->visualisation))); - - /* Check if the visualisation is already in a bin */ - if (GST_IS_BIN (vis_bin)) { - GstPad *vis_sink_pad = NULL, *tee_pad = NULL; - - /* Now get tee pad and block it async */ - vis_sink_pad = gst_element_get_static_pad (play_bin->visualisation, - "sink"); - if (!GST_IS_PAD (vis_sink_pad)) { - goto beach; - } - tee_pad = gst_pad_get_peer (vis_sink_pad); - if (!GST_IS_PAD (tee_pad)) { - goto beach; - } - - play_bin->pending_visualisation = pending_visualisation; - /* Block with callback */ - gst_pad_add_probe (tee_pad, GST_PROBE_TYPE_BLOCK, - gst_play_bin_vis_blocked, play_bin, NULL); - beach: - if (vis_sink_pad) { - gst_object_unref (vis_sink_pad); - } - if (tee_pad) { - gst_object_unref (tee_pad); - } - gst_object_unref (vis_bin); - } else { - play_bin->visualisation = pending_visualisation; - } - } else { - play_bin->visualisation = pending_visualisation; - } - } - break; - } - case ARG_VOLUME: - play_bin->volume = g_value_get_double (value); - if (play_bin->volume_element) { - g_object_set (G_OBJECT (play_bin->volume_element), "volume", - play_bin->volume, NULL); - } - break; - case ARG_FONT_DESC: - g_free (play_bin->font_desc); - play_bin->font_desc = g_strdup (g_value_get_string (value)); - if (play_bin->textoverlay_element) { - g_object_set (G_OBJECT (play_bin->textoverlay_element), - "font-desc", g_value_get_string (value), NULL); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (object); - - switch (prop_id) { - case ARG_VIDEO_SINK: - g_value_set_object (value, play_bin->video_sink); - break; - case ARG_AUDIO_SINK: - g_value_set_object (value, play_bin->audio_sink); - break; - case ARG_VIS_PLUGIN: - g_value_set_object (value, play_bin->visualisation); - break; - case ARG_VOLUME: - g_value_set_double (value, play_bin->volume); - break; - case ARG_FRAME:{ - GstBuffer *cur_frame = NULL; - - gst_buffer_replace (&cur_frame, play_bin->frame); - gst_value_take_buffer (value, cur_frame); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* signal fired when the identity has received a new buffer. This is used for - * making screenshots. - */ -static void -handoff (GstElement * identity, GstBuffer * frame, gpointer data) -{ - GstPlayBin *play_bin = GST_PLAY_BIN (data); - -#if 0 - /* applications need to know the buffer caps, - * make sure they are always set on the frame */ - if (GST_BUFFER_CAPS (frame) == NULL) { - GstPad *pad; - - if ((pad = gst_element_get_static_pad (identity, "sink"))) { - gst_buffer_set_caps (frame, GST_PAD_CAPS (pad)); - gst_object_unref (pad); - } - } -#endif - - gst_buffer_replace (&play_bin->frame, frame); -} - -static void -post_missing_element_message (GstPlayBin * playbin, const gchar * name) -{ - GstMessage *msg; - - msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), name); - gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); -} - -/* make the element (bin) that contains the elements needed to perform - * video display. We connect a handoff signal to identity so that we - * can grab snapshots. Identity's sinkpad is ghosted to vbin. - * - * +-------------------------------------------------------------+ - * | vbin | - * | +--------+ +----------+ +----------+ +---------+ | - * | |identity| |colorspace| |videoscale| |videosink| | - * | +-sink src-sink src-sink src-sink | | - * | | +---+----+ +----------+ +----------+ +---------+ | - * sink-+ | | - * +----------|--------------------------------------------------+ - * handoff - */ -static GstElement * -gen_video_element (GstPlayBin * play_bin) -{ - GstElement *element; - GstElement *conv; - - GstElement *scale; - GstElement *sink; - GstElement *identity; - GstPad *pad; - - /* first see if we have it in the cache */ - element = g_hash_table_lookup (play_bin->cache, "vbin"); - if (element != NULL) { - return element; - } - - if (play_bin->video_sink) { - sink = play_bin->video_sink; - } else { - sink = gst_element_factory_make ("autovideosink", "videosink"); - if (sink == NULL) { - sink = gst_element_factory_make ("xvimagesink", "videosink"); - } - if (sink == NULL) - goto no_sinks; - } - gst_object_ref (sink); - g_hash_table_insert (play_bin->cache, (gpointer) "video_sink", sink); - - /* create a bin to hold objects, as we create them we add them to this bin so - * that when something goes wrong we only need to unref the bin */ - element = gst_bin_new ("vbin"); - gst_bin_add (GST_BIN_CAST (element), sink); - - conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); - if (conv == NULL) - goto no_colorspace; - gst_bin_add (GST_BIN_CAST (element), conv); - - scale = gst_element_factory_make ("videoscale", "vscale"); - if (scale == NULL) - goto no_videoscale; - gst_bin_add (GST_BIN_CAST (element), scale); - - identity = gst_element_factory_make ("identity", "id"); - g_object_set (identity, "silent", TRUE, NULL); - g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin); - gst_bin_add (GST_BIN_CAST (element), identity); - - gst_element_link_pads (identity, "src", conv, "sink"); - gst_element_link_pads (conv, "src", scale, "sink"); - /* be more careful with the pad from the custom sink element, it might not - * be named 'sink' */ - if (!gst_element_link_pads (scale, "src", sink, NULL)) - goto link_failed; - - pad = gst_element_get_static_pad (identity, "sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - - gst_element_set_state (element, GST_STATE_READY); - - /* since we're gonna add it to a bin but don't want to lose it, - * we keep a reference. */ - gst_object_ref (element); - g_hash_table_insert (play_bin->cache, (gpointer) "vbin", element); - - return element; - - /* ERRORS */ -no_sinks: - { - post_missing_element_message (play_bin, "autovideosink"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Both autovideosink and xvimagesink elements are missing.")), - (NULL)); - return NULL; - } -no_colorspace: - { - post_missing_element_message (play_bin, "ffmpegcolorspace"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "ffmpegcolorspace"), (NULL)); - gst_object_unref (element); - return NULL; - } - -no_videoscale: - { - post_missing_element_message (play_bin, "videoscale"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "videoscale"), ("possibly a liboil version mismatch?")); - gst_object_unref (element); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (play_bin, CORE, PAD, - (NULL), ("Failed to configure the video sink.")); - gst_object_unref (element); - return NULL; - } -} - -/* make an element for playback of video with subtitles embedded. - * - * +--------------------------------------------------+ - * | tbin +-------------+ | - * | +-----+ | textoverlay | +------+ | - * | | csp | +--video_sink | | vbin | | - * video_sink-sink src+ +-text_sink src---sink | | - * | +-----+ | +-------------+ +------+ | - * text_sink-------------+ | - * +--------------------------------------------------+ - * - * If there is no subtitle renderer this function will simply return the - * videosink without the text_sink pad. - */ -static GstElement * -add_text_element (GstPlayBin * play_bin, GstElement * vbin) -{ - GstElement *element, *csp, *overlay; - GstPad *pad; - - /* Text overlay */ - overlay = gst_element_factory_make ("textoverlay", "overlay"); - - /* If no overlay return the video bin without subtitle support. */ - if (!overlay) - goto no_overlay; - - /* Create our bin */ - element = gst_bin_new ("textbin"); - - /* Set some parameters */ - g_object_set (G_OBJECT (overlay), - "halign", "center", "valign", "bottom", NULL); - if (play_bin->font_desc) { - g_object_set (G_OBJECT (overlay), "font-desc", play_bin->font_desc, NULL); - } - - /* Take a ref */ - play_bin->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay)); - - /* we know this will succeed, as the video bin already created one before */ - csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp"); - - /* Add our elements */ - gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL); - - /* Link */ - gst_element_link_pads (csp, "src", overlay, "video_sink"); - gst_element_link_pads (overlay, "src", vbin, "sink"); - - /* Add ghost pads on the subtitle bin */ - pad = gst_element_get_static_pad (overlay, "text_sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad)); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (csp, "sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - - /* If the vbin provides a subpicture sink pad, ghost it too */ - pad = gst_element_get_static_pad (vbin, "subpicture_sink"); - if (pad) { - gst_element_add_pad (element, gst_ghost_pad_new ("subpicture_sink", pad)); - gst_object_unref (pad); - } - - /* Set state to READY */ - gst_element_set_state (element, GST_STATE_READY); - - return element; - - /* ERRORS */ -no_overlay: - { - post_missing_element_message (play_bin, "textoverlay"); - GST_WARNING_OBJECT (play_bin, - "No overlay (pango) element, subtitles disabled"); - return vbin; - } -} - -/* make an element for rendering DVD subpictures onto output video - * - * +---------------------------------------------+ - * | tbin +--------+ | - * | +-----+ | | +------+ | - * | | csp | src-videosink | | vbin | | - * video_sink-sink src+ | src-sink | | - * | +-----+ +subpicture | +------+ | - * subpicture_pad--------+ +--------+ | - * +---------- ----------------------------------+ - * - */ -static GstElement * -add_spu_element (GstPlayBin * play_bin, GstElement * vbin) -{ - GstElement *element, *csp, *overlay; - GstPad *pad; - - /* DVD spu overlay */ - GST_DEBUG_OBJECT (play_bin, "Attempting to insert DVD SPU element"); - - overlay = gst_element_factory_make ("dvdspu", "overlay"); - - /* If no overlay return the video bin without subpicture support. */ - if (!overlay) - goto no_overlay; - - /* Create our bin */ - element = gst_bin_new ("spubin"); - - /* Take a ref */ - play_bin->spu_element = GST_ELEMENT_CAST (gst_object_ref (overlay)); - - /* we know this will succeed, as the video bin already created one before */ - csp = gst_element_factory_make ("ffmpegcolorspace", "spucsp"); - - /* Add our elements */ - gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL); - - /* Link */ - gst_element_link_pads (csp, "src", overlay, "video"); - gst_element_link_pads (overlay, "src", vbin, "sink"); - - /* Add ghost pad on the subpicture bin so it looks like vbin */ - pad = gst_element_get_static_pad (csp, "sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (overlay, "subpicture"); - gst_element_add_pad (element, gst_ghost_pad_new ("subpicture_sink", pad)); - gst_object_unref (pad); - - /* Set state to READY */ - gst_element_set_state (element, GST_STATE_READY); - - return element; - - /* ERRORS */ -no_overlay: - { - post_missing_element_message (play_bin, "dvdspu"); - GST_WARNING_OBJECT (play_bin, - "No DVD overlay (dvdspu) element. " - "menu highlight/subtitles unavailable"); - return vbin; - } -} - -/* make the element (bin) that contains the elements needed to perform - * audio playback. - * - * +-------------------------------------------------------------+ - * | abin | - * | +---------+ +----------+ +---------+ +---------+ | - * | |audioconv| |audioscale| | volume | |audiosink| | - * | +-sink src-sink src-sink src-sink | | - * | | +---------+ +----------+ +---------+ +---------+ | - * sink-+ | - * +-------------------------------------------------------------+ - */ -static GstElement * -gen_audio_element (GstPlayBin * play_bin) -{ - gboolean res; - GstElement *element; - GstElement *conv; - GstElement *scale; - GstElement *sink; - GstElement *volume; - GstPad *pad; - - element = g_hash_table_lookup (play_bin->cache, "abin"); - if (element != NULL) - return element; - - if (play_bin->audio_sink) { - sink = play_bin->audio_sink; - } else { - sink = gst_element_factory_make ("autoaudiosink", "audiosink"); - if (sink == NULL) { - sink = gst_element_factory_make ("alsasink", "audiosink"); - } - if (sink == NULL) - goto no_sinks; - - play_bin->audio_sink = GST_ELEMENT_CAST (gst_object_ref (sink)); - } - - gst_object_ref (sink); - g_hash_table_insert (play_bin->cache, (gpointer) "audio_sink", sink); - - element = gst_bin_new ("abin"); - gst_bin_add (GST_BIN_CAST (element), sink); - - conv = gst_element_factory_make ("audioconvert", "aconv"); - if (conv == NULL) - goto no_audioconvert; - gst_bin_add (GST_BIN_CAST (element), conv); - - scale = gst_element_factory_make ("audioresample", "aresample"); - if (scale == NULL) - goto no_audioresample; - gst_bin_add (GST_BIN_CAST (element), scale); - - volume = gst_element_factory_make ("volume", "volume"); - if (volume == NULL) - goto no_volume; - g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL); - play_bin->volume_element = GST_ELEMENT_CAST (gst_object_ref (volume)); - gst_bin_add (GST_BIN_CAST (element), volume); - - res = gst_element_link_pads (conv, "src", scale, "sink"); - res &= gst_element_link_pads (scale, "src", volume, "sink"); - res &= gst_element_link_pads (volume, "src", sink, NULL); - if (!res) - goto link_failed; - - pad = gst_element_get_static_pad (conv, "sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - - gst_element_set_state (element, GST_STATE_READY); - - /* since we're gonna add it to a bin but don't want to lose it, - * we keep a reference. */ - gst_object_ref (element); - g_hash_table_insert (play_bin->cache, (gpointer) "abin", element); - - return element; - - /* ERRORS */ -no_sinks: - { - post_missing_element_message (play_bin, "autoaudiosink"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); - return NULL; - } -no_audioconvert: - { - post_missing_element_message (play_bin, "audioconvert"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioconvert"), ("possibly a liboil version mismatch?")); - gst_object_unref (element); - return NULL; - } -no_audioresample: - { - post_missing_element_message (play_bin, "audioresample"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioresample"), ("possibly a liboil version mismatch?")); - gst_object_unref (element); - return NULL; - } -no_volume: - { - post_missing_element_message (play_bin, "volume"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "volume"), ("possibly a liboil version mismatch?")); - gst_object_unref (element); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (play_bin, CORE, PAD, - (NULL), ("Failed to configure the audio sink.")); - gst_object_unref (element); - return NULL; - } -} - -/* make the element (bin) that contains the elements needed to perform - * visualisation ouput. The idea is to split the audio using tee, then - * sending the output to the regular audio bin and the other output to - * the vis plugin that transforms it into a video that is rendered with the - * normal video bin. The video and audio bins are run in threads to make sure - * they don't block eachother. - * - * +-----------------------------------------------------------------------+ - * | visbin | - * | +------+ +--------+ +----------------+ | - * | | tee | | aqueue | | abin ... | | - * | +-sink src-sink src-sink | | - * | | | | +--------+ +----------------+ | - * | | | | | - * | | | | +------+ +------------+ +------+ +-----------+ | - * | | | | |vqueue| | audioconv | | vis | | vbin ... | | - * | | | src-sink src-sink + samp src-sink src-sink | | - * | | | | +------+ +------------+ +------+ +-----------+ | - * | | | | | - * | | +------+ | - * sink-+ | - * +-----------------------------------------------------------------------+ - */ -static GstElement * -gen_vis_element (GstPlayBin * play_bin) -{ - gboolean res; - GstElement *element; - GstElement *tee; - GstElement *asink; - GstElement *vsink; - GstElement *conv; - GstElement *resamp; - GstElement *conv2; - GstElement *vis; - GstElement *vqueue, *aqueue; - GstPad *pad, *rpad; - - /* errors are already posted when these fail. */ - asink = gen_audio_element (play_bin); - if (!asink) - return NULL; - vsink = gen_video_element (play_bin); - if (!vsink) { - gst_object_unref (asink); - return NULL; - } - - element = gst_bin_new ("visbin"); - tee = gst_element_factory_make ("tee", "tee"); - - vqueue = gst_element_factory_make ("queue", "vqueue"); - aqueue = gst_element_factory_make ("queue", "aqueue"); - - gst_bin_add (GST_BIN_CAST (element), asink); - gst_bin_add (GST_BIN_CAST (element), vqueue); - gst_bin_add (GST_BIN_CAST (element), aqueue); - gst_bin_add (GST_BIN_CAST (element), vsink); - gst_bin_add (GST_BIN_CAST (element), tee); - - conv = gst_element_factory_make ("audioconvert", "aconv"); - if (conv == NULL) - goto no_audioconvert; - gst_bin_add (GST_BIN_CAST (element), conv); - - resamp = gst_element_factory_make ("audioresample", "aresamp"); - if (resamp == NULL) - goto no_audioresample; - gst_bin_add (GST_BIN_CAST (element), resamp); - - conv2 = gst_element_factory_make ("audioconvert", "aconv2"); - if (conv2 == NULL) - goto no_audioconvert; - gst_bin_add (GST_BIN_CAST (element), conv2); - - if (play_bin->visualisation) { - gst_object_ref (play_bin->visualisation); - vis = play_bin->visualisation; - } else { - vis = gst_element_factory_make ("goom", "vis"); - if (!vis) - goto no_goom; - } - gst_bin_add (GST_BIN_CAST (element), vis); - - res = gst_element_link_pads (vqueue, "src", conv, "sink"); - res &= gst_element_link_pads (conv, "src", resamp, "sink"); - res &= gst_element_link_pads (resamp, "src", conv2, "sink"); - res &= gst_element_link_pads (conv2, "src", vis, "sink"); - res &= gst_element_link_pads (vis, "src", vsink, "sink"); - if (!res) - goto link_failed; - - pad = gst_element_get_static_pad (aqueue, "sink"); - rpad = gst_element_get_request_pad (tee, "src%d"); - gst_pad_link (rpad, pad); - gst_object_unref (rpad); - gst_object_unref (pad); - gst_element_link_pads (aqueue, "src", asink, "sink"); - - pad = gst_element_get_static_pad (vqueue, "sink"); - rpad = gst_element_get_request_pad (tee, "src%d"); - gst_pad_link (rpad, pad); - gst_object_unref (rpad); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (tee, "sink"); - gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - - return element; - - /* ERRORS */ -no_audioconvert: - { - post_missing_element_message (play_bin, "audioconvert"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioconvert"), ("possibly a liboil version mismatch?")); - gst_object_unref (element); - return NULL; - } -no_audioresample: - { - post_missing_element_message (play_bin, "audioresample"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioresample"), (NULL)); - gst_object_unref (element); - return NULL; - } -no_goom: - { - post_missing_element_message (play_bin, "goom"); - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "goom"), (NULL)); - gst_object_unref (element); - return NULL; - } -link_failed: - { - GST_ELEMENT_ERROR (play_bin, CORE, PAD, - (NULL), ("Failed to configure the visualisation element.")); - gst_object_unref (element); - return NULL; - } -} - -/* get rid of all installed sinks */ -static void -remove_sinks (GstPlayBin * play_bin) -{ - GList *sinks; - GstObject *parent; - GstElement *element; - GstPad *pad, *peer; - - if (play_bin->cache == NULL) - return; - - GST_DEBUG ("removesinks"); - element = g_hash_table_lookup (play_bin->cache, "abin"); - if (element != NULL) { - parent = gst_element_get_parent (element); - if (parent != NULL) { - /* we remove the element from the parent so that - * there is no unwanted state change when the parent - * is disposed */ - play_bin->sinks = g_list_remove (play_bin->sinks, element); - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (parent), element); - gst_object_unref (parent); - } - pad = gst_element_get_static_pad (element, "sink"); - if (pad != NULL) { - peer = gst_pad_get_peer (pad); - if (peer != NULL) { - gst_pad_unlink (peer, pad); - gst_object_unref (peer); - } - gst_object_unref (pad); - } - } - element = g_hash_table_lookup (play_bin->cache, "vbin"); - if (element != NULL) { - parent = gst_element_get_parent (element); - if (parent != NULL) { - play_bin->sinks = g_list_remove (play_bin->sinks, element); - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (parent), element); - gst_object_unref (parent); - } - pad = gst_element_get_static_pad (element, "sink"); - if (pad != NULL) { - peer = gst_pad_get_peer (pad); - if (peer != NULL) { - gst_pad_unlink (peer, pad); - gst_object_unref (peer); - } - gst_object_unref (pad); - } - } - - for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) { - GstElement *element = GST_ELEMENT_CAST (sinks->data); - GstPad *pad; - GstPad *peer; - - pad = gst_element_get_static_pad (element, "sink"); - - GST_LOG ("removing sink %p", element); - - peer = gst_pad_get_peer (pad); - if (peer) { - gst_pad_unlink (peer, pad); - gst_object_unref (peer); - } - gst_object_unref (pad); - - gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_bin), element); - } - g_list_free (play_bin->sinks); - play_bin->sinks = NULL; - - if (play_bin->visualisation) { - GstElement *vis_bin; - - vis_bin = - GST_ELEMENT_CAST (gst_element_get_parent (play_bin->visualisation)); - - gst_element_set_state (play_bin->visualisation, GST_STATE_NULL); - - if (vis_bin) { - gst_bin_remove (GST_BIN_CAST (vis_bin), play_bin->visualisation); - gst_object_unref (vis_bin); - } - } - - if (play_bin->frame) { - gst_buffer_unref (play_bin->frame); - play_bin->frame = NULL; - } - - if (play_bin->textoverlay_element) { - gst_object_unref (play_bin->textoverlay_element); - play_bin->textoverlay_element = NULL; - } -} - -/* loop over the streams and set up the pipeline to play this - * media file. First we count the number of audio and video streams. - * If there is no video stream but there exists an audio stream, - * we install a visualisation pipeline. - * - * Also make sure to only connect the first audio and video pad. FIXME - * this should eventually be handled with a tuner interface so that - * one can switch the streams. - * - * This function takes ownership of @sink.* - */ -static gboolean -add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, - GstPad * subtitle_pad) -{ - GstPad *sinkpad; - GstPadLinkReturn linkres; - GstElement *parent; - GstStateChangeReturn stateret; - GstState state; - - g_return_val_if_fail (sink != NULL, FALSE); - - state = GST_STATE_PAUSED; - - /* this is only for debugging */ - parent = gst_pad_get_parent_element (srcpad); - if (parent) { - GST_DEBUG ("Adding sink %" GST_PTR_FORMAT - " with state %d (parent: %d, peer: %d)", sink, - GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent)); - gst_object_unref (parent); - } - gst_bin_add (GST_BIN_CAST (play_bin), sink); - - /* bring it to the required state so we can link to the peer without - * breaking the flow */ - stateret = gst_element_set_state (sink, state); - if (stateret == GST_STATE_CHANGE_FAILURE) - goto state_failed; - - /* we found a sink for this stream, now try to install it */ - sinkpad = gst_element_get_static_pad (sink, "sink"); - linkres = gst_pad_link (srcpad, sinkpad); - gst_object_unref (sinkpad); - - /* try to link the pad of the sink to the stream */ - if (GST_PAD_LINK_FAILED (linkres)) - goto link_failed; - - if (GST_IS_PAD (subtitle_pad)) { - sinkpad = gst_element_get_static_pad (sink, "text_sink"); - linkres = gst_pad_link (subtitle_pad, sinkpad); - gst_object_unref (sinkpad); - } - - /* try to link the subtitle pad of the sink to the stream, this is not - * fatal. */ - if (GST_PAD_LINK_FAILED (linkres)) - goto subtitle_failed; - -done: - /* we got the sink succesfully linked, now keep the sink - * in our internal list */ - play_bin->sinks = g_list_prepend (play_bin->sinks, sink); - - return TRUE; - - /* ERRORS */ -state_failed: - { - gst_element_set_state (sink, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_bin), sink); - GST_DEBUG_OBJECT (play_bin, "state change failure when adding sink"); - return FALSE; - } -link_failed: - { - gchar *capsstr; - GstCaps *caps; - - /* could not link this stream */ - caps = gst_pad_get_caps (srcpad, NULL); - capsstr = gst_caps_to_string (caps); - g_warning ("could not link %s: %d", capsstr, linkres); - GST_DEBUG_OBJECT (play_bin, - "link failed when adding sink, caps %s, reason %d", capsstr, linkres); - g_free (capsstr); - gst_caps_unref (caps); - - gst_element_set_state (sink, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_bin), sink); - return FALSE; - } -subtitle_failed: - { - GstCaps *caps; - - /* could not link this stream */ - caps = gst_pad_get_caps (subtitle_pad, NULL); - GST_WARNING_OBJECT (play_bin, "subtitle link failed when adding sink, " - "caps = %" GST_PTR_FORMAT ", reason %d", caps, linkres); - gst_caps_unref (caps); - - /* not fatal */ - goto done; - } -} - -static gboolean -setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group) -{ - GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin); - gboolean have_video = FALSE; - gboolean need_vis = FALSE; - gboolean need_text = FALSE; - gboolean need_spu = FALSE; - GstPad *textsrcpad = NULL, *pad = NULL, *origtextsrcpad = NULL; - GstElement *sink; - gboolean res = TRUE; - - /* get rid of existing sinks */ - if (play_bin->sinks) { - remove_sinks (play_bin); - } - GST_DEBUG_OBJECT (play_base_bin, "setupsinks"); - - /* find out what to do */ - have_video = (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0); - need_spu = (group->type[GST_STREAM_TYPE_SUBPICTURE - 1].npads != 0); - - if (have_video && group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) { - need_text = TRUE; - } else if (!have_video && - group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 && - play_bin->visualisation != NULL) { - need_vis = TRUE; - } - - /* now actually connect everything */ - - /* link audio */ - if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) { - if (need_vis) { - sink = gen_vis_element (play_bin); - } else { - sink = gen_audio_element (play_bin); - } - if (!sink) - return FALSE; - - pad = - gst_element_get_static_pad (group->type[GST_STREAM_TYPE_AUDIO - - 1].preroll, "src"); - res = add_sink (play_bin, sink, pad, NULL); - gst_object_unref (pad); - } - - /* link video */ - if (have_video) { - /* Create the video rendering bin, error is posted when this fails. */ - sink = gen_video_element (play_bin); - if (!sink) - return FALSE; - if (need_spu) { - sink = add_spu_element (play_bin, sink); - } - - if (need_text) { - GstObject *parent = NULL, *grandparent = NULL; - GstPad *ghost = NULL; - - /* Add the subtitle overlay element into the video sink */ - sink = add_text_element (play_bin, sink); - - /* Link the incoming subtitle stream into the output bin */ - textsrcpad = - gst_element_get_static_pad (group->type[GST_STREAM_TYPE_TEXT - - 1].preroll, "src"); - - /* This pad is from subtitle-bin, we need to create a ghost pad to have - common grandparents */ - parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad)); - if (!parent) { - GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !"); - gst_object_unref (textsrcpad); - textsrcpad = NULL; - goto beach; - } - - grandparent = gst_object_get_parent (parent); - if (!grandparent) { - GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no grandparent !"); - gst_object_unref (parent); - gst_object_unref (textsrcpad); - textsrcpad = NULL; - goto beach; - } - - /* We ghost the pad on subtitle_bin only, if the text pad is from the - media demuxer we keep it as it is */ - if (!GST_IS_PLAY_BIN (grandparent)) { - GST_DEBUG_OBJECT (textsrcpad, "this subtitle pad is from a subtitle " - "file, ghosting to a suitable hierarchy"); - /* Block the pad first, because as soon as we add a ghostpad, the queue - * will try and start pushing */ - play_bin->text_id = - gst_pad_add_probe (textsrcpad, GST_PROBE_TYPE_BLOCK, NULL, NULL, - NULL); - origtextsrcpad = gst_object_ref (textsrcpad); - - ghost = gst_ghost_pad_new ("text_src", textsrcpad); - if (!GST_IS_PAD (ghost)) { - GST_WARNING_OBJECT (textsrcpad, "failed creating ghost pad for " - "subtitle-bin"); - gst_object_unref (parent); - gst_object_unref (grandparent); - gst_object_unref (textsrcpad); - textsrcpad = NULL; - goto beach; - } - - gst_pad_set_active (ghost, TRUE); - if (gst_element_add_pad (GST_ELEMENT_CAST (grandparent), ghost)) { - gst_object_unref (textsrcpad); - textsrcpad = gst_object_ref (ghost); - } else { - GST_WARNING_OBJECT (ghost, "failed adding ghost pad on subtitle-bin"); - gst_pad_set_active (ghost, FALSE); - gst_object_unref (ghost); - gst_object_unref (textsrcpad); - textsrcpad = NULL; - } - } else { - GST_DEBUG_OBJECT (textsrcpad, "this subtitle pad is from the demuxer " - "no changes to hierarchy needed"); - } - - gst_object_unref (parent); - gst_object_unref (grandparent); - } - beach: - if (!sink) - return FALSE; - pad = - gst_element_get_static_pad (group->type[GST_STREAM_TYPE_VIDEO - - 1].preroll, "src"); - res = add_sink (play_bin, sink, pad, textsrcpad); - gst_object_unref (pad); - if (textsrcpad) - gst_object_unref (textsrcpad); - if (origtextsrcpad) { - if (play_bin->text_id) { - gst_pad_remove_probe (origtextsrcpad, play_bin->text_id); - play_bin->text_id = 0; - } - gst_object_unref (origtextsrcpad); - } - - /* If we have a DVD subpicture stream, link it to the SPU now */ - if (need_spu) { - GstPad *subpic_pad; - GstPad *spu_sink_pad; - - subpic_pad = - gst_element_get_static_pad (group->type[GST_STREAM_TYPE_SUBPICTURE - - 1].preroll, "src"); - spu_sink_pad = gst_element_get_static_pad (sink, "subpicture_sink"); - if (subpic_pad && spu_sink_pad) { - GST_LOG_OBJECT (play_bin, "Linking DVD subpicture stream onto SPU"); - play_bin->sub_id = - gst_pad_add_probe (subpic_pad, GST_PROBE_TYPE_BLOCK, NULL, NULL, - NULL); - if (gst_pad_link (subpic_pad, spu_sink_pad) != GST_PAD_LINK_OK) { - GST_WARNING_OBJECT (play_bin, - "Failed to link DVD subpicture stream onto SPU"); - } - if (play_bin->sub_id) { - gst_pad_remove_probe (subpic_pad, play_bin->sub_id); - play_bin->sub_id = 0; - } - } - if (subpic_pad) - gst_object_unref (subpic_pad); - if (spu_sink_pad) - gst_object_unref (spu_sink_pad); - } - } - - /* remove the sinks now, pipeline get_state will now wait for the - * sinks to preroll */ - if (play_bin->fakesink) { - gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink); - play_bin->fakesink = NULL; - } - - return res; -} - -static void -playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin, gboolean visible) -{ - GstPlayBin *playbin = GST_PLAY_BIN (play_base_bin); - - /* we're ignoring the case of someone setting the 'current-text' property - * before textoverlay is set up (which is probably okay, since playbasebin - * will just select the first subtitle stream as active stream regardless) */ - if (playbin->textoverlay_element != NULL) { - GST_LOG_OBJECT (playbin, "setting subtitle visibility to %d", visible); - g_object_set (playbin->textoverlay_element, "silent", !visible, NULL); - } -} - -static void -playbin_set_audio_mute (GstPlayBaseBin * play_base_bin, gboolean mute) -{ - GstPlayBin *playbin = GST_PLAY_BIN (play_base_bin); - - if (playbin->volume_element) { - g_object_set (G_OBJECT (playbin->volume_element), "mute", mute, NULL); - } -} - -/* Send an event to our sinks until one of them works; don't then send to the - * remaining sinks (unlike GstBin) - */ -static gboolean -gst_play_bin_send_event_to_sink (GstPlayBin * play_bin, GstEvent * event) -{ - GList *sinks = play_bin->sinks; - gboolean res = TRUE; - - while (sinks) { - GstElement *sink = GST_ELEMENT_CAST (sinks->data); - - gst_event_ref (event); - if ((res = gst_element_send_event (sink, event))) { - GST_DEBUG_OBJECT (play_bin, - "Sent event succesfully to sink %" GST_PTR_FORMAT, sink); - break; - } - GST_DEBUG_OBJECT (play_bin, - "Event failed when sent to sink %" GST_PTR_FORMAT, sink); - - sinks = g_list_next (sinks); - } - - gst_event_unref (event); - - return res; -} - -/* We only want to send the event to a single sink (overriding GstBin's - * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek - * events appropriately. So, this is a messy duplication of code. */ -static gboolean -gst_play_bin_send_event (GstElement * element, GstEvent * event) -{ - gboolean res = FALSE; - GstEventType event_type = GST_EVENT_TYPE (event); - - switch (event_type) { - case GST_EVENT_SEEK: - GST_DEBUG_OBJECT (element, "Sending seek event to a sink"); - res = gst_play_bin_send_event_to_sink (GST_PLAY_BIN (element), event); - break; - default: - res = parent_class->send_event (element, event); - break; - } - - return res; -} - -static void -value_list_append_structure_list (GValue * list_val, GstStructure ** first, - GList * structure_list) -{ - GList *l; - - for (l = structure_list; l != NULL; l = l->next) { - GValue val = { 0, }; - - if (*first == NULL) - *first = gst_structure_copy ((GstStructure *) l->data); - - g_value_init (&val, GST_TYPE_STRUCTURE); - g_value_take_boxed (&val, gst_structure_copy ((GstStructure *) l->data)); - gst_value_list_append_value (list_val, &val); - g_value_unset (&val); - } -} - -/* if it's a redirect message with multiple redirect locations we might - * want to pick a different 'best' location depending on the required - * bitrates and the connection speed */ -static GstMessage * -gst_play_bin_handle_redirect_message (GstPlayBin * playbin, GstMessage * msg) -{ - const GValue *locations_list, *location_val; - GstMessage *new_msg; - GstStructure *new_structure = NULL; - GList *l_good = NULL, *l_neutral = NULL, *l_bad = NULL; - GValue new_list = { 0, }; - guint size, i; - GstPlayBaseBin *playbasebin = GST_PLAY_BASE_BIN (playbin); - guint connection_speed = playbasebin->connection_speed; - const GstStructure *structure; - - GST_DEBUG_OBJECT (playbin, "redirect message: %" GST_PTR_FORMAT, msg); - GST_DEBUG_OBJECT (playbin, "connection speed: %u", connection_speed); - - structure = gst_message_get_structure (msg); - if (connection_speed == 0 || structure == NULL) - return msg; - - locations_list = gst_structure_get_value (structure, "locations"); - if (locations_list == NULL) - return msg; - - size = gst_value_list_get_size (locations_list); - if (size < 2) - return msg; - - /* maintain existing order as much as possible, just sort references - * with too high a bitrate to the end (the assumption being that if - * bitrates are given they are given for all interesting streams and - * that the you-need-at-least-version-xyz redirect has the same bitrate - * as the lowest referenced redirect alternative) */ - for (i = 0; i < size; ++i) { - const GstStructure *s; - gint bitrate = 0; - - location_val = gst_value_list_get_value (locations_list, i); - s = (const GstStructure *) g_value_get_boxed (location_val); - if (!gst_structure_get_int (s, "minimum-bitrate", &bitrate) || bitrate <= 0) { - GST_DEBUG_OBJECT (playbin, "no bitrate: %" GST_PTR_FORMAT, s); - l_neutral = g_list_append (l_neutral, (gpointer) s); - } else if (bitrate > connection_speed) { - GST_DEBUG_OBJECT (playbin, "bitrate too high: %" GST_PTR_FORMAT, s); - l_bad = g_list_append (l_bad, (gpointer) s); - } else if (bitrate <= connection_speed) { - GST_DEBUG_OBJECT (playbin, "bitrate OK: %" GST_PTR_FORMAT, s); - l_good = g_list_append (l_good, (gpointer) s); - } - } - - g_value_init (&new_list, GST_TYPE_LIST); - value_list_append_structure_list (&new_list, &new_structure, l_good); - value_list_append_structure_list (&new_list, &new_structure, l_neutral); - value_list_append_structure_list (&new_list, &new_structure, l_bad); - gst_structure_set_value (new_structure, "locations", &new_list); - g_value_unset (&new_list); - - g_list_free (l_good); - g_list_free (l_neutral); - g_list_free (l_bad); - - new_msg = gst_message_new_element (msg->src, new_structure); - gst_message_unref (msg); - - GST_DEBUG_OBJECT (playbin, "new redirect message: %" GST_PTR_FORMAT, new_msg); - return new_msg; -} - -static void -gst_play_bin_handle_message (GstBin * bin, GstMessage * msg) -{ - const GstStructure *structure; - - structure = gst_message_get_structure (msg); - if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ELEMENT && structure != NULL - && gst_structure_has_name (structure, "redirect")) { - msg = gst_play_bin_handle_redirect_message (GST_PLAY_BIN (bin), msg); - } - - GST_BIN_CLASS (parent_class)->handle_message (bin, msg); -} - -static GstStateChangeReturn -gst_play_bin_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (element); - - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* this really is the easiest way to make the state change return - * ASYNC until we added the sinks */ - if (!play_bin->fakesink) { - play_bin->fakesink = gst_element_factory_make ("fakesink", "test"); - gst_bin_add (GST_BIN_CAST (play_bin), play_bin->fakesink); - } - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* remember us being a live pipeline */ - play_bin->is_live = (ret == GST_STATE_CHANGE_NO_PREROLL); - GST_DEBUG_OBJECT (play_bin, "is live: %d", play_bin->is_live); - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - /* FIXME Release audio device when we implement that */ - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: - /* remove sinks we added */ - remove_sinks (play_bin); - /* and there might be a fakesink we need to clean up now */ - if (play_bin->fakesink) { - gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink); - play_bin->fakesink = NULL; - } - break; - default: - break; - } - - return ret; -} - -gboolean -gst_play_bin_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin"); - - return gst_element_register (plugin, "playbin", GST_RANK_NONE, - GST_TYPE_PLAY_BIN); -} diff --git a/gst/playback/gststreaminfo.c b/gst/playback/gststreaminfo.c deleted file mode 100644 index cfbc9b2..0000000 --- a/gst/playback/gststreaminfo.c +++ /dev/null @@ -1,404 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include "gststreaminfo.h" - -GST_DEBUG_CATEGORY_STATIC (gst_streaminfo_debug); -#define GST_CAT_DEFAULT gst_streaminfo_debug - -/* props */ -enum -{ - ARG_0, - ARG_PAD, - ARG_TYPE, - ARG_DECODER, - ARG_MUTE, - ARG_CAPS, - ARG_LANG_CODE, - ARG_CODEC -}; - -/* signals */ -enum -{ - SIGNAL_MUTED, - LAST_SIGNAL -}; - -static guint gst_stream_info_signals[LAST_SIGNAL] = { 0 }; - -#define GST_TYPE_STREAM_TYPE (gst_stream_type_get_type()) -static GType -gst_stream_type_get_type (void) -{ - static GType stream_type_type = 0; - static const GEnumValue stream_type[] = { - {GST_STREAM_TYPE_UNKNOWN, "Unknown stream", "unknown"}, - {GST_STREAM_TYPE_AUDIO, "Audio stream", "audio"}, - {GST_STREAM_TYPE_VIDEO, "Video stream", "video"}, - {GST_STREAM_TYPE_TEXT, "Text stream", "text"}, - {GST_STREAM_TYPE_SUBPICTURE, "Subpicture stream", "subpicture"}, - {GST_STREAM_TYPE_ELEMENT, - "Stream handled by element", "element"}, - {0, NULL, NULL}, - }; - - if (!stream_type_type) { - stream_type_type = g_enum_register_static ("GstStreamType", stream_type); - } - return stream_type_type; -} - -static void gst_stream_info_class_init (GstStreamInfoClass * klass); -static void gst_stream_info_init (GstStreamInfo * stream_info); -static void gst_stream_info_dispose (GObject * object); - -static void stream_info_change_state (GstElement * element, - gint old_state, gint new_state, gpointer data); - -static void gst_stream_info_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * spec); -static void gst_stream_info_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * spec); - -static GObjectClass *parent_class; - -//static guint gst_stream_info_signals[LAST_SIGNAL] = { 0 }; - -GType -gst_stream_info_get_type (void) -{ - static GType gst_stream_info_type = 0; - - if (!gst_stream_info_type) { - static const GTypeInfo gst_stream_info_info = { - sizeof (GstStreamInfoClass), - NULL, - NULL, - (GClassInitFunc) gst_stream_info_class_init, - NULL, - NULL, - sizeof (GstStreamInfo), - 0, - (GInstanceInitFunc) gst_stream_info_init, - NULL - }; - gst_stream_info_type = g_type_register_static (G_TYPE_OBJECT, - "GstStreamInfo", &gst_stream_info_info, 0); - } - - return gst_stream_info_type; -} - -static void -gst_stream_info_class_init (GstStreamInfoClass * klass) -{ - GObjectClass *gobject_klass; - - gobject_klass = (GObjectClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_klass->set_property = gst_stream_info_set_property; - gobject_klass->get_property = gst_stream_info_get_property; - - g_object_class_install_property (gobject_klass, ARG_PAD, - g_param_spec_object ("object", "object", - "Source Pad or object of the stream", GST_TYPE_OBJECT, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_TYPE, - g_param_spec_enum ("type", "Type", "Type of the stream", - GST_TYPE_STREAM_TYPE, GST_STREAM_TYPE_UNKNOWN, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_DECODER, - g_param_spec_string ("decoder", "Decoder", - "The decoder used to decode the stream", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_MUTE, - g_param_spec_boolean ("mute", "Mute", "Mute or unmute this stream", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_CAPS, - g_param_spec_boxed ("caps", "Capabilities", - "Capabilities (or type) of this stream", GST_TYPE_CAPS, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_LANG_CODE, - g_param_spec_string ("language-code", "Language code", - "Language code for this stream, conforming to ISO-639-1", NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_klass, ARG_CODEC, - g_param_spec_string ("codec", "Codec", "Codec used to encode the stream", - NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - gst_stream_info_signals[SIGNAL_MUTED] = - g_signal_new ("muted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstStreamInfoClass, muted), NULL, NULL, - gst_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); - - gobject_klass->dispose = gst_stream_info_dispose; - - GST_DEBUG_CATEGORY_INIT (gst_streaminfo_debug, "streaminfo", 0, - "Playbin Stream Info"); -} - - -static void -gst_stream_info_init (GstStreamInfo * stream_info) -{ - stream_info->object = NULL; - stream_info->origin = NULL; - stream_info->type = GST_STREAM_TYPE_UNKNOWN; - stream_info->decoder = NULL; - stream_info->mute = FALSE; - stream_info->caps = NULL; -} - -static GstProbeReturn -cb_probe (GstPad * pad, GstProbeType type, gpointer type_data, - gpointer user_data) -{ - GstEvent *e = type_data; - GstStreamInfo *info = user_data; - - if (GST_EVENT_TYPE (e) == GST_EVENT_TAG) { - gchar *codec, *lang; - GstTagList *list; - - gst_event_parse_tag (e, &list); - - if (info->type != GST_STREAM_TYPE_AUDIO && - gst_tag_list_get_string (list, GST_TAG_VIDEO_CODEC, &codec)) { - g_free (info->codec); - info->codec = codec; - GST_LOG_OBJECT (pad, "codec = %s (video)", codec); - g_object_notify (G_OBJECT (info), "codec"); - } else if (info->type != GST_STREAM_TYPE_VIDEO && - gst_tag_list_get_string (list, GST_TAG_AUDIO_CODEC, &codec)) { - g_free (info->codec); - info->codec = codec; - GST_LOG_OBJECT (pad, "codec = %s (audio)", codec); - g_object_notify (G_OBJECT (info), "codec"); - } else if (gst_tag_list_get_string (list, GST_TAG_CODEC, &codec)) { - g_free (info->codec); - info->codec = codec; - GST_LOG_OBJECT (pad, "codec = %s (generic)", codec); - g_object_notify (G_OBJECT (info), "codec"); - } - if (gst_tag_list_get_string (list, GST_TAG_LANGUAGE_CODE, &lang)) { - g_free (info->langcode); - info->langcode = lang; - GST_LOG_OBJECT (pad, "language-code = %s", lang); - g_object_notify (G_OBJECT (info), "language-code"); - } - } - - return TRUE; -} - -GstStreamInfo * -gst_stream_info_new (GstObject * object, - GstStreamType type, const gchar * decoder, const GstCaps * caps) -{ - GstStreamInfo *info; - - info = g_object_new (GST_TYPE_STREAM_INFO, NULL); - - gst_object_ref (object); - if (GST_IS_PAD (object)) { - gst_pad_add_probe (GST_PAD_CAST (object), GST_PROBE_TYPE_EVENT, - cb_probe, info, NULL); - } - info->object = object; - info->type = type; - info->decoder = g_strdup (decoder); - info->origin = object; - if (caps) { - info->caps = gst_caps_copy (caps); - } - - return info; -} - -static void -gst_stream_info_dispose (GObject * object) -{ - GstStreamInfo *stream_info; - - stream_info = GST_STREAM_INFO (object); - - if (stream_info->object) { - GstElement *parent; - - parent = gst_pad_get_parent_element ((GstPad *) - GST_PAD_CAST (stream_info->object)); - if (parent != NULL) { - g_signal_handlers_disconnect_by_func (parent, - (gpointer) stream_info_change_state, stream_info); - gst_object_unref (parent); - } - - gst_object_unref (stream_info->object); - stream_info->object = NULL; - } - stream_info->origin = NULL; - stream_info->type = GST_STREAM_TYPE_UNKNOWN; - g_free (stream_info->decoder); - stream_info->decoder = NULL; - g_free (stream_info->langcode); - stream_info->langcode = NULL; - g_free (stream_info->codec); - stream_info->codec = NULL; - if (stream_info->caps) { - gst_caps_unref (stream_info->caps); - stream_info->caps = NULL; - } - - if (G_OBJECT_CLASS (parent_class)->dispose) { - G_OBJECT_CLASS (parent_class)->dispose (object); - } -} - -static void -stream_info_change_state (GstElement * element, - gint old_state, gint new_state, gpointer data) -{ - GstStreamInfo *stream_info = data; - - if (new_state == GST_STATE_PLAYING) { - /* state change will annoy us */ - g_return_if_fail (stream_info->mute == TRUE); - GST_DEBUG_OBJECT (stream_info, "Re-muting pads after state-change"); - //gst_pad_set_active_recursive (GST_PAD (stream_info->object), FALSE); - g_warning ("FIXME"); - } -} - -gboolean -gst_stream_info_set_mute (GstStreamInfo * stream_info, gboolean mute) -{ - g_return_val_if_fail (GST_IS_STREAM_INFO (stream_info), FALSE); - - if (stream_info->type == GST_STREAM_TYPE_ELEMENT) { - g_warning ("cannot mute element stream"); - return FALSE; - } - - if (mute != stream_info->mute) { - /* nothing really happens here. it looks like gstplaybasebin installs a - * buffer probe hat drops buffers when muting. but the this removes it self - * after first call. - */ - - stream_info->mute = mute; -#if 0 - gst_pad_set_active ((GstPad *) GST_PAD_CAST (stream_info->object), !mute); -#endif -#if 0 - { - GstElement *element; - - element = gst_pad_get_parent_element ((GstPad *) - GST_PAD_CAST (stream_info->object)); - if (element) { - if (mute) { - g_signal_connect (element, "state-changed", - G_CALLBACK (stream_info_change_state), stream_info); - } else { - g_signal_handlers_disconnect_by_func (element, - G_CALLBACK (stream_info_change_state), stream_info); - } - gst_object_unref (element); - } - } -#endif - } - return TRUE; -} - -gboolean -gst_stream_info_is_mute (GstStreamInfo * stream_info) -{ - g_return_val_if_fail (GST_IS_STREAM_INFO (stream_info), TRUE); - - return stream_info->mute; -} - -static void -gst_stream_info_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstStreamInfo *stream_info; - - g_return_if_fail (GST_IS_STREAM_INFO (object)); - - stream_info = GST_STREAM_INFO (object); - - switch (prop_id) { - case ARG_MUTE: - gst_stream_info_set_mute (stream_info, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_stream_info_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstStreamInfo *stream_info; - - g_return_if_fail (GST_IS_STREAM_INFO (object)); - - stream_info = GST_STREAM_INFO (object); - - switch (prop_id) { - case ARG_PAD: - g_value_set_object (value, stream_info->object); - break; - case ARG_TYPE: - g_value_set_enum (value, stream_info->type); - break; - case ARG_DECODER: - g_value_set_string (value, stream_info->decoder); - break; - case ARG_MUTE: - g_value_set_boolean (value, stream_info->mute); - break; - case ARG_CAPS: - g_value_set_boxed (value, stream_info->caps); - break; - case ARG_LANG_CODE: - g_value_set_string (value, stream_info->langcode); - break; - case ARG_CODEC: - g_value_set_string (value, stream_info->codec); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/gst/playback/gststreaminfo.h b/gst/playback/gststreaminfo.h deleted file mode 100644 index b4157ea..0000000 --- a/gst/playback/gststreaminfo.h +++ /dev/null @@ -1,86 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * <2007> Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#ifndef __GST_STREAMINFO_H__ -#define __GST_STREAMINFO_H__ - -#include - -G_BEGIN_DECLS - -#define GST_TYPE_STREAM_INFO (gst_stream_info_get_type()) -#define GST_STREAM_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_INFO,GstStreamInfo)) -#define GST_STREAM_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_INFO,GstStreamInfoClass)) -#define GST_IS_STREAM_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_INFO)) -#define GST_IS_STREAM_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_INFO)) - -typedef struct _GstStreamInfo GstStreamInfo; -typedef struct _GstStreamInfoClass GstStreamInfoClass; - -typedef enum { - GST_STREAM_TYPE_UNKNOWN = 0, - GST_STREAM_TYPE_AUDIO = 1, /* an audio stream */ - GST_STREAM_TYPE_VIDEO = 2, /* a video stream */ - GST_STREAM_TYPE_TEXT = 3, /* a subtitle/text stream */ - GST_STREAM_TYPE_SUBPICTURE = 4, /* a subtitle in picture-form */ - GST_STREAM_TYPE_ELEMENT = 5 /* stream handled by an element */ -} GstStreamType; - -struct _GstStreamInfo { - GObject parent; - - GstObject *object; /* pad/element providing/handling this stream */ - GstStreamType type; /* the type of the provided stream */ - gchar *decoder; /* string describing the decoder */ - gboolean mute; /* is the stream muted or not */ - GstObject *origin; /* the real object providing this stream, this can - be different from the object as the object can be - a queue pad, inserted for preroll. */ - GstCaps *caps; /* the caps of the stream */ - - /* this is tream information cached here because the streaminfo may be - * created before the app can know about it. */ - gchar *langcode, - *codec; -}; - -struct _GstStreamInfoClass { - GObjectClass parent_class; - - /* signals */ - void (*muted) (GstStreamInfo *info, gboolean mute); -}; - -GType gst_stream_info_get_type (void); - -GstStreamInfo* gst_stream_info_new (GstObject *object, - GstStreamType type, - const gchar *decoder, - const GstCaps *caps); - -gboolean gst_stream_info_set_mute (GstStreamInfo *stream_info, - gboolean mute); -gboolean gst_stream_info_is_mute (GstStreamInfo *stream_info); - - -G_END_DECLS - -#endif /* __GST_STREAMINFO_H__ */ -- 2.7.4