diff --git a/ChangeLog b/ChangeLog index 75da801d3b..c39c2f1e70 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,48 @@ +2004-07-07 Wim Taymans + + * gst/playback/Makefile.am: + * gst/playback/README: + * gst/playback/gstdecodebin.c: (gst_decode_bin_get_type), + (gst_decode_bin_class_init), (gst_decode_bin_factory_filter), + (compare_ranks), (print_feature), (gst_decode_bin_init), + (gst_decode_bin_dispose), (find_compatibles), (close_pad_link), + (try_to_link_1), (new_pad), (close_link), (type_found), + (gst_decode_bin_set_property), (gst_decode_bin_get_property), + (plugin_init): + * gst/playback/gstplaybasebin.c: (gst_play_base_bin_get_type), + (gst_play_base_bin_class_init), (gst_play_base_bin_init), + (gst_play_base_bin_dispose), (rebuild_pipeline), (queue_overrun), + (gen_preroll_element), (no_more_pads), (new_stream), + (setup_source), (gst_play_base_bin_set_property), + (gst_play_base_bin_get_property), (gst_play_base_bin_change_state), + (gst_play_base_bin_add_element), + (gst_play_base_bin_remove_element), + (gst_play_base_bin_mute_stream), (gst_play_base_bin_link_stream), + (gst_play_base_bin_unlink_stream), + (gst_play_base_bin_get_streaminfo): + * gst/playback/gstplaybasebin.h: + * gst/playback/gstplaybin.c: (gst_play_bin_get_type), + (gst_play_bin_class_init), (gst_play_bin_init), + (gst_play_bin_dispose), (gst_play_bin_set_property), + (gst_play_bin_get_property), (gen_video_element), + (gen_audio_element), (setup_sinks), (gst_play_bin_change_state), + (gst_play_bin_get_event_masks), (gst_play_bin_send_event), + (gst_play_bin_get_formats), (gst_play_bin_convert), + (gst_play_bin_get_query_types), (gst_play_bin_query), + (plugin_init): + * gst/playback/gststreaminfo.c: (gst_stream_type_get_type), + (gst_stream_info_get_type), (gst_stream_info_class_init), + (gst_stream_info_init), (gst_stream_info_new), + (gst_stream_info_dispose), (gst_stream_info_set_property), + (gst_stream_info_get_property): + * gst/playback/gststreaminfo.h: + * gst/playback/test.c: (gen_video_element), (gen_audio_element), + (main): + * gst/playback/test2.c: (main): + * gst/playback/test3.c: (update_scale), (main): + More playbin fixes. Added README. Do better element filtering. + Added base class to preroll media. Added test apps. + 2004-07-07 Thomas Vander Stichele * ext/mpeg2dec/gstmpeg2dec.c: (gst_mpeg2dec_flush_decoder): diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index dbf5a68ae8..f989acb2b8 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -2,7 +2,7 @@ plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ plugin_LTLIBRARIES = libgstplaybin.la libgstdecodebin.la -libgstplaybin_la_SOURCES = gstplaybin.c +libgstplaybin_la_SOURCES = gstplaybin.c gstplaybasebin.c gststreaminfo.c libgstplaybin_la_CFLAGS = $(GST_CFLAGS) libgstplaybin_la_LIBADD = libgstplaybin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) @@ -12,10 +12,16 @@ libgstdecodebin_la_CFLAGS = $(GST_CFLAGS) libgstdecodebin_la_LIBADD = libgstdecodebin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_PROGRAMS = test decodetest +noinst_PROGRAMS = test decodetest test2 test3 test_LDADD = $(GST_LIBS) test_CFLAGS = $(GST_CFLAGS) +test2_LDADD = $(GST_LIBS) +test2_CFLAGS = $(GST_CFLAGS) + +test3_LDADD = $(GST_LIBS) +test3_CFLAGS = $(GST_CFLAGS) + decodetest_LDADD = $(GST_LIBS) decodetest_CFLAGS = $(GST_CFLAGS) diff --git a/gst/playback/README b/gst/playback/README new file mode 100644 index 0000000000..c0553cafe2 --- /dev/null +++ b/gst/playback/README @@ -0,0 +1,48 @@ +decoderbin: + + 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. + + TODO + - reuse of decoderbin, cleanup in READY state + - debugging + - threading after demuxing? + - better factory selection, based on Demuxer/Decoder types and ranks. + - error handling + +baseplaybin: + + 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. + 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 + - debugging + - 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. + - error handling + +playbin: + + Extends baseplaybin, sets up default audiosink and videosink for first audio/video + stream detected. implements seeking and querying on the configured sinks. + + TODO + - reuse, refcounting, cleanup in READY state + - debugging + - error handling + diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c index cbb27ef520..3481fa2e99 100644 --- a/gst/playback/gstdecodebin.c +++ b/gst/playback/gstdecodebin.c @@ -56,6 +56,7 @@ struct _GstDecodeBin GstElement *typefind; gboolean threaded; + gboolean dynamic; GList *factories; gint numpads; @@ -64,6 +65,8 @@ struct _GstDecodeBin struct _GstDecodeBinClass { GstBinClass parent_class; + + void (*new_stream) (GstElement * element, GstPad * pad, gboolean last); }; /* props */ @@ -76,6 +79,7 @@ enum /* signals */ enum { + SIGNAL_NEW_STREAM, LAST_SIGNAL }; @@ -89,19 +93,6 @@ static void gst_decode_bin_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * spec); static GstElementStateReturn gst_decode_bin_change_state (GstElement * element); -static const GstEventMask *gst_decode_bin_get_event_masks (GstElement * - element); -static gboolean gst_decode_bin_send_event (GstElement * element, - GstEvent * event); -static const GstFormat *gst_decode_bin_get_formats (GstElement * element); -static gboolean gst_decode_bin_convert (GstElement * element, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); -static const GstQueryType *gst_decode_bin_get_query_types (GstElement * - element); -static gboolean gst_decode_bin_query (GstElement * element, GstQueryType type, - GstFormat * format, gint64 * value); - static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin); static GstElement *try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, @@ -111,8 +102,7 @@ static void close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, GstDecodeBin * decode_bin); static GstElementClass *parent_class; - -//static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; +static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; static GstElementDetails gst_decode_bin_details = { "Decoder Bin", @@ -169,6 +159,12 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) g_param_spec_boolean ("threaded", "Threaded", "Use threads", FALSE, G_PARAM_READWRITE)); + gst_decode_bin_signals[SIGNAL_NEW_STREAM] = + g_signal_new ("new-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstDecodeBinClass, new_stream), NULL, NULL, + gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, G_TYPE_OBJECT, + G_TYPE_BOOLEAN); + gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose); gst_element_class_add_pad_template (gstelement_klass, @@ -178,15 +174,6 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_decode_bin_change_state); - gstelement_klass->get_event_masks = - GST_DEBUG_FUNCPTR (gst_decode_bin_get_event_masks); - gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_decode_bin_send_event); - gstelement_klass->get_formats = - GST_DEBUG_FUNCPTR (gst_decode_bin_get_formats); - gstelement_klass->convert = GST_DEBUG_FUNCPTR (gst_decode_bin_convert); - gstelement_klass->get_query_types = - GST_DEBUG_FUNCPTR (gst_decode_bin_get_query_types); - gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_decode_bin_query); } @@ -195,12 +182,18 @@ gst_decode_bin_factory_filter (GstPluginFeature * feature, GstDecodeBin * decode_bin) { guint rank; + const gchar *klass; if (!GST_IS_ELEMENT_FACTORY (feature)) return FALSE; + klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); + if (strstr (klass, "Demux") == NULL && strstr (klass, "Decoder") == NULL) { + return FALSE; + } + rank = gst_plugin_feature_get_rank (feature); - if (rank < GST_RANK_SECONDARY) + if (rank < GST_RANK_MARGINAL) return FALSE; return TRUE; @@ -212,6 +205,12 @@ compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) return gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); } +static void +print_feature (GstPluginFeature * feature) +{ + g_print ("%s\n", gst_plugin_feature_get_name (feature)); +} + static void gst_decode_bin_init (GstDecodeBin * decode_bin) { @@ -222,6 +221,7 @@ gst_decode_bin_init (GstDecodeBin * decode_bin) FALSE, decode_bin); decode_bin->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); + g_list_foreach (decode_bin->factories, (GFunc) print_feature, NULL); decode_bin->typefind = gst_element_factory_make ("typefind", "typefind"); if (!decode_bin->typefind) { @@ -274,6 +274,7 @@ find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) if (!gst_caps_is_empty (intersect)) { to_try = g_list_append (to_try, factory); } + gst_caps_free (intersect); } } } @@ -295,11 +296,19 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, if (g_str_has_prefix (mimetype, "video/x-raw") || g_str_has_prefix (mimetype, "audio/x-raw")) { gchar *padname; + GstPad *ghost; padname = g_strdup_printf ("src%d", decode_bin->numpads); decode_bin->numpads++; gst_element_add_ghost_pad (GST_ELEMENT (decode_bin), pad, padname); + + ghost = gst_element_get_pad (GST_ELEMENT (decode_bin), padname); + + g_signal_emit (G_OBJECT (decode_bin), + gst_decode_bin_signals[SIGNAL_NEW_STREAM], 0, + ghost, !decode_bin->dynamic); + g_free (padname); return; } @@ -327,7 +336,8 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) GstElement *element; gboolean ret; - //g_print ("trying to link %s\n", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); + g_print ("trying to link %s\n", + gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); element = gst_element_factory_create (factory, NULL); if (element == NULL) @@ -344,7 +354,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) klass = gst_element_factory_get_klass (factory); if (decode_bin->threaded) { if (strstr (klass, "Demux") != NULL) { - //g_print ("thread after %s\n", gst_element_get_name (element)); + /* FIXME, do something with threads here */ } } @@ -369,6 +379,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) { GList *pads; gboolean dynamic = FALSE; + GList *to_connect = NULL; for (pads = gst_element_get_pad_template_list (element); pads; pads = g_list_next (pads)) { @@ -384,7 +395,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) gst_element_get_pad (element, GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); if (pad) { - close_pad_link (element, pad, gst_pad_get_caps (pad), decode_bin); + to_connect = g_list_prepend (to_connect, pad); } break; } @@ -394,7 +405,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) gst_element_get_pad (element, GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); if (pad) { - close_pad_link (element, pad, gst_pad_get_caps (pad), decode_bin); + to_connect = g_list_prepend (to_connect, pad); } else { dynamic = TRUE; } @@ -407,21 +418,29 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) if (dynamic) { g_signal_connect (G_OBJECT (element), "new_pad", G_CALLBACK (new_pad), decode_bin); + decode_bin->dynamic = TRUE; } + + for (pads = to_connect; pads; pads = g_list_next (pads)) { + GstPad *pad = GST_PAD (pads->data); + + close_pad_link (element, pad, gst_pad_get_caps (pad), decode_bin); + } + g_list_free (to_connect); } static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin) { - gchar *capsstr; - - capsstr = gst_caps_to_string (caps); - //g_print ("found type %s\n", capsstr); - g_free (capsstr); + decode_bin->dynamic = FALSE; close_pad_link (typefind, gst_element_get_pad (typefind, "src"), caps, decode_bin); + + if (decode_bin->dynamic == FALSE) { + gst_element_no_more_pads (GST_ELEMENT (decode_bin)); + } } static void @@ -497,64 +516,6 @@ gst_decode_bin_change_state (GstElement * element) return ret; } -static const GstEventMask * -gst_decode_bin_get_event_masks (GstElement * element) -{ - return NULL; -} - -static gboolean -gst_decode_bin_send_event (GstElement * element, GstEvent * event) -{ - gboolean res = FALSE; - - return res; -} - -static const GstFormat * -gst_decode_bin_get_formats (GstElement * element) -{ - static GstFormat formats[] = { - GST_FORMAT_TIME, - GST_FORMAT_BYTES, - GST_FORMAT_DEFAULT, - 0, - }; - - return formats; -} - -static gboolean -gst_decode_bin_convert (GstElement * element, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) -{ - return FALSE; -} - -static const GstQueryType * -gst_decode_bin_get_query_types (GstElement * element) -{ - static const GstQueryType query_types[] = { - GST_QUERY_TOTAL, - GST_QUERY_POSITION, - GST_QUERY_START, - GST_QUERY_SEGMENT_END, - 0 - }; - - return query_types; -} - -static gboolean -gst_decode_bin_query (GstElement * element, GstQueryType type, - GstFormat * format, gint64 * value) -{ - gboolean res = FALSE; - - return res; -} - static gboolean plugin_init (GstPlugin * plugin) { diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c new file mode 100644 index 0000000000..ffae8b83df --- /dev/null +++ b/gst/playback/gstplaybasebin.c @@ -0,0 +1,539 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstplaybasebin.h" + +GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); +#define GST_CAT_DEFAULT gst_play_base_bin_debug + +/* props */ +enum +{ + ARG_0, + ARG_URI, + ARG_THREADED, + ARG_NSTREAMS, + ARG_STREAMINFO, +}; + +/* signals */ +enum +{ + MUTE_STREAM_SIGNAL, + LINK_STREAM_SIGNAL, + UNLINK_STREAM_SIGNAL, + LAST_SIGNAL +}; + +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_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 GstElementStateReturn gst_play_base_bin_change_state (GstElement * + element); + +static void gst_play_base_bin_add_element (GstBin * bin, GstElement * element); +static void gst_play_base_bin_remove_element (GstBin * bin, + GstElement * element); + + +static GstElementClass *parent_class; +static guint gst_play_base_bin_signals[LAST_SIGNAL] = { 0 }; + +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_BIN, + "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_ref (gst_bin_get_type ()); + + 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_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_object_class_install_property (gobject_klass, ARG_STREAMINFO, + g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo", + G_PARAM_READABLE)); + + gst_play_base_bin_signals[MUTE_STREAM_SIGNAL] = + g_signal_new ("mute-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstPlayBaseBinClass, mute_stream), + NULL, NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, + G_TYPE_OBJECT, G_TYPE_BOOLEAN); + gst_play_base_bin_signals[LINK_STREAM_SIGNAL] = + g_signal_new ("link-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstPlayBaseBinClass, link_stream), + NULL, NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, + G_TYPE_OBJECT, GST_TYPE_PAD); + gst_play_base_bin_signals[UNLINK_STREAM_SIGNAL] = + g_signal_new ("unlink-stream", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstPlayBaseBinClass, unlink_stream), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 1, G_TYPE_OBJECT); + + gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose); + + gstelement_klass->change_state = + GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); + + gstbin_klass->add_element = GST_DEBUG_FUNCPTR (gst_play_base_bin_add_element); + gstbin_klass->remove_element = + GST_DEBUG_FUNCPTR (gst_play_base_bin_remove_element); + + klass->mute_stream = gst_play_base_bin_mute_stream; + klass->link_stream = gst_play_base_bin_link_stream; + klass->unlink_stream = gst_play_base_bin_unlink_stream; +} + +static void +gst_play_base_bin_init (GstPlayBaseBin * play_base_bin) +{ + play_base_bin->uri = NULL; + play_base_bin->threaded = FALSE; + + play_base_bin->preroll_lock = g_mutex_new (); + play_base_bin->preroll_cond = g_cond_new (); + + GST_FLAG_SET (play_base_bin, GST_BIN_SELF_SCHEDULABLE); +} + +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); + + if (G_OBJECT_CLASS (parent_class)->dispose) { + G_OBJECT_CLASS (parent_class)->dispose (object); + } +} + +static void +rebuild_pipeline (GstPlayBaseBin * play_base_bin) +{ + GstElementState oldstate; + + if (play_base_bin->thread == NULL) + return; + + oldstate = gst_element_get_state (play_base_bin->thread); + + gst_element_set_state (play_base_bin->thread, GST_STATE_NULL); + /* remove old elements */ + + /* set to old state again */ + gst_element_set_state (play_base_bin->thread, oldstate); +} + +static void +queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) +{ + g_mutex_lock (play_base_bin->preroll_lock); + g_cond_signal (play_base_bin->preroll_cond); + g_mutex_unlock (play_base_bin->preroll_lock); + //g_signal_handlers_disconnect_by_func(G_OBJECT (element), +// G_CALLBACK (queue_overrun), play_base_bin); +} + +static GstElement * +gen_preroll_element (GstPlayBaseBin * play_base_bin, GstPad * pad) +{ + GstElement *element; + gchar *name; + + name = g_strdup_printf ("preroll_%s", gst_pad_get_name (pad)); + element = gst_element_factory_make ("queue", name); + g_signal_connect (G_OBJECT (element), "overrun", + G_CALLBACK (queue_overrun), play_base_bin); + g_free (name); + + return element; +} + +static void +no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin) +{ + g_mutex_lock (play_base_bin->preroll_lock); + g_cond_signal (play_base_bin->preroll_cond); + g_mutex_unlock (play_base_bin->preroll_lock); +} + +static void +new_stream (GstElement * element, GstPad * pad, gboolean last, + GstPlayBaseBin * play_base_bin) +{ + GstStructure *structure; + const gchar *mimetype; + GstCaps *caps; + GstElement *new_element = NULL; + GstStreamInfo *info; + GstStreamType type; + GstPad *srcpad; + + caps = gst_pad_get_caps (pad); + + structure = gst_caps_get_structure (caps, 0); + mimetype = gst_structure_get_name (structure); + + play_base_bin->nstreams++; + + if (g_str_has_prefix (mimetype, "audio/")) { + type = GST_STREAM_TYPE_AUDIO; + } else if (g_str_has_prefix (mimetype, "video/")) { + type = GST_STREAM_TYPE_VIDEO; + } else { + type = GST_STREAM_TYPE_UNKNOWN; + } + + if (last) { + srcpad = pad; + no_more_pads (NULL, play_base_bin); + } else { + new_element = gen_preroll_element (play_base_bin, pad); + srcpad = gst_element_get_pad (new_element, "src"); + gst_bin_add (GST_BIN (play_base_bin->thread), new_element); + play_base_bin->threaded = TRUE; + + gst_pad_link (pad, gst_element_get_pad (new_element, "sink")); + gst_element_sync_state_with_parent (new_element); + } + + info = gst_stream_info_new (srcpad, type, NULL); + + play_base_bin->streaminfo = g_list_append (play_base_bin->streaminfo, info); +} + +static gboolean +setup_source (GstPlayBaseBin * play_base_bin) +{ + if (play_base_bin->source) { + gst_bin_remove (GST_BIN (play_base_bin->thread), play_base_bin->source); + gst_object_unref (GST_OBJECT (play_base_bin->source)); + } + + play_base_bin->source = + gst_element_make_from_uri (GST_URI_SRC, play_base_bin->uri, "source"); + if (!play_base_bin->source) { + g_warning ("don't know how to read %s", play_base_bin->uri); + return FALSE; + } + + { + GstElement *decoder; + gboolean res; + + gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->source); + + decoder = gst_element_factory_make ("decodebin", "decoder"); + if (!decoder) { + g_warning ("can't find decoder element"); + return FALSE; + } + + + gst_bin_add (GST_BIN (play_base_bin->thread), decoder); + res = gst_element_link_pads (play_base_bin->source, "src", decoder, "sink"); + if (!res) { + g_warning ("can't link source to typefind element"); + return FALSE; + } + g_signal_connect (G_OBJECT (decoder), "new_stream", + G_CALLBACK (new_stream), play_base_bin); + g_signal_connect (G_OBJECT (decoder), "no-more-pads", + G_CALLBACK (no_more_pads), play_base_bin); + + g_mutex_lock (play_base_bin->preroll_lock); + gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING); + g_cond_wait (play_base_bin->preroll_cond, play_base_bin->preroll_lock); + g_mutex_unlock (play_base_bin->preroll_lock); + } + + return TRUE; +} + +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 (!play_base_bin->uri || !strcmp (play_base_bin->uri, uri)) { + g_free (play_base_bin->uri); + play_base_bin->uri = g_strdup (uri); + + rebuild_pipeline (play_base_bin); + } + 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_NSTREAMS: + g_value_set_int (value, play_base_bin->nstreams); + break; + case ARG_STREAMINFO: + g_value_set_pointer (value, play_base_bin->streaminfo); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstElementStateReturn +gst_play_base_bin_change_state (GstElement * element) +{ + GstElementStateReturn ret = GST_STATE_SUCCESS; + GstPlayBaseBin *play_base_bin; + + play_base_bin = GST_PLAY_BASE_BIN (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + { + GstScheduler *sched; + + play_base_bin->thread = gst_thread_new ("internal_thread"); + sched = gst_scheduler_factory_make ("opt", play_base_bin->thread); + if (sched) { + gst_element_set_scheduler (play_base_bin->thread, sched); + + gst_object_set_parent (GST_OBJECT (play_base_bin->thread), + GST_OBJECT (play_base_bin)); + + gst_element_set_state (play_base_bin->thread, GST_STATE_READY); + } else { + g_warning ("could not get 'opt' scheduler"); + gst_object_unref (GST_OBJECT (play_base_bin->thread)); + play_base_bin->thread = NULL; + + ret = GST_STATE_FAILURE; + } + break; + } + case GST_STATE_READY_TO_PAUSED: + { + if (!setup_source (play_base_bin)) { + GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), LIBRARY, TOO_LAZY, + (NULL), ("cannot handle uri \"%s\"", play_base_bin->uri)); + ret = GST_STATE_FAILURE; + } else { + ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); + } + break; + } + case GST_STATE_PAUSED_TO_PLAYING: + ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING); + break; + case GST_STATE_PLAYING_TO_PAUSED: + ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); + break; + case GST_STATE_PAUSED_TO_READY: + ret = gst_element_set_state (play_base_bin->thread, GST_STATE_READY); + break; + case GST_STATE_READY_TO_NULL: + ret = gst_element_set_state (play_base_bin->thread, GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (play_base_bin->thread)); + break; + default: + g_assert_not_reached (); + break; + } + + if (ret == GST_STATE_SUCCESS) { + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + } + + return ret; +} + +static void +gst_play_base_bin_add_element (GstBin * bin, GstElement * element) +{ + GstPlayBaseBin *play_base_bin; + + play_base_bin = GST_PLAY_BASE_BIN (bin); + + if (play_base_bin->thread) { + GstScheduler *sched; + GstClock *clock; + + if (play_base_bin->threaded) { + gchar *name; + GstElement *thread; + + name = g_strdup_printf ("thread_%s", gst_element_get_name (element)); + thread = gst_thread_new (name); + g_free (name); + + gst_bin_add (GST_BIN (thread), element); + element = thread; + } + gst_bin_add (GST_BIN (play_base_bin->thread), element); + + sched = gst_element_get_scheduler (GST_ELEMENT (play_base_bin->thread)); + clock = gst_scheduler_get_clock (sched); + gst_scheduler_set_clock (sched, clock); + + gst_element_sync_state_with_parent (element); + } else { + g_warning ("adding elements is not allowed in NULL"); + } +} + +static void +gst_play_base_bin_remove_element (GstBin * bin, GstElement * element) +{ + GstPlayBaseBin *play_base_bin; + + play_base_bin = GST_PLAY_BASE_BIN (bin); + + if (play_base_bin->thread) { + gst_bin_remove (GST_BIN (play_base_bin->thread), element); + } else { + g_warning ("removing elements is not allowed in NULL"); + } +} + +void +gst_play_base_bin_mute_stream (GstPlayBaseBin * play_base_bin, + GstStreamInfo * info, gboolean mute) +{ + g_print ("mute\n"); +} + +void +gst_play_base_bin_link_stream (GstPlayBaseBin * play_base_bin, + GstStreamInfo * info, GstPad * pad) +{ + if (info == NULL) { + GList *streams; + + for (streams = play_base_bin->streaminfo; streams; + streams = g_list_next (streams)) { + GstStreamInfo *sinfo = (GstStreamInfo *) streams->data; + + if (gst_pad_is_linked (sinfo->pad)) + continue; + + if (gst_pad_can_link (sinfo->pad, pad)) { + info = sinfo; + break; + } + } + } + if (info) { + if (!gst_pad_link (info->pad, pad)) { + g_print ("could not link\n"); + } + } else { + g_print ("could not find pad to link\n"); + } +} + +void +gst_play_base_bin_unlink_stream (GstPlayBaseBin * play_base_bin, + GstStreamInfo * info) +{ + g_print ("unlink\n"); +} + +const GList * +gst_play_base_bin_get_streaminfo (GstPlayBaseBin * play_base_bin) +{ + return play_base_bin->streaminfo; +} diff --git a/gst/playback/gstplaybasebin.h b/gst/playback/gstplaybasebin.h new file mode 100644 index 0000000000..f238508615 --- /dev/null +++ b/gst/playback/gstplaybasebin.h @@ -0,0 +1,90 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_PLAYBASEBIN_H__ +#define __GST_PLAYBASEBIN_H__ + +#include +#include "gststreaminfo.h" + +G_BEGIN_DECLS + +#define GST_TYPE_PLAY_BASE_BIN (gst_play_base_bin_get_type()) +#define GST_PLAY_BASE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BASE_BIN,GstPlayBaseBin)) +#define GST_PLAY_BASE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BASE_BIN,GstPlayBaseBinClass)) +#define GST_IS_PLAY_BASE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BASE_BIN)) +#define GST_IS_PLAY_BASE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BASE_BIN)) + +typedef struct _GstPlayBaseBin GstPlayBaseBin; +typedef struct _GstPlayBaseBinClass GstPlayBaseBinClass; + +struct _GstPlayBaseBin { + GstBin bin; + + /* properties */ + gboolean threaded; + GMutex *preroll_lock; + GCond *preroll_cond; + + /* internal thread */ + GstElement *thread; + gchar *uri; + GstElement *source; + + gint nstreams; + GList *streaminfo; + + /* list of usable factories */ + GList *factories; +}; + +struct _GstPlayBaseBinClass { + GstBinClass parent_class; + + void (*mute_stream) (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info, + gboolean mute); + void (*link_stream) (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info, + GstPad *pad); + void (*unlink_stream) (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info); +}; + +GType gst_play_base_bin_get_type (void); + +gint gst_play_base_bin_get_nstreams (GstPlayBaseBin *play_base_bin); +const GList* gst_play_base_bin_get_streaminfo (GstPlayBaseBin *play_base_bin); +gint gst_play_base_bin_get_nstreams_of_type (GstPlayBaseBin *play_base_bin, + GstStreamType type); + +void gst_play_base_bin_mute_stream (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info, + gboolean mute); +void gst_play_base_bin_link_stream (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info, + GstPad *pad); +void gst_play_base_bin_unlink_stream (GstPlayBaseBin *play_base_bin, + GstStreamInfo *info); + +G_END_DECLS + +#endif /* __GST_PLAYBASEBIN_H__ */ + diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c index 39fb0a9dc2..d27e9d5957 100644 --- a/gst/playback/gstplaybin.c +++ b/gst/playback/gstplaybin.c @@ -24,6 +24,8 @@ #include #include +#include "gstplaybasebin.h" + GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug); #define GST_CAT_DEFAULT gst_play_bin_debug @@ -38,38 +40,24 @@ typedef struct _GstPlayBinClass GstPlayBinClass; struct _GstPlayBin { - GstBin bin; + GstPlayBaseBin parent; - GstElement *thread; - - gboolean threaded; - - /* properties */ - gchar *uri; - GstElement *source; - - GList *factories; GstElement *audio_sink; GstElement *video_sink; - GstElement *vis_plugin; + GstElement *visualisation; - gboolean audio_added; - gboolean video_added; - - GList *outpads; + GList *seekables; }; struct _GstPlayBinClass { - GstBinClass parent_class; + GstPlayBaseBinClass parent_class; }; /* props */ enum { ARG_0, - ARG_URI, - ARG_THREADED, ARG_AUDIO_SINK, ARG_VIDEO_SINK, ARG_VIS_PLUGIN, @@ -101,9 +89,6 @@ static gboolean gst_play_bin_convert (GstElement * element, static const GstQueryType *gst_play_bin_get_query_types (GstElement * element); static gboolean gst_play_bin_query (GstElement * element, GstQueryType type, GstFormat * format, gint64 * value); -static GstClock *gst_play_bin_get_clock (GstElement * element); -static void gst_play_bin_add_element (GstBin * bin, GstElement * element); -static void gst_play_bin_remove_element (GstBin * bin, GstElement * element); static GstElementClass *parent_class; @@ -137,9 +122,8 @@ gst_play_bin_get_type (void) NULL }; - gst_play_bin_type = - g_type_register_static (GST_TYPE_BIN, "GstPlayBin", &gst_play_bin_info, - 0); + gst_play_bin_type = g_type_register_static (GST_TYPE_PLAY_BASE_BIN, + "GstPlayBin", &gst_play_bin_info, 0); } return gst_play_bin_type; @@ -156,17 +140,11 @@ gst_play_bin_class_init (GstPlayBinClass * klass) gstelement_klass = (GstElementClass *) klass; gstbin_klass = (GstBinClass *) klass; - parent_class = g_type_class_ref (gst_bin_get_type ()); + parent_class = g_type_class_ref (gst_play_base_bin_get_type ()); 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_URI, - g_param_spec_string ("uri", "URI", "URI of the media to play", - NULL, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_klass, ARG_THREADED, - g_param_spec_boolean ("threaded", "Threaded", "Use threads", - TRUE, G_PARAM_READWRITE)); 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)", @@ -194,23 +172,15 @@ gst_play_bin_class_init (GstPlayBinClass * klass) gstelement_klass->get_query_types = GST_DEBUG_FUNCPTR (gst_play_bin_get_query_types); gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query); - gstelement_klass->get_clock = GST_DEBUG_FUNCPTR (gst_play_bin_get_clock); - - gstbin_klass->add_element = GST_DEBUG_FUNCPTR (gst_play_bin_add_element); - gstbin_klass->remove_element = - GST_DEBUG_FUNCPTR (gst_play_bin_remove_element); } static void gst_play_bin_init (GstPlayBin * play_bin) { - play_bin->uri = NULL; play_bin->video_sink = NULL; play_bin->audio_sink = NULL; - play_bin->vis_plugin = NULL; - play_bin->audio_added = FALSE; - play_bin->video_added = FALSE; - play_bin->threaded = TRUE; + play_bin->visualisation = NULL; + play_bin->seekables = NULL; GST_FLAG_SET (play_bin, GST_BIN_SELF_SCHEDULABLE); } @@ -221,181 +191,12 @@ gst_play_bin_dispose (GObject * object) GstPlayBin *play_bin; play_bin = GST_PLAY_BIN (object); - g_free (play_bin->uri); if (G_OBJECT_CLASS (parent_class)->dispose) { G_OBJECT_CLASS (parent_class)->dispose (object); } } -static void -rebuild_pipeline (GstPlayBin * play_bin) -{ - GstElementState oldstate; - - if (play_bin->thread == NULL) - return; - - oldstate = gst_element_get_state (play_bin->thread); - - gst_element_set_state (play_bin->thread, GST_STATE_NULL); - /* remove old elements */ - - /* set to old state again */ - gst_element_set_state (play_bin->thread, oldstate); -} - -static GstElement * -get_audio_element (GstPlayBin * play_bin) -{ - GstElement *result; - GstElement *queue; - GstElement *conv; - GstElement *sink; - GstPad *sinkpad; - - conv = gst_element_factory_make ("audioconvert", "a_conv"); - - if (play_bin->threaded) { - result = gst_thread_new ("audio_thread"); - queue = gst_element_factory_make ("queue", "a_queue"); - gst_bin_add (GST_BIN (result), queue); - gst_bin_add (GST_BIN (result), conv); - gst_element_link_pads (queue, "src", conv, "sink"); - sinkpad = gst_element_get_pad (queue, "sink"); - } else { - result = gst_bin_new ("audio_bin"); - gst_bin_add (GST_BIN (result), conv); - sinkpad = gst_element_get_pad (conv, "sink"); - } - - sink = gst_element_factory_make ("osssink", "a_sink"); - gst_bin_add (GST_BIN (result), sink); - - gst_element_link_pads (conv, "src", sink, "sink"); - - gst_element_add_ghost_pad (result, sinkpad, "sink"); - - return result; -} - -static GstElement * -get_video_element (GstPlayBin * play_bin) -{ - GstElement *result; - GstElement *queue; - GstElement *conv; - GstElement *sink; - GstPad *sinkpad; - - conv = gst_element_factory_make ("ffmpegcolorspace", "v_conv"); - - if (play_bin->threaded) { - result = gst_thread_new ("video_thread"); - queue = gst_element_factory_make ("queue", "v_queue"); - gst_bin_add (GST_BIN (result), queue); - gst_bin_add (GST_BIN (result), conv); - gst_element_link_pads (queue, "src", conv, "sink"); - sinkpad = gst_element_get_pad (queue, "sink"); - } else { - result = gst_bin_new ("video_bin"); - gst_bin_add (GST_BIN (result), conv); - sinkpad = gst_element_get_pad (conv, "sink"); - } - - sink = gst_element_factory_make ("ximagesink", "v_sink"); - gst_bin_add (GST_BIN (result), sink); - - gst_element_link_pads (conv, "src", sink, "sink"); - - gst_element_add_ghost_pad (result, sinkpad, "sink"); - - return result; -} - -static void -new_pad (GstElement * element, GstPad * pad, GstPlayBin * play_bin) -{ - GstStructure *structure; - const gchar *mimetype; - GstCaps *caps; - GstClock *clock; - GstElement *new_element = NULL; - - caps = gst_pad_get_caps (pad); - - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - if (g_str_has_prefix (mimetype, "audio/")) { - if (!play_bin->audio_added) { - new_element = get_audio_element (play_bin); - play_bin->audio_added = TRUE; - } - } else if (g_str_has_prefix (mimetype, "video/")) { - if (!play_bin->video_added) { - new_element = get_video_element (play_bin); - play_bin->video_added = TRUE; - } - } else { - } - - if (new_element) { - GstScheduler *sched; - - gst_bin_add (GST_BIN (play_bin->thread), new_element); - - gst_pad_link (pad, gst_element_get_pad (new_element, "sink")); - - sched = gst_element_get_scheduler (GST_ELEMENT (play_bin->thread)); - - gst_bin_auto_clock (GST_BIN (play_bin->thread)); - clock = gst_bin_get_clock (GST_BIN (play_bin->thread)); - gst_scheduler_set_clock (sched, clock); - gst_element_sync_state_with_parent (new_element); - } -} - -static gboolean -setup_source (GstPlayBin * play_bin) -{ - if (play_bin->source) { - gst_bin_remove (GST_BIN (play_bin->thread), play_bin->source); - gst_object_unref (GST_OBJECT (play_bin->source)); - } - - play_bin->source = - gst_element_make_from_uri (GST_URI_SRC, play_bin->uri, "source"); - if (!play_bin->source) { - g_warning ("don't know how to read %s", play_bin->uri); - return FALSE; - } - - { - GstElement *decoder; - gboolean res; - - gst_bin_add (GST_BIN (play_bin->thread), play_bin->source); - - decoder = gst_element_factory_make ("decodebin", "decoder"); - if (!decoder) { - g_warning ("can't find decoder element"); - return FALSE; - } - - gst_bin_add (GST_BIN (play_bin->thread), decoder); - res = gst_element_link_pads (play_bin->source, "src", decoder, "sink"); - if (!res) { - g_warning ("can't link source to typefind element"); - return FALSE; - } - g_signal_connect (G_OBJECT (decoder), "new_pad", - G_CALLBACK (new_pad), play_bin); - } - - return TRUE; -} - static void gst_play_bin_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -407,25 +208,6 @@ gst_play_bin_set_property (GObject * object, guint prop_id, play_bin = GST_PLAY_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 (!play_bin->uri || !strcmp (play_bin->uri, uri)) { - g_free (play_bin->uri); - play_bin->uri = g_strdup (uri); - - rebuild_pipeline (play_bin); - } - break; - } - case ARG_THREADED: - play_bin->threaded = g_value_get_boolean (value); - break; case ARG_VIDEO_SINK: break; case ARG_AUDIO_SINK: @@ -449,12 +231,6 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value, play_bin = GST_PLAY_BIN (object); switch (prop_id) { - case ARG_URI: - g_value_set_string (value, play_bin->uri); - break; - case ARG_THREADED: - g_value_set_boolean (value, play_bin->threaded); - break; case ARG_VIDEO_SINK: break; case ARG_AUDIO_SINK: @@ -467,89 +243,137 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value, } } + +static GstElement * +gen_video_element (GstPlayBin * play_bin) +{ + GstElement *element; + GstElement *conv; + GstElement *sink; + + element = gst_bin_new ("vbin"); + conv = gst_element_factory_make ("ffmpegcolorspace", "conv"); + if (play_bin->video_sink) { + sink = play_bin->video_sink; + } else { + sink = gst_element_factory_make ("ximagesink", "sink"); + } + + play_bin->seekables = g_list_prepend (play_bin->seekables, sink); + + gst_bin_add (GST_BIN (element), conv); + gst_bin_add (GST_BIN (element), sink); + gst_element_link_pads (conv, "src", sink, "sink"); + + gst_element_add_ghost_pad (element, gst_element_get_pad (conv, "sink"), + "sink"); + + return element; +} + +static GstElement * +gen_audio_element (GstPlayBin * play_bin) +{ + GstElement *element; + GstElement *conv; + GstElement *sink; + + element = gst_bin_new ("abin"); + conv = gst_element_factory_make ("audioconvert", "conv"); + if (play_bin->audio_sink) { + sink = play_bin->audio_sink; + } else { + sink = gst_element_factory_make ("osssink", "sink"); + } + + play_bin->seekables = g_list_prepend (play_bin->seekables, sink); + + gst_bin_add (GST_BIN (element), conv); + gst_bin_add (GST_BIN (element), sink); + gst_element_link_pads (conv, "src", sink, "sink"); + + gst_element_add_ghost_pad (element, + gst_element_get_pad (conv, "sink"), "sink"); + + return element; +} + +static void +setup_sinks (GstPlayBin * play_bin) +{ + GList *streaminfo; + GList *s; + + /* get info about the stream */ + g_object_get (G_OBJECT (play_bin), "stream-info", &streaminfo, NULL); + + for (s = streaminfo; s; s = g_list_next (s)) { + GObject *obj = G_OBJECT (s->data); + int type; + GstPad *srcpad, *sinkpad; + GstElement *sink; + gboolean res; + + g_object_get (obj, "type", &type, NULL); + g_object_get (obj, "pad", &srcpad, NULL); + + if (type == 1) { + sink = gen_audio_element (play_bin); + } else if (type == 2) { + sink = gen_video_element (play_bin); + } else { + g_warning ("unknown stream found"); + continue; + } + + gst_bin_add (GST_BIN (play_bin), sink); + sinkpad = gst_element_get_pad (sink, "sink"); + res = gst_pad_link (srcpad, sinkpad); + if (!res) { + gchar *capsstr; + + capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad)); + g_warning ("could not link %s", capsstr); + g_free (capsstr); + } + } +} + static GstElementStateReturn gst_play_bin_change_state (GstElement * element) { - GstElementStateReturn ret = GST_STATE_SUCCESS; + GstElementStateReturn ret; GstPlayBin *play_bin; + int transition; play_bin = GST_PLAY_BIN (element); - switch (GST_STATE_TRANSITION (element)) { + transition = GST_STATE_TRANSITION (element); + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { case GST_STATE_NULL_TO_READY: - { - GstScheduler *sched; - - play_bin->thread = gst_thread_new ("internal_thread"); - sched = gst_scheduler_factory_make ("opt", play_bin->thread); - if (sched) { - gst_element_set_scheduler (play_bin->thread, sched); - - gst_object_set_parent (GST_OBJECT (play_bin->thread), - GST_OBJECT (play_bin)); - - gst_element_set_state (play_bin->thread, GST_STATE_READY); - } else { - g_warning ("could not get 'opt' scheduler"); - gst_object_unref (GST_OBJECT (play_bin->thread)); - play_bin->thread = NULL; - - ret = GST_STATE_FAILURE; - } break; - } case GST_STATE_READY_TO_PAUSED: - { - if (!setup_source (play_bin)) { - GST_ELEMENT_ERROR (GST_ELEMENT (play_bin), LIBRARY, TOO_LAZY, (NULL), - ("cannot handle uri \"%s\"", play_bin->uri)); - ret = GST_STATE_FAILURE; - } else { - ret = gst_element_set_state (play_bin->thread, GST_STATE_PAUSED); - } + setup_sinks (play_bin); break; - } case GST_STATE_PAUSED_TO_PLAYING: - ret = gst_element_set_state (play_bin->thread, GST_STATE_PLAYING); break; case GST_STATE_PLAYING_TO_PAUSED: - ret = gst_element_set_state (play_bin->thread, GST_STATE_PAUSED); break; case GST_STATE_PAUSED_TO_READY: - ret = gst_element_set_state (play_bin->thread, GST_STATE_READY); break; case GST_STATE_READY_TO_NULL: - ret = gst_element_set_state (play_bin->thread, GST_STATE_NULL); - - gst_object_unref (GST_OBJECT (play_bin->thread)); break; default: g_assert_not_reached (); break; } - if (ret == GST_STATE_SUCCESS) { - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - } - return ret; } -static void -gst_play_bin_add_element (GstBin * bin, GstElement * element) -{ - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (bin); -} - -static void -gst_play_bin_remove_element (GstBin * bin, GstElement * element) -{ - GstPlayBin *play_bin; - - play_bin = GST_PLAY_BIN (bin); -} static const GstEventMask * gst_play_bin_get_event_masks (GstElement * element) @@ -561,7 +385,20 @@ static gboolean gst_play_bin_send_event (GstElement * element, GstEvent * event) { gboolean res = FALSE; + GList *s; + GstPlayBin *play_bin; + play_bin = GST_PLAY_BIN (element); + + s = play_bin->seekables; + + for (s = play_bin->seekables; s; s = g_list_next (s)) { + GstElement *element = GST_ELEMENT (s->data); + gboolean ok; + + ok = gst_element_send_event (element, event); + res |= ok; + } return res; } @@ -570,8 +407,6 @@ gst_play_bin_get_formats (GstElement * element) { static GstFormat formats[] = { GST_FORMAT_TIME, - GST_FORMAT_BYTES, - GST_FORMAT_DEFAULT, 0, }; @@ -583,7 +418,23 @@ gst_play_bin_convert (GstElement * element, GstFormat src_format, gint64 src_value, GstFormat * dest_format, gint64 * dest_value) { - return FALSE; + gboolean res = FALSE; + GList *s; + GstPlayBin *play_bin; + + play_bin = GST_PLAY_BIN (element); + + s = play_bin->seekables; + + for (s = play_bin->seekables; s; s = g_list_next (s)) { + GstElement *element = GST_ELEMENT (s->data); + + res = gst_element_convert (element, src_format, src_value, + dest_format, dest_value); + if (res) + break; + } + return res; } static const GstQueryType * @@ -592,8 +443,6 @@ gst_play_bin_get_query_types (GstElement * element) static const GstQueryType query_types[] = { GST_QUERY_TOTAL, GST_QUERY_POSITION, - GST_QUERY_START, - GST_QUERY_SEGMENT_END, 0 }; @@ -605,18 +454,21 @@ gst_play_bin_query (GstElement * element, GstQueryType type, GstFormat * format, gint64 * value) { gboolean res = FALSE; - - return res; -} - -static GstClock * -gst_play_bin_get_clock (GstElement * element) -{ + GList *s; GstPlayBin *play_bin; play_bin = GST_PLAY_BIN (element); - return gst_bin_get_clock (GST_BIN (play_bin->thread)); + s = play_bin->seekables; + + for (s = play_bin->seekables; s; s = g_list_next (s)) { + GstElement *element = GST_ELEMENT (s->data); + + res = gst_element_query (element, type, format, value); + if (res) + break; + } + return res; } static gboolean diff --git a/gst/playback/gststreaminfo.c b/gst/playback/gststreaminfo.c new file mode 100644 index 0000000000..16483c1373 --- /dev/null +++ b/gst/playback/gststreaminfo.c @@ -0,0 +1,208 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gststreaminfo.h" + +/* props */ +enum +{ + ARG_0, + ARG_PAD, + ARG_TYPE, + ARG_DECODER, +}; + +/* signals */ +enum +{ + LAST_SIGNAL +}; + +#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 GEnumValue stream_type[] = { + {GST_STREAM_TYPE_UNKNOWN, "GST_STREAM_TYPE_UNKNOWN", "Unknown stream"}, + {GST_STREAM_TYPE_AUDIO, "GST_STREAM_TYPE_AUDIO", "Audio stream"}, + {GST_STREAM_TYPE_VIDEO, "GST_STREAM_TYPE_VIDEO", "Video stream"}, + {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 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_ref (G_TYPE_OBJECT); + + 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 ("pad", "Pad", "Source Pad of the stream", + GST_TYPE_PAD, G_PARAM_READABLE)); + 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_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)); + + gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_stream_info_dispose); +} + + +static void +gst_stream_info_init (GstStreamInfo * stream_info) +{ + stream_info->pad = NULL; + stream_info->type = GST_STREAM_TYPE_UNKNOWN; + stream_info->decoder = NULL; +} + +GstStreamInfo * +gst_stream_info_new (GstPad * pad, GstStreamType type, gchar * decoder) +{ + GstStreamInfo *info; + + info = g_object_new (GST_TYPE_STREAM_INFO, NULL); + + gst_object_ref (GST_OBJECT (pad)); + info->pad = pad; + info->type = type; + info->decoder = g_strdup (decoder); + + return info; +} + +static void +gst_stream_info_dispose (GObject * object) +{ + GstStreamInfo *stream_info; + + stream_info = GST_STREAM_INFO (object); + + gst_object_unref (GST_OBJECT (stream_info->pad)); + stream_info->pad = NULL; + stream_info->type = GST_STREAM_TYPE_UNKNOWN; + g_free (stream_info->decoder); + + if (G_OBJECT_CLASS (parent_class)->dispose) { + G_OBJECT_CLASS (parent_class)->dispose (object); + } +} + + +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) { + 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->pad); + 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; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} diff --git a/gst/playback/gststreaminfo.h b/gst/playback/gststreaminfo.h new file mode 100644 index 0000000000..b97f58d00f --- /dev/null +++ b/gst/playback/gststreaminfo.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_STREAMINFO_H__ +#define __GST_STREAMINFO_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_STREAM_INFO (gst_stream_info_get_type()) +#define GST_STREAM_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STREAM_INFO,GstStreamInfo)) +#define GST_STREAM_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STREAM_INFO,GstStreamInfoClass)) +#define GST_IS_STREAM_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STREAM_INFO)) +#define GST_IS_STREAM_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STREAM_INFO)) + +typedef struct _GstStreamInfo GstStreamInfo; +typedef struct _GstStreamInfoClass GstStreamInfoClass; + +typedef enum { + GST_STREAM_TYPE_UNKNOWN = 0, + GST_STREAM_TYPE_AUDIO = 1, + GST_STREAM_TYPE_VIDEO = 2, +} GstStreamType; + +struct _GstStreamInfo { + GObject parent; + + GstPad *pad; + GstStreamType type; + gchar *decoder; +}; + +struct _GstStreamInfoClass { + GObjectClass parent_class; +}; + +GType gst_stream_info_get_type (void); + +GstStreamInfo* gst_stream_info_new (GstPad *pad, GstStreamType type, gchar *decoder); + + +G_END_DECLS + +#endif /* __GST_STREAMINFO_H__ */ + diff --git a/gst/playback/test.c b/gst/playback/test.c index e4dae85e4b..a53133d511 100644 --- a/gst/playback/test.c +++ b/gst/playback/test.c @@ -18,11 +18,56 @@ */ #include +static GstElement * +gen_video_element () +{ + GstElement *element; + GstElement *conv; + GstElement *sink; + + element = gst_thread_new ("vbin"); + conv = gst_element_factory_make ("ffmpegcolorspace", "conv"); + sink = gst_element_factory_make ("ximagesink", "sink"); + + gst_bin_add (GST_BIN (element), conv); + gst_bin_add (GST_BIN (element), sink); + gst_element_link_pads (conv, "src", sink, "sink"); + + gst_element_add_ghost_pad (element, gst_element_get_pad (conv, "sink"), + "sink"); + + return element; +} + +static GstElement * +gen_audio_element () +{ + GstElement *element; + GstElement *conv; + GstElement *sink; + + element = gst_thread_new ("abin"); + conv = gst_element_factory_make ("audioconvert", "conv"); + sink = gst_element_factory_make ("osssink", "sink"); + + gst_bin_add (GST_BIN (element), conv); + gst_bin_add (GST_BIN (element), sink); + gst_element_link_pads (conv, "src", sink, "sink"); + + gst_element_add_ghost_pad (element, + gst_element_get_pad (conv, "sink"), "sink"); + + return element; +} + gint main (gint argc, gchar * argv[]) { GstElement *player; GstElementStateReturn res; + gint streams; + GList *streaminfo; + GList *s; gst_init (&argc, &argv); @@ -33,10 +78,48 @@ main (gint argc, gchar * argv[]) res = gst_element_set_state (player, GST_STATE_PAUSED); if (res != GST_STATE_SUCCESS) { - g_print ("could not play\n"); + g_print ("could not pause\n"); return -1; } /* get info about the stream */ + g_print ("stream info:\n"); + g_object_get (G_OBJECT (player), "nstreams", &streams, NULL); + g_print (" number of streams: %d\n", streams); + g_object_get (G_OBJECT (player), "stream-info", &streaminfo, NULL); + + for (s = streaminfo; s; s = g_list_next (s)) { + GObject *obj = G_OBJECT (s->data); + int type; + GstPad *srcpad, *sinkpad; + GstElement *sink; + gboolean res; + + g_object_get (obj, "type", &type, NULL); + g_print (" type: %d\n", type); + g_object_get (obj, "pad", &srcpad, NULL); + g_print (" pad: %p\n", srcpad); + + if (type == 1) { + sink = gen_audio_element (); + } else if (type == 2) { + sink = gen_video_element (); + } else { + g_warning ("unknown stream found"); + continue; + } + + gst_bin_add (GST_BIN (player), sink); + sinkpad = gst_element_get_pad (sink, "sink"); + res = gst_pad_link (srcpad, sinkpad); + if (!res) { + gchar *capsstr; + + capsstr = gst_caps_to_string (gst_pad_get_caps (srcpad)); + g_warning ("could not link %s", capsstr); + g_free (capsstr); + } + //g_signal_emit_by_name (G_OBJECT (player), "link_stream", obj, sinkpad); + } /* set effects sinks */ res = gst_element_set_state (player, GST_STATE_PLAYING); diff --git a/gst/playback/test2.c b/gst/playback/test2.c new file mode 100644 index 0000000000..9df35089b7 --- /dev/null +++ b/gst/playback/test2.c @@ -0,0 +1,43 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *player; + GstElementStateReturn res; + + gst_init (&argc, &argv); + + player = gst_element_factory_make ("playbin", "player"); + g_assert (player); + + g_object_set (G_OBJECT (player), "uri", argv[1], NULL); + + res = gst_element_set_state (player, GST_STATE_PLAYING); + if (res != GST_STATE_SUCCESS) { + g_print ("could not play\n"); + return -1; + } + + gst_main (); + + return 0; +} diff --git a/gst/playback/test3.c b/gst/playback/test3.c new file mode 100644 index 0000000000..a4c64f1bac --- /dev/null +++ b/gst/playback/test3.c @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include + +#define UPDATE_INTERVAL 500 + +static gboolean +update_scale (GstElement * element) +{ + guint64 duration; + guint64 position; + GstFormat format = GST_FORMAT_TIME; + + gst_element_query (element, GST_QUERY_TOTAL, &format, &duration); + gst_element_query (element, GST_QUERY_POSITION, &format, &position); + + g_print ("%lld %lld\n", duration, position); + + return TRUE; +} + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *player; + GstElementStateReturn res; + + gst_init (&argc, &argv); + + player = gst_element_factory_make ("playbin", "player"); + g_assert (player); + + g_object_set (G_OBJECT (player), "uri", argv[1], NULL); + + res = gst_element_set_state (player, GST_STATE_PLAYING); + if (res != GST_STATE_SUCCESS) { + g_print ("could not play\n"); + return -1; + } + + g_timeout_add (UPDATE_INTERVAL, (GSourceFunc) update_scale, player); + + gst_main (); + + return 0; +}