/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
#define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
#define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
+enum
+{
+ PLAYBIN_STREAM_AUDIO = 0,
+ PLAYBIN_STREAM_VIDEO,
+ PLAYBIN_STREAM_TEXT,
+ PLAYBIN_STREAM_LAST
+};
+
/* a structure to hold the objects for decoding a uri and the subtitle uri
*/
struct _GstSourceGroup
GList *stream_changed_pending;
/* selectors for different streams */
- GstSourceSelect selector[GST_PLAY_SINK_TYPE_LAST];
+ GstSourceSelect selector[PLAYBIN_STREAM_LAST];
};
#define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
/* If you add any items to these lists, check that media_list[] is defined
* above to be large enough to hold MAX(items)+1, so as to accomodate a
* NULL terminator (set when the memory is zeroed on allocation) */
- group->selector[0].media_list[0] = "audio/x-raw-";
- group->selector[0].type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
- group->selector[0].channels = group->audio_channels;
- group->selector[1].media_list[0] = "audio/";
- group->selector[1].type = GST_PLAY_SINK_TYPE_AUDIO;
- group->selector[1].channels = group->audio_channels;
- group->selector[2].media_list[0] = "text/";
- group->selector[2].media_list[1] = "application/x-subtitle";
- group->selector[2].media_list[2] = "application/x-ssa";
- group->selector[2].media_list[3] = "application/x-ass";
- group->selector[2].media_list[4] = "video/x-dvd-subpicture";
- group->selector[2].media_list[5] = "subpicture/";
- group->selector[2].media_list[6] = "subtitle/";
- group->selector[2].get_media_caps = gst_subtitle_overlay_create_factory_caps;
- group->selector[2].type = GST_PLAY_SINK_TYPE_TEXT;
- group->selector[2].channels = group->text_channels;
- group->selector[3].media_list[0] = "video/x-raw-";
- group->selector[3].type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
- group->selector[3].channels = group->video_channels;
- group->selector[4].media_list[0] = "video/";
- group->selector[4].type = GST_PLAY_SINK_TYPE_VIDEO;
- group->selector[4].channels = group->video_channels;
-
- for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) {
+ group->selector[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
+ group->selector[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
+ group->selector[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
+ group->selector[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
+ group->selector[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
+ group->selector[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[4] = "video/x-dvd-subpicture";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
+ group->selector[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
+ group->selector[PLAYBIN_STREAM_TEXT].get_media_caps =
+ gst_subtitle_overlay_create_factory_caps;
+ group->selector[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
+ group->selector[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
+
+ for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
GstSourceSelect *select = &group->selector[n];
select->sinkpad_delayed_event = NULL;
select->sinkpad_data_probe = 0;
{
int n;
- for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) {
+ for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
GstSourceSelect *select = &group->selector[n];
if (select->sinkpad && select->sinkpad_data_probe)
gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe);
GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin);
+
+ GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
+ playbin->current_video, stream);
+
group = get_group (playbin);
if (!(channels = group->video_channels))
goto no_channels;
GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin);
+
+ GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
+ playbin->current_audio, stream);
+
group = get_group (playbin);
if (!(channels = group->audio_channels))
goto no_channels;
GstPad *sinkpad;
GST_PLAY_BIN_LOCK (playbin);
+
+ GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
+ playbin->current_text, stream);
+
group = get_group (playbin);
if (!(channels = group->text_channels))
goto no_channels;
GST_PLAY_BIN_LOCK (playbin);
group = get_group (playbin);
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
if (selector == G_OBJECT (group->selector[i].selector)) {
select = &group->selector[i];
}
gint i;
for (i = 0; values[i]; i++) {
- if (g_str_has_prefix (value, values[i]))
+ if (values[i] && g_str_has_prefix (value, values[i]))
return TRUE;
}
return FALSE;
GST_DEBUG_PAD_NAME (pad), caps, group);
/* major type of the pad, this determines the selector to use */
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
if (array_has_value (group->selector[i].media_list, name)) {
select = &group->selector[i];
break;
GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
GST_SOURCE_GROUP_LOCK (group);
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i];
/* check if the specific media type was detected and thus has a selector
GST_SOURCE_GROUP_UNLOCK (group);
- GST_LOG_OBJECT (playbin, "reconfigure sink");
- /* we configure the modes if we were the last decodebin to complete. */
- gst_play_sink_reconfigure (playbin->playsink);
-
/* signal the other decodebins that they can continue now. */
GST_SOURCE_GROUP_LOCK (group);
/* unblock all selectors */
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i];
/* All streamsynchronizer streams should see stream-changed message,
* instead of a NOT_LINKED error.
*/
GST_SOURCE_GROUP_LOCK (group);
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i];
if (select->srcpad) {
GST_SOURCE_GROUP_LOCK (group);
group->active = FALSE;
- for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
+ for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
GstSourceSelect *select = &group->selector[i];
GST_DEBUG_OBJECT (playbin, "unlinking selector %s", select->media_list[0]);
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
/* audio */
GstPad *audio_pad;
gboolean audio_pad_raw;
+ gboolean audio_pad_blocked;
GstPad *audio_srcpad_stream_synchronizer;
GstPad *audio_sinkpad_stream_synchronizer;
/* audio tee */
/* video */
GstPad *video_pad;
gboolean video_pad_raw;
+ gboolean video_pad_blocked;
GstPad *video_srcpad_stream_synchronizer;
GstPad *video_sinkpad_stream_synchronizer;
/* text */
GstPad *text_pad;
+ gboolean text_pad_blocked;
GstPad *text_srcpad_stream_synchronizer;
GstPad *text_sinkpad_stream_synchronizer;
GstBuffer *(*convert_frame) (GstPlaySink * playsink, GstCaps * caps);
};
-static GstStaticPadTemplate audiorawtemplate =
-GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
+
+static GstStaticPadTemplate audiotemplate =
+GST_STATIC_PAD_TEMPLATE ("audio_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
-static GstStaticPadTemplate audiotemplate =
-GST_STATIC_PAD_TEMPLATE ("audio_sink",
+static GstStaticPadTemplate videotemplate =
+GST_STATIC_PAD_TEMPLATE ("video_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
-static GstStaticPadTemplate videorawtemplate =
-GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
+static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
-static GstStaticPadTemplate videotemplate =
-GST_STATIC_PAD_TEMPLATE ("video_sink",
+
+/* FIXME 0.11: Remove */
+static GstStaticPadTemplate audiorawtemplate =
+GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
-static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
+static GstStaticPadTemplate videorawtemplate =
+GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_STATIC_CAPS_ANY);
+
/* props */
enum
{
GST_PLAY_SINK_LOCK (playsink);
switch (type) {
case GST_PLAY_SINK_TYPE_AUDIO:
+ case GST_PLAY_SINK_TYPE_AUDIO_RAW:
{
GstPlayAudioChain *chain;
if ((chain = (GstPlayAudioChain *) playsink->audiochain))
break;
}
case GST_PLAY_SINK_TYPE_VIDEO:
+ case GST_PLAY_SINK_TYPE_VIDEO_RAW:
{
GstPlayVideoChain *chain;
if ((chain = (GstPlayVideoChain *) playsink->videochain))
}
/* make the element (bin) that contains the elements needed to perform
- * video display.
+ * video display. Only used for *raw* video streams.
*
* +------------------------------------------------------------+
* | vbin |
chain = playsink->videochain;
+ if (chain->chain.raw != raw)
+ return FALSE;
+
/* if the chain was active we don't do anything */
if (GST_PLAY_CHAIN (chain)->activated == TRUE)
return TRUE;
- if (chain->chain.raw != raw)
- return FALSE;
-
/* try to set the sink element to READY again */
ret = gst_element_set_state (chain->sink, GST_STATE_READY);
if (ret == GST_STATE_CHANGE_FAILURE)
}
/* make an element for playback of video with subtitles embedded.
+ * Only used for *raw* video streams.
*
* +--------------------------------------------+
* | tbin |
chain = playsink->audiochain;
+ if (chain->chain.raw != raw)
+ return FALSE;
+
/* if the chain was active we don't do anything */
if (GST_PLAY_CHAIN (chain)->activated == TRUE)
return TRUE;
- if (chain->chain.raw != raw)
- return FALSE;
-
/* try to set the sink element to READY again */
ret = gst_element_set_state (chain->sink, GST_STATE_READY);
if (ret == GST_STATE_CHANGE_FAILURE)
return result;
}
+static gboolean
+is_raw_structure (GstStructure * s)
+{
+ const gchar *name;
+
+ name = gst_structure_get_name (s);
+
+ if (g_str_has_prefix (name, "video/x-raw-") ||
+ g_str_has_prefix (name, "audio/x-raw-"))
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+is_raw_pad (GstPad * pad)
+{
+ GstPad *peer = gst_pad_get_peer (pad);
+ GstCaps *caps;
+ gboolean raw = TRUE;
+
+ if (!peer)
+ return raw;
+
+ caps = gst_pad_get_negotiated_caps (peer);
+ if (!caps) {
+ guint i, n;
+
+ caps = gst_pad_get_caps_reffed (peer);
+
+ n = gst_caps_get_size (caps);
+ for (i = 0; i < n; i++) {
+ gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
+
+ if (i == 0) {
+ raw = r;
+ } else if (raw != r) {
+ GST_ERROR_OBJECT (pad,
+ "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
+ raw = FALSE;
+ break;
+ }
+ }
+ } else {
+ raw = is_raw_structure (gst_caps_get_structure (caps, 0));
+ }
+ gst_caps_unref (caps);
+ gst_object_unref (peer);
+
+ return raw;
+}
+
+static GstPad *
+get_internally_linked_pad (GstPad * pad)
+{
+ GstIterator *it;
+ GstPad *res = NULL;
+
+ it = gst_pad_iterate_internal_links (pad);
+ if (!it)
+ return NULL;
+
+ gst_iterator_next (it, (gpointer) & res);
+ gst_iterator_free (it);
+
+ return res;
+}
+
+static void
+sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
+{
+ GstPlaySink *playsink = (GstPlaySink *) user_data;
+ GstPad *pad;
+
+ GST_PLAY_SINK_LOCK (playsink);
+
+ pad = get_internally_linked_pad (blockedpad);
+ if (pad == playsink->video_pad) {
+ playsink->video_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
+ } else if (pad == playsink->audio_pad) {
+ playsink->audio_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
+ } else if (pad == playsink->text_pad) {
+ playsink->text_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
+ }
+
+ if (!blocked) {
+ gst_object_unref (pad);
+ GST_PLAY_SINK_UNLOCK (playsink);
+ return;
+ }
+
+ if ((!playsink->video_pad || playsink->video_pad_blocked) &&
+ (!playsink->audio_pad || playsink->audio_pad_blocked) &&
+ (!playsink->text_pad || playsink->text_pad_blocked)) {
+ GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
+
+ if (playsink->video_pad) {
+ playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
+ GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
+ playsink->video_pad_raw);
+ }
+
+ if (playsink->audio_pad) {
+ playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
+ GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
+ playsink->audio_pad_raw);
+ }
+
+ gst_play_sink_reconfigure (playsink);
+
+ if (playsink->video_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->video_pad);
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+
+ if (playsink->audio_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->audio_pad);
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+
+ if (playsink->text_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->text_pad);
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+ }
+
+ gst_object_unref (pad);
+
+ GST_PLAY_SINK_UNLOCK (playsink);
+}
+
+static void
+caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
+{
+ gboolean reconfigure = FALSE;
+ GstCaps *caps;
+ gboolean raw;
+
+ g_object_get (pad, "caps", &caps, NULL);
+ if (!caps)
+ return;
+
+ if (pad == playsink->audio_pad) {
+ raw = is_raw_pad (pad);
+ reconfigure = (! !playsink->audio_pad_raw != ! !raw)
+ && playsink->audiochain;
+ GST_DEBUG_OBJECT (pad,
+ "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
+ reconfigure, caps);
+ } else if (pad == playsink->video_pad) {
+ raw = is_raw_pad (pad);
+ reconfigure = (! !playsink->video_pad_raw != ! !raw)
+ && playsink->videochain;
+ GST_DEBUG_OBJECT (pad,
+ "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
+ reconfigure, caps);
+ }
+
+ gst_caps_unref (caps);
+
+ if (reconfigure) {
+ GST_PLAY_SINK_LOCK (playsink);
+ if (playsink->video_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->video_pad);
+ gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+
+ if (playsink->audio_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->audio_pad);
+ gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+
+ if (playsink->text_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->text_pad);
+ gst_pad_set_blocked_async_full (opad, TRUE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (opad);
+ }
+ GST_PLAY_SINK_UNLOCK (playsink);
+ }
+}
+
/**
* gst_play_sink_request_pad
* @playsink: a #GstPlaySink
{
GstPad *res = NULL;
gboolean created = FALSE;
- gboolean raw = FALSE;
gboolean activate = TRUE;
const gchar *pad_name = NULL;
GST_PLAY_SINK_LOCK (playsink);
switch (type) {
case GST_PLAY_SINK_TYPE_AUDIO_RAW:
- pad_name = "audio_raw_sink";
- raw = TRUE;
case GST_PLAY_SINK_TYPE_AUDIO:
- if (pad_name == NULL)
- pad_name = "audio_sink";
+ pad_name = "audio_sink";
if (!playsink->audio_tee) {
GST_LOG_OBJECT (playsink, "creating tee");
/* create tee when needed. This element will feed the audio sink chain
GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
playsink->audio_pad =
gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
+ g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
+ G_CALLBACK (caps_notify_cb), playsink);
created = TRUE;
}
- playsink->audio_pad_raw = raw;
+ playsink->audio_pad_raw = FALSE;
res = playsink->audio_pad;
break;
case GST_PLAY_SINK_TYPE_VIDEO_RAW:
- pad_name = "video_raw_sink";
- raw = TRUE;
case GST_PLAY_SINK_TYPE_VIDEO:
- if (pad_name == NULL)
- pad_name = "video_sink";
+ pad_name = "video_sink";
if (!playsink->video_pad) {
GST_LOG_OBJECT (playsink, "ghosting videosink");
playsink->video_pad =
gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
+ g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
+ G_CALLBACK (caps_notify_cb), playsink);
created = TRUE;
}
- playsink->video_pad_raw = raw;
+ playsink->video_pad_raw = FALSE;
res = playsink->video_pad;
break;
case GST_PLAY_SINK_TYPE_TEXT:
* element is 'running' */
gst_pad_set_active (res, TRUE);
gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
+ if (type != GST_PLAY_SINK_TYPE_FLUSHING) {
+ GstPad *blockpad = get_internally_linked_pad (res);
+
+ gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ gst_object_unref (blockpad);
+ }
if (!activate)
gst_pad_set_active (res, activate);
}
GST_PLAY_SINK_LOCK (playsink);
if (pad == playsink->video_pad) {
res = &playsink->video_pad;
+ g_signal_handlers_disconnect_by_func (playsink->video_pad, caps_notify_cb,
+ playsink);
} else if (pad == playsink->audio_pad) {
res = &playsink->audio_pad;
+ g_signal_handlers_disconnect_by_func (playsink->audio_pad, caps_notify_cb,
+ playsink);
} else if (pad == playsink->text_pad) {
res = &playsink->text_pad;
} else {
ret = GST_STATE_CHANGE_ASYNC;
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* unblock all pads here */
+ GST_PLAY_SINK_LOCK (playsink);
+ if (playsink->video_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->video_pad);
+ if (gst_pad_is_blocked (opad)) {
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ }
+ gst_object_unref (opad);
+ playsink->video_pad_blocked = FALSE;
+ }
+
+ if (playsink->audio_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->audio_pad);
+
+ if (gst_pad_is_blocked (opad)) {
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ }
+ gst_object_unref (opad);
+ playsink->audio_pad_blocked = FALSE;
+ }
+
+ if (playsink->text_pad) {
+ GstPad *opad = get_internally_linked_pad (playsink->text_pad);
+ if (gst_pad_is_blocked (opad)) {
+ gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ }
+ gst_object_unref (opad);
+ playsink->text_pad_blocked = FALSE;
+ }
+ GST_PLAY_SINK_UNLOCK (playsink);
+ /* fall through */
case GST_STATE_CHANGE_READY_TO_NULL:
if (playsink->audiochain && playsink->audiochain->sink_volume) {
/* remove our links to the mute and volume elements when they were