/* 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
#include "config.h"
#endif
+/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
#include <string.h>
#include <gst/gst.h>
#include "gstplaysink.h"
#include "gststreamsynchronizer.h"
+#include "gstplaysinkvideoconvert.h"
+#include "gstplaysinkaudioconvert.h"
GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
#define GST_CAT_DEFAULT gst_play_sink_debug
GstPad *sinkpad;
GstElement *queue;
GstElement *conv;
- GstElement *resample;
GstElement *volume; /* element with the volume property */
gboolean sink_volume; /* if the volume was provided by the sink */
GstElement *mute; /* element with the mute property */
GstPad *sinkpad;
GstElement *queue;
GstElement *conv;
- GstElement *scale;
GstElement *sink;
gboolean async;
GstElement *ts_offset;
g_static_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
} G_STMT_END
+#define PENDING_FLAG_SET(playsink, flagtype) \
+ ((playsink->pending_blocked_pads) |= (1 << flagtype))
+#define PENDING_FLAG_UNSET(playsink, flagtype) \
+ ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
+#define PENDING_FLAG_IS_SET(playsink, flagtype) \
+ ((playsink->pending_blocked_pads) & (1 << flagtype))
+#define PENDING_VIDEO_BLOCK(playsink) \
+ ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO_RAW | 1 << GST_PLAY_SINK_TYPE_VIDEO))
+#define PENDING_AUDIO_BLOCK(playsink) \
+ ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO_RAW | 1 << GST_PLAY_SINK_TYPE_AUDIO))
+#define PENDING_TEXT_BLOCK(playsink) \
+ PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
+
struct _GstPlaySink
{
GstBin bin;
/* 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;
+ guint32 pending_blocked_pads;
+
/* properties */
GstElement *audio_sink;
GstElement *video_sink;
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
{
PROP_VIS_PLUGIN,
PROP_FRAME,
PROP_AV_OFFSET,
+ PROP_VIDEO_SINK,
+ PROP_AUDIO_SINK,
+ PROP_TEXT_SINK,
PROP_LAST
};
G_MININT64, G_MAXINT64, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstPlaySink:video-sink:
+ *
+ * Set the used video sink element. NULL will use the default sink. playsink
+ * must be in %GST_STATE_NULL
+ *
+ * Since: 0.10.36
+ */
+ g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
+ g_param_spec_object ("video-sink", "Video Sink",
+ "the video output element to use (NULL = default sink)",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstPlaySink:audio-sink:
+ *
+ * Set the used audio sink element. NULL will use the default sink. playsink
+ * must be in %GST_STATE_NULL
+ *
+ * Since: 0.10.36
+ */
+ g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
+ g_param_spec_object ("audio-sink", "Audio Sink",
+ "the audio output element to use (NULL = default sink)",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstPlaySink:text-sink:
+ *
+ * Set the used text sink element. NULL will use the default sink. playsink
+ * must be in %GST_STATE_NULL
+ *
+ * Since: 0.10.36
+ */
+ g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
+ g_param_spec_object ("text-sink", "Text sink",
+ "the text output element to use (NULL = default textoverlay)",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+
g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
reconfigure), NULL, NULL, gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN,
G_STRUCT_OFFSET (GstPlaySinkClass, convert_frame), NULL, NULL,
gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&audiorawtemplate));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&audiotemplate));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&videorawtemplate));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&videotemplate));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&texttemplate));
+ gst_element_class_add_static_pad_template (gstelement_klass,
+ &audiorawtemplate);
+ gst_element_class_add_static_pad_template (gstelement_klass,
+ &audiotemplate);
+ gst_element_class_add_static_pad_template (gstelement_klass,
+ &videorawtemplate);
+ gst_element_class_add_static_pad_template (gstelement_klass,
+ &videotemplate);
+ gst_element_class_add_static_pad_template (gstelement_klass,
+ &texttemplate);
gst_element_class_set_details_simple (gstelement_klass, "Player Sink",
"Generic/Bin/Sink",
"Convenience sink for multiple streams",
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 |
bin = GST_BIN_CAST (chain->chain.bin);
gst_object_ref_sink (bin);
- GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
- chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vdconv");
+ GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
+ chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
if (chain->conv == NULL) {
- post_missing_element_message (playsink, "ffmpegcolorspace");
+ post_missing_element_message (playsink, COLORSPACE);
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
- "ffmpegcolorspace"), ("video rendering might fail"));
+ COLORSPACE), ("video rendering might fail"));
} else {
gst_bin_add (bin, chain->conv);
head = chain->conv;
}
}
+#ifdef GST_EXT_LINK_FIMCCONVERT
+static gint
+compare_name (GstElement * element, const gchar * name)
+{
+ gchar *str;
+ GST_OBJECT_LOCK (element);
+ str = g_strrstr(GST_ELEMENT_NAME (element), name);
+ GST_OBJECT_UNLOCK (element);
+
+ if (str)
+ return 0;
+ else
+ return -1;
+}
+#endif
+
/* make the element (bin) that contains the elements needed to perform
* video display.
*
GstBin *bin;
GstPad *pad;
GstElement *head = NULL, *prev = NULL, *elem = NULL;
+ GstPlugin *p = NULL;
chain = g_new0 (GstPlayVideoChain, 1);
chain->chain.playsink = playsink;
}
/* find ts-offset element */
- chain->ts_offset =
+ gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
- G_TYPE_INT64);
+ G_TYPE_INT64));
/* 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 */
gst_object_ref_sink (bin);
gst_bin_add (bin, chain->sink);
+#ifndef GST_EXT_XV_ENHANCEMENT
/* decouple decoder from sink, this improves playback quite a lot since the
* decoder can continue while the sink blocks for synchronisation. We don't
* need a lot of buffers as this consumes a lot of memory and we don't want
gst_bin_add (bin, chain->queue);
head = prev = chain->queue;
}
+#endif
- if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
- GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
- chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
- if (chain->conv == NULL) {
- post_missing_element_message (playsink, "ffmpegcolorspace");
- GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "ffmpegcolorspace"), ("video rendering might fail"));
- } else {
- gst_bin_add (bin, chain->conv);
- if (prev) {
- if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
- GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
- goto link_failed;
- } else {
- head = chain->conv;
- }
- prev = chain->conv;
- }
-
- GST_DEBUG_OBJECT (playsink, "creating videoscale");
- chain->scale = gst_element_factory_make ("videoscale", "vscale");
- if (chain->scale == NULL) {
- post_missing_element_message (playsink, "videoscale");
- GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "videoscale"), ("possibly a liboil version mismatch?"));
+ if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
+ GST_DEBUG_OBJECT (playsink, "creating videoconverter");
+#ifdef GST_EXT_LINK_FIMCCONVERT
+ GstIterator *iter = gst_bin_iterate_recurse (gst_element_get_parent(playsink));
+ gpointer e = gst_iterator_find_custom (iter, (GCompareFunc) compare_name, "omx");
+ gst_iterator_free (iter);
+ if (e) {
+ chain->conv = gst_element_factory_make ("fimcconvert", "vconv");
+ gst_object_unref(p);
+ } else
+ chain->conv = gst_element_factory_make (COLORSPACE, "vconv");
+#else
+ chain->conv = g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
+#endif
+ gst_bin_add (bin, chain->conv);
+ if (prev) {
+ if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+ goto link_failed;
} else {
- /* Add black borders if necessary to keep the DAR */
- g_object_set (chain->scale, "add-borders", TRUE, NULL);
- gst_bin_add (bin, chain->scale);
- if (prev) {
- if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
- GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
- goto link_failed;
- } else {
- head = chain->scale;
- }
- prev = chain->scale;
+ head = chain->conv;
}
+ prev = chain->conv;
}
if (prev) {
chain = playsink->videochain;
+ chain->chain.raw = raw;
+
/* 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 FALSE;
/* find ts-offset element */
- chain->ts_offset =
+
+ gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
- G_TYPE_INT64);
+ G_TYPE_INT64));
/* if we can disable async behaviour of the sink, we can avoid adding a
* queue for the audio chain. */
}
/* make an element for playback of video with subtitles embedded.
+ * Only used for *raw* video streams.
*
* +--------------------------------------------+
* | tbin |
chain->queue = NULL;
}
/* try to set sync to true but it's no biggie when we can't */
- if ((elem =
+ if (chain->sink && (elem =
gst_play_sink_find_property_sinks (playsink, chain->sink,
"sync", G_TYPE_BOOLEAN)))
g_object_set (elem, "sync", TRUE, NULL);
"max-size-bytes", 0, "max-size-time", (gint64) 0,
"silent", TRUE, NULL);
gst_bin_add (bin, element);
- gst_element_link_pads_full (element, "src", chain->overlay,
- "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
- textsinkpad = gst_element_get_static_pad (element, "sink");
- srcpad = gst_element_get_static_pad (chain->overlay, "src");
+ if (gst_element_link_pads_full (element, "src", chain->overlay,
+ "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
+ textsinkpad = gst_element_get_static_pad (element, "sink");
+ srcpad = gst_element_get_static_pad (chain->overlay, "src");
+ } else {
+ gst_bin_remove (bin, chain->sink);
+ gst_bin_remove (bin, chain->overlay);
+ chain->sink = NULL;
+ chain->overlay = NULL;
+ gst_object_unref (videosinkpad);
+ videosinkpad = NULL;
+ }
}
}
}
}
/* find ts-offset element */
- chain->ts_offset =
+ gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
- G_TYPE_INT64);
+ G_TYPE_INT64));
/* check if the sink, or something within the sink, has the volume property.
* If it does we don't need to add a volume element. */
chain->sink_volume = FALSE;
}
- if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
- GST_DEBUG_OBJECT (playsink, "creating audioconvert");
- chain->conv = gst_element_factory_make ("audioconvert", "aconv");
- if (chain->conv == NULL) {
- post_missing_element_message (playsink, "audioconvert");
- GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "audioconvert"), ("possibly a liboil version mismatch?"));
- } else {
- gst_bin_add (bin, chain->conv);
- if (prev) {
- if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
- GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
- goto link_failed;
- } else {
- head = chain->conv;
- }
- prev = chain->conv;
- }
-
- GST_DEBUG_OBJECT (playsink, "creating audioresample");
- chain->resample = gst_element_factory_make ("audioresample", "aresample");
- if (chain->resample == NULL) {
- post_missing_element_message (playsink, "audioresample");
- GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "audioresample"), ("possibly a liboil version mismatch?"));
+ if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
+ && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
+ gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
+ gboolean use_volume =
+ !have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME;
+ GST_DEBUG_OBJECT (playsink,
+ "creating audioconvert with use-converters %d, use-volume %d",
+ use_converters, use_volume);
+ chain->conv =
+ g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
+ "use-converters", use_converters, "use-volume", use_volume, NULL);
+ gst_bin_add (bin, chain->conv);
+ if (prev) {
+ if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
+ GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+ goto link_failed;
} else {
- gst_bin_add (bin, chain->resample);
- if (prev) {
- if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
- GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
- goto link_failed;
- } else {
- head = chain->resample;
- }
- prev = chain->resample;
+ head = chain->conv;
}
+ prev = chain->conv;
if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
- GST_DEBUG_OBJECT (playsink, "creating volume");
- chain->volume = gst_element_factory_make ("volume", "volume");
- if (chain->volume == NULL) {
- post_missing_element_message (playsink, "volume");
- GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "volume"), ("possibly a liboil version mismatch?"));
- } else {
+ GstPlaySinkAudioConvert *conv =
+ GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
+
+ if (conv->volume) {
+ chain->volume = conv->volume;
have_volume = TRUE;
g_signal_connect (chain->volume, "notify::volume",
g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
NULL);
g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
- gst_bin_add (bin, chain->volume);
-
- if (prev) {
- if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
- GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
- goto link_failed;
- } else {
- head = chain->volume;
- }
- prev = chain->volume;
}
}
}
chain = playsink->audiochain;
+ chain->chain.raw = raw;
+
/* 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 FALSE;
/* find ts-offset element */
- chain->ts_offset =
+ gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
- G_TYPE_INT64);
+ G_TYPE_INT64));
/* check if the sink, or something within the sink, has the volume property.
* If it does we don't need to add a volume element. */
g_signal_connect (chain->mute, "notify::mute",
G_CALLBACK (notify_mute_cb), playsink);
}
+
+ g_object_set (chain->conv, "use-volume", FALSE, NULL);
} else {
+ GstPlaySinkAudioConvert *conv =
+ GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
+
/* no volume, we need to add a volume element when we can */
+ g_object_set (chain->conv, "use-volume", TRUE, NULL);
GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
- if (!raw) {
- GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
- disconnect_chain (chain, playsink);
- chain->volume = NULL;
- chain->mute = NULL;
- } else {
- /* both last and current chain are raw audio, there should be a volume
- * element already, unless the sink changed from one with a volume
- * property to one that hasn't got a volume property, in which case we
- * re-generate the chain */
- if (chain->volume == NULL) {
- GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
- /* undo background state change done earlier */
- gst_element_set_state (chain->sink, GST_STATE_NULL);
- return FALSE;
- }
+ /* Disconnect signals */
+ disconnect_chain (chain, playsink);
+
+ if (conv->volume) {
+ chain->volume = conv->volume;
+ chain->mute = chain->volume;
- GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
+ g_signal_connect (chain->volume, "notify::volume",
+ G_CALLBACK (notify_volume_cb), playsink);
+
+ g_signal_connect (chain->mute, "notify::mute",
+ G_CALLBACK (notify_mute_cb), playsink);
+
+ /* configure with the latest volume and mute */
+ g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
+ g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
}
+
+ GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
}
return TRUE;
}
add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
+ if (playsink->videochain->ts_offset)
+ gst_object_unref (playsink->videochain->ts_offset);
playsink->videochain->ts_offset = NULL;
}
disconnect_chain (playsink->audiochain, playsink);
playsink->audiochain->volume = NULL;
playsink->audiochain->mute = NULL;
+ if (playsink->audiochain->ts_offset)
+ gst_object_unref (playsink->audiochain->ts_offset);
playsink->audiochain->ts_offset = NULL;
free_chain ((GstPlayChain *) playsink->audiochain);
playsink->audiochain = NULL;
disconnect_chain (playsink->audiochain, playsink);
playsink->audiochain->volume = NULL;
playsink->audiochain->mute = NULL;
+ if (playsink->audiochain->ts_offset)
+ gst_object_unref (playsink->audiochain->ts_offset);
playsink->audiochain->ts_offset = NULL;
}
add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
* on the bus or not. It's not like it's a critical issue regarding
* playsink behaviour. */
GST_ERROR ("Error converting frame: %s", err->message);
+ g_error_free (err);
}
result = temp;
}
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 void
+sinkpad_blocked_cb (GstPad * blockedpad, gboolean blocked, gpointer user_data)
+{
+ GstPlaySink *playsink = (GstPlaySink *) user_data;
+ GstPad *pad;
+
+ GST_PLAY_SINK_LOCK (playsink);
+
+ pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
+ if (pad == playsink->video_pad) {
+ playsink->video_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Video pad blocked: %d", blocked);
+ if (!blocked) {
+ PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO_RAW);
+ PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
+ }
+ } else if (pad == playsink->audio_pad) {
+ playsink->audio_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Audio pad blocked: %d", blocked);
+ if (!blocked) {
+ PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO_RAW);
+ PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
+ }
+ } else if (pad == playsink->text_pad) {
+ playsink->text_pad_blocked = blocked;
+ GST_DEBUG_OBJECT (pad, "Text pad blocked: %d", blocked);
+ if (!blocked)
+ PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
+ }
+
+ if (!blocked) {
+ gst_object_unref (pad);
+ GST_PLAY_SINK_UNLOCK (playsink);
+ return;
+ }
+
+ /* We reconfigure when for ALL streams:
+ * * there isn't a pad
+ * * OR the pad is blocked
+ * * OR there are no pending blocks on that pad
+ */
+
+ if ((!playsink->video_pad || playsink->video_pad_blocked
+ || !PENDING_VIDEO_BLOCK (playsink)) && (!playsink->audio_pad
+ || playsink->audio_pad_blocked || !PENDING_AUDIO_BLOCK (playsink))
+ && (!playsink->text_pad || playsink->text_pad_blocked
+ || !PENDING_TEXT_BLOCK (playsink))) {
+ 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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
+
+ gst_pad_set_blocked_async_full (blockpad, TRUE, sinkpad_blocked_cb,
+ gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
+ PENDING_FLAG_SET (playsink, type);
+ 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 {
if (playsink->textchain && playsink->textchain->sink) {
gst_event_ref (event);
if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
- GST_DEBUG_OBJECT (playsink, "Sent event succesfully to text sink");
+ GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
} else {
GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
}
if (playsink->videochain) {
gst_event_ref (event);
if ((res = gst_element_send_event (playsink->videochain->chain.bin, event))) {
- GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
+ GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
goto done;
}
GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
if (playsink->audiochain) {
gst_event_ref (event);
if ((res = gst_element_send_event (playsink->audiochain->chain.bin, event))) {
- GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
+ GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
goto done;
}
GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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 =
+ GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_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
disconnect_chain (playsink->audiochain, playsink);
playsink->audiochain->volume = NULL;
playsink->audiochain->mute = NULL;
+ }
+
+ if (playsink->audiochain && playsink->audiochain->ts_offset) {
+ gst_object_unref (playsink->audiochain->ts_offset);
playsink->audiochain->ts_offset = NULL;
}
+
+ if (playsink->videochain && playsink->videochain->ts_offset) {
+ gst_object_unref (playsink->videochain->ts_offset);
+ playsink->videochain->ts_offset = NULL;
+ }
ret = GST_STATE_CHANGE_SUCCESS;
break;
default:
case PROP_AV_OFFSET:
gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
break;
+ case PROP_VIDEO_SINK:
+ gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
+ g_value_get_object (value));
+ break;
+ case PROP_AUDIO_SINK:
+ gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
+ g_value_get_object (value));
+ break;
+ case PROP_TEXT_SINK:
+ gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
+ g_value_get_object (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
break;
case PROP_AV_OFFSET:
g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
break;
+ case PROP_VIDEO_SINK:
+ g_value_take_object (value, gst_play_sink_get_sink (playsink,
+ GST_PLAY_SINK_TYPE_VIDEO));
+ break;
+ case PROP_AUDIO_SINK:
+ g_value_take_object (value, gst_play_sink_get_sink (playsink,
+ GST_PLAY_SINK_TYPE_AUDIO));
+ break;
+ case PROP_TEXT_SINK:
+ g_value_take_object (value, gst_play_sink_get_sink (playsink,
+ GST_PLAY_SINK_TYPE_TEXT));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
break;