[clutter-gst] Add ClutterGstContent



commit a661cbdca1c5a807ab48017cd4c93c149f878b0b
Author: Bastian Winkler <buz netbuz org>
Date:   Fri May 3 15:40:07 2013 +0200

    Add ClutterGstContent
    
    This adds a ClutterContent implementation that uses a CoglGstVideoSink
    to display GStreamer video frames in any ClutterActor.
    It does not implement the ClutterGstPlayer interface, so you have to
    manage all GStreamer related tasks on your own.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=699825

 clutter-gst/Makefile.am           |    2 +
 clutter-gst/clutter-gst-content.c |  397 +++++++++++++++++++++++++++++++++++++
 clutter-gst/clutter-gst-content.h |   91 +++++++++
 clutter-gst/clutter-gst.h         |    1 +
 examples/.gitignore               |    1 +
 examples/Makefile.am              |    9 +-
 examples/video-content.c          |  172 ++++++++++++++++
 7 files changed, 672 insertions(+), 1 deletions(-)
---
diff --git a/clutter-gst/Makefile.am b/clutter-gst/Makefile.am
index 661e69c..ccb8e04 100644
--- a/clutter-gst/Makefile.am
+++ b/clutter-gst/Makefile.am
@@ -33,6 +33,7 @@ source_h =                                    \
        $(srcdir)/clutter-gst-aspectratio.h     \
        $(srcdir)/clutter-gst-crop.h            \
        $(srcdir)/clutter-gst-pipeline.h        \
+       $(srcdir)/clutter-gst-content.h         \
        $(NULL)
 
 source_priv_h =                                        \
@@ -54,6 +55,7 @@ source_c =                                    \
        $(srcdir)/clutter-gst-aspectratio.c     \
        $(srcdir)/clutter-gst-crop.c            \
        $(srcdir)/clutter-gst-pipeline.c        \
+       $(srcdir)/clutter-gst-content.c         \
        $(glib_enum_c)                          \
        $(NULL)
 
