From 2884f0660ceef11f38a0ea4b573ef4c341b31387 Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Wed, 25 Jan 2012 00:10:14 +0000 Subject: plugin: Add a autocluttersink element Add a bin element that is capable of autoplugging clutter compatible sinks. --- clutter-gst/Makefile.am | 3 + clutter-gst/clutter-gst-auto-video-sink.c | 811 ++++++++++++++++++++++++++++++ clutter-gst/clutter-gst-auto-video-sink.h | 113 +++++ clutter-gst/clutter-gst-plugin.c | 88 ++++ clutter-gst/clutter-gst-video-sink.c | 29 -- doc/reference/Makefile.am | 15 +- 6 files changed, 1023 insertions(+), 36 deletions(-) create mode 100644 clutter-gst/clutter-gst-auto-video-sink.c create mode 100644 clutter-gst/clutter-gst-auto-video-sink.h create mode 100644 clutter-gst/clutter-gst-plugin.c diff --git a/clutter-gst/Makefile.am b/clutter-gst/Makefile.am index 4201e4f..3f130ee 100644 --- a/clutter-gst/Makefile.am +++ b/clutter-gst/Makefile.am @@ -97,7 +97,10 @@ cluttergstheaders_HEADERS = $(source_h) $(glib_enum_h) # plugin_source_c = \ + $(srcdir)/clutter-gst-plugin.c \ $(srcdir)/clutter-gst-video-sink.c \ + $(srcdir)/clutter-gst-auto-video-sink.c \ + $(srcdir)/clutter-gst-auto-video-sink.h \ $(NULL) libgstclutter_la_SOURCES = \ diff --git a/clutter-gst/clutter-gst-auto-video-sink.c b/clutter-gst/clutter-gst-auto-video-sink.c new file mode 100644 index 0000000..8ee030d --- /dev/null +++ b/clutter-gst/clutter-gst-auto-video-sink.c @@ -0,0 +1,811 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-auto-video-sink.c - GStreamer Auto Clutter Video Sink bin. + * + * Authored by Josep Torra + * + * Copyright (C) 2011 Fluendo, S.A. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 +#include +#include +#include + +#include "clutter-gst-auto-video-sink.h" +#include "clutter-gst-private.h" + +GST_DEBUG_CATEGORY_EXTERN (clutter_gst_auto_video_sink_debug); +#define GST_CAT_DEFAULT clutter_gst_auto_video_sink_debug + +static GstElementDetails clutter_gst_auto_video_sink_details = { + "Auto Clutter Sink", + "Sink/Video", + "Autoplug clutter capable video sinks", + "Josep Torra " +}; + +static GstStaticPadTemplate sink_template_factory = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum +{ + PROP_0, + PROP_TEXTURE +}; + +GST_BOILERPLATE (ClutterGstAutoVideoSink, + clutter_gst_auto_video_sink, + GstBin, + GST_TYPE_BIN); + +typedef struct +{ + const gchar *factory_name; + GstElement *element; + GstCaps *caps; +} SinkElement; + +static GstCaps * +_get_sink_caps (GstElement *sink) +{ + GstPad *sinkpad; + GstCaps *caps = NULL; + + /* try to activate */ + if (GST_STATE (sink) < GST_STATE_READY && + gst_element_set_state (sink, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) + { + goto beach; + } + + if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) + { + /* Got the sink pad, now let's see which caps will be accepted */ + caps = gst_pad_get_caps (sinkpad); + } + gst_object_unref (sinkpad); + +beach: + return caps; +} + +static SinkElement * +_sink_element_create (GstElement *element) +{ + SinkElement *se = NULL; + GstCaps *caps = NULL; + + /* Check if the sink can be set to READY and recover it's caps */ + if (!(caps = _get_sink_caps (element))) + { + gst_element_set_state (element, GST_STATE_NULL); + gst_object_unref (element); + goto beach; + } + + if ((se = g_new0 (SinkElement, 1))) + { + gst_object_ref_sink (element); + se->element = element; + se->caps = caps; + } + else + { + gst_caps_unref (caps); + gst_object_unref (element); + } + +beach: + return se; +} + +static void +_sink_element_free (gpointer data, gpointer user_data) +{ + SinkElement *se = (SinkElement *)data; + + gst_element_set_state (se->element, GST_STATE_NULL); + gst_caps_unref (se->caps); + gst_object_unref (se->element); + g_free (se); +} + +static gboolean +_factory_filter (GstPluginFeature *feature, gpointer data) +{ + const gchar *klass; + guint rank; + + /* we only care about element factories */ + if (!GST_IS_ELEMENT_FACTORY (feature)) + return FALSE; + + /* video sinks */ + klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); + if (!(strstr (klass, "Sink") && strstr (klass, "Video"))) + return FALSE; + + /* only select elements with autoplugging rank */ + rank = gst_plugin_feature_get_rank (feature); + if (rank < GST_RANK_MARGINAL) + return FALSE; + + return TRUE; +} + +static gint +_factories_compare_ranks (GstPluginFeature *f1, GstPluginFeature *f2) +{ + gint diff; + + diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); + + if (diff != 0) + return diff; + + return strcmp (gst_plugin_feature_get_name (f2), + gst_plugin_feature_get_name (f1)); +} + +static GstElement * +_create_element_with_pretty_name (ClutterGstAutoVideoSink *bin, + GstElementFactory *factory) +{ + GstElement *element; + gchar *name, *marker; + + marker = g_strdup (GST_PLUGIN_FEATURE (factory)->name); + if (g_str_has_suffix (marker, "sink")) + marker[strlen (marker) - 4] = '\0'; + if (g_str_has_prefix (marker, "gst")) + g_memmove (marker, marker + 3, strlen (marker + 3) + 1); + name = g_strdup_printf ("%s-actual-sink-%s", GST_OBJECT_NAME (bin), marker); + g_free (marker); + + element = gst_element_factory_create (factory, name); + g_free (name); + + return element; +} + +static inline gboolean +_is_clutter_sink (GstElement *element) +{ + GParamSpec *pspec; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), + "texture"); + + if (pspec == NULL) + { + GST_DEBUG_OBJECT (element, "don't have a texture property"); + return FALSE; + } + + if (CLUTTER_TYPE_TEXTURE == pspec->value_type || + g_type_is_a (pspec->value_type, CLUTTER_TYPE_TEXTURE)) + { + GST_DEBUG_OBJECT (element, "has a clutter texture property"); + return TRUE; + } + + GST_WARNING_OBJECT (element, "has texture property, but it's of type %s " + "and we expected it to be of type CLUTTER_TYPE_TEXTURE", + g_type_name (pspec->value_type)); + + return FALSE; +} + +static inline void +_sinks_discover (ClutterGstAutoVideoSink *bin) +{ + GstCaps *caps = gst_caps_new_empty (); + GList *factories, *item; + + factories = gst_default_registry_feature_filter ( + (GstPluginFeatureFilter)_factory_filter, FALSE, bin); + factories = g_list_sort (factories, (GCompareFunc)_factories_compare_ranks); + + for (item = factories; item != NULL; item = item->next) + { + GstElementFactory *f = GST_ELEMENT_FACTORY (item->data); + GstElement *el; + SinkElement *se; + + if ((el = _create_element_with_pretty_name (bin, f))) + { + GST_DEBUG_OBJECT (bin, "Testing %s", GST_PLUGIN_FEATURE (f)->name); + + /* Check for a texture property with CLUTTER_TYPE_TEXTURE type */ + if (!_is_clutter_sink (el)) + { + gst_object_unref (el); + continue; + } + se = _sink_element_create (el); + if (se) + { + GstCaps *caps_union = gst_caps_union (caps, se->caps); + gst_caps_unref (caps); + caps = caps_union; + bin->sinks = g_slist_append (bin->sinks, se); + GST_DEBUG_OBJECT (bin, "Added %s with caps %" GST_PTR_FORMAT, + GST_PLUGIN_FEATURE (f)->name, se->caps); + } + else + { + gst_object_unref (el); + } + } + } + + if (!gst_caps_is_empty (caps)) + { + gst_caps_replace (&bin->video_caps, caps); + GST_DEBUG_OBJECT (bin, "Supported caps %" GST_PTR_FORMAT, + bin->video_caps); + } + gst_caps_unref (caps); +} + +static inline void +_sinks_destroy (ClutterGstAutoVideoSink *bin) +{ + g_slist_foreach (bin->sinks, _sink_element_free, NULL); + g_slist_free (bin->sinks); + bin->sinks = NULL; +} + +static inline GstElement * +_sinks_find_sink_by_caps (ClutterGstAutoVideoSink *bin, GstCaps *caps) +{ + GstElement *element = NULL; + GSList *walk = bin->sinks; + + while (walk) + { + SinkElement *se = (SinkElement *)walk->data; + if (se) + { + GstCaps *intersect = NULL; + + intersect = gst_caps_intersect (caps, se->caps); + if (!gst_caps_is_empty (intersect)) + { + element = se->element; + gst_caps_unref (intersect); + GST_DEBUG_OBJECT (bin, "found sink %" GST_PTR_FORMAT, element); + goto beach; + } + gst_caps_unref (intersect); + } + walk = g_slist_next (walk); + } + +beach: + return element; +} + +static void +clutter_gst_auto_video_sink_do_async_start (ClutterGstAutoVideoSink *bin) +{ + GstMessage *message; + + bin->async_pending = TRUE; + + GST_INFO_OBJECT (bin, "Sending async_start message"); + message = gst_message_new_async_start (GST_OBJECT_CAST (bin), FALSE); + GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (bin), message); +} + +static void +clutter_gst_auto_video_sink_do_async_done (ClutterGstAutoVideoSink *bin) +{ + GstMessage *message; + + if (bin->async_pending) + { + GST_INFO_OBJECT (bin, "Sending async_done message"); + message = gst_message_new_async_done (GST_OBJECT_CAST (bin)); + GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (bin), + message); + + bin->async_pending = FALSE; + } +} + +static gboolean +clutter_gst_auto_video_sink_reconfigure (ClutterGstAutoVideoSink *bin, + GstCaps *caps) +{ + GstElement *sink; + GstPad *sink_pad_target = NULL; + gboolean ret = FALSE; + + GST_DEBUG_OBJECT (bin, "reconfigure the bin"); + + sink = _sinks_find_sink_by_caps (bin, caps); + + if (sink && sink == bin->child) + { + GST_DEBUG_OBJECT (bin, "we already using that sink, done"); + ret = TRUE; + goto beach; + } + + if (bin->child) + { + /* Deactivate current child */ + GST_DEBUG_OBJECT (bin, "going to remove %" GST_PTR_FORMAT, bin->child); + gst_ghost_pad_set_target (GST_GHOST_PAD (bin->sink_pad), NULL); + gst_element_set_state (bin->child, GST_STATE_NULL); + gst_bin_remove (GST_BIN (bin), bin->child); + bin->child = NULL; + } + + /* This might have failed */ + if (!sink) + { + GST_ELEMENT_ERROR (bin, LIBRARY, INIT, + ("No usable video rendering element found."), + ("Failed detecting a video sink for the requested" + " caps.")); + goto beach; + } + + /* Now we are ready to add the sink to bin */ + bin->child = gst_object_ref (sink); + g_object_set (G_OBJECT(bin->child), "texture", bin->texture, NULL); + + GST_DEBUG_OBJECT (bin, "going to add %" GST_PTR_FORMAT, bin->child); + /* Add our child */ + gst_bin_add (GST_BIN (bin), bin->child); + /* Bring all elements to the bin's state */ + gst_element_sync_state_with_parent (bin->child); + /* Get the child's sink pad */ + sink_pad_target = gst_element_get_static_pad (bin->child, "sink"); + + /* Ghost the sink pad to the appropriate element */ + GST_DEBUG_OBJECT (sink_pad_target, "ghosting pad as bin sink pad"); + gst_ghost_pad_set_target (GST_GHOST_PAD (bin->sink_pad), sink_pad_target); + gst_object_unref (sink_pad_target); + ret = TRUE; +beach: + return ret; +} + +static void +clutter_gst_auto_video_sink_sink_pad_blocked_cb (GstPad *pad, gboolean blocked, + gpointer user_data) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK_CAST (user_data); + GstCaps *caps = NULL; + + /* In case the pad is not blocked we should not do anything but return */ + if (!blocked) + { + GST_DEBUG_OBJECT (bin, "pad successfully unblocked"); + return; + } + + CLUTTER_GST_AUTO_VIDEO_SINK_LOCK (bin); + + /* This only occurs when our bin is first initialised || stream changes */ + if (G_UNLIKELY (!bin->setup)) + { + + caps = gst_pad_peer_get_caps_reffed (bin->sink_pad); + + if (G_UNLIKELY (!caps)) + { + GST_WARNING_OBJECT (bin, "no incoming caps defined, can't setup"); + goto beach; + } + + if (G_UNLIKELY (gst_caps_is_empty (caps))) + { + GST_WARNING_OBJECT (bin, "caps empty, can't setup"); + goto beach; + } + + GST_DEBUG_OBJECT (bin, "incoming caps %" GST_PTR_FORMAT, caps); + + if (!clutter_gst_auto_video_sink_reconfigure (bin, caps)) + goto beach; + + /* We won't be doing this again unless stream changes */ + bin->setup = TRUE; + } + + /* Note that we finished our ASYNC state change but our children will have + * posted their own messages on our bus. */ + clutter_gst_auto_video_sink_do_async_done (bin); + + GST_DEBUG_OBJECT (bin, "unblock the pad"); + +beach: + if (caps) + { + gst_caps_unref (caps); + } + gst_pad_set_blocked_async (bin->sink_block_pad, FALSE, + clutter_gst_auto_video_sink_sink_pad_blocked_cb, + bin); + CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK (bin); + return; +} + +static gboolean +clutter_gst_auto_video_sink_set_caps (GstPad *pad, GstCaps *caps) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK ( + gst_pad_get_parent (pad)); + gboolean ret = TRUE; + GstPad *target = NULL; + + GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); + + target = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (bin->sink_pad)); + + CLUTTER_GST_AUTO_VIDEO_SINK_LOCK (bin); + + if (target && gst_pad_accept_caps (target, caps)) + { + GST_DEBUG_OBJECT (pad, "Target accepts caps"); + ret = bin->sink_setcaps (pad, caps); + CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK (bin); + goto out; + } + + GST_DEBUG_OBJECT (pad, "Target did not accept caps"); + + bin->setup = FALSE; + gst_pad_set_blocked_async (bin->sink_block_pad, TRUE, + clutter_gst_auto_video_sink_sink_pad_blocked_cb, + bin); + CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK (bin); + +out: + if (target) + gst_object_unref (target); + gst_object_unref (bin); + return ret; +} + +static GstCaps * +clutter_gst_auto_video_sink_get_caps (GstPad *pad) +{ + GstCaps *ret; + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK ( + gst_pad_get_parent (pad)); + + if (bin->video_caps) + { + ret = gst_caps_ref (bin->video_caps); + } + else + { + ret = gst_static_pad_template_get_caps (&sink_template_factory); + } + + gst_object_unref (bin); + return ret; +} + +static gboolean +clutter_gst_auto_video_sink_accept_caps (GstPad *pad, GstCaps *caps) +{ + gboolean ret = FALSE; + GstCaps *allowed_caps = clutter_gst_auto_video_sink_get_caps (pad); + + if (allowed_caps) + { + GstCaps *result = NULL; + + result = gst_caps_intersect (caps, allowed_caps); + + if (!gst_caps_is_empty (result)) + ret = TRUE; + + gst_caps_unref (result); + } + + gst_caps_unref (allowed_caps); + + return ret; +} + +static GstStateChangeReturn +clutter_gst_auto_video_sink_change_state (GstElement *element, + GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS, bret; + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK (element); + + switch (transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: + _sinks_discover (bin); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + CLUTTER_GST_AUTO_VIDEO_SINK_LOCK (bin); + /* Here we set our callback to intercept data flow on the first buffer */ + GST_DEBUG_OBJECT (bin, "try to block input pad to setup internal " + "pipeline"); + gst_pad_set_blocked_async ( + bin->sink_block_pad, TRUE, + clutter_gst_auto_video_sink_sink_pad_blocked_cb, + bin); + ret = GST_STATE_CHANGE_ASYNC; + clutter_gst_auto_video_sink_do_async_start (bin); + CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK (bin); + break; + default: + break; + } + + bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (bret == GST_STATE_CHANGE_FAILURE) + return bret; + + switch (transition) + { + case GST_STATE_CHANGE_PAUSED_TO_READY: + CLUTTER_GST_AUTO_VIDEO_SINK_LOCK (bin); + + /* Unblock pad */ + gst_pad_set_blocked_async ( + bin->sink_block_pad, FALSE, + clutter_gst_auto_video_sink_sink_pad_blocked_cb, + bin); + /* Unset ghost pad target */ + GST_DEBUG_OBJECT (bin, "setting ghost pad target to NULL"); + gst_ghost_pad_set_target (GST_GHOST_PAD (bin->sink_pad), NULL); + + /* Destroy our child */ + if (bin->child) + { + GST_DEBUG_OBJECT (bin->child, "removing child sink"); + gst_element_set_state (bin->child, GST_STATE_NULL); + gst_bin_remove (GST_BIN (bin), bin->child); + bin->child = NULL; + } + + bin->setup = FALSE; + CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK (bin); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + _sinks_destroy (bin); + break; + default: + break; + } + + return ret; +} + +static void +clutter_gst_auto_video_sink_dispose (GObject *object) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK (object); + + GST_DEBUG_OBJECT (bin, "Disposing"); + + if (bin->child) + { + gst_element_set_state (bin->child, GST_STATE_NULL); + gst_object_unref (bin->child); + bin->child = NULL; + } + + if (bin->sink_block_pad) + { + gst_object_unref (bin->sink_block_pad); + bin->sink_block_pad = NULL; + } + + if (bin->texture) + { + g_object_unref (bin->texture); + bin->texture = NULL; + } + + GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static void +clutter_gst_auto_video_sink_finalize (GObject *object) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK (object); + + GST_DEBUG_OBJECT (bin, "Destroying"); + + _sinks_destroy (bin); + + if (bin->lock) + { + g_mutex_free (bin->lock); + bin->lock = NULL; + } + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +clutter_gst_auto_video_sink_set_texture (ClutterGstAutoVideoSink *bin, + ClutterTexture *texture) +{ + if (bin->texture) + { + g_object_unref (bin->texture); + } + bin->texture = texture; + if (bin->setup) + { + g_object_set (G_OBJECT(bin->child), "texture", texture, NULL); + } +} + +static void +clutter_gst_auto_video_sink_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK (object); + + switch (prop_id) + { + case PROP_TEXTURE: + clutter_gst_auto_video_sink_set_texture (bin, g_value_dup_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_gst_auto_video_sink_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterGstAutoVideoSink *bin = CLUTTER_GST_AUTO_VIDEO_SINK (object); + + switch (prop_id) + { + case PROP_TEXTURE: + g_value_set_object (value, bin->texture); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clutter_gst_auto_video_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template_factory)); + + gst_element_class_set_details (element_class, + &clutter_gst_auto_video_sink_details); +} + +static void +clutter_gst_auto_video_sink_class_init (ClutterGstAutoVideoSinkClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class; + GParamSpec *pspec; + + oclass->dispose = clutter_gst_auto_video_sink_dispose; + oclass->finalize = clutter_gst_auto_video_sink_finalize; + oclass->set_property = clutter_gst_auto_video_sink_set_property; + oclass->get_property = clutter_gst_auto_video_sink_get_property; + + /** + * ClutterGstAutoVideoSink:texture: + * + * This is the texture the video is decoded into. It can be any + * #ClutterTexture, however Cluter-Gst has a handy subclass, + * #ClutterGstVideoTexture, that implements the #ClutterMedia + * interface. + */ + pspec = g_param_spec_object ("texture", + "Texture", + "Texture the video will be decoded into", + CLUTTER_TYPE_TEXTURE, + CLUTTER_GST_PARAM_READWRITE); + + g_object_class_install_property (oclass, PROP_TEXTURE, pspec); + + gstelement_class = (GstElementClass *)klass; + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (clutter_gst_auto_video_sink_change_state); +} + +static void +clutter_gst_auto_video_sink_init (ClutterGstAutoVideoSink *bin, + ClutterGstAutoVideoSinkClass *g_class) +{ + GstPad *proxypad; + GstPadTemplate *template; + + bin->setup = FALSE; + bin->texture = NULL; + + /* Create a ghost pad with no target at first */ + template = gst_static_pad_template_get ( + &sink_template_factory); + bin->sink_pad = gst_ghost_pad_new_no_target_from_template ("sink", template); + gst_object_unref (template); + + gst_pad_set_active (bin->sink_pad, TRUE); + + proxypad = NULL; + + if (bin->sink_pad) + { + GstIterator *it = gst_pad_iterate_internal_links (bin->sink_pad); + if (G_UNLIKELY (!it || + gst_iterator_next (it, + (gpointer) & proxypad) != + GST_ITERATOR_OK || + proxypad == NULL)) + { + GST_ERROR_OBJECT (bin, + "failed to get internally linked pad from sinkpad"); + } + if (it) + gst_iterator_free (it); + } + + bin->sink_block_pad = proxypad; + + bin->sink_setcaps = GST_PAD_SETCAPSFUNC (bin->sink_pad); + gst_pad_set_setcaps_function (bin->sink_pad, + GST_DEBUG_FUNCPTR ( + clutter_gst_auto_video_sink_set_caps)); + gst_pad_set_getcaps_function (bin->sink_pad, + GST_DEBUG_FUNCPTR ( + clutter_gst_auto_video_sink_get_caps)); + gst_pad_set_acceptcaps_function (bin->sink_pad, + GST_DEBUG_FUNCPTR ( + clutter_gst_auto_video_sink_accept_caps)); + gst_element_add_pad (GST_ELEMENT (bin), bin->sink_pad); + /* Setup the element */ + GST_OBJECT_FLAG_SET (GST_OBJECT (bin), GST_ELEMENT_IS_SINK); + bin->lock = g_mutex_new (); + return; +} diff --git a/clutter-gst/clutter-gst-auto-video-sink.h b/clutter-gst/clutter-gst-auto-video-sink.h new file mode 100644 index 0000000..dfd7095 --- /dev/null +++ b/clutter-gst/clutter-gst-auto-video-sink.h @@ -0,0 +1,113 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-auto-video-sink.c - GStreamer Auto Clutter Video Sink bin. + * + * Authored by Josep Torra + * + * Copyright (C) 2011 Fluendo, S.A. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + */ + +#ifndef __CLUTTER_GST_AUTO_VIDEO_SINK_H__ +#define __CLUTTER_GST_AUTO_VIDEO_SINK_H__ + +#include +#include + +G_BEGIN_DECLS +#define CLUTTER_GST_TYPE_AUTO_VIDEO_SINK clutter_gst_auto_video_sink_get_type() + +#define CLUTTER_GST_AUTO_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK, \ + ClutterGstAutoVideoSink)) + +#define CLUTTER_GST_AUTO_VIDEO_SINK_CAST(obj) \ + ((ClutterGstAutoVideoSink *)(obj)) + +#define CLUTTER_GST_AUTO_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK, \ + ClutterGstAutoVideoSinkClass)) + +#define CLUTTER_GST_AUTO_VIDEO_SINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK, \ + ClutterGstAutoVideoSinkClass)) + +#define CLUTTER_GST_IS_AUTO_VIDEO_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK)) + +#define CLUTTER_GST_IS_AUTO_VIDEO_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK)) + +#define CLUTTER_GST_AUTO_VIDEO_SINK_LOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "locking from thread %p", \ + g_thread_self ()); \ + g_mutex_lock (CLUTTER_GST_AUTO_VIDEO_SINK(obj)->lock); \ + GST_LOG_OBJECT (obj, \ + "locked from thread %p", \ + g_thread_self ()); \ +} G_STMT_END + +#define CLUTTER_GST_AUTO_VIDEO_SINK_UNLOCK(obj) G_STMT_START { \ + GST_LOG_OBJECT (obj, \ + "unlocking from thread %p", \ + g_thread_self ()); \ + g_mutex_unlock (CLUTTER_GST_AUTO_VIDEO_SINK(obj)->lock); \ +} G_STMT_END + +typedef struct _ClutterGstAutoVideoSink ClutterGstAutoVideoSink; +typedef struct _ClutterGstAutoVideoSinkClass ClutterGstAutoVideoSinkClass; + +struct _ClutterGstAutoVideoSink +{ + GstBin parent; + + GstPad *sink_pad; + GstPad *sink_block_pad; + GstPadSetCapsFunction sink_setcaps; + + GstElement *child; + + GstCaps *video_caps; + GSList *sinks; + + gboolean async_pending; + gboolean setup; + + ClutterTexture *texture; + + GMutex *lock; +}; + +struct _ClutterGstAutoVideoSinkClass +{ + GstBinClass parent_class; +}; + +GType clutter_gst_auto_video_sink_get_type (void); + +G_END_DECLS + +#endif /* __CLUTTER_GST_AUTO_VIDEO_SINK_H__ */ diff --git a/clutter-gst/clutter-gst-plugin.c b/clutter-gst/clutter-gst-plugin.c new file mode 100644 index 0000000..3d30ad0 --- /dev/null +++ b/clutter-gst/clutter-gst-plugin.c @@ -0,0 +1,88 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-plugin.c - GStreamer plugin. + * + * Authored by Josep Torra + * + * Copyright (C) 2011 Fluendo, S.A. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "clutter-gst-video-sink.h" +#include "clutter-gst-auto-video-sink.h" + +GST_DEBUG_CATEGORY (clutter_gst_video_sink_debug); +GST_DEBUG_CATEGORY (clutter_gst_auto_video_sink_debug); + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and pad templates + * register the features + */ +static gboolean +plugin_init (GstPlugin *plugin) +{ + gboolean ret; + + GST_DEBUG_CATEGORY_INIT (clutter_gst_auto_video_sink_debug, + "autocluttersink", + 0, + "clutter auto video sink"); + +#if defined (CLUTTER_WINDOWING_X11) + /* Required by some GStreamer element like VA */ + XInitThreads (); +#endif + + /* We must enshure that clutter is initialized */ + if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) + return FALSE; + + ret = gst_element_register (plugin, + "cluttersink", + GST_RANK_MARGINAL, + CLUTTER_GST_TYPE_VIDEO_SINK); + if (!ret) + return FALSE; + + ret = gst_element_register (plugin, + "autocluttersink", + GST_RANK_NONE, + CLUTTER_GST_TYPE_AUTO_VIDEO_SINK); + if (!ret) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "clutter", + "Elements to render to Clutter textures", + plugin_init, + VERSION, + "LGPL", /* license */ + PACKAGE, + "http://www.clutter-project.org"); + diff --git a/clutter-gst/clutter-gst-video-sink.c b/clutter-gst/clutter-gst-video-sink.c index 6e5d6b2..0793b62 100644 --- a/clutter-gst/clutter-gst-video-sink.c +++ b/clutter-gst/clutter-gst-video-sink.c @@ -1621,32 +1621,3 @@ clutter_gst_navigation_interface_init (GstNavigationInterface *iface) { iface->send_event = clutter_gst_navigation_send_event; } - -static gboolean -plugin_init (GstPlugin *plugin) -{ - -#if defined (CLUTTER_WINDOWING_X11) - /* Required by some GStreamer element like VA */ - XInitThreads (); -#endif - - /* We must enshure that clutter is initialized */ - if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) - return FALSE; - - return gst_element_register (plugin, - "cluttersink", - GST_RANK_NONE, - CLUTTER_GST_TYPE_VIDEO_SINK);; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "cluttersink", - "Element to render to Clutter textures", - plugin_init, - VERSION, - "LGPL", /* license */ - PACKAGE, - "http://www.clutter-project.org"); diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am index 80c9167..290cc3a 100644 --- a/doc/reference/Makefile.am +++ b/doc/reference/Makefile.am @@ -48,13 +48,14 @@ CFILE_GLOB=$(top_srcdir)/clutter-gst/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES= \ - I420.h \ - YV12.h \ - clutter-gst.h \ - clutter-gst-debug.h \ - clutter-gst-private.h \ - clutter-gst-shaders.h \ +IGNORE_HFILES= \ + I420.h \ + YV12.h \ + clutter-gst.h \ + clutter-gst-debug.h \ + clutter-gst-private.h \ + clutter-gst-shaders.h \ + clutter-gst-auto-video-sink.h \ $(NULL) # Images to copy into HTML directory. -- cgit v1.2.1