Commit 24f28cfd authored by Tim-Philipp Müller's avatar Tim-Philipp Müller
Browse files

playback: remove old playbin and decodebin elements

parent 2c6dbae4
......@@ -6,16 +6,13 @@ glib_gen_basename = gstplay
built_sources = gstplay-marshal.c
built_headers = gstplay-marshal.h
plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la libgstdecodebin2.la
plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin2.la
libgstplaybin_la_SOURCES = \
gstplayback.c \
gstplaybin.c \
gstplaybin2.c \
gstplaysink.c \
gstplaybasebin.c \
gstplay-enum.c \
gststreaminfo.c \
gststreamselector.c \
gstsubtitleoverlay.c \
gstplaysinkvideoconvert.c \
......@@ -32,15 +29,6 @@ libgstplaybin_la_LIBADD = \
$(GST_LIBS)
libgstplaybin_la_LIBTOOLFLAGS = --tag=disable-static
libgstdecodebin_la_SOURCES = gstdecodebin.c
nodist_libgstdecodebin_la_SOURCES = $(built_sources)
libgstdecodebin_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libgstdecodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstdecodebin_la_LIBADD = \
$(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la \
$(GST_LIBS)
libgstdecodebin_la_LIBTOOLFLAGS = --tag=disable-static
libgstdecodebin2_la_SOURCES = gstdecodebin2.c gsturidecodebin.c gstplay-enum.c
nodist_libgstdecodebin2_la_SOURCES = $(built_sources)
libgstdecodebin2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
......@@ -52,9 +40,7 @@ libgstdecodebin2_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = \
gstplayback.h \
gstplaybasebin.h \
gstplaysink.h \
gststreaminfo.h \
gstplay-enum.h \
gststreamselector.h \
gstrawcaps.h \
......@@ -73,18 +59,6 @@ include $(top_srcdir)/common/gst-glib-gen.mak
Android.mk: Makefile.am $(BUILT_SOURCES)
androgenizer \
-:PROJECT libgstdecodebin -:SHARED libgstdecodebin \
-:TAGS eng debug \
-:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
-:SOURCES $(libgstdecodebin_la_SOURCES) \
$(nodist_libgstdecodebin_la_SOURCES) \
-:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstdecodebin_la_CFLAGS) \
-:LDFLAGS $(libgstdecodebin_la_LDFLAGS) \
$(libgstdecodebin_la_LIBADD) \
-ldl \
-:PASSTHROUGH LOCAL_ARM_MODE:=arm \
LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
\
-:PROJECT libgstdecodebin2 -:SHARED libgstdecodebin2 \
-:TAGS eng debug \
-:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
......
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.
/* 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));