[clutter-gst] plugin: Add a autocluttersink element



commit 2884f0660ceef11f38a0ea4b573ef4c341b31387
Author: Josep Torra <n770galaxy gmail com>
Date:   Wed Jan 25 00:10:14 2012 +0000

    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(-)
---
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  <support fluendo com>
+  *
+  * 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 <string.h>
+#include <clutter/clutter.h>
+#include <clutter/x11/clutter-x11.h>
+#include <clutter/glx/clutter-glx-texture-pixmap.h>
+
+#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 <support fluendo com>"
+};
+
+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  <support fluendo com>
+  *
+  * 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 <gst/gst.h>
+#include <clutter/clutter.h>
+
+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  <support fluendo com>
+  *
+  * 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.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]