summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gst/playback/gstplaybin2.c17
-rw-r--r--gst/playback/gstplaysink.c261
-rw-r--r--gst/playback/gstplaysink.h3
3 files changed, 200 insertions, 81 deletions
diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c
index 5d15cdef6..cd6a27683 100644
--- a/gst/playback/gstplaybin2.c
+++ b/gst/playback/gstplaybin2.c
@@ -411,6 +411,7 @@ struct _GstPlayBinClass
#define DEFAULT_AUDIO_SINK NULL
#define DEFAULT_VIDEO_SINK NULL
#define DEFAULT_VIS_PLUGIN NULL
+#define DEFAULT_TEXT_SINK NULL
#define DEFAULT_VOLUME 1.0
#define DEFAULT_MUTE FALSE
#define DEFAULT_FRAME NULL
@@ -436,13 +437,15 @@ enum
PROP_AUDIO_SINK,
PROP_VIDEO_SINK,
PROP_VIS_PLUGIN,
+ PROP_TEXT_SINK,
PROP_VOLUME,
PROP_MUTE,
PROP_FRAME,
PROP_FONT_DESC,
PROP_CONNECTION_SPEED,
PROP_BUFFER_SIZE,
- PROP_BUFFER_DURATION
+ PROP_BUFFER_DURATION,
+ PROP_LAST
};
/* signals */
@@ -690,6 +693,10 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
g_param_spec_object ("vis-plugin", "Vis plugin",
"the visualization element to use (NULL = default)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
+ g_param_spec_object ("text-sink", "Text plugin",
+ "the text output element to use (NULL = default textoverlay)",
+ GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_klass, PROP_VOLUME,
g_param_spec_double ("volume", "Volume", "The audio volume",
@@ -1424,6 +1431,10 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
gst_play_sink_set_vis_plugin (playbin->playsink,
g_value_get_object (value));
break;
+ case PROP_TEXT_SINK:
+ gst_play_sink_set_text_sink (playbin->playsink,
+ g_value_get_object (value));
+ break;
case PROP_VOLUME:
gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
break;
@@ -1558,6 +1569,10 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
g_value_set_object (value,
gst_play_sink_get_vis_plugin (playbin->playsink));
break;
+ case PROP_TEXT_SINK:
+ g_value_set_object (value,
+ gst_play_sink_get_text_sink (playbin->playsink));
+ break;
case PROP_VOLUME:
g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
break;
diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c
index 7484079c5..33644a501 100644
--- a/gst/playback/gstplaysink.c
+++ b/gst/playback/gstplaysink.c
@@ -95,6 +95,7 @@ typedef struct
GstPad *textsinkpad;
GstPad *srcpad; /* outgoing srcpad, used to connect to the next
* chain */
+ GstElement *sink; /* custom sink to receive subtitle buffers */
} GstPlayTextChain;
#define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
@@ -130,6 +131,7 @@ struct _GstPlaySink
GstElement *audio_sink;
GstElement *video_sink;
GstElement *visualisation;
+ GstElement *text_sink;
gfloat volume;
gboolean mute;
gchar *font_desc; /* font description */
@@ -212,6 +214,7 @@ gst_play_sink_init (GstPlaySink * playsink)
playsink->video_sink = NULL;
playsink->audio_sink = NULL;
playsink->visualisation = NULL;
+ playsink->text_sink = NULL;
playsink->volume = 1.0;
playsink->font_desc = NULL;
playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
@@ -251,6 +254,11 @@ gst_play_sink_dispose (GObject * object)
gst_object_unref (playsink->visualisation);
playsink->visualisation = NULL;
}
+ if (playsink->text_sink != NULL) {
+ gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
+ gst_object_unref (playsink->text_sink);
+ playsink->text_sink = NULL;
+ }
free_chain ((GstPlayChain *) playsink->videochain);
playsink->videochain = NULL;
@@ -261,7 +269,6 @@ gst_play_sink_dispose (GObject * object)
free_chain ((GstPlayChain *) playsink->textchain);
playsink->textchain = NULL;
-
if (playsink->audio_tee_sink) {
gst_object_unref (playsink->audio_tee_sink);
playsink->audio_tee_sink = NULL;
@@ -486,6 +493,41 @@ gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
}
void
+gst_play_sink_set_text_sink (GstPlaySink * playsink, GstElement * sink)
+{
+ GST_PLAY_SINK_LOCK (playsink);
+ if (playsink->text_sink)
+ gst_object_unref (playsink->text_sink);
+
+ if (sink) {
+ gst_object_ref (sink);
+ gst_object_sink (sink);
+ }
+ playsink->text_sink = sink;
+ GST_PLAY_SINK_UNLOCK (playsink);
+}
+
+GstElement *
+gst_play_sink_get_text_sink (GstPlaySink * playsink)
+{
+ GstElement *result = NULL;
+ GstPlayTextChain *chain;
+
+ GST_PLAY_SINK_LOCK (playsink);
+ if ((chain = (GstPlayTextChain *) playsink->textchain)) {
+ /* we have an active chain, get the sink */
+ if (chain->sink)
+ result = gst_object_ref (chain->sink);
+ }
+ /* nothing found, return last configured sink */
+ if (result == NULL && playsink->text_sink)
+ result = gst_object_ref (playsink->text_sink);
+ GST_PLAY_SINK_UNLOCK (playsink);
+
+ return result;
+}
+
+void
gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
{
GstPlayAudioChain *chain;
@@ -663,7 +705,9 @@ find_property_sink (GstElement * element, const gchar * name)
return res;
}
-/* find a sink in the hierarchy with a property named @name */
+/* find a sink in the hierarchy with a property named @name. This function does
+ * not increase the refcount of the returned object and thus remains valid as
+ * long as the bin is valid. */
static GstElement *
gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
const gchar * name)
@@ -671,16 +715,16 @@ gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
GstElement *result = NULL;
GstIterator *it;
- if (GST_IS_BIN (obj)) {
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
+ result = obj;
+ } else if (GST_IS_BIN (obj)) {
it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
result = gst_iterator_find_custom (it,
(GCompareFunc) find_property_sink, (gpointer) name);
gst_iterator_free (it);
- } else {
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
- result = obj;
- gst_object_ref (obj);
- }
+ /* we don't need the extra ref */
+ if (result)
+ gst_object_unref (result);
}
return result;
}
@@ -751,12 +795,12 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
goto no_sinks;
/* if we can disable async behaviour of the sink, we can avoid adding a
- * queue for the audio chain. We can't use the deep property here because the
- * sink might change it's internal sink element later. */
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "async")) {
- GST_DEBUG_OBJECT (playsink, "setting async property to %d on video sink",
- async);
- g_object_set (chain->sink, "async", async, NULL);
+ * queue for the audio chain. */
+ elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
+ if (elem) {
+ GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
+ async, GST_ELEMENT_NAME (elem));
+ g_object_set (elem, "async", async, NULL);
chain->async = async;
} else {
GST_DEBUG_OBJECT (playsink, "no async property on the sink");
@@ -788,7 +832,7 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
post_missing_element_message (playsink, "ffmpegcolorspace");
GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
(_("Missing element '%s' - check your GStreamer installation."),
- "ffmpegcolorspace"), (NULL));
+ "ffmpegcolorspace"), ("video rendering might fail"));
} else {
gst_bin_add (bin, chain->conv);
if (!gst_element_link_pads (prev, "src", chain->conv, "sink"))
@@ -851,7 +895,7 @@ link_failed:
* | tbin +-------------+ |
* | +-----+ | textoverlay | |
* | | csp | +--video_sink | |
- * video_sink-sink src+ +-text_sink src--+ |
+ * sink-------sink src+ +-text_sink src--+ |
* | +-----+ | +-------------+ +-- src
* text_sink-------------+ |
* +----------------------------------------------+
@@ -861,7 +905,8 @@ gen_text_chain (GstPlaySink * playsink)
{
GstPlayTextChain *chain;
GstBin *bin;
- GstPad *pad;
+ GstElement *elem;
+ GstPad *videosinkpad, *textsinkpad, *srcpad;
chain = g_new0 (GstPlayTextChain, 1);
chain->chain.playsink = playsink;
@@ -873,65 +918,127 @@ gen_text_chain (GstPlaySink * playsink)
gst_object_ref (bin);
gst_object_sink (bin);
- chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
- if (chain->conv == NULL)
- goto no_colorspace;
- gst_bin_add (bin, chain->conv);
+ videosinkpad = textsinkpad = srcpad = NULL;
- chain->overlay = gst_element_factory_make ("textoverlay", "overlay");
- if (chain->overlay == NULL)
- goto no_overlay;
- gst_bin_add (bin, chain->overlay);
-
- /* Set some parameters */
- g_object_set (G_OBJECT (chain->overlay),
- "halign", "center", "valign", "bottom", NULL);
- if (playsink->font_desc) {
- g_object_set (G_OBJECT (chain->overlay), "font-desc", playsink->font_desc,
- NULL);
+ /* first try to hook the text pad to the custom sink */
+ if (playsink->text_sink) {
+ GST_DEBUG_OBJECT (playsink, "trying configured textsink");
+ elem = gst_object_ref (playsink->text_sink);
+ chain->sink = try_element (playsink, elem);
+ if (chain->sink) {
+ elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "async");
+ if (elem) {
+ g_object_set (elem, "async", TRUE, NULL);
+ /* we have a custom sink, this will be our textsinkpad */
+ textsinkpad = gst_element_get_static_pad (chain->sink, "sink");
+ if (textsinkpad) {
+ /* we're all fine now and we can add the sink to the chain */
+ GST_DEBUG_OBJECT (playsink, "adding custom text sink");
+ gst_bin_add (bin, chain->sink);
+ } else {
+ GST_WARNING_OBJECT (playsink,
+ "can't find a sink pad on custom text sink");
+ gst_object_unref (chain->sink);
+ chain->sink = NULL;
+ }
+ } else {
+ GST_WARNING_OBJECT (playsink,
+ "can't find async property in custom text sink");
+ }
+ }
+ if (textsinkpad == NULL) {
+ GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
+ (_("Custom text sink element is not usable.")),
+ ("fallback to default textoverlay"));
+ }
}
- g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL);
- /* Link */
- gst_element_link_pads (chain->conv, "src", chain->overlay, "video_sink");
+ if (textsinkpad == NULL) {
+ if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
+ /* no custom sink, try to setup the colorspace and textoverlay elements */
+ chain->conv = gst_element_factory_make ("ffmpegcolorspace", "tconv");
+ if (chain->conv == NULL) {
+ /* not really needed, it might work without colorspace */
+ post_missing_element_message (playsink, "ffmpegcolorspace");
+ GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
+ (_("Missing element '%s' - check your GStreamer installation."),
+ "ffmpegcolorspace"), ("subtitle rendering might fail"));
+ } else {
+ gst_bin_add (bin, chain->conv);
+ videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
+ }
+ }
- /* Add ghost pads on the subtitle bin */
- pad = gst_element_get_static_pad (chain->overlay, "text_sink");
- chain->textsinkpad = gst_ghost_pad_new ("text_sink", pad);
- gst_object_unref (pad);
- gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
+ chain->overlay = gst_element_factory_make ("textoverlay", "overlay");
+ if (chain->overlay == NULL) {
+ post_missing_element_message (playsink, "textoverlay");
+ GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
+ (_("Missing element '%s' - check your GStreamer installation."),
+ "textoverlay"), ("subtitle rendering disabled"));
+ } else {
+ gst_bin_add (bin, chain->overlay);
+
+ /* Set some parameters */
+ g_object_set (G_OBJECT (chain->overlay),
+ "halign", "center", "valign", "bottom", NULL);
+ if (playsink->font_desc) {
+ g_object_set (G_OBJECT (chain->overlay), "font-desc",
+ playsink->font_desc, NULL);
+ }
+ g_object_set (G_OBJECT (chain->overlay), "wait-text", FALSE, NULL);
- pad = gst_element_get_static_pad (chain->conv, "sink");
- chain->videosinkpad = gst_ghost_pad_new ("sink", pad);
- gst_object_unref (pad);
- gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
+ textsinkpad = gst_element_get_static_pad (chain->overlay, "text_sink");
- pad = gst_element_get_static_pad (chain->overlay, "src");
- chain->srcpad = gst_ghost_pad_new ("src", pad);
- gst_object_unref (pad);
- gst_element_add_pad (chain->chain.bin, chain->srcpad);
+ srcpad = gst_element_get_static_pad (chain->overlay, "src");
- return chain;
+ if (videosinkpad) {
+ /* if we had a videosinkpad, we had a converter and we can link it, we
+ * know that this will work */
+ gst_element_link_pads (chain->conv, "src", chain->overlay,
+ "video_sink");
+ } else {
+ /* no videopad, expose our own video pad then */
+ videosinkpad =
+ gst_element_get_static_pad (chain->overlay, "video_sink");
+ }
+ }
+ }
- /* ERRORS */
-no_colorspace:
- {
- post_missing_element_message (playsink, "ffmpegcolorspace");
- GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "ffmpegcolorspace"), (NULL));
- free_chain ((GstPlayChain *) chain);
- return NULL;
+ if (videosinkpad == NULL) {
+ /* if we still don't have a videosink, we don't have a converter nor an
+ * overlay. the only thing we can do is insert an identity and ghost the src
+ * and sink pads. */
+ chain->conv = gst_element_factory_make ("identity", "tidentity");
+ gst_bin_add (bin, chain->conv);
+ srcpad = gst_element_get_static_pad (chain->conv, "src");
+ videosinkpad = gst_element_get_static_pad (chain->conv, "sink");
+ } else {
+ /* we have a videosink but maybe not a srcpad because there was no
+ * overlay */
+ if (srcpad == NULL) {
+ /* ghost the source pad of the converter then */
+ srcpad = gst_element_get_static_pad (chain->conv, "src");
+ }
}
-no_overlay:
- {
- post_missing_element_message (playsink, "textoverlay");
- GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
- (_("Missing element '%s' - check your GStreamer installation."),
- "textoverlay"), (NULL));
- free_chain ((GstPlayChain *) chain);
- return NULL;
+
+ /* expose the ghostpads */
+ if (videosinkpad) {
+ chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
+ gst_object_unref (videosinkpad);
+ gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
}
+ if (textsinkpad) {
+ chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
+ gst_object_unref (textsinkpad);
+ gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
+ }
+ if (srcpad) {
+ chain->srcpad = gst_ghost_pad_new ("src", srcpad);
+ gst_object_unref (srcpad);
+ gst_element_add_pad (chain->chain.bin, chain->srcpad);
+ }
+
+ return chain;
}
/* make the chain that contains the elements needed to perform
@@ -982,7 +1089,6 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
if (chain->sink == NULL)
goto no_sinks;
-
chain->chain.bin = gst_bin_new ("abin");
bin = GST_BIN_CAST (chain->chain.bin);
gst_object_ref (bin);
@@ -1003,26 +1109,21 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
/* 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. */
- elem = NULL;
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "volume")) {
- elem = gst_object_ref (chain->sink);
- } else if (GST_IS_BIN (chain->sink)) {
- elem = gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
- }
- if (elem) {
+ chain->volume =
+ gst_play_sink_find_property_sinks (playsink, chain->sink, "volume");
+ if (chain->volume) {
GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
have_volume = TRUE;
/* use the sink to control the volume */
- chain->volume = elem;
- g_object_set (G_OBJECT (elem), "volume", playsink->volume, NULL);
+ g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
/* if the sink also has a mute property we can use this as well. We'll only
* use the mute property if there is a volume property. We can simulate the
* mute with the volume otherwise. */
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (elem), "mute")) {
+ chain->mute =
+ gst_play_sink_find_property_sinks (playsink, chain->sink, "mute");
+ if (chain->mute) {
GST_DEBUG_OBJECT (playsink, "the sink has a mute property");
- chain->mute = elem;
}
- gst_object_unref (elem);
} else {
/* no volume, we need to add a volume element when we can */
GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
@@ -1108,7 +1209,7 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
/* post a warning if we have no way to configure the volume */
if (!have_volume) {
GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
- (_("No volume control found")), ("No volume control found"));
+ (_("No volume control found")), ("Volume/mute is not available"));
}
/* and ghost the sinkpad of the headmost element */
diff --git a/gst/playback/gstplaysink.h b/gst/playback/gstplaysink.h
index 50585441c..aa6252145 100644
--- a/gst/playback/gstplaysink.h
+++ b/gst/playback/gstplaysink.h
@@ -76,6 +76,9 @@ GstElement * gst_play_sink_get_audio_sink (GstPlaySink * playsink);
void gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis);
GstElement * gst_play_sink_get_vis_plugin (GstPlaySink * playsink);
+void gst_play_sink_set_text_sink (GstPlaySink * playsink, GstElement * sink);
+GstElement * gst_play_sink_get_text_sink (GstPlaySink * playsink);
+
void gst_play_sink_set_volume (GstPlaySink *playsink, gdouble volume);
gdouble gst_play_sink_get_volume (GstPlaySink *playsink);