diff --git a/clutter-gst/clutter-gst-content.c b/clutter-gst/clutter-gst-content.c
new file mode 100644
index 0000000..2a8fea7
--- /dev/null
+++ b/clutter-gst/clutter-gst-content.c
@@ -0,0 +1,397 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * Authored By Lionel Landwerlin <lionel g landwerlin linux intel com>
+ *             Bastian Winkler   <buz netbuz org>
+ *
+ * Copyright (C) 2013 Intel Corporation
+ * Copyright (C) 2013 Bastian Winkler <buz netbuz org>
+ *
+ * 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 "clutter-gst-content.h"
+#include "clutter-gst-private.h"
+#include "clutter-gst-marshal.h"
+
+static void clutter_content_iface_init (ClutterContentIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterGstContent, clutter_gst_content, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+                                                clutter_content_iface_init));
+
+#define CLUTTER_GST_CONTENT_GET_PRIVATE(obj)\
+  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+  CLUTTER_GST_TYPE_CONTENT, \
+  ClutterGstContentPrivate))
+
+
+struct _ClutterGstContentPrivate
+{
+  CoglGstVideoSink *sink;
+  ClutterGstFrame *current_frame;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_VIDEO_SINK,
+
+  PROP_LAST
+};
+
+static GParamSpec *props[PROP_LAST];
+
+enum
+{
+  SIZE_CHANGE,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+
+static void
+update_frame (ClutterGstContent *self,
+              CoglPipeline      *pipeline)
+{
+  ClutterGstContentPrivate *priv = self->priv;
+  ClutterGstFrame *old_frame, *new_frame;
+
+  old_frame = priv->current_frame;
+  new_frame = clutter_gst_frame_new (pipeline);
+  priv->current_frame = new_frame;
+
+  new_frame->resolution.par_n = old_frame->resolution.par_n;
+  new_frame->resolution.par_d = old_frame->resolution.par_d;
+
+  if (new_frame->resolution.width != old_frame->resolution.width ||
+      new_frame->resolution.height != old_frame->resolution.height)
+    {
+      g_signal_emit (self, signals[SIZE_CHANGE], 0,
+                     new_frame->resolution.width,
+                     new_frame->resolution.height);
+    }
+  if (old_frame)
+    g_boxed_free (CLUTTER_GST_TYPE_FRAME, old_frame);
+}
+
+static void
+_new_frame_from_pipeline (CoglGstVideoSink  *sink,
+                          ClutterGstContent *self)
+{
+  update_frame (self, cogl_gst_video_sink_get_pipeline (sink));
+
+  clutter_content_invalidate (CLUTTER_CONTENT (self));
+}
+
+static void
+_pixel_aspect_ratio_changed (CoglGstVideoSink  *sink,
+                             GParamSpec        *pspec,
+                             ClutterGstContent *self)
+{
+  clutter_gst_frame_update_pixel_aspect_ratio (self->priv->current_frame,
+                                               sink);
+}
+
+static void
+content_set_sink (ClutterGstContent *self,
+                  CoglGstVideoSink  *sink)
+{
+  ClutterGstContentPrivate *priv = self->priv;
+
+  if (priv->sink == sink)
+    return;
+
+  if (priv->sink)
+    {
+      g_signal_handlers_disconnect_by_func (priv->sink,
+                                            _new_frame_from_pipeline, self);
+      g_signal_handlers_disconnect_by_func (priv->sink,
+                                            _pixel_aspect_ratio_changed, self);
+      g_clear_object (&priv->sink);
+    }
+
+  if (sink)
+    {
+      CoglPipeline *pipeline;
+
+      priv->sink = g_object_ref_sink (sink);
+      g_signal_connect (priv->sink, "new-frame",
+                        G_CALLBACK (_new_frame_from_pipeline), self);
+      g_signal_connect (priv->sink, "notify::pixel-aspect-ratio",
+                        G_CALLBACK (_pixel_aspect_ratio_changed), self);
+
+      pipeline = cogl_gst_video_sink_get_pipeline (priv->sink);
+      if (pipeline)
+        update_frame (self, pipeline);
+    }
+
+  g_object_notify (G_OBJECT (self), "video-sink");
+}
+
+static gboolean
+clutter_gst_content_get_preferred_size (ClutterContent *content,
+                                        gfloat         *width,
+                                        gfloat         *height)
+{
+  ClutterGstContentPrivate *priv = CLUTTER_GST_CONTENT (content)->priv;
+
+  if (!priv->current_frame)
+    return FALSE;
+
+  if (width)
+    *width = priv->current_frame->resolution.width;
+  if (height)
+    *height = priv->current_frame->resolution.height;
+
+  return TRUE;
+}
+
+static void
+clutter_gst_content_paint_content (ClutterContent   *content,
+                                   ClutterActor     *actor,
+                                   ClutterPaintNode *root)
+{
+  ClutterGstContentPrivate *priv = CLUTTER_GST_CONTENT (content)->priv;
+  ClutterActorBox box;
+  ClutterPaintNode *node;
+  ClutterContentRepeat repeat;
+  guint8 paint_opacity;
+
+  if (!priv->current_frame)
+    return;
+
+  clutter_actor_get_content_box (actor, &box);
+  paint_opacity = clutter_actor_get_paint_opacity (actor);
+  repeat = clutter_actor_get_content_repeat (actor);
+
+
+  cogl_pipeline_set_color4ub (priv->current_frame->pipeline,
+                              paint_opacity, paint_opacity,
+                              paint_opacity, paint_opacity);
+
+  node = clutter_pipeline_node_new (priv->current_frame->pipeline);
+  clutter_paint_node_set_name (node, "Video");
+
+  if (repeat == CLUTTER_REPEAT_NONE)
+    clutter_paint_node_add_rectangle (node, &box);
+  else
+    {
+      float t_w = 1.f, t_h = 1.f;
+
+      if ((repeat & CLUTTER_REPEAT_X_AXIS) != FALSE)
+        t_w = (box.x2 - box.x1) / priv->current_frame->resolution.width;
+
+      if ((repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE)
+        t_h = (box.y2 - box.y1) / priv->current_frame->resolution.height;
+
+      clutter_paint_node_add_texture_rectangle (node, &box,
+                                                0.f, 0.f,
+                                                t_w, t_h);
+    }
+
+  clutter_paint_node_add_child (root, node);
+  clutter_paint_node_unref (node);
+}
+
+static void
+clutter_content_iface_init (ClutterContentIface *iface)
+{
+  iface->get_preferred_size = clutter_gst_content_get_preferred_size;
+  iface->paint_content = clutter_gst_content_paint_content;
+}
+
+static void
+clutter_gst_content_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  ClutterGstContent *self = CLUTTER_GST_CONTENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_VIDEO_SINK:
+      content_set_sink (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_gst_content_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  ClutterGstContentPrivate *priv = CLUTTER_GST_CONTENT (object)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_VIDEO_SINK:
+      g_value_set_object (value, priv->sink);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_gst_content_dispose (GObject *object)
+{
+  ClutterGstContentPrivate *priv = CLUTTER_GST_CONTENT (object)->priv;
+
+  g_clear_object (&priv->sink);
+
+  if (priv->current_frame)
+    {
+      g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->current_frame);
+      priv->current_frame = NULL;
+    }
+
+  G_OBJECT_CLASS (clutter_gst_content_parent_class)->dispose (object);
+}
+
+static void
+clutter_gst_content_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (clutter_gst_content_parent_class)->finalize (object);
+}
+
+static void
+clutter_gst_content_class_init (ClutterGstContentClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property   = clutter_gst_content_set_property;
+  gobject_class->get_property   = clutter_gst_content_get_property;
+  gobject_class->dispose        = clutter_gst_content_dispose;
+  gobject_class->finalize       = clutter_gst_content_finalize;
+
+  g_type_class_add_private (klass, sizeof (ClutterGstContentPrivate));
+
+  props[PROP_VIDEO_SINK] =
+    g_param_spec_object ("video-sink",
+                         "video-sink",
+                         "video-sink",
+                         COGL_GST_TYPE_VIDEO_SINK,
+                         G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+  g_object_class_install_properties (gobject_class, PROP_LAST, props);
+
+
+  /**
+   * ClutterGstContent::size-change:
+   * @content: the #ClutterGstContent instance that received the signal
+   * @width: new width of the frames
+   * @height: new height of the frames
+   *
+   * The ::size-change signal is emitted each time the video size changes.
+   */
+  signals[SIZE_CHANGE] =
+    g_signal_new ("size-change",
+                  CLUTTER_GST_TYPE_CONTENT,
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  _clutter_gst_marshal_VOID__INT_INT,
+                  G_TYPE_NONE, 2,
+                  G_TYPE_INT, G_TYPE_INT);
+}
+
+
+static void
+clutter_gst_content_init (ClutterGstContent *self)
+{
+  ClutterGstContentPrivate *priv;
+
+  self->priv = priv = CLUTTER_GST_CONTENT_GET_PRIVATE (self);
+  priv->sink = NULL;
+  priv->current_frame = clutter_gst_create_blank_frame (NULL);
+}
+
+
+/**
+ * clutter_gst_content_new:
+ *
+ * Returns: (transfer full): a new #ClutterGstContent instance
+ */
+ClutterContent *
+clutter_gst_content_new (void)
+{
+  CoglGstVideoSink *sink;
+
+  sink = cogl_gst_video_sink_new (clutter_gst_get_cogl_context ());
+  return g_object_new (CLUTTER_GST_TYPE_CONTENT,
+                       "video-sink", sink,
+                       NULL);
+}
+
+/**
+ * clutter_gst_content_new_with_sink:
+ *
+ * Returns: (transfer full): a new #ClutterGstContent instance
+ */
+ClutterContent *
+clutter_gst_content_new_with_sink (CoglGstVideoSink *sink)
+{
+  return g_object_new (CLUTTER_GST_TYPE_CONTENT,
+                       "video-sink", sink,
+                       NULL);
+}
+
+
+/**
+ * clutter_gst_content_set_sink:
+ * @self: A #ClutterGstContent
+ * @sink: A #CoglGstVideoSink or %NULL
+ */
+void
+clutter_gst_content_set_sink (ClutterGstContent *self,
+                              CoglGstVideoSink  *sink)
+{
+  g_return_if_fail (CLUTTER_GST_IS_CONTENT (self));
+  g_return_if_fail (sink == NULL || COGL_GST_IS_VIDEO_SINK (sink));
+
+  content_set_sink (self, sink);
+}
+
+/**
+ * clutter_gst_content_get_sink:
+ * @self: A #ClutterGstContent
+ *
+ * Returns: (transfer none):
+ */
+CoglGstVideoSink *
+clutter_gst_content_get_sink (ClutterGstContent *self)
+{
+  g_return_val_if_fail (CLUTTER_GST_IS_CONTENT (self), NULL);
+
+  return self->priv->sink;
+}
diff --git a/clutter-gst/clutter-gst-content.h b/clutter-gst/clutter-gst-content.h
new file mode 100644
index 0000000..15d1337
--- /dev/null
+++ b/clutter-gst/clutter-gst-content.h
@@ -0,0 +1,91 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * Copyright (C) 2013 Bastian Winkler <buz netbuz org>
+ *
+ * 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.
+ */
+
+#ifndef __CLUTTER_GST_CONTENT_H__
+#define __CLUTTER_GST_CONTENT_H__
+
+#include <glib-object.h>
+
+#include <cogl-gst/cogl-gst.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+
+#define CLUTTER_GST_TYPE_CONTENT            (clutter_gst_content_get_type())
+#define CLUTTER_GST_CONTENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_GST_TYPE_CONTENT, 
ClutterGstContent))
+#define CLUTTER_GST_IS_CONTENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_GST_TYPE_CONTENT))
+#define CLUTTER_GST_CONTENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_GST_TYPE_CONTENT, 
ClutterGstContentClass))
+#define CLUTTER_GST_IS_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_GST_TYPE_CONTENT))
+#define CLUTTER_GST_CONTENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_GST_TYPE_CONTENT, 
ClutterGstContentClass))
+
+
+typedef struct _ClutterGstContent            ClutterGstContent;
+typedef struct _ClutterGstContentPrivate     ClutterGstContentPrivate;
+typedef struct _ClutterGstContentClass       ClutterGstContentClass;
+
+
+/**
+ * ClutterGstContent:
+ *
+ * The #ClutterGstContent structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 0.0
+ */
+struct _ClutterGstContent
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  ClutterGstContentPrivate *priv;
+};
+
+/**
+ * ClutterGstContentClass:
+ *
+ * The #ClutterGstContentClass structure contains only private data
+ * and should be accessed using the provided API
+ *
+ * Since: 0.0
+ */
+struct _ClutterGstContentClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+};
+
+GType                     clutter_gst_content_get_type      (void) G_GNUC_CONST;
+
+ClutterContent *          clutter_gst_content_new           (void);
+
+ClutterContent *          clutter_gst_content_new_with_sink (CoglGstVideoSink *sink);
+
+void                      clutter_gst_content_set_sink      (ClutterGstContent *self,
+                                                             CoglGstVideoSink  *sink);
+
+CoglGstVideoSink *        clutter_gst_content_get_sink      (ClutterGstContent *self);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_GST_CONTENT_H__ */
diff --git a/clutter-gst/clutter-gst.h b/clutter-gst/clutter-gst.h
index b517766..be2aadc 100644
--- a/clutter-gst/clutter-gst.h
+++ b/clutter-gst/clutter-gst.h
@@ -36,6 +36,7 @@
 #include "clutter-gst-aspectratio.h"
 #include "clutter-gst-camera-device.h"
 #include "clutter-gst-camera.h"
