From fd27e34582fb8785e741785e265380497bf15e14 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 17 Oct 2011 16:55:30 +0000 Subject: [PATCH] playsink: refactor the converter bins since they are almost identical https://bugzilla.gnome.org/show_bug.cgi?id=661262 --- gst/playback/Makefile.am | 2 + gst/playback/gstplaysinkaudioconvert.c | 496 ++---------------------------- gst/playback/gstplaysinkaudioconvert.h | 35 +-- gst/playback/gstplaysinkconvertbin.c | 544 +++++++++++++++++++++++++++++++++ gst/playback/gstplaysinkconvertbin.h | 96 ++++++ gst/playback/gstplaysinkvideoconvert.c | 489 ++--------------------------- gst/playback/gstplaysinkvideoconvert.h | 34 +-- 7 files changed, 706 insertions(+), 990 deletions(-) create mode 100644 gst/playback/gstplaysinkconvertbin.c create mode 100644 gst/playback/gstplaysinkconvertbin.h diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am index 9c8273f..3ee27e4 100644 --- a/gst/playback/Makefile.am +++ b/gst/playback/Makefile.am @@ -22,6 +22,7 @@ libgstplaybin_la_SOURCES = \ gstsubtitleoverlay.c \ gstplaysinkvideoconvert.c \ gstplaysinkaudioconvert.c \ + gstplaysinkconvertbin.c \ gststreamsynchronizer.c nodist_libgstplaybin_la_SOURCES = $(built_sources) @@ -63,6 +64,7 @@ noinst_HEADERS = \ gstsubtitleoverlay.h \ gstplaysinkvideoconvert.h \ gstplaysinkaudioconvert.h \ + gstplaysinkconvertbin.h \ gststreamsynchronizer.h BUILT_SOURCES = $(built_headers) $(built_sources) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 9169c81..b470939 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -32,378 +32,51 @@ GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug); #define parent_class gst_play_sink_audio_convert_parent_class G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert, - GST_TYPE_BIN); - -static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); + GST_TYPE_PLAY_SINK_CONVERT_BIN); static gboolean -is_raw_caps (GstCaps * caps) -{ - gint i, n; - GstStructure *s; - const gchar *name; - - n = gst_caps_get_size (caps); - for (i = 0; i < n; i++) { - s = gst_caps_get_structure (caps, i); - name = gst_structure_get_name (s); - if (!g_str_has_prefix (name, "audio/x-raw")) - return FALSE; - } - - return TRUE; -} - -static void -post_missing_element_message (GstPlaySinkAudioConvert * self, - const gchar * name) -{ - GstMessage *msg; - - msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name); - gst_element_post_message (GST_ELEMENT_CAST (self), msg); -} - -static void -distribute_running_time (GstElement * element, const GstSegment * segment) -{ - GstEvent *event; - GstPad *pad; - - pad = gst_element_get_static_pad (element, "sink"); - - if (segment->accum) { - event = gst_event_new_new_segment_full (FALSE, segment->rate, - segment->applied_rate, segment->format, 0, segment->accum, 0); - gst_pad_send_event (pad, event); - } - - event = gst_event_new_new_segment_full (FALSE, segment->rate, - segment->applied_rate, segment->format, - segment->start, segment->stop, segment->time); - gst_pad_send_event (pad, event); - - gst_object_unref (pad); -} - -static void -gst_play_sink_audio_convert_add_identity (GstPlaySinkAudioConvert * self) -{ - self->identity = gst_element_factory_make ("identity", "identity"); - if (self->identity == NULL) { - post_missing_element_message (self, "identity"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "identity"), ("audio rendering might fail")); - } else { - gst_bin_add (GST_BIN_CAST (self), self->identity); - gst_element_sync_state_with_parent (self->identity); - distribute_running_time (self->identity, &self->segment); - } -} - -static void -gst_play_sink_audio_convert_set_targets (GstPlaySinkAudioConvert * self, - GstElement * head, GstElement * tail) -{ - GstPad *pad; - - pad = gst_element_get_static_pad (head, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (tail, "src"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); - gst_object_unref (pad); -} - -static void -pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self) +gst_play_sink_audio_convert_add_conversion_elements (GstPlaySinkConvertBin * + cbin) { - GstPad *peer; - GstCaps *caps; - gboolean raw; - GstElement *head = NULL, *prev = NULL; - GstBin *bin = GST_BIN_CAST (self); - - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - self->sink_proxypad_blocked = blocked; - GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked); - if (!blocked) - goto done; - - /* There must be a peer at this point */ - peer = gst_pad_get_peer (self->sinkpad); - caps = gst_pad_get_negotiated_caps (peer); - if (!caps) - caps = gst_pad_get_caps_reffed (peer); - gst_object_unref (peer); - - raw = is_raw_caps (caps); - GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw); - gst_caps_unref (caps); + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT (cbin); + GstElement *el, *prev = NULL; - if (raw == self->raw) - goto unblock; - self->raw = raw; + g_assert (cbin->conversion_elements == NULL); - if (raw) { - GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline"); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - - if (self->use_converters) { - self->conv = gst_element_factory_make ("audioconvert", "conv"); - if (self->conv == NULL) { - post_missing_element_message (self, "audioconvert"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioconvert"), ("audio rendering might fail")); - } else { - gst_bin_add (bin, self->conv); - gst_element_sync_state_with_parent (self->conv); - distribute_running_time (self->conv, &self->segment); - prev = head = self->conv; - } - - self->resample = gst_element_factory_make ("audioresample", "resample"); - if (self->resample == NULL) { - post_missing_element_message (self, "audioresample"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "audioresample"), ("possibly a liboil version mismatch?")); - } else { - gst_bin_add (bin, self->resample); - gst_element_sync_state_with_parent (self->resample); - distribute_running_time (self->resample, &self->segment); - if (prev) { - if (!gst_element_link_pads_full (prev, "src", self->resample, "sink", - GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) - goto link_failed; - } else { - head = self->resample; - } - prev = self->resample; - } + if (self->use_converters) { + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "audioconvert", "conv"); + if (el) { + prev = el; } - if (self->use_volume && self->volume) { - gst_bin_add (bin, gst_object_ref (self->volume)); - gst_element_sync_state_with_parent (self->volume); - distribute_running_time (self->volume, &self->segment); + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "audioresample", "resample"); + if (el) { + if (prev) { - if (!gst_element_link_pads_full (prev, "src", self->volume, "sink", + if (!gst_element_link_pads_full (prev, "src", el, "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) goto link_failed; - } else { - head = self->volume; - } - prev = self->volume; - } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); - } else { - - GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline"); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - - if (self->conv) { - gst_element_set_state (self->conv, GST_STATE_NULL); - gst_bin_remove (bin, self->conv); - self->conv = NULL; - } - if (self->resample) { - gst_element_set_state (self->resample, GST_STATE_NULL); - gst_bin_remove (bin, self->resample); - self->resample = NULL; - } - if (self->volume) { - gst_element_set_state (self->volume, GST_STATE_NULL); - if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) { - gst_bin_remove (GST_BIN_CAST (self), self->volume); } + prev = el; } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); } - g_assert ((head != NULL) == (prev != NULL)); - - /* to make things simple and avoid counterintuitive pad juggling, - ensure there is at least one element in the list */ - if (!head) { - gst_play_sink_audio_convert_add_identity (self); - prev = head = self->identity; - } - - gst_play_sink_audio_convert_set_targets (self, head, prev); - -unblock: - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - -done: - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - return; - -link_failed: - { - GST_ELEMENT_ERROR (self, CORE, PAD, - (NULL), ("Failed to configure the audio converter.")); - - /* use a simple identity, better than nothing */ - gst_play_sink_audio_convert_add_identity (self); - gst_play_sink_audio_convert_set_targets (self, self->identity, - self->identity); - - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - return; - } -} - -static gboolean -gst_play_sink_audio_convert_sink_event (GstPad * pad, GstEvent * event) -{ - GstPlaySinkAudioConvert *self = - GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad)); - gboolean ret; - - ret = gst_proxy_pad_event_default (pad, gst_event_ref (event)); - - if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { - gboolean update; - gdouble rate, applied_rate; - GstFormat format; - gint64 start, stop, position; - - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &position); - - GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT, - &self->segment); - gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate, - format, start, stop, position); - GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT, - &self->segment); - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - GST_DEBUG_OBJECT (self, "Resetting segment"); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - } - - gst_event_unref (event); - gst_object_unref (self); - - return ret; -} - -static gboolean -gst_play_sink_audio_convert_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstPlaySinkAudioConvert *self = - GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad)); - gboolean ret; - GstStructure *s; - const gchar *name; - gboolean reconfigure = FALSE; - - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - s = gst_caps_get_structure (caps, 0); - name = gst_structure_get_name (s); - - if (g_str_has_prefix (name, "audio/x-raw-")) { - if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { - GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); - reconfigure = TRUE; - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - } - } else { - if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { - GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw"); - reconfigure = TRUE; - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); + if (self->use_volume && self->volume) { + el = self->volume; + gst_play_sink_convert_bin_add_conversion_element (cbin, el); + if (prev) { + if (!gst_element_link_pads_full (prev, "src", el, "sink", + GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) + goto link_failed; } + prev = el; } + return TRUE; - /* Otherwise the setcaps below fails */ - if (reconfigure) { - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - } - - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - ret = gst_ghost_pad_setcaps_default (pad, caps); - - GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps, - ret); - - gst_object_unref (self); - - return ret; -} - -static GstCaps * -gst_play_sink_audio_convert_getcaps (GstPad * pad) -{ - GstPlaySinkAudioConvert *self = - GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad)); - GstCaps *ret; - GstPad *otherpad, *peer; - - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); - if (!otherpad) { - if (pad == self->srcpad) { - otherpad = self->sink_proxypad; - } else if (pad == self->sinkpad) { - otherpad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD - (self->sinkpad))); - } else { - GST_ERROR_OBJECT (pad, "Not one of our pads"); - } - } - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - - if (otherpad) { - peer = gst_pad_get_peer (otherpad); - if (peer) { - ret = gst_pad_get_caps_reffed (peer); - gst_object_unref (peer); - } else { - ret = gst_caps_new_any (); - } - gst_object_unref (otherpad); - } else { - GST_WARNING_OBJECT (self, "Could not traverse bin"); - ret = gst_caps_new_any (); - } - gst_object_unref (self); - - return ret; +link_failed: + return FALSE; } static void @@ -414,86 +87,9 @@ gst_play_sink_audio_convert_finalize (GObject * object) if (self->volume) gst_object_unref (self->volume); - gst_object_unref (self->sink_proxypad); - g_mutex_free (self->lock); - G_OBJECT_CLASS (parent_class)->finalize (object); } -static GstStateChangeReturn -gst_play_sink_audio_convert_change_state (GstElement * element, - GstStateChange transition) -{ - GstStateChangeReturn ret; - GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (element); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - if (gst_pad_is_blocked (self->sink_proxypad)) - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - if (self->conv) { - gst_element_set_state (self->conv, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->conv); - self->conv = NULL; - } - if (self->resample) { - gst_element_set_state (self->resample, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->resample); - self->resample = NULL; - } - if (self->volume) { - gst_element_set_state (self->volume, GST_STATE_NULL); - if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) { - gst_bin_remove (GST_BIN_CAST (self), self->volume); - } - } - if (!self->identity) { - gst_play_sink_audio_convert_add_identity (self); - } - gst_play_sink_audio_convert_set_targets (self, self->identity, - self->identity); - self->raw = FALSE; - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - if (!gst_pad_is_blocked (self->sink_proxypad)) - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } - break; - default: - break; - } - - return ret; -} - static void gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) { @@ -508,48 +104,20 @@ gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) gobject_class->finalize = gst_play_sink_audio_convert_finalize; - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&srctemplate)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&sinktemplate)); gst_element_class_set_details_simple (gstelement_class, "Player Sink Audio Converter", "Audio/Bin/Converter", "Convenience bin for audio conversion", "Sebastian Dröge "); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_change_state); } static void gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self) { - GstPadTemplate *templ; - - self->lock = g_mutex_new (); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - - templ = gst_static_pad_template_get (&sinktemplate); - self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ); - gst_pad_set_event_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_event)); - gst_pad_set_setcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_setcaps)); - gst_pad_set_getcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps)); - - self->sink_proxypad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad))); - - gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad); - gst_object_unref (templ); + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); - templ = gst_static_pad_template_get (&srctemplate); - self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ); - gst_pad_set_getcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps)); - gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); - gst_object_unref (templ); + cbin->audio = TRUE; + cbin->add_conversion_elements = + gst_play_sink_audio_convert_add_conversion_elements; /* FIXME: Only create this on demand but for now we need * it to always exist because of playsink's volume proxying diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h index e9c0ab3..5f80af6 100644 --- a/gst/playback/gstplaysinkaudioconvert.h +++ b/gst/playback/gstplaysinkaudioconvert.h @@ -18,6 +18,7 @@ */ #include +#include "gstplaysinkconvertbin.h" #ifndef __GST_PLAY_SINK_AUDIO_CONVERT_H__ #define __GST_PLAY_SINK_AUDIO_CONVERT_H__ @@ -36,42 +37,12 @@ G_BEGIN_DECLS #define GST_IS_PLAY_SINK_AUDIO_CONVERT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_AUDIO_CONVERT)) -#define GST_PLAY_SINK_AUDIO_CONVERT_LOCK(obj) G_STMT_START { \ - GST_LOG_OBJECT (obj, \ - "locking from thread %p", \ - g_thread_self ()); \ - g_mutex_lock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \ - GST_LOG_OBJECT (obj, \ - "locked from thread %p", \ - g_thread_self ()); \ -} G_STMT_END - -#define GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK(obj) G_STMT_START { \ - GST_LOG_OBJECT (obj, \ - "unlocking from thread %p", \ - g_thread_self ()); \ - g_mutex_unlock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \ -} G_STMT_END - typedef struct _GstPlaySinkAudioConvert GstPlaySinkAudioConvert; typedef struct _GstPlaySinkAudioConvertClass GstPlaySinkAudioConvertClass; struct _GstPlaySinkAudioConvert { - GstBin parent; - - /* < private > */ - GMutex *lock; - - GstPad *sinkpad, *sink_proxypad; - gboolean sink_proxypad_blocked; - GstSegment segment; - - GstPad *srcpad; - - gboolean raw; - GstElement *conv, *resample; - GstElement *identity; + GstPlaySinkConvertBin parent; /* < pseudo public > */ GstElement *volume; @@ -81,7 +52,7 @@ struct _GstPlaySinkAudioConvert struct _GstPlaySinkAudioConvertClass { - GstBinClass parent; + GstPlaySinkConvertBinClass parent; }; GType gst_play_sink_audio_convert_get_type (void); diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c new file mode 100644 index 0000000..cfd8dc8 --- /dev/null +++ b/gst/playback/gstplaysinkconvertbin.c @@ -0,0 +1,544 @@ +/* GStreamer + * Copyright (C) <2011> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstplaysinkconvertbin.h" + +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_convert_bin_debug); +#define GST_CAT_DEFAULT gst_play_sink_convert_bin_debug + +#define parent_class gst_play_sink_convert_bin_parent_class + +G_DEFINE_TYPE (GstPlaySinkConvertBin, gst_play_sink_convert_bin, GST_TYPE_BIN); + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static gboolean +is_raw_caps (GstCaps * caps) +{ + gint i, n; + GstStructure *s; + const gchar *name; + + n = gst_caps_get_size (caps); + for (i = 0; i < n; i++) { + s = gst_caps_get_structure (caps, i); + name = gst_structure_get_name (s); + if (!g_str_has_prefix (name, "audio/x-raw") + && !g_str_has_prefix (name, "video/x-raw")) + return FALSE; + } + + return TRUE; +} + +static void +gst_play_sink_convert_bin_post_missing_element_message (GstPlaySinkConvertBin * + self, const gchar * name) +{ + GstMessage *msg; + + msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name); + gst_element_post_message (GST_ELEMENT_CAST (self), msg); +} + +static void +distribute_running_time (GstElement * element, const GstSegment * segment) +{ + GstEvent *event; + GstPad *pad; + + pad = gst_element_get_static_pad (element, "sink"); + + if (segment->accum) { + event = gst_event_new_new_segment_full (FALSE, segment->rate, + segment->applied_rate, segment->format, 0, segment->accum, 0); + gst_pad_send_event (pad, event); + } + + event = gst_event_new_new_segment_full (FALSE, segment->rate, + segment->applied_rate, segment->format, + segment->start, segment->stop, segment->time); + gst_pad_send_event (pad, event); + + gst_object_unref (pad); +} + +void +gst_play_sink_convert_bin_add_conversion_element (GstPlaySinkConvertBin * self, + GstElement * el) +{ + self->conversion_elements = g_list_append (self->conversion_elements, el); + gst_bin_add (GST_BIN (self), gst_object_ref (el)); +} + +GstElement * +gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin + * self, const char *factory, const char *name) +{ + GstElement *el; + + el = gst_element_factory_make (factory, name); + if (el == NULL) { + gst_play_sink_convert_bin_post_missing_element_message (self, factory); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + factory), + (self->audio ? "audio rendering might fail" : + "video rendering might fail")); + } else { + gst_play_sink_convert_bin_add_conversion_element (self, el); + } + return el; +} + +static void +gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) +{ + self->identity = gst_element_factory_make ("identity", "identity"); + if (self->identity == NULL) { + gst_play_sink_convert_bin_post_missing_element_message (self, "identity"); + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + "identity"), (self->audio ? + "audio rendering might fail" : "video rendering might fail") + + ); + } else { + gst_bin_add (GST_BIN_CAST (self), self->identity); + gst_element_sync_state_with_parent (self->identity); + distribute_running_time (self->identity, &self->segment); + } +} + +static void +gst_play_sink_convert_bin_set_targets (GstPlaySinkConvertBin * self) +{ + GstPad *pad; + GstElement *head, *tail; + + if (self->conversion_elements == NULL) { + head = tail = self->identity; + } else { + head = GST_ELEMENT (g_list_first (self->conversion_elements)->data); + tail = GST_ELEMENT (g_list_last (self->conversion_elements)->data); + } + + pad = gst_element_get_static_pad (head, "sink"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); + gst_object_unref (pad); + + pad = gst_element_get_static_pad (tail, "src"); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); + gst_object_unref (pad); +} + +static void +gst_play_sink_convert_bin_remove_element (GstElement * element, + GstPlaySinkConvertBin * self) +{ + gst_element_set_state (element, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), element); +} + +static void +gst_play_sink_convert_bin_on_element_added (GstElement * element, + GstPlaySinkConvertBin * self) +{ + gst_element_sync_state_with_parent (element); + distribute_running_time (element, &self->segment); +} + +static void +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkConvertBin * self) +{ + GstPad *peer; + GstCaps *caps; + gboolean raw; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + self->sink_proxypad_blocked = blocked; + GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked); + if (!blocked) + goto done; + + /* There must be a peer at this point */ + peer = gst_pad_get_peer (self->sinkpad); + caps = gst_pad_get_negotiated_caps (peer); + if (!caps) + caps = gst_pad_get_caps_reffed (peer); + gst_object_unref (peer); + + raw = is_raw_caps (caps); + GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw); + gst_caps_unref (caps); + + if (raw == self->raw) + goto unblock; + self->raw = raw; + + if (raw) { + GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline"); + + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); + + g_assert (self->add_conversion_elements); + if (!(*self->add_conversion_elements) (self)) { + goto link_failed; + } + if (self->conversion_elements) + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_on_element_added, self); + + GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); + } else { + + GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline"); + + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); + + if (self->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } + + GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); + } + + /* to make things simple and avoid counterintuitive pad juggling, + ensure there is at least one element in the list */ + if (!self->conversion_elements) { + gst_play_sink_convert_bin_add_identity (self); + } + + gst_play_sink_convert_bin_set_targets (self); + +unblock: + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + +done: + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + return; + +link_failed: + { + GST_ELEMENT_ERROR (self, CORE, PAD, + (NULL), ("Failed to configure the converter bin.")); + + /* use a simple identity, better than nothing */ + gst_play_sink_convert_bin_add_identity (self); + gst_play_sink_convert_bin_set_targets (self); + + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + return; + } +} + +static gboolean +gst_play_sink_convert_bin_sink_event (GstPad * pad, GstEvent * event) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); + gboolean ret; + + ret = gst_proxy_pad_event_default (pad, gst_event_ref (event)); + + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + gboolean update; + gdouble rate, applied_rate; + GstFormat format; + gint64 start, stop, position; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, + &format, &start, &stop, &position); + + GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT, + &self->segment); + gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate, + format, start, stop, position); + GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT, + &self->segment); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + GST_DEBUG_OBJECT (self, "Resetting segment"); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + } + + gst_event_unref (event); + gst_object_unref (self); + + return ret; +} + +static gboolean +gst_play_sink_convert_bin_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); + gboolean ret; + GstStructure *s; + const gchar *name; + gboolean reconfigure = FALSE; + gboolean raw; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + s = gst_caps_get_structure (caps, 0); + name = gst_structure_get_name (s); + + if (self->audio) { + raw = g_str_has_prefix (name, "audio/x-raw-"); + } else { + raw = g_str_has_prefix (name, "video/x-raw-"); + } + + if (raw) { + if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { + GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); + reconfigure = TRUE; + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + } + } else { + if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { + GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw"); + reconfigure = TRUE; + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + } + } + + /* Otherwise the setcaps below fails */ + if (reconfigure) { + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); + } + + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + ret = gst_ghost_pad_setcaps_default (pad, caps); + + GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps, + ret); + + gst_object_unref (self); + + return ret; +} + +static GstCaps * +gst_play_sink_convert_bin_getcaps (GstPad * pad) +{ + GstPlaySinkConvertBin *self = + GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); + GstCaps *ret; + GstPad *otherpad, *peer; + + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); + if (!otherpad) { + if (pad == self->srcpad) { + otherpad = self->sink_proxypad; + } else if (pad == self->sinkpad) { + otherpad = + GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD + (self->sinkpad))); + } else { + GST_ERROR_OBJECT (pad, "Not one of our pads"); + } + } + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + + if (otherpad) { + peer = gst_pad_get_peer (otherpad); + if (peer) { + ret = gst_pad_get_caps_reffed (peer); + gst_object_unref (peer); + } else { + ret = gst_caps_new_any (); + } + gst_object_unref (otherpad); + } else { + GST_WARNING_OBJECT (self, "Could not traverse bin"); + ret = gst_caps_new_any (); + } + gst_object_unref (self); + + return ret; +} + +static void +gst_play_sink_convert_bin_finalize (GObject * object) +{ + GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (object); + + gst_object_unref (self->sink_proxypad); + g_mutex_free (self->lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstStateChangeReturn +gst_play_sink_convert_bin_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret; + GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (element); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + if (gst_pad_is_blocked (self->sink_proxypad)) + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + if (self->conversion_elements) { + g_list_foreach (self->conversion_elements, + (GFunc) gst_play_sink_convert_bin_remove_element, self); + g_list_free (self->conversion_elements); + self->conversion_elements = NULL; + } + if (!self->identity) { + gst_play_sink_convert_bin_add_identity (self); + } + gst_play_sink_convert_bin_set_targets (self); + self->raw = FALSE; + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_PLAY_SINK_CONVERT_BIN_LOCK (self); + if (!gst_pad_is_blocked (self->sink_proxypad)) + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), + (GDestroyNotify) gst_object_unref); + GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->identity) { + gst_element_set_state (self->identity, GST_STATE_NULL); + gst_bin_remove (GST_BIN_CAST (self), self->identity); + self->identity = NULL; + } + break; + default: + break; + } + + return ret; +} + +static void +gst_play_sink_convert_bin_class_init (GstPlaySinkConvertBinClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + GST_DEBUG_CATEGORY_INIT (gst_play_sink_convert_bin_debug, + "playsinkconvertbin", 0, "play bin"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_play_sink_convert_bin_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&srctemplate)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sinktemplate)); + gst_element_class_set_details_simple (gstelement_class, + "Player Sink Converter Bin", "Bin/Converter", + "Convenience bin for audio/video conversion", + "Sebastian Dröge "); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_change_state); +} + +static void +gst_play_sink_convert_bin_init (GstPlaySinkConvertBin * self) +{ + GstPadTemplate *templ; + + self->lock = g_mutex_new (); + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + + templ = gst_static_pad_template_get (&sinktemplate); + self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ); + gst_pad_set_event_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_sink_event)); + gst_pad_set_setcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_sink_setcaps)); + gst_pad_set_getcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_getcaps)); + + self->sink_proxypad = + GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad))); + + gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad); + gst_object_unref (templ); + + templ = gst_static_pad_template_get (&srctemplate); + self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ); + gst_pad_set_getcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_play_sink_convert_bin_getcaps)); + gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); + gst_object_unref (templ); +} diff --git a/gst/playback/gstplaysinkconvertbin.h b/gst/playback/gstplaysinkconvertbin.h new file mode 100644 index 0000000..2665651 --- /dev/null +++ b/gst/playback/gstplaysinkconvertbin.h @@ -0,0 +1,96 @@ +/* GStreamer + * Copyright (C) <2011> Sebastian Dröge + * Copyright (C) <2011> Vincent Penquerc'h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef __GST_PLAY_SINK_CONVERT_BIN_H__ +#define __GST_PLAY_SINK_CONVERT_BIN_H__ + +G_BEGIN_DECLS +#define GST_TYPE_PLAY_SINK_CONVERT_BIN \ + (gst_play_sink_convert_bin_get_type()) +#define GST_PLAY_SINK_CONVERT_BIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_CONVERT_BIN, GstPlaySinkConvertBin)) +#define GST_PLAY_SINK_CONVERT_BIN_CAST(obj) \ + ((GstPlaySinkConvertBin *) obj) +#define GST_PLAY_SINK_CONVERT_BIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_CONVERT_BIN, GstPlaySinkConvertBinClass)) +#define GST_IS_PLAY_SINK_CONVERT_BIN(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_CONVERT_BIN)) +#define GST_IS_PLAY_SINK_CONVERT_BIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_CONVERT_BIN)) + +#define GST_PLAY_SINK_CONVERT_BIN_LOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "locking from thread %p", \ + g_thread_self ()); \ + g_mutex_lock (GST_PLAY_SINK_CONVERT_BIN_CAST(obj)->lock); \ + GST_LOG_OBJECT (obj, \ + "locked from thread %p", \ + g_thread_self ()); \ +} G_STMT_END + +#define GST_PLAY_SINK_CONVERT_BIN_UNLOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "unlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_unlock (GST_PLAY_SINK_CONVERT_BIN_CAST(obj)->lock); \ +} G_STMT_END + +typedef struct _GstPlaySinkConvertBin GstPlaySinkConvertBin; +typedef struct _GstPlaySinkConvertBinClass GstPlaySinkConvertBinClass; + +struct _GstPlaySinkConvertBin +{ + GstBin parent; + + /* < private > */ + GMutex *lock; + + GstPad *sinkpad, *sink_proxypad; + gboolean sink_proxypad_blocked; + GstSegment segment; + + GstPad *srcpad; + + gboolean raw; + GList *conversion_elements; + GstElement *identity; + + /* configuration for derived classes */ + gboolean audio; + gboolean (*add_conversion_elements)(GstPlaySinkConvertBin *); +}; + +struct _GstPlaySinkConvertBinClass +{ + GstBinClass parent; +}; + +GType gst_play_sink_convert_bin_get_type (void); +GstElement * +gst_play_sink_convert_bin_add_conversion_element_factory (GstPlaySinkConvertBin *self, + const char *factory, const char *name); +void +gst_play_sink_convert_bin_add_conversion_element (GstPlaySinkConvertBin *self, + GstElement *el); + +G_END_DECLS +#endif /* __GST_PLAY_SINK_CONVERT_BIN_H__ */ diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index 96f9e62..0159f7c 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -32,439 +32,36 @@ GST_DEBUG_CATEGORY_STATIC (gst_play_sink_video_convert_debug); #define parent_class gst_play_sink_video_convert_parent_class G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert, - GST_TYPE_BIN); - -static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); + GST_TYPE_PLAY_SINK_CONVERT_BIN); static gboolean -is_raw_caps (GstCaps * caps) -{ - gint i, n; - GstStructure *s; - const gchar *name; - - n = gst_caps_get_size (caps); - for (i = 0; i < n; i++) { - s = gst_caps_get_structure (caps, i); - name = gst_structure_get_name (s); - if (!g_str_has_prefix (name, "video/x-raw")) - return FALSE; - } - - return TRUE; -} - -static void -post_missing_element_message (GstPlaySinkVideoConvert * self, - const gchar * name) -{ - GstMessage *msg; - - msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name); - gst_element_post_message (GST_ELEMENT_CAST (self), msg); -} - -static void -distribute_running_time (GstElement * element, const GstSegment * segment) +gst_play_sink_video_convert_add_conversion_elements (GstPlaySinkConvertBin * + cbin) { - GstEvent *event; - GstPad *pad; - - pad = gst_element_get_static_pad (element, "sink"); - - if (segment->accum) { - event = gst_event_new_new_segment_full (FALSE, segment->rate, - segment->applied_rate, segment->format, 0, segment->accum, 0); - gst_pad_send_event (pad, event); - } - - event = gst_event_new_new_segment_full (FALSE, segment->rate, - segment->applied_rate, segment->format, - segment->start, segment->stop, segment->time); - gst_pad_send_event (pad, event); - - gst_object_unref (pad); -} - -static void -gst_play_sink_video_convert_add_identity (GstPlaySinkVideoConvert * self) -{ - self->identity = gst_element_factory_make ("identity", "identity"); - if (self->identity == NULL) { - post_missing_element_message (self, "identity"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "identity"), ("video rendering might fail")); - } else { - gst_bin_add (GST_BIN_CAST (self), self->identity); - gst_element_sync_state_with_parent (self->identity); - distribute_running_time (self->identity, &self->segment); - } -} - -static void -gst_play_sink_video_convert_set_targets (GstPlaySinkVideoConvert * self, - GstElement * head, GstElement * tail) -{ - GstPad *pad; - - pad = gst_element_get_static_pad (head, "sink"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad); - gst_object_unref (pad); - - pad = gst_element_get_static_pad (tail, "src"); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad); - gst_object_unref (pad); -} - -static void -pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self) -{ - GstPad *peer; - GstCaps *caps; - gboolean raw; - GstBin *bin = GST_BIN_CAST (self); - GstElement *head = NULL, *prev = NULL; - - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - self->sink_proxypad_blocked = blocked; - GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked); - if (!blocked) - goto done; - - /* There must be a peer at this point */ - peer = gst_pad_get_peer (self->sinkpad); - caps = gst_pad_get_negotiated_caps (peer); - if (!caps) - caps = gst_pad_get_caps_reffed (peer); - gst_object_unref (peer); - - raw = is_raw_caps (caps); - GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw); - gst_caps_unref (caps); - - if (raw == self->raw) - goto unblock; - self->raw = raw; - - if (raw) { - - GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline"); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - - self->conv = gst_element_factory_make (COLORSPACE, "conv"); - if (self->conv == NULL) { - post_missing_element_message (self, COLORSPACE); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - COLORSPACE), ("video rendering might fail")); - } else { - gst_bin_add (bin, self->conv); - gst_element_sync_state_with_parent (self->conv); - distribute_running_time (self->conv, &self->segment); - prev = head = self->conv; - } - - self->scale = gst_element_factory_make ("videoscale", "scale"); - if (self->scale == NULL) { - post_missing_element_message (self, "videoscale"); - GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - "videoscale"), ("possibly a liboil version mismatch?")); - } else { - /* Add black borders if necessary to keep the DAR */ - g_object_set (self->scale, "add-borders", TRUE, NULL); - gst_bin_add (bin, self->scale); - gst_element_sync_state_with_parent (self->scale); - distribute_running_time (self->scale, &self->segment); - if (prev) { - if (!gst_element_link_pads_full (prev, "src", self->scale, "sink", - GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) - goto link_failed; - } else { - head = self->scale; - } - prev = self->scale; - } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline created"); - } else { - - GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline"); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - - if (self->conv) { - gst_element_set_state (self->conv, GST_STATE_NULL); - gst_bin_remove (bin, self->conv); - self->conv = NULL; + GstElement *el, *prev = NULL; + + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + COLORSPACE, "conv"); + if (el) + prev = el; + + el = gst_play_sink_convert_bin_add_conversion_element_factory (cbin, + "videoscale", "scale"); + if (el) { + /* Add black borders if necessary to keep the DAR */ + g_object_set (el, "add-borders", TRUE, NULL); + if (prev) { + if (!gst_element_link_pads_full (prev, "src", el, "sink", + GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) + goto link_failed; } - if (self->scale) { - gst_element_set_state (self->scale, GST_STATE_NULL); - gst_bin_remove (bin, self->scale); - self->scale = NULL; - } - - GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed"); + prev = el; } - g_assert ((head != NULL) == (prev != NULL)); - - /* to make things simple and avoid counterintuitive pad juggling, - ensure there is at least one element in the list */ - if (!head) { - gst_play_sink_video_convert_add_identity (self); - prev = head = self->identity; - } - - gst_play_sink_video_convert_set_targets (self, head, prev); - -unblock: - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - -done: - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - return; + return TRUE; link_failed: - { - GST_ELEMENT_ERROR (self, CORE, PAD, - (NULL), ("Failed to configure the video converter.")); - - /* use a simple identity, better than nothing */ - gst_play_sink_video_convert_add_identity (self); - gst_play_sink_video_convert_set_targets (self, self->identity, - self->identity); - - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - return; - } -} - -static gboolean -gst_play_sink_video_convert_sink_event (GstPad * pad, GstEvent * event) -{ - GstPlaySinkVideoConvert *self = - GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad)); - gboolean ret; - - ret = gst_proxy_pad_event_default (pad, gst_event_ref (event)); - - if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { - gboolean update; - gdouble rate, applied_rate; - GstFormat format; - gint64 start, stop, position; - - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &position); - - GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT, - &self->segment); - gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate, - format, start, stop, position); - GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT, - &self->segment); - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - GST_DEBUG_OBJECT (self, "Resetting segment"); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - } - - gst_event_unref (event); - gst_object_unref (self); - - return ret; -} - -static gboolean -gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstPlaySinkVideoConvert *self = - GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad)); - gboolean ret; - GstStructure *s; - const gchar *name; - gboolean reconfigure = FALSE; - - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - s = gst_caps_get_structure (caps, 0); - name = gst_structure_get_name (s); - - if (g_str_has_prefix (name, "video/x-raw-")) { - if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { - GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); - reconfigure = TRUE; - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - } - } else { - if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { - GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw"); - reconfigure = TRUE; - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - } - } - - /* Otherwise the setcaps below fails */ - if (reconfigure) { - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); - } - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - - ret = gst_ghost_pad_setcaps_default (pad, caps); - - GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps, - ret); - - gst_object_unref (self); - - return ret; -} - -static GstCaps * -gst_play_sink_video_convert_getcaps (GstPad * pad) -{ - GstPlaySinkVideoConvert *self = - GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad)); - GstCaps *ret; - GstPad *otherpad, *peer; - - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); - if (!otherpad) { - if (pad == self->srcpad) { - otherpad = self->sink_proxypad; - } else if (pad == self->sinkpad) { - otherpad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD - (self->sinkpad))); - } else { - GST_ERROR_OBJECT (pad, "Not one of our pads"); - } - } - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - - if (otherpad) { - peer = gst_pad_get_peer (otherpad); - if (peer) { - ret = gst_pad_get_caps_reffed (peer); - gst_object_unref (peer); - } else { - ret = gst_caps_new_any (); - } - gst_object_unref (otherpad); - } else { - GST_WARNING_OBJECT (self, "Could not traverse bin"); - ret = gst_caps_new_any (); - } - - gst_object_unref (self); - - return ret; -} - -static void -gst_play_sink_video_convert_finalize (GObject * object) -{ - GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object); - - gst_object_unref (self->sink_proxypad); - g_mutex_free (self->lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GstStateChangeReturn -gst_play_sink_video_convert_change_state (GstElement * element, - GstStateChange transition) -{ - GstStateChangeReturn ret; - GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - if (gst_pad_is_blocked (self->sink_proxypad)) - gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - if (self->conv) { - gst_element_set_state (self->conv, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->conv); - self->conv = NULL; - } - if (self->scale) { - gst_element_set_state (self->scale, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->scale); - self->scale = NULL; - } - if (!self->identity) { - gst_play_sink_video_convert_add_identity (self); - } - gst_play_sink_video_convert_set_targets (self, self->identity, - self->identity); - self->raw = FALSE; - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - if (!gst_pad_is_blocked (self->sink_proxypad)) - gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE, - (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self), - (GDestroyNotify) gst_object_unref); - GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (self->identity) { - gst_element_set_state (self->identity, GST_STATE_NULL); - gst_bin_remove (GST_BIN_CAST (self), self->identity); - self->identity = NULL; - } - break; - default: - break; - } - - return ret; + return FALSE; } static void @@ -479,51 +76,17 @@ gst_play_sink_video_convert_class_init (GstPlaySinkVideoConvertClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; - gobject_class->finalize = gst_play_sink_video_convert_finalize; - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&srctemplate)); - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&sinktemplate)); gst_element_class_set_details_simple (gstelement_class, "Player Sink Video Converter", "Video/Bin/Converter", "Convenience bin for video conversion", "Sebastian Dröge "); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_change_state); } static void gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self) { - GstPadTemplate *templ; - - self->lock = g_mutex_new (); - gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); - - templ = gst_static_pad_template_get (&sinktemplate); - self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ); - gst_pad_set_event_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_event)); - gst_pad_set_setcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_setcaps)); - gst_pad_set_getcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps)); - - self->sink_proxypad = - GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad))); - - gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad); - gst_object_unref (templ); - - templ = gst_static_pad_template_get (&srctemplate); - self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ); - gst_pad_set_getcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps)); - gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad); - gst_object_unref (templ); - - gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), - self->sink_proxypad); + GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); + cbin->audio = FALSE; + cbin->add_conversion_elements = + gst_play_sink_video_convert_add_conversion_elements; } diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h index 10027ee..33030ad 100644 --- a/gst/playback/gstplaysinkvideoconvert.h +++ b/gst/playback/gstplaysinkvideoconvert.h @@ -18,6 +18,7 @@ */ #include +#include "gstplaysinkconvertbin.h" #ifndef __GST_PLAY_SINK_VIDEO_CONVERT_H__ #define __GST_PLAY_SINK_VIDEO_CONVERT_H__ @@ -36,47 +37,18 @@ G_BEGIN_DECLS #define GST_IS_PLAY_SINK_VIDEO_CONVERT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_VIDEO_CONVERT)) -#define GST_PLAY_SINK_VIDEO_CONVERT_LOCK(obj) G_STMT_START { \ - GST_LOG_OBJECT (obj, \ - "locking from thread %p", \ - g_thread_self ()); \ - g_mutex_lock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \ - GST_LOG_OBJECT (obj, \ - "locked from thread %p", \ - g_thread_self ()); \ -} G_STMT_END - -#define GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK(obj) G_STMT_START { \ - GST_LOG_OBJECT (obj, \ - "unlocking from thread %p", \ - g_thread_self ()); \ - g_mutex_unlock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \ -} G_STMT_END - typedef struct _GstPlaySinkVideoConvert GstPlaySinkVideoConvert; typedef struct _GstPlaySinkVideoConvertClass GstPlaySinkVideoConvertClass; struct _GstPlaySinkVideoConvert { - GstBin parent; - - /* < private > */ - GMutex *lock; - - GstPad *sinkpad, *sink_proxypad; - gboolean sink_proxypad_blocked; - GstSegment segment; - - GstPad *srcpad; + GstPlaySinkConvertBin parent; - gboolean raw; - GstElement *conv, *scale; - GstElement *identity; }; struct _GstPlaySinkVideoConvertClass { - GstBinClass parent; + GstPlaySinkConvertBinClass parent; }; GType gst_play_sink_video_convert_get_type (void); -- 2.7.4