diff --git a/ChangeLog b/ChangeLog index ec71dfa635..65a62f3810 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-09-25 Wim Taymans + + * gst/playback/gstplaybin.c: (gst_play_bin_class_init), + (gst_play_bin_vis_blocked), (gst_play_bin_set_property), + (gen_video_element), (gen_text_element), (gen_audio_element), + (gen_vis_element), (remove_sinks), (add_sink), (setup_sinks), + (gst_play_bin_set_clock_func), (gst_play_bin_change_state): + Detect NO_PREROLL state change returns and disable clock distribution to + the sinks so that sync is disabled. + Avoid some type checking and do simple casts instead. + Small cleanups, fix some FIXMEs. + Be more robust when linking user specified elements, catch an report + errors. Fixes #357404. + Fix some leaks in the error paths. + 2006-09-25 Stefan Kost * ChangeLog: diff --git a/gst/playback/gstplaybin.c b/gst/playback/gstplaybin.c index d7448e8512..d03a4e4ee4 100644 --- a/gst/playback/gstplaybin.c +++ b/gst/playback/gstplaybin.c @@ -285,6 +285,9 @@ struct _GstPlayBin /* connection speed in bits/sec (0 = unknown) */ guint connection_speed; + + /* indication if the pipeline is live */ + gboolean is_live; }; struct _GstPlayBinClass @@ -326,8 +329,11 @@ static void gst_play_bin_get_property (GObject * object, guint prop_id, static gboolean gst_play_bin_send_event (GstElement * element, GstEvent * event); +static gboolean gst_play_bin_set_clock_func (GstElement * element, + GstClock * clock); static GstStateChangeReturn gst_play_bin_change_state (GstElement * element, GstStateChange transition); + static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message); static GstElementClass *parent_class; @@ -427,6 +433,7 @@ gst_play_bin_class_init (GstPlayBinClass * klass) gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_play_bin_change_state); gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event); + gstelement_klass->set_clock = GST_DEBUG_FUNCPTR (gst_play_bin_set_clock_func); gstbin_klass->handle_message = GST_DEBUG_FUNCPTR (gst_play_bin_handle_message); @@ -518,7 +525,8 @@ gst_play_bin_vis_blocked (GstPad * tee_pad, gboolean blocked, } vis_bin = - GST_BIN (gst_object_get_parent (GST_OBJECT (play_bin->visualisation))); + GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (play_bin-> + visualisation))); if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) { goto beach; @@ -620,7 +628,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id, play_bin->video_sink = g_value_get_object (value); if (play_bin->video_sink != NULL) { gst_object_ref (play_bin->video_sink); - gst_object_sink (GST_OBJECT (play_bin->video_sink)); + gst_object_sink (GST_OBJECT_CAST (play_bin->video_sink)); } /* when changing the videosink, we just remove the * video pipeline from the cache so that it will be @@ -634,7 +642,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id, play_bin->audio_sink = g_value_get_object (value); if (play_bin->audio_sink != NULL) { gst_object_ref (play_bin->audio_sink); - gst_object_sink (GST_OBJECT (play_bin->audio_sink)); + gst_object_sink (GST_OBJECT_CAST (play_bin->audio_sink)); } g_hash_table_remove (play_bin->cache, "abin"); break; @@ -647,7 +655,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id, /* Take ownership */ if (play_bin->pending_visualisation) { gst_object_ref (play_bin->pending_visualisation); - gst_object_sink (GST_OBJECT (play_bin->pending_visualisation)); + gst_object_sink (GST_OBJECT_CAST (play_bin->pending_visualisation)); } } else { play_bin->pending_visualisation = g_value_get_object (value); @@ -655,7 +663,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id, /* Take ownership */ if (play_bin->pending_visualisation) { gst_object_ref (play_bin->pending_visualisation); - gst_object_sink (GST_OBJECT (play_bin->pending_visualisation)); + gst_object_sink (GST_OBJECT_CAST (play_bin->pending_visualisation)); } /* Was there a visualisation already set ? */ @@ -663,7 +671,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id, GstBin *vis_bin = NULL; vis_bin = - GST_BIN (gst_object_get_parent (GST_OBJECT (play_bin-> + GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (play_bin-> visualisation))); /* Check if the visualisation is already in a bin */ @@ -798,8 +806,6 @@ handoff (GstElement * identity, GstBuffer * frame, gpointer data) * +----------|--------------------------------------------------+ * handoff */ -/* FIXME: this might return NULL if no videosink was found, handle - * this in callers */ static GstElement * gen_video_element (GstPlayBin * play_bin) { @@ -824,36 +830,38 @@ gen_video_element (GstPlayBin * play_bin) if (sink == NULL) { sink = gst_element_factory_make ("xvimagesink", "videosink"); } - /* FIXME: this warrants adding a CORE error category for missing - * elements/plugins */ - if (sink == NULL) { - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Both autovideosink and xvimagesink elements are missing.")), - (NULL)); - return NULL; - } + if (sink == NULL) + goto no_sinks; } gst_object_ref (sink); g_hash_table_insert (play_bin->cache, "video_sink", sink); - + /* create a bin to hold objects, as we create them we add them to this bin so + * that when something goes wrong we only need to unref the bin */ element = gst_bin_new ("vbin"); - identity = gst_element_factory_make ("identity", "id"); - g_object_set (identity, "silent", TRUE, NULL); - g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin); - gst_bin_add (GST_BIN (element), identity); + gst_bin_add (GST_BIN_CAST (element), sink); + conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); if (conv == NULL) goto no_colorspace; + gst_bin_add (GST_BIN_CAST (element), conv); + scale = gst_element_factory_make ("videoscale", "vscale"); if (scale == NULL) goto no_videoscale; - gst_bin_add (GST_BIN (element), conv); - gst_bin_add (GST_BIN (element), scale); - gst_bin_add (GST_BIN (element), sink); + gst_bin_add (GST_BIN_CAST (element), scale); + + identity = gst_element_factory_make ("identity", "id"); + g_object_set (identity, "silent", TRUE, NULL); + g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin); + gst_bin_add (GST_BIN_CAST (element), identity); + gst_element_link_pads (identity, "src", conv, "sink"); gst_element_link_pads (conv, "src", scale, "sink"); - gst_element_link_pads (scale, "src", sink, "sink"); + /* be more careful with the pad from the custom sink element, it might not + * be named 'sink' */ + if (!gst_element_link_pads (scale, "src", sink, NULL)) + goto link_failed; pad = gst_element_get_pad (identity, "sink"); gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); @@ -868,6 +876,16 @@ gen_video_element (GstPlayBin * play_bin) return element; + /* ERRORS */ +no_sinks: + { + /* FIXME: this warrants adding a CORE error category for missing + * elements/plugins */ + GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, + (_("Both autovideosink and xvimagesink elements are missing.")), + (NULL)); + return NULL; + } no_colorspace: { GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, @@ -885,6 +903,13 @@ no_videoscale: gst_object_unref (element); return NULL; } +link_failed: + { + GST_ELEMENT_ERROR (play_bin, CORE, PAD, + (NULL), ("Failed to configure the video sink.")); + gst_object_unref (element); + return NULL; + } } /* make an element for playback of video with subtitles embedded. @@ -897,28 +922,30 @@ no_videoscale: * | +-----+ | +-------------+ +------+ | * text_sink-------------+ | * +--------------------------------------------------+ + * + * If there is no subtitle renderer this function will simply return the + * videosink without the text_sink pad. */ - static GstElement * gen_text_element (GstPlayBin * play_bin) { GstElement *element, *csp, *overlay, *vbin; GstPad *pad; - /* Create our bin */ - element = gst_bin_new ("textbin"); + /* Create the video rendering bin, error is posted when this fails. */ + vbin = gen_video_element (play_bin); + if (!vbin) + return NULL; /* Text overlay */ overlay = gst_element_factory_make ("textoverlay", "overlay"); - /* Create the video rendering bin */ - vbin = gen_video_element (play_bin); + /* If no overlay return the video bin without subtitle support. */ + if (!overlay) + goto no_overlay; - /* If no overlay return the video bin */ - if (!overlay) { - GST_WARNING ("No overlay (pango) element, subtitles disabled"); - return vbin; - } + /* Create our bin */ + element = gst_bin_new ("textbin"); /* Set some parameters */ g_object_set (G_OBJECT (overlay), @@ -928,13 +955,13 @@ gen_text_element (GstPlayBin * play_bin) } /* Take a ref */ - play_bin->textoverlay_element = GST_ELEMENT (gst_object_ref (overlay)); + play_bin->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay)); /* we know this will succeed, as the video bin already created one before */ csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp"); /* Add our elements */ - gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL); + gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL); /* Link */ gst_element_link_pads (csp, "src", overlay, "video_sink"); @@ -953,6 +980,14 @@ gen_text_element (GstPlayBin * play_bin) gst_element_set_state (element, GST_STATE_READY); return element; + + /* ERRORS */ +no_overlay: + { + GST_WARNING_OBJECT (play_bin, + "No overlay (pango) element, subtitles disabled"); + return vbin; + } } /* make the element (bin) that contains the elements needed to perform @@ -966,11 +1001,11 @@ gen_text_element (GstPlayBin * play_bin) * | | +---------+ +----------+ +---------+ +---------+ | * sink-+ | * +-------------------------------------------------------------+ - * */ static GstElement * gen_audio_element (GstPlayBin * play_bin) { + gboolean res; GstElement *element; GstElement *conv; GstElement *scale; @@ -979,21 +1014,8 @@ gen_audio_element (GstPlayBin * play_bin) GstPad *pad; element = g_hash_table_lookup (play_bin->cache, "abin"); - if (element != NULL) { + if (element != NULL) return element; - } - element = gst_bin_new ("abin"); - conv = gst_element_factory_make ("audioconvert", "aconv"); - if (conv == NULL) - goto no_audioconvert; - - scale = gst_element_factory_make ("audioresample", "aresample"); - if (scale == NULL) - goto no_audioresample; - - volume = gst_element_factory_make ("volume", "volume"); - g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL); - play_bin->volume_element = volume; if (play_bin->audio_sink) { sink = play_bin->audio_sink; @@ -1002,25 +1024,38 @@ gen_audio_element (GstPlayBin * play_bin) if (sink == NULL) { sink = gst_element_factory_make ("alsasink", "audiosink"); } - if (sink == NULL) { - GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, - (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); - return NULL; - } - play_bin->audio_sink = GST_ELEMENT (gst_object_ref (sink)); + if (sink == NULL) + goto no_sinks; + + play_bin->audio_sink = GST_ELEMENT_CAST (gst_object_ref (sink)); } gst_object_ref (sink); g_hash_table_insert (play_bin->cache, "audio_sink", sink); - gst_bin_add (GST_BIN (element), conv); - gst_bin_add (GST_BIN (element), scale); - gst_bin_add (GST_BIN (element), volume); - gst_bin_add (GST_BIN (element), sink); + element = gst_bin_new ("abin"); + gst_bin_add (GST_BIN_CAST (element), sink); - gst_element_link_pads (conv, "src", scale, "sink"); - gst_element_link_pads (scale, "src", volume, "sink"); - gst_element_link_pads (volume, "src", sink, "sink"); + conv = gst_element_factory_make ("audioconvert", "aconv"); + if (conv == NULL) + goto no_audioconvert; + gst_bin_add (GST_BIN_CAST (element), conv); + + scale = gst_element_factory_make ("audioresample", "aresample"); + if (scale == NULL) + goto no_audioresample; + gst_bin_add (GST_BIN_CAST (element), scale); + + volume = gst_element_factory_make ("volume", "volume"); + g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL); + play_bin->volume_element = volume; + gst_bin_add (GST_BIN_CAST (element), volume); + + res = gst_element_link_pads (conv, "src", scale, "sink"); + res &= gst_element_link_pads (scale, "src", volume, "sink"); + res &= gst_element_link_pads (volume, "src", sink, NULL); + if (!res) + goto link_failed; pad = gst_element_get_pad (conv, "sink"); gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); @@ -1035,6 +1070,13 @@ gen_audio_element (GstPlayBin * play_bin) return element; + /* ERRORS */ +no_sinks: + { + GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, + (_("Both autoaudiosink and alsasink elements are missing.")), (NULL)); + return NULL; + } no_audioconvert: { GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, @@ -1052,6 +1094,13 @@ no_audioresample: gst_object_unref (element); return NULL; } +link_failed: + { + GST_ELEMENT_ERROR (play_bin, CORE, PAD, + (NULL), ("Failed to configure the audio sink.")); + gst_object_unref (element); + return NULL; + } } /* make the element (bin) that contains the elements needed to perform @@ -1080,6 +1129,7 @@ no_audioresample: static GstElement * gen_vis_element (GstPlayBin * play_bin) { + gboolean res; GstElement *element; GstElement *tee; GstElement *asink; @@ -1089,6 +1139,7 @@ gen_vis_element (GstPlayBin * play_bin) GstElement *vqueue, *aqueue; GstPad *pad, *rpad; + /* errors are already posted when these fail. */ asink = gen_audio_element (play_bin); if (!asink) return NULL; @@ -1104,29 +1155,32 @@ gen_vis_element (GstPlayBin * play_bin) vqueue = gst_element_factory_make ("queue", "vqueue"); aqueue = gst_element_factory_make ("queue", "aqueue"); - gst_bin_add (GST_BIN (element), asink); - gst_bin_add (GST_BIN (element), vqueue); - gst_bin_add (GST_BIN (element), aqueue); - gst_bin_add (GST_BIN (element), vsink); - gst_bin_add (GST_BIN (element), tee); + gst_bin_add (GST_BIN_CAST (element), asink); + gst_bin_add (GST_BIN_CAST (element), vqueue); + gst_bin_add (GST_BIN_CAST (element), aqueue); + gst_bin_add (GST_BIN_CAST (element), vsink); + gst_bin_add (GST_BIN_CAST (element), tee); conv = gst_element_factory_make ("audioconvert", "aconv"); if (conv == NULL) goto no_audioconvert; + gst_bin_add (GST_BIN_CAST (element), conv); if (play_bin->visualisation) { gst_object_ref (play_bin->visualisation); vis = play_bin->visualisation; } else { vis = gst_element_factory_make ("goom", "vis"); + if (!vis) + goto no_goom; } + gst_bin_add (GST_BIN_CAST (element), vis); - gst_bin_add (GST_BIN (element), conv); - gst_bin_add (GST_BIN (element), vis); - - gst_element_link_pads (vqueue, "src", conv, "sink"); - gst_element_link_pads (conv, "src", vis, "sink"); - gst_element_link_pads (vis, "src", vsink, "sink"); + res = gst_element_link_pads (vqueue, "src", conv, "sink"); + res &= gst_element_link_pads (conv, "src", vis, "sink"); + res &= gst_element_link_pads (vis, "src", vsink, "sink"); + if (!res) + goto link_failed; pad = gst_element_get_pad (aqueue, "sink"); rpad = gst_element_get_request_pad (tee, "src%d"); @@ -1147,6 +1201,7 @@ gen_vis_element (GstPlayBin * play_bin) return element; + /* ERRORS */ no_audioconvert: { GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, @@ -1155,6 +1210,21 @@ no_audioconvert: gst_object_unref (element); return NULL; } +no_goom: + { + GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "goom"), (NULL)); + gst_object_unref (element); + return NULL; + } +link_failed: + { + GST_ELEMENT_ERROR (play_bin, CORE, PAD, + (NULL), ("Failed to configure the visualisation element.")); + gst_object_unref (element); + return NULL; + } } /* get rid of all installed sinks */ @@ -1166,6 +1236,9 @@ remove_sinks (GstPlayBin * play_bin) GstElement *element; GstPad *pad, *peer; + if (play_bin->cache == NULL) + return; + GST_DEBUG ("removesinks"); element = g_hash_table_lookup (play_bin->cache, "abin"); if (element != NULL) { @@ -1176,7 +1249,7 @@ remove_sinks (GstPlayBin * play_bin) * is disposed */ play_bin->sinks = g_list_remove (play_bin->sinks, element); gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (parent), element); + gst_bin_remove (GST_BIN_CAST (parent), element); gst_object_unref (parent); } pad = gst_element_get_pad (element, "sink"); @@ -1195,7 +1268,7 @@ remove_sinks (GstPlayBin * play_bin) if (parent != NULL) { play_bin->sinks = g_list_remove (play_bin->sinks, element); gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (parent), element); + gst_bin_remove (GST_BIN_CAST (parent), element); gst_object_unref (parent); } pad = gst_element_get_pad (element, "sink"); @@ -1210,7 +1283,7 @@ remove_sinks (GstPlayBin * play_bin) } for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) { - GstElement *element = GST_ELEMENT (sinks->data); + GstElement *element = GST_ELEMENT_CAST (sinks->data); GstPad *pad; GstPad *peer; @@ -1226,16 +1299,21 @@ remove_sinks (GstPlayBin * play_bin) gst_object_unref (pad); gst_element_set_state (element, GST_STATE_NULL); - gst_bin_remove (GST_BIN (play_bin), element); + gst_bin_remove (GST_BIN_CAST (play_bin), element); } g_list_free (play_bin->sinks); play_bin->sinks = NULL; - /* FIXME: this is probably some refcounting problem */ - if (play_bin->visualisation && GST_OBJECT_PARENT (play_bin->visualisation)) { + if (play_bin->visualisation) { + GstElement *vis_bin; + + vis_bin = + GST_ELEMENT_CAST (gst_element_get_parent (play_bin->visualisation)); + gst_element_set_state (play_bin->visualisation, GST_STATE_NULL); - gst_bin_remove (GST_BIN (GST_OBJECT_PARENT (play_bin->visualisation)), - play_bin->visualisation); + gst_bin_remove (GST_BIN_CAST (vis_bin), play_bin->visualisation); + + gst_object_unref (vis_bin); } if (play_bin->frame) { @@ -1257,6 +1335,8 @@ remove_sinks (GstPlayBin * play_bin) * Also make sure to only connect the first audio and video pad. FIXME * this should eventually be handled with a tuner interface so that * one can switch the streams. + * + * This function takes ownership of @sink.* */ static gboolean add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, @@ -1266,8 +1346,17 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, GstPadLinkReturn linkres; GstElement *parent; GstStateChangeReturn stateret; + GstState state; g_return_val_if_fail (sink != NULL, FALSE); + + /* For live pipelines we need to add the bin in the same state as the + * parent so that it starts as soon as it is prerolled. */ + if (play_bin->is_live) + state = GST_STATE_PLAYING; + else + state = GST_STATE_PAUSED; + /* this is only for debugging */ parent = gst_pad_get_parent_element (srcpad); if (parent) { @@ -1275,15 +1364,19 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent)); gst_object_unref (parent); } + gst_bin_add (GST_BIN_CAST (play_bin), sink); + + /* for live pipelines, disable the sync in the sinks until core handles this + * correctly. */ + if (play_bin->is_live) + gst_element_set_clock (sink, NULL); /* bring it to the PAUSED state so we can link to the peer without * breaking the flow */ - if ((stateret = gst_element_set_state (sink, GST_STATE_PAUSED)) == - GST_STATE_CHANGE_FAILURE) + stateret = gst_element_set_state (sink, state); + if (stateret == GST_STATE_CHANGE_FAILURE) goto state_failed; - gst_bin_add (GST_BIN (play_bin), sink); - /* we found a sink for this stream, now try to install it */ sinkpad = gst_element_get_pad (sink, "sink"); linkres = gst_pad_link (srcpad, sinkpad); @@ -1299,11 +1392,12 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, gst_object_unref (sinkpad); } - /* try to link the subtitle pad of the sink to the stream */ - if (GST_PAD_LINK_FAILED (linkres)) { + /* try to link the subtitle pad of the sink to the stream, this is not + * fatal. */ + if (GST_PAD_LINK_FAILED (linkres)) goto subtitle_failed; - } +done: /* we got the sink succesfully linked, now keep the sink * in our internal list */ play_bin->sinks = g_list_prepend (play_bin->sinks, sink); @@ -1313,6 +1407,8 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, /* ERRORS */ state_failed: { + gst_element_set_state (sink, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (play_bin), sink); GST_DEBUG_OBJECT (play_bin, "state change failure when adding sink"); return FALSE; } @@ -1331,7 +1427,7 @@ link_failed: gst_caps_unref (caps); gst_element_set_state (sink, GST_STATE_NULL); - gst_bin_remove (GST_BIN (play_bin), sink); + gst_bin_remove (GST_BIN_CAST (play_bin), sink); return FALSE; } subtitle_failed: @@ -1348,7 +1444,8 @@ subtitle_failed: g_free (capsstr); gst_caps_unref (caps); - return TRUE; + /* not fatal */ + goto done; } } @@ -1399,6 +1496,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group) } if (!sink) return FALSE; + pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll, "src"); res = add_sink (play_bin, sink, pad, NULL); @@ -1417,7 +1515,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group) "src"); /* This pad is from subtitle-bin, we need to create a ghost pad to have common grandparents */ - parent = gst_object_get_parent (GST_OBJECT (textsrcpad)); + parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad)); if (!parent) { GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !"); gst_object_unref (textsrcpad); @@ -1450,7 +1548,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group) goto beach; } - if (gst_element_add_pad (GST_ELEMENT (grandparent), ghost)) { + if (gst_element_add_pad (GST_ELEMENT_CAST (grandparent), ghost)) { gst_object_unref (textsrcpad); textsrcpad = gst_object_ref (ghost); } else { @@ -1476,16 +1574,15 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group) "src"); res = add_sink (play_bin, sink, pad, textsrcpad); gst_object_unref (pad); - if (textsrcpad) { + if (textsrcpad) gst_object_unref (textsrcpad); - } } /* remove the sinks now, pipeline get_state will now wait for the * sinks to preroll */ if (play_bin->fakesink) { gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink); + gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink); play_bin->fakesink = NULL; } @@ -1585,6 +1682,42 @@ gst_play_bin_send_event (GstElement * element, GstEvent * event) return res; } +/* Override the set_clock function, we don't want to set a clock on the sinks + * when we are live pipeline so that they don't synchronize until this is + * fixed in core. */ +static gboolean +gst_play_bin_set_clock_func (GstElement * element, GstClock * clock) +{ + GList *children; + GstBin *bin; + GstPlayBin *play_bin; + gboolean res = TRUE; + GstElement *asink, *vsink; + + bin = GST_BIN (element); + play_bin = GST_PLAY_BIN (element); + + asink = g_hash_table_lookup (play_bin->cache, "audio_sink"); + vsink = g_hash_table_lookup (play_bin->cache, "video_sink"); + + GST_DEBUG_OBJECT (play_bin, "setting clock, is_live %d", play_bin->is_live); + + GST_OBJECT_LOCK (bin); + if (element->clock != clock) { + for (children = bin->children; children; children = g_list_next (children)) { + GstElement *child = GST_ELEMENT (children->data); + + if (play_bin->is_live && (child == asink || child == vsink)) + res &= gst_element_set_clock (child, NULL); + else + res &= gst_element_set_clock (child, clock); + } + } + GST_OBJECT_UNLOCK (bin); + + return res; +} + static void value_list_append_structure_list (GValue * list_val, GstStructure ** first, GList * structure_list) @@ -1698,7 +1831,7 @@ gst_play_bin_change_state (GstElement * element, GstStateChange transition) * ASYNC until we added the sinks */ if (!play_bin->fakesink) { play_bin->fakesink = gst_element_factory_make ("fakesink", "test"); - gst_bin_add (GST_BIN (play_bin), play_bin->fakesink); + gst_bin_add (GST_BIN_CAST (play_bin), play_bin->fakesink); } break; default: @@ -1710,27 +1843,21 @@ gst_play_bin_change_state (GstElement * element, GstStateChange transition) return ret; switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + /* remember us being a live pipeline */ + play_bin->is_live = (ret == GST_STATE_CHANGE_NO_PREROLL); + GST_DEBUG_OBJECT (play_bin, "is live: %d", play_bin->is_live); + break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - /* Set audio sink state to NULL to release the sound device, - * but only if we own it (else we might be in chain-transition). */ - //if (play_bin->audio_sink != NULL && - // GST_STATE (play_bin->audio_sink) == GST_STATE_PAUSED) { - // gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL); - //} + /* FIXME Release audio device when we implement that */ break; case GST_STATE_CHANGE_PAUSED_TO_READY: - /* Check for NULL because the state transition may be done by - * gst_bin_dispose which is called by gst_play_bin_dispose, and in that - * case, we don't want to run remove_sinks. - * FIXME: should the NULL test be done in remove_sinks? Should we just - * set the state to NULL in gst_play_bin_dispose? - */ - if (play_bin->cache != NULL) { - remove_sinks (play_bin); - } + /* remove sinks we added */ + remove_sinks (play_bin); + /* and there might be a fakesink we need to clean up now */ if (play_bin->fakesink) { gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); - gst_bin_remove (GST_BIN (play_bin), play_bin->fakesink); + gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink); play_bin->fakesink = NULL; } break;