diff options
author | Bastian Winkler <buz@netbuz.org> | 2013-05-03 15:40:07 +0200 |
---|---|---|
committer | Lionel Landwerlin <llandwerlin@gmail.com> | 2013-07-08 12:09:35 +0100 |
commit | a661cbdca1c5a807ab48017cd4c93c149f878b0b (patch) | |
tree | e250c4b8e9716ebcafe387e2f953fa6138e33825 | |
parent | 90577622f044adb722154ba42a19a81a9a7d56d1 (diff) | |
download | clutter-gst-a661cbdca1c5a807ab48017cd4c93c149f878b0b.tar.gz |
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
-rw-r--r-- | clutter-gst/Makefile.am | 2 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-content.c | 397 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-content.h | 91 | ||||
-rw-r--r-- | clutter-gst/clutter-gst.h | 1 | ||||
-rw-r--r-- | examples/.gitignore | 1 | ||||
-rw-r--r-- | examples/Makefile.am | 9 | ||||
-rw-r--r-- | 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; +} |