+#include "clutter-gst-content.h"
 #include "clutter-gst-crop.h"
 #include "clutter-gst-pipeline.h"
 #include "clutter-gst-playback.h"
diff --git a/examples/.gitignore b/examples/.gitignore
index c71f574..9fe58de 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -1,4 +1,5 @@
 camera-player
+video-content
 video-player
 video-sink
 video-sink-navigation
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 0052d75..f8117ff 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1,6 +1,6 @@
 NULL = #
 
-noinst_PROGRAMS = camera-player video-player video-sink video-sink-navigation
+noinst_PROGRAMS = camera-player video-player video-sink video-sink-navigation video-content
 
 INCLUDES = -I$(top_srcdir) \
           $(MAINTAINER_CFLAGS) \
@@ -34,6 +34,13 @@ video_sink_navigation_LDFLAGS = \
     $(GST_LIBS)   \
     $(top_builddir)/clutter-gst/libclutter-gst- CLUTTER_GST_MAJORMINOR@.la
 
+video_content_SOURCES = video-content.c
+video_content_CFLAGS = $(CLUTTER_GST_CFLAGS) $(GST_CFLAGS)
+video_content_LDFLAGS = \
+    $(CLUTTER_GST_LIBS) \
+    $(GST_LIBS)   \
+    $(top_builddir)/clutter-gst/libclutter-gst- CLUTTER_GST_MAJORMINOR@.la
+
 EXTRA_DIST = \
        media-actions-pause.png \
        media-actions-start.png \
