From 50e2f24b4424d35941390adb2b43fc35c49c21a1 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 12 Apr 2005 10:48:58 +0000 Subject: [PATCH] Raw and crude port of decodebin. Original commit message from CVS: * gst/playback/README: * gst/playback/gstdecodebin.c: (gst_decode_bin_class_init), (compare_ranks), (print_feature), (gst_decode_bin_init), (dynamic_create), (dynamic_free), (find_compatibles), (mimetype_is_raw), (close_pad_link), (got_redirect), (try_to_link_1), (get_our_ghost_pad), (remove_element_chain), (new_pad), (no_more_pads), (unlinked), (close_link), (type_found), (gst_decode_bin_change_state): * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), (gst_play_base_bin_init), (group_destroy), (group_commit), (check_queue), (queue_overrun), (queue_threshold_reached), (queue_out_of_data), (gen_preroll_element), (unknown_type), (new_decoded_pad), (setup_subtitle), (gen_source_element), (got_redirect), (setup_source), (play_base_eos), (gst_play_base_bin_change_state), (gst_play_base_bin_add_element), (gst_play_base_bin_remove_element): * gst/playback/gstplaybasebin.h: * gst/playback/gstplaybin.c: (gst_play_bin_class_init), (gst_play_bin_init), (gst_play_bin_dispose), (gst_play_bin_set_property), (gen_video_element), (gen_text_element), (gen_audio_element), (remove_sinks), (gst_play_bin_send_event): * gst/playback/gststreaminfo.c: (gst_stream_info_dispose), (stream_info_change_state), (gst_stream_info_set_mute): * gst/playback/gststreamselector.c: (gst_stream_selector_init), (gst_stream_selector_get_caps), (gst_stream_selector_setcaps), (gst_stream_selector_request_new_pad), (gst_stream_selector_event), (gst_stream_selector_chain): * gst/playback/test.c: (gen_video_element), (gen_audio_element), (main): * sys/xvimage/xvimagesink.c: (gst_xvimagesink_getcaps), (gst_xvimagesink_setcaps), (gst_xvimagesink_get_times), (gst_xvimagesink_show_frame), (gst_xvimagesink_chain), (gst_xvimagesink_buffer_alloc), (gst_xvimagesink_class_init): Raw and crude port of decodebin. Make playbin compile. --- ChangeLog | 39 ++++ gst/playback/README | 21 ++ gst/playback/gstdecodebin.c | 259 +++++++++++++++------- gst/playback/gstplaybasebin.c | 357 ++++++++++++++++++------------- gst/playback/gstplaybasebin.h | 6 + gst/playback/gstplaybin.c | 46 +++- gst/playback/gststreaminfo.c | 8 +- gst/playback/gststreamselector.c | 67 +++--- gst/playback/test.c | 7 +- sys/xvimage/xvimagesink.c | 2 - 10 files changed, 525 insertions(+), 287 deletions(-) diff --git a/ChangeLog b/ChangeLog index 29b2ecc395..5287ef1a91 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +2005-04-12 Wim Taymans + + * gst/playback/README: + * gst/playback/gstdecodebin.c: (gst_decode_bin_class_init), + (compare_ranks), (print_feature), (gst_decode_bin_init), + (dynamic_create), (dynamic_free), (find_compatibles), + (mimetype_is_raw), (close_pad_link), (got_redirect), + (try_to_link_1), (get_our_ghost_pad), (remove_element_chain), + (new_pad), (no_more_pads), (unlinked), (close_link), (type_found), + (gst_decode_bin_change_state): + * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), + (gst_play_base_bin_init), (group_destroy), (group_commit), + (check_queue), (queue_overrun), (queue_threshold_reached), + (queue_out_of_data), (gen_preroll_element), (unknown_type), + (new_decoded_pad), (setup_subtitle), (gen_source_element), + (got_redirect), (setup_source), (play_base_eos), + (gst_play_base_bin_change_state), (gst_play_base_bin_add_element), + (gst_play_base_bin_remove_element): + * gst/playback/gstplaybasebin.h: + * gst/playback/gstplaybin.c: (gst_play_bin_class_init), + (gst_play_bin_init), (gst_play_bin_dispose), + (gst_play_bin_set_property), (gen_video_element), + (gen_text_element), (gen_audio_element), (remove_sinks), + (gst_play_bin_send_event): + * gst/playback/gststreaminfo.c: (gst_stream_info_dispose), + (stream_info_change_state), (gst_stream_info_set_mute): + * gst/playback/gststreamselector.c: (gst_stream_selector_init), + (gst_stream_selector_get_caps), (gst_stream_selector_setcaps), + (gst_stream_selector_request_new_pad), (gst_stream_selector_event), + (gst_stream_selector_chain): + * gst/playback/test.c: (gen_video_element), (gen_audio_element), + (main): + * sys/xvimage/xvimagesink.c: (gst_xvimagesink_getcaps), + (gst_xvimagesink_setcaps), (gst_xvimagesink_get_times), + (gst_xvimagesink_show_frame), (gst_xvimagesink_chain), + (gst_xvimagesink_buffer_alloc), (gst_xvimagesink_class_init): + Raw and crude port of decodebin. + Make playbin compile. + 2005-04-06 Wim Taymans * ext/gnomevfs/Makefile.am: diff --git a/gst/playback/README b/gst/playback/README index 5726a1a4f8..3a50371a6b 100644 --- a/gst/playback/README +++ b/gst/playback/README @@ -12,6 +12,27 @@ decoderbin: - 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: diff --git a/gst/playback/gstdecodebin.c b/gst/playback/gstdecodebin.c index e7e680ef8f..33a740935c 100644 --- a/gst/playback/gstdecodebin.c +++ b/gst/playback/gstdecodebin.c @@ -78,8 +78,12 @@ struct _GstDecodeBinClass 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); + /* signal fired when we got a redirect attempt */ + void (*got_redirect) (GstElement * element, const gchar * new_location); }; +#define DEFAULT_THREADED FALSE + /* props */ enum { @@ -93,6 +97,7 @@ enum SIGNAL_NEW_DECODED_PAD, SIGNAL_REMOVED_DECODED_PAD, SIGNAL_UNKNOWN_TYPE, + SIGNAL_REDIRECT, LAST_SIGNAL }; @@ -127,6 +132,8 @@ 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 GstElementClass *parent_class; static guint gst_decode_bin_signals[LAST_SIGNAL] = { 0 }; @@ -184,7 +191,7 @@ gst_decode_bin_class_init (GstDecodeBinClass * klass) g_object_class_install_property (gobject_klass, ARG_THREADED, g_param_spec_boolean ("threaded", "Threaded", "Use threads", - FALSE, G_PARAM_READWRITE)); + DEFAULT_THREADED, G_PARAM_READWRITE)); gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD] = g_signal_new ("new-decoded-pad", G_TYPE_FROM_CLASS (klass), @@ -202,6 +209,11 @@ gst_decode_bin_class_init (GstDecodeBinClass * 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); + gst_decode_bin_signals[SIGNAL_REDIRECT] = + g_signal_new ("got-redirect", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDecodeBinClass, got_redirect), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, + G_TYPE_STRING); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_decode_bin_dispose); @@ -260,18 +272,28 @@ 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; - return strcmp (gst_plugin_feature_get_name (f2), - gst_plugin_feature_get_name (f1)); + + 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) { - GST_DEBUG ("%s", gst_plugin_feature_get_name (feature)); + const gchar *rname; + + rname = gst_plugin_feature_get_name (feature); + + GST_DEBUG ("%s", rname); } static void @@ -307,6 +329,7 @@ gst_decode_bin_init (GstDecodeBin * decode_bin) G_CALLBACK (type_found), decode_bin); } + decode_bin->threaded = DEFAULT_THREADED; decode_bin->dynamics = NULL; } @@ -338,6 +361,40 @@ gst_decode_bin_dispose (GObject * object) } } +static GstDynamic * +dynamic_create (GstElement * element, GstDecodeBin * decode_bin) +{ + GstDynamic *dyn; + + /* take refs */ + gst_object_ref (GST_OBJECT (element)); + gst_object_ref (GST_OBJECT (decode_bin)); + + dyn = g_new0 (GstDynamic, 1); + dyn->element = element; + dyn->decode_bin = decode_bin; + dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad", + G_CALLBACK (new_pad), dyn); + dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads", + G_CALLBACK (no_more_pads), dyn); + + return dyn; +} + +static void +dynamic_free (GstDynamic * dyn) +{ + /* disconnect signals */ + g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->np_sig_id); + g_signal_handler_disconnect (G_OBJECT (dyn->element), dyn->nmp_sig_id); + + gst_object_unref (GST_OBJECT (dyn->element)); + gst_object_unref (GST_OBJECT (dyn->decode_bin)); + dyn->element = NULL; + dyn->decode_bin = NULL; + g_free (dyn); +} + /* this function runs through the element factories and returns a list * of all elements that are able to sink the given caps */ @@ -369,17 +426,27 @@ find_compatibles (GstDecodeBin * decode_bin, const GstCaps * caps) /* check if the intersection is empty */ if (!gst_caps_is_empty (intersect)) { /* non empty intersection, we can use this element */ - to_try = g_list_append (to_try, factory); - gst_caps_free (intersect); + to_try = g_list_prepend (to_try, factory); + gst_caps_unref (intersect); break; } - gst_caps_free (intersect); + gst_caps_unref (intersect); } } } + 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"); +} + /* given a pad and a caps from an element, find the list of elements * that could connect to the pad * @@ -393,26 +460,28 @@ static void close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, GstDecodeBin * decode_bin, gboolean more) { - GList *to_try; GstStructure *structure; const gchar *mimetype; + gchar *padname; + gint diff; - if (!strncmp (gst_pad_get_name (pad), "current_", 8)) + 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)) { - g_signal_emit (G_OBJECT (decode_bin), - gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); - return; - } + 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)) { - return; - } + if (gst_caps_is_any (caps)) + goto dont_know_yet; GST_LOG_OBJECT (element, "trying to close %" GST_PTR_FORMAT, caps); @@ -424,9 +493,7 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, /* first see if this is raw. If the type is raw, we can * create a ghostpad for this pad. */ - if (g_str_has_prefix (mimetype, "video/x-raw") || - g_str_has_prefix (mimetype, "audio/x-raw") || - g_str_has_prefix (mimetype, "text/plain")) { + if (mimetype_is_raw (mimetype)) { gchar *padname; GstPad *ghost; @@ -444,27 +511,62 @@ close_pad_link (GstElement * element, GstPad * pad, GstCaps * caps, gst_decode_bin_signals[SIGNAL_NEW_DECODED_PAD], 0, ghost, !more); g_free (padname); + } else { + GList *to_try; + + /* if the caps has many types, we need to delay */ + if (gst_caps_get_size (caps) != 1) + 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; + + try_to_link_1 (decode_bin, pad, to_try); + /* can free the list again now */ + g_list_free (to_try); + } + return; + +unknown_type: + { + GST_LOG_OBJECT (pad, "unkown type found, fire signal"); + g_signal_emit (G_OBJECT (decode_bin), + gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); return; } - - if (gst_caps_get_size (caps) == 1) { - /* then continue plugging, first find all compatible elements */ - to_try = find_compatibles (decode_bin, caps); - if (to_try == NULL) { - /* no compatible elements, fire the unknown_type signal, we cannot go - * on */ - g_signal_emit (G_OBJECT (decode_bin), - gst_decode_bin_signals[SIGNAL_UNKNOWN_TYPE], 0, pad, caps); - return; - } - try_to_link_1 (decode_bin, pad, to_try); - } else { - GST_LOG_OBJECT (element, "multiple possibilities, delaying"); +dont_know_yet: + { + GST_LOG_OBJECT (pad, "type is not known yet, waiting to close link"); + return; + } +many_types: + { + GST_LOG_OBJECT (pad, "many possible types, waiting to close link"); + return; } } -/* given a list of element factories, try to link one of the factories - * to the given pad */ +/* + * Called when we're redirected to a new URI. + */ +static void +got_redirect (GstElement * element, + const gchar * new_location, GstDecodeBin * decode_bin) +{ + g_signal_emit (decode_bin, gst_decode_bin_signals[SIGNAL_REDIRECT], 0, + new_location); +} + +/** + * 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, GstPad * pad, GList * factories) { @@ -474,7 +576,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) for (walk = factories; walk; walk = g_list_next (walk)) { GstElementFactory *factory = GST_ELEMENT_FACTORY (walk->data); GstElement *element; - gboolean ret; + GstPadLinkReturn ret; GST_DEBUG_OBJECT (decode_bin, "trying to link %s", gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); @@ -492,17 +594,17 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) GST_DEBUG_OBJECT (decode_bin, "adding %s", gst_element_get_name (element)); gst_bin_add (GST_BIN (decode_bin), element); - /* set to ready first so it can do negotiation */ + /* set to ready first so it is ready */ gst_element_set_state (element, GST_STATE_READY); - /* keep out own list of elements */ + /* keep our own list of elements */ decode_bin->elements = g_list_prepend (decode_bin->elements, element); /* 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 */ ret = gst_pad_link (pad, gst_element_get_pad (element, "sink")); - if (ret) { + if (ret == GST_PAD_LINK_OK) { const gchar *klass; GstElementFactory *factory; guint sig; @@ -522,6 +624,12 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) * because that would consume less memory. */ } } + /* catch redirects */ + if (g_signal_lookup ("got-redirect", G_OBJECT_TYPE (element))) { + g_signal_connect (element, "got-redirect", + G_CALLBACK (got_redirect), decode_bin); + } + /* make sure we catch unlink signals */ sig = g_signal_connect (G_OBJECT (GST_PAD_REALIZE (pad)), "unlinked", G_CALLBACK (unlinked), decode_bin); @@ -532,7 +640,7 @@ try_to_link_1 (GstDecodeBin * decode_bin, GstPad * pad, GList * factories) * on it until we have a raw type */ close_link (element, decode_bin); /* change the state of the element to that of the parent */ - gst_element_sync_state_with_parent (element); + gst_element_set_state (element, GST_STATE_PAUSED); return element; } else { GST_DEBUG_OBJECT (decode_bin, "link failed on pad %s:%s", @@ -560,6 +668,7 @@ get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad) GST_DEBUG_OBJECT (decode_bin, "pad parent %s", gst_element_get_name (parent)); + if (parent == GST_ELEMENT (decode_bin)) { GST_DEBUG_OBJECT (decode_bin, "pad is our ghostpad"); return pad; @@ -570,7 +679,7 @@ get_our_ghost_pad (GstDecodeBin * decode_bin, GstPad * pad) } GST_DEBUG_OBJECT (decode_bin, "looping over ghostpads"); - ghostpads = gst_pad_get_ghost_pad_list (pad); + ghostpads = GST_REAL_PAD (pad)->ghostpads; while (ghostpads) { GstPad *ghostpad; @@ -595,6 +704,10 @@ remove_element_chain (GstDecodeBin * decode_bin, GstPad * pad) GList *int_links; GstElement *elem = gst_pad_get_parent (pad); + while (GST_OBJECT_PARENT (elem) && + GST_OBJECT_PARENT (elem) != GST_OBJECT (decode_bin)) + elem = GST_ELEMENT (GST_OBJECT_PARENT (elem)); + GST_DEBUG_OBJECT (decode_bin, "%s:%s", GST_DEBUG_PAD_NAME (pad)); /* remove all elements linked to this pad up to the ghostpad @@ -657,7 +770,7 @@ new_pad (GstElement * element, GstPad * pad, GstDynamic * dynamic) caps = gst_pad_get_caps (pad); close_pad_link (element, pad, caps, decode_bin, more); if (caps) - gst_caps_free (caps); + gst_caps_unref (caps); } /* this signal is fired when an element signals the no_more_pads signal. @@ -673,14 +786,9 @@ no_more_pads (GstElement * element, GstDynamic * dynamic) GST_DEBUG_OBJECT (decode_bin, "no more pads on element %s", gst_element_get_name (element)); - /* disconnect signals */ - g_signal_handler_disconnect (G_OBJECT (dynamic->element), dynamic->np_sig_id); - g_signal_handler_disconnect (G_OBJECT (dynamic->element), - dynamic->nmp_sig_id); - /* remove the element from the list of dynamic elements */ decode_bin->dynamics = g_list_remove (decode_bin->dynamics, dynamic); - g_free (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 */ @@ -697,7 +805,6 @@ no_more_pads (GstElement * element, GstDynamic * dynamic) static void unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin) { - GList *walk; GstDynamic *dyn; GstElement *element; @@ -709,25 +816,17 @@ unlinked (GstPad * pad, GstPad * peerpad, GstDecodeBin * decode_bin) /* if an element removes two pads, then we don't want this twice */ element = gst_pad_get_parent (pad); - for (walk = decode_bin->dynamics; walk != NULL; walk = walk->next) { - dyn = walk->data; - if (dyn->element == element) - return; - } + if (g_list_find (decode_bin->dynamics, element) != NULL) + goto exit; GST_DEBUG_OBJECT (decode_bin, "pad removal while alive - chained?"); - /* re-setup dynamic plugging */ - dyn = g_new0 (GstDynamic, 1); - dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad", - G_CALLBACK (new_pad), dyn); - dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads", - G_CALLBACK (no_more_pads), dyn); - dyn->element = element; - dyn->decode_bin = decode_bin; - + dyn = dynamic_create (element, decode_bin); /* and add this element to the dynamic elements */ decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); + +exit: + gst_object_unref (GST_OBJECT (element)); } /* this function inspects the given element and tries to connect something @@ -745,7 +844,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) gst_element_get_name (element)); /* loop over all the padtemplates */ - for (pads = gst_element_get_pad_template_list (element); pads; + 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; @@ -809,14 +908,8 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) 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 */ - dyn = g_new0 (GstDynamic, 1); - dyn->np_sig_id = g_signal_connect (G_OBJECT (element), "new-pad", - G_CALLBACK (new_pad), dyn); - dyn->nmp_sig_id = g_signal_connect (G_OBJECT (element), "no-more-pads", - G_CALLBACK (no_more_pads), dyn); - dyn->element = element; - dyn->decode_bin = decode_bin; + dyn = dynamic_create (element, decode_bin); /* and add this element to the dynamic elements */ decode_bin->dynamics = g_list_prepend (decode_bin->dynamics, dyn); } @@ -844,7 +937,7 @@ close_link (GstElement * element, GstDecodeBin * decode_bin) caps = gst_pad_get_caps (pad); close_pad_link (element, pad, caps, decode_bin, more); if (caps) - gst_caps_free (caps); + gst_caps_unref (caps); } g_list_free (to_connect); @@ -857,12 +950,14 @@ 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); /* autoplug the new pad with the caps that the signal gave us. */ - close_pad_link (typefind, gst_element_get_pad (typefind, "src"), caps, - decode_bin, FALSE); + pad = gst_element_get_pad (typefind, "src"); + close_pad_link (typefind, pad, caps, decode_bin, FALSE); + gst_object_unref (GST_OBJECT (pad)); dynamic = gst_decode_bin_is_dynamic (decode_bin); if (dynamic == FALSE) { @@ -927,23 +1022,23 @@ gst_decode_bin_change_state (GstElement * element) transition = GST_STATE_TRANSITION (element); - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - if (ret != GST_STATE_SUCCESS) { - return ret; - } - switch (transition) { case GST_STATE_NULL_TO_READY: decode_bin->numpads = 0; - decode_bin->threaded = FALSE; decode_bin->dynamics = NULL; break; case GST_STATE_READY_TO_PAUSED: case GST_STATE_PAUSED_TO_PLAYING: + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (transition) { case GST_STATE_PLAYING_TO_PAUSED: case GST_STATE_PAUSED_TO_READY: case GST_STATE_READY_TO_NULL: - break; default: break; } diff --git a/gst/playback/gstplaybasebin.c b/gst/playback/gstplaybasebin.c index dcae3bb978..c87d4f9f5a 100644 --- a/gst/playback/gstplaybasebin.c +++ b/gst/playback/gstplaybasebin.c @@ -29,6 +29,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); #define GST_CAT_DEFAULT gst_play_base_bin_debug +#define DEFAULT_QUEUE_THRESHOLD (2 * GST_SECOND) #define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) /* props */ @@ -37,9 +38,9 @@ enum ARG_0, ARG_URI, ARG_SUBURI, - ARG_THREADED, - ARG_NSTREAMS, ARG_QUEUE_SIZE, + ARG_QUEUE_THRESHOLD, + ARG_NSTREAMS, ARG_STREAMINFO, ARG_SOURCE, ARG_VIDEO, @@ -56,6 +57,7 @@ enum GROUP_SWITCH_SIGNAL, LINK_STREAM_SIGNAL, UNLINK_STREAM_SIGNAL, + REDIRECT, LAST_SIGNAL }; @@ -70,8 +72,9 @@ static void gst_play_base_bin_get_property (GObject * object, guint prop_id, 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, +static gboolean gst_play_base_bin_add_element (GstBin * bin, + GstElement * element); +static gboolean gst_play_base_bin_remove_element (GstBin * bin, GstElement * element); extern GstElementStateReturn gst_element_set_state_func (GstElement * element, @@ -141,13 +144,19 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) 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_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_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_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_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)); @@ -192,6 +201,12 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstPlayBaseBinClass, group_switch), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + gst_play_base_bin_signals[REDIRECT] = + g_signal_new ("got-redirect", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GstPlayBaseBinClass, got_redirect), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, + G_TYPE_STRING); /* action signals */ gst_play_base_bin_signals[LINK_STREAM_SIGNAL] = @@ -209,7 +224,6 @@ gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose); /* we handle state changes like an element */ - gstelement_klass->set_state = GST_ELEMENT_CLASS (element_class)->set_state; gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); @@ -227,6 +241,7 @@ 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->decoder = NULL; play_base_bin->subtitle = NULL; @@ -238,8 +253,7 @@ gst_play_base_bin_init (GstPlayBaseBin * play_base_bin) play_base_bin->queued_groups = NULL; play_base_bin->queue_size = DEFAULT_QUEUE_SIZE; - - GST_FLAG_SET (play_base_bin, GST_BIN_SELF_SCHEDULABLE); + play_base_bin->queue_threshold = DEFAULT_QUEUE_THRESHOLD; } static void @@ -314,7 +328,7 @@ group_destroy (GstPlayBaseGroup * group) continue; /* remove any fakesrc elements for this preroll element */ - for (item = gst_element_get_pad_list (group->type[n].selector); + for (item = GST_ELEMENT (group->type[n].selector)->pads; item != NULL; item = item->next) { GstPad *pad = GST_PAD (item->data); guint sig_id; @@ -417,7 +431,7 @@ group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal) GST_DEBUG ("signaled group done"); g_mutex_unlock (play_base_bin->group_lock); - if (!had_active_group && GST_STATE (play_base_bin) > GST_STATE_READY) { + if (group && !had_active_group && GST_STATE (play_base_bin) > GST_STATE_READY) { setup_substreams (play_base_bin); GST_DEBUG ("Emitting signal"); g_signal_emit (play_base_bin, @@ -441,6 +455,30 @@ group_is_muted (GstPlayBaseGroup * group) return TRUE; } +/* + * Buffer/cache checking. + */ + +static gboolean +check_queue (GstProbe * probe, GstData ** data, gpointer user_data) +{ + GstElement *queue = GST_ELEMENT (user_data); + GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb"); + guint64 level = 0; + + g_object_get (G_OBJECT (queue), "current-level-time", &level, NULL); + GST_DEBUG ("Queue size: %" GST_TIME_FORMAT, GST_TIME_ARGS (level)); + level = level * 100 / play_base_bin->queue_threshold; + if (level > 100) + level = 100; + + g_signal_emit (play_base_bin, + gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, level); + + /* continue! */ + return TRUE; +} + /* 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 */ @@ -448,7 +486,72 @@ static void queue_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) { GST_DEBUG ("queue %s overrun", gst_element_get_name (element)); + group_commit (play_base_bin, FALSE); + + g_signal_handlers_disconnect_by_func (element, + G_CALLBACK (queue_overrun), play_base_bin); +} + +/* Used for time-based buffering. */ +static void +queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin) +{ + GstProbe *probe; + + GST_DEBUG ("Running"); + + /* play */ + g_object_set (queue, "min-threshold-time", (guint64) 0, NULL); + + if ((probe = g_object_get_data (G_OBJECT (queue), "probe"))) { + GstPad *sinkpad; + + sinkpad = gst_element_get_pad (queue, "sink"); + GST_DEBUG_OBJECT (play_base_bin, + "Removing buffer probe %p from pad %s:%s (%p)", + probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad); + + g_signal_emit (play_base_bin, + gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100); + + g_object_set_data (G_OBJECT (queue), "probe", NULL); + gst_pad_remove_probe (sinkpad, probe); + gst_probe_destroy (probe); + + g_signal_emit (play_base_bin, + gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100); + } +} + +static void +queue_out_of_data (GstElement * queue, GstPlayBaseBin * play_base_bin) +{ + GstProbe *probe; + + GST_DEBUG ("Underrun, re-caching"); + + /* On underrun, we want to temoprarily pause playback, set a "min-size" + * threshold and wait for the running signal and then play again. Take + * care of possible deadlocks and so on, */ + g_object_set (queue, "min-threshold-time", + (guint64) play_base_bin->queue_threshold, NULL); + + /* re-connect probe */ + if (!(probe = g_object_get_data (G_OBJECT (queue), "probe"))) { + GstPad *sinkpad; + + probe = gst_probe_new (FALSE, check_queue, queue); + sinkpad = gst_element_get_pad (queue, "sink"); + gst_pad_add_probe (sinkpad, probe); + g_object_set_data (G_OBJECT (queue), "probe", probe); + GST_DEBUG_OBJECT (play_base_bin, + "Re-attaching buffering probe %p to pad %s:%s (%p)", + probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad); + + g_signal_emit (play_base_bin, + gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 0); + } } /* generate a preroll element which is simply a queue. While there @@ -491,6 +594,31 @@ gen_preroll_element (GstPlayBaseBin * play_base_bin, "max-size-time", play_base_bin->queue_size, NULL); sig = g_signal_connect (G_OBJECT (preroll), "overrun", G_CALLBACK (queue_overrun), play_base_bin); + 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))) { + GstProbe *probe; + GstPad *sinkpad; + + g_signal_connect (G_OBJECT (preroll), "running", + G_CALLBACK (queue_threshold_reached), play_base_bin); + g_object_set (G_OBJECT (preroll), + "min-threshold-time", (guint64) play_base_bin->queue_threshold, NULL); + + /* give updates on queue size */ + probe = gst_probe_new (FALSE, check_queue, preroll); + sinkpad = gst_element_get_pad (preroll, "sink"); + gst_pad_add_probe (sinkpad, probe); + GST_DEBUG_OBJECT (play_base_bin, "Attaching probe %p to pad %s:%s (%p)", + probe, GST_DEBUG_PAD_NAME (sinkpad), sinkpad); + g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin); + g_object_set_data (G_OBJECT (preroll), "probe", probe); + + g_signal_connect (G_OBJECT (preroll), "underrun", + G_CALLBACK (queue_out_of_data), 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), "signal_id", GINT_TO_POINTER (sig)); @@ -583,7 +711,7 @@ unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, GstPlayBaseGroup *group = get_building_group (play_base_bin); capsstr = gst_caps_to_string (caps); - g_message /*warning */ ("don't know how to handle %s", capsstr); + g_message ("don't know how to handle %s", capsstr); /* add the stream to the list */ info = gst_stream_info_new (GST_OBJECT (pad), GST_STREAM_TYPE_UNKNOWN, @@ -807,7 +935,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, g_warning ("no type on pad %s:%s", GST_DEBUG_PAD_NAME (GST_PAD_REALIZE (pad))); if (caps) - gst_caps_free (caps); + gst_caps_unref (caps); return; } @@ -848,7 +976,7 @@ new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, gst_pad_link (pad, sinkpad); /* add the stream to the list */ - gst_caps_free (caps); + gst_caps_unref (caps); info->origin = GST_OBJECT (pad); /* select 1st for now - we'll select a preferred one after preroll */ @@ -905,67 +1033,6 @@ state_change (GstElement * element, } } -/* - * Buffer/cache checking. FIXME: make configurable. - * Note that we could also do this (buffering) at the - * preroll-level. The advantage there is that it'd - * allow us to cache in time-units rather than byte- - * units. Ohwell... - */ - -static gboolean -check_queue (GstProbe * probe, GstData ** data, gpointer user_data) -{ - GstElement *queue = GST_ELEMENT (user_data); - GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb"); - guint level = 0; - - g_object_get (G_OBJECT (queue), "current-level-bytes", &level, NULL); - GST_DEBUG ("Queue size: %u bytes", level); - g_signal_emit (play_base_bin, - gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, - level * 100 / (512 * 1024)); - - /* continue! */ - return TRUE; -} - -static void -buffer_underrun (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GST_DEBUG ("Underrun, re-caching"); - - /* On underrun, we want to temoprarily pause playback, set a "min-size" - * treshold and wait for the running signal and then play again. Take - * care of possible deadlocks and so on, */ - g_object_set (queue, "min-threshold-bytes", 64 * 1024, NULL); - - g_signal_emit (play_base_bin, - gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 0); -} - -static void -buffer_running (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GST_DEBUG ("Running"); - - /* When we had an underrun, we now want to play again. */ - g_object_set (queue, "min-threshold-bytes", 0, - "max-size-bytes", 512 * 1024, NULL); -} - -static void -buffer_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin) -{ - GST_DEBUG ("Overrun, leaking upstream and flushing next few buffers"); - - /* we want to decrease max-size here so the next few bytes are flushed */ - g_object_set (queue, "max-size-bytes", 448 * 1024, NULL); - - g_signal_emit (play_base_bin, - gst_play_base_bin_signals[BUFFERING_SIGNAL], 0, 100); -} - /* * Generate source ! subparse bins. */ @@ -980,9 +1047,12 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri) if (!source) return NULL; - subparse = gst_element_factory_make ("subparse", NULL); + if (!(subparse = gst_element_factory_make ("subparse", NULL))) { + gst_object_unref (GST_OBJECT (source)); + return NULL; + } name = g_strdup_printf ("subbin"); - subbin = gst_thread_new (name); + subbin = gst_bin_new (name); g_free (name); gst_bin_add_many (GST_BIN (subbin), source, subparse, NULL); @@ -999,12 +1069,9 @@ setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri) */ static GstElement * -gen_source_element (GstPlayBaseBin * play_base_bin, - GstElement ** subbin, gboolean * _is_stream) +gen_source_element (GstPlayBaseBin * play_base_bin, GstElement ** subbin) { - GstElement *source, *queue, *bin; - GstProbe *probe; - gboolean is_stream; + GstElement *source; /* stip subtitle from uri */ if (!play_base_bin->uri) @@ -1023,34 +1090,12 @@ gen_source_element (GstPlayBaseBin * play_base_bin, return NULL; /* lame - FIXME, maybe we can use seek_types/mask here? */ - *_is_stream = is_stream = !strncmp (play_base_bin->uri, "http://", 7) || - !strncmp (play_base_bin->uri, "mms://", 6); - if (!is_stream) - return source; + play_base_bin->is_stream = !strncmp (play_base_bin->uri, "http://", 7) || + !strncmp (play_base_bin->uri, "mms://", 6) || + !strncmp (play_base_bin->uri, "rtp://", 6) || + !strncmp (play_base_bin->uri, "rtsp://", 7); - /* buffer */ - bin = gst_thread_new ("sourcebin"); - queue = gst_element_factory_make ("queue", "buffer"); - g_object_set (queue, "max-size-bytes", 512 * 1024, - "max-size-buffers", 0, NULL); - /* I'd like it to be leaky too, but only for live sources. How? */ - g_signal_connect (queue, "underrun", G_CALLBACK (buffer_underrun), - play_base_bin); - g_signal_connect (queue, "running", G_CALLBACK (buffer_running), - play_base_bin); - g_signal_connect (queue, "overrun", G_CALLBACK (buffer_overrun), - play_base_bin); - - /* give updates on queue size */ - probe = gst_probe_new (FALSE, check_queue, queue); - gst_pad_add_probe (gst_element_get_pad (source, "src"), probe); - g_object_set_data (G_OBJECT (queue), "pbb", play_base_bin); - - gst_element_link (source, queue); - gst_bin_add_many (GST_BIN (bin), source, queue, NULL); - gst_element_add_ghost_pad (bin, gst_element_get_pad (queue, "src"), "src"); - - return bin; + return source; } /* Setup the substreams (to be called right after group_commit ()) */ @@ -1097,18 +1142,29 @@ setup_substreams (GstPlayBaseBin * play_base_bin) } } +/* + * Called when we're redirected to a new URI. + */ +static void +got_redirect (GstElement * element, const gchar * new_location, gpointer data) +{ + gchar **location = data; + + if (!*location) + *location = g_strdup (new_location); +} + /* 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, - gboolean * _stream, GError ** error) + gchar ** new_location, GError ** error) { GstElement *old_src; GstElement *old_dec; GstPad *srcpad = NULL; GstElement *subbin; - gboolean stream; if (!play_base_bin->need_rebuild) return TRUE; @@ -1119,8 +1175,7 @@ setup_source (GstPlayBaseBin * play_base_bin, old_src = play_base_bin->source; /* create and configure an element that can handle the uri */ - play_base_bin->source = gen_source_element (play_base_bin, &subbin, &stream); - *_stream = stream; + play_base_bin->source = gen_source_element (play_base_bin, &subbin); if (!play_base_bin->source) { /* whoops, could not create the source element */ @@ -1137,11 +1192,14 @@ setup_source (GstPlayBaseBin * play_base_bin, } gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->source); g_object_notify (G_OBJECT (play_base_bin), "source"); + /* make sure the new element has the same state as the parent */ +#if 0 if (gst_bin_sync_children_state (GST_BIN (play_base_bin->thread)) == GST_STATE_FAILURE) { return FALSE; } +#endif } /* remove the old decoder now, if any */ @@ -1180,7 +1238,7 @@ setup_source (GstPlayBaseBin * play_base_bin, /* assume we are going to have no output streams */ gboolean no_out = TRUE; - for (pads = gst_element_get_pad_list (play_base_bin->source); + for (pads = GST_ELEMENT (play_base_bin->source)->pads; pads; pads = g_list_next (pads)) { GstPad *pad = GST_PAD (pads->data); GstStructure *structure; @@ -1198,7 +1256,7 @@ setup_source (GstPlayBaseBin * play_base_bin, if (caps == NULL || gst_caps_is_empty (caps) || gst_caps_get_size (caps) == 0) { if (caps != NULL) - gst_caps_free (caps); + gst_caps_unref (caps); continue; } @@ -1212,7 +1270,7 @@ setup_source (GstPlayBaseBin * play_base_bin, is_raw = TRUE; } - gst_caps_free (caps); + gst_caps_unref (caps); } if (is_raw) { no_more_pads (play_base_bin->source, play_base_bin); @@ -1246,6 +1304,8 @@ setup_source (GstPlayBaseBin * play_base_bin, gst_object_unref (GST_OBJECT (old_dec)); } + g_signal_connect (play_base_bin->decoder, "got_redirect", + G_CALLBACK (got_redirect), new_location); res = gst_pad_link (srcpad, gst_element_get_pad (play_base_bin->decoder, "sink")); if (!res) { @@ -1259,7 +1319,7 @@ setup_source (GstPlayBaseBin * play_base_bin, sig3 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "no-more-pads", G_CALLBACK (no_more_pads), play_base_bin); - if (!stream) { + if (!play_base_bin->is_stream) { sig4 = g_signal_connect (G_OBJECT (play_base_bin->decoder), "unknown-type", G_CALLBACK (unknown_type), play_base_bin); sig5 = g_signal_connect (G_OBJECT (play_base_bin->thread), "error", @@ -1270,7 +1330,7 @@ setup_source (GstPlayBaseBin * play_base_bin, * the signal handlers then */ g_mutex_lock (play_base_bin->group_lock); - if (gst_element_set_state (play_base_bin->thread, GST_STATE_PLAYING) == + if (gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED) == GST_STATE_SUCCESS) { GST_DEBUG ("waiting for first group..."); sig6 = g_signal_connect (G_OBJECT (play_base_bin->thread), @@ -1300,7 +1360,7 @@ setup_source (GstPlayBaseBin * play_base_bin, /* make subs iterate from now on */ gst_bin_add (GST_BIN (play_base_bin->thread), play_base_bin->subtitle); } - if (!stream) { + if (!play_base_bin->is_stream) { setup_substreams (play_base_bin); } } @@ -1550,7 +1610,7 @@ play_base_eos (GstBin * bin, GstPlayBaseBin * play_base_bin) GST_LOG ("forwarding EOS"); - gst_element_set_eos (GST_ELEMENT (play_base_bin)); + //gst_element_set_eos (GST_ELEMENT (play_base_bin)); } static GstElementStateReturn @@ -1564,32 +1624,20 @@ gst_play_base_bin_change_state (GstElement * element) switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: { - GstScheduler *sched; + play_base_bin->thread = gst_bin_new ("internal_thread"); - 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_element_set_state (play_base_bin->thread, GST_STATE_READY); - gst_element_set_state (play_base_bin->thread, GST_STATE_READY); - - g_signal_connect (play_base_bin->thread, "found_tag", - G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin); - } 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; - } + g_signal_connect (play_base_bin->thread, "found_tag", + G_CALLBACK (gst_play_base_bin_found_tag), play_base_bin); break; } case GST_STATE_READY_TO_PAUSED: { GError *error = NULL; - gboolean stream; + gchar *new_location = NULL; - if (!setup_source (play_base_bin, &stream, &error) || error != NULL) { + if (!setup_source (play_base_bin, &new_location, &error) || error != NULL) { if (!error) { /* opening failed but no error - hellup */ GST_ELEMENT_ERROR (GST_ELEMENT (play_base_bin), STREAM, @@ -1602,7 +1650,12 @@ gst_play_base_bin_change_state (GstElement * element) g_error_free (error); } ret = GST_STATE_FAILURE; - } else if (stream) { + } else if (new_location) { + g_signal_emit (play_base_bin, gst_play_base_bin_signals[REDIRECT], + 0, new_location); + g_free (new_location); + ret = GST_STATE_FAILURE; + } else if (play_base_bin->is_stream) { ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); } else { const GList *item; @@ -1652,6 +1705,7 @@ gst_play_base_bin_change_state (GstElement * element) ("File \"%s\" is not a media file", play_base_bin->uri), (NULL)); } + gst_element_set_state (play_base_bin->thread, GST_STATE_READY); ret = GST_STATE_FAILURE; } else { ret = gst_element_set_state (play_base_bin->thread, GST_STATE_PAUSED); @@ -1666,7 +1720,7 @@ gst_play_base_bin_change_state (GstElement * element) G_CALLBACK (gst_play_base_bin_error), play_base_bin); g_signal_connect (G_OBJECT (play_base_bin->thread), "eos", G_CALLBACK (play_base_eos), play_base_bin); - if (!stream) { + if (!play_base_bin->is_stream) { GST_DEBUG ("emit signal"); g_signal_emit (play_base_bin, gst_play_base_bin_signals[SETUP_OUTPUT_PADS_SIGNAL], 0); @@ -1713,7 +1767,7 @@ gst_play_base_bin_change_state (GstElement * element) /* virtual function to add elements to this bin. The idea is to * wrap the element in a thread automatically. */ -static void +static gboolean gst_play_base_bin_add_element (GstBin * bin, GstElement * element) { GstPlayBaseBin *play_base_bin; @@ -1721,15 +1775,12 @@ gst_play_base_bin_add_element (GstBin * bin, GstElement * element) 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); + thread = gst_bin_new (name); g_free (name); gst_bin_add (GST_BIN (thread), element); @@ -1739,21 +1790,19 @@ gst_play_base_bin_add_element (GstBin * bin, GstElement * element) if (GST_STATE (play_base_bin) > GST_STATE_READY) { gst_element_set_state (element, GST_STATE (play_base_bin)); } - - /* hack, the clock is not correctly distributed in the core */ - sched = gst_element_get_scheduler (GST_ELEMENT (play_base_bin->thread)); - clock = gst_scheduler_get_clock (sched); - gst_scheduler_set_clock (sched, clock); } else { g_warning ("adding elements is not allowed in NULL"); + return FALSE; } + + return TRUE; } /* virtual function to remove an element from this bin. We have to make * sure that we also remove the thread that we used as a container for * this element. */ -static void +static gboolean gst_play_base_bin_remove_element (GstBin * bin, GstElement * element) { GstPlayBaseBin *play_base_bin; @@ -1786,7 +1835,9 @@ gst_play_base_bin_remove_element (GstBin * bin, GstElement * element) } } else { g_warning ("removing elements is not allowed in NULL"); + return FALSE; } + return TRUE; } static void diff --git a/gst/playback/gstplaybasebin.h b/gst/playback/gstplaybasebin.h index e30db72108..538aa66d8a 100644 --- a/gst/playback/gstplaybasebin.h +++ b/gst/playback/gstplaybasebin.h @@ -68,12 +68,14 @@ struct _GstPlayBaseBin { /* properties */ gboolean threaded; guint64 queue_size; + guint queue_threshold; gint current[NUM_TYPES]; /* internal thread */ GstElement *thread; gchar *uri, *suburi; + gboolean is_stream; GstElement *source; GstElement *decoder; GstElement *subtitle; /* additional filesrc ! subparse bin */ @@ -103,6 +105,10 @@ struct _GstPlayBaseBinClass { gint percentage); void (*group_switch) (GstPlayBaseBin *play_base_bin); + /* Called on redirect */ + void (*got_redirect) (GstPlayBaseBin *play_base_bin, + const gchar *new_location); + /* action signals */ gboolean (*link_stream) (GstPlayBaseBin *play_base_bin, GstStreamInfo *info, diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c index 769abf6201..abed3807e7 100644 --- a/gst/playback/gstplaybin.c +++ b/gst/playback/gstplaybin.c @@ -49,6 +49,7 @@ struct _GstPlayBin GstElement *video_sink; GstElement *visualisation; GstElement *volume_element; + GstElement *textoverlay_element; gfloat volume; /* these are the currently active sinks */ @@ -65,6 +66,9 @@ struct _GstPlayBin /* boolean to see if we're currently switching groups */ gboolean group_switch; + + /* font description */ + gchar *font_desc; }; struct _GstPlayBinClass @@ -80,7 +84,8 @@ enum ARG_VIDEO_SINK, ARG_VIS_PLUGIN, ARG_VOLUME, - ARG_FRAME + ARG_FRAME, + ARG_FONT_DESC }; /* signals */ @@ -182,13 +187,18 @@ gst_play_bin_class_init (GstPlayBinClass * klass) g_param_spec_object ("vis-plugin", "Vis plugin", "the visualization element to use (NULL = none)", GST_TYPE_ELEMENT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (gobject_klass), ARG_VOLUME, + 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_object_class_install_property (G_OBJECT_CLASS (gobject_klass), ARG_FRAME, + 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_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)); gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose); @@ -216,16 +226,15 @@ gst_play_bin_init (GstPlayBin * play_bin) play_bin->audio_sink = NULL; play_bin->visualisation = NULL; play_bin->volume_element = NULL; + play_bin->textoverlay_element = NULL; play_bin->volume = 1.0; play_bin->seekables = NULL; 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); play_bin->group_switch = FALSE; - - /* no iterate is needed */ - GST_FLAG_SET (play_bin, GST_BIN_SELF_SCHEDULABLE); } static void @@ -253,7 +262,8 @@ gst_play_bin_dispose (GObject * object) gst_object_unref (GST_OBJECT (play_bin->visualisation)); play_bin->visualisation = NULL; } - + g_free (play_bin->font_desc); + play_bin->font_desc = NULL; if (G_OBJECT_CLASS (parent_class)->dispose) { G_OBJECT_CLASS (parent_class)->dispose (object); @@ -314,6 +324,14 @@ gst_play_bin_set_property (GObject * object, guint prop_id, 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; @@ -406,7 +424,7 @@ gen_video_element (GstPlayBin * play_bin) if (play_bin->video_sink) { sink = play_bin->video_sink; } else { - sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink"); + sink = gst_element_factory_make ("autovideosink", "sink"); } gst_object_ref (GST_OBJECT (sink)); g_hash_table_insert (play_bin->cache, "video_sink", sink); @@ -455,6 +473,11 @@ gen_text_element (GstPlayBin * play_bin) overlay = gst_element_factory_make ("textoverlay", "overlay"); g_object_set (G_OBJECT (overlay), "halign", "center", "valign", "bottom", NULL); + play_bin->textoverlay_element = overlay; + if (play_bin->font_desc) { + g_object_set (G_OBJECT (play_bin->textoverlay_element), + "font-desc", play_bin->font_desc, NULL); + } vbin = gen_video_element (play_bin); if (!overlay) { g_warning ("No overlay (pango) element, subtitles disabled"); @@ -511,7 +534,7 @@ gen_audio_element (GstPlayBin * play_bin) if (play_bin->audio_sink) { sink = play_bin->audio_sink; } else { - sink = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink"); + sink = gst_element_factory_make ("autoaudiosink", "sink"); play_bin->audio_sink = GST_ELEMENT (gst_object_ref (GST_OBJECT (sink))); } @@ -674,6 +697,9 @@ remove_sinks (GstPlayBin * play_bin) gst_buffer_unref (play_bin->frame); play_bin->frame = NULL; } + + play_bin->textoverlay_element = NULL; + play_bin->volume_element = NULL; } /* loop over the streams and set up the pipeline to play this @@ -855,7 +881,7 @@ gst_play_bin_send_event (GstElement * element, GstEvent * event) play_bin = GST_PLAY_BIN (element); - state = gst_element_get_state (element); + gst_element_get_state (element, &state, NULL, NULL); /* we pause the pipeline first before sending the event. We only * do this if the pipeline was playing. */ if (state == GST_STATE_PLAYING) { diff --git a/gst/playback/gststreaminfo.c b/gst/playback/gststreaminfo.c index 160738cf7c..44ab638517 100644 --- a/gst/playback/gststreaminfo.c +++ b/gst/playback/gststreaminfo.c @@ -206,7 +206,7 @@ gst_stream_info_dispose (GObject * object) g_free (stream_info->decoder); stream_info->decoder = NULL; if (stream_info->caps) { - gst_caps_free (stream_info->caps); + gst_caps_unref (stream_info->caps); stream_info->caps = NULL; } @@ -225,7 +225,7 @@ stream_info_change_state (GstElement * element, /* 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); + //gst_pad_set_active_recursive (GST_PAD (stream_info->object), FALSE); } } @@ -241,8 +241,8 @@ gst_stream_info_set_mute (GstStreamInfo * stream_info, gboolean mute) if (mute != stream_info->mute) { stream_info->mute = mute; - gst_pad_set_active_recursive ((GstPad *) - GST_PAD_REALIZE (stream_info->object), !mute); + //gst_pad_set_active_recursive ((GstPad *) + // GST_PAD_REALIZE (stream_info->object), !mute); if (mute) { g_signal_connect (gst_pad_get_parent ((GstPad *) diff --git a/gst/playback/gststreamselector.c b/gst/playback/gststreamselector.c index d4f9a26bb7..153188a080 100644 --- a/gst/playback/gststreamselector.c +++ b/gst/playback/gststreamselector.c @@ -58,12 +58,13 @@ static void gst_stream_selector_base_init (GstStreamSelectorClass * klass); static void gst_stream_selector_class_init (GstStreamSelectorClass * klass); static GstCaps *gst_stream_selector_get_caps (GstPad * pad); -static GstPadLinkReturn gst_stream_selector_link (GstPad * pad, - const GstCaps * caps); +static gboolean gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps); static GList *gst_stream_selector_get_linked_pads (GstPad * pad); static GstPad *gst_stream_selector_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * unused); -static void gst_stream_selector_chain (GstPad * pad, GstData * data); +static gboolean gst_stream_selector_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_stream_selector_chain (GstPad * pad, + GstBuffer * buffer); static GstElementClass *parent_class = NULL; @@ -134,8 +135,8 @@ gst_stream_selector_init (GstStreamSelector * sel) sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); gst_pad_set_internal_link_function (sel->srcpad, GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); - gst_pad_set_link_function (sel->srcpad, - GST_DEBUG_FUNCPTR (gst_stream_selector_link)); + gst_pad_set_setcaps_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_setcaps)); gst_pad_set_getcaps_function (sel->srcpad, GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); @@ -173,7 +174,6 @@ gst_stream_selector_get_linked_pad (GstPad * pad) static GstCaps * gst_stream_selector_get_caps (GstPad * pad) { - GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad)); GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); if (!otherpad) { @@ -181,19 +181,17 @@ gst_stream_selector_get_caps (GstPad * pad) "Pad %s not linked, returning ANY", gst_pad_get_name (pad)); return gst_caps_new_any (); - } else if (otherpad == sel->last_active_sinkpad && sel->in_chain) { - return gst_caps_copy (GST_PAD_CAPS (sel->last_active_sinkpad)); } GST_DEBUG_OBJECT (gst_pad_get_parent (pad), - "Pad %s is linked (to %s), returning allowed-caps", + "Pad %s is linked (to %s), returning peer-caps", gst_pad_get_name (pad), gst_pad_get_name (otherpad)); - return gst_pad_get_allowed_caps (otherpad); + return gst_pad_peer_get_caps (otherpad); } -static GstPadLinkReturn -gst_stream_selector_link (GstPad * pad, const GstCaps * caps) +static gboolean +gst_stream_selector_setcaps (GstPad * pad, GstCaps * caps) { GstPad *otherpad = gst_stream_selector_get_linked_pad (pad); @@ -202,14 +200,16 @@ gst_stream_selector_link (GstPad * pad, const GstCaps * caps) "Pad %s not linked, returning %s", gst_pad_get_name (pad), GST_PAD_IS_SINK (pad) ? "ok" : "delayed"); - return GST_PAD_IS_SINK (pad) ? GST_PAD_LINK_OK : GST_PAD_LINK_DELAYED; + return FALSE; } GST_DEBUG_OBJECT (gst_pad_get_parent (pad), "Pad %s is linked (to %s), returning other-trysetcaps", gst_pad_get_name (pad), gst_pad_get_name (otherpad)); - return gst_pad_try_set_caps (otherpad, caps); + gst_pad_set_caps (otherpad, caps); + + return TRUE; } static GList * @@ -241,12 +241,12 @@ gst_stream_selector_request_new_pad (GstElement * element, sel->last_active_sinkpad = sinkpad; g_free (name); - gst_pad_set_link_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_stream_selector_link)); gst_pad_set_getcaps_function (sinkpad, GST_DEBUG_FUNCPTR (gst_stream_selector_get_caps)); gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_stream_selector_chain)); + gst_pad_set_event_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_stream_selector_event)); gst_pad_set_internal_link_function (sinkpad, GST_DEBUG_FUNCPTR (gst_stream_selector_get_linked_pads)); gst_element_add_pad (GST_ELEMENT (sel), sinkpad); @@ -254,33 +254,36 @@ gst_stream_selector_request_new_pad (GstElement * element, return sinkpad; } -static void -gst_stream_selector_chain (GstPad * pad, GstData * data) +static gboolean +gst_stream_selector_event (GstPad * pad, GstEvent * event) { - GstStreamSelector *sel = GST_STREAM_SELECTOR (gst_pad_get_parent (pad)); + GstStreamSelector *sel = GST_STREAM_SELECTOR (GST_PAD_PARENT (pad)); + + /* forward */ + GST_DEBUG_OBJECT (sel, "Forwarding event %p from pad %s", + event, GST_OBJECT_NAME (pad)); + + return gst_pad_push_event (sel->srcpad, event); +} + +static GstFlowReturn +gst_stream_selector_chain (GstPad * pad, GstBuffer * buffer) +{ + GstStreamSelector *sel = GST_STREAM_SELECTOR (GST_PAD_PARENT (pad)); /* first, check if the active pad changed. If so, redo * negotiation and fail if that fails. */ if (pad != sel->last_active_sinkpad) { - GstPadLinkReturn ret; - GST_LOG_OBJECT (sel, "stream change detected, switching from %s to %s", sel->last_active_sinkpad ? gst_pad_get_name (sel->last_active_sinkpad) : "none", gst_pad_get_name (pad)); sel->last_active_sinkpad = pad; - sel->in_chain = TRUE; - ret = gst_pad_renegotiate (sel->srcpad); - sel->in_chain = FALSE; - if (GST_PAD_LINK_FAILED (ret)) { - GST_ELEMENT_ERROR (sel, CORE, NEGOTIATION, (NULL), (NULL)); - sel->last_active_sinkpad = NULL; - return; - } } /* forward */ - GST_DEBUG_OBJECT (sel, "Forwarding %s %p from pad %s", - GST_IS_EVENT (data) ? "event" : "buffer", data, gst_pad_get_name (pad)); - gst_pad_push (sel->srcpad, data); + GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s", + buffer, GST_OBJECT_NAME (pad)); + + return gst_pad_push (sel->srcpad, buffer); } diff --git a/gst/playback/test.c b/gst/playback/test.c index 61dbaa4e81..6b36781604 100644 --- a/gst/playback/test.c +++ b/gst/playback/test.c @@ -28,7 +28,7 @@ gen_video_element () GstElement *conv; GstElement *sink; - element = gst_thread_new ("vbin"); + element = gst_bin_new ("vbin"); conv = gst_element_factory_make ("ffmpegcolorspace", "conv"); sink = gst_element_factory_make (DEFAULT_VIDEOSINK, "sink"); @@ -49,7 +49,7 @@ gen_audio_element () GstElement *conv; GstElement *sink; - element = gst_thread_new ("abin"); + element = gst_bin_new ("abin"); conv = gst_element_factory_make ("audioconvert", "conv"); sink = gst_element_factory_make (DEFAULT_AUDIOSINK, "sink"); @@ -130,8 +130,7 @@ main (gint argc, gchar * argv[]) g_print ("could not play\n"); return -1; } - - gst_main (); + //gst_main (); return 0; } diff --git a/sys/xvimage/xvimagesink.c b/sys/xvimage/xvimagesink.c index cc942e9f03..5bc66371ed 100644 --- a/sys/xvimage/xvimagesink.c +++ b/sys/xvimage/xvimagesink.c @@ -1183,8 +1183,6 @@ gst_xvimagesink_getcaps (GstBaseSink * bsink) xvimagesink = GST_XVIMAGESINK (bsink); - g_print ("getcaps\n"); - if (xvimagesink->xcontext) return gst_caps_ref (xvimagesink->xcontext->caps);