gst/playback/gstplaybin.c: Detect NO_PREROLL state change returns and disable clock distribution to the sinks so that...

Original commit message from CVS:
* 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.
This commit is contained in:
Wim Taymans 2006-09-25 13:24:59 +00:00
parent dfbd97a889
commit 5c5edca364
2 changed files with 257 additions and 115 deletions

View file

@ -1,3 +1,18 @@
2006-09-25 Wim Taymans <wim@fluendo.com>
* 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 <ensonic@users.sf.net> 2006-09-25 Stefan Kost <ensonic@users.sf.net>
* ChangeLog: * ChangeLog:

View file

@ -285,6 +285,9 @@ struct _GstPlayBin
/* connection speed in bits/sec (0 = unknown) */ /* connection speed in bits/sec (0 = unknown) */
guint connection_speed; guint connection_speed;
/* indication if the pipeline is live */
gboolean is_live;
}; };
struct _GstPlayBinClass 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, static gboolean gst_play_bin_send_event (GstElement * element,
GstEvent * event); GstEvent * event);
static gboolean gst_play_bin_set_clock_func (GstElement * element,
GstClock * clock);
static GstStateChangeReturn gst_play_bin_change_state (GstElement * element, static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message); static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
static GstElementClass *parent_class; static GstElementClass *parent_class;
@ -427,6 +433,7 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
gstelement_klass->change_state = gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_bin_change_state); GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event); 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 = gstbin_klass->handle_message =
GST_DEBUG_FUNCPTR (gst_play_bin_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 = 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)) { if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
goto beach; 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); play_bin->video_sink = g_value_get_object (value);
if (play_bin->video_sink != NULL) { if (play_bin->video_sink != NULL) {
gst_object_ref (play_bin->video_sink); 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 /* when changing the videosink, we just remove the
* video pipeline from the cache so that it will be * 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); play_bin->audio_sink = g_value_get_object (value);
if (play_bin->audio_sink != NULL) { if (play_bin->audio_sink != NULL) {
gst_object_ref (play_bin->audio_sink); 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"); g_hash_table_remove (play_bin->cache, "abin");
break; break;
@ -647,7 +655,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
/* Take ownership */ /* Take ownership */
if (play_bin->pending_visualisation) { if (play_bin->pending_visualisation) {
gst_object_ref (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 { } else {
play_bin->pending_visualisation = g_value_get_object (value); 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 */ /* Take ownership */
if (play_bin->pending_visualisation) { if (play_bin->pending_visualisation) {
gst_object_ref (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 ? */ /* Was there a visualisation already set ? */
@ -663,7 +671,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
GstBin *vis_bin = NULL; GstBin *vis_bin = NULL;
vis_bin = 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))); visualisation)));
/* Check if the visualisation is already in a bin */ /* Check if the visualisation is already in a bin */
@ -798,8 +806,6 @@ handoff (GstElement * identity, GstBuffer * frame, gpointer data)
* +----------|--------------------------------------------------+ * +----------|--------------------------------------------------+
* handoff * handoff
*/ */
/* FIXME: this might return NULL if no videosink was found, handle
* this in callers */
static GstElement * static GstElement *
gen_video_element (GstPlayBin * play_bin) gen_video_element (GstPlayBin * play_bin)
{ {
@ -824,36 +830,38 @@ gen_video_element (GstPlayBin * play_bin)
if (sink == NULL) { if (sink == NULL) {
sink = gst_element_factory_make ("xvimagesink", "videosink"); sink = gst_element_factory_make ("xvimagesink", "videosink");
} }
/* FIXME: this warrants adding a CORE error category for missing if (sink == NULL)
* elements/plugins */ goto no_sinks;
if (sink == NULL) {
GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
(_("Both autovideosink and xvimagesink elements are missing.")),
(NULL));
return NULL;
}
} }
gst_object_ref (sink); gst_object_ref (sink);
g_hash_table_insert (play_bin->cache, "video_sink", 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"); element = gst_bin_new ("vbin");
identity = gst_element_factory_make ("identity", "id"); gst_bin_add (GST_BIN_CAST (element), sink);
g_object_set (identity, "silent", TRUE, NULL);
g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin);
gst_bin_add (GST_BIN (element), identity);
conv = gst_element_factory_make ("ffmpegcolorspace", "vconv"); conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
if (conv == NULL) if (conv == NULL)
goto no_colorspace; goto no_colorspace;
gst_bin_add (GST_BIN_CAST (element), conv);
scale = gst_element_factory_make ("videoscale", "vscale"); scale = gst_element_factory_make ("videoscale", "vscale");
if (scale == NULL) if (scale == NULL)
goto no_videoscale; goto no_videoscale;
gst_bin_add (GST_BIN (element), conv); gst_bin_add (GST_BIN_CAST (element), scale);
gst_bin_add (GST_BIN (element), scale);
gst_bin_add (GST_BIN (element), sink); 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 (identity, "src", conv, "sink");
gst_element_link_pads (conv, "src", scale, "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"); pad = gst_element_get_pad (identity, "sink");
gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
@ -868,6 +876,16 @@ gen_video_element (GstPlayBin * play_bin)
return element; 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: no_colorspace:
{ {
GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
@ -885,6 +903,13 @@ no_videoscale:
gst_object_unref (element); gst_object_unref (element);
return NULL; 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. /* make an element for playback of video with subtitles embedded.
@ -897,28 +922,30 @@ no_videoscale:
* | +-----+ | +-------------+ +------+ | * | +-----+ | +-------------+ +------+ |
* text_sink-------------+ | * text_sink-------------+ |
* +--------------------------------------------------+ * +--------------------------------------------------+
*
* If there is no subtitle renderer this function will simply return the
* videosink without the text_sink pad.
*/ */
static GstElement * static GstElement *
gen_text_element (GstPlayBin * play_bin) gen_text_element (GstPlayBin * play_bin)
{ {
GstElement *element, *csp, *overlay, *vbin; GstElement *element, *csp, *overlay, *vbin;
GstPad *pad; GstPad *pad;
/* Create our bin */ /* Create the video rendering bin, error is posted when this fails. */
element = gst_bin_new ("textbin"); vbin = gen_video_element (play_bin);
if (!vbin)
return NULL;
/* Text overlay */ /* Text overlay */
overlay = gst_element_factory_make ("textoverlay", "overlay"); overlay = gst_element_factory_make ("textoverlay", "overlay");
/* Create the video rendering bin */ /* If no overlay return the video bin without subtitle support. */
vbin = gen_video_element (play_bin); if (!overlay)
goto no_overlay;
/* If no overlay return the video bin */ /* Create our bin */
if (!overlay) { element = gst_bin_new ("textbin");
GST_WARNING ("No overlay (pango) element, subtitles disabled");
return vbin;
}
/* Set some parameters */ /* Set some parameters */
g_object_set (G_OBJECT (overlay), g_object_set (G_OBJECT (overlay),
@ -928,13 +955,13 @@ gen_text_element (GstPlayBin * play_bin)
} }
/* Take a ref */ /* 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 */ /* we know this will succeed, as the video bin already created one before */
csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp"); csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
/* Add our elements */ /* 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 */ /* Link */
gst_element_link_pads (csp, "src", overlay, "video_sink"); 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); gst_element_set_state (element, GST_STATE_READY);
return element; 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 /* make the element (bin) that contains the elements needed to perform
@ -966,11 +1001,11 @@ gen_text_element (GstPlayBin * play_bin)
* | | +---------+ +----------+ +---------+ +---------+ | * | | +---------+ +----------+ +---------+ +---------+ |
* sink-+ | * sink-+ |
* +-------------------------------------------------------------+ * +-------------------------------------------------------------+
*
*/ */
static GstElement * static GstElement *
gen_audio_element (GstPlayBin * play_bin) gen_audio_element (GstPlayBin * play_bin)
{ {
gboolean res;
GstElement *element; GstElement *element;
GstElement *conv; GstElement *conv;
GstElement *scale; GstElement *scale;
@ -979,21 +1014,8 @@ gen_audio_element (GstPlayBin * play_bin)
GstPad *pad; GstPad *pad;
element = g_hash_table_lookup (play_bin->cache, "abin"); element = g_hash_table_lookup (play_bin->cache, "abin");
if (element != NULL) { if (element != NULL)
return element; 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) { if (play_bin->audio_sink) {
sink = play_bin->audio_sink; sink = play_bin->audio_sink;
@ -1002,25 +1024,38 @@ gen_audio_element (GstPlayBin * play_bin)
if (sink == NULL) { if (sink == NULL) {
sink = gst_element_factory_make ("alsasink", "audiosink"); sink = gst_element_factory_make ("alsasink", "audiosink");
} }
if (sink == NULL) { if (sink == NULL)
GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, goto no_sinks;
(_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
return NULL; play_bin->audio_sink = GST_ELEMENT_CAST (gst_object_ref (sink));
}
play_bin->audio_sink = GST_ELEMENT (gst_object_ref (sink));
} }
gst_object_ref (sink); gst_object_ref (sink);
g_hash_table_insert (play_bin->cache, "audio_sink", sink); g_hash_table_insert (play_bin->cache, "audio_sink", sink);
gst_bin_add (GST_BIN (element), conv); element = gst_bin_new ("abin");
gst_bin_add (GST_BIN (element), scale); gst_bin_add (GST_BIN_CAST (element), sink);
gst_bin_add (GST_BIN (element), volume);
gst_bin_add (GST_BIN (element), sink);
gst_element_link_pads (conv, "src", scale, "sink"); conv = gst_element_factory_make ("audioconvert", "aconv");
gst_element_link_pads (scale, "src", volume, "sink"); if (conv == NULL)
gst_element_link_pads (volume, "src", sink, "sink"); 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"); pad = gst_element_get_pad (conv, "sink");
gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad)); gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
@ -1035,6 +1070,13 @@ gen_audio_element (GstPlayBin * play_bin)
return element; 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: no_audioconvert:
{ {
GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
@ -1052,6 +1094,13 @@ no_audioresample:
gst_object_unref (element); gst_object_unref (element);
return NULL; 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 /* make the element (bin) that contains the elements needed to perform
@ -1080,6 +1129,7 @@ no_audioresample:
static GstElement * static GstElement *
gen_vis_element (GstPlayBin * play_bin) gen_vis_element (GstPlayBin * play_bin)
{ {
gboolean res;
GstElement *element; GstElement *element;
GstElement *tee; GstElement *tee;
GstElement *asink; GstElement *asink;
@ -1089,6 +1139,7 @@ gen_vis_element (GstPlayBin * play_bin)
GstElement *vqueue, *aqueue; GstElement *vqueue, *aqueue;
GstPad *pad, *rpad; GstPad *pad, *rpad;
/* errors are already posted when these fail. */
asink = gen_audio_element (play_bin); asink = gen_audio_element (play_bin);
if (!asink) if (!asink)
return NULL; return NULL;
@ -1104,29 +1155,32 @@ gen_vis_element (GstPlayBin * play_bin)
vqueue = gst_element_factory_make ("queue", "vqueue"); vqueue = gst_element_factory_make ("queue", "vqueue");
aqueue = gst_element_factory_make ("queue", "aqueue"); aqueue = gst_element_factory_make ("queue", "aqueue");
gst_bin_add (GST_BIN (element), asink); gst_bin_add (GST_BIN_CAST (element), asink);
gst_bin_add (GST_BIN (element), vqueue); gst_bin_add (GST_BIN_CAST (element), vqueue);
gst_bin_add (GST_BIN (element), aqueue); gst_bin_add (GST_BIN_CAST (element), aqueue);
gst_bin_add (GST_BIN (element), vsink); gst_bin_add (GST_BIN_CAST (element), vsink);
gst_bin_add (GST_BIN (element), tee); gst_bin_add (GST_BIN_CAST (element), tee);
conv = gst_element_factory_make ("audioconvert", "aconv"); conv = gst_element_factory_make ("audioconvert", "aconv");
if (conv == NULL) if (conv == NULL)
goto no_audioconvert; goto no_audioconvert;
gst_bin_add (GST_BIN_CAST (element), conv);
if (play_bin->visualisation) { if (play_bin->visualisation) {
gst_object_ref (play_bin->visualisation); gst_object_ref (play_bin->visualisation);
vis = play_bin->visualisation; vis = play_bin->visualisation;
} else { } else {
vis = gst_element_factory_make ("goom", "vis"); 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); res = gst_element_link_pads (vqueue, "src", conv, "sink");
gst_bin_add (GST_BIN (element), vis); res &= gst_element_link_pads (conv, "src", vis, "sink");
res &= gst_element_link_pads (vis, "src", vsink, "sink");
gst_element_link_pads (vqueue, "src", conv, "sink"); if (!res)
gst_element_link_pads (conv, "src", vis, "sink"); goto link_failed;
gst_element_link_pads (vis, "src", vsink, "sink");
pad = gst_element_get_pad (aqueue, "sink"); pad = gst_element_get_pad (aqueue, "sink");
rpad = gst_element_get_request_pad (tee, "src%d"); rpad = gst_element_get_request_pad (tee, "src%d");
@ -1147,6 +1201,7 @@ gen_vis_element (GstPlayBin * play_bin)
return element; return element;
/* ERRORS */
no_audioconvert: no_audioconvert:
{ {
GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN, GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
@ -1155,6 +1210,21 @@ no_audioconvert:
gst_object_unref (element); gst_object_unref (element);
return NULL; 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 */ /* get rid of all installed sinks */
@ -1166,6 +1236,9 @@ remove_sinks (GstPlayBin * play_bin)
GstElement *element; GstElement *element;
GstPad *pad, *peer; GstPad *pad, *peer;
if (play_bin->cache == NULL)
return;
GST_DEBUG ("removesinks"); GST_DEBUG ("removesinks");
element = g_hash_table_lookup (play_bin->cache, "abin"); element = g_hash_table_lookup (play_bin->cache, "abin");
if (element != NULL) { if (element != NULL) {
@ -1176,7 +1249,7 @@ remove_sinks (GstPlayBin * play_bin)
* is disposed */ * is disposed */
play_bin->sinks = g_list_remove (play_bin->sinks, element); play_bin->sinks = g_list_remove (play_bin->sinks, element);
gst_element_set_state (element, GST_STATE_NULL); 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); gst_object_unref (parent);
} }
pad = gst_element_get_pad (element, "sink"); pad = gst_element_get_pad (element, "sink");
@ -1195,7 +1268,7 @@ remove_sinks (GstPlayBin * play_bin)
if (parent != NULL) { if (parent != NULL) {
play_bin->sinks = g_list_remove (play_bin->sinks, element); play_bin->sinks = g_list_remove (play_bin->sinks, element);
gst_element_set_state (element, GST_STATE_NULL); 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); gst_object_unref (parent);
} }
pad = gst_element_get_pad (element, "sink"); 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)) { 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 *pad;
GstPad *peer; GstPad *peer;
@ -1226,16 +1299,21 @@ remove_sinks (GstPlayBin * play_bin)
gst_object_unref (pad); gst_object_unref (pad);
gst_element_set_state (element, GST_STATE_NULL); 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); g_list_free (play_bin->sinks);
play_bin->sinks = NULL; play_bin->sinks = NULL;
/* FIXME: this is probably some refcounting problem */ if (play_bin->visualisation) {
if (play_bin->visualisation && GST_OBJECT_PARENT (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_element_set_state (play_bin->visualisation, GST_STATE_NULL);
gst_bin_remove (GST_BIN (GST_OBJECT_PARENT (play_bin->visualisation)), gst_bin_remove (GST_BIN_CAST (vis_bin), play_bin->visualisation);
play_bin->visualisation);
gst_object_unref (vis_bin);
} }
if (play_bin->frame) { 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 * Also make sure to only connect the first audio and video pad. FIXME
* this should eventually be handled with a tuner interface so that * this should eventually be handled with a tuner interface so that
* one can switch the streams. * one can switch the streams.
*
* This function takes ownership of @sink.*
*/ */
static gboolean static gboolean
add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad, add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
@ -1266,8 +1346,17 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
GstPadLinkReturn linkres; GstPadLinkReturn linkres;
GstElement *parent; GstElement *parent;
GstStateChangeReturn stateret; GstStateChangeReturn stateret;
GstState state;
g_return_val_if_fail (sink != NULL, FALSE); 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 */ /* this is only for debugging */
parent = gst_pad_get_parent_element (srcpad); parent = gst_pad_get_parent_element (srcpad);
if (parent) { 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_STATE (sink), GST_STATE (play_bin), GST_STATE (parent));
gst_object_unref (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 /* bring it to the PAUSED state so we can link to the peer without
* breaking the flow */ * breaking the flow */
if ((stateret = gst_element_set_state (sink, GST_STATE_PAUSED)) == stateret = gst_element_set_state (sink, state);
GST_STATE_CHANGE_FAILURE) if (stateret == GST_STATE_CHANGE_FAILURE)
goto state_failed; goto state_failed;
gst_bin_add (GST_BIN (play_bin), sink);
/* we found a sink for this stream, now try to install it */ /* we found a sink for this stream, now try to install it */
sinkpad = gst_element_get_pad (sink, "sink"); sinkpad = gst_element_get_pad (sink, "sink");
linkres = gst_pad_link (srcpad, sinkpad); linkres = gst_pad_link (srcpad, sinkpad);
@ -1299,11 +1392,12 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
gst_object_unref (sinkpad); gst_object_unref (sinkpad);
} }
/* try to link the subtitle pad of the sink to the stream */ /* try to link the subtitle pad of the sink to the stream, this is not
if (GST_PAD_LINK_FAILED (linkres)) { * fatal. */
if (GST_PAD_LINK_FAILED (linkres))
goto subtitle_failed; goto subtitle_failed;
}
done:
/* we got the sink succesfully linked, now keep the sink /* we got the sink succesfully linked, now keep the sink
* in our internal list */ * in our internal list */
play_bin->sinks = g_list_prepend (play_bin->sinks, sink); play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
@ -1313,6 +1407,8 @@ add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
/* ERRORS */ /* ERRORS */
state_failed: 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"); GST_DEBUG_OBJECT (play_bin, "state change failure when adding sink");
return FALSE; return FALSE;
} }
@ -1331,7 +1427,7 @@ link_failed:
gst_caps_unref (caps); gst_caps_unref (caps);
gst_element_set_state (sink, GST_STATE_NULL); 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; return FALSE;
} }
subtitle_failed: subtitle_failed:
@ -1348,7 +1444,8 @@ subtitle_failed:
g_free (capsstr); g_free (capsstr);
gst_caps_unref (caps); gst_caps_unref (caps);
return TRUE; /* not fatal */
goto done;
} }
} }
@ -1399,6 +1496,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
} }
if (!sink) if (!sink)
return FALSE; return FALSE;
pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll, pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
"src"); "src");
res = add_sink (play_bin, sink, pad, NULL); res = add_sink (play_bin, sink, pad, NULL);
@ -1417,7 +1515,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
"src"); "src");
/* This pad is from subtitle-bin, we need to create a ghost pad to have /* This pad is from subtitle-bin, we need to create a ghost pad to have
common grandparents */ common grandparents */
parent = gst_object_get_parent (GST_OBJECT (textsrcpad)); parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad));
if (!parent) { if (!parent) {
GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !"); GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !");
gst_object_unref (textsrcpad); gst_object_unref (textsrcpad);
@ -1450,7 +1548,7 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
goto beach; 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); gst_object_unref (textsrcpad);
textsrcpad = gst_object_ref (ghost); textsrcpad = gst_object_ref (ghost);
} else { } else {
@ -1476,16 +1574,15 @@ setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
"src"); "src");
res = add_sink (play_bin, sink, pad, textsrcpad); res = add_sink (play_bin, sink, pad, textsrcpad);
gst_object_unref (pad); gst_object_unref (pad);
if (textsrcpad) { if (textsrcpad)
gst_object_unref (textsrcpad); gst_object_unref (textsrcpad);
}
} }
/* remove the sinks now, pipeline get_state will now wait for the /* remove the sinks now, pipeline get_state will now wait for the
* sinks to preroll */ * sinks to preroll */
if (play_bin->fakesink) { if (play_bin->fakesink) {
gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); 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; play_bin->fakesink = NULL;
} }
@ -1585,6 +1682,42 @@ gst_play_bin_send_event (GstElement * element, GstEvent * event)
return res; 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 static void
value_list_append_structure_list (GValue * list_val, GstStructure ** first, value_list_append_structure_list (GValue * list_val, GstStructure ** first,
GList * structure_list) GList * structure_list)
@ -1698,7 +1831,7 @@ gst_play_bin_change_state (GstElement * element, GstStateChange transition)
* ASYNC until we added the sinks */ * ASYNC until we added the sinks */
if (!play_bin->fakesink) { if (!play_bin->fakesink) {
play_bin->fakesink = gst_element_factory_make ("fakesink", "test"); 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; break;
default: default:
@ -1710,27 +1843,21 @@ gst_play_bin_change_state (GstElement * element, GstStateChange transition)
return ret; return ret;
switch (transition) { 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: case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
/* Set audio sink state to NULL to release the sound device, /* FIXME Release audio device when we implement that */
* 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);
//}
break; break;
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
/* Check for NULL because the state transition may be done by /* remove sinks we added */
* gst_bin_dispose which is called by gst_play_bin_dispose, and in that remove_sinks (play_bin);
* case, we don't want to run remove_sinks. /* and there might be a fakesink we need to clean up now */
* 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);
}
if (play_bin->fakesink) { if (play_bin->fakesink) {
gst_element_set_state (play_bin->fakesink, GST_STATE_NULL); 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; play_bin->fakesink = NULL;
} }
break; break;