diff --git a/examples/video-content.c b/examples/video-content.c
new file mode 100644
index 0000000..c7368cc
--- /dev/null
+++ b/examples/video-content.c
@@ -0,0 +1,172 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * Copyright (C) 2013 Bastian Winkler <buz netbuz org>
+ *
+ * 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 <stdlib.h>
+#include <cogl-gst/cogl-gst.h>
+#include <clutter-gst/clutter-gst.h>
+
+
+static const struct {
+  ClutterContentGravity gravity;
+  const char *name;
+} gravities[] = {
+  { CLUTTER_CONTENT_GRAVITY_TOP_LEFT, "Top Left" },
+  { CLUTTER_CONTENT_GRAVITY_TOP, "Top" },
+  { CLUTTER_CONTENT_GRAVITY_TOP_RIGHT, "Top Right" },
+  { CLUTTER_CONTENT_GRAVITY_LEFT, "Left" },
+  { CLUTTER_CONTENT_GRAVITY_CENTER, "Center" },
+  { CLUTTER_CONTENT_GRAVITY_RIGHT, "Right" },
+  { CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT, "Bottom Left" },
+  { CLUTTER_CONTENT_GRAVITY_BOTTOM, "Bottom" },
+  { CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT, "Bottom Right" },
+  { CLUTTER_CONTENT_GRAVITY_RESIZE_FILL, "Resize Fill" },
+  { CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT, "Resize Aspect" },
+};
+
+static int n_gravities = G_N_ELEMENTS (gravities);
+static int cur_gravity = 0;
+
+static const struct {
+    ClutterContentRepeat repeat;
+    const gchar *name;
+} repeats[] = {
+  { CLUTTER_REPEAT_NONE, "None" },
+  { CLUTTER_REPEAT_X_AXIS, "X-Axis" },
+  { CLUTTER_REPEAT_Y_AXIS, "Y-Axis" },
+  { CLUTTER_REPEAT_BOTH, "Both" },
+};
+
+static int n_repeats = G_N_ELEMENTS (repeats);
+static int cur_repeat = 0;
+
+
+static GstElement *pipeline = NULL;
+
+static gboolean
+on_key_press (ClutterActor *stage,
+              ClutterEvent *event,
+              ClutterActor *actor)
+{
+
+  switch (clutter_event_get_key_symbol (event))
+    {
+    case CLUTTER_KEY_r:
+      clutter_actor_set_content_repeat (actor, repeats[cur_repeat].repeat);
+      g_print ("Content repeat: %s\n", repeats[cur_repeat].name);
+      cur_repeat += 1;
+      if (cur_repeat >= n_repeats)
+        cur_repeat = 0;
+      break;
+
+    case CLUTTER_KEY_q:
+      clutter_main_quit ();
+      break;
+
+    case CLUTTER_KEY_g:
+      clutter_actor_save_easing_state (actor);
+      clutter_actor_set_content_gravity (actor, gravities[cur_gravity].gravity);
+      clutter_actor_restore_easing_state (actor);
+      g_print ("Content gravity: %s\n", gravities[cur_gravity].name);
+      cur_gravity += 1;
+
+      if (cur_gravity >= n_gravities)
+        cur_gravity = 0;
+      break;
+
+    case CLUTTER_KEY_Left:
+        {
+          gint64 pos, dur;
+
+          if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur))
+            break;
+          if (!gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos))
+            break;
+
+          gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
+                                   GST_SEEK_FLAG_FLUSH,
+                                   CLAMP (pos - GST_SECOND * 10, 0, dur));
+          break;
+        }
+
+    case CLUTTER_KEY_Right:
+        {
+          gint64 pos, dur;
+
+          if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur))
+            break;
+          if (!gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos))
+            break;
+
+          gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
+                                   GST_SEEK_FLAG_FLUSH,
+                                   CLAMP (pos + GST_SECOND * 10, 0, dur));
+          break;
+        }
+
+    default:
+      return CLUTTER_EVENT_PROPAGATE;
+    }
+
+  return CLUTTER_EVENT_STOP;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  ClutterActor *stage, *actor;
+  ClutterContent *video;
+  CoglGstVideoSink *video_sink;
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+  gst_init (&argc, &argv);
+
+  stage = clutter_stage_new ();
+  g_signal_connect (stage, "destroy",
+                    G_CALLBACK (clutter_main_quit), NULL);
+  clutter_stage_set_fullscreen (CLUTTER_STAGE (stage), TRUE);
+
+  video = clutter_gst_content_new ();
+  video_sink = clutter_gst_content_get_sink (CLUTTER_GST_CONTENT (video));
+
+  actor = clutter_actor_new ();
+  clutter_actor_set_reactive (actor, TRUE);
+  clutter_actor_set_background_color (actor, CLUTTER_COLOR_Black);
+  clutter_actor_add_constraint (actor, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.f));
+  clutter_actor_set_content_gravity (actor, gravities[n_gravities - 1].gravity);
+  clutter_actor_set_content (actor, video);
+  clutter_actor_add_child (stage, actor);
+
+  g_signal_connect (stage, "key-press-event",
+                    G_CALLBACK (on_key_press), actor);
+
+  pipeline = gst_element_factory_make ("playbin", NULL);
+  g_object_set (pipeline, "uri", argv[1], "video-sink", video_sink, NULL);
+  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+  clutter_actor_show (stage);
+  clutter_main ();
+
+  return 0;
+}


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