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 \
$(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)
noinst_HEADERS = \
gstplayback.h \
- gstplaybasebin.h \
gstplaysink.h \
- gststreaminfo.h \
gstplay-enum.h \
gststreamselector.h \
gstrawcaps.h \
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) \
+++ /dev/null
-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.
-
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2004> Wim Taymans <wim.taymans@gmail.com>
- *
- * 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.
- *
- * <note>
- * This element is deprecated and no longer supported. You should use the
- * #uridecodebin or #decodebin2 element instead (or, even better: #playbin2).
- * </note>
- *
- * Deprecated: use uridecodebin or decodebin2 instead.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gst/gst-i18n-plugin.h>
-
-#include <string.h>
-#include <gst/gst.h>
-#include <gst/pbutils/pbutils.h>
-
-#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 <wim.taymans@gmail.com>");
-
- 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)
#include "gstplayback.h"
#include "gstplaysink.h"
#include "gststreamselector.h"
-#include "gststreaminfo.h"
#include "gstsubtitleoverlay.h"
static gboolean
/* 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);
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * 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 <gst/gst-i18n-plugin.h>
-#include <string.h>
-#include "gstplaybasebin.h"
-#include "gststreamselector.h"
-#include "gstplay-marshal.h"
-
-#include <gst/pbutils/pbutils.h>
-
-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)
- * <note><simpara>
- * Since version 0.10.10 in #GstPlayBin, at 0.10.15 moved to #GstPlayBaseBin
- * </simpara></note>
- *
- * 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;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- * <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * 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 <gst/gst.h>
-#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__ */
-
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * 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.
- *
- * <note>
- * This element is deprecated and no longer supported. You should use
- * the #playbin2 element instead.
- * </note>
- *
- * It can handle both audio and video files and features
- * <itemizedlist>
- * <listitem>
- * automatic file type recognition and based on that automatic
- * selection and usage of the right audio/video/subtitle demuxers/decoders
- * </listitem>
- * <listitem>
- * visualisations for audio files
- * </listitem>
- * <listitem>
- * subtitle support for video files
- * </listitem>
- * <listitem>
- * stream selection between different audio/subtitles streams
- * </listitem>
- * <listitem>
- * meta info (tag) extraction
- * </listitem>
- * <listitem>
- * easy access to the last video frame
- * </listitem>
- * <listitem>
- * buffering when playing streams over a network
- * </listitem>
- * <listitem>
- * volume control
- * </listitem>
- * </itemizedlist>
- *
- * <refsect2>
- * <title>Usage</title>
- * <para>
- * 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.
- * </para>
- * </refsect2>
- * <refsect2>
- * <title>Advanced Usage: specifying the audio and video sink</title>
- * <para>
- * 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!).
- * </para>
- * </refsect2>
- * <refsect2>
- * <title>Retrieving Tags and Other Meta Data</title>
- * <para>
- * 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:
- * <itemizedlist>
- * <listitem>"object" (GstObject) (the decoder source pad usually)</listitem>
- * <listitem>"type" (enum) (if this is an audio/video/subtitle stream)</listitem>
- * <listitem>"decoder" (string) (name of decoder used to decode this stream)</listitem>
- * <listitem>"mute" (boolean) (to mute or unmute this stream)</listitem>
- * <listitem>"caps" (GstCaps) (caps of the decoded stream)</listitem>
- * <listitem>"language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams)</listitem>
- * <listitem>"codec" (string) (format this stream was encoded in)</listitem>
- * </itemizedlist>
- * 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).
- * </para>
- * </refsect2>
- * <refsect2>
- * <title>Buffering</title>
- * 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).
- * </refsect2>
- * <refsect2>
- * <title>Embedding the video window in your application</title>
- * 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.
- * </refsect2>
- * <refsect2>
- * <title>Specifying which CD/DVD device to use</title>
- * 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.
- * </refsect2>
- * <refsect2>
- * <title>Examples</title>
- * |[
- * 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).
- * </refsect2>
- *
- * Deprecated: use playbin2 instead
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <gst/gst.h>
-
-#include <gst/gst-i18n-plugin.h>
-#include <gst/pbutils/pbutils.h>
-
-#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 <wim.taymans@gmail.com>");
-
- 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);
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * 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 <string.h>
-#include <gst/gst.h>
-#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;
- }
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- * <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * 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 <gst/gst.h>
-
-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__ */