diff options
25 files changed, 4610 insertions, 5331 deletions
diff --git a/clutter-gst/Makefile.am b/clutter-gst/Makefile.am index 49f1763..2feae3d 100644 --- a/clutter-gst/Makefile.am +++ b/clutter-gst/Makefile.am @@ -25,11 +25,10 @@ source_h = \ $(srcdir)/clutter-gst-types.h \ $(srcdir)/clutter-gst-util.h \ $(srcdir)/clutter-gst-version.h \ - $(srcdir)/clutter-gst-actor.h \ - $(srcdir)/clutter-gst-video-sink.h \ - $(srcdir)/clutter-gst-video-actor.h \ - $(srcdir)/clutter-gst-camera-actor.h \ + $(srcdir)/clutter-gst-actor.h \ + $(srcdir)/clutter-gst-camera.h \ $(srcdir)/clutter-gst-camera-device.h \ + $(srcdir)/clutter-gst-playback.h \ $(srcdir)/clutter-gst-player.h \ $(NULL) @@ -41,13 +40,13 @@ source_priv_h = \ source_c = \ $(srcdir)/clutter-gst-debug.c \ + $(srcdir)/clutter-gst-types.c \ $(srcdir)/clutter-gst-marshal.c \ $(srcdir)/clutter-gst-player.c \ $(srcdir)/clutter-gst-actor.c \ - $(srcdir)/clutter-gst-video-sink.c \ - $(srcdir)/clutter-gst-video-actor.c \ - $(srcdir)/clutter-gst-camera-actor.c \ + $(srcdir)/clutter-gst-camera.c \ $(srcdir)/clutter-gst-camera-device.c \ + $(srcdir)/clutter-gst-playback.c \ $(srcdir)/clutter-gst-util.c \ $(glib_enum_c) \ $(NULL) @@ -102,8 +101,7 @@ cluttergstheaders_HEADERS = $(source_h) $(glib_enum_h) # plugin_source_c = \ - $(srcdir)/clutter-gst-plugin.c \ - $(srcdir)/clutter-gst-video-sink.c \ + $(srcdir)/clutter-gst-plugin.c \ $(srcdir)/clutter-gst-auto-video-sink.c \ $(srcdir)/clutter-gst-auto-video-sink.h \ $(NULL) @@ -139,7 +137,9 @@ ClutterGst-@CLUTTER_GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libclutter-gs --add-include-path=$(srcdir) --add-include=path=. \ --c-include="clutter-gst/clutter-gst.h" \ --include=GObject-2.0 \ + --include=Cogl-1.0 \ --include=Clutter-1.0 \ + --include=GdkPixbuf-2.0 \ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-1.0` \ --include=Gst-1.0 \ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-base-1.0` \ @@ -148,12 +148,16 @@ ClutterGst-@CLUTTER_GST_API_VERSION@.gir: $(INTROSPECTION_SCANNER) libclutter-gs --include=GstVideo-1.0 \ --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-audio-1.0` \ --include=GstAudio-1.0 \ + --add-include-path=`$(PKG_CONFIG) --variable=girdir gstreamer-pbutils-1.0` \ + --include=GstPbutils-1.0 \ --add-init-section="clutter_gst_init(0,NULL);" \ --library=libclutter-gst-@CLUTTER_GST_API_VERSION@.la \ --libtool="$(top_builddir)/libtool" \ --output $@ \ --pkg gobject-2.0 \ + --pkg cogl-1.0 \ --pkg clutter-1.0 \ + --pkg gdk-pixbuf-2.0 \ --pkg gstreamer-1.0 \ --pkg gstreamer-base-1.0 \ --pkg-export clutter-gst-@CLUTTER_GST_API_VERSION@ \ @@ -178,6 +182,7 @@ typelibs_DATA = $(BUILT_GIRSOURCES:.gir=.typelib) --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-base-1.0` \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-video-1.0` \ --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-audio-1.0` \ + --includedir=`$(PKG_CONFIG) --variable=girdir gstreamer-pbutils-1.0` \ $(INTROSPECTION_COMPILER_OPTS) $< -o $(builddir)/$(@F) CLEANFILES += $(BUILT_GIRSOURCES) $(typelibs_DATA) diff --git a/clutter-gst/clutter-gst-actor.c b/clutter-gst/clutter-gst-actor.c index b36c01a..c33fe35 100644 --- a/clutter-gst/clutter-gst-actor.c +++ b/clutter-gst/clutter-gst-actor.c @@ -11,7 +11,7 @@ * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> * * Copyright (C) 2006 OpenedHand - * Copyright (C) 2010, 2011 Intel Corporation + * Copyright (C) 2010-2013 Intel Corporation * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> * * This library is free software; you can redistribute it and/or @@ -56,365 +56,73 @@ struct _ClutterGstActorPrivate { - CoglPipeline *pipeline; - - /* width / height (in pixels) of the frame data before applying the pixel - * aspect ratio */ - gint buffer_width; - gint buffer_height; - - /* Pixel aspect ration is par_n / par_d. this is set by the sink */ - guint par_n, par_d; - - /* natural width / height (in pixels) of the actor (after par applied) */ - guint texture_width; - guint texture_height; - - CoglHandle idle_material; - CoglColor idle_color_unpre; + ClutterGstPlayer *player; + ClutterGstFrame *frame; }; static CoglPipeline *texture_template_pipeline = NULL; enum { PROP_0, - PROP_TEXTURE, - PROP_MATERIAL, - PROP_IDLE, - PROP_IDLE_MATERIAL, - PROP_PAR -}; -enum -{ - SIZE_CHANGE, - LAST_SIGNAL + PROP_PLAYER }; -static int actor_signals[LAST_SIGNAL] = { 0 }; - G_DEFINE_TYPE (ClutterGstActor, clutter_gst_actor, CLUTTER_TYPE_ACTOR) -/* Clutter 1.4 has this symbol, we don't want to depend on 1.4 just for that - * just yet */ -static void -_cogl_color_unpremultiply (CoglColor *color) -{ - gfloat alpha; - - alpha = cogl_color_get_alpha (color); - - if (alpha != 0) - { - gfloat red, green, blue; - - red = cogl_color_get_red (color); - green = cogl_color_get_green (color); - blue = cogl_color_get_blue (color); - - red = red / alpha; - green = green / alpha; - blue = blue / alpha; - - cogl_color_set_from_4f (color, red, green, blue, alpha); - } -} - -/* Clutter 1.4 has this symbol, we don't want to depend on 1.4 just for that - * just yet */ -static void -_cogl_color_set_alpha_byte (CoglColor *color, - unsigned char alpha) -{ - unsigned char red, green, blue; - - red = cogl_color_get_red_byte (color); - green = cogl_color_get_green_byte (color); - blue = cogl_color_get_blue_byte (color); - - cogl_color_set_from_4ub (color, red, green, blue, alpha); -} - static void -texture_free_gl_resources (ClutterGstActor *actor) +clutter_gst_actor_paint_frame (ClutterGstActor *self, + ClutterGstFrame *frame) { - ClutterGstActorPrivate *priv = actor->priv; - - if (priv->pipeline != NULL) - { - /* We want to keep the layer so that the filter settings will - remain but we want to free its resources so we clear the - texture handle */ - cogl_pipeline_set_layer_texture (priv->pipeline, 0, NULL); - } -} - -static void -gen_texcoords_and_draw_cogl_rectangle (ClutterActor *actor) -{ - ClutterActorBox box; - - clutter_actor_get_allocation_box (actor, &box); - - cogl_rectangle_with_texture_coords (0, 0, - box.x2 - box.x1, - box.y2 - box.y1, - 0, 0, 1.0, 1.0); -} - -static void -create_black_idle_material (ClutterGstActor *actor) -{ - ClutterGstActorPrivate *priv = actor->priv; - - priv->idle_material = cogl_material_new (); - cogl_color_set_from_4ub (&priv->idle_color_unpre, 0, 0, 0, 0xff); - cogl_material_set_color (priv->idle_material, &priv->idle_color_unpre); -} - -/* - * ClutterActor implementation - */ - -static gboolean -clutter_gst_actor_get_paint_volume (ClutterActor *actor, - ClutterPaintVolume *volume) -{ - ClutterGstActorPrivate *priv = CLUTTER_GST_ACTOR (actor)->priv; + ClutterGstActorPrivate *priv = self->priv; ClutterActorBox box; - - if (priv->pipeline == NULL) - return FALSE; - - if (priv->buffer_width == 0 || priv->buffer_height == 0) - return FALSE; - - /* calling clutter_actor_get_allocation_* can potentially be very - * expensive, as it can result in a synchronous full stage relayout - * and redraw - */ - if (!clutter_actor_has_allocation (actor)) - return FALSE; - - clutter_actor_get_allocation_box (actor, &box); - - /* we only set the width and height, as the paint volume is defined - * to be relative to the actor's modelview, which means that the - * allocation's origin has already been applied - */ - clutter_paint_volume_set_width (volume, box.x2 - box.x1); - clutter_paint_volume_set_height (volume, box.y2 - box.y1); - - return TRUE; -} - -static void -clutter_gst_actor_get_natural_size (ClutterGstActor *actor, - gfloat *width, - gfloat *height) -{ - ClutterGstActorPrivate *priv = actor->priv; - guint dar_n, dar_d; - gboolean ret; - - /* we cache texture_width and texture_height */ - - if (G_UNLIKELY (priv->buffer_width == 0 || priv->buffer_height == 0)) - { - /* we don't know the size of the frames yet default to 0,0 */ - priv->texture_width = 0; - priv->texture_height = 0; - } - else if (G_UNLIKELY (priv->texture_width == 0 || priv->texture_height == 0)) - { - CLUTTER_GST_NOTE (ASPECT_RATIO, "frame is %dx%d with par %d/%d", - priv->buffer_width, priv->buffer_height, - priv->par_n, priv->par_d); - - ret = gst_video_calculate_display_ratio (&dar_n, &dar_d, - priv->buffer_width, - priv->buffer_height, - priv->par_n, priv->par_d, - 1, 1); - if (ret == FALSE) - dar_n = dar_d = 1; - - if (priv->buffer_height % dar_d == 0) - { - priv->texture_width = gst_util_uint64_scale (priv->buffer_height, - dar_n, dar_d); - priv->texture_height = priv->buffer_height; - } - else if (priv->buffer_width % dar_n == 0) - { - priv->texture_width = priv->buffer_width; - priv->texture_height = gst_util_uint64_scale (priv->buffer_width, - dar_d, dar_n); - - } - else - { - priv->texture_width = gst_util_uint64_scale (priv->buffer_height, - dar_n, dar_d); - priv->texture_height = priv->buffer_height; - } - - CLUTTER_GST_NOTE (ASPECT_RATIO, - "final size is %dx%d (calculated par is %d/%d)", - priv->texture_width, priv->texture_height, - dar_n, dar_d); - } - - if (width) - *width = (gfloat)priv->texture_width; - - if (height) - *height = (gfloat)priv->texture_height; -} - -static void -clutter_gst_actor_get_preferred_width (ClutterActor *self, - gfloat for_height, - gfloat *min_width_p, - gfloat *natural_width_p) -{ - ClutterGstActor *actor = CLUTTER_GST_ACTOR (self); - ClutterGstActorPrivate *priv = actor->priv; - gfloat natural_width, natural_height; - - /* Min request is always 0 since we can scale down or clip */ - if (min_width_p) - *min_width_p = 0; - - if (natural_width_p) - { - clutter_gst_actor_get_natural_size (actor, - &natural_width, - &natural_height); - - if (for_height < 0 || - priv->buffer_height <= 0) - { - *natural_width_p = natural_width; - } - else - { - gfloat ratio = natural_width / natural_height; - - *natural_width_p = ratio * for_height; - } - } -} - -static void -clutter_gst_actor_get_preferred_height (ClutterActor *self, - gfloat for_width, - gfloat *min_height_p, - gfloat *natural_height_p) -{ - ClutterGstActor *actor = CLUTTER_GST_ACTOR (self); - ClutterGstActorPrivate *priv = actor->priv; - gfloat natural_width, natural_height; - - /* Min request is always 0 since we can scale down or clip */ - if (min_height_p) - *min_height_p = 0; - - if (natural_height_p) - { - clutter_gst_actor_get_natural_size (actor, - &natural_width, - &natural_height); - - if (for_width < 0 || - priv->buffer_width <= 0) - { - *natural_height_p = natural_height; - } - else - { - gfloat ratio = natural_height / natural_width; - - *natural_height_p = ratio * for_width; - } - } + guint8 paint_opacity; + + clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &box); + paint_opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)); + cogl_pipeline_set_color4ub (priv->frame->pipeline, + paint_opacity, + paint_opacity, + paint_opacity, + paint_opacity); + cogl_set_source (frame->pipeline); + + cogl_rectangle (0, 0, box.x2 - box.x1, box.y2 - box.y1); } static void clutter_gst_actor_paint (ClutterActor *actor) { - ClutterGstActorPrivate *priv = CLUTTER_GST_ACTOR (actor)->priv; - gboolean is_idle; + ClutterGstActor *self = CLUTTER_GST_ACTOR (actor); + ClutterGstActorPrivate *priv = self->priv; - is_idle = clutter_gst_actor_is_idle (CLUTTER_GST_ACTOR (actor)); - if (G_UNLIKELY (is_idle) || !priv->pipeline) + if (priv->player) { - CoglColor *color; - gfloat alpha; + ClutterGstFrame *frame = clutter_gst_player_get_frame (priv->player); - /* blend the alpha of the idle material with the actor's opacity */ - color = cogl_color_copy (&priv->idle_color_unpre); - alpha = clutter_actor_get_paint_opacity (actor) * - cogl_color_get_alpha_byte (color) / 0xff; - _cogl_color_set_alpha_byte (color, alpha); - cogl_color_premultiply (color); - cogl_material_set_color (priv->idle_material, color); - - cogl_set_source (priv->idle_material); - - /* draw */ - gen_texcoords_and_draw_cogl_rectangle (actor); - } - else - { - guint8 paint_opacity; - - paint_opacity = clutter_actor_get_paint_opacity (actor); - cogl_pipeline_set_color4ub (priv->pipeline, - paint_opacity, - paint_opacity, - paint_opacity, - paint_opacity); - cogl_set_source (priv->pipeline); - - gen_texcoords_and_draw_cogl_rectangle (actor); + if (frame) + CLUTTER_GST_ACTOR_GET_CLASS (self)->paint_frame (self, frame); } } /* - * ClutterGstActor implementation - */ - -static gboolean -clutter_gst_actor_is_idle_impl (ClutterGstActor *actor) -{ - ClutterGstActorPrivate *priv = actor->priv; - - return !priv->pipeline; -} - -/* * GObject implementation */ static void -clutter_gst_actor_finalize (GObject *object) +clutter_gst_actor_dispose (GObject *object) { - ClutterGstActor *actor = CLUTTER_GST_ACTOR (object); - ClutterGstActorPrivate *priv = actor->priv; + ClutterGstActorPrivate *priv = CLUTTER_GST_ACTOR (object)->priv; - texture_free_gl_resources (actor); + g_clear_object (&priv->player); - if (priv->pipeline != NULL) + if (priv->frame) { - cogl_object_unref (priv->pipeline); - priv->pipeline = NULL; + g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->frame); + priv->frame = NULL; } - if (priv->idle_material != COGL_INVALID_HANDLE) - cogl_handle_unref (priv->idle_material); - - G_OBJECT_CLASS (clutter_gst_actor_parent_class)->finalize (object); + G_OBJECT_CLASS (clutter_gst_actor_parent_class)->dispose (object); } static void @@ -428,20 +136,11 @@ clutter_gst_actor_get_property (GObject *object, switch (property_id) { - case PROP_TEXTURE: - g_value_set_boxed (value, clutter_gst_actor_get_cogl_texture (actor)); - break; - case PROP_MATERIAL: - g_value_set_boxed (value, clutter_gst_actor_get_cogl_material (actor)); - break; - case PROP_IDLE: - g_value_set_boolean (value, clutter_gst_actor_is_idle (actor)); - break; - case PROP_IDLE_MATERIAL: - g_value_set_boxed (value, priv->idle_material); - break; - case PROP_PAR: - gst_value_set_fraction (value, priv->par_n, priv->par_d); + /* case PROP_PAR: */ + /* gst_value_set_fraction (value, priv->par_n, priv->par_d); */ + /* break; */ + case PROP_PLAYER: + g_value_set_object (value, priv->player); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -459,18 +158,13 @@ clutter_gst_actor_set_property (GObject *object, switch (property_id) { - case PROP_TEXTURE: - clutter_gst_actor_set_cogl_texture (actor, g_value_get_boxed (value)); - break; - case PROP_MATERIAL: - clutter_gst_actor_set_cogl_material (actor, g_value_get_boxed (value)); - break; - case PROP_IDLE_MATERIAL: - clutter_gst_actor_set_idle_material (actor, g_value_get_boxed (value)); - break; - case PROP_PAR: - priv->par_n = gst_value_get_fraction_numerator (value); - priv->par_d = gst_value_get_fraction_denominator (value); + /* case PROP_PAR: */ + /* priv->par_n = gst_value_get_fraction_numerator (value); */ + /* priv->par_d = gst_value_get_fraction_denominator (value); */ + /* break; */ + case PROP_PLAYER: + g_clear_object (&priv->player); + priv->player = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -486,92 +180,27 @@ clutter_gst_actor_class_init (ClutterGstActorClass *klass) g_type_class_add_private (klass, sizeof (ClutterGstActorPrivate)); - klass->is_idle = clutter_gst_actor_is_idle_impl; - - object_class->finalize = clutter_gst_actor_finalize; + object_class->dispose = clutter_gst_actor_dispose; object_class->set_property = clutter_gst_actor_set_property; object_class->get_property = clutter_gst_actor_get_property; - actor_class->get_paint_volume = clutter_gst_actor_get_paint_volume; - actor_class->get_preferred_width = clutter_gst_actor_get_preferred_width; - actor_class->get_preferred_height = clutter_gst_actor_get_preferred_height; actor_class->paint = clutter_gst_actor_paint; - /** - * ClutterGstActor:idle: - * - * Whether the #ClutterGstActor is in idle mode. - */ - pspec = g_param_spec_boolean ("idle", - "Idle", - "Idle state of the actor", - TRUE, - CLUTTER_GST_PARAM_READABLE); - g_object_class_install_property (object_class, PROP_IDLE, pspec); - - /** - * ClutterGstActor:texture: - * - * Texture to use for drawing when not in idle. - */ - pspec = g_param_spec_boxed ("texture", - "Texture", - "Texture to use for drawing when not in idle", - COGL_TYPE_HANDLE, - CLUTTER_GST_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_TEXTURE, pspec); - - /** - * ClutterGstActor:material: - * - * Material to use for drawing when not in idle. - */ - pspec = g_param_spec_boxed ("material", - "Material", - "Material to use for drawing when not in idle", - COGL_TYPE_HANDLE, - CLUTTER_GST_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_MATERIAL, pspec); - - /** - * ClutterGstActor:idle-material: - * - * Material to use for drawing when in idle. - */ - pspec = g_param_spec_boxed ("idle-material", - "Idle material", - "Material to use for drawing when in idle", - COGL_TYPE_HANDLE, - CLUTTER_GST_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_IDLE_MATERIAL, pspec); - - pspec = gst_param_spec_fraction ("pixel-aspect-ratio", - "Pixel Aspect Ratio", - "Pixel aspect ratio of incoming frames", - 1, 100, 100, 1, 1, 1, - CLUTTER_GST_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_PAR, pspec); - - /** - * ClutterGstActor::size-change: - * @actor: the actor which received the signal - * @width: the width of the new actor - * @height: the height of the new actor - * - * The ::size-change signal is emitted each time the size of the - * pixbuf used by @actor changes. The new size is given as - * argument to the callback. - */ - actor_signals[SIZE_CHANGE] = - g_signal_new ("size-change", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterGstActorClass, size_change), - NULL, NULL, - _clutter_gst_marshal_VOID__INT_INT, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_INT); + klass->paint_frame = clutter_gst_actor_paint_frame; + + /* pspec = gst_param_spec_fraction ("pixel-aspect-ratio", */ + /* "Pixel Aspect Ratio", */ + /* "Pixel aspect ratio of incoming frames", */ + /* 1, 100, 100, 1, 1, 1, */ + /* CLUTTER_GST_PARAM_READWRITE); */ + /* g_object_class_install_property (object_class, PROP_PAR, pspec); */ + + pspec = g_param_spec_object ("player", + "Player", + "Player", + G_TYPE_OBJECT, + CLUTTER_GST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_PLAYER, pspec); } static void @@ -586,65 +215,23 @@ idle_cb (ClutterGstActor *actor, static void clutter_gst_actor_init (ClutterGstActor *actor) { - ClutterGstActorPrivate *priv; - - actor->priv = priv = - G_TYPE_INSTANCE_GET_PRIVATE (actor, - CLUTTER_GST_TYPE_ACTOR, - ClutterGstActorPrivate); - - create_black_idle_material (actor); - - if (G_UNLIKELY (texture_template_pipeline == NULL)) - { - CoglPipeline *pipeline; - CoglContext *ctx = - clutter_backend_get_cogl_context (clutter_get_default_backend ()); - - texture_template_pipeline = cogl_pipeline_new (ctx); - pipeline = COGL_PIPELINE (texture_template_pipeline); - cogl_pipeline_set_layer_null_texture (pipeline, - 0, /* layer_index */ - COGL_TEXTURE_TYPE_2D); - } - - g_assert (texture_template_pipeline != NULL); - priv->pipeline = cogl_pipeline_copy (texture_template_pipeline); - - priv->par_n = priv->par_d = 1; - - g_signal_connect (actor, "notify::idle", - G_CALLBACK (idle_cb), - NULL); + actor->priv = G_TYPE_INSTANCE_GET_PRIVATE (actor, + CLUTTER_GST_TYPE_ACTOR, + ClutterGstActorPrivate); } -typedef struct _GetLayerState -{ - gboolean has_layer; - int first_layer; -} GetLayerState; - -static gboolean -layer_cb (CoglPipeline *pipeline, int layer, void *user_data) +static void +_player_new_frame (ClutterGstPlayer *player, + ClutterGstFrame *frame, + ClutterGstActor *self) { - GetLayerState *state = user_data; - - state->has_layer = TRUE; - state->first_layer = layer; - - /* We only care about the first layer. */ - return FALSE; -} + ClutterGstActorPrivate *priv = self->priv; -static gboolean -get_first_layer_index (CoglPipeline *pipeline, int *layer_index) -{ - GetLayerState state = { FALSE }; - cogl_pipeline_foreach_layer (pipeline, layer_cb, &state); - if (state.has_layer) - *layer_index = state.first_layer; + if (priv->frame) + g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->frame); + priv->frame = g_boxed_copy (CLUTTER_GST_TYPE_FRAME, frame); - return state.has_layer; + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } /* @@ -652,238 +239,67 @@ get_first_layer_index (CoglPipeline *pipeline, int *layer_index) */ /** - * clutter_gst_actor_get_cogl_texture: - * @actor: A #ClutterActor + * clutter_gst_actor_get_player: + * @self: a #ClutterGstActor * - * Retrieves the handle to the underlying COGL texture used for drawing - * the actor. No extra reference is taken so if you need to keep the - * handle then you should call cogl_handle_ref() on it. + * Retrieves the #ClutterGstPlayer used by the @self. * - * The texture handle returned is the first layer of the material - * handle used by the #ClutterActor. If you need to access the other - * layers you should use clutter_gst_actor_get_cogl_material() instead - * and use the #CoglMaterial API. + * Return value: (transfer none): the #ClutterGstPlayer element used by the actor * - * Return value: (transfer none): a #CoglHandle for the texture. The returned - * handle is owned by the #ClutterActor and it should not be unreferenced + * Since: 3.0 */ -CoglHandle -clutter_gst_actor_get_cogl_texture (ClutterGstActor *actor) +ClutterGstPlayer * +clutter_gst_actor_get_player (ClutterGstActor *self) { ClutterGstActorPrivate *priv; - int layer_index; - - g_return_val_if_fail (CLUTTER_GST_IS_ACTOR (actor), NULL); - - priv = actor->priv; - - if (get_first_layer_index (priv->pipeline, &layer_index)) - return cogl_pipeline_get_layer_texture (priv->pipeline, layer_index); - - return NULL; -} - -/** - * clutter_gst_actor_set_cogl_texture: - * @actor: A #ClutterActor - * @cogl_tex: A CoglHandle for a texture - * - * Replaces the underlying COGL texture drawn by this actor with - * @cogl_tex. A reference to the texture is taken so if the handle is - * no longer needed it should be deref'd with cogl_handle_unref. - */ -void -clutter_gst_actor_set_cogl_texture (ClutterGstActor *actor, - CoglHandle cogl_tex) -{ - ClutterGstActorPrivate *priv; - gboolean size_changed; - guint width, height; - - g_return_if_fail (CLUTTER_GST_IS_ACTOR (actor)); - g_return_if_fail (cogl_is_texture (cogl_tex)); - - /* This function can set the texture without the actor being - realized. This is ok because Clutter requires that the GL context - always be current so there is no point in waiting to realization - to set the texture. */ - - priv = actor->priv; - - width = cogl_texture_get_width (cogl_tex); - height = cogl_texture_get_height (cogl_tex); - - /* Reference the new texture now in case it is the same one we are - already using */ - cogl_object_ref (cogl_tex); - - /* Remove old texture */ - texture_free_gl_resources (actor); - - /* Use the new texture */ - if (priv->pipeline == NULL) - priv->pipeline = cogl_pipeline_copy (texture_template_pipeline); - - g_assert (priv->pipeline != NULL); - cogl_pipeline_set_layer_texture (priv->pipeline, 0, cogl_tex); - /* The pipeline now holds a reference to the texture so we can - safely release the reference we claimed above */ - cogl_object_unref (cogl_tex); + g_return_val_if_fail (CLUTTER_GST_IS_ACTOR (self), NULL); - size_changed = (width != (guint) priv->buffer_width || height != (guint) priv->buffer_height); - priv->buffer_width = width; - priv->buffer_height = height; + priv = self->priv; - if (size_changed) - { - priv->texture_width = priv->texture_height = 0; - - /* queue a relayout to ask containers/layout manager to ask for - * the preferred size again */ - clutter_actor_queue_relayout (CLUTTER_ACTOR (actor)); - - g_signal_emit (actor, actor_signals[SIZE_CHANGE], 0, - priv->buffer_width, - priv->buffer_height); - } - - /* If resized actor may need resizing but paint() will do this */ - clutter_actor_queue_redraw (CLUTTER_ACTOR (actor)); - - g_object_notify (G_OBJECT (actor), "texture"); + return priv->player; } /** - * clutter_gst_actor_get_cogl_material: - * @actor: A #ClutterActor + * clutter_gst_actor_get_player: + * @self: a #ClutterGstActor * - * Returns a handle to the underlying COGL material used for drawing - * the actor. + * Retrieves the #ClutterGstPlayer used by the @self. * - * Return value: (transfer none): a handle for a #CoglMaterial. The - * material is owned by the #ClutterActor and it should not be - * unreferenced - */ -CoglHandle -clutter_gst_actor_get_cogl_material (ClutterGstActor *actor) -{ - g_return_val_if_fail (CLUTTER_GST_IS_ACTOR (actor), NULL); - - return actor->priv->pipeline; -} - -/** - * clutter_gst_actor_set_cogl_material: - * @actor: A #ClutterActor - * @cogl_material: A CoglHandle for a material + * Return value: (transfer none): the #ClutterGstPlayer element used by the actor * - * Replaces the underlying Cogl material drawn by this actor with - * @cogl_material. A reference to the material is taken so if the - * handle is no longer needed it should be deref'd with - * cogl_handle_unref. Texture data is attached to the material so - * calling this function also replaces the Cogl - * texture. #ClutterActor requires that the material have a texture - * layer so you should set one on the material before calling this - * function. + * Since: 3.0 */ void -clutter_gst_actor_set_cogl_material (ClutterGstActor *actor, - CoglHandle cogl_material) -{ - CoglPipeline *cogl_pipeline = cogl_material; - CoglHandle cogl_texture; - - g_return_if_fail (CLUTTER_GST_IS_ACTOR (actor)); - - cogl_object_ref (cogl_pipeline); - - if (actor->priv->pipeline) - cogl_object_unref (actor->priv->pipeline); - - actor->priv->pipeline = cogl_pipeline; - - /* XXX: We are re-asserting the first layer of the new pipeline to ensure the - * priv state is in sync with the contents of the pipeline. */ - cogl_texture = clutter_gst_actor_get_cogl_texture (actor); - clutter_gst_actor_set_cogl_texture (actor, cogl_texture); - /* XXX: If we add support for more pipeline layers, this will need - * extending */ - - g_object_notify (G_OBJECT (actor), "material"); -} - -/** - * clutter_gst_actor_is_idle: - * @actor: a #ClutterGstActor - * - * Get the idle state of actor. - * - * Return value: TRUE if the actor is in idle, FALSE otherwise. - */ -gboolean -clutter_gst_actor_is_idle (ClutterGstActor *actor) +clutter_gst_actor_set_player (ClutterGstActor *self, + ClutterGstPlayer *player) { - ClutterGstActorClass *klass; - - g_return_val_if_fail (CLUTTER_GST_IS_ACTOR (actor), TRUE); - - klass = CLUTTER_GST_ACTOR_GET_CLASS (actor); - - return klass->is_idle (actor); -} + ClutterGstActorPrivate *priv; -/** - * clutter_gst_actor_get_idle_material: - * @actor: a #ClutterGstActor - * - * Retrieves the material used to draw when the actor is idle. - * - * Return value: (transfer none): the #CoglHandle of the idle material - */ -CoglHandle -clutter_gst_actor_get_idle_material (ClutterGstActor *actor) -{ - g_return_val_if_fail (CLUTTER_GST_IS_ACTOR (actor), - COGL_INVALID_HANDLE); + g_return_if_fail (CLUTTER_GST_IS_ACTOR (self)); + g_return_if_fail (CLUTTER_GST_IS_PLAYER (player) || player == NULL); - return actor->priv->idle_material; -} + priv = self->priv; -/** - * clutter_gst_actor_set_idle_material: - * @actor: a #ClutterGstActor - * @cogl_material: the handle of a Cogl material - * - * Sets a material to use to draw when the actor is idle. The - * #ClutterGstActor holds a reference of the @material. - * - * The default idle material will paint the #ClutterGstActor in black. - * If %COGL_INVALID_HANDLE is given as @cogl_material to this function, this - * default idle material will be used. - */ -void -clutter_gst_actor_set_idle_material (ClutterGstActor *actor, - CoglHandle cogl_material) -{ - ClutterGstActorPrivate *priv; + if (priv->player) { + g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->frame); + priv->frame = NULL; + g_signal_handlers_disconnect_by_func (priv->player, + _player_new_frame, + self); - g_return_if_fail (CLUTTER_GST_IS_ACTOR (actor)); + g_clear_object (&priv->player); + } - priv = actor->priv; - /* priv->idle_material always has a valid material */ - cogl_handle_unref (priv->idle_material); + if (player != NULL) { + priv->player = g_object_ref_sink (player); + priv->frame = g_boxed_copy (CLUTTER_GST_TYPE_FRAME, + clutter_gst_player_get_frame (player)); - if (cogl_material != COGL_INVALID_HANDLE) - { - priv->idle_material = cogl_handle_ref (cogl_material); - cogl_material_get_color (cogl_material, &priv->idle_color_unpre); - _cogl_color_unpremultiply (&priv->idle_color_unpre); - } - else - { - create_black_idle_material (actor); - } + g_signal_connect (priv->player, "new-frame", + G_CALLBACK (_player_new_frame), self); + } - g_object_notify (G_OBJECT (actor), "idle-material"); + clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); + g_object_notify (G_OBJECT (self), "player"); } diff --git a/clutter-gst/clutter-gst-actor.h b/clutter-gst/clutter-gst-actor.h index ab21550..baf4b8b 100644 --- a/clutter-gst/clutter-gst-actor.h +++ b/clutter-gst/clutter-gst-actor.h @@ -6,8 +6,10 @@ * clutter-gst-actor.h - ClutterActor using GStreamer * * Authored By Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> + * Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com> * * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2013 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,33 +36,36 @@ #include <glib-object.h> #include <clutter/clutter.h> -#include <gst/gstelement.h> #include <clutter-gst/clutter-gst-types.h> +#include <clutter-gst/clutter-gst-player.h> G_BEGIN_DECLS #define CLUTTER_GST_TYPE_ACTOR clutter_gst_actor_get_type() #define CLUTTER_GST_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_GST_TYPE_ACTOR, ClutterGstActor)) + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_GST_TYPE_ACTOR, \ + ClutterGstActor)) -#define CLUTTER_GST_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_GST_TYPE_ACTOR, ClutterGstActorClass)) +#define CLUTTER_GST_ACTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_GST_TYPE_ACTOR, \ + ClutterGstActorClass)) -#define CLUTTER_GST_IS_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_GST_TYPE_ACTOR)) +#define CLUTTER_GST_IS_ACTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_GST_TYPE_ACTOR)) -#define CLUTTER_GST_IS_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_GST_TYPE_ACTOR)) +#define CLUTTER_GST_IS_ACTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_GST_TYPE_ACTOR)) -#define CLUTTER_GST_ACTOR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_GST_TYPE_ACTOR, ClutterGstActorClass)) +#define CLUTTER_GST_ACTOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_GST_TYPE_ACTOR, \ + ClutterGstActorClass)) typedef struct _ClutterGstActor ClutterGstActor; typedef struct _ClutterGstActorClass ClutterGstActorClass; @@ -90,33 +95,23 @@ struct _ClutterGstActorClass ClutterActorClass parent_class; /*< public >*/ - void (* size_change) (ClutterGstActor *actor, - gint width, - gint height); - gboolean (* is_idle) (ClutterGstActor *actor); + void (* paint_frame) (ClutterGstActor *actor, ClutterGstFrame *frame); /* Future padding */ - void (* _clutter_reserved1) (void); void (* _clutter_reserved2) (void); void (* _clutter_reserved3) (void); void (* _clutter_reserved4) (void); void (* _clutter_reserved5) (void); void (* _clutter_reserved6) (void); + void (* _clutter_reserved7) (void); + void (* _clutter_reserved8) (void); }; GType clutter_gst_actor_get_type (void) G_GNUC_CONST; -CoglHandle clutter_gst_actor_get_cogl_texture (ClutterGstActor *actor); -void clutter_gst_actor_set_cogl_texture (ClutterGstActor *actor, - CoglHandle cogl_tex); -CoglHandle clutter_gst_actor_get_cogl_material (ClutterGstActor *actor); -void clutter_gst_actor_set_cogl_material (ClutterGstActor *actor, - CoglHandle cogl_material); - -gboolean clutter_gst_actor_is_idle (ClutterGstActor *actor); -CoglHandle clutter_gst_actor_get_idle_material (ClutterGstActor *actor); -void clutter_gst_actor_set_idle_material (ClutterGstActor *actor, - CoglHandle cogl_material); +ClutterGstPlayer *clutter_gst_actor_get_player (ClutterGstActor *self); +void clutter_gst_actor_set_player (ClutterGstActor *self, + ClutterGstPlayer *player); G_END_DECLS diff --git a/clutter-gst/clutter-gst-camera-actor.h b/clutter-gst/clutter-gst-camera-actor.h deleted file mode 100644 index bbbafbd..0000000 --- a/clutter-gst/clutter-gst-camera-actor.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Clutter-GStreamer. - * - * GStreamer integration library for Clutter. - * - * clutter-gst-camera-actor.h - ClutterActor using GStreamer to display/manipulate a - * camera stream. - * - * Authored By Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> - * - * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> - * - * 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. - */ - -#if !defined(__CLUTTER_GST_H_INSIDE__) && !defined(CLUTTER_GST_COMPILATION) -#error "Only <clutter-gst/clutter-gst.h> can be included directly." -#endif - -#ifndef __CLUTTER_GST_CAMERA_ACTOR_H__ -#define __CLUTTER_GST_CAMERA_ACTOR_H__ - -#include <clutter/clutter.h> -#include <gdk-pixbuf/gdk-pixbuf.h> -#include <glib-object.h> -#include <gst/gstelement.h> -#include <gst/pbutils/encoding-profile.h> - -#include <clutter-gst/clutter-gst-actor.h> -#include <clutter-gst/clutter-gst-camera-device.h> -#include <clutter-gst/clutter-gst-types.h> - -G_BEGIN_DECLS - -#define CLUTTER_GST_TYPE_CAMERA_ACTOR clutter_gst_camera_actor_get_type() - -#define CLUTTER_GST_CAMERA_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_GST_TYPE_CAMERA_ACTOR, ClutterGstCameraActor)) - -#define CLUTTER_GST_CAMERA_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_GST_TYPE_CAMERA_ACTOR, ClutterGstCameraActorClass)) - -#define CLUTTER_GST_IS_CAMERA_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_GST_TYPE_CAMERA_ACTOR)) - -#define CLUTTER_GST_IS_CAMERA_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_GST_TYPE_CAMERA_ACTOR)) - -#define CLUTTER_GST_CAMERA_ACTOR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_GST_TYPE_CAMERA_ACTOR, ClutterGstCameraActorClass)) - -typedef struct _ClutterGstCameraActor ClutterGstCameraActor; -typedef struct _ClutterGstCameraActorClass ClutterGstCameraActorClass; -typedef struct _ClutterGstCameraActorPrivate ClutterGstCameraActorPrivate; - -/** - * ClutterGstCameraActor: - * - * Subclass of #ClutterGstActor that displays camera streams using GStreamer. - * - * The #ClutterGstCameraActor structure contains only private data and - * should not be accessed directly. - */ -struct _ClutterGstCameraActor -{ - /*< private >*/ - ClutterGstActor parent; - ClutterGstCameraActorPrivate *priv; -}; - -/** - * ClutterGstCameraActorClass: - * - * Base class for #ClutterGstCameraActor. - */ -struct _ClutterGstCameraActorClass -{ - /*< private >*/ - ClutterGstActorClass parent_class; - - void (* ready_for_capture) (ClutterGstCameraActor *camera_actor, - gboolean ready); - void (* photo_saved) (ClutterGstCameraActor *camera_actor); - void (* photo_taken) (ClutterGstCameraActor *camera_actor, - GdkPixbuf *pixbuf); - void (* video_saved) (ClutterGstCameraActor *camera_actor); - - /* Future padding */ - void (* _clutter_reserved1) (void); - void (* _clutter_reserved2) (void); - void (* _clutter_reserved3) (void); - void (* _clutter_reserved4) (void); - void (* _clutter_reserved5) (void); - void (* _clutter_reserved6) (void); -}; - -GType clutter_gst_camera_actor_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_gst_camera_actor_new (void); - -GstElement * clutter_gst_camera_actor_get_pipeline (ClutterGstCameraActor *camera_actor); -GstElement * clutter_gst_camera_actor_get_camerabin (ClutterGstCameraActor *camera_actor); - -const GPtrArray * - clutter_gst_camera_actor_get_camera_devices (ClutterGstCameraActor *camera_actor); -ClutterGstCameraDevice * - clutter_gst_camera_actor_get_camera_device (ClutterGstCameraActor *camera_actor); -gboolean clutter_gst_camera_actor_set_camera_device - (ClutterGstCameraActor *camera_actor, - ClutterGstCameraDevice *camera_device); - -gboolean clutter_gst_camera_actor_supports_gamma_correction - (ClutterGstCameraActor *camera_actor); -gboolean clutter_gst_camera_actor_get_gamma_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_gamma (ClutterGstCameraActor *camera_actor, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_gamma (ClutterGstCameraActor *camera_actor, - gdouble value); - -gboolean clutter_gst_camera_actor_supports_color_balance - (ClutterGstCameraActor *camera_actor); -gboolean clutter_gst_camera_actor_get_color_balance_property_range - (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_color_balance_property - (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_color_balance_property - (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble value); -gboolean clutter_gst_camera_actor_get_brightness_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_brightness (ClutterGstCameraActor *camera_actor, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_brightness (ClutterGstCameraActor *camera_actor, - gdouble value); -gboolean clutter_gst_camera_actor_get_contrast_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_contrast (ClutterGstCameraActor *camera_actor, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_contrast (ClutterGstCameraActor *camera_actor, - gdouble value); -gboolean clutter_gst_camera_actor_get_saturation_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_saturation (ClutterGstCameraActor *camera_actor, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_saturation (ClutterGstCameraActor *camera_actor, - gdouble value); -gboolean clutter_gst_camera_actor_get_hue_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value); -gboolean clutter_gst_camera_actor_get_hue (ClutterGstCameraActor *camera_actor, - gdouble *cur_value); -gboolean clutter_gst_camera_actor_set_hue (ClutterGstCameraActor *camera_actor, - gdouble value); - -GstElement * clutter_gst_camera_actor_get_filter (ClutterGstCameraActor *camera_actor); -gboolean clutter_gst_camera_actor_set_filter (ClutterGstCameraActor *camera_actor, - GstElement *filter); -gboolean clutter_gst_camera_actor_remove_filter (ClutterGstCameraActor *camera_actor); - -gboolean clutter_gst_camera_actor_is_playing (ClutterGstCameraActor *camera_actor); -void clutter_gst_camera_actor_set_playing (ClutterGstCameraActor *camera_actor, - gboolean playing); - -gboolean clutter_gst_camera_actor_is_ready_for_capture (ClutterGstCameraActor *camera_actor); - -void clutter_gst_camera_actor_set_video_profile (ClutterGstCameraActor *camera_actor, - GstEncodingProfile *profile); -gboolean clutter_gst_camera_actor_is_recording_video (ClutterGstCameraActor *camera_actor); -gboolean clutter_gst_camera_actor_start_video_recording (ClutterGstCameraActor *camera_actor, - const gchar *filename); -void clutter_gst_camera_actor_stop_video_recording (ClutterGstCameraActor *camera_actor); - -void clutter_gst_camera_actor_set_photo_profile (ClutterGstCameraActor *camera_actor, - GstEncodingProfile *profile); -gboolean clutter_gst_camera_actor_take_photo (ClutterGstCameraActor *camera_actor, - const gchar *filename); -gboolean clutter_gst_camera_actor_take_photo_pixbuf (ClutterGstCameraActor *camera); - -G_END_DECLS - -#endif /* __CLUTTER_GST_CAMERA_ACTOR_H__ */ diff --git a/clutter-gst/clutter-gst-camera-actor.c b/clutter-gst/clutter-gst-camera.c index 4d87657..43a95f2 100644 --- a/clutter-gst/clutter-gst-camera-actor.c +++ b/clutter-gst/clutter-gst-camera.c @@ -3,8 +3,8 @@ * * GStreamer integration library for Clutter. * - * clutter-gst-camera-actor.c - ClutterActor using GStreamer to display/manipulate a - * camera stream. + * clutter-gst-camera.c - a GStreamer pipeline to display/manipulate a + * camera stream. * * Authored By Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> * @@ -27,10 +27,11 @@ */ /** - * SECTION:clutter-gst-camera-actor - * @short_description: Actor for playback of camera streams. + * SECTION:clutter-gst-camera + * @short_description: A player of camera streams. * - * #ClutterGstCameraActor is a #ClutterActor that plays camera streams. + * #ClutterGstCamera implements the #ClutterGstPlayer interface and + * plays camera streams. */ #ifdef HAVE_CONFIG_H @@ -47,10 +48,11 @@ #include <gudev/gudev.h> #endif -#include "clutter-gst-camera-actor.h" +#include "clutter-gst-camera.h" #include "clutter-gst-debug.h" #include "clutter-gst-enum-types.h" #include "clutter-gst-marshal.h" +#include "clutter-gst-player.h" #include "clutter-gst-private.h" static const gchar *supported_media_types[] = { @@ -58,7 +60,7 @@ static const gchar *supported_media_types[] = { NULL }; -struct _ClutterGstCameraActorPrivate +struct _ClutterGstCameraPrivate { GPtrArray *camera_devices; ClutterGstCameraDevice *camera_device; @@ -90,6 +92,15 @@ enum enum { + PROP_0, + + PROP_IDLE, + PROP_PLAYING, + PROP_AUDIO_VOLUME +}; + +enum +{ READY_FOR_CAPTURE, PHOTO_SAVED, PHOTO_TAKEN, @@ -97,33 +108,154 @@ enum LAST_SIGNAL }; -static int camera_actor_signals[LAST_SIGNAL] = { 0 }; +static int camera_signals[LAST_SIGNAL] = { 0 }; + +static void player_iface_init (ClutterGstPlayerIface *iface); -G_DEFINE_TYPE (ClutterGstCameraActor, - clutter_gst_camera_actor, - CLUTTER_GST_TYPE_ACTOR); +G_DEFINE_TYPE_WITH_CODE (ClutterGstCamera, clutter_gst_camera, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER, player_iface_init)); /* - * ClutterGstActor implementation + * ClutterGstPlayer implementation */ +static GstElement * +clutter_gst_camera_get_pipeline (ClutterGstPlayer *player) +{ + ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv; + + return priv->camerabin; +} + static gboolean -clutter_gst_camera_actor_is_idle (ClutterGstActor *actor) +clutter_gst_camera_get_idle (ClutterGstPlayer *player) { - ClutterGstCameraActorPrivate *priv = CLUTTER_GST_CAMERA_ACTOR (actor)->priv; + ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv; return priv->is_idle; } +static gdouble +clutter_gst_camera_get_audio_volume (ClutterGstPlayer *player) +{ + return 0; +} + + +static void +clutter_gst_camera_set_audio_volume (ClutterGstPlayer *player, + gdouble volume) +{ +} + +static gboolean +clutter_gst_camera_get_playing (ClutterGstPlayer *player) +{ + ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv; + GstState state, pending; + gboolean playing; + + if (!priv->camerabin) + return FALSE; + + gst_element_get_state (priv->camerabin, &state, &pending, 0); + + if (pending) + playing = (pending == GST_STATE_PLAYING); + else + playing = (state == GST_STATE_PLAYING); + + return playing; +} + +static void +clutter_gst_camera_set_playing (ClutterGstPlayer *player, + gboolean playing) +{ + ClutterGstCameraPrivate *priv = CLUTTER_GST_CAMERA (player)->priv; + GstState target_state; + + if (!priv->camerabin) + return; + + target_state = playing ? GST_STATE_PLAYING : GST_STATE_NULL; + + gst_element_set_state (priv->camerabin, target_state); +} + +static void +player_iface_init (ClutterGstPlayerIface *iface) +{ + iface->get_pipeline = clutter_gst_camera_get_pipeline; + iface->get_idle = clutter_gst_camera_get_idle; + + iface->get_audio_volume = clutter_gst_camera_get_audio_volume; + iface->set_audio_volume = clutter_gst_camera_set_audio_volume; + + iface->get_playing = clutter_gst_camera_get_playing; + iface->set_playing = clutter_gst_camera_set_playing; +} + /* * GObject implementation */ static void -clutter_gst_camera_actor_dispose (GObject *object) +clutter_gst_camera_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_IDLE: + g_value_set_boolean (value, + clutter_gst_camera_get_idle (CLUTTER_GST_PLAYER (object))); + break; + + case PROP_PLAYING: + g_value_set_boolean (value, + clutter_gst_camera_get_playing (CLUTTER_GST_PLAYER (object))); + break; + + case PROP_AUDIO_VOLUME: + g_value_set_double (value, + clutter_gst_camera_get_audio_volume (CLUTTER_GST_PLAYER (object))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +clutter_gst_camera_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) + { + case PROP_PLAYING: + clutter_gst_camera_set_playing (CLUTTER_GST_PLAYER (object), + g_value_get_boolean (value)); + break; + + case PROP_AUDIO_VOLUME: + clutter_gst_camera_set_audio_volume (CLUTTER_GST_PLAYER (object), + g_value_get_double (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +clutter_gst_camera_dispose (GObject *object) { - ClutterGstCameraActor *camera_actor = CLUTTER_GST_CAMERA_ACTOR (object); - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCamera *self = CLUTTER_GST_CAMERA (object); + ClutterGstCameraPrivate *priv = self->priv; g_free (priv->photo_filename); priv->photo_filename = NULL; @@ -147,102 +279,111 @@ clutter_gst_camera_actor_dispose (GObject *object) priv->camerabin = NULL; } - G_OBJECT_CLASS (clutter_gst_camera_actor_parent_class)->dispose (object); + G_OBJECT_CLASS (clutter_gst_camera_parent_class)->dispose (object); } static void -clutter_gst_camera_actor_class_init (ClutterGstCameraActorClass *klass) +clutter_gst_camera_class_init (ClutterGstCameraClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterGstActorClass *gst_actor_class = CLUTTER_GST_ACTOR_CLASS (klass); - g_type_class_add_private (klass, sizeof (ClutterGstCameraActorPrivate)); + g_type_class_add_private (klass, sizeof (ClutterGstCameraPrivate)); + + object_class->get_property = clutter_gst_camera_get_property; + object_class->set_property = clutter_gst_camera_set_property; + object_class->dispose = clutter_gst_camera_dispose; - object_class->dispose = clutter_gst_camera_actor_dispose; + g_object_class_override_property (object_class, + PROP_IDLE, "idle"); + g_object_class_override_property (object_class, + PROP_PLAYING, "playing"); + g_object_class_override_property (object_class, + PROP_AUDIO_VOLUME, "audio-volume"); - gst_actor_class->is_idle = clutter_gst_camera_actor_is_idle; + /* Signals */ /** - * ClutterGstCameraActor::ready-for-capture: - * @camera_actor: the actor which received the signal - * @ready: whether the @camera_actor is ready for a new capture + * ClutterGstCamera::ready-for-capture: + * @self: the actor which received the signal + * @ready: whether the @self is ready for a new capture * * The ::ready-for-capture signal is emitted whenever the value of - * clutter_gst_camera_actor_is_ready_for_capture changes. + * clutter_gst_camera_is_ready_for_capture changes. */ - camera_actor_signals[READY_FOR_CAPTURE] = + camera_signals[READY_FOR_CAPTURE] = g_signal_new ("ready-for-capture", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterGstCameraActorClass, ready_for_capture), + G_STRUCT_OFFSET (ClutterGstCameraClass, ready_for_capture), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); /** - * ClutterGstCameraActor::photo-saved: - * @camera_actor: the actor which received the signal + * ClutterGstCamera::photo-saved: + * @self: the actor which received the signal * * The ::photo-saved signal is emitted when a photo was saved to disk. */ - camera_actor_signals[PHOTO_SAVED] = - g_signal_new ("photo-saved", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (ClutterGstCameraActorClass, photo_saved), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + camera_signals[PHOTO_SAVED] = + g_signal_new ("photo-saved", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ClutterGstCameraClass, photo_saved), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); /** - * ClutterGstCameraActor::photo-taken: - * @camera_actor: the actor which received the signal + * ClutterGstCamera::photo-taken: + * @self: the actor which received the signal * @pixbuf: the photo taken as a #GdkPixbuf * * The ::photo-taken signal is emitted when a photo was taken. */ - camera_actor_signals[PHOTO_TAKEN] = - g_signal_new ("photo-taken", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (ClutterGstCameraActorClass, photo_taken), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GDK_TYPE_PIXBUF); + camera_signals[PHOTO_TAKEN] = + g_signal_new ("photo-taken", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ClutterGstCameraClass, photo_taken), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, GDK_TYPE_PIXBUF); /** - * ClutterGstCameraActor::video-saved: - * @camera_actor: the actor which received the signal + * ClutterGstCamera::video-saved: + * @self: the actor which received the signal * * The ::video-saved signal is emitted when a video was saved to disk. */ - camera_actor_signals[VIDEO_SAVED] = - g_signal_new ("video-saved", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (ClutterGstCameraActorClass, video_saved), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + camera_signals[VIDEO_SAVED] = + g_signal_new ("video-saved", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (ClutterGstCameraClass, video_saved), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void notify_ready_for_capture (GObject *object, GParamSpec *pspec, - ClutterGstCameraActor *camera_actor) + ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; gboolean ready_for_capture; g_object_get (priv->camera_source, "ready-for-capture", &ready_for_capture, NULL); - g_signal_emit (camera_actor, camera_actor_signals[READY_FOR_CAPTURE], + g_signal_emit (self, camera_signals[READY_FOR_CAPTURE], 0, ready_for_capture); } static void -parse_photo_data (ClutterGstCameraActor *camera_actor, - GstSample *sample) +parse_photo_data (ClutterGstCamera *self, + GstSample *sample) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstBuffer *buffer; GstCaps *caps; const GstStructure *structure; @@ -270,106 +411,106 @@ parse_photo_data (ClutterGstCameraActor *camera_actor, data ? (GdkPixbufDestroyNotify) g_free : NULL, NULL); g_object_set (G_OBJECT (priv->camerabin), "post-previews", FALSE, NULL); - g_signal_emit (camera_actor, camera_actor_signals[PHOTO_TAKEN], 0, pixbuf); + g_signal_emit (self, camera_signals[PHOTO_TAKEN], 0, pixbuf); g_object_unref (pixbuf); } static void -bus_message_cb (GstBus *bus, - GstMessage *message, - ClutterGstCameraActor *camera_actor) +bus_message_cb (GstBus *bus, + GstMessage *message, + ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_ERROR: - { - GError *err = NULL; - gchar *debug = NULL; - - gst_message_parse_error (message, &err, &debug); - if (err && err->message) - g_warning ("%s", err->message); - else - g_warning ("Unparsable GST_MESSAGE_ERROR message."); - - if (err) - g_error_free (err); - g_free (debug); - - priv->is_idle = TRUE; - g_object_notify (G_OBJECT (camera_actor), "idle"); - break; - } - - case GST_MESSAGE_STATE_CHANGED: - { - if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0) - { - GstState new; - - gst_message_parse_state_changed (message, NULL, &new, NULL); - if (new == GST_STATE_PLAYING) - priv->is_idle = FALSE; - else - priv->is_idle = TRUE; - g_object_notify (G_OBJECT (camera_actor), "idle"); - } - break; - } - - case GST_MESSAGE_ELEMENT: - { - const GstStructure *structure; - const GValue *image; - - if (strcmp (GST_MESSAGE_SRC_NAME (message), "camera_source") == 0) - { - structure = gst_message_get_structure (message); - if (strcmp (gst_structure_get_name (structure), "preview-image") == 0) - { - if (gst_structure_has_field_typed (structure, "sample", GST_TYPE_SAMPLE)) - { - image = gst_structure_get_value (structure, "sample"); - if (image) - { - GstSample *sample; - - sample = gst_value_get_sample (image); - parse_photo_data (camera_actor, sample); - } - else - g_warning ("Could not get buffer from bus message"); - } - } - } - else if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0) - { - structure = gst_message_get_structure (message); - if (strcmp (gst_structure_get_name (structure), "image-done") == 0) - { - const gchar *filename = gst_structure_get_string (structure, "filename"); - if (priv->photo_filename != NULL && filename != NULL && - (strcmp (priv->photo_filename, filename) == 0)) - g_signal_emit (camera_actor, camera_actor_signals[PHOTO_SAVED], 0); - } - else if (strcmp (gst_structure_get_name (structure), "video-done") == 0) - { - g_signal_emit (camera_actor, camera_actor_signals[VIDEO_SAVED], 0); - priv->is_recording = FALSE; - } - } - break; - } + case GST_MESSAGE_ERROR: + { + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_error (message, &err, &debug); + if (err && err->message) + g_warning ("%s", err->message); + else + g_warning ("Unparsable GST_MESSAGE_ERROR message."); + + if (err) + g_error_free (err); + g_free (debug); + + priv->is_idle = TRUE; + g_object_notify (G_OBJECT (self), "idle"); + break; + } + + case GST_MESSAGE_STATE_CHANGED: + { + if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0) + { + GstState new; + + gst_message_parse_state_changed (message, NULL, &new, NULL); + if (new == GST_STATE_PLAYING) + priv->is_idle = FALSE; + else + priv->is_idle = TRUE; + g_object_notify (G_OBJECT (self), "idle"); + } + break; + } + + case GST_MESSAGE_ELEMENT: + { + const GstStructure *structure; + const GValue *image; + + if (strcmp (GST_MESSAGE_SRC_NAME (message), "camera_source") == 0) + { + structure = gst_message_get_structure (message); + if (strcmp (gst_structure_get_name (structure), "preview-image") == 0) + { + if (gst_structure_has_field_typed (structure, "sample", GST_TYPE_SAMPLE)) + { + image = gst_structure_get_value (structure, "sample"); + if (image) + { + GstSample *sample; + + sample = gst_value_get_sample (image); + parse_photo_data (self, sample); + } + else + g_warning ("Could not get buffer from bus message"); + } + } + } + else if (strcmp (GST_MESSAGE_SRC_NAME (message), "camerabin") == 0) + { + structure = gst_message_get_structure (message); + if (strcmp (gst_structure_get_name (structure), "image-done") == 0) + { + const gchar *filename = gst_structure_get_string (structure, "filename"); + if (priv->photo_filename != NULL && filename != NULL && + (strcmp (priv->photo_filename, filename) == 0)) + g_signal_emit (self, camera_signals[PHOTO_SAVED], 0); + } + else if (strcmp (gst_structure_get_name (structure), "video-done") == 0) + { + g_signal_emit (self, camera_signals[VIDEO_SAVED], 0); + priv->is_recording = FALSE; + } + } + break; + } default: - break; - } + break; + } } static void -set_video_profile (ClutterGstCameraActor *camera_actor) +set_video_profile (ClutterGstCamera *self) { GstEncodingContainerProfile *prof; GstEncodingAudioProfile *audio_prof; @@ -378,8 +519,8 @@ set_video_profile (ClutterGstCameraActor *camera_actor) caps = gst_caps_from_string ("application/ogg"); prof = gst_encoding_container_profile_new ("Ogg audio/video", - "Standard Ogg/Theora/Vorbis", - caps, NULL); + "Standard Ogg/Theora/Vorbis", + caps, NULL); gst_caps_unref (caps); caps = gst_caps_from_string ("video/x-theora"); @@ -392,16 +533,16 @@ set_video_profile (ClutterGstCameraActor *camera_actor) gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) audio_prof); gst_caps_unref (caps); - clutter_gst_camera_actor_set_video_profile (camera_actor, - (GstEncodingProfile *) prof); + clutter_gst_camera_set_video_profile (self, + (GstEncodingProfile *) prof); gst_encoding_profile_unref (prof); } static GstElement * -setup_video_filter_bin (ClutterGstCameraActor *camera_actor) +setup_video_filter_bin (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstElement *bin; GstPad *pad; @@ -438,7 +579,7 @@ setup_video_filter_bin (ClutterGstCameraActor *camera_actor) gst_object_unref (pad); return bin; -error: + error: if (priv->identity) gst_object_unref (priv->identity); if (priv->valve) @@ -453,14 +594,14 @@ error: gst_object_unref (priv->post_colorspace); return NULL; -error_not_linked: + error_not_linked: gst_object_unref (bin); return NULL; } static GstCaps * -create_caps_for_formats (gint width, - gint height) +create_caps_for_formats (gint width, + gint height) { GstCaps *ret = NULL; guint length; @@ -487,9 +628,9 @@ static void device_capture_resolution_changed (ClutterGstCameraDevice *camera_device, gint width, gint height, - ClutterGstCameraActor *camera_actor) + ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstCaps *caps; if (priv->camera_device != camera_device) @@ -503,45 +644,45 @@ device_capture_resolution_changed (ClutterGstCameraDevice *camera_device, } static void -set_device_resolutions (ClutterGstCameraActor *camera_actor, - ClutterGstCameraDevice *camera_device) +set_device_resolutions (ClutterGstCamera *self, + ClutterGstCameraDevice *device) { gint width; gint height; - clutter_gst_camera_device_get_capture_resolution (camera_device, &width, &height); - device_capture_resolution_changed (camera_device, width, height, camera_actor); + clutter_gst_camera_device_get_capture_resolution (device, &width, &height); + device_capture_resolution_changed (device, width, height, self); } static void -add_device (ClutterGstCameraActor *camera_actor, - GstElementFactory *element_factory, - const gchar *device_node, - const gchar *device_name) +add_device (ClutterGstCamera *self, + GstElementFactory *element_factory, + const gchar *device_node, + const gchar *device_name) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; - ClutterGstCameraDevice *camera_device; + ClutterGstCameraPrivate *priv = self->priv; + ClutterGstCameraDevice *device; if (!priv->camera_devices) priv->camera_devices = - g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - - camera_device = g_object_new (CLUTTER_GST_TYPE_CAMERA_DEVICE, - "element-factory", element_factory, - "node", device_node, - "name", device_name, - NULL); - g_signal_connect (camera_device, "capture-resolution-changed", + g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + + device = g_object_new (CLUTTER_GST_TYPE_CAMERA_DEVICE, + "element-factory", element_factory, + "node", device_node, + "name", device_name, + NULL); + g_signal_connect (device, "capture-resolution-changed", G_CALLBACK (device_capture_resolution_changed), - camera_actor); - g_ptr_array_add (priv->camera_devices, camera_device); + self); + g_ptr_array_add (priv->camera_devices, device); } static gboolean -probe_camera_devices (ClutterGstCameraActor *camera_actor) +probe_camera_devices (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstElement *videosrc; GstElementFactory *element_factory; GParamSpec *pspec; @@ -562,7 +703,7 @@ probe_camera_devices (ClutterGstCameraActor *camera_actor) } pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (G_OBJECT (videosrc)), "device"); + G_OBJECT_GET_CLASS (G_OBJECT (videosrc)), "device"); if (!G_IS_PARAM_SPEC_STRING (pspec)) { g_warning ("Unable to get available camera devices, " @@ -592,7 +733,7 @@ probe_camera_devices (ClutterGstCameraActor *camera_actor) device_node = (gchar *) g_udev_device_get_device_file (udevice); device_name = (gchar *) g_udev_device_get_property (udevice, "ID_V4L_PRODUCT"); - add_device (camera_actor, element_factory, device_node, device_name); + add_device (self, element_factory, device_node, device_name); } g_object_unref (udevice); @@ -604,21 +745,21 @@ probe_camera_devices (ClutterGstCameraActor *camera_actor) * device as only known device */ g_object_get (videosrc, "device", &device_node, NULL); g_object_get (videosrc, "device-name", &device_name, NULL); - add_device (camera_actor, element_factory, device_node, device_name); + add_device (self, element_factory, device_node, device_name); g_free (device_node); g_free (device_name); #endif -out: + out: gst_object_unref (videosrc); return (priv->camera_devices != NULL); } static gboolean -setup_camera_source (ClutterGstCameraActor *camera_actor) +setup_camera_source (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstElement *camera_source; GstElement *old_camera_source = NULL; @@ -637,7 +778,7 @@ setup_camera_source (ClutterGstCameraActor *camera_actor) g_signal_connect (camera_source, "notify::ready-for-capture", G_CALLBACK (notify_ready_for_capture), - camera_actor); + self); if (priv->video_filter_bin) { @@ -650,12 +791,12 @@ setup_camera_source (ClutterGstCameraActor *camera_actor) } static gboolean -setup_pipeline (ClutterGstCameraActor *camera_actor) +setup_pipeline (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv = camera_actor->priv; + ClutterGstCameraPrivate *priv = self->priv; GstElement *camera_sink; - if (!probe_camera_devices (camera_actor)) + if (!probe_camera_devices (self)) { g_critical ("Unable to find any suitable capture device"); return FALSE; @@ -668,11 +809,11 @@ setup_pipeline (ClutterGstCameraActor *camera_actor) return FALSE; } - priv->video_filter_bin = setup_video_filter_bin (camera_actor); + priv->video_filter_bin = setup_video_filter_bin (self); if (!priv->video_filter_bin) g_warning ("Unable to setup video filter, some features will be disabled"); - if (G_UNLIKELY (!setup_camera_source (camera_actor))) + if (G_UNLIKELY (!setup_camera_source (self))) { g_critical ("Unable to create camera source element"); gst_object_unref (priv->camerabin); @@ -680,8 +821,8 @@ setup_pipeline (ClutterGstCameraActor *camera_actor) return FALSE; } - if (!clutter_gst_camera_actor_set_camera_device (camera_actor, - g_ptr_array_index (priv->camera_devices, 0))) + if (!clutter_gst_camera_set_camera_device (self, + g_ptr_array_index (priv->camera_devices, 0))) { g_critical ("Unable to select capture device"); gst_object_unref (priv->camerabin); @@ -689,36 +830,33 @@ setup_pipeline (ClutterGstCameraActor *camera_actor) return FALSE; } - camera_sink = gst_element_factory_make ("cluttersink", NULL); - g_object_set (camera_sink, - "actor", CLUTTER_GST_ACTOR (camera_actor), - NULL); + camera_sink = gst_element_factory_make ("coglsink", NULL); g_object_set (priv->camerabin, "viewfinder-sink", camera_sink, NULL); - set_video_profile (camera_actor); + set_video_profile (self); priv->bus = gst_element_get_bus (priv->camerabin); gst_bus_add_signal_watch (priv->bus); g_signal_connect (G_OBJECT (priv->bus), "message", - G_CALLBACK (bus_message_cb), camera_actor); + G_CALLBACK (bus_message_cb), self); return TRUE; } static void -clutter_gst_camera_actor_init (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_init (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - camera_actor->priv = priv = - G_TYPE_INSTANCE_GET_PRIVATE (camera_actor, - CLUTTER_GST_TYPE_CAMERA_ACTOR, - ClutterGstCameraActorPrivate); + self->priv = priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, + CLUTTER_GST_TYPE_CAMERA, + ClutterGstCameraPrivate); - if (!setup_pipeline (camera_actor)) + if (!setup_pipeline (self)) { g_warning ("Failed to initiate suitable elements for pipeline."); return; @@ -732,7 +870,7 @@ clutter_gst_camera_actor_init (ClutterGstCameraActor *camera_actor) */ /** - * clutter_gst_camera_actor_new: + * clutter_gst_camera_new: * * Create a camera actor. * @@ -743,50 +881,50 @@ clutter_gst_camera_actor_init (ClutterGstCameraActor *camera_actor) * * Return value: the newly created camera actor */ -ClutterActor* -clutter_gst_camera_actor_new (void) +ClutterGstCamera * +clutter_gst_camera_new (void) { - return g_object_new (CLUTTER_GST_TYPE_CAMERA_ACTOR, + return g_object_new (CLUTTER_GST_TYPE_CAMERA, NULL); } -/** - * clutter_gst_camera_actor_get_pipeline: - * @camera_actor: a #ClutterGstCameraActor - * - * Retrieve the #GstPipeline used by the @camera_actor, for direct use with - * GStreamer API. - * - * Return value: (transfer none): the pipeline element used by the camera actor - */ -GstElement * -clutter_gst_camera_actor_get_pipeline (ClutterGstCameraActor *camera_actor) -{ - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), NULL); - - return camera_actor->priv->camerabin; -} - -/** - * clutter_gst_camera_actor_get_camerabin: - * @camera_actor: a #ClutterGstCameraActor - * - * Retrieve the camerabin element used by the @camera_actor, for direct use with - * GStreamer API. - * - * Return value: (transfer none): the pipeline element used by the camera actor - */ -GstElement * -clutter_gst_camera_actor_get_camerabin (ClutterGstCameraActor *camera_actor) -{ - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), NULL); - - return camera_actor->priv->camerabin; -} +/* /\** */ +/* * clutter_gst_camera_get_pipeline: */ +/* * @self: a #ClutterGstCamera */ +/* * */ +/* * Retrieve the #GstPipeline used by the @self, for direct use with */ +/* * GStreamer API. */ +/* * */ +/* * Return value: (transfer none): the pipeline element used by the camera actor */ +/* *\/ */ +/* GstElement * */ +/* clutter_gst_camera_get_pipeline (ClutterGstCamera *self) */ +/* { */ +/* g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); */ + +/* return self->priv->camerabin; */ +/* } */ + +/* /\** */ +/* * clutter_gst_camera_get_camerabin: */ +/* * @self: a #ClutterGstCamera */ +/* * */ +/* * Retrieve the camerabin element used by the @self, for direct use with */ +/* * GStreamer API. */ +/* * */ +/* * Return value: (transfer none): the pipeline element used by the camera actor */ +/* *\/ */ +/* GstElement * */ +/* clutter_gst_camera_get_camerabin (ClutterGstCamera *self) */ +/* { */ +/* g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); */ + +/* return self->priv->camerabin; */ +/* } */ /** - * clutter_gst_camera_actor_get_camera_devices: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_camera_devices: + * @self: a #ClutterGstCamera * * Retrieve an array of supported camera devices. * @@ -794,66 +932,66 @@ clutter_gst_camera_actor_get_camerabin (ClutterGstCameraActor *camera_actor) * the supported camera devices */ const GPtrArray * -clutter_gst_camera_actor_get_camera_devices (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_get_camera_devices (ClutterGstCamera *self) { - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), NULL); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); - return camera_actor->priv->camera_devices; + return self->priv->camera_devices; } /** - * clutter_gst_camera_actor_get_camera_device: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_camera_device: + * @self: a #ClutterGstCamera * * Retrieve the current selected camera device. * * Return value: (transfer none): The currently selected camera device */ ClutterGstCameraDevice * -clutter_gst_camera_actor_get_camera_device (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_get_camera_device (ClutterGstCamera *self) { - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), NULL); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), NULL); - return camera_actor->priv->camera_device; + return self->priv->camera_device; } /** - * clutter_gst_camera_actor_set_camera_device: - * @camera_actor: a #ClutterGstCameraActor - * @camera_device: a #ClutterGstCameraDevice + * clutter_gst_camera_set_camera_device: + * @self: a #ClutterGstCamera + * @device: a #ClutterGstCameraDevice * * Set the new active camera device. * * Return value: %TRUE on success, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_set_camera_device (ClutterGstCameraActor *camera_actor, - ClutterGstCameraDevice *camera_device) +clutter_gst_camera_set_camera_device (ClutterGstCamera *self, + ClutterGstCameraDevice *device) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GstElementFactory *element_factory; GstElement *src; gchar *node; gboolean was_playing = FALSE; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); - g_return_val_if_fail (camera_device != NULL, FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); + g_return_val_if_fail (device != NULL, FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return FALSE; if (priv->is_recording) - clutter_gst_camera_actor_stop_video_recording (camera_actor); + clutter_gst_camera_stop_video_recording (self); - if (clutter_gst_camera_actor_is_playing (camera_actor)) + if (clutter_gst_camera_is_playing (self)) { gst_element_set_state (priv->camerabin, GST_STATE_NULL); was_playing = TRUE; } - g_object_get (camera_device, + g_object_get (device, "element-factory", &element_factory, "node", &node, NULL); @@ -875,13 +1013,13 @@ clutter_gst_camera_actor_set_camera_device (ClutterGstCameraActor *camera_actor gst_object_unref (element_factory); - priv->camera_device = camera_device; + priv->camera_device = device; g_object_set (G_OBJECT (src), "device", node, NULL); g_free (node); g_object_set (G_OBJECT (priv->camera_source), "video-source", src, NULL); - set_device_resolutions (camera_actor, camera_device); + set_device_resolutions (self, device); if (was_playing) gst_element_set_state (priv->camerabin, GST_STATE_PLAYING); @@ -890,24 +1028,24 @@ clutter_gst_camera_actor_set_camera_device (ClutterGstCameraActor *camera_actor } /** - * clutter_gst_camera_actor_supports_gamma_correction: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_supports_gamma_correction: + * @self: a #ClutterGstCamera * - * Check whether the @camera_actor supports gamma correction. + * Check whether the @self supports gamma correction. * - * Return value: %TRUE if @camera_actor supports gamma correction, %FALSE otherwise + * Return value: %TRUE if @self supports gamma correction, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_supports_gamma_correction (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_supports_gamma_correction (ClutterGstCamera *self) { - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - return (camera_actor->priv->gamma != NULL); + return (self->priv->gamma != NULL); } /** - * clutter_gst_camera_actor_get_gamma_range: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_gamma_range: + * @self: a #ClutterGstCamera * @min_value: Pointer to store the minimum gamma value, or %NULL * @max_value: Pointer to store the maximum gamma value, or %NULL * @default_value: Pointer to store the default gamma value, or %NULL @@ -915,29 +1053,29 @@ clutter_gst_camera_actor_supports_gamma_correction (ClutterGstCameraActor *camer * Retrieve the minimum, maximum and default gamma values. * * This method will return FALSE if gamma correction is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_gamma_correction(). + * supported on @self. + * See clutter_gst_camera_supports_gamma_correction(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_get_gamma_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_gamma_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GParamSpec *pspec; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->gamma) return FALSE; pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (G_OBJECT (priv->gamma)), "gamma"); + G_OBJECT_GET_CLASS (G_OBJECT (priv->gamma)), "gamma"); /* shouldn't happen */ g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE); @@ -951,28 +1089,28 @@ clutter_gst_camera_actor_get_gamma_range (ClutterGstCameraActor *camera_actor, } /** - * clutter_gst_camera_actor_get_gamma: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_gamma: + * @self: a #ClutterGstCamera * @cur_value: Pointer to store the current gamma value * * Retrieve the current gamma value. * * This method will return FALSE if gamma correction is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_gamma_correction(). + * supported on @self. + * See clutter_gst_camera_supports_gamma_correction(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_get_gamma (ClutterGstCameraActor *camera_actor, - gdouble *cur_value) +clutter_gst_camera_get_gamma (ClutterGstCamera *self, + gdouble *cur_value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); g_return_val_if_fail (cur_value != NULL, FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->gamma) return FALSE; @@ -982,29 +1120,29 @@ clutter_gst_camera_actor_get_gamma (ClutterGstCameraActor *camera_actor, } /** - * clutter_gst_camera_actor_set_gamma: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_set_gamma: + * @self: a #ClutterGstCamera * @value: The value to set * * Set the gamma value. * Allowed values can be retrieved with - * clutter_gst_camera_actor_get_gamma_range(). + * clutter_gst_camera_get_gamma_range(). * * This method will return FALSE if gamma correction is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_gamma_correction(). + * supported on @self. + * See clutter_gst_camera_supports_gamma_correction(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_set_gamma (ClutterGstCameraActor *camera_actor, - gdouble value) +clutter_gst_camera_set_gamma (ClutterGstCamera *self, + gdouble value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->gamma) return FALSE; @@ -1014,24 +1152,24 @@ clutter_gst_camera_actor_set_gamma (ClutterGstCameraActor *camera_actor, } /** - * clutter_gst_camera_actor_supports_color_balance: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_supports_color_balance: + * @self: a #ClutterGstCamera * - * Check whether the @camera_actor supports color balance. + * Check whether the @self supports color balance. * - * Return value: %TRUE if @camera_actor supports color balance, %FALSE otherwise + * Return value: %TRUE if @self supports color balance, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_supports_color_balance (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_supports_color_balance (ClutterGstCamera *self) { - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - return (camera_actor->priv->color_balance != NULL); + return (self->priv->color_balance != NULL); } /** - * clutter_gst_camera_actor_get_color_balance_property_range: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_color_balance_property_range: + * @self: a #ClutterGstCamera * @property: Property name * @min_value: Pointer to store the minimum value of @property, or %NULL * @max_value: Pointer to store the maximum value of @property, or %NULL @@ -1040,30 +1178,30 @@ clutter_gst_camera_actor_supports_color_balance (ClutterGstCameraActor *camera_a * Retrieve the minimum, maximum and default values for the color balance property @property, * * This method will return FALSE if @property does not exist or color balance is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_color_balance(). + * supported on @self. + * See clutter_gst_camera_supports_color_balance(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_get_color_balance_property_range (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_color_balance_property_range (ClutterGstCamera *self, + const gchar *property, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GParamSpec *pspec; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->color_balance) return FALSE; pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); + G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE); if (min_value) @@ -1076,37 +1214,37 @@ clutter_gst_camera_actor_get_color_balance_property_range (ClutterGstCameraActor } /** - * clutter_gst_camera_actor_get_color_balance_property: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_color_balance_property: + * @self: a #ClutterGstCamera * @property: Property name * @cur_value: Pointer to store the current value of @property * * Retrieve the current value for the color balance property @property, * * This method will return FALSE if @property does not exist or color balance is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_color_balance(). + * supported on @self. + * See clutter_gst_camera_supports_color_balance(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_get_color_balance_property (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble *cur_value) +clutter_gst_camera_get_color_balance_property (ClutterGstCamera *self, + const gchar *property, + gdouble *cur_value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GParamSpec *pspec; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); g_return_val_if_fail (cur_value != NULL, FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->color_balance) return FALSE; pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); + G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE); g_object_get (G_OBJECT (priv->color_balance), property, cur_value, NULL); @@ -1114,38 +1252,38 @@ clutter_gst_camera_actor_get_color_balance_property (ClutterGstCameraActor *came } /** - * clutter_gst_camera_actor_set_color_balance_property: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_set_color_balance_property: + * @self: a #ClutterGstCamera * @property: Property name * @value: The value to set * * Set the value for the color balance property @property to @value. * Allowed values can be retrieved with - * clutter_gst_camera_actor_get_color_balance_property_range(). + * clutter_gst_camera_get_color_balance_property_range(). * * This method will return FALSE if @property does not exist or color balance is not - * supported on @camera_actor. - * See clutter_gst_camera_actor_supports_color_balance(). + * supported on @self. + * See clutter_gst_camera_supports_color_balance(). * * Return value: %TRUE if successful, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_set_color_balance_property (ClutterGstCameraActor *camera_actor, - const gchar *property, - gdouble value) +clutter_gst_camera_set_color_balance_property (ClutterGstCamera *self, + const gchar *property, + gdouble value) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GParamSpec *pspec; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->color_balance) return FALSE; pspec = g_object_class_find_property ( - G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); + G_OBJECT_GET_CLASS (G_OBJECT (priv->color_balance)), property); g_return_val_if_fail (G_IS_PARAM_SPEC_DOUBLE (pspec), FALSE); g_object_set (G_OBJECT (priv->color_balance), property, value, NULL); @@ -1153,123 +1291,123 @@ clutter_gst_camera_actor_set_color_balance_property (ClutterGstCameraActor *came } gboolean -clutter_gst_camera_actor_get_brightness_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_brightness_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - return clutter_gst_camera_actor_get_color_balance_property_range (camera_actor, - "brightness", min_value, max_value, default_value); + return clutter_gst_camera_get_color_balance_property_range (self, + "brightness", min_value, max_value, default_value); } gboolean -clutter_gst_camera_actor_get_brightness (ClutterGstCameraActor *camera_actor, - gdouble *cur_value) +clutter_gst_camera_get_brightness (ClutterGstCamera *self, + gdouble *cur_value) { - return clutter_gst_camera_actor_get_color_balance_property (camera_actor, - "brightness", cur_value); + return clutter_gst_camera_get_color_balance_property (self, + "brightness", cur_value); } gboolean -clutter_gst_camera_actor_set_brightness (ClutterGstCameraActor *camera_actor, - gdouble value) +clutter_gst_camera_set_brightness (ClutterGstCamera *self, + gdouble value) { - return clutter_gst_camera_actor_set_color_balance_property (camera_actor, - "brightness", value); + return clutter_gst_camera_set_color_balance_property (self, + "brightness", value); } gboolean -clutter_gst_camera_actor_get_contrast_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_contrast_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - return clutter_gst_camera_actor_get_color_balance_property_range (camera_actor, - "contrast", min_value, max_value, default_value); + return clutter_gst_camera_get_color_balance_property_range (self, + "contrast", min_value, max_value, default_value); } gboolean -clutter_gst_camera_actor_get_contrast (ClutterGstCameraActor *camera_actor, - gdouble *cur_value) +clutter_gst_camera_get_contrast (ClutterGstCamera *self, + gdouble *cur_value) { - return clutter_gst_camera_actor_get_color_balance_property (camera_actor, - "contrast", cur_value); + return clutter_gst_camera_get_color_balance_property (self, + "contrast", cur_value); } gboolean -clutter_gst_camera_actor_set_contrast (ClutterGstCameraActor *camera_actor, - gdouble value) +clutter_gst_camera_set_contrast (ClutterGstCamera *self, + gdouble value) { - return clutter_gst_camera_actor_set_color_balance_property (camera_actor, - "contrast", value); + return clutter_gst_camera_set_color_balance_property (self, + "contrast", value); } gboolean -clutter_gst_camera_actor_get_saturation_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_saturation_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - return clutter_gst_camera_actor_get_color_balance_property_range (camera_actor, - "saturation", min_value, max_value, default_value); + return clutter_gst_camera_get_color_balance_property_range (self, + "saturation", min_value, max_value, default_value); } gboolean -clutter_gst_camera_actor_get_saturation (ClutterGstCameraActor *camera_actor, - gdouble *cur_value) +clutter_gst_camera_get_saturation (ClutterGstCamera *self, + gdouble *cur_value) { - return clutter_gst_camera_actor_get_color_balance_property (camera_actor, - "saturation", cur_value); + return clutter_gst_camera_get_color_balance_property (self, + "saturation", cur_value); } gboolean -clutter_gst_camera_actor_set_saturation (ClutterGstCameraActor *camera_actor, - gdouble value) +clutter_gst_camera_set_saturation (ClutterGstCamera *self, + gdouble value) { - return clutter_gst_camera_actor_set_color_balance_property (camera_actor, - "saturation", value); + return clutter_gst_camera_set_color_balance_property (self, + "saturation", value); } gboolean -clutter_gst_camera_actor_get_hue_range (ClutterGstCameraActor *camera_actor, - gdouble *min_value, - gdouble *max_value, - gdouble *default_value) +clutter_gst_camera_get_hue_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value) { - return clutter_gst_camera_actor_get_color_balance_property_range (camera_actor, - "hue", min_value, max_value, default_value); + return clutter_gst_camera_get_color_balance_property_range (self, + "hue", min_value, max_value, default_value); } gboolean -clutter_gst_camera_actor_get_hue (ClutterGstCameraActor *camera_actor, - gdouble *cur_value) +clutter_gst_camera_get_hue (ClutterGstCamera *self, + gdouble *cur_value) { - return clutter_gst_camera_actor_get_color_balance_property (camera_actor, - "hue", cur_value); + return clutter_gst_camera_get_color_balance_property (self, + "hue", cur_value); } gboolean -clutter_gst_camera_actor_set_hue (ClutterGstCameraActor *camera_actor, - gdouble value) +clutter_gst_camera_set_hue (ClutterGstCamera *self, + gdouble value) { - return clutter_gst_camera_actor_set_color_balance_property (camera_actor, - "hue", value); + return clutter_gst_camera_set_color_balance_property (self, + "hue", value); } /** - * clutter_gst_camera_actor_get_filter: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_get_filter: + * @self: a #ClutterGstCamera * * Retrieve the current filter being used. * * Return value: (transfer none): The current filter or %NULL if none is set */ GstElement * -clutter_gst_camera_actor_get_filter (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_get_filter (ClutterGstCamera *self) { - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - return camera_actor->priv->custom_filter; + return self->priv->custom_filter; } static GstElement * @@ -1300,25 +1438,25 @@ create_filter_bin (GstElement *filter) gst_element_add_pad (filter_bin, gst_ghost_pad_new ("src", pad)); gst_object_unref (GST_OBJECT (pad)); -out: + out: return filter_bin; -err: + err: if (pre_filter_colorspace) gst_object_unref (pre_filter_colorspace); if (post_filter_colorspace) gst_object_unref (post_filter_colorspace); goto out; -err_not_linked: + err_not_linked: gst_object_unref (filter_bin); filter_bin = NULL; goto out; } /** - * clutter_gst_camera_actor_set_filter: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_set_filter: + * @self: a #ClutterGstCamera * @filter: a #GstElement for the filter * * Set the filter element to be used. @@ -1327,15 +1465,15 @@ err_not_linked: * Return value: %TRUE on success, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_set_filter (ClutterGstCameraActor *camera_actor, - GstElement *filter) +clutter_gst_camera_set_filter (ClutterGstCamera *self, + GstElement *filter) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; gboolean ret = FALSE; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->custom_filter && !filter) { @@ -1381,7 +1519,7 @@ clutter_gst_camera_actor_set_filter (ClutterGstCameraActor *camera_actor, goto err_restore; } - if (clutter_gst_camera_actor_is_playing (camera_actor)) + if (clutter_gst_camera_is_playing (self)) gst_element_set_state (priv->custom_filter, GST_STATE_PLAYING); } else @@ -1389,11 +1527,11 @@ clutter_gst_camera_actor_set_filter (ClutterGstCameraActor *camera_actor, ret = TRUE; -out: + out: g_object_set (G_OBJECT (priv->valve), "drop", FALSE, NULL); return ret; -err_restore: + err_restore: ret = FALSE; /* restore default pipeline, should always work */ gst_element_link (priv->valve, priv->gamma); @@ -1401,37 +1539,37 @@ err_restore: } /** - * clutter_gst_camera_actor_remove_filter: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_remove_filter: + * @self: a #ClutterGstCamera * * Remove the current filter, if any. * * Return value: %TRUE on success, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_remove_filter (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_remove_filter (ClutterGstCamera *self) { - return clutter_gst_camera_actor_set_filter (camera_actor, NULL); + return clutter_gst_camera_set_filter (self, NULL); } /** - * clutter_gst_camera_actor_is_playing: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_is_playing: + * @self: a #ClutterGstCamera * - * Retrieve whether the @camera_actor is playing. + * Retrieve whether the @self is playing. * * Return value: %TRUE if playing, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_is_playing (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_is_playing (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GstState state, pending; gboolean playing; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return FALSE; @@ -1446,48 +1584,22 @@ clutter_gst_camera_actor_is_playing (ClutterGstCameraActor *camera_actor) } /** - * clutter_gst_camera_actor_set_playing: - * @camera_actor: a #ClutterGstCameraActor - * @playing: %TRUE to start playback - * - * Starts or stops playback. - */ -void -clutter_gst_camera_actor_set_playing (ClutterGstCameraActor *camera_actor, - gboolean playing) -{ - ClutterGstCameraActorPrivate *priv; - GstState target_state; - - g_return_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor)); - - priv = camera_actor->priv; - - if (!priv->camerabin) - return; - - target_state = playing ? GST_STATE_PLAYING : GST_STATE_NULL; - - gst_element_set_state (priv->camerabin, target_state); -} - -/** - * clutter_gst_camera_actor_is_ready_for_capture: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_is_ready_for_capture: + * @self: a #ClutterGstCamera * - * Check whether the @camera_actor is ready for video/photo capture. + * Check whether the @self is ready for video/photo capture. * - * Return value: %TRUE if @camera_actor is ready for capture, %FALSE otherwise + * Return value: %TRUE if @self is ready for capture, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_is_ready_for_capture (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_is_ready_for_capture (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; gboolean ready_for_capture; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; g_object_get (priv->camera_source, "ready-for-capture", &ready_for_capture, NULL); @@ -1495,42 +1607,42 @@ clutter_gst_camera_actor_is_ready_for_capture (ClutterGstCameraActor *camera_act } /** - * clutter_gst_camera_actor_is_recording_video: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_is_recording_video: + * @self: a #ClutterGstCamera * - * Check whether the @camera_actor is recording video. + * Check whether the @self is recording video. * - * Return value: %TRUE if @camera_actor is recording video, %FALSE otherwise + * Return value: %TRUE if @self is recording video, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_is_recording_video (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_is_recording_video (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; return priv->is_recording; } /** - * clutter_gst_camera_actor_set_video_profile: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_set_video_profile: + * @self: a #ClutterGstCamera * @profile: A #GstEncodingProfile to be used for video recording. * * Set the encoding profile to be used for video recording. * The default profile saves videos as Ogg/Theora videos. */ void -clutter_gst_camera_actor_set_video_profile (ClutterGstCameraActor *camera_actor, - GstEncodingProfile *profile) +clutter_gst_camera_set_video_profile (ClutterGstCamera *self, + GstEncodingProfile *profile) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor)); + g_return_if_fail (CLUTTER_GST_IS_CAMERA (self)); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return; @@ -1539,27 +1651,27 @@ clutter_gst_camera_actor_set_video_profile (ClutterGstCameraActor *camera_actor, } /** - * clutter_gst_camera_actor_start_video_recording: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_start_video_recording: + * @self: a #ClutterGstCamera * @filename: (type filename): the name of the video file to where the * recording will be saved * - * Start a video recording with the @camera_actor and save it to @filename. - * This method requires that @camera_actor is playing and ready for capture. + * Start a video recording with the @self and save it to @filename. + * This method requires that @self is playing and ready for capture. * * The ::video-saved signal will be emitted when the video is saved. * * Return value: %TRUE if the video recording was successfully started, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_start_video_recording (ClutterGstCameraActor *camera_actor, - const gchar *filename) +clutter_gst_camera_start_video_recording (ClutterGstCamera *self, + const gchar *filename) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return FALSE; @@ -1567,34 +1679,34 @@ clutter_gst_camera_actor_start_video_recording (ClutterGstCameraActor *camera_ac if (priv->is_recording) return TRUE; - if (!clutter_gst_camera_actor_is_playing (camera_actor)) + if (!clutter_gst_camera_is_playing (self)) return FALSE; - if (!clutter_gst_camera_actor_is_ready_for_capture (camera_actor)) + if (!clutter_gst_camera_is_ready_for_capture (self)) return FALSE; g_object_set (priv->camerabin, "mode", CAPTURE_MODE_VIDEO, NULL); g_object_set (priv->camerabin, "location", filename, NULL); - g_signal_emit_by_name (priv->camerabin, "start-capture", 0); + g_signal_emit_by_name (priv->camerabin, "start-capture"); priv->is_recording = TRUE; return TRUE; } /** * clutter_gst_camera_stop_video_recording: - * @camera_actor: a #ClutterGstCameraActor + * @self: a #ClutterGstCamera * - * Stop recording video on the @camera_actor. + * Stop recording video on the @self. */ void -clutter_gst_camera_actor_stop_video_recording (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_stop_video_recording (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GstState state; - g_return_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor)); + g_return_if_fail (CLUTTER_GST_IS_CAMERA (self)); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return; @@ -1602,13 +1714,13 @@ clutter_gst_camera_actor_stop_video_recording (ClutterGstCameraActor *camera_act if (!priv->is_recording) return; - if (!clutter_gst_camera_actor_is_playing (camera_actor)) + if (!clutter_gst_camera_is_playing (self)) return; gst_element_get_state (priv->camerabin, &state, NULL, 0); if (state == GST_STATE_PLAYING) - g_signal_emit_by_name (priv->camerabin, "stop-capture", 0); + g_signal_emit_by_name (priv->camerabin, "stop-capture"); else if (priv->is_recording) { g_warning ("Cannot cleanly shutdown recording pipeline, forcing"); @@ -1620,22 +1732,22 @@ clutter_gst_camera_actor_stop_video_recording (ClutterGstCameraActor *camera_act } /** - * clutter_gst_camera_actor_set_photo_profile: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_set_photo_profile: + * @self: a #ClutterGstCamera * @profile: A #GstEncodingProfile to be used for photo captures. * * Set the encoding profile to be used for photo captures. * The default profile saves photos as JPEG images. */ void -clutter_gst_camera_actor_set_photo_profile (ClutterGstCameraActor *camera_actor, - GstEncodingProfile *profile) +clutter_gst_camera_set_photo_profile (ClutterGstCamera *self, + GstEncodingProfile *profile) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor)); + g_return_if_fail (CLUTTER_GST_IS_CAMERA (self)); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return; @@ -1644,36 +1756,36 @@ clutter_gst_camera_actor_set_photo_profile (ClutterGstCameraActor *camera_actor, } /** - * clutter_gst_camera_actor_take_photo: - * @camera_actor: a #ClutterGstCameraActor + * clutter_gst_camera_take_photo: + * @self: a #ClutterGstCamera * @filename: (type filename): the name of the file to where the * photo will be saved * - * Take a photo with the @camera_actor and save it to @filename. - * This method requires that @camera_actor is playing and ready for capture. + * Take a photo with the @self and save it to @filename. + * This method requires that @self is playing and ready for capture. * * The ::photo-saved signal will be emitted when the video is saved. * * Return value: %TRUE if the photo was successfully captured, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_take_photo (ClutterGstCameraActor *camera_actor, - const gchar *filename) +clutter_gst_camera_take_photo (ClutterGstCamera *self, + const gchar *filename) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); g_return_val_if_fail (filename != NULL, FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return FALSE; - if (!clutter_gst_camera_actor_is_playing (camera_actor)) + if (!clutter_gst_camera_is_playing (self)) return FALSE; - if (!clutter_gst_camera_actor_is_ready_for_capture (camera_actor)) + if (!clutter_gst_camera_is_ready_for_capture (self)) return FALSE; g_free (priv->photo_filename); @@ -1682,37 +1794,37 @@ clutter_gst_camera_actor_take_photo (ClutterGstCameraActor *camera_actor, /* Take the photo */ g_object_set (priv->camerabin, "location", filename, NULL); g_object_set (priv->camerabin, "mode", CAPTURE_MODE_IMAGE, NULL); - g_signal_emit_by_name (priv->camerabin, "start-capture", 0); + g_signal_emit_by_name (priv->camerabin, "start-capture"); return TRUE; } /** * clutter_gst_camera_take_photo_pixbuf: - * @camera_actor: a #ClutterGstCameraActor + * @self: a #ClutterGstCamera * - * Take a photo with the @camera_actor and emit it in the ::photo-taken signal as a + * Take a photo with the @self and emit it in the ::photo-taken signal as a * #GdkPixbuf. - * This method requires that @camera_actor is playing and ready for capture. + * This method requires that @self is playing and ready for capture. * * Return value: %TRUE if the photo was successfully captured, %FALSE otherwise */ gboolean -clutter_gst_camera_actor_take_photo_pixbuf (ClutterGstCameraActor *camera_actor) +clutter_gst_camera_take_photo_pixbuf (ClutterGstCamera *self) { - ClutterGstCameraActorPrivate *priv; + ClutterGstCameraPrivate *priv; GstCaps *caps; - g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_ACTOR (camera_actor), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_CAMERA (self), FALSE); - priv = camera_actor->priv; + priv = self->priv; if (!priv->camerabin) return FALSE; - if (!clutter_gst_camera_actor_is_playing (camera_actor)) + if (!clutter_gst_camera_is_playing (self)) return FALSE; - if (!clutter_gst_camera_actor_is_ready_for_capture (camera_actor)) + if (!clutter_gst_camera_is_ready_for_capture (self)) return FALSE; caps = gst_caps_new_simple ("video/x-raw", @@ -1729,6 +1841,6 @@ clutter_gst_camera_actor_take_photo_pixbuf (ClutterGstCameraActor *camera_actor) /* Take the photo */ g_object_set (priv->camerabin, "location", NULL, NULL); g_object_set (priv->camerabin, "mode", CAPTURE_MODE_IMAGE, NULL); - g_signal_emit_by_name (priv->camerabin, "start-capture", 0); + g_signal_emit_by_name (priv->camerabin, "start-capture"); return TRUE; } diff --git a/clutter-gst/clutter-gst-camera.h b/clutter-gst/clutter-gst-camera.h new file mode 100644 index 0000000..f348ab0 --- /dev/null +++ b/clutter-gst/clutter-gst-camera.h @@ -0,0 +1,208 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-camera-actor.h - ClutterActor using GStreamer to display/manipulate a + * camera stream. + * + * Authored By Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> + * + * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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. + */ + +#if !defined(__CLUTTER_GST_H_INSIDE__) && !defined(CLUTTER_GST_COMPILATION) +#error "Only <clutter-gst/clutter-gst.h> can be included directly." +#endif + +#ifndef __CLUTTER_GST_CAMERA_H__ +#define __CLUTTER_GST_CAMERA_H__ + +#include <clutter/clutter.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <glib-object.h> +#include <gst/gstelement.h> +#include <gst/pbutils/encoding-profile.h> + +#include <clutter-gst/clutter-gst-actor.h> +#include <clutter-gst/clutter-gst-camera-device.h> +#include <clutter-gst/clutter-gst-types.h> + +G_BEGIN_DECLS + +#define CLUTTER_GST_TYPE_CAMERA clutter_gst_camera_get_type() + +#define CLUTTER_GST_CAMERA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_GST_TYPE_CAMERA, ClutterGstCamera)) + +#define CLUTTER_GST_CAMERA_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_GST_TYPE_CAMERA, ClutterGstCameraClass)) + +#define CLUTTER_GST_IS_CAMERA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_GST_TYPE_CAMERA)) + +#define CLUTTER_GST_IS_CAMERA_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_GST_TYPE_CAMERA)) + +#define CLUTTER_GST_CAMERA_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_GST_TYPE_CAMERA, ClutterGstCameraClass)) + +typedef struct _ClutterGstCamera ClutterGstCamera; +typedef struct _ClutterGstCameraClass ClutterGstCameraClass; +typedef struct _ClutterGstCameraPrivate ClutterGstCameraPrivate; + +/** + * ClutterGstCamera: + * + * Subclass of #ClutterGstActor that displays camera streams using GStreamer. + * + * The #ClutterGstCamera structure contains only private data and + * should not be accessed directly. + */ +struct _ClutterGstCamera +{ + /*< private >*/ + ClutterGstActor parent; + ClutterGstCameraPrivate *priv; +}; + +/** + * ClutterGstCameraClass: + * + * Base class for #ClutterGstCamera. + */ +struct _ClutterGstCameraClass +{ + /*< private >*/ + ClutterGstActorClass parent_class; + + void (* ready_for_capture) (ClutterGstCamera *self, + gboolean ready); + void (* photo_saved) (ClutterGstCamera *self); + void (* photo_taken) (ClutterGstCamera *self, + GdkPixbuf *pixbuf); + void (* video_saved) (ClutterGstCamera *self); + + /* Future padding */ + void (* _clutter_reserved1) (void); + void (* _clutter_reserved2) (void); + void (* _clutter_reserved3) (void); + void (* _clutter_reserved4) (void); + void (* _clutter_reserved5) (void); + void (* _clutter_reserved6) (void); +}; + +GType clutter_gst_camera_get_type (void) G_GNUC_CONST; + +ClutterGstCamera * clutter_gst_camera_new (void); + +const GPtrArray * + clutter_gst_camera_get_camera_devices (ClutterGstCamera *self); +ClutterGstCameraDevice * + clutter_gst_camera_get_camera_device (ClutterGstCamera *self); +gboolean clutter_gst_camera_set_camera_device (ClutterGstCamera *self, + ClutterGstCameraDevice *device); + +gboolean clutter_gst_camera_supports_gamma_correction + (ClutterGstCamera *self); +gboolean clutter_gst_camera_get_gamma_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_gamma (ClutterGstCamera *self, + gdouble *cur_value); +gboolean clutter_gst_camera_set_gamma (ClutterGstCamera *self, + gdouble value); + +gboolean clutter_gst_camera_supports_color_balance + (ClutterGstCamera *self); +gboolean clutter_gst_camera_get_color_balance_property_range + (ClutterGstCamera *self, + const gchar *property, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_color_balance_property + (ClutterGstCamera *self, + const gchar *property, + gdouble *cur_value); +gboolean clutter_gst_camera_set_color_balance_property + (ClutterGstCamera *self, + const gchar *property, + gdouble value); +gboolean clutter_gst_camera_get_brightness_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_brightness (ClutterGstCamera *self, + gdouble *cur_value); +gboolean clutter_gst_camera_set_brightness (ClutterGstCamera *self, + gdouble value); +gboolean clutter_gst_camera_get_contrast_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_contrast (ClutterGstCamera *self, + gdouble *cur_value); +gboolean clutter_gst_camera_set_contrast (ClutterGstCamera *self, + gdouble value); +gboolean clutter_gst_camera_get_saturation_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_saturation (ClutterGstCamera *self, + gdouble *cur_value); +gboolean clutter_gst_camera_set_saturation (ClutterGstCamera *self, + gdouble value); +gboolean clutter_gst_camera_get_hue_range (ClutterGstCamera *self, + gdouble *min_value, + gdouble *max_value, + gdouble *default_value); +gboolean clutter_gst_camera_get_hue (ClutterGstCamera *self, + gdouble *cur_value); +gboolean clutter_gst_camera_set_hue (ClutterGstCamera *self, + gdouble value); + +GstElement * clutter_gst_camera_get_filter (ClutterGstCamera *self); +gboolean clutter_gst_camera_set_filter (ClutterGstCamera *self, + GstElement *filter); +gboolean clutter_gst_camera_remove_filter (ClutterGstCamera *self); + +gboolean clutter_gst_camera_is_ready_for_capture (ClutterGstCamera *self); + +void clutter_gst_camera_set_video_profile (ClutterGstCamera *self, + GstEncodingProfile *profile); +gboolean clutter_gst_camera_is_recording_video (ClutterGstCamera *self); +gboolean clutter_gst_camera_start_video_recording (ClutterGstCamera *self, + const gchar *filename); +void clutter_gst_camera_stop_video_recording (ClutterGstCamera *self); + +void clutter_gst_camera_set_photo_profile (ClutterGstCamera *self, + GstEncodingProfile *profile); +gboolean clutter_gst_camera_take_photo (ClutterGstCamera *self, + const gchar *filename); +gboolean clutter_gst_camera_take_photo_pixbuf (ClutterGstCamera *self); + +G_END_DECLS + +#endif /* __CLUTTER_GST_CAMERA_H__ */ diff --git a/clutter-gst/clutter-gst-playback.c b/clutter-gst/clutter-gst-playback.c new file mode 100644 index 0000000..2152547 --- /dev/null +++ b/clutter-gst/clutter-gst-playback.c @@ -0,0 +1,2799 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-player.c - Wrap some convenience functions around playbin + * + * Authored By Damien Lespiau <damien.lespiau@intel.com> + * Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com> + * Matthew Allum <mallum@openedhand.com> + * Emmanuele Bassi <ebassi@linux.intel.com> + * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> + * + * Copyright (C) 2006 OpenedHand + * Copyright (C) 2009-2013 Intel Corporation + * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> + * + * 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-debug.h" +#include "clutter-gst-enum-types.h" +#include "clutter-gst-marshal.h" +#include "clutter-gst-playback.h" +#include "clutter-gst-player.h" +#include "clutter-gst-private.h" + +#include <string.h> + +#include <gio/gio.h> +#include <cogl-gst/cogl-gst.h> +#include <gst/video/video.h> +#include <gst/tag/tag.h> +#include <gst/audio/streamvolume.h> + +#if defined (CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT) +#define GST_USE_UNSTABLE_API 1 +#include <gst/video/videocontext.h> +#include <clutter/x11/clutter-x11.h> +#endif + +static void player_iface_init (ClutterGstPlayerIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterGstPlayback, clutter_gst_playback, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER, player_iface_init)) + +#define GST_PLAYBACK_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CLUTTER_GST_TYPE_PLAYBACK, ClutterGstPlaybackPrivate)) + +/* idle timeouts (in ms) */ +#define TICK_TIMEOUT 500 +#define BUFFERING_TIMEOUT 250 + +enum +{ + PROP_0, + + PROP_URI, + PROP_PLAYING, + PROP_PROGRESS, + PROP_SUBTITLE_URI, + PROP_SUBTITLE_FONT_NAME, + PROP_AUDIO_VOLUME, + PROP_CAN_SEEK, + PROP_BUFFER_FILL, + PROP_DURATION, + PROP_IDLE, + PROP_USER_AGENT, + PROP_SEEK_FLAGS, + PROP_AUDIO_STREAMS, + PROP_AUDIO_STREAM, + PROP_SUBTITLE_TRACKS, + PROP_SUBTITLE_TRACK, + PROP_IN_SEEK +}; + +enum +{ + DOWNLOAD_BUFFERING_SIGNAL, + + LAST_SIGNAL +}; + +/* Elements don't expose header files */ +typedef enum { + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9) +} GstPlayFlags; + +struct _ClutterGstPlaybackPrivate +{ + GstElement *pipeline; + GstBus *bus; + + ClutterGstFrame *current_frame; + + gchar *uri; + + guint is_idle : 1; + guint is_live : 1; + guint can_seek : 1; + guint in_seek : 1; + guint is_changing_uri : 1; + guint in_error : 1; + guint in_eos : 1; + guint in_download_buffering : 1; + /* when in progressive download, we use the buffer-fill property to signal + * that we have enough data to play the stream. This flag allows to send + * the notify that buffer-fill is 1.0 only once */ + guint virtual_stream_buffer_signalled : 1; + + gdouble stacked_progress; + + gdouble target_progress; + GstState target_state; + + guint tick_timeout_id; + guint buffering_timeout_id; + + /* This is a cubic volume, suitable for use in a UI cf. StreamVolume doc */ + gdouble volume; + + gdouble buffer_fill; + gdouble duration; + gchar *font_name; + gchar *user_agent; + + GstSeekFlags seek_flags; /* flags for the seek in set_progress(); */ + + GstElement *download_buffering_element; + + GList *audio_streams; + GList *subtitle_tracks; +}; + + +static guint signals[LAST_SIGNAL] = { 0, }; + +static gboolean player_buffering_timeout (gpointer data); + +/* Logic */ + +#ifdef CLUTTER_GST_ENABLE_DEBUG +static gchar * +get_stream_description (GstTagList *tags, + gint track_num) +{ + gchar *description = NULL; + + if (tags) + { + + gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description); + + if (description) + { + const gchar *language = gst_tag_get_language_name (description); + + if (language) + { + g_free (description); + description = g_strdup (language); + } + } + + if (!description) + gst_tag_list_get_string (tags, GST_TAG_CODEC, &description); + } + + if (!description) + description = g_strdup_printf ("Track %d", track_num); + + return description; +} + +gchar * +list_to_string (GList *list) +{ + GstTagList *tags; + gchar *description; + GString *string; + GList *l; + gint n, i; + + if (!list) + return g_strdup ("<empty list>"); + + string = g_string_new (NULL); + n = g_list_length (list); + for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l)) + { + tags = l->data; + description = get_stream_description (tags, i); + g_string_append_printf (string, "%s, ", description); + g_free (description); + } + + tags = l->data; + description = get_stream_description (tags, i); + g_string_append_printf (string, "%s", (gchar *) description); + g_free (description); + + return g_string_free (string, FALSE); +} +#endif + +static const gchar * +gst_state_to_string (GstState state) +{ + switch (state) + { + case GST_STATE_VOID_PENDING: + return "pending"; + case GST_STATE_NULL: + return "null"; + case GST_STATE_READY: + return "ready"; + case GST_STATE_PAUSED: + return "paused"; + case GST_STATE_PLAYING: + return "playing"; + } + + return "Unknown state"; +} + +static void +free_tags_list (GList **listp) +{ + GList *l; + + l = *listp; + while (l) + { + if (l->data) + gst_tag_list_unref (l->data); + l = g_list_delete_link (l, l); + } + + *listp = NULL; +} + +static gboolean +tick_timeout (gpointer data) +{ + GObject *player = data; + + g_object_notify (player, "progress"); + + return TRUE; +} + +static void +player_set_user_agent (ClutterGstPlayback *self, + const gchar *user_agent) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstElement *source; + GParamSpec *pspec; + + if (user_agent == NULL) + return; + + g_object_get (priv->pipeline, "source", &source, NULL); + if (source == NULL) + return; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), + "user-agent"); + if (pspec == NULL) + return; + + CLUTTER_GST_NOTE (MEDIA, "setting user agent: %s", user_agent); + + g_object_set (source, "user-agent", user_agent, NULL); +} + +static void +autoload_subtitle (ClutterGstPlayback *self, + const gchar *uri) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + gchar *path, *dot, *subtitle_path; + GFile *video; + guint i; + + static const char subtitles_extensions[][4] = + { + "sub", "SUB", + "srt", "SRT", + "smi", "SMI", + "ssa", "SSA", + "ass", "ASS", + "asc", "ASC" + }; + + /* do not try to look for subtitle files if the video file is not mounted + * locally */ + if (!g_str_has_prefix (uri, "file://")) + return; + + /* Retrieve the absolute path of the video file */ + video = g_file_new_for_uri (uri); + path = g_file_get_path (video); + g_object_unref (video); + if (path == NULL) + return; + + /* Put a '\0' after the dot of the extension */ + dot = strrchr (path, '.'); + if (dot == NULL) { + g_free (path); + return; + } + *++dot = '\0'; + + /* we can't use path as the temporary buffer for the paths of the potential + * subtitle files as we may not have enough room there */ + subtitle_path = g_malloc (strlen (path) + 1 + 4); + strcpy (subtitle_path, path); + + /* reuse dot to point to the first byte of the extension of subtitle_path */ + dot = subtitle_path + (dot - path); + + for (i = 0; i < G_N_ELEMENTS (subtitles_extensions); i++) + { + GFile *candidate; + + memcpy (dot, subtitles_extensions[i], 4); + candidate = g_file_new_for_path (subtitle_path); + if (g_file_query_exists (candidate, NULL)) + { + gchar *suburi; + + suburi = g_file_get_uri (candidate); + + CLUTTER_GST_NOTE (MEDIA, "found subtitle: %s", suburi); + + g_object_set (priv->pipeline, "suburi", suburi, NULL); + g_free (suburi); + + g_object_unref (candidate); + break; + } + + g_object_unref (candidate); + } + + g_free (path); + g_free (subtitle_path); +} + +static void +set_subtitle_uri (ClutterGstPlayback *self, + const gchar *uri) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstPlayFlags flags; + + if (!priv->pipeline) + return; + + CLUTTER_GST_NOTE (MEDIA, "setting subtitle URI: %s", uri); + + g_object_get (priv->pipeline, "flags", &flags, NULL); + + g_object_set (priv->pipeline, "suburi", uri, NULL); + + g_object_set (priv->pipeline, "flags", flags, NULL); +} + +static void +player_configure_buffering_timeout (ClutterGstPlayback *self, + guint ms) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (priv->buffering_timeout_id) + { + g_source_remove (priv->buffering_timeout_id); + priv->buffering_timeout_id = 0; + } + + if (ms) + { + priv->buffering_timeout_id = + g_timeout_add (ms, player_buffering_timeout, self); + } +} + +static void +player_clear_download_buffering (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (priv->download_buffering_element) + { + g_object_unref (priv->download_buffering_element); + priv->download_buffering_element = NULL; + } + player_configure_buffering_timeout (self, 0); + priv->in_download_buffering = FALSE; + priv->virtual_stream_buffer_signalled = 0; +} + +static gboolean +is_live_pipeline (GstElement *pipeline) +{ + GstState state, pending; + GstStateChangeReturn state_change_res; + gboolean is_live = FALSE; + + /* get pipeline current state, we need to change the pipeline state to PAUSED to + * see if we are dealing with a live source and we want to restore the pipeline + * state afterwards */ + gst_element_get_state (pipeline, &state, &pending, 0); + + /* a pipeline with live source should return NO_PREROLL in PAUSE */ + state_change_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + is_live = (state_change_res == GST_STATE_CHANGE_NO_PREROLL); + + /* restore pipeline previous state */ + if (pending == GST_STATE_VOID_PENDING) + gst_element_set_state (pipeline, state); + else + gst_element_set_state (pipeline, pending); + + return is_live; +} + +static void +set_uri (ClutterGstPlayback *self, + const gchar *uri) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstState state, pending; + + CLUTTER_GST_NOTE (MEDIA, "setting uri %s", uri); + + if (!priv->pipeline) + return; + + g_free (priv->uri); + + priv->in_eos = FALSE; + priv->in_error = FALSE; + + if (uri) + { + priv->uri = g_strdup (uri); + + /* Ensure the tick timeout is installed. + * + * We also have it installed in PAUSED state, because + * seeks etc may have a delayed effect on the position. + */ + if (priv->tick_timeout_id == 0) + { + priv->tick_timeout_id = + g_timeout_add (TICK_TIMEOUT, tick_timeout, self); + } + + /* try to load subtitles based on the uri of the file */ + set_subtitle_uri (self, NULL); + + /* reset the states of download buffering */ + player_clear_download_buffering (self); + } + else + { + priv->uri = NULL; + + if (priv->tick_timeout_id) + { + g_source_remove (priv->tick_timeout_id); + priv->tick_timeout_id = 0; + } + + if (priv->buffering_timeout_id) + { + g_source_remove (priv->buffering_timeout_id); + priv->buffering_timeout_id = 0; + } + + if (priv->download_buffering_element) + { + g_object_unref (priv->download_buffering_element); + priv->download_buffering_element = NULL; + } + + } + + priv->can_seek = FALSE; + priv->duration = 0.0; + priv->stacked_progress = 0.0; + priv->target_progress = 0.0; + + CLUTTER_GST_NOTE (MEDIA, "setting URI: %s", uri); + + if (uri) + { + gst_element_get_state (priv->pipeline, &state, &pending, 0); + if (pending) + state = pending; + + gst_element_set_state (priv->pipeline, GST_STATE_NULL); + + g_object_set (priv->pipeline, "uri", uri, NULL); + + priv->is_live = is_live_pipeline (priv->pipeline); + + set_subtitle_uri (self, NULL); + autoload_subtitle (self, uri); + + gst_element_set_state (priv->pipeline, state); + + priv->is_changing_uri = TRUE; + } + else + { + priv->is_idle = TRUE; + priv->is_live = FALSE; + set_subtitle_uri (self, NULL); + gst_element_set_state (priv->pipeline, GST_STATE_NULL); + g_object_notify (G_OBJECT (self), "idle"); + } + + /* + * Emit notifications for all these to make sure UI is not showing + * any properties of the old URI. + */ + g_object_notify (G_OBJECT (self), "uri"); + g_object_notify (G_OBJECT (self), "can-seek"); + g_object_notify (G_OBJECT (self), "duration"); + g_object_notify (G_OBJECT (self), "progress"); + + free_tags_list (&priv->audio_streams); + CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed"); + g_object_notify (G_OBJECT (self), "audio-streams"); + + free_tags_list (&priv->subtitle_tracks); + CLUTTER_GST_NOTE (SUBTITLES, "subtitle-tracks changed"); + g_object_notify (G_OBJECT (self), "subtitle-tracks"); +} + +static gdouble +get_audio_volume (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (!priv->pipeline) + return 0.0; + + CLUTTER_GST_NOTE (MEDIA, "get volume: %.02f", priv->volume); + + return priv->volume; +} + +static void +set_audio_volume (ClutterGstPlayback *self, + gdouble volume) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (!priv->pipeline) + return; + + CLUTTER_GST_NOTE (MEDIA, "set volume: %.02f", volume); + + volume = CLAMP (volume, 0.0, 1.0); + gst_stream_volume_set_volume (GST_STREAM_VOLUME (priv->pipeline), + GST_STREAM_VOLUME_FORMAT_CUBIC, + volume); + g_object_notify (G_OBJECT (self), "audio-volume"); +} + +static void +set_in_seek (ClutterGstPlayback *self, + gboolean seeking) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + priv->in_seek = seeking; + g_object_notify (G_OBJECT (self), "in-seek"); +} + + +static void +set_playing (ClutterGstPlayback *self, + gboolean playing) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (!priv->pipeline) + return; + + CLUTTER_GST_NOTE (MEDIA, "set playing: %d", playing); + + priv->in_error = FALSE; + priv->in_eos = FALSE; + + priv->target_state = playing ? GST_STATE_PLAYING : GST_STATE_PAUSED; + + if (priv->uri) + { + set_in_seek (self, FALSE); + + gst_element_set_state (priv->pipeline, priv->target_state); + } + else + { + if (playing) + g_warning ("Unable to start playing: no URI is set"); + } + + g_object_notify (G_OBJECT (self), "playing"); + g_object_notify (G_OBJECT (self), "progress"); +} + +static gboolean +get_playing (ClutterGstPlayback *player) +{ + ClutterGstPlaybackPrivate *priv = player->priv; + GstState state, pending; + gboolean playing; + + if (!priv->pipeline) + return FALSE; + + gst_element_get_state (priv->pipeline, &state, &pending, 0); + + if (pending) + playing = (pending == GST_STATE_PLAYING); + else + playing = (state == GST_STATE_PLAYING); + + CLUTTER_GST_NOTE (MEDIA, "get playing: %d", playing); + + return playing; +} + +static void +set_progress (ClutterGstPlayback *self, + gdouble progress) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstQuery *duration_q; + gint64 position; + + if (!priv->pipeline) + return; + + CLUTTER_GST_NOTE (MEDIA, "set progress: %.02f", progress); + + priv->in_eos = FALSE; + priv->target_progress = progress; + + if (priv->in_download_buffering) + { + /* we clear the virtual_stream_buffer_signalled flag as it's likely we + * need to buffer again */ + priv->virtual_stream_buffer_signalled = 0; + } + + if (priv->in_seek || priv->is_idle || priv->is_changing_uri) + { + /* We can't seek right now, let's save the position where we + want to seek and do that later. */ + CLUTTER_GST_NOTE (MEDIA, + "already seeking/idleing. stacking progress point."); + priv->stacked_progress = progress; + return; + } + + duration_q = gst_query_new_duration (GST_FORMAT_TIME); + + if (gst_element_query (priv->pipeline, duration_q)) + { + gint64 duration = 0; + + gst_query_parse_duration (duration_q, NULL, &duration); + + position = progress * duration; + } + else + position = 0; + + gst_query_unref (duration_q); + + gst_element_seek (priv->pipeline, + 1.0, + GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH | priv->seek_flags, + GST_SEEK_TYPE_SET, + position, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + + set_in_seek (self, TRUE); + + priv->stacked_progress = 0.0; + + CLUTTER_GST_NOTE (MEDIA, "set progress (seeked): %.02f", progress); +} + +static gdouble +get_progress (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstQuery *position_q, *duration_q; + gdouble progress; + + if (!priv->pipeline) + return 0.0; + + /* when hitting an error or after an EOS, playbin has some weird values when + * querying the duration and progress. We default to 0.0 on error and 1.0 on + * EOS */ + if (priv->in_error) + { + CLUTTER_GST_NOTE (MEDIA, "get progress (error): 0.0"); + return 0.0; + } + + if (priv->in_eos) + { + CLUTTER_GST_NOTE (MEDIA, "get progress (eos): 1.0"); + return 1.0; + } + + /* When seeking, the progress returned by playbin is 0.0. We want that to be + * the last known position instead as returning 0.0 will have some ugly + * effects, say on a progress bar getting updated from the progress tick. */ + if (priv->in_seek || priv->is_changing_uri) + { + CLUTTER_GST_NOTE (MEDIA, "get progress (target): %.02f", + priv->target_progress); + return priv->target_progress; + } + + position_q = gst_query_new_position (GST_FORMAT_TIME); + duration_q = gst_query_new_duration (GST_FORMAT_TIME); + + if (gst_element_query (priv->pipeline, position_q) && + gst_element_query (priv->pipeline, duration_q)) + { + gint64 position, duration; + + position = duration = 0; + + gst_query_parse_position (position_q, NULL, &position); + gst_query_parse_duration (duration_q, NULL, &duration); + + progress = CLAMP ((gdouble) position / (gdouble) duration, 0.0, 1.0); + } + else + progress = 0.0; + + gst_query_unref (position_q); + gst_query_unref (duration_q); + + CLUTTER_GST_NOTE (MEDIA, "get progress (pipeline): %.02f", progress); + + return progress; +} + +static void +set_subtitle_font_name (ClutterGstPlayback *self, + const gchar *font_name) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (!priv->pipeline) + return; + + CLUTTER_GST_NOTE (MEDIA, "setting subtitle font to %s", font_name); + + g_free (priv->font_name); + priv->font_name = g_strdup (font_name); + g_object_set (priv->pipeline, "subtitle-font-desc", font_name, NULL); +} + +static gboolean +player_buffering_timeout (gpointer data) +{ + ClutterGstPlayback *self = (ClutterGstPlayback *) data; + ClutterGstPlaybackPrivate *priv = self->priv; + gdouble start_d, stop_d, seconds_buffered; + gint64 start, stop, left; + gint buffer_percent; + GstState current_state; + GstElement *element; + GstQuery *query; + gboolean res; + + element = priv->download_buffering_element; + if (element == NULL) + element = priv->pipeline; + + /* queue2 only knows about _PERCENT and _BYTES */ + query = gst_query_new_buffering (GST_FORMAT_PERCENT); + res = gst_element_query (element, query); + + if (res == FALSE) + { + priv->buffering_timeout_id = 0; + player_clear_download_buffering (self); + return FALSE; + } + + /* signal the current range */ + + gst_query_parse_buffering_stats (query, NULL, NULL, NULL, &left); + gst_query_parse_buffering_range (query, NULL, &start, &stop, NULL); + + CLUTTER_GST_NOTE (BUFFERING, + "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT + ", buffering left %" G_GINT64_FORMAT, start, stop, left); + + start_d = (gdouble) start / GST_FORMAT_PERCENT_MAX; + stop_d = (gdouble) stop / GST_FORMAT_PERCENT_MAX; + + g_signal_emit (self, signals[DOWNLOAD_BUFFERING_SIGNAL], 0, start_d, stop_d); + + /* handle the "virtual stream buffer" and the associated pipeline state. + * We pause the pipeline until the content is buffered. + */ + seconds_buffered = priv->duration * (stop_d - start_d); + gst_query_parse_buffering_percent (query, NULL, &buffer_percent); + priv->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0); + + if (priv->buffer_fill != 1.0 || !priv->virtual_stream_buffer_signalled) + { + CLUTTER_GST_NOTE (BUFFERING, "buffer holds %0.2fs of data, buffer-fill " + "is %.02f", seconds_buffered, priv->buffer_fill); + + g_object_notify (G_OBJECT (self), "buffer-fill"); + + if (priv->buffer_fill == 1.0) + priv->virtual_stream_buffer_signalled = 1; + } + + gst_element_get_state (priv->pipeline, ¤t_state, NULL, 0); + if (priv->buffer_fill < 1.0) + { + if (current_state != GST_STATE_PAUSED) + { + CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline"); + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + } + } + else + { + if (current_state != priv->target_state) + { + CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline"); + gst_element_set_state (priv->pipeline, priv->target_state); + } + } + + /* the file has finished downloading */ + if (left == G_GINT64_CONSTANT (0)) + { + priv->buffering_timeout_id = 0; + + player_clear_download_buffering (self); + gst_query_unref (query); + return FALSE; + } + + gst_query_unref (query); + return TRUE; +} + +static void +bus_message_error_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GError *error = NULL; + + gst_element_set_state (priv->pipeline, GST_STATE_NULL); + + gst_message_parse_error (message, &error, NULL); + g_signal_emit_by_name (self, "error", error); + g_error_free (error); + + priv->is_idle = TRUE; + g_object_notify (G_OBJECT (self), "idle"); +} + +/* + * This is what's intented in the EOS callback: + * - receive EOS from playbin + * - fire the EOS signal, the user can install a signal handler to loop the + * video for instance. + * - after having emitted the signal, check the state of the pipeline + * - if the pipeline has been set back to playing or pause, don't touch the + * idle state. This will avoid drawing a frame (or more) with the idle + * material when looping + */ +static void +bus_message_eos_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstState state, pending; + + priv->in_eos = TRUE; + + gst_element_set_state (priv->pipeline, GST_STATE_READY); + + g_signal_emit_by_name (self, "eos"); + g_object_notify (G_OBJECT (self), "progress"); + + gst_element_get_state (priv->pipeline, &state, &pending, 0); + if (pending) + state = pending; + + if (!(state == GST_STATE_PLAYING || state == GST_STATE_PAUSED)) + { + priv->is_idle = TRUE; + g_object_notify (G_OBJECT (self), "idle"); + } +} + +static void +bus_message_buffering_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstBufferingMode mode; + GstState current_state; + gint buffer_percent; + + gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL); + + if (mode != GST_BUFFERING_DOWNLOAD) + priv->in_download_buffering = FALSE; + + switch (mode) + { + case GST_BUFFERING_LIVE: + case GST_BUFFERING_STREAM: + gst_message_parse_buffering (message, &buffer_percent); + priv->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0); + + CLUTTER_GST_NOTE (BUFFERING, "buffer-fill: %.02f", priv->buffer_fill); + + /* no state management needed for live pipelines */ + if (!priv->is_live) + { + /* The playbin documentation says that we need to pause the pipeline + * when there's not enough data yet. We try to limit the calls to + * gst_element_set_state() */ + gst_element_get_state (priv->pipeline, ¤t_state, NULL, 0); + + if (priv->buffer_fill < 1.0) + { + if (current_state != GST_STATE_PAUSED) + { + CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline"); + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + } + } + else + { + if (current_state != priv->target_state) + { + CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline"); + gst_element_set_state (priv->pipeline, priv->target_state); + } + } + } + + g_object_notify (G_OBJECT (self), "buffer-fill"); + break; + + case GST_BUFFERING_DOWNLOAD: + /* we rate limit the messages from GStreamer for a usage in a UI (we + * don't want *that* many updates). This is done by installing an idle + * handler querying the buffer range and sending a signal from there */ + + if (priv->in_download_buffering) + break; + + /* install the querying idle handler the first time we receive a download + * buffering message */ + player_configure_buffering_timeout (self, BUFFERING_TIMEOUT); + + /* pause the stream. the idle timeout will set the target state when + * having received enough data. We'll use buffer_fill as a "virtual + * stream buffer" to signal the application we're buffering until we + * can play back from the downloaded stream. */ + gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + priv->buffer_fill = 0.0; + g_object_notify (G_OBJECT (self), "buffer-fill"); + + priv->download_buffering_element = g_object_ref (message->src); + priv->in_download_buffering = TRUE; + priv->virtual_stream_buffer_signalled = 0; + break; + + case GST_BUFFERING_TIMESHIFT: + default: + g_warning ("Buffering mode %d not handled", mode); + break; + } +} + +static void +on_source_changed (GstElement *pipeline, + GParamSpec *pspec, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + player_set_user_agent (self, priv->user_agent); +} + +static void +query_duration (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + gboolean success; + gint64 duration; + gdouble new_duration, difference; + + success = gst_element_query_duration (priv->pipeline, + GST_FORMAT_TIME, + &duration); + if (G_UNLIKELY (success != TRUE)) + return; + + new_duration = (gdouble) duration / GST_SECOND; + + /* while we store the new duration if it sligthly changes, the duration + * signal is sent only if the new duration is at least one second different + * from the old one (as the duration signal is mainly used to update the + * time displayed in a UI */ + difference = ABS (priv->duration - new_duration); + if (difference > 1e-3) + { + CLUTTER_GST_NOTE (MEDIA, "duration: %.02f", new_duration); + priv->duration = new_duration; + + if (difference > 1.0) + g_object_notify (G_OBJECT (self), "duration"); + } +} + +static void +bus_message_duration_changed_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + /* GstElements send a duration-changed message on the bus to signal + * that the duration has changed and should be re-queried */ + query_duration (self); +} + +static void +bus_message_state_change_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstState old_state, new_state; + gpointer src; + + src = GST_MESSAGE_SRC (message); + if (src != priv->pipeline) + return; + + gst_message_parse_state_changed (message, &old_state, &new_state, NULL); + + CLUTTER_GST_NOTE (MEDIA, "state change: %s -> %s", + gst_state_to_string (old_state), + gst_state_to_string (new_state)); + + if (old_state == new_state) + return; + + if (old_state == GST_STATE_READY && + new_state == GST_STATE_PAUSED) + { + GstQuery *query; + + /* Determine whether we can seek */ + query = gst_query_new_seeking (GST_FORMAT_TIME); + + if (gst_element_query (priv->pipeline, query)) + { + gboolean can_seek = FALSE; + + gst_query_parse_seeking (query, NULL, &can_seek, + NULL, + NULL); + + priv->can_seek = (can_seek == TRUE) ? TRUE : FALSE; + } + else + { + /* could not query for ability to seek by querying the + * pipeline; let's crudely try by using the URI + */ + if (priv->uri && g_str_has_prefix (priv->uri, "http://")) + priv->can_seek = FALSE; + else + priv->can_seek = TRUE; + } + + gst_query_unref (query); + + CLUTTER_GST_NOTE (MEDIA, "can-seek: %d", priv->can_seek); + + g_object_notify (G_OBJECT (self), "can-seek"); + + query_duration (self); + } + + /* is_idle controls the drawing with the idle material */ + if (new_state == GST_STATE_NULL) + { + priv->is_idle = TRUE; + g_object_notify (G_OBJECT (self), "idle"); + } + else if (new_state == GST_STATE_PLAYING) + { + priv->is_idle = FALSE; + priv->is_changing_uri = FALSE; + g_object_notify (G_OBJECT (self), "idle"); + } + + if (!priv->is_idle) + { + if (priv->stacked_progress) + { + set_progress (self, priv->stacked_progress); + } + } +} + +static void +bus_message_async_done_cb (GstBus *bus, + GstMessage *message, + ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + if (priv->in_seek) + { + g_object_notify (G_OBJECT (self), "progress"); + + set_in_seek (self, FALSE); + + if (priv->stacked_progress) + { + set_progress (self, priv->stacked_progress); + } + } +} + +static gboolean +on_volume_changed_main_context (gpointer data) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data); + ClutterGstPlaybackPrivate *priv = self->priv; + gdouble volume; + + volume = + gst_stream_volume_get_volume (GST_STREAM_VOLUME (priv->pipeline), + GST_STREAM_VOLUME_FORMAT_CUBIC); + priv->volume = volume; + + g_object_notify (G_OBJECT (self), "audio-volume"); + + g_object_unref (self); + + return FALSE; +} + +/* playbin proxies the volume property change notification directly from + * the element having the "volume" property. This means this callback is + * called from the thread that runs the element, potentially different from + * the main thread */ +static void +on_volume_changed (GstElement *pipeline, + GParamSpec *pspec, + ClutterGstPlayback *self) +{ + g_idle_add (on_volume_changed_main_context, g_object_ref (self)); +} + +static GList * +get_tags (GstElement *pipeline, + const gchar *property_name, + const gchar *action_signal) +{ + GList *ret = NULL; + gint i, n; + + g_object_get (G_OBJECT (pipeline), property_name, &n, NULL); + if (n == 0) + return NULL; + + for (i = 0; i < n; i++) + { + GstTagList *tags = NULL; + + g_signal_emit_by_name (G_OBJECT (pipeline), action_signal, i, &tags); + + ret = g_list_prepend (ret, tags); + } + + return g_list_reverse (ret); +} + +static gboolean +on_audio_changed_main_context (gpointer data) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data); + ClutterGstPlaybackPrivate *priv = self->priv; + + free_tags_list (&priv->audio_streams); + priv->audio_streams = get_tags (priv->pipeline, "n-audio", "get-audio-tags"); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed"); + + g_object_notify (G_OBJECT (self), "audio-streams"); + + g_object_unref (self); + + return FALSE; +} + +/* same explanation as for notify::volume's usage of g_idle_add() */ +static void +on_audio_changed (GstElement *pipeline, + ClutterGstPlayback *self) +{ + g_idle_add (on_audio_changed_main_context, g_object_ref (self)); +} + +static void +on_audio_tags_changed (GstElement *pipeline, + gint stream, + ClutterGstPlayback *self) +{ + gint current_stream; + + g_object_get (G_OBJECT (pipeline), "current-audio", ¤t_stream, NULL); + + if (current_stream != stream) + return; + + g_idle_add (on_audio_changed_main_context, g_object_ref (self)); +} + +static gboolean +on_current_audio_changed_main_context (gpointer data) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream changed"); + g_object_notify (G_OBJECT (self), "audio-stream"); + + g_object_unref (self); + + return FALSE; +} + +static void +on_current_audio_changed (GstElement *pipeline, + GParamSpec *pspec, + ClutterGstPlayback *self) +{ + g_idle_add (on_current_audio_changed_main_context, g_object_ref (self)); +} + +static gboolean +on_text_changed_main_context (gpointer data) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data); + ClutterGstPlaybackPrivate *priv = self->priv; + + free_tags_list (&priv->subtitle_tracks); + priv->subtitle_tracks = get_tags (priv->pipeline, "n-text", "get-text-tags"); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "subtitle-tracks changed"); + + g_object_notify (G_OBJECT (self), "subtitle-tracks"); + + g_object_unref (self); + + return FALSE; +} + +/* same explanation as for notify::volume's usage of g_idle_add() */ +static void +on_text_changed (GstElement *pipeline, + ClutterGstPlayback *self) +{ + g_idle_add (on_text_changed_main_context, g_object_ref (self)); +} + +static void +on_text_tags_changed (GstElement *pipeline, + gint stream, + ClutterGstPlayback *self) +{ + g_idle_add (on_text_changed_main_context, g_object_ref (self)); +} + +static gboolean +on_current_text_changed_main_context (gpointer data) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "text stream changed"); + g_object_notify (G_OBJECT (self), "subtitle-track"); + + g_object_unref (self); + + return FALSE; +} + +static void +on_current_text_changed (GstElement *pipeline, + GParamSpec *pspec, + ClutterGstPlayback *self) +{ + g_idle_add (on_current_text_changed_main_context, g_object_ref (self)); +} + +/**/ + +static ClutterGstFrame * +clutter_gst_playback_get_frame (ClutterGstPlayer *self) +{ + ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv; + + return priv->current_frame; +} + +static GstElement * +clutter_gst_playback_get_pipeline (ClutterGstPlayer *self) +{ + ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv; + + return priv->pipeline; +} + +static gboolean +clutter_gst_playback_get_idle (ClutterGstPlayer *self) +{ + ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv; + + return priv->is_idle; +} + +static gdouble +clutter_gst_playback_get_audio_volume (ClutterGstPlayer *self) +{ + return get_audio_volume (CLUTTER_GST_PLAYBACK (self)); +} + +static void +clutter_gst_playback_set_audio_volume (ClutterGstPlayer *self, + gdouble volume) +{ + set_audio_volume (CLUTTER_GST_PLAYBACK (self), volume); +} + +static gboolean +clutter_gst_playback_get_playing (ClutterGstPlayer *self) +{ + return get_playing (CLUTTER_GST_PLAYBACK (self)); +} + +static void +clutter_gst_playback_set_playing (ClutterGstPlayer *self, + gboolean playing) +{ + set_playing (CLUTTER_GST_PLAYBACK (self), playing); +} + +static void +player_iface_init (ClutterGstPlayerIface *iface) +{ + iface->get_frame = clutter_gst_playback_get_frame; + iface->get_pipeline = clutter_gst_playback_get_pipeline; + iface->get_idle = clutter_gst_playback_get_idle; + + iface->get_audio_volume = clutter_gst_playback_get_audio_volume; + iface->set_audio_volume = clutter_gst_playback_set_audio_volume; + + iface->get_playing = clutter_gst_playback_get_playing; + iface->set_playing = clutter_gst_playback_set_playing; +} + +/**/ + +static void +clutter_gst_playback_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterGstPlayback *self; + ClutterGstPlaybackPrivate *priv = self->priv; + gchar *str; + + switch (property_id) + { + case PROP_URI: + g_value_set_string (value, priv->uri); + break; + + case PROP_PLAYING: + g_value_set_boolean (value, get_playing (self)); + break; + + case PROP_PROGRESS: + g_value_set_double (value, get_progress (self)); + break; + + case PROP_SUBTITLE_URI: + g_object_get (priv->pipeline, "suburi", &str, NULL); + g_value_take_string (value, str); + break; + + case PROP_SUBTITLE_FONT_NAME: + g_value_set_string (value, priv->font_name); + break; + + case PROP_AUDIO_VOLUME: + g_value_set_double (value, get_audio_volume (self)); + break; + + case PROP_CAN_SEEK: + g_value_set_boolean (value, priv->can_seek); + break; + + case PROP_BUFFER_FILL: + g_value_set_double (value, priv->buffer_fill); + break; + + case PROP_DURATION: + g_value_set_double (value, priv->duration); + break; + + case PROP_IDLE: + g_value_set_boolean (value, priv->is_idle); + break; + + case PROP_USER_AGENT: + { + gchar *user_agent; + + user_agent = clutter_gst_playback_get_user_agent (self); + g_value_take_string (value, user_agent); + } + break; + + case PROP_SEEK_FLAGS: + { + ClutterGstSeekFlags seek_flags; + + seek_flags = clutter_gst_playback_get_seek_flags (self); + g_value_set_flags (value, seek_flags); + } + break; + + case PROP_AUDIO_STREAMS: + g_value_set_pointer (value, priv->audio_streams); + break; + + case PROP_AUDIO_STREAM: + { + gint index_; + + index_ = clutter_gst_playback_get_audio_stream (self); + g_value_set_int (value, index_); + } + break; + + case PROP_SUBTITLE_TRACKS: + g_value_set_pointer (value, priv->subtitle_tracks); + break; + + case PROP_SUBTITLE_TRACK: + { + gint index_; + + index_ = clutter_gst_playback_get_subtitle_track (self); + g_value_set_int (value, index_); + } + break; + + case PROP_IN_SEEK: + g_value_set_boolean (value, priv->in_seek); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +clutter_gst_playback_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (object); + + switch (property_id) + { + case PROP_URI: + set_uri (self, g_value_get_string (value)); + break; + + case PROP_PLAYING: + set_playing (self, g_value_get_boolean (value)); + break; + + case PROP_PROGRESS: + set_progress (self, g_value_get_double (value)); + break; + + case PROP_SUBTITLE_URI: + set_subtitle_uri (self, g_value_get_string (value)); + break; + + case PROP_SUBTITLE_FONT_NAME: + set_subtitle_font_name (self, g_value_get_string (value)); + break; + + case PROP_AUDIO_VOLUME: + set_audio_volume (self, g_value_get_double (value)); + break; + + case PROP_USER_AGENT: + clutter_gst_playback_set_user_agent (self, + g_value_get_string (value)); + break; + + case PROP_SEEK_FLAGS: + clutter_gst_playback_set_seek_flags (self, + g_value_get_flags (value)); + break; + + case PROP_AUDIO_STREAM: + clutter_gst_playback_set_audio_stream (self, + g_value_get_int (value)); + break; + + case PROP_SUBTITLE_TRACK: + clutter_gst_playback_set_subtitle_track (self, + g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +clutter_gst_playback_dispose (GObject *object) +{ + ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (object)->priv; + + if (priv->tick_timeout_id) + { + g_source_remove (priv->tick_timeout_id); + priv->tick_timeout_id = 0; + } + + if (priv->buffering_timeout_id) + { + g_source_remove (priv->buffering_timeout_id); + priv->buffering_timeout_id = 0; + } + + if (priv->download_buffering_element) + g_clear_object (&priv->download_buffering_element); + + if (priv->bus) + { + gst_bus_remove_signal_watch (priv->bus); + priv->bus = NULL; + } + + if (priv->pipeline) + { + gst_element_set_state (priv->pipeline, GST_STATE_NULL); + g_clear_object (&priv->pipeline); + } + + if (priv->current_frame) + { + g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->current_frame); + priv->current_frame = NULL; + } + + g_free (priv->uri); + g_free (priv->font_name); + g_free (priv->user_agent); + priv->uri = priv->font_name = priv->user_agent = NULL; + free_tags_list (&priv->audio_streams); + free_tags_list (&priv->subtitle_tracks); + + G_OBJECT_CLASS (clutter_gst_playback_parent_class)->dispose (object); +} + +static void +clutter_gst_playback_finalize (GObject *object) +{ + G_OBJECT_CLASS (clutter_gst_playback_parent_class)->finalize (object); +} + +static void +clutter_gst_playback_class_init (ClutterGstPlaybackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (ClutterGstPlaybackPrivate)); + + object_class->get_property = clutter_gst_playback_get_property; + object_class->set_property = clutter_gst_playback_set_property; + object_class->dispose = clutter_gst_playback_dispose; + object_class->finalize = clutter_gst_playback_finalize; + + /** + * ClutterGstPlayback:uri: + * + * The location of a media file, expressed as a valid URI. + */ + pspec = g_param_spec_string ("uri", + "URI", + "URI of a media file", + NULL, + CLUTTER_GST_PARAM_READWRITE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_URI, pspec); + + /** + * ClutterGstPlayback:progress: + * + * The current progress of the playback, as a normalized + * value between 0.0 and 1.0. + */ + pspec = g_param_spec_double ("progress", + "Progress", + "Current progress of the playback", + 0.0, 1.0, 0.0, + CLUTTER_GST_PARAM_READWRITE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_PROGRESS, pspec); + + /** + * ClutterGstPlayback:subtitle-uri: + * + * The location of a subtitle file, expressed as a valid URI. + */ + pspec = g_param_spec_string ("subtitle-uri", + "Subtitle URI", + "URI of a subtitle file", + NULL, + CLUTTER_GST_PARAM_READWRITE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_SUBTITLE_URI, pspec); + + /** + * ClutterGstPlayback:subtitle-font-name: + * + * The font used to display subtitles. The font description has to + * follow the same grammar as the one recognized by + * pango_font_description_from_string(). + */ + pspec = g_param_spec_string ("subtitle-font-name", + "Subtitle Font Name", + "The font used to display subtitles", + NULL, + CLUTTER_GST_PARAM_READWRITE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_SUBTITLE_FONT_NAME, pspec); + + /** + * ClutterGstPlayback:can-seek: + * + * Whether the current stream is seekable. + */ + pspec = g_param_spec_boolean ("can-seek", + "Can Seek", + "Whether the current stream is seekable", + FALSE, + CLUTTER_GST_PARAM_READABLE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_CAN_SEEK, pspec); + + /** + * ClutterGstPlayback:buffer-fill: + * + * The fill level of the buffer for the current stream, + * as a value between 0.0 and 1.0. + */ + pspec = g_param_spec_double ("buffer-fill", + "Buffer Fill", + "The fill level of the buffer", + 0.0, 1.0, 0.0, + CLUTTER_GST_PARAM_READABLE | + G_PARAM_DEPRECATED); + g_object_class_install_property (object_class, PROP_BUFFER_FILL, pspec); + + /** + * ClutterGstPlayback:duration: + * + * The duration of the current stream, in seconds + */ + pspec = g_param_spec_double ("duration", + "Duration", + "The duration of the stream, in seconds", + 0, G_MAXDOUBLE, 0, + CLUTTER_GST_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_DURATION, pspec); + + /** + * ClutterGstPlayback:user-agent: + * + * The User Agent used by #ClutterGstPlayback with network protocols. + * + * Since: 1.4 + */ + pspec = g_param_spec_string ("user-agent", + "User Agent", + "User Agent used with network protocols", + NULL, + CLUTTER_GST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_USER_AGENT, pspec); + + /** + * ClutterGstPlayback:seek-flags: + * + * Flags to use when seeking. + * + * Since: 1.4 + */ + pspec = g_param_spec_flags ("seek-flags", + "Seek Flags", + "Flags to use when seeking", + CLUTTER_GST_TYPE_SEEK_FLAGS, + CLUTTER_GST_SEEK_FLAG_NONE, + CLUTTER_GST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SEEK_FLAGS, pspec); + + /** + * ClutterGstPlayback:audio-streams: + * + * List of audio streams available on the current media. + * + * Since: 1.4 + */ + pspec = g_param_spec_pointer ("audio-streams", + "Audio Streams", + "List of the audio streams of the media", + CLUTTER_GST_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_AUDIO_STREAMS, pspec); + + /** + * ClutterGstPlayback:audio-stream: + * + * Index of the current audio stream. + * + * Since: 1.4 + */ + pspec = g_param_spec_int ("audio-stream", + "Audio Stream", + "Index of the current audio stream", + -1, G_MAXINT, -1, + CLUTTER_GST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_AUDIO_STREAM, pspec); + + /** + * ClutterGstPlayback:subtitle-tracks: + * + * List of subtitle tracks available. + * + * Since: 1.4 + */ + pspec = g_param_spec_pointer ("subtitle-tracks", + "Subtitles Tracks", + "List of the subtitles tracks of the media", + CLUTTER_GST_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_SUBTITLE_TRACKS, pspec); + + /** + * ClutterGstPlayback:subtitle-track: + * + * Current subtitle track being displayed. + * + * Since: 1.4 + */ + pspec = g_param_spec_int ("subtitle-track", + "Subtitle Track", + "Index of the current subtitles track", + -1, G_MAXINT, -1, + CLUTTER_GST_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SUBTITLE_TRACK, pspec); + + /** + * ClutterGstPlayback:in-seek: + * + * Whether or not the stream is being seeked. + * + * Since: 1.6 + */ + pspec = g_param_spec_boolean ("in-seek", + "In seek mode", + "If currently seeking", + FALSE, + CLUTTER_GST_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_IN_SEEK, pspec); + + + g_object_class_override_property (object_class, + PROP_IDLE, "idle"); + g_object_class_override_property (object_class, + PROP_PLAYING, "playing"); + g_object_class_override_property (object_class, + PROP_AUDIO_VOLUME, "audio-volume"); + + + /* Signals */ + + /** + * ClutterGstPlayback::download-buffering: + * @player: the #ClutterGstPlayback instance that received the signal + * @start: start position of the buffering + * @stop: start position of the buffering + * + * The ::download-buffering signal is emitted each time their an + * update about the buffering of the current media. + * + * Since: 1.4 + */ + signals[DOWNLOAD_BUFFERING_SIGNAL] = + g_signal_new ("download-buffering", + CLUTTER_GST_TYPE_PLAYBACK, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGstPlaybackClass, + download_buffering), + NULL, NULL, + _clutter_gst_marshal_VOID__DOUBLE_DOUBLE, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +static void +_new_frame_from_pipeline (CoglGstVideoSink *sink, ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + + clutter_gst_util_update_frame (CLUTTER_GST_PLAYER (self), + &priv->current_frame, + cogl_gst_video_sink_get_pipeline (sink)); +} + +static void +_ready_from_pipeline (CoglGstVideoSink *sink, ClutterGstPlayback *self) +{ + + ClutterGstPlaybackPrivate *priv = self->priv; + + g_signal_emit_by_name (self, "ready"); + + /* clutter_gst_util_update_frame (CLUTTER_GST_PLAYER (self), */ + /* &priv->current_frame, */ + /* cogl_gst_video_sink_get_pipeline (sink)); */ +} + +static GstElement * +get_pipeline (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv = self->priv; + GstElement *pipeline, *audio_sink; + CoglGstVideoSink *video_sink; + + pipeline = gst_element_factory_make ("playbin", "pipeline"); + if (!pipeline) + { + g_critical ("Unable to create playbin element"); + return NULL; + } + + audio_sink = gst_element_factory_make ("gconfaudiosink", "audio-sink"); + if (!audio_sink) + { + audio_sink = gst_element_factory_make ("autoaudiosink", "audio-sink"); + if (!audio_sink) + { + audio_sink = gst_element_factory_make ("alsasink", "audio-sink"); + g_warning ("Could not create a GST audio_sink. " + "Audio unavailable."); + + /* do we even need to bother? */ + if (!audio_sink) + audio_sink = gst_element_factory_make ("fakesink", "audio-sink"); + } + } + + video_sink = cogl_gst_video_sink_new (clutter_backend_get_cogl_context (clutter_get_default_backend ())); + /* gst_element_factory_make ("coglsink", "video-sink"); */ + + g_signal_connect (video_sink, "new-frame", + G_CALLBACK (_new_frame_from_pipeline), self); + g_signal_connect (video_sink, "pipeline-ready", + G_CALLBACK (_ready_from_pipeline), self); + + g_object_set (G_OBJECT (pipeline), + "audio-sink", audio_sink, + "video-sink", video_sink, + "subtitle-font-desc", "Sans 16", + NULL); + + return pipeline; +} + +static void +clutter_gst_playback_init (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + + self->priv = priv = GST_PLAYBACK_PRIVATE (self); + + priv->is_idle = TRUE; + priv->in_seek = FALSE; + priv->is_changing_uri = FALSE; + priv->in_download_buffering = FALSE; + + priv->pipeline = get_pipeline (self); + g_assert (priv->pipeline != NULL); + + g_signal_connect (priv->pipeline, "notify::source", + G_CALLBACK (on_source_changed), self); + + /* We default to not playing until someone calls set_playing(TRUE) */ + priv->target_state = GST_STATE_PAUSED; + + /* Default to a fast seek, ie. same effect than set_seek_flags (NONE); */ + priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT; + + priv->bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline)); + + gst_bus_add_signal_watch (priv->bus); + + g_signal_connect_object (priv->bus, "message::error", + G_CALLBACK (bus_message_error_cb), + self, 0); + g_signal_connect_object (priv->bus, "message::eos", + G_CALLBACK (bus_message_eos_cb), + self, 0); + g_signal_connect_object (priv->bus, "message::buffering", + G_CALLBACK (bus_message_buffering_cb), + self, 0); + g_signal_connect_object (priv->bus, "message::duration-changed", + G_CALLBACK (bus_message_duration_changed_cb), + self, 0); + g_signal_connect_object (priv->bus, "message::state-changed", + G_CALLBACK (bus_message_state_change_cb), + self, 0); + g_signal_connect_object (priv->bus, "message::async-done", + G_CALLBACK (bus_message_async_done_cb), + self, 0); + + g_signal_connect (priv->pipeline, "notify::volume", + G_CALLBACK (on_volume_changed), + self); + + g_signal_connect (priv->pipeline, "audio-changed", + G_CALLBACK (on_audio_changed), + self); + g_signal_connect (priv->pipeline, "audio-tags-changed", + G_CALLBACK (on_audio_tags_changed), + self); + g_signal_connect (priv->pipeline, "notify::current-audio", + G_CALLBACK (on_current_audio_changed), + self); + + g_signal_connect (priv->pipeline, "text-changed", + G_CALLBACK (on_text_changed), + self); + g_signal_connect (priv->pipeline, "text-tags-changed", + G_CALLBACK (on_text_tags_changed), + self); + g_signal_connect (priv->pipeline, "notify::current-text", + G_CALLBACK (on_current_text_changed), + self); + +#if defined(CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT) + gst_bus_set_sync_handler (priv->bus, on_sync_message, + clutter_x11_get_default_display (), NULL); +#endif + + gst_object_unref (GST_OBJECT (priv->bus)); +} + +ClutterGstPlayback * +clutter_gst_playback_new (void) +{ + return g_object_new (CLUTTER_GST_TYPE_PLAYBACK, NULL); +} + +/** + * clutter_gst_playback_set_uri: + * @self: a #ClutterGstPlayback + * @uri: the URI of the media stream + * + * Sets the URI of @self to @uri. + */ +void +clutter_gst_playback_set_uri (ClutterGstPlayback *self, + const gchar *uri) +{ + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + g_object_set (G_OBJECT (self), "uri", uri, NULL); +} + +/** + * clutter_gst_playback_get_uri: + * @self: a #ClutterGstPlayback + * + * Retrieves the URI from @self. + * + * Return value: the URI of the media stream. Use g_free() + * to free the returned string + */ +gchar * +clutter_gst_playback_get_uri (ClutterGstPlayback *self) +{ + gchar *retval = NULL; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + g_object_get (G_OBJECT (self), "uri", &retval, NULL); + + return retval; +} + +/** + * clutter_gst_playback_set_filename: + * @self: a #ClutterGstPlayback + * @filename: A filename + * + * Sets the source of @self using a file path. + */ +void +clutter_gst_playback_set_filename (ClutterGstPlayback *self, + const gchar *filename) +{ + gchar *uri; + GError *uri_error = NULL; + + if (!g_path_is_absolute (filename)) + { + gchar *abs_path; + + abs_path = g_build_filename (g_get_current_dir (), filename, NULL); + uri = g_filename_to_uri (abs_path, NULL, &uri_error); + g_free (abs_path); + } + else + uri = g_filename_to_uri (filename, NULL, &uri_error); + + if (uri_error) + { + g_signal_emit_by_name (self, "error", uri_error); + g_error_free (uri_error); + return; + } + + clutter_gst_playback_set_uri (self, uri); + + g_free (uri); +} + +/** + * clutter_gst_playback_get_user_agent: + * @self: a #ClutterGstPlayback + * + * Retrieves the user agent used when streaming. + * + * Return value: the user agent used. The returned string has to be freed with + * g_free() + * + * Since: 1.4 + */ +gchar * +clutter_gst_playback_get_user_agent (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + GstElement *source; + GParamSpec *pspec; + gchar *user_agent; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + priv = self->priv; + + /* If the user has set a custom user agent, we just return it even if it is + * not used by the current source element of the pipeline */ + if (priv->user_agent) + return g_strdup (priv->user_agent); + + /* If not, we try to retrieve the user agent used by the current source */ + g_object_get (priv->pipeline, "source", &source, NULL); + if (source == NULL) + return NULL; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), + "user-agent"); + if (pspec == NULL) + return NULL; + + g_object_get (source, "user-agent", &user_agent, NULL); + + return user_agent; +} + +/** + * clutter_gst_playback_set_user_agent: + * @self: a #ClutterGstPlayback + * @user_agent: the user agent + * + * Sets the user agent to use when streaming. + * + * When streaming content, you might want to set a custom user agent, eg. to + * promote your software, make it appear in statistics or because the server + * requires a special user agent you want to impersonate. + * + * Since: 1.4 + */ +void +clutter_gst_playback_set_user_agent (ClutterGstPlayback *self, + const gchar *user_agent) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_free (priv->user_agent); + if (user_agent) + priv->user_agent = g_strdup (user_agent); + else + priv->user_agent = NULL; + + player_set_user_agent (self, user_agent); +} + +/** + * clutter_gst_playback_get_seek_flags: + * @self: a #ClutterGstPlayback + * + * Get the current value of the seek-flags property. + * + * Return value: a combination of #ClutterGstSeekFlags + * + * Since: 1.4 + */ +ClutterGstSeekFlags +clutter_gst_playback_get_seek_flags (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), + CLUTTER_GST_SEEK_FLAG_NONE); + + priv = self->priv; + + if (priv->seek_flags == GST_SEEK_FLAG_ACCURATE) + return CLUTTER_GST_SEEK_FLAG_ACCURATE; + else + return CLUTTER_GST_SEEK_FLAG_NONE; +} + +/** + * clutter_gst_playback_set_seek_flags: + * @self: a #ClutterGstPlayback + * @flags: a combination of #ClutterGstSeekFlags + * + * Seeking can be done with several trade-offs. Clutter-gst defaults + * to %CLUTTER_GST_SEEK_FLAG_NONE. + * + * Since: 1.4 + */ +void +clutter_gst_playback_set_seek_flags (ClutterGstPlayback *self, + ClutterGstSeekFlags flags) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + if (flags == CLUTTER_GST_SEEK_FLAG_NONE) + priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT; + else if (flags & CLUTTER_GST_SEEK_FLAG_ACCURATE) + priv->seek_flags = GST_SEEK_FLAG_ACCURATE; +} + +/** + * clutter_gst_playback_get_buffering_mode: + * @self: a #ClutterGstPlayback + * + * Return value: a #ClutterGstBufferingMode + * + * Since: 1.4 + */ +ClutterGstBufferingMode +clutter_gst_playback_get_buffering_mode (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + GstPlayFlags flags; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), + CLUTTER_GST_BUFFERING_MODE_STREAM); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL); + + if (flags & GST_PLAY_FLAG_DOWNLOAD) + return CLUTTER_GST_BUFFERING_MODE_DOWNLOAD; + + return CLUTTER_GST_BUFFERING_MODE_STREAM; +} + +/** + * clutter_gst_playback_set_buffering_mode: + * @self: a #ClutterGstPlayback + * @mode: a #ClutterGstBufferingMode + * + * Since: 1.4 + */ +void +clutter_gst_playback_set_buffering_mode (ClutterGstPlayback *self, + ClutterGstBufferingMode mode) +{ + ClutterGstPlaybackPrivate *priv; + GstPlayFlags flags; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL); + + switch (mode) + { + case CLUTTER_GST_BUFFERING_MODE_STREAM: + flags &= ~GST_PLAY_FLAG_DOWNLOAD; + break; + + case CLUTTER_GST_BUFFERING_MODE_DOWNLOAD: + flags |= GST_PLAY_FLAG_DOWNLOAD; + break; + + default: + g_warning ("Unexpected buffering mode %d", mode); + break; + } + + g_object_set (G_OBJECT (priv->pipeline), "flags", flags, NULL); +} + +/** + * clutter_gst_playback_get_buffer_fill: + * @self: a #ClutterGstPlayback + * + * Retrieves the amount of the stream that is buffered. + * + * Return value: the fill level, between 0.0 and 1.0 + */ +gdouble +clutter_gst_playback_get_buffer_fill (ClutterGstPlayback *self) +{ + gdouble retval = 0.0; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0); + + g_object_get (G_OBJECT (self), "buffer-fill", &retval, NULL); + + return retval; +} + +/** + * clutter_gst_playback_get_buffer_size: + * @self: a #ClutterGstPlayback + * + * Retrieves the buffer size when buffering network streams. + * + * Return value: The buffer size + */ +gint +clutter_gst_playback_get_buffer_size (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + gint size; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), "buffer-size", &size, NULL); + + return size; +} + +/** + * clutter_gst_playback_set_buffer_size: + * @self: a #ClutterGstPlayback + * @size: The new size + * + * Sets the buffer size to be used when buffering network streams. + */ +void +clutter_gst_playback_set_buffer_size (ClutterGstPlayback *self, + gint size) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_object_set (G_OBJECT (priv->pipeline), "buffer-size", size, NULL); +} + +/** + * clutter_gst_playback_get_buffer_duration: + * @self: a #ClutterGstPlayback + * + * Retrieves the buffer duration when buffering network streams. + * + * Return value: The buffer duration + */ +gint64 +clutter_gst_playback_get_buffer_duration (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + gint64 duration; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), "buffer-duration", &duration, NULL); + + return duration; +} + +/** + * clutter_gst_playback_set_buffer_duration: + * @self: a #ClutterGstPlayback + * @duration: The new duration + * + * Sets the buffer duration to be used when buffering network streams. + */ +void +clutter_gst_playback_set_buffer_duration (ClutterGstPlayback *self, + gint64 duration) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_object_set (G_OBJECT (priv->pipeline), "buffer-duration", duration, NULL); +} + +/** + * clutter_gst_playback_get_audio_streams: + * @self: a #ClutterGstPlayback + * + * Get the list of audio streams of the current media. + * + * Return value: (transfer none) (element-type utf8): a list of + * strings describing the available audio streams + * + * Since: 1.4 + */ +GList * +clutter_gst_playback_get_audio_streams (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + priv = self->priv; + + if (CLUTTER_GST_DEBUG_ENABLED (AUDIO_STREAM)) + { + gchar *streams; + + streams = list_to_string (priv->audio_streams); + CLUTTER_GST_NOTE (AUDIO_STREAM, "audio streams: %s", streams); + g_free (streams); + } + + return priv->audio_streams; +} + +/** + * clutter_gst_playback_get_audio_stream: + * @self: a #ClutterGstPlayback + * + * Get the current audio stream. The number returned in the index of the + * audio stream playing in the list returned by + * clutter_gst_playback_get_audio_streams(). + * + * Return value: the index of the current audio stream, -1 if the media has no + * audio stream + * + * Since: 1.4 + */ +gint +clutter_gst_playback_get_audio_stream (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + gint index_ = -1; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), + "current-audio", &index_, + NULL); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream is #%d", index_); + + return index_; +} + +/** + * clutter_gst_playback_set_audio_stream: + * @self: a #ClutterGstPlayback + * @index_: the index of the audio stream + * + * Set the audio stream to play. @index_ is the index of the stream + * in the list returned by clutter_gst_playback_get_audio_streams(). + * + * Since: 1.4 + */ +void +clutter_gst_playback_set_audio_stream (ClutterGstPlayback *self, + gint index_) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_return_if_fail (index_ >= 0 && + index_ < (gint) g_list_length (priv->audio_streams)); + + CLUTTER_GST_NOTE (AUDIO_STREAM, "set audio audio stream to #%d", index_); + + g_object_set (G_OBJECT (priv->pipeline), + "current-audio", index_, + NULL); +} + +/** + * clutter_gst_playback_set_subtitle_uri: + * @self: a #ClutterGstPlayback + * @uri: the URI of a subtitle file + * + * Sets the location of a subtitle file to display while playing @self. + */ +void +clutter_gst_playback_set_subtitle_uri (ClutterGstPlayback *self, + const char *uri) +{ + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + g_object_set (G_OBJECT (self), "subtitle-uri", uri, NULL); +} + +/** + * clutter_gst_playback_get_subtitle_uri: + * @self: a #ClutterGstPlayback + * + * Retrieves the URI of the subtitle file in use. + * + * Return value: the URI of the subtitle file. Use g_free() + * to free the returned string + */ +gchar * +clutter_gst_playback_get_subtitle_uri (ClutterGstPlayback *self) +{ + gchar *retval = NULL; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + g_object_get (G_OBJECT (self), "subtitle-uri", &retval, NULL); + + return retval; +} + +/** + * clutter_gst_playback_set_subtitle_font_name: + * @self: a #ClutterGstPlayback + * @font_name: a font name, or %NULL to set the default font name + * + * Sets the font used by the subtitle renderer. The @font_name string must be + * either %NULL, which means that the default font name of the underlying + * implementation will be used; or must follow the grammar recognized by + * pango_font_description_from_string() like: + * + * |[ + * clutter_gst_playback_set_subtitle_font_name (player, "Sans 24pt"); + * ]| + */ +void +clutter_gst_playback_set_subtitle_font_name (ClutterGstPlayback *self, + const char *font_name) +{ + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + g_object_set (G_OBJECT (self), "subtitle-font-name", font_name, NULL); +} + +/** + * clutter_gst_playback_get_subtitle_font_name: + * @self: a #ClutterGstPlayback + * + * Retrieves the font name currently used. + * + * Return value: a string containing the font name. Use g_free() + * to free the returned string + */ +gchar * +clutter_gst_playback_get_subtitle_font_name (ClutterGstPlayback *self) +{ + gchar *retval = NULL; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + g_object_get (G_OBJECT (self), "subtitle-font-name", &retval, NULL); + + return retval; +} + +/** + * clutter_gst_playback_get_subtitle_tracks: + * @self: a #ClutterGstPlayback + * + * Get the list of subtitles tracks of the current media. + * + * Return value: (transfer none) (element-type utf8): a list of + * strings describing the available subtitles tracks + * + * Since: 1.4 + */ +GList * +clutter_gst_playback_get_subtitle_tracks (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL); + + priv = self->priv; + + if (CLUTTER_GST_DEBUG_ENABLED (SUBTITLES)) + { + gchar *tracks; + + tracks = list_to_string (priv->subtitle_tracks); + CLUTTER_GST_NOTE (SUBTITLES, "subtitle tracks: %s", tracks); + g_free (tracks); + } + + return priv->subtitle_tracks; +} + +/** + * clutter_gst_playback_get_subtitle_track: + * @self: a #ClutterGstPlayback + * + * Get the current subtitles track. The number returned is the index of the + * subtiles track in the list returned by + * clutter_gst_playback_get_subtitle_tracks(). + * + * Return value: the index of the current subtitlest track, -1 if the media has + * no subtitles track or if the subtitles have been turned off + * + * Since: 1.4 + */ +gint +clutter_gst_playback_get_subtitle_track (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + gint index_ = -1; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), -1); + + priv = self->priv; + + g_object_get (G_OBJECT (priv->pipeline), + "current-text", &index_, + NULL); + + CLUTTER_GST_NOTE (SUBTITLES, "text track is #%d", index_); + + return index_; +} + +/** + * clutter_gst_playback_set_subtitle_track: + * @self: a #ClutterGstPlayback + * @index_: the index of the subtitles track + * + * Set the subtitles track to play. @index_ is the index of the stream + * in the list returned by clutter_gst_playback_get_subtitle_tracks(). + * + * If @index_ is -1, the subtitles are turned off. + * + * Since: 1.4 + */ +void +clutter_gst_playback_set_subtitle_track (ClutterGstPlayback *self, + gint index_) +{ + ClutterGstPlaybackPrivate *priv; + GstPlayFlags flags; + + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + priv = self->priv; + + g_return_if_fail (index_ >= -1 && + index_ < (gint) g_list_length (priv->subtitle_tracks)); + + CLUTTER_GST_NOTE (SUBTITLES, "set subtitle track to #%d", index_); + + g_object_get (priv->pipeline, "flags", &flags, NULL); + flags &= ~GST_PLAY_FLAG_TEXT; + g_object_set (priv->pipeline, "flags", flags, NULL); + + if (index_ >= 0) + { + g_object_set (G_OBJECT (priv->pipeline), + "current-text", index_, + NULL); + + flags |= GST_PLAY_FLAG_TEXT; + g_object_set (priv->pipeline, "flags", flags, NULL); + } +} + +/** + * clutter_gst_playback_get_in_seek: + * @self: a #ClutterGstPlayback + * + * Whether the player is seeking. + * + * Return value: TRUE if the player is seeking, FALSE otherwise. + * + * Since: 1.6 + */ +gboolean +clutter_gst_playback_get_in_seek (ClutterGstPlayback *self) +{ + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE); + + return self->priv->in_seek; +} + +/** + * clutter_gst_playback_get_can_seek: + * @self: a #ClutterGstPlayback + * + * Retrieves whether @self is seekable or not. + * + * Return value: %TRUE if @self can seek, %FALSE otherwise. + */ +gboolean +clutter_gst_playback_get_can_seek (ClutterGstPlayback *self) +{ + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE); + + return self->priv->can_seek; +} + +/** + * clutter_gst_playback_set_progress: + * @self: a #ClutterGstPlayback + * @progress: the progress of the playback, between 0.0 and 1.0 + * + * Sets the playback progress of @self. The @progress is + * a normalized value between 0.0 (begin) and 1.0 (end). + */ +void +clutter_gst_playback_set_progress (ClutterGstPlayback *self, + gdouble progress) +{ + g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self)); + + set_progress (self, progress); +} + +/** + * clutter_gst_playback_get_progress: + * @self: a #ClutterGstPlayback + * + * Retrieves the playback progress of @self. + * + * Return value: the playback progress, between 0.0 and 1.0 + */ +gdouble +clutter_gst_playback_get_progress (ClutterGstPlayback *self) +{ + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0); + + return get_progress (self); +} + +/** + * clutter_gst_playback_get_duration: + * @self: a #ClutterGstPlayback + * + * Retrieves the duration of the media stream that @self represents. + * + * Return value: the duration of the media stream, in seconds + */ +gdouble +clutter_gst_playback_get_duration (ClutterGstPlayback *self) +{ + gdouble retval = 0; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0); + + g_object_get (G_OBJECT (self), "duration", &retval, NULL); + + return retval; +} + +/** + * clutter_gst_playback_is_live_media: + * @self: a #ClutterGstPlayback + * + * Whether the player is using a live media. + * + * Return value: TRUE if the player is using a live media, FALSE otherwise. + */ +gboolean +clutter_gst_playback_is_live_media (ClutterGstPlayback *self) +{ + ClutterGstPlaybackPrivate *priv; + + g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE); + + return self->priv->is_live; +} diff --git a/clutter-gst/clutter-gst-playback.h b/clutter-gst/clutter-gst-playback.h new file mode 100644 index 0000000..1be5084 --- /dev/null +++ b/clutter-gst/clutter-gst-playback.h @@ -0,0 +1,116 @@ +/* clutter-gst-playback.h */ + +#ifndef __CLUTTER_GST_PLAYBACK_H__ +#define __CLUTTER_GST_PLAYBACK_H__ + +#include <glib-object.h> + +#include <clutter-gst/clutter-gst-types.h> + +G_BEGIN_DECLS + +#define CLUTTER_GST_TYPE_PLAYBACK clutter_gst_playback_get_type() + +#define CLUTTER_GST_PLAYBACK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_GST_TYPE_PLAYBACK, \ + ClutterGstPlayback)) + +#define CLUTTER_GST_PLAYBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + CLUTTER_GST_TYPE_PLAYBACK, \ + ClutterGstPlaybackClass)) + +#define CLUTTER_GST_IS_PLAYBACK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_GST_TYPE_PLAYBACK)) + +#define CLUTTER_GST_IS_PLAYBACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + CLUTTER_GST_TYPE_PLAYBACK)) + +#define CLUTTER_GST_PLAYBACK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + CLUTTER_GST_TYPE_PLAYBACK, \ + ClutterGstPlaybackClass)) + +typedef struct _ClutterGstPlayback ClutterGstPlayback; +typedef struct _ClutterGstPlaybackClass ClutterGstPlaybackClass; +typedef struct _ClutterGstPlaybackPrivate ClutterGstPlaybackPrivate; + +struct _ClutterGstPlayback +{ + GObject parent; + + ClutterGstPlaybackPrivate *priv; +}; + +struct _ClutterGstPlaybackClass +{ + GObjectClass parent_class; + + /* signals */ + void (* download_buffering) (ClutterGstPlayback *self, + gdouble start, + gdouble stop); +}; + +GType clutter_gst_playback_get_type (void) G_GNUC_CONST; + +ClutterGstPlayback * clutter_gst_playback_new (void); + +void clutter_gst_playback_set_uri (ClutterGstPlayback *self, + const gchar *uri); +gchar * clutter_gst_playback_get_uri (ClutterGstPlayback *self); +void clutter_gst_playback_set_filename (ClutterGstPlayback *self, + const gchar *filename); + +gchar * clutter_gst_playback_get_user_agent (ClutterGstPlayback *self); +void clutter_gst_playback_set_user_agent (ClutterGstPlayback *self, + const gchar *user_agent); + +ClutterGstSeekFlags clutter_gst_playback_get_seek_flags (ClutterGstPlayback *self); +void clutter_gst_playback_set_seek_flags (ClutterGstPlayback *self, + ClutterGstSeekFlags flags); + +ClutterGstBufferingMode clutter_gst_playback_get_buffering_mode (ClutterGstPlayback *self); +void clutter_gst_playback_set_buffering_mode (ClutterGstPlayback *self, + ClutterGstBufferingMode mode); +gdouble clutter_gst_playback_get_buffer_fill (ClutterGstPlayback *self); +gint clutter_gst_playback_get_buffer_size (ClutterGstPlayback *self); +void clutter_gst_playback_set_buffer_size (ClutterGstPlayback *self, + gint size); +gint64 clutter_gst_playback_get_buffer_duration (ClutterGstPlayback *self); +void clutter_gst_playback_set_buffer_duration (ClutterGstPlayback *self, + gint64 duration); + +GList * clutter_gst_playback_get_audio_streams (ClutterGstPlayback *self); +gint clutter_gst_playback_get_audio_stream (ClutterGstPlayback *self); +void clutter_gst_playback_set_audio_stream (ClutterGstPlayback *self, + gint index_); + +void clutter_gst_playback_set_subtitle_uri (ClutterGstPlayback *self, + const gchar *uri); +gchar * clutter_gst_playback_get_subtitle_uri (ClutterGstPlayback *self); +void clutter_gst_playback_set_subtitle_font_name + (ClutterGstPlayback *self, + const char *font_name); +gchar * clutter_gst_playback_get_subtitle_font_name + (ClutterGstPlayback *self); +GList * clutter_gst_playback_get_subtitle_tracks (ClutterGstPlayback *self); +gint clutter_gst_playback_get_subtitle_track (ClutterGstPlayback *self); +void clutter_gst_playback_set_subtitle_track (ClutterGstPlayback *self, + gint index_); + +gboolean clutter_gst_playback_get_in_seek (ClutterGstPlayback *self); + +void clutter_gst_playback_set_progress (ClutterGstPlayback *self, + gdouble progress); +gdouble clutter_gst_playback_get_progress (ClutterGstPlayback *self); +gdouble clutter_gst_playback_get_duration (ClutterGstPlayback *self); + +gboolean clutter_gst_playback_is_live_media (ClutterGstPlayback *self); + +G_END_DECLS + +#endif /* __CLUTTER_GST_PLAYBACK_H__ */ diff --git a/clutter-gst/clutter-gst-player.c b/clutter-gst/clutter-gst-player.c index 0b5dc81..25f8db5 100644 --- a/clutter-gst/clutter-gst-player.c +++ b/clutter-gst/clutter-gst-player.c @@ -12,7 +12,7 @@ * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> * * Copyright (C) 2006 OpenedHand - * Copyright (C) 2009-2011 Intel Corporation + * Copyright (C) 2009-2013 Intel Corporation * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> * * This library is free software; you can redistribute it and/or @@ -45,2213 +45,34 @@ #include "config.h" #endif -#include <string.h> - -#include <gst/video/video.h> -#include <gst/tag/tag.h> -#include <gst/audio/streamvolume.h> - -#include "clutter-gst-debug.h" #include "clutter-gst-enum-types.h" #include "clutter-gst-marshal.h" #include "clutter-gst-player.h" #include "clutter-gst-private.h" -#if defined (CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT) -#define GST_USE_UNSTABLE_API 1 -#include <gst/video/videocontext.h> -#include <clutter/x11/clutter-x11.h> -#endif - - -typedef ClutterGstPlayerIface ClutterGstPlayerInterface; +typedef ClutterGstPlayerIface ClutterGstPlayerInterface; G_DEFINE_INTERFACE (ClutterGstPlayer, clutter_gst_player, G_TYPE_OBJECT) -#define PLAYER_GET_PRIVATE(player) \ - (g_object_get_qdata (G_OBJECT (player), \ - clutter_gst_player_private_quark)) -#define PLAYER_SET_PRIVATE(player,private) \ - (g_object_set_qdata (G_OBJECT (player), \ - clutter_gst_player_private_quark, \ - private)) - -#define PLAYER_GET_CLASS_PRIVATE(player) \ - (g_type_get_qdata (G_OBJECT_TYPE (player), \ - clutter_gst_player_class_quark)) - -/* idle timeouts (in ms) */ -#define TICK_TIMEOUT 500 -#define BUFFERING_TIMEOUT 250 - enum { + NEW_FRAME, + READY_SIGNAL, EOS_SIGNAL, + SIZE_CHANGE, ERROR_SIGNAL, /* can't be called 'ERROR' otherwise it clashes with wingdi.h */ - DOWNLOAD_BUFFERING_SIGNAL, LAST_SIGNAL }; -enum -{ - PROP_0, - - /* ClutterGstPlayer properties */ - PROP_URI, - PROP_PLAYING, - PROP_PROGRESS, - PROP_SUBTITLE_URI, - PROP_SUBTITLE_FONT_NAME, - PROP_AUDIO_VOLUME, - PROP_CAN_SEEK, - PROP_BUFFER_FILL, - PROP_DURATION, - PROP_IDLE, - PROP_USER_AGENT, - PROP_SEEK_FLAGS, - PROP_AUDIO_STREAMS, - PROP_AUDIO_STREAM, - PROP_SUBTITLE_TRACKS, - PROP_SUBTITLE_TRACK, - PROP_IN_SEEK -}; - -struct _ClutterGstPlayerIfacePrivate -{ - void (*set_property) (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); - void (*get_property) (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); -}; - -typedef struct _ClutterGstPlayerPrivate ClutterGstPlayerPrivate; - -/* Elements don't expose header files */ -typedef enum { - GST_PLAY_FLAG_VIDEO = (1 << 0), - GST_PLAY_FLAG_AUDIO = (1 << 1), - GST_PLAY_FLAG_TEXT = (1 << 2), - GST_PLAY_FLAG_VIS = (1 << 3), - GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), - GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), - GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), - GST_PLAY_FLAG_DOWNLOAD = (1 << 7), - GST_PLAY_FLAG_BUFFERING = (1 << 8), - GST_PLAY_FLAG_DEINTERLACE = (1 << 9) -} GstPlayFlags; - -struct _ClutterGstPlayerPrivate -{ - GObject parent; - - GstElement *pipeline; - GstBus *bus; - - gchar *uri; - - guint is_idle : 1; - guint is_live : 1; - guint can_seek : 1; - guint in_seek : 1; - guint is_changing_uri : 1; - guint in_error : 1; - guint in_eos : 1; - guint in_download_buffering : 1; - /* when in progressive download, we use the buffer-fill property to signal - * that we have enough data to play the stream. This flag allows to send - * the notify that buffer-fill is 1.0 only once */ - guint virtual_stream_buffer_signalled : 1; - - gdouble stacked_progress; - - gdouble target_progress; - GstState target_state; - - guint tick_timeout_id; - guint buffering_timeout_id; - - /* This is a cubic volume, suitable for use in a UI cf. StreamVolume doc */ - gdouble volume; - - gdouble buffer_fill; - gdouble duration; - gchar *font_name; - gchar *user_agent; - - GstSeekFlags seek_flags; /* flags for the seek in set_progress(); */ - - GstElement *download_buffering_element; - - GList *audio_streams; - GList *subtitle_tracks; -}; - -static GQuark clutter_gst_player_private_quark = 0; -static GQuark clutter_gst_player_class_quark = 0; - static guint signals[LAST_SIGNAL] = { 0, }; -static gboolean player_buffering_timeout (gpointer data); - -/* Logic */ - -#ifdef CLUTTER_GST_ENABLE_DEBUG -static gchar * -get_stream_description (GstTagList *tags, - gint track_num) -{ - gchar *description = NULL; - - if (tags) - { - - gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description); - - if (description) - { - const gchar *language = gst_tag_get_language_name (description); - - if (language) - { - g_free (description); - description = g_strdup (language); - } - } - - if (!description) - gst_tag_list_get_string (tags, GST_TAG_CODEC, &description); - } - - if (!description) - description = g_strdup_printf ("Track %d", track_num); - - return description; -} - -gchar * -list_to_string (GList *list) -{ - GstTagList *tags; - gchar *description; - GString *string; - GList *l; - gint n, i; - - if (!list) - return g_strdup ("<empty list>"); - - string = g_string_new (NULL); - n = g_list_length (list); - for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l)) - { - tags = l->data; - description = get_stream_description (tags, i); - g_string_append_printf (string, "%s, ", description); - g_free (description); - } - - tags = l->data; - description = get_stream_description (tags, i); - g_string_append_printf (string, "%s", (gchar *) description); - g_free (description); - - return g_string_free (string, FALSE); -} -#endif - -static const gchar * -gst_state_to_string (GstState state) -{ - switch (state) - { - case GST_STATE_VOID_PENDING: - return "pending"; - case GST_STATE_NULL: - return "null"; - case GST_STATE_READY: - return "ready"; - case GST_STATE_PAUSED: - return "paused"; - case GST_STATE_PLAYING: - return "playing"; - } - - return "Unknown state"; -} - -static void -free_tags_list (GList **listp) -{ - GList *l; - - l = *listp; - while (l) - { - if (l->data) - gst_tag_list_unref (l->data); - l = g_list_delete_link (l, l); - } - - *listp = NULL; -} - -static gboolean -tick_timeout (gpointer data) -{ - GObject *player = data; - - g_object_notify (player, "progress"); - - return TRUE; -} - -static void -player_set_user_agent (ClutterGstPlayer *player, - const gchar *user_agent) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstElement *source; - GParamSpec *pspec; - - if (user_agent == NULL) - return; - - g_object_get (priv->pipeline, "source", &source, NULL); - if (source == NULL) - return; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), - "user-agent"); - if (pspec == NULL) - return; - - CLUTTER_GST_NOTE (MEDIA, "setting user agent: %s", user_agent); - - g_object_set (source, "user-agent", user_agent, NULL); -} - -static void -autoload_subtitle (ClutterGstPlayer *player, - const gchar *uri) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - gchar *path, *dot, *subtitle_path; - GFile *video; - guint i; - - static const char subtitles_extensions[][4] = - { - "sub", "SUB", - "srt", "SRT", - "smi", "SMI", - "ssa", "SSA", - "ass", "ASS", - "asc", "ASC" - }; - - /* do not try to look for subtitle files if the video file is not mounted - * locally */ - if (!g_str_has_prefix (uri, "file://")) - return; - - /* Retrieve the absolute path of the video file */ - video = g_file_new_for_uri (uri); - path = g_file_get_path (video); - g_object_unref (video); - if (path == NULL) - return; - - /* Put a '\0' after the dot of the extension */ - dot = strrchr (path, '.'); - if (dot == NULL) { - g_free (path); - return; - } - *++dot = '\0'; - - /* we can't use path as the temporary buffer for the paths of the potential - * subtitle files as we may not have enough room there */ - subtitle_path = g_malloc (strlen (path) + 1 + 4); - strcpy (subtitle_path, path); - - /* reuse dot to point to the first byte of the extension of subtitle_path */ - dot = subtitle_path + (dot - path); - - for (i = 0; i < G_N_ELEMENTS (subtitles_extensions); i++) - { - GFile *candidate; - - memcpy (dot, subtitles_extensions[i], 4); - candidate = g_file_new_for_path (subtitle_path); - if (g_file_query_exists (candidate, NULL)) - { - gchar *suburi; - - suburi = g_file_get_uri (candidate); - - CLUTTER_GST_NOTE (MEDIA, "found subtitle: %s", suburi); - - g_object_set (priv->pipeline, "suburi", suburi, NULL); - g_free (suburi); - - g_object_unref (candidate); - break; - } - - g_object_unref (candidate); - } - - g_free (path); - g_free (subtitle_path); -} - -static void -set_subtitle_uri (ClutterGstPlayer *player, - const gchar *uri) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstPlayFlags flags; - - if (!priv->pipeline) - return; - - CLUTTER_GST_NOTE (MEDIA, "setting subtitle URI: %s", uri); - - g_object_get (priv->pipeline, "flags", &flags, NULL); - - g_object_set (priv->pipeline, "suburi", uri, NULL); - - g_object_set (priv->pipeline, "flags", flags, NULL); -} - -static void -player_configure_buffering_timeout (ClutterGstPlayer *player, - guint ms) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (priv->buffering_timeout_id) - { - g_source_remove (priv->buffering_timeout_id); - priv->buffering_timeout_id = 0; - } - - if (ms) - { - priv->buffering_timeout_id = - g_timeout_add (ms, player_buffering_timeout, player); - } -} - -static void -player_clear_download_buffering (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (priv->download_buffering_element) - { - g_object_unref (priv->download_buffering_element); - priv->download_buffering_element = NULL; - } - player_configure_buffering_timeout (player, 0); - priv->in_download_buffering = FALSE; - priv->virtual_stream_buffer_signalled = 0; -} - -static gboolean -is_live_pipeline (GstElement *pipeline) -{ - GstState state, pending; - GstStateChangeReturn state_change_res; - gboolean is_live = FALSE; - - /* get pipeline current state, we need to change the pipeline state to PAUSED to - * see if we are dealing with a live source and we want to restore the pipeline - * state afterwards */ - gst_element_get_state (pipeline, &state, &pending, 0); - - /* a pipeline with live source should return NO_PREROLL in PAUSE */ - state_change_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); - is_live = (state_change_res == GST_STATE_CHANGE_NO_PREROLL); - - /* restore pipeline previous state */ - if (pending == GST_STATE_VOID_PENDING) - gst_element_set_state (pipeline, state); - else - gst_element_set_state (pipeline, pending); - - return is_live; -} - -static void -set_uri (ClutterGstPlayer *player, - const gchar *uri) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GObject *self = G_OBJECT (player); - GstState state, pending; - - CLUTTER_GST_NOTE (MEDIA, "setting uri %s", uri); - - if (!priv->pipeline) - return; - - g_free (priv->uri); - - priv->in_eos = FALSE; - priv->in_error = FALSE; - - if (uri) - { - priv->uri = g_strdup (uri); - - /* Ensure the tick timeout is installed. - * - * We also have it installed in PAUSED state, because - * seeks etc may have a delayed effect on the position. - */ - if (priv->tick_timeout_id == 0) - { - priv->tick_timeout_id = - g_timeout_add (TICK_TIMEOUT, tick_timeout, self); - } - - /* try to load subtitles based on the uri of the file */ - set_subtitle_uri (player, NULL); - - /* reset the states of download buffering */ - player_clear_download_buffering (player); - } - else - { - priv->uri = NULL; - - if (priv->tick_timeout_id) - { - g_source_remove (priv->tick_timeout_id); - priv->tick_timeout_id = 0; - } - - if (priv->buffering_timeout_id) - { - g_source_remove (priv->buffering_timeout_id); - priv->buffering_timeout_id = 0; - } - - if (priv->download_buffering_element) - { - g_object_unref (priv->download_buffering_element); - priv->download_buffering_element = NULL; - } - - } - - priv->can_seek = FALSE; - priv->duration = 0.0; - priv->stacked_progress = 0.0; - priv->target_progress = 0.0; - - CLUTTER_GST_NOTE (MEDIA, "setting URI: %s", uri); - - if (uri) - { - gst_element_get_state (priv->pipeline, &state, &pending, 0); - if (pending) - state = pending; - - gst_element_set_state (priv->pipeline, GST_STATE_NULL); - - g_object_set (priv->pipeline, "uri", uri, NULL); - - priv->is_live = is_live_pipeline (priv->pipeline); - - set_subtitle_uri (player, NULL); - autoload_subtitle (player, uri); - - gst_element_set_state (priv->pipeline, state); - - priv->is_changing_uri = TRUE; - } - else - { - priv->is_idle = TRUE; - priv->is_live = FALSE; - set_subtitle_uri (player, NULL); - gst_element_set_state (priv->pipeline, GST_STATE_NULL); - g_object_notify (G_OBJECT (player), "idle"); - } - - /* - * Emit notifications for all these to make sure UI is not showing - * any properties of the old URI. - */ - g_object_notify (self, "uri"); - g_object_notify (self, "can-seek"); - g_object_notify (self, "duration"); - g_object_notify (self, "progress"); - - free_tags_list (&priv->audio_streams); - CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed"); - g_object_notify (self, "audio-streams"); - - free_tags_list (&priv->subtitle_tracks); - CLUTTER_GST_NOTE (SUBTITLES, "subtitle-tracks changed"); - g_object_notify (self, "subtitle-tracks"); -} - -static void -set_in_seek (ClutterGstPlayer *player, - gboolean seeking) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - priv->in_seek = seeking; - g_object_notify (G_OBJECT (player), "in-seek"); -} - - -static void -set_playing (ClutterGstPlayer *player, - gboolean playing) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (!priv->pipeline) - return; - - CLUTTER_GST_NOTE (MEDIA, "set playing: %d", playing); - - priv->in_error = FALSE; - priv->in_eos = FALSE; - - priv->target_state = playing ? GST_STATE_PLAYING : GST_STATE_PAUSED; - - if (priv->uri) - { - set_in_seek (player, FALSE); - - gst_element_set_state (priv->pipeline, priv->target_state); - } - else - { - if (playing) - g_warning ("Unable to start playing: no URI is set"); - } - - g_object_notify (G_OBJECT (player), "playing"); - g_object_notify (G_OBJECT (player), "progress"); -} - -static gboolean -get_playing (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstState state, pending; - gboolean playing; - - if (!priv->pipeline) - return FALSE; - - gst_element_get_state (priv->pipeline, &state, &pending, 0); - - if (pending) - playing = (pending == GST_STATE_PLAYING); - else - playing = (state == GST_STATE_PLAYING); - - CLUTTER_GST_NOTE (MEDIA, "get playing: %d", playing); - - return playing; -} - -static void -set_progress (ClutterGstPlayer *player, - gdouble progress) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstQuery *duration_q; - gint64 position; - - if (!priv->pipeline) - return; - - CLUTTER_GST_NOTE (MEDIA, "set progress: %.02f", progress); - - priv->in_eos = FALSE; - priv->target_progress = progress; - - if (priv->in_download_buffering) - { - /* we clear the virtual_stream_buffer_signalled flag as it's likely we - * need to buffer again */ - priv->virtual_stream_buffer_signalled = 0; - } - - if (priv->in_seek || priv->is_idle || priv->is_changing_uri) - { - /* We can't seek right now, let's save the position where we - want to seek and do that later. */ - CLUTTER_GST_NOTE (MEDIA, - "already seeking/idleing. stacking progress point."); - priv->stacked_progress = progress; - return; - } - - duration_q = gst_query_new_duration (GST_FORMAT_TIME); - - if (gst_element_query (priv->pipeline, duration_q)) - { - gint64 duration = 0; - - gst_query_parse_duration (duration_q, NULL, &duration); - - position = progress * duration; - } - else - position = 0; - - gst_query_unref (duration_q); - - gst_element_seek (priv->pipeline, - 1.0, - GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH | priv->seek_flags, - GST_SEEK_TYPE_SET, - position, - GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); - - set_in_seek (player, TRUE); - - priv->stacked_progress = 0.0; - - CLUTTER_GST_NOTE (MEDIA, "set progress (seeked): %.02f", progress); -} - -static gdouble -get_progress (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstQuery *position_q, *duration_q; - gdouble progress; - - if (!priv->pipeline) - return 0.0; - - /* when hitting an error or after an EOS, playbin has some weird values when - * querying the duration and progress. We default to 0.0 on error and 1.0 on - * EOS */ - if (priv->in_error) - { - CLUTTER_GST_NOTE (MEDIA, "get progress (error): 0.0"); - return 0.0; - } - - if (priv->in_eos) - { - CLUTTER_GST_NOTE (MEDIA, "get progress (eos): 1.0"); - return 1.0; - } - - /* When seeking, the progress returned by playbin is 0.0. We want that to be - * the last known position instead as returning 0.0 will have some ugly - * effects, say on a progress bar getting updated from the progress tick. */ - if (priv->in_seek || priv->is_changing_uri) - { - CLUTTER_GST_NOTE (MEDIA, "get progress (target): %.02f", - priv->target_progress); - return priv->target_progress; - } - - position_q = gst_query_new_position (GST_FORMAT_TIME); - duration_q = gst_query_new_duration (GST_FORMAT_TIME); - - if (gst_element_query (priv->pipeline, position_q) && - gst_element_query (priv->pipeline, duration_q)) - { - gint64 position, duration; - - position = duration = 0; - - gst_query_parse_position (position_q, NULL, &position); - gst_query_parse_duration (duration_q, NULL, &duration); - - progress = CLAMP ((gdouble) position / (gdouble) duration, 0.0, 1.0); - } - else - progress = 0.0; - - gst_query_unref (position_q); - gst_query_unref (duration_q); - - CLUTTER_GST_NOTE (MEDIA, "get progress (pipeline): %.02f", progress); - - return progress; -} - -static void -set_subtitle_font_name (ClutterGstPlayer *player, - const gchar *font_name) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (!priv->pipeline) - return; - - CLUTTER_GST_NOTE (MEDIA, "setting subtitle font to %s", font_name); - - g_free (priv->font_name); - priv->font_name = g_strdup (font_name); - g_object_set (priv->pipeline, "subtitle-font-desc", font_name, NULL); -} - -static void -set_audio_volume (ClutterGstPlayer *player, - gdouble volume) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (!priv->pipeline) - return; - - CLUTTER_GST_NOTE (MEDIA, "set volume: %.02f", volume); - - volume = CLAMP (volume, 0.0, 1.0); - gst_stream_volume_set_volume (GST_STREAM_VOLUME (priv->pipeline), - GST_STREAM_VOLUME_FORMAT_CUBIC, - volume); - g_object_notify (G_OBJECT (player), "audio-volume"); -} - -static gdouble -get_audio_volume (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (!priv->pipeline) - return 0.0; - - CLUTTER_GST_NOTE (MEDIA, "get volume: %.02f", priv->volume); - - return priv->volume; -} - -static gboolean -player_buffering_timeout (gpointer data) -{ - ClutterGstPlayer *player = (ClutterGstPlayer *) data; - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - gdouble start_d, stop_d, seconds_buffered; - gint64 start, stop, left; - gint buffer_percent; - GstState current_state; - GstElement *element; - GstQuery *query; - gboolean res; - - element = priv->download_buffering_element; - if (element == NULL) - element = priv->pipeline; - - /* queue2 only knows about _PERCENT and _BYTES */ - query = gst_query_new_buffering (GST_FORMAT_PERCENT); - res = gst_element_query (element, query); - - if (res == FALSE) - { - priv->buffering_timeout_id = 0; - player_clear_download_buffering (player); - return FALSE; - } - - /* signal the current range */ - - gst_query_parse_buffering_stats (query, NULL, NULL, NULL, &left); - gst_query_parse_buffering_range (query, NULL, &start, &stop, NULL); - - CLUTTER_GST_NOTE (BUFFERING, - "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT - ", buffering left %" G_GINT64_FORMAT, start, stop, left); - - start_d = (gdouble) start / GST_FORMAT_PERCENT_MAX; - stop_d = (gdouble) stop / GST_FORMAT_PERCENT_MAX; - - g_signal_emit (player, signals[DOWNLOAD_BUFFERING_SIGNAL], 0, start_d, stop_d); - - /* handle the "virtual stream buffer" and the associated pipeline state. - * We pause the pipeline until the content is buffered. - */ - seconds_buffered = priv->duration * (stop_d - start_d); - gst_query_parse_buffering_percent (query, NULL, &buffer_percent); - priv->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0); - - if (priv->buffer_fill != 1.0 || !priv->virtual_stream_buffer_signalled) - { - CLUTTER_GST_NOTE (BUFFERING, "buffer holds %0.2fs of data, buffer-fill " - "is %.02f", seconds_buffered, priv->buffer_fill); - - g_object_notify (G_OBJECT (player), "buffer-fill"); - - if (priv->buffer_fill == 1.0) - priv->virtual_stream_buffer_signalled = 1; - } - - gst_element_get_state (priv->pipeline, ¤t_state, NULL, 0); - if (priv->buffer_fill < 1.0) - { - if (current_state != GST_STATE_PAUSED) - { - CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline"); - gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); - } - } - else - { - if (current_state != priv->target_state) - { - CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline"); - gst_element_set_state (priv->pipeline, priv->target_state); - } - } - - /* the file has finished downloading */ - if (left == G_GINT64_CONSTANT (0)) - { - priv->buffering_timeout_id = 0; - - player_clear_download_buffering (player); - gst_query_unref (query); - return FALSE; - } - - gst_query_unref (query); - return TRUE; -} - -static void -bus_message_error_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GError *error = NULL; - - gst_element_set_state (priv->pipeline, GST_STATE_NULL); - - gst_message_parse_error (message, &error, NULL); - g_signal_emit (player, signals[ERROR_SIGNAL], 0, error); - g_error_free (error); - - priv->is_idle = TRUE; - g_object_notify (G_OBJECT (player), "idle"); -} - -/* - * This is what's intented in the EOS callback: - * - receive EOS from playbin - * - fire the EOS signal, the user can install a signal handler to loop the - * video for instance. - * - after having emitted the signal, check the state of the pipeline - * - if the pipeline has been set back to playing or pause, don't touch the - * idle state. This will avoid drawing a frame (or more) with the idle - * material when looping - */ -static void -bus_message_eos_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstState state, pending; - - priv->in_eos = TRUE; - - gst_element_set_state (priv->pipeline, GST_STATE_READY); - - g_signal_emit (player, signals[EOS_SIGNAL], 0); - g_object_notify (G_OBJECT (player), "progress"); - - gst_element_get_state (priv->pipeline, &state, &pending, 0); - if (pending) - state = pending; - - if (!(state == GST_STATE_PLAYING || state == GST_STATE_PAUSED)) - { - priv->is_idle = TRUE; - g_object_notify (G_OBJECT (player), "idle"); - } -} - -static void -bus_message_buffering_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstBufferingMode mode; - GstState current_state; - gint buffer_percent; - - gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL); - - if (mode != GST_BUFFERING_DOWNLOAD) - priv->in_download_buffering = FALSE; - - switch (mode) - { - case GST_BUFFERING_LIVE: - case GST_BUFFERING_STREAM: - gst_message_parse_buffering (message, &buffer_percent); - priv->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0); - - CLUTTER_GST_NOTE (BUFFERING, "buffer-fill: %.02f", priv->buffer_fill); - - /* no state management needed for live pipelines */ - if (!priv->is_live) - { - /* The playbin documentation says that we need to pause the pipeline - * when there's not enough data yet. We try to limit the calls to - * gst_element_set_state() */ - gst_element_get_state (priv->pipeline, ¤t_state, NULL, 0); - - if (priv->buffer_fill < 1.0) - { - if (current_state != GST_STATE_PAUSED) - { - CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline"); - gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); - } - } - else - { - if (current_state != priv->target_state) - { - CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline"); - gst_element_set_state (priv->pipeline, priv->target_state); - } - } - } - - g_object_notify (G_OBJECT (player), "buffer-fill"); - break; - - case GST_BUFFERING_DOWNLOAD: - /* we rate limit the messages from GStreamer for a usage in a UI (we - * don't want *that* many updates). This is done by installing an idle - * handler querying the buffer range and sending a signal from there */ - - if (priv->in_download_buffering) - break; - - /* install the querying idle handler the first time we receive a download - * buffering message */ - player_configure_buffering_timeout (player, BUFFERING_TIMEOUT); - - /* pause the stream. the idle timeout will set the target state when - * having received enough data. We'll use buffer_fill as a "virtual - * stream buffer" to signal the application we're buffering until we - * can play back from the downloaded stream. */ - gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); - priv->buffer_fill = 0.0; - g_object_notify (G_OBJECT (player), "buffer-fill"); - - priv->download_buffering_element = g_object_ref (message->src); - priv->in_download_buffering = TRUE; - priv->virtual_stream_buffer_signalled = 0; - break; - - case GST_BUFFERING_TIMESHIFT: - default: - g_warning ("Buffering mode %d not handled", mode); - break; - } -} - -static void -on_source_changed (GstElement *pipeline, - GParamSpec *pspec, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - player_set_user_agent (player, priv->user_agent); -} - -static void -query_duration (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - gboolean success; - gint64 duration; - gdouble new_duration, difference; - - success = gst_element_query_duration (priv->pipeline, - GST_FORMAT_TIME, - &duration); - if (G_UNLIKELY (success != TRUE)) - return; - - new_duration = (gdouble) duration / GST_SECOND; - - /* while we store the new duration if it sligthly changes, the duration - * signal is sent only if the new duration is at least one second different - * from the old one (as the duration signal is mainly used to update the - * time displayed in a UI */ - difference = ABS (priv->duration - new_duration); - if (difference > 1e-3) - { - CLUTTER_GST_NOTE (MEDIA, "duration: %.02f", new_duration); - priv->duration = new_duration; - - if (difference > 1.0) - g_object_notify (G_OBJECT (player), "duration"); - } -} - -static void -bus_message_duration_changed_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - /* GstElements send a duration-changed message on the bus to signal - * that the duration has changed and should be re-queried */ - query_duration (player); -} - -static void -bus_message_state_change_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - GstState old_state, new_state; - gpointer src; - - src = GST_MESSAGE_SRC (message); - if (src != priv->pipeline) - return; - - gst_message_parse_state_changed (message, &old_state, &new_state, NULL); - - CLUTTER_GST_NOTE (MEDIA, "state change: %s -> %s", - gst_state_to_string (old_state), - gst_state_to_string (new_state)); - - if (old_state == new_state) - return; - - if (old_state == GST_STATE_READY && - new_state == GST_STATE_PAUSED) - { - GstQuery *query; - - /* Determine whether we can seek */ - query = gst_query_new_seeking (GST_FORMAT_TIME); - - if (gst_element_query (priv->pipeline, query)) - { - gboolean can_seek = FALSE; - - gst_query_parse_seeking (query, NULL, &can_seek, - NULL, - NULL); - - priv->can_seek = (can_seek == TRUE) ? TRUE : FALSE; - } - else - { - /* could not query for ability to seek by querying the - * pipeline; let's crudely try by using the URI - */ - if (priv->uri && g_str_has_prefix (priv->uri, "http://")) - priv->can_seek = FALSE; - else - priv->can_seek = TRUE; - } - - gst_query_unref (query); - - CLUTTER_GST_NOTE (MEDIA, "can-seek: %d", priv->can_seek); - - g_object_notify (G_OBJECT (player), "can-seek"); - - query_duration (player); - } - - /* is_idle controls the drawing with the idle material */ - if (new_state == GST_STATE_NULL) - { - priv->is_idle = TRUE; - g_object_notify (G_OBJECT (player), "idle"); - } - else if (new_state == GST_STATE_PLAYING) - { - priv->is_idle = FALSE; - priv->is_changing_uri = FALSE; - g_object_notify (G_OBJECT (player), "idle"); - } - - if (!priv->is_idle) - { - if (priv->stacked_progress) - { - set_progress (player, priv->stacked_progress); - } - } -} - -static void -bus_message_async_done_cb (GstBus *bus, - GstMessage *message, - ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - if (priv->in_seek) - { - g_object_notify (G_OBJECT (player), "progress"); - - set_in_seek (player, FALSE); - - if (priv->stacked_progress) - { - set_progress (player, priv->stacked_progress); - } - } -} - -static gboolean -on_volume_changed_main_context (gpointer data) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (data); - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - gdouble volume; - - volume = - gst_stream_volume_get_volume (GST_STREAM_VOLUME (priv->pipeline), - GST_STREAM_VOLUME_FORMAT_CUBIC); - priv->volume = volume; - - g_object_notify (G_OBJECT (player), "audio-volume"); - - g_object_unref (player); - - return FALSE; -} - -/* playbin proxies the volume property change notification directly from - * the element having the "volume" property. This means this callback is - * called from the thread that runs the element, potentially different from - * the main thread */ -static void -on_volume_changed (GstElement *pipeline, - GParamSpec *pspec, - ClutterGstPlayer *player) -{ - g_idle_add (on_volume_changed_main_context, g_object_ref (player)); -} - -static GList * -get_tags (GstElement *pipeline, - const gchar *property_name, - const gchar *action_signal) -{ - GList *ret = NULL; - gint i, n; - - g_object_get (G_OBJECT (pipeline), property_name, &n, NULL); - if (n == 0) - return NULL; - - for (i = 0; i < n; i++) - { - GstTagList *tags = NULL; - - g_signal_emit_by_name (G_OBJECT (pipeline), action_signal, i, &tags); - - ret = g_list_prepend (ret, tags); - } - - return g_list_reverse (ret); -} - -static gboolean -on_audio_changed_main_context (gpointer data) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (data); - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - free_tags_list (&priv->audio_streams); - priv->audio_streams = get_tags (priv->pipeline, "n-audio", "get-audio-tags"); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed"); - - g_object_notify (G_OBJECT (player), "audio-streams"); - - g_object_unref (player); - - return FALSE; -} - -/* same explanation as for notify::volume's usage of g_idle_add() */ -static void -on_audio_changed (GstElement *pipeline, - ClutterGstPlayer *player) -{ - g_idle_add (on_audio_changed_main_context, g_object_ref (player)); -} - -static void -on_audio_tags_changed (GstElement *pipeline, - gint stream, - ClutterGstPlayer *player) -{ - gint current_stream; - - g_object_get (G_OBJECT (pipeline), "current-audio", ¤t_stream, NULL); - - if (current_stream != stream) - return; - - g_idle_add (on_audio_changed_main_context, g_object_ref (player)); -} - -static gboolean -on_current_audio_changed_main_context (gpointer data) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (data); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream changed"); - g_object_notify (G_OBJECT (player), "audio-stream"); - - g_object_unref (player); - - return FALSE; -} - -static void -on_current_audio_changed (GstElement *pipeline, - GParamSpec *pspec, - ClutterGstPlayer *player) -{ - g_idle_add (on_current_audio_changed_main_context, g_object_ref (player)); -} - -static gboolean -on_text_changed_main_context (gpointer data) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (data); - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - - free_tags_list (&priv->subtitle_tracks); - priv->subtitle_tracks = get_tags (priv->pipeline, "n-text", "get-text-tags"); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "subtitle-tracks changed"); - - g_object_notify (G_OBJECT (player), "subtitle-tracks"); - - g_object_unref (player); - - return FALSE; -} - -/* same explanation as for notify::volume's usage of g_idle_add() */ -static void -on_text_changed (GstElement *pipeline, - ClutterGstPlayer *player) -{ - g_idle_add (on_text_changed_main_context, g_object_ref (player)); -} - -static void -on_text_tags_changed (GstElement *pipeline, - gint stream, - ClutterGstPlayer *player) -{ - g_idle_add (on_text_changed_main_context, g_object_ref (player)); -} - -static gboolean -on_current_text_changed_main_context (gpointer data) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (data); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "text stream changed"); - g_object_notify (G_OBJECT (player), "subtitle-track"); - - g_object_unref (player); - - return FALSE; -} - -static void -on_current_text_changed (GstElement *pipeline, - GParamSpec *pspec, - ClutterGstPlayer *player) -{ - g_idle_add (on_current_text_changed_main_context, g_object_ref (player)); -} - -/* GObject's magic/madness */ - -static void -clutter_gst_player_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (object); - ClutterGstPlayerIfacePrivate *iface_priv; - - switch (property_id) - { - case PROP_URI: - set_uri (player, g_value_get_string (value)); - break; - - case PROP_PLAYING: - set_playing (player, g_value_get_boolean (value)); - break; - - case PROP_PROGRESS: - set_progress (player, g_value_get_double (value)); - break; - - case PROP_SUBTITLE_URI: - set_subtitle_uri (player, g_value_get_string (value)); - break; - - case PROP_SUBTITLE_FONT_NAME: - set_subtitle_font_name (player, g_value_get_string (value)); - break; - - case PROP_AUDIO_VOLUME: - set_audio_volume (player, g_value_get_double (value)); - break; - - case PROP_USER_AGENT: - clutter_gst_player_set_user_agent (player, - g_value_get_string (value)); - break; - - case PROP_SEEK_FLAGS: - clutter_gst_player_set_seek_flags (player, - g_value_get_flags (value)); - break; - - case PROP_AUDIO_STREAM: - clutter_gst_player_set_audio_stream (player, - g_value_get_int (value)); - break; - - case PROP_SUBTITLE_TRACK: - clutter_gst_player_set_subtitle_track (player, - g_value_get_int (value)); - break; - - default: - iface_priv = PLAYER_GET_CLASS_PRIVATE (object); - iface_priv->set_property (object, property_id, value, pspec); - } -} - - -static void -clutter_gst_player_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterGstPlayer *player = CLUTTER_GST_PLAYER (object); - ClutterGstPlayerPrivate *priv = PLAYER_GET_PRIVATE (player); - ClutterGstPlayerIfacePrivate *iface_priv; - gchar *str; - - switch (property_id) - { - case PROP_URI: - g_value_set_string (value, priv->uri); - break; - - case PROP_PLAYING: - g_value_set_boolean (value, get_playing (player)); - break; - - case PROP_PROGRESS: - g_value_set_double (value, get_progress (player)); - break; - - case PROP_SUBTITLE_URI: - g_object_get (priv->pipeline, "suburi", &str, NULL); - g_value_take_string (value, str); - break; - - case PROP_SUBTITLE_FONT_NAME: - g_value_set_string (value, priv->font_name); - break; - - case PROP_AUDIO_VOLUME: - g_value_set_double (value, get_audio_volume (player)); - break; - - case PROP_CAN_SEEK: - g_value_set_boolean (value, priv->can_seek); - break; - - case PROP_BUFFER_FILL: - g_value_set_double (value, priv->buffer_fill); - break; - - case PROP_DURATION: - g_value_set_double (value, priv->duration); - break; - - case PROP_IDLE: - g_value_set_boolean (value, priv->is_idle); - break; - - case PROP_USER_AGENT: - { - gchar *user_agent; - - user_agent = clutter_gst_player_get_user_agent (player); - g_value_take_string (value, user_agent); - } - break; - - case PROP_SEEK_FLAGS: - { - ClutterGstSeekFlags seek_flags; - - seek_flags = clutter_gst_player_get_seek_flags (player); - g_value_set_flags (value, seek_flags); - } - break; - - case PROP_AUDIO_STREAMS: - g_value_set_pointer (value, priv->audio_streams); - break; - - case PROP_AUDIO_STREAM: - { - gint index_; - - index_ = clutter_gst_player_get_audio_stream (player); - g_value_set_int (value, index_); - } - break; - - case PROP_SUBTITLE_TRACKS: - g_value_set_pointer (value, priv->subtitle_tracks); - break; - - case PROP_SUBTITLE_TRACK: - { - gint index_; - - index_ = clutter_gst_player_get_subtitle_track (player); - g_value_set_int (value, index_); - } - break; - - case PROP_IN_SEEK: - g_value_set_boolean (value, priv->in_seek); - break; - - default: - iface_priv = PLAYER_GET_CLASS_PRIVATE (object); - iface_priv->get_property (object, property_id, value, pspec); - } -} - -/** - * clutter_gst_player_class_init: - * @object_class: a #GObjectClass - * - * Adds the #ClutterGstPlayer properties to a class and surchages the - * set/get_property of #GObjectClass. You should call this - * function at the end of the class_init method of the class - * implementing #ClutterGstPlayer. - * - * Since: 1.4 - */ -void -clutter_gst_player_class_init (GObjectClass *object_class) -{ - ClutterGstPlayerIfacePrivate *priv; - - priv = g_new0 (ClutterGstPlayerIfacePrivate, 1); - g_type_set_qdata (G_OBJECT_CLASS_TYPE (object_class), - clutter_gst_player_class_quark, - priv); - - /* Save object's methods we want to override */ - priv->set_property = object_class->set_property; - priv->get_property = object_class->get_property; - - /* Replace by our methods */ - object_class->set_property = clutter_gst_player_set_property; - object_class->get_property = clutter_gst_player_get_property; - - /* Override ClutterGstPlayer's properties */ - g_object_class_override_property (object_class, - PROP_URI, "uri"); - g_object_class_override_property (object_class, - PROP_PLAYING, "playing"); - g_object_class_override_property (object_class, - PROP_PROGRESS, "progress"); - g_object_class_override_property (object_class, - PROP_SUBTITLE_URI, "subtitle-uri"); - g_object_class_override_property (object_class, - PROP_SUBTITLE_FONT_NAME, - "subtitle-font-name"); - g_object_class_override_property (object_class, - PROP_AUDIO_VOLUME, "audio-volume"); - g_object_class_override_property (object_class, - PROP_CAN_SEEK, "can-seek"); - g_object_class_override_property (object_class, - PROP_DURATION, "duration"); - g_object_class_override_property (object_class, - PROP_BUFFER_FILL, "buffer-fill"); - - g_object_class_override_property (object_class, - PROP_IDLE, "idle"); - g_object_class_override_property (object_class, - PROP_USER_AGENT, "user-agent"); - g_object_class_override_property (object_class, - PROP_SEEK_FLAGS, "seek-flags"); - - g_object_class_override_property (object_class, - PROP_AUDIO_STREAMS, "audio-streams"); - g_object_class_override_property (object_class, - PROP_AUDIO_STREAM, "audio-stream"); - - g_object_class_override_property (object_class, - PROP_SUBTITLE_TRACKS, "subtitle-tracks"); - g_object_class_override_property (object_class, - PROP_SUBTITLE_TRACK, "subtitle-track"); - g_object_class_override_property (object_class, - PROP_IN_SEEK, "in-seek"); -} - -static GstElement * -get_pipeline (void) -{ - GstElement *pipeline, *audio_sink; - - pipeline = gst_element_factory_make ("playbin", "pipeline"); - if (!pipeline) - { - g_critical ("Unable to create playbin element"); - return NULL; - } - - audio_sink = gst_element_factory_make ("gconfaudiosink", "audio-sink"); - if (!audio_sink) - { - audio_sink = gst_element_factory_make ("autoaudiosink", "audio-sink"); - if (!audio_sink) - { - audio_sink = gst_element_factory_make ("alsasink", "audio-sink"); - g_warning ("Could not create a GST audio_sink. " - "Audio unavailable."); - - /* do we even need to bother? */ - if (!audio_sink) - audio_sink = gst_element_factory_make ("fakesink", "audio-sink"); - } - } - - g_object_set (G_OBJECT (pipeline), - "audio-sink", audio_sink, - "subtitle-font-desc", "Sans 16", - NULL); - - return pipeline; -} - -/* ClutterGstPlayerIface implementation */ - -static GstElement * -clutter_gst_player_get_pipeline_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - return priv->pipeline; -} - -static gchar * -clutter_gst_player_get_user_agent_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - GstElement *source; - GParamSpec *pspec; - gchar *user_agent; - - priv = PLAYER_GET_PRIVATE (player); - - /* If the user has set a custom user agent, we just return it even if it is - * not used by the current source element of the pipeline */ - if (priv->user_agent) - return g_strdup (priv->user_agent); - - /* If not, we try to retrieve the user agent used by the current source */ - g_object_get (priv->pipeline, "source", &source, NULL); - if (source == NULL) - return NULL; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), - "user-agent"); - if (pspec == NULL) - return NULL; - - g_object_get (source, "user-agent", &user_agent, NULL); - - return user_agent; -} - -static void -clutter_gst_player_set_user_agent_impl (ClutterGstPlayer *player, - const gchar *user_agent) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - g_free (priv->user_agent); - if (user_agent) - priv->user_agent = g_strdup (user_agent); - else - priv->user_agent = NULL; - - player_set_user_agent (player, user_agent); -} - -static ClutterGstSeekFlags -clutter_gst_player_get_seek_flags_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - if (priv->seek_flags == GST_SEEK_FLAG_ACCURATE) - return CLUTTER_GST_SEEK_FLAG_ACCURATE; - else - return CLUTTER_GST_SEEK_FLAG_NONE; -} - -static void -clutter_gst_player_set_seek_flags_impl (ClutterGstPlayer *player, - ClutterGstSeekFlags flags) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - if (flags == CLUTTER_GST_SEEK_FLAG_NONE) - priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT; - else if (flags & CLUTTER_GST_SEEK_FLAG_ACCURATE) - priv->seek_flags = GST_SEEK_FLAG_ACCURATE; -} - -static ClutterGstBufferingMode -clutter_gst_player_get_buffering_mode_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - GstPlayFlags flags; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL); - - if (flags & GST_PLAY_FLAG_DOWNLOAD) - return CLUTTER_GST_BUFFERING_MODE_DOWNLOAD; - - return CLUTTER_GST_BUFFERING_MODE_STREAM; -} - -static void -clutter_gst_player_set_buffering_mode_impl (ClutterGstPlayer *player, - ClutterGstBufferingMode mode) -{ - ClutterGstPlayerPrivate *priv; - GstPlayFlags flags; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL); - - switch (mode) - { - case CLUTTER_GST_BUFFERING_MODE_STREAM: - flags &= ~GST_PLAY_FLAG_DOWNLOAD; - break; - - case CLUTTER_GST_BUFFERING_MODE_DOWNLOAD: - flags |= GST_PLAY_FLAG_DOWNLOAD; - break; - - default: - g_warning ("Unexpected buffering mode %d", mode); - break; - } - - g_object_set (G_OBJECT (priv->pipeline), "flags", flags, NULL); -} - -static gint -clutter_gst_player_get_buffer_size_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - gint size; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), "buffer-size", &size, NULL); - - return size; -} - -static void -clutter_gst_player_set_buffer_size_impl (ClutterGstPlayer *player, - gint size) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_set (G_OBJECT (priv->pipeline), "buffer-size", size, NULL); -} - -static gint64 -clutter_gst_player_get_buffer_duration_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - gint64 duration; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), "buffer-duration", &duration, NULL); - - return duration; -} - -static void -clutter_gst_player_set_buffer_duration_impl (ClutterGstPlayer *player, - gint64 duration) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_set (G_OBJECT (priv->pipeline), "buffer-duration", duration, NULL); -} - -static GList * -clutter_gst_player_get_audio_streams_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - if (CLUTTER_GST_DEBUG_ENABLED (AUDIO_STREAM)) - { - gchar *streams; - - streams = list_to_string (priv->audio_streams); - CLUTTER_GST_NOTE (AUDIO_STREAM, "audio streams: %s", streams); - g_free (streams); - } - - return priv->audio_streams; -} - -static gint -clutter_gst_player_get_audio_stream_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - gint index_ = -1; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), - "current-audio", &index_, - NULL); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream is #%d", index_); - - return index_; -} - -static void -clutter_gst_player_set_audio_stream_impl (ClutterGstPlayer *player, - gint index_) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - g_return_if_fail (index_ >= 0 && - index_ < (gint) g_list_length (priv->audio_streams)); - - CLUTTER_GST_NOTE (AUDIO_STREAM, "set audio audio stream to #%d", index_); - - g_object_set (G_OBJECT (priv->pipeline), - "current-audio", index_, - NULL); -} - -static GList * -clutter_gst_player_get_subtitle_tracks_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - if (CLUTTER_GST_DEBUG_ENABLED (SUBTITLES)) - { - gchar *tracks; - - tracks = list_to_string (priv->subtitle_tracks); - CLUTTER_GST_NOTE (SUBTITLES, "subtitle tracks: %s", tracks); - g_free (tracks); - } - - return priv->subtitle_tracks; -} - -static gint -clutter_gst_player_get_subtitle_track_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - gint index_ = -1; - - priv = PLAYER_GET_PRIVATE (player); - - g_object_get (G_OBJECT (priv->pipeline), - "current-text", &index_, - NULL); - - CLUTTER_GST_NOTE (SUBTITLES, "text track is #%d", index_); - - return index_; -} - -static void -clutter_gst_player_set_subtitle_track_impl (ClutterGstPlayer *player, - gint index_) -{ - ClutterGstPlayerPrivate *priv; - GstPlayFlags flags; - - priv = PLAYER_GET_PRIVATE (player); - - g_return_if_fail (index_ >= -1 && - index_ < (gint) g_list_length (priv->subtitle_tracks)); - - CLUTTER_GST_NOTE (SUBTITLES, "set subtitle track to #%d", index_); - - g_object_get (priv->pipeline, "flags", &flags, NULL); - flags &= ~GST_PLAY_FLAG_TEXT; - g_object_set (priv->pipeline, "flags", flags, NULL); - - if (index_ >= 0) - { - g_object_set (G_OBJECT (priv->pipeline), - "current-text", index_, - NULL); - - flags |= GST_PLAY_FLAG_TEXT; - g_object_set (priv->pipeline, "flags", flags, NULL); - } -} - -static gboolean -clutter_gst_player_get_idle_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - return priv->is_idle; -} - -static gboolean -clutter_gst_player_get_in_seek_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - return priv->in_seek; -} - -static gboolean -clutter_gst_player_is_live_media_impl (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - priv = PLAYER_GET_PRIVATE (player); - - return priv->is_live; -} - -/**/ - -#if defined (CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT) -static GstBusSyncReply -on_sync_message (GstBus * bus, GstMessage * message, gpointer user_data) -{ - Display *display = user_data; - GstVideoContext *context; - const gchar **types; - - if (gst_video_context_message_parse_prepare (message, &types, &context)) { - gint i; - - for (i = 0; types[i]; i++) { - - if (!strcmp(types[i], "x11-display")) { - gst_video_context_set_context_pointer (context, "x11-display", display); - } - else if (!strcmp(types[i], "x11-display-name")) { - gst_video_context_set_context_string (context, "x11-display-name", - DisplayString (display)); - } else { - continue; - } - - gst_message_unref (message); - return GST_BUS_DROP; - } - } - - return GST_BUS_PASS; -} -#endif - -/** - * clutter_gst_player_init: - * @player: a #ClutterGstPlayer - * - * Initialize a #ClutterGstPlayer instance. You should call this - * function at the beginning of the init method of the class - * implementing #ClutterGstPlayer. - * - * When you're finished with the ClutterGstPlayer mixin features (usually in - * the dispose or finalize vfuncs), call clutter_gst_player_deinit() to - * desallocate the resources created by clutter_gst_player_init(). - * - * Return value: TRUE if the initialization was successfull, FALSE otherwise. - * - * Since: 1.4 - */ -gboolean -clutter_gst_player_init (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), FALSE); - - priv = PLAYER_GET_PRIVATE (player); - if (priv) - return TRUE; - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->get_pipeline = clutter_gst_player_get_pipeline_impl; - iface->get_user_agent = clutter_gst_player_get_user_agent_impl; - iface->set_user_agent = clutter_gst_player_set_user_agent_impl; - iface->get_seek_flags = clutter_gst_player_get_seek_flags_impl; - iface->set_seek_flags = clutter_gst_player_set_seek_flags_impl; - iface->get_buffering_mode = clutter_gst_player_get_buffering_mode_impl; - iface->set_buffering_mode = clutter_gst_player_set_buffering_mode_impl; - iface->get_buffer_size = clutter_gst_player_get_buffer_size_impl; - iface->set_buffer_size = clutter_gst_player_set_buffer_size_impl; - iface->get_buffer_duration = clutter_gst_player_get_buffer_duration_impl; - iface->set_buffer_duration = clutter_gst_player_set_buffer_duration_impl; - iface->get_audio_streams = clutter_gst_player_get_audio_streams_impl; - iface->get_audio_stream = clutter_gst_player_get_audio_stream_impl; - iface->set_audio_stream = clutter_gst_player_set_audio_stream_impl; - iface->get_subtitle_tracks = clutter_gst_player_get_subtitle_tracks_impl; - iface->get_subtitle_track = clutter_gst_player_get_subtitle_track_impl; - iface->set_subtitle_track = clutter_gst_player_set_subtitle_track_impl; - iface->get_idle = clutter_gst_player_get_idle_impl; - iface->get_in_seek = clutter_gst_player_get_in_seek_impl; - iface->is_live_media = clutter_gst_player_is_live_media_impl; - - priv = g_slice_new0 (ClutterGstPlayerPrivate); - PLAYER_SET_PRIVATE (player, priv); - - priv->is_idle = TRUE; - priv->in_seek = FALSE; - priv->is_changing_uri = FALSE; - priv->in_download_buffering = FALSE; - - priv->pipeline = get_pipeline (); - if (!priv->pipeline) - { - g_critical ("Unable to create pipeline"); - return FALSE; - } - - g_signal_connect (priv->pipeline, "notify::source", - G_CALLBACK (on_source_changed), player); - - /* We default to not playing until someone calls set_playing(TRUE) */ - priv->target_state = GST_STATE_PAUSED; - - /* Default to a fast seek, ie. same effect than set_seek_flags (NONE); */ - priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT; - - priv->bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline)); - - gst_bus_add_signal_watch (priv->bus); - - g_signal_connect_object (priv->bus, "message::error", - G_CALLBACK (bus_message_error_cb), - player, 0); - g_signal_connect_object (priv->bus, "message::eos", - G_CALLBACK (bus_message_eos_cb), - player, 0); - g_signal_connect_object (priv->bus, "message::buffering", - G_CALLBACK (bus_message_buffering_cb), - player, 0); - g_signal_connect_object (priv->bus, "message::duration-changed", - G_CALLBACK (bus_message_duration_changed_cb), - player, 0); - g_signal_connect_object (priv->bus, "message::state-changed", - G_CALLBACK (bus_message_state_change_cb), - player, 0); - g_signal_connect_object (priv->bus, "message::async-done", - G_CALLBACK (bus_message_async_done_cb), - player, 0); - - g_signal_connect (priv->pipeline, "notify::volume", - G_CALLBACK (on_volume_changed), - player); - - g_signal_connect (priv->pipeline, "audio-changed", - G_CALLBACK (on_audio_changed), - player); - g_signal_connect (priv->pipeline, "audio-tags-changed", - G_CALLBACK (on_audio_tags_changed), - player); - g_signal_connect (priv->pipeline, "notify::current-audio", - G_CALLBACK (on_current_audio_changed), - player); - - g_signal_connect (priv->pipeline, "text-changed", - G_CALLBACK (on_text_changed), - player); - g_signal_connect (priv->pipeline, "text-tags-changed", - G_CALLBACK (on_text_tags_changed), - player); - g_signal_connect (priv->pipeline, "notify::current-text", - G_CALLBACK (on_current_text_changed), - player); - -#if defined(CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT) - gst_bus_set_sync_handler (priv->bus, on_sync_message, - clutter_x11_get_default_display (), NULL); -#endif - - gst_object_unref (GST_OBJECT (priv->bus)); - - return TRUE; -} - -/** - * clutter_gst_player_deinit: - * @player: a #ClutterGstPlayer - * - * Frees the resources created by clutter_gst_player_init(). After - * clutter_gst_player_deinit() has been called, no other player method can be - * called on the instance. - * - * Since: 1.4 - */ -void -clutter_gst_player_deinit (ClutterGstPlayer *player) -{ - ClutterGstPlayerPrivate *priv; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - priv = PLAYER_GET_PRIVATE (player); - - if (priv == NULL) - return; - - PLAYER_SET_PRIVATE (player, NULL); - - if (priv->tick_timeout_id) - { - g_source_remove (priv->tick_timeout_id); - priv->tick_timeout_id = 0; - } - - if (priv->buffering_timeout_id) - { - g_source_remove (priv->buffering_timeout_id); - priv->buffering_timeout_id = 0; - } - - if (priv->download_buffering_element) - { - g_object_unref (priv->download_buffering_element); - priv->download_buffering_element = NULL; - } - - gst_element_set_state (priv->pipeline, GST_STATE_NULL); - - if (priv->bus) - { - gst_bus_remove_signal_watch (priv->bus); - priv->bus = NULL; - } - - if (priv->pipeline) - { - gst_object_unref (GST_OBJECT (priv->pipeline)); - priv->pipeline = NULL; - } - - g_free (priv->uri); - g_free (priv->font_name); - g_free (priv->user_agent); - free_tags_list (&priv->audio_streams); - free_tags_list (&priv->subtitle_tracks); - - g_slice_free (ClutterGstPlayerPrivate, priv); -} - static void clutter_gst_player_default_init (ClutterGstPlayerIface *iface) { GParamSpec *pspec; /** - * ClutterGstPlayer:uri: - * - * The location of a media file, expressed as a valid URI. - */ - pspec = g_param_spec_string ("uri", - "URI", - "URI of a media file", - NULL, - CLUTTER_GST_PARAM_READWRITE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** * ClutterGstPlayer:playing: * * Whether the #ClutterGstPlayer actor is playing. @@ -2265,48 +86,6 @@ clutter_gst_player_default_init (ClutterGstPlayerIface *iface) g_object_interface_install_property (iface, pspec); /** - * ClutterGstPlayer:progress: - * - * The current progress of the playback, as a normalized - * value between 0.0 and 1.0. - */ - pspec = g_param_spec_double ("progress", - "Progress", - "Current progress of the playback", - 0.0, 1.0, 0.0, - CLUTTER_GST_PARAM_READWRITE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:subtitle-uri: - * - * The location of a subtitle file, expressed as a valid URI. - */ - pspec = g_param_spec_string ("subtitle-uri", - "Subtitle URI", - "URI of a subtitle file", - NULL, - CLUTTER_GST_PARAM_READWRITE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:subtitle-font-name: - * - * The font used to display subtitles. The font description has to - * follow the same grammar as the one recognized by - * pango_font_description_from_string(). - */ - pspec = g_param_spec_string ("subtitle-font-name", - "Subtitle Font Name", - "The font used to display subtitles", - NULL, - CLUTTER_GST_PARAM_READWRITE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** * ClutterGstPlayer:audio-volume: * * The volume of the audio, as a normalized value between @@ -2321,45 +100,6 @@ clutter_gst_player_default_init (ClutterGstPlayerIface *iface) g_object_interface_install_property (iface, pspec); /** - * ClutterGstPlayer:can-seek: - * - * Whether the current stream is seekable. - */ - pspec = g_param_spec_boolean ("can-seek", - "Can Seek", - "Whether the current stream is seekable", - FALSE, - CLUTTER_GST_PARAM_READABLE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:buffer-fill: - * - * The fill level of the buffer for the current stream, - * as a value between 0.0 and 1.0. - */ - pspec = g_param_spec_double ("buffer-fill", - "Buffer Fill", - "The fill level of the buffer", - 0.0, 1.0, 0.0, - CLUTTER_GST_PARAM_READABLE | - G_PARAM_DEPRECATED); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:duration: - * - * The duration of the current stream, in seconds - */ - pspec = g_param_spec_double ("duration", - "Duration", - "The duration of the stream, in seconds", - 0, G_MAXDOUBLE, 0, - CLUTTER_GST_PARAM_READABLE); - g_object_interface_install_property (iface, pspec); - - /** * ClutterGstPlayer:idle: * * Whether the #ClutterGstPlayer is in idle mode. @@ -2373,96 +113,42 @@ clutter_gst_player_default_init (ClutterGstPlayerIface *iface) CLUTTER_GST_PARAM_READABLE); g_object_interface_install_property (iface, pspec); - /** - * ClutterGstPlayer:user-agent: - * - * The User Agent used by #ClutterGstPlayer with network protocols. - * - * Since: 1.4 - */ - pspec = g_param_spec_string ("user-agent", - "User Agent", - "User Agent used with network protocols", - NULL, - CLUTTER_GST_PARAM_READWRITE); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:seek-flags: - * - * Flags to use when seeking. - * - * Since: 1.4 - */ - pspec = g_param_spec_flags ("seek-flags", - "Seek Flags", - "Flags to use when seeking", - CLUTTER_GST_TYPE_SEEK_FLAGS, - CLUTTER_GST_SEEK_FLAG_NONE, - CLUTTER_GST_PARAM_READWRITE); - g_object_interface_install_property (iface, pspec); - - /** - * ClutterGstPlayer:audio-streams: - * - * List of audio streams available on the current media. - * - * Since: 1.4 - */ - pspec = g_param_spec_pointer ("audio-streams", - "Audio Streams", - "List of the audio streams of the media", - CLUTTER_GST_PARAM_READABLE); - g_object_interface_install_property (iface, pspec); + /* Signals */ /** - * ClutterGstPlayer:audio-stream: - * - * Index of the current audio stream. + * ClutterGstPlayer::new-frame: + * @player: the #ClutterGstPlayer instance that received the signal * - * Since: 1.4 + * The ::ready signal is emitted each time the gstreamer pipeline + * becomes ready. */ - pspec = g_param_spec_int ("audio-stream", - "Audio Stream", - "Index of the current audio stream", - -1, G_MAXINT, -1, - CLUTTER_GST_PARAM_READWRITE); - g_object_interface_install_property (iface, pspec); - - pspec = g_param_spec_pointer ("subtitle-tracks", - "Subtitles Tracks", - "List of the subtitles tracks of the media", - CLUTTER_GST_PARAM_READABLE); - g_object_interface_install_property (iface, pspec); - - pspec = g_param_spec_int ("subtitle-track", - "Subtitles Track", - "Index of the current subtitles track", - -1, G_MAXINT, -1, - CLUTTER_GST_PARAM_READWRITE); - g_object_interface_install_property (iface, pspec); - - + signals[NEW_FRAME] = + g_signal_new ("new-frame", + CLUTTER_GST_TYPE_PLAYER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGstPlayerIface, new_frame), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + CLUTTER_GST_TYPE_FRAME); /** - * ClutterGstPlayer:in-seek: - * - * Whether or not the stream is being seeked. + * ClutterGstPlayer::ready: + * @player: the #ClutterGstPlayer instance that received the signal * - * Since: 1.6 + * The ::ready signal is emitted each time the gstreamer pipeline + * becomes ready. */ - pspec = g_param_spec_boolean ("in-seek", - "In seek mode", - "If currently seeking", - FALSE, - CLUTTER_GST_PARAM_READABLE); - g_object_interface_install_property (iface, pspec); - - - /* Signals */ - + signals[READY_SIGNAL] = + g_signal_new ("ready", + CLUTTER_GST_TYPE_PLAYER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGstPlayerIface, ready), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); /** * ClutterGstPlayer::eos: - * @media: the #ClutterGstPlayer instance that received the signal + * @player: the #ClutterGstPlayer instance that received the signal * * The ::eos signal is emitted each time the media stream ends. */ @@ -2476,7 +162,7 @@ clutter_gst_player_default_init (ClutterGstPlayerIface *iface) G_TYPE_NONE, 0); /** * ClutterGstPlayer::error: - * @media: the #ClutterGstPlayer instance that received the signal + * @player: the #ClutterGstPlayer instance that received the signal * @error: the #GError * * The ::error signal is emitted each time an error occurred. @@ -2492,811 +178,184 @@ clutter_gst_player_default_init (ClutterGstPlayerIface *iface) G_TYPE_ERROR); /** - * ClutterGstPlayer::download-buffering: + * ClutterGstPlayer::size-change: * @player: the #ClutterGstPlayer instance that received the signal - * @start: start position of the buffering - * @stop: start position of the buffering - * - * The ::download-buffering signal is emitted each time their an - * update about the buffering of the current media. + * @width: new width of the frames + * @height: new height of the frames * - * Since: 1.4 + * The ::size-change signal is emitted each time the gstreamer pipeline + * becomes ready. */ - signals[DOWNLOAD_BUFFERING_SIGNAL] = - g_signal_new ("download-buffering", + signals[SIZE_CHANGE] = + g_signal_new ("size-change", CLUTTER_GST_TYPE_PLAYER, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterGstPlayerIface, - download_buffering), + G_STRUCT_OFFSET (ClutterGstPlayerIface, size_change), NULL, NULL, - _clutter_gst_marshal_VOID__DOUBLE_DOUBLE, - G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); - - /* Setup a quark for per instance private data */ - if (!clutter_gst_player_private_quark) - { - clutter_gst_player_private_quark = - g_quark_from_static_string ("clutter-gst-player-private-quark"); - clutter_gst_player_class_quark = - g_quark_from_static_string ("clutter-gst-player-class-quark"); - } + _clutter_gst_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, G_TYPE_INT); } /* ClutterGstIface */ /** - * clutter_gst_player_get_pipeline: - * @player: a #ClutterGstPlayer + * clutter_gst_player_get_frame: + * @self: a #ClutterGstPlayer * - * Retrieves the #GstPipeline used by the @player, for direct use with - * GStreamer API. + * Retrieves the #CoglHandle of the last frame produced by @self. * - * Return value: (transfer none): the #GstPipeline element used by the player + * Return value: (transfer none): the #CoglHandle of the last frame. * - * Since: 1.4 + * Since: 3.0 */ -GstElement * -clutter_gst_player_get_pipeline (ClutterGstPlayer *player) +ClutterGstFrame * +clutter_gst_player_get_frame (ClutterGstPlayer *self) { ClutterGstPlayerIface *iface; - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); + g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (self), COGL_INVALID_HANDLE); - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - return iface->get_pipeline (player); + return iface->get_frame (self); } /** - * clutter_gst_player_set_uri: - * @player: a #ClutterGstPlayer - * @uri: the URI of the media stream + * clutter_gst_player_get_pipeline: + * @self: a #ClutterGstPlayer * - * Sets the URI of @player to @uri. - */ -void -clutter_gst_player_set_uri (ClutterGstPlayer *player, - const gchar *uri) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "uri", uri, NULL); -} - -/** - * clutter_gst_player_get_uri: - * @player: a #ClutterGstPlayer + * Retrieves the #GstPipeline used by the @self, for direct use with + * GStreamer API. * - * Retrieves the URI from @player. + * Return value: (transfer none): the #GstPipeline element used by the player * - * Return value: the URI of the media stream. Use g_free() - * to free the returned string + * Since: 3.0 */ -gchar * -clutter_gst_player_get_uri (ClutterGstPlayer *player) +GstElement * +clutter_gst_player_get_pipeline (ClutterGstPlayer *self) { - gchar *retval = NULL; + ClutterGstPlayerIface *iface; - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); + g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (self), NULL); - g_object_get (G_OBJECT (player), "uri", &retval, NULL); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - return retval; + return iface->get_pipeline (self); } /** - * clutter_gst_player_set_filename: - * @player: a #ClutterGstPlayer - * @filename: A filename + * clutter_gst_player_get_playing: + * @self: A #ClutterGstPlayer object * - * Sets the source of @player using a file path. + * Retrieves the playing status of @self. + * + * Return value: %TRUE if playing, %FALSE if stopped. + * + * Since: 3.0 */ -void -clutter_gst_player_set_filename (ClutterGstPlayer *player, - const gchar *filename) +gboolean +clutter_gst_player_get_playing (ClutterGstPlayer *self) { - gchar *uri; - GError *uri_error = NULL; - - if (!g_path_is_absolute (filename)) - { - gchar *abs_path; - - abs_path = g_build_filename (g_get_current_dir (), filename, NULL); - uri = g_filename_to_uri (abs_path, NULL, &uri_error); - g_free (abs_path); - } - else - uri = g_filename_to_uri (filename, NULL, &uri_error); + ClutterGstPlayerIface *iface; - if (uri_error) - { - g_signal_emit (player, signals[ERROR_SIGNAL], 0, uri_error); - g_error_free (uri_error); - return; - } + g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (self), TRUE); - clutter_gst_player_set_uri (player, uri); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - g_free (uri); + return iface->get_playing (self); } /** * clutter_gst_player_set_playing: - * @player: a #ClutterGstPlayer + * @self: a #ClutterGstPlayer * @playing: %TRUE to start playing * - * Starts or stops playing of @player. + * Starts or stops playing of @self. * * The implementation might be asynchronous, so the way to know whether - * the actual playing state of the @player is to use the #GObject::notify + * the actual playing state of the @self is to use the #GObject::notify * signal on the #ClutterGstPlayer:playing property and then retrieve the - * current state with clutter_gst_player_is_playing(). ClutterGstVideoActor + * current state with clutter_gst_player_get_playing(). ClutterGstVideoActor * in clutter-gst is an example of such an asynchronous implementation. - */ -void -clutter_gst_player_set_playing (ClutterGstPlayer *player, - gboolean playing) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "playing", playing, NULL); -} - -/** - * clutter_gst_player_is_playing: - * @player: A #ClutterGstPlayer object - * - * Retrieves the playing status of @player. - * - * Return value: %TRUE if playing, %FALSE if stopped. - */ -gboolean -clutter_gst_player_is_playing (ClutterGstPlayer *player) -{ - gboolean is_playing = FALSE; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), FALSE); - - g_object_get (G_OBJECT (player), "playing", &is_playing, NULL); - - return is_playing; -} - -/** - * clutter_gst_player_get_user_agent: - * @player: a #ClutterGstPlayer - * - * Retrieves the user agent used when streaming. - * - * Return value: the user agent used. The returned string has to be freed with - * g_free() - * - * Since: 1.4 - */ -gchar * -clutter_gst_player_get_user_agent (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_user_agent (player); -} - -/** - * clutter_gst_player_set_user_agent: - * @player: a #ClutterGstPlayer - * @user_agent: the user agent - * - * Sets the user agent to use when streaming. - * - * When streaming content, you might want to set a custom user agent, eg. to - * promote your software, make it appear in statistics or because the server - * requires a special user agent you want to impersonate. - * - * Since: 1.4 - */ -void -clutter_gst_player_set_user_agent (ClutterGstPlayer *player, - const gchar *user_agent) -{ - ClutterGstPlayerIface *iface; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->set_user_agent (player, user_agent); -} - -/** - * clutter_gst_player_get_seek_flags: - * @player: a #ClutterGstPlayer * - * Get the current value of the seek-flags property. - * - * Return value: a combination of #ClutterGstSeekFlags - * - * Since: 1.4 - */ -ClutterGstSeekFlags -clutter_gst_player_get_seek_flags (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), - CLUTTER_GST_SEEK_FLAG_NONE); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_seek_flags (player); -} - -/** - * clutter_gst_player_set_seek_flags: - * @player: a #ClutterGstPlayer - * @flags: a combination of #ClutterGstSeekFlags - * - * Seeking can be done with several trade-offs. Clutter-gst defaults - * to %CLUTTER_GST_SEEK_FLAG_NONE. - * - * Since: 1.4 + * Since: 3.0 */ void -clutter_gst_player_set_seek_flags (ClutterGstPlayer *player, - ClutterGstSeekFlags flags) +clutter_gst_player_set_playing (ClutterGstPlayer *self, + gboolean playing) { ClutterGstPlayerIface *iface; - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); + g_return_if_fail (CLUTTER_GST_IS_PLAYER (self)); - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - iface->set_seek_flags (player, flags); + iface->set_playing (self, playing); } /** - * clutter_gst_player_get_buffering_mode: - * @player: a #ClutterGstPlayer + * clutter_gst_player_get_audio_volume: + * @self: a #ClutterGstPlayer * - * Return value: a #ClutterGstBufferingMode + * Retrieves the playback volume of @self. * - * Since: 1.4 - */ -ClutterGstBufferingMode -clutter_gst_player_get_buffering_mode (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), - CLUTTER_GST_BUFFERING_MODE_STREAM); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_buffering_mode (player); -} - -/** - * clutter_gst_player_set_buffering_mode: - * @player: a #ClutterGstPlayer - * @mode: a #ClutterGstBufferingMode - * - * Since: 1.4 - */ -void -clutter_gst_player_set_buffering_mode (ClutterGstPlayer *player, - ClutterGstBufferingMode mode) -{ - ClutterGstPlayerIface *iface; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->set_buffering_mode (player, mode); -} - -/** - * clutter_gst_player_get_buffer_fill: - * @player: a #ClutterGstPlayer - * - * Retrieves the amount of the stream that is buffered. + * Return value: The playback volume between 0.0 and 1.0 * - * Return value: the fill level, between 0.0 and 1.0 + * Since: 3.0 */ gdouble -clutter_gst_player_get_buffer_fill (ClutterGstPlayer *player) -{ - gdouble retval = 0.0; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), 0); - - g_object_get (G_OBJECT (player), "buffer-fill", &retval, NULL); - - return retval; -} - -/* - * clutter_gst_player_get_buffer_size: - * @player: a #ClutterGstPlayer - * - * Retrieves the buffer size when buffering network streams. - * - * Return value: The buffer size - */ -gint -clutter_gst_player_get_buffer_size (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), -1); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_buffer_size (player); -} - -/** - * clutter_gst_player_set_buffer_size: - * @player: a #ClutterGstPlayer - * @size: The new size - * - * Sets the buffer size to be used when buffering network streams. - */ -void -clutter_gst_player_set_buffer_size (ClutterGstPlayer *player, - gint size) -{ - ClutterGstPlayerIface *iface; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->set_buffer_size (player, size); -} - -/** - * clutter_gst_player_get_buffer_duration: - * @player: a #ClutterGstPlayer - * - * Retrieves the buffer duration when buffering network streams. - * - * Return value: The buffer duration - */ -gint64 -clutter_gst_player_get_buffer_duration (ClutterGstPlayer *player) +clutter_gst_player_get_audio_volume (ClutterGstPlayer *self) { ClutterGstPlayerIface *iface; - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), -1); + g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (self), TRUE); - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - return iface->get_buffer_duration (player); -} - -/** - * clutter_gst_player_set_buffer_duration: - * @player: a #ClutterGstPlayer - * @duration: The new duration - * - * Sets the buffer duration to be used when buffering network streams. - */ -void -clutter_gst_player_set_buffer_duration (ClutterGstPlayer *player, - gint64 duration) -{ - ClutterGstPlayerIface *iface; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->set_buffer_duration (player, duration); + return iface->get_audio_volume (self); } /** * clutter_gst_player_set_audio_volume: - * @player: a #ClutterGstPlayer + * @self: a #ClutterGstPlayer * @volume: the volume as a double between 0.0 and 1.0 * - * Sets the playback volume of @player to @volume. - */ -void -clutter_gst_player_set_audio_volume (ClutterGstPlayer *player, - gdouble volume) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "audio-volume", volume, NULL); -} - -/** - * clutter_gst_player_get_audio_volume: - * @player: a #ClutterGstPlayer - * - * Retrieves the playback volume of @player. - * - * Return value: The playback volume between 0.0 and 1.0 - */ -gdouble -clutter_gst_player_get_audio_volume (ClutterGstPlayer *player) -{ - gdouble retval = 0.0; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), 0.0); - - g_object_get (G_OBJECT (player), "audio-volume", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_get_audio_streams: - * @player: a #ClutterGstPlayer - * - * Get the list of audio streams of the current media. - * - * Return value: (transfer none) (element-type utf8): a list of - * strings describing the available audio streams - * - * Since: 1.4 - */ -GList * -clutter_gst_player_get_audio_streams (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_audio_streams (player); -} - -/** - * clutter_gst_player_get_audio_stream: - * @player: a #ClutterGstPlayer - * - * Get the current audio stream. The number returned in the index of the - * audio stream playing in the list returned by - * clutter_gst_player_get_audio_streams(). - * - * Return value: the index of the current audio stream, -1 if the media has no - * audio stream - * - * Since: 1.4 - */ -gint -clutter_gst_player_get_audio_stream (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), -1); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_audio_stream (player); -} - -/** - * clutter_gst_player_set_audio_stream: - * @player: a #ClutterGstPlayer - * @index_: the index of the audio stream - * - * Set the audio stream to play. @index_ is the index of the stream - * in the list returned by clutter_gst_player_get_audio_streams(). - * - * Since: 1.4 - */ -void -clutter_gst_player_set_audio_stream (ClutterGstPlayer *player, - gint index_) -{ - ClutterGstPlayerIface *iface; - - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - iface->set_audio_stream (player, index_); -} - -/** - * clutter_gst_player_set_subtitle_uri: - * @player: a #ClutterGstPlayer - * @uri: the URI of a subtitle file - * - * Sets the location of a subtitle file to display while playing @player. - */ -void -clutter_gst_player_set_subtitle_uri (ClutterGstPlayer *player, - const char *uri) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "subtitle-uri", uri, NULL); -} - -/** - * clutter_gst_player_get_subtitle_uri: - * @player: a #ClutterGstPlayer - * - * Retrieves the URI of the subtitle file in use. - * - * Return value: the URI of the subtitle file. Use g_free() - * to free the returned string - */ -gchar * -clutter_gst_player_get_subtitle_uri (ClutterGstPlayer *player) -{ - gchar *retval = NULL; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); - - g_object_get (G_OBJECT (player), "subtitle-uri", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_set_subtitle_font_name: - * @player: a #ClutterGstPlayer - * @font_name: a font name, or %NULL to set the default font name - * - * Sets the font used by the subtitle renderer. The @font_name string must be - * either %NULL, which means that the default font name of the underlying - * implementation will be used; or must follow the grammar recognized by - * pango_font_description_from_string() like: - * - * |[ - * clutter_gst_player_set_subtitle_font_name (player, "Sans 24pt"); - * ]| - */ -void -clutter_gst_player_set_subtitle_font_name (ClutterGstPlayer *player, - const char *font_name) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "subtitle-font-name", font_name, NULL); -} - -/** - * clutter_gst_player_get_subtitle_font_name: - * @player: a #ClutterGstPlayer - * - * Retrieves the font name currently used. - * - * Return value: a string containing the font name. Use g_free() - * to free the returned string - */ -gchar * -clutter_gst_player_get_subtitle_font_name (ClutterGstPlayer *player) -{ - gchar *retval = NULL; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); - - g_object_get (G_OBJECT (player), "subtitle-font-name", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_get_subtitle_tracks: - * @player: a #ClutterGstPlayer - * - * Get the list of subtitles tracks of the current media. - * - * Return value: (transfer none) (element-type utf8): a list of - * strings describing the available subtitles tracks - * - * Since: 1.4 - */ -GList * -clutter_gst_player_get_subtitle_tracks (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), NULL); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_subtitle_tracks (player); -} - -/** - * clutter_gst_player_get_subtitle_track: - * @player: a #ClutterGstPlayer - * - * Get the current subtitles track. The number returned is the index of the - * subtiles track in the list returned by - * clutter_gst_player_get_subtitle_tracks(). - * - * Return value: the index of the current subtitlest track, -1 if the media has - * no subtitles track or if the subtitles have been turned off - * - * Since: 1.4 - */ -gint -clutter_gst_player_get_subtitle_track (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), -1); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_subtitle_track (player); -} - -/** - * clutter_gst_player_set_subtitle_track: - * @player: a #ClutterGstPlayer - * @index_: the index of the subtitles track - * - * Set the subtitles track to play. @index_ is the index of the stream - * in the list returned by clutter_gst_player_get_subtitle_tracks(). - * - * If @index_ is -1, the subtitles are turned off. + * Sets the playback volume of @self to @volume. * - * Since: 1.4 + * Since: 3.0 */ void -clutter_gst_player_set_subtitle_track (ClutterGstPlayer *player, - gint index_) +clutter_gst_player_set_audio_volume (ClutterGstPlayer *self, + gdouble volume) { ClutterGstPlayerIface *iface; - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); + g_return_if_fail (CLUTTER_GST_IS_PLAYER (self)); - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - iface->set_subtitle_track (player, index_); + iface->set_audio_volume (self, volume); } /** * clutter_gst_player_get_idle: - * @player: a #ClutterGstPlayer + * @self: a #ClutterGstPlayer * * Get the idle state of the pipeline. * * Return value: TRUE if the pipline is in idle mode, FALSE otherwise. * - * Since: 1.4 - */ -gboolean -clutter_gst_player_get_idle (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), TRUE); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_idle (player); -} - -/** - * clutter_gst_player_get_can_seek: - * @player: a #ClutterGstPlayer - * - * Retrieves whether @player is seekable or not. - * - * Return value: %TRUE if @player can seek, %FALSE otherwise. - */ -gboolean -clutter_gst_player_get_can_seek (ClutterGstPlayer *player) -{ - gboolean retval = FALSE; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), FALSE); - - g_object_get (G_OBJECT (player), "can-seek", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_get_in_seek: - * @player: a #ClutterGstPlayer - * - * Whether the player is seeking. - * - * Return value: TRUE if the player is seeking, FALSE otherwise. - * - * Since: 1.6 - */ -gboolean -clutter_gst_player_get_in_seek (ClutterGstPlayer *player) -{ - ClutterGstPlayerIface *iface; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), FALSE); - - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); - - return iface->get_in_seek (player); -} - -/** - * clutter_gst_player_set_progress: - * @player: a #ClutterGstPlayer - * @progress: the progress of the playback, between 0.0 and 1.0 - * - * Sets the playback progress of @player. The @progress is - * a normalized value between 0.0 (begin) and 1.0 (end). - */ -void -clutter_gst_player_set_progress (ClutterGstPlayer *player, - gdouble progress) -{ - g_return_if_fail (CLUTTER_GST_IS_PLAYER (player)); - - g_object_set (G_OBJECT (player), "progress", progress, NULL); -} - -/** - * clutter_gst_player_get_progress: - * @player: a #ClutterGstPlayer - * - * Retrieves the playback progress of @player. - * - * Return value: the playback progress, between 0.0 and 1.0 - */ -gdouble -clutter_gst_player_get_progress (ClutterGstPlayer *player) -{ - gdouble retval = 0.0; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), 0); - - g_object_get (G_OBJECT (player), "progress", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_get_duration: - * @player: a #ClutterGstPlayer - * - * Retrieves the duration of the media stream that @player represents. - * - * Return value: the duration of the media stream, in seconds - */ -gdouble -clutter_gst_player_get_duration (ClutterGstPlayer *player) -{ - gdouble retval = 0; - - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), 0); - - g_object_get (G_OBJECT (player), "duration", &retval, NULL); - - return retval; -} - -/** - * clutter_gst_player_is_live_media: - * @player: a #ClutterGstPlayer - * - * Whether the player is using a live media. - * - * Return value: TRUE if the player is using a live media, FALSE otherwise. + * Since: 3.0 */ gboolean -clutter_gst_player_is_live_media (ClutterGstPlayer *player) +clutter_gst_player_get_idle (ClutterGstPlayer *self) { ClutterGstPlayerIface *iface; - g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (player), FALSE); + g_return_val_if_fail (CLUTTER_GST_IS_PLAYER (self), TRUE); - iface = CLUTTER_GST_PLAYER_GET_INTERFACE (player); + iface = CLUTTER_GST_PLAYER_GET_INTERFACE (self); - return iface->is_live_media (player); + return iface->get_idle (self); } diff --git a/clutter-gst/clutter-gst-player.h b/clutter-gst/clutter-gst-player.h index 3626520..012d2e1 100644 --- a/clutter-gst/clutter-gst-player.h +++ b/clutter-gst/clutter-gst-player.h @@ -12,8 +12,7 @@ * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> * * Copyright (C) 2006 OpenedHand - * Copyright (C) 2009-2011 Intel Corporation - * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2009-2013 Intel Corporation * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> * * This library is free software; you can redistribute it and/or @@ -36,8 +35,8 @@ #define __CLUTTER_GST_PLAYER_H__ #include <glib-object.h> - -#include <clutter/clutter.h> +#include <cogl/cogl.h> +#include <gst/gst.h> #include <clutter-gst/clutter-gst-types.h> @@ -88,148 +87,60 @@ struct _ClutterGstPlayerIface ClutterGstPlayerIfacePrivate *priv; /*< public >*/ - GstElement * (* get_pipeline) (ClutterGstPlayer *player); - - gchar * (* get_user_agent) (ClutterGstPlayer *player); - void (* set_user_agent) (ClutterGstPlayer *player, - const gchar *user_agent); - - ClutterGstSeekFlags (* get_seek_flags) (ClutterGstPlayer *player); - void (* set_seek_flags) (ClutterGstPlayer *player, - ClutterGstSeekFlags flags); - - ClutterGstBufferingMode (* get_buffering_mode) (ClutterGstPlayer *player); - void (* set_buffering_mode) (ClutterGstPlayer *player, - ClutterGstBufferingMode mode); - - GList * (* get_audio_streams) (ClutterGstPlayer *player); - gint (* get_audio_stream) (ClutterGstPlayer *player); - void (* set_audio_stream) (ClutterGstPlayer *player, - gint index_); - - GList * (* get_subtitle_tracks) (ClutterGstPlayer *player); - gint (* get_subtitle_track) (ClutterGstPlayer *player); - void (* set_subtitle_track) (ClutterGstPlayer *player, - gint index_); - - gboolean (*get_idle) (ClutterGstPlayer *player); - - gboolean (*get_in_seek) (ClutterGstPlayer *player); - - gint (* get_buffer_size) (ClutterGstPlayer *player); - void (* set_buffer_size) (ClutterGstPlayer *player, - gint size); - - gint64 (* get_buffer_duration) (ClutterGstPlayer *player); - void (* set_buffer_duration) (ClutterGstPlayer *player, - gint64 duration); - - gboolean (* is_live_media) (ClutterGstPlayer *player); - - void (* _iface_reserved21) (void); - void (* _iface_reserved22) (void); - void (* _iface_reserved23) (void); - void (* _iface_reserved24) (void); - void (* _iface_reserved25) (void); - void (* _iface_reserved26) (void); - void (* _iface_reserved27) (void); - void (* _iface_reserved28) (void); - void (* _iface_reserved29) (void); - void (* _iface_reserved30) (void); - void (* _iface_reserved31) (void); - void (* _iface_reserved32) (void); - void (* _iface_reserved33) (void); - void (* _iface_reserved34) (void); - void (* _iface_reserved35) (void); + ClutterGstFrame * (* get_frame) (ClutterGstPlayer *self); + GstElement * (* get_pipeline) (ClutterGstPlayer *self); + + gboolean (* get_idle) (ClutterGstPlayer *self); + + gdouble (* get_audio_volume) (ClutterGstPlayer *self); + void (* set_audio_volume) (ClutterGstPlayer *self, + gdouble volume); + + gboolean (* get_playing) (ClutterGstPlayer *self); + void (* set_playing) (ClutterGstPlayer *self, + gboolean playing); + + void (* _iface_reserved7) (void); + void (* _iface_reserved8) (void); + void (* _iface_reserved9) (void); + void (* _iface_reserved10) (void); + void (* _iface_reserved11) (void); + void (* _iface_reserved12) (void); /* signals */ - void (* download_buffering) (ClutterGstPlayer *player, - gdouble start, - gdouble stop); - void (* eos) (ClutterGstPlayer *player); - void (* error) (ClutterGstPlayer *player, - const GError *error); - - void (* _clutter_reserved4) (void); - void (* _clutter_reserved5) (void); + void (* new_frame) (ClutterGstPlayer *self, ClutterGstFrame *frame); + void (* ready) (ClutterGstPlayer *self); + void (* eos) (ClutterGstPlayer *self); + void (* error) (ClutterGstPlayer *self, + const GError *error); + void (* size_change) (ClutterGstPlayer *self, + gint width, + gint height); + void (* _clutter_reserved6) (void); void (* _clutter_reserved7) (void); void (* _clutter_reserved8) (void); void (* _clutter_reserved9) (void); void (* _clutter_reserved10) (void); + void (* _clutter_reserved11) (void); + void (* _clutter_reserved12) (void); }; GType clutter_gst_player_get_type (void) G_GNUC_CONST; -void clutter_gst_player_class_init (GObjectClass *object_class); - -gboolean clutter_gst_player_init (ClutterGstPlayer *player); -void clutter_gst_player_deinit (ClutterGstPlayer *player); - -GstElement * clutter_gst_player_get_pipeline (ClutterGstPlayer *player); - -void clutter_gst_player_set_uri (ClutterGstPlayer *player, - const gchar *uri); -gchar * clutter_gst_player_get_uri (ClutterGstPlayer *player); -void clutter_gst_player_set_filename (ClutterGstPlayer *player, - const gchar *filename); - -void clutter_gst_player_set_playing (ClutterGstPlayer *player, - gboolean playing); -gboolean clutter_gst_player_is_playing (ClutterGstPlayer *player); - - -gchar * clutter_gst_player_get_user_agent (ClutterGstPlayer *player); -void clutter_gst_player_set_user_agent (ClutterGstPlayer *player, - const gchar *user_agent); - -ClutterGstSeekFlags clutter_gst_player_get_seek_flags (ClutterGstPlayer *player); -void clutter_gst_player_set_seek_flags (ClutterGstPlayer *player, - ClutterGstSeekFlags flags); - -ClutterGstBufferingMode clutter_gst_player_get_buffering_mode (ClutterGstPlayer *player); -void clutter_gst_player_set_buffering_mode (ClutterGstPlayer *player, - ClutterGstBufferingMode mode); -gdouble clutter_gst_player_get_buffer_fill (ClutterGstPlayer *player); -gint clutter_gst_player_get_buffer_size (ClutterGstPlayer *player); -void clutter_gst_player_set_buffer_size (ClutterGstPlayer *player, - gint size); -gint64 clutter_gst_player_get_buffer_duration (ClutterGstPlayer *player); -void clutter_gst_player_set_buffer_duration (ClutterGstPlayer *player, - gint64 duration); - -void clutter_gst_player_set_audio_volume (ClutterGstPlayer *player, - gdouble volume); -gdouble clutter_gst_player_get_audio_volume (ClutterGstPlayer *player); -GList * clutter_gst_player_get_audio_streams (ClutterGstPlayer *player); -gint clutter_gst_player_get_audio_stream (ClutterGstPlayer *player); -void clutter_gst_player_set_audio_stream (ClutterGstPlayer *player, - gint index_); - -void clutter_gst_player_set_subtitle_uri (ClutterGstPlayer *player, - const gchar *uri); -gchar * clutter_gst_player_get_subtitle_uri (ClutterGstPlayer *player); -void clutter_gst_player_set_subtitle_font_name - (ClutterGstPlayer *player, - const char *font_name); -gchar * clutter_gst_player_get_subtitle_font_name - (ClutterGstPlayer *player); -GList * clutter_gst_player_get_subtitle_tracks (ClutterGstPlayer *player); -gint clutter_gst_player_get_subtitle_track (ClutterGstPlayer *player); -void clutter_gst_player_set_subtitle_track (ClutterGstPlayer *player, - gint index_); - -gboolean clutter_gst_player_get_idle (ClutterGstPlayer *player); - -gboolean clutter_gst_player_get_can_seek (ClutterGstPlayer *player); -gboolean clutter_gst_player_get_in_seek (ClutterGstPlayer *player); - -void clutter_gst_player_set_progress (ClutterGstPlayer *player, - gdouble progress); -gdouble clutter_gst_player_get_progress (ClutterGstPlayer *player); -gdouble clutter_gst_player_get_duration (ClutterGstPlayer *player); - -gboolean clutter_gst_player_is_live_media (ClutterGstPlayer *player); +ClutterGstFrame * clutter_gst_player_get_frame (ClutterGstPlayer *self); + +GstElement * clutter_gst_player_get_pipeline (ClutterGstPlayer *self); + +gboolean clutter_gst_player_get_idle (ClutterGstPlayer *self); + +gboolean clutter_gst_player_get_playing (ClutterGstPlayer *self); +void clutter_gst_player_set_playing (ClutterGstPlayer *self, + gboolean playing); + +gdouble clutter_gst_player_get_audio_volume (ClutterGstPlayer *self); +void clutter_gst_player_set_audio_volume (ClutterGstPlayer *self, + gdouble volume); G_END_DECLS diff --git a/clutter-gst/clutter-gst-private.h b/clutter-gst/clutter-gst-private.h index a7e08e7..18b3c66 100644 --- a/clutter-gst/clutter-gst-private.h +++ b/clutter-gst/clutter-gst-private.h @@ -27,6 +27,7 @@ #define __CLUTTER_GST_PRIVATE_H__ #include <glib.h> +#include "clutter-gst.h" G_BEGIN_DECLS @@ -47,6 +48,12 @@ G_BEGIN_DECLS gboolean _internal_plugin_init (GstPlugin *plugin); +ClutterGstFrame *clutter_gst_frame_new (CoglPipeline *pipeline); + +void clutter_gst_util_update_frame (ClutterGstPlayer *player, + ClutterGstFrame **frame, + CoglPipeline *pipeline); + G_END_DECLS #endif /* __CLUTTER_GST_PRIVATE_H__ */ diff --git a/clutter-gst/clutter-gst-types.c b/clutter-gst/clutter-gst-types.c new file mode 100644 index 0000000..d4f632d --- /dev/null +++ b/clutter-gst/clutter-gst-types.c @@ -0,0 +1,75 @@ +/* + * Clutter-GStreamer. + * + * GStreamer integration library for Clutter. + * + * clutter-gst-types.c - Some basic types. + * + * Authored by Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com> + * + * Copyright (C) 2013 Intel Corporation + * + * 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. + */ + +#include "clutter-gst-types.h" + +ClutterGstFrame * +clutter_gst_frame_new (CoglPipeline *pipeline) +{ + ClutterGstFrame *frame = g_slice_new0 (ClutterGstFrame); + CoglTexture *texture; + + frame->pipeline = cogl_object_ref (pipeline); + texture = cogl_pipeline_get_layer_texture (pipeline, 0); + + frame->resolution.width = cogl_texture_get_width (texture); + frame->resolution.height = cogl_texture_get_width (texture); +} + +static gpointer +clutter_gst_frame_copy (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstFrame *frame = g_slice_dup (ClutterGstFrame, data); + + if (frame->pipeline != COGL_INVALID_HANDLE) + frame->pipeline = cogl_handle_ref (frame->pipeline); + + return frame; + } + + return NULL; +} + +static void +clutter_gst_frame_free (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstFrame *frame = (ClutterGstFrame *) data; + + if (frame->pipeline != COGL_INVALID_HANDLE) + cogl_handle_unref (frame->pipeline); + g_slice_free (ClutterGstFrame, frame); + } +} + +G_DEFINE_BOXED_TYPE (ClutterGstFrame, + clutter_gst_frame, + clutter_gst_frame_copy, + clutter_gst_frame_free); diff --git a/clutter-gst/clutter-gst-types.h b/clutter-gst/clutter-gst-types.h index 60b8d95..ffff545 100644 --- a/clutter-gst/clutter-gst-types.h +++ b/clutter-gst/clutter-gst-types.h @@ -29,9 +29,14 @@ #error "Only <clutter-gst/clutter-gst.h> can be included directly." #endif +#include <cogl/cogl.h> + #ifndef __CLUTTER_GST_TYPES_H__ #define __CLUTTER_GST_TYPES_H__ +#define CLUTTER_GST_TYPE_FRAME (clutter_gst_frame_get_type ()) + +typedef struct _ClutterGstFrame ClutterGstFrame; typedef struct _ClutterGstVideoResolution ClutterGstVideoResolution; /** @@ -77,4 +82,20 @@ struct _ClutterGstVideoResolution gint height; }; +/** + * ClutterGstFrame: + * @resolution: a #ClutterGstVideoResolution + * @frame: a #CoglHandle to the pipeline to paint a frame + * + * Represents a frame outputted by the #ClutterGstVideoSink. + */ +struct _ClutterGstFrame +{ + ClutterGstVideoResolution resolution; + CoglPipeline *pipeline; +}; + + +GType clutter_gst_frame_get_type (void) G_GNUC_CONST; + #endif /* __CLUTTER_GST_TYPES_H__ */ diff --git a/clutter-gst/clutter-gst-util.c b/clutter-gst/clutter-gst-util.c index d5b5cb6..9d6f80a 100644 --- a/clutter-gst/clutter-gst-util.c +++ b/clutter-gst/clutter-gst-util.c @@ -148,7 +148,7 @@ clutter_gst_init (int *argc, GST_VERSION_MINOR, "cluttersink", "Element to render to ClutterGst actors", - _internal_plugin_init, + /* _internal_plugin_init */NULL, VERSION, "LGPL", /* license */ "clutter-gst", PACKAGE, @@ -228,7 +228,7 @@ clutter_gst_init_with_args (int *argc, GST_VERSION_MINOR, "cluttersink", "Element to render to ClutterGst actors", - _internal_plugin_init, + /* _internal_plugin_init */NULL, VERSION, "LGPL", /* license */ "clutter-gst", PACKAGE, @@ -239,3 +239,28 @@ clutter_gst_init_with_args (int *argc, return CLUTTER_INIT_SUCCESS; } + +void +clutter_gst_util_update_frame (ClutterGstPlayer *player, + ClutterGstFrame **frame, + CoglPipeline *pipeline) +{ + ClutterGstFrame *old_frame = *frame; + ClutterGstFrame *new_frame = clutter_gst_frame_new (pipeline); + + *frame = new_frame; + + if (old_frame == NULL || + new_frame->resolution.width != old_frame->resolution.width || + new_frame->resolution.height != old_frame->resolution.height) + { + g_signal_emit_by_name (player, "size-change", + new_frame->resolution.width, + new_frame->resolution.height); + } + + if (old_frame) + g_boxed_free (CLUTTER_GST_TYPE_FRAME, old_frame); + + g_signal_emit_by_name (player, "new-frame", new_frame); +} diff --git a/clutter-gst/clutter-gst-video-actor.c b/clutter-gst/clutter-gst-video-actor.c deleted file mode 100644 index b865dd4..0000000 --- a/clutter-gst/clutter-gst-video-actor.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Clutter-GStreamer. - * - * GStreamer integration library for Clutter. - * - * clutter-gst-video-actor.c - ClutterActor using GStreamer to display a - * video stream. - * - * Authored By Matthew Allum <mallum@openedhand.com> - * Damien Lespiau <damien.lespiau@intel.com> - * Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com> - * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> - * - * Copyright (C) 2006 OpenedHand - * Copyright (C) 2010, 2011 Intel Corporation - * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> - * - * 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. - */ - -/** - * SECTION:clutter-gst-actor - * @short_description: Actor for playback of video files. - * - * #ClutterGstVideoActor is a #ClutterActor that plays video files. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> - -#include <glib.h> -#include <gio/gio.h> -#include <gst/base/gstbasesink.h> -#include <gst/video/video.h> - -#include "clutter-gst-video-actor.h" -#include "clutter-gst-debug.h" -#include "clutter-gst-enum-types.h" -#include "clutter-gst-marshal.h" -#include "clutter-gst-player.h" -#include "clutter-gst-private.h" - -struct _ClutterGstVideoActorPrivate -{ -}; - -static void clutter_gst_video_actor_player_init (ClutterGstPlayerIface *iface); - -G_DEFINE_TYPE_WITH_CODE (ClutterGstVideoActor, - clutter_gst_video_actor, - CLUTTER_GST_TYPE_ACTOR, - G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER, - clutter_gst_video_actor_player_init)); - -/* - * ClutterGstPlayer implementation - */ - -static void -clutter_gst_video_actor_player_init (ClutterGstPlayerIface *iface) -{ -} - -/* - * ClutterGstActor implementation - */ - -static gboolean -clutter_gst_video_actor_is_idle (ClutterGstActor *actor) -{ - return clutter_gst_player_get_idle (CLUTTER_GST_PLAYER (actor)); -} - -/* - * GObject implementation - */ - -static void -clutter_gst_video_actor_dispose (GObject *object) -{ - clutter_gst_player_deinit (CLUTTER_GST_PLAYER (object)); - - G_OBJECT_CLASS (clutter_gst_video_actor_parent_class)->dispose (object); -} - -static void -clutter_gst_video_actor_class_init (ClutterGstVideoActorClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - ClutterGstActorClass *gst_actor_class = CLUTTER_GST_ACTOR_CLASS (klass); - - //g_type_class_add_private (klass, sizeof (ClutterGstVideoActorPrivate)); - - object_class->dispose = clutter_gst_video_actor_dispose; - - gst_actor_class->is_idle = clutter_gst_video_actor_is_idle; - - clutter_gst_player_class_init (object_class); -} - -static gboolean -setup_pipeline (ClutterGstVideoActor *video_actor) -{ - GstElement *pipeline, *video_sink; - - pipeline = - clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (video_actor)); - if (!pipeline) - { - g_critical ("Unable to create pipeline"); - return FALSE; - } - - video_sink = gst_element_factory_make ("cluttersink", NULL); - g_object_set (video_sink, - "actor", CLUTTER_GST_ACTOR (video_actor), - NULL); - g_object_set (pipeline, - "video-sink", video_sink, - "subtitle-font-desc", "Sans 16", - NULL); - - return TRUE; -} - -static void -clutter_gst_video_actor_init (ClutterGstVideoActor *video_actor) -{ - //video_actor->priv = - // G_TYPE_INSTANCE_GET_PRIVATE (video_actor, - // CLUTTER_GST_TYPE_VIDEO_ACTOR, - // ClutterGstVideoActorPrivate); - - if (!clutter_gst_player_init (CLUTTER_GST_PLAYER (video_actor))) - { - g_warning ("Failed to initiate suitable playback pipeline."); - return; - } - - if (!setup_pipeline (video_actor)) - { - g_warning ("Failed to initiate suitable sinks for pipeline."); - return; - } -} - -/* - * Public symbols - */ - -/** - * clutter_gst_video_actor_new: - * - * Creates a video actor. - * - * <note>This function has to be called from Clutter's main thread. While - * GStreamer will spawn threads to do its work, we want all the GL calls to - * happen in the same thread. Clutter-gst knows which thread it is by - * assuming this constructor is called from the Clutter thread.</note> - * - * Return value: the newly created video actor - */ -ClutterActor* -clutter_gst_video_actor_new (void) -{ - return g_object_new (CLUTTER_GST_TYPE_VIDEO_ACTOR, - NULL); -} diff --git a/clutter-gst/clutter-gst-video-actor.h b/clutter-gst/clutter-gst-video-actor.h deleted file mode 100644 index 6f8bd1b..0000000 --- a/clutter-gst/clutter-gst-video-actor.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Clutter-GStreamer. - * - * GStreamer integration library for Clutter. - * - * clutter-gst-video-actor.h - ClutterActor using GStreamer to display a - * video stream. - * - * Authored By Matthew Allum <mallum@openedhand.com> - * Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk> - * - * Copyright (C) 2006 OpenedHand - * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/> - * - * 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. - */ - -#if !defined(__CLUTTER_GST_H_INSIDE__) && !defined(CLUTTER_GST_COMPILATION) -#error "Only <clutter-gst/clutter-gst.h> can be included directly." -#endif - -#ifndef __CLUTTER_GST_VIDEO_ACTOR_H__ -#define __CLUTTER_GST_VIDEO_ACTOR_H__ - -#include <glib-object.h> -#include <clutter/clutter.h> -#include <gst/gstelement.h> - -#include <clutter-gst/clutter-gst-actor.h> -#include <clutter-gst/clutter-gst-types.h> - -G_BEGIN_DECLS - -#define CLUTTER_GST_TYPE_VIDEO_ACTOR clutter_gst_video_actor_get_type() - -#define CLUTTER_GST_VIDEO_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - CLUTTER_GST_TYPE_VIDEO_ACTOR, ClutterGstVideoActor)) - -#define CLUTTER_GST_VIDEO_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - CLUTTER_GST_TYPE_VIDEO_ACTOR, ClutterGstVideoActorClass)) - -#define CLUTTER_GST_IS_VIDEO_ACTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - CLUTTER_GST_TYPE_VIDEO_ACTOR)) - -#define CLUTTER_GST_IS_VIDEO_ACTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - CLUTTER_GST_TYPE_VIDEO_ACTOR)) - -#define CLUTTER_GST_VIDEO_ACTOR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - CLUTTER_GST_TYPE_VIDEO_ACTOR, ClutterGstVideoActorClass)) - -typedef struct _ClutterGstVideoActor ClutterGstVideoActor; -typedef struct _ClutterGstVideoActorClass ClutterGstVideoActorClass; -typedef struct _ClutterGstVideoActorPrivate ClutterGstVideoActorPrivate; - -/** - * ClutterGstVideoActor: - * - * Subclass of #ClutterGstActor that displays videos using GStreamer. - * - * The #ClutterGstVideoActor structure contains only private data and - * should not be accessed directly. - */ -struct _ClutterGstVideoActor -{ - /*< private >*/ - ClutterGstActor parent; - ClutterGstVideoActorPrivate *priv; -}; - -/** - * ClutterGstVideoActorClass: - * - * Base class for #ClutterGstVideoActor. - */ -struct _ClutterGstVideoActorClass -{ - /*< private >*/ - ClutterGstActorClass parent_class; - - /* Future padding */ - void (* _clutter_reserved1) (void); - void (* _clutter_reserved2) (void); - void (* _clutter_reserved3) (void); - void (* _clutter_reserved4) (void); - void (* _clutter_reserved5) (void); - void (* _clutter_reserved6) (void); -}; - -GType clutter_gst_video_actor_get_type (void) G_GNUC_CONST; - -ClutterActor * clutter_gst_video_actor_new (void); - -G_END_DECLS - -#endif /* __CLUTTER_GST_VIDEO_ACTOR_H__ */ diff --git a/clutter-gst/clutter-gst-video-sink.c b/clutter-gst/clutter-gst-video-sink.c index 742b3a7..2104f70 100644 --- a/clutter-gst/clutter-gst-video-sink.c +++ b/clutter-gst/clutter-gst-video-sink.c @@ -131,10 +131,17 @@ GST_DEBUG_CATEGORY_STATIC (clutter_gst_video_sink_debug); enum { PROP_0, - PROP_ACTOR, + PROP_COGL_PIPELINE, PROP_UPDATE_PRIORITY }; +enum +{ + SEND_EVENT, + + LAST_SIGNAL +}; + typedef enum { CLUTTER_GST_NOFORMAT, @@ -194,8 +201,10 @@ typedef struct _ClutterGstRenderer struct _ClutterGstVideoSinkPrivate { - ClutterActor *actor; + ClutterStage *default_stage; + CoglMaterial *material_template; + CoglMaterial *cogl_pipeline; GstFlowReturn flow_ret; @@ -227,8 +236,7 @@ G_DEFINE_TYPE_WITH_CODE (ClutterGstVideoSink, clutter_gst_video_sink, GST_TYPE_BASE_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, clutter_gst_navigation_interface_init)); -static void clutter_gst_video_sink_set_actor (ClutterGstVideoSink * sink, - ClutterActor * actor); +static guint sink_signals[LAST_SIGNAL] = { 0, }; /* * ClutterGstSource implementation @@ -297,18 +305,19 @@ ensure_texture_pixel_aspect_ratio (ClutterGstVideoSink * sink) GParamSpec *pspec; GValue par = { 0, }; - if (priv->actor == NULL) - return; + /* if (priv->actor == NULL) */ + /* return; */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->actor), - "pixel-aspect-ratio"); - if (pspec) { + /* pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (priv->actor), */ + /* "pixel-aspect-ratio"); */ + /* if (pspec) { */ g_value_init (&par, GST_TYPE_FRACTION); gst_value_set_fraction (&par, priv->info.par_n, priv->info.par_d); - g_object_set_property (G_OBJECT (priv->actor), - "pixel-aspect-ratio", &par); + /* TODO: propagate the value through property/signal */ + /* g_object_set_property (G_OBJECT (priv->actor), */ + /* "pixel-aspect-ratio", &par); */ g_value_unset (&par); - } + /* } */ } static gboolean @@ -417,47 +426,47 @@ no_suitable_renderer: } } -static gboolean -on_stage_destroyed (ClutterStage * stage, - ClutterEvent * event, gpointer user_data) -{ - ClutterGstSource *gst_source = user_data; - ClutterGstVideoSinkPrivate *priv = gst_source->sink->priv; +/* static gboolean */ +/* on_stage_destroyed (ClutterStage * stage, */ +/* ClutterEvent * event, gpointer user_data) */ +/* { */ +/* ClutterGstSource *gst_source = user_data; */ +/* ClutterGstVideoSinkPrivate *priv = gst_source->sink->priv; */ - g_mutex_lock (&gst_source->buffer_lock); +/* g_mutex_lock (&gst_source->buffer_lock); */ - clutter_actor_hide (CLUTTER_ACTOR (stage)); - clutter_actor_remove_child (CLUTTER_ACTOR (stage), priv->actor); +/* clutter_actor_hide (CLUTTER_ACTOR (stage)); */ +/* clutter_actor_remove_child (CLUTTER_ACTOR (stage), priv->actor); */ - if (gst_source->buffer) - gst_buffer_unref (gst_source->buffer); +/* if (gst_source->buffer) */ +/* gst_buffer_unref (gst_source->buffer); */ - gst_source->stage_lost = TRUE; - gst_source->buffer = NULL; - priv->actor = NULL; +/* gst_source->stage_lost = TRUE; */ +/* gst_source->buffer = NULL; */ +/* priv->actor = NULL; */ - g_mutex_unlock (&gst_source->buffer_lock); +/* g_mutex_unlock (&gst_source->buffer_lock); */ - return TRUE; -} +/* return TRUE; */ +/* } */ -static void -on_stage_allocation_changed (ClutterStage * stage, - ClutterActorBox * box, ClutterAllocationFlags flags, gpointer user_data) -{ - ClutterGstSource *gst_source = user_data; - ClutterGstVideoSinkPrivate *priv = gst_source->sink->priv; - gint width, height; +/* static void */ +/* on_stage_allocation_changed (ClutterStage * stage, */ +/* ClutterActorBox * box, ClutterAllocationFlags flags, gpointer user_data) */ +/* { */ +/* ClutterGstSource *gst_source = user_data; */ +/* ClutterGstVideoSinkPrivate *priv = gst_source->sink->priv; */ +/* gint width, height; */ - if (gst_source->stage_lost) - return; +/* if (gst_source->stage_lost) */ +/* return; */ - width = (gint) (box->x2 - box->x1); - height = (gint) (box->y2 - box->y1); +/* width = (gint) (box->x2 - box->x1); */ +/* height = (gint) (box->y2 - box->y1); */ - GST_DEBUG ("Size changed to %i/%i", width, height); - clutter_actor_set_size (priv->actor, width, height); -} +/* GST_DEBUG ("Size changed to %i/%i", width, height); */ +/* clutter_actor_set_size (priv->actor, width, height); */ +/* } */ static gboolean clutter_gst_source_dispatch (GSource * source, @@ -486,35 +495,35 @@ clutter_gst_source_dispatch (GSource * source, goto negotiation_fail; gst_source->has_new_caps = FALSE; - if (!priv->actor) { - ClutterActor *stage; - ClutterActor *actor; + /* if (!priv->actor) { */ + /* ClutterActor *stage; */ + /* ClutterActor *actor; */ - GST_DEBUG_OBJECT (gst_source->sink, - "No existing texture, creating stage and actor"); - stage = clutter_stage_new (); - actor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); + /* GST_DEBUG_OBJECT (gst_source->sink, */ + /* "No existing texture, creating stage and actor"); */ + /* stage = clutter_stage_new (); */ + /* actor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); */ - clutter_gst_video_sink_set_actor (gst_source->sink, actor); - clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); - clutter_actor_add_child (stage, actor); - clutter_stage_set_no_clear_hint (CLUTTER_STAGE (stage), TRUE); + /* clutter_gst_video_sink_set_actor (gst_source->sink, actor); */ + /* clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE); */ + /* clutter_actor_add_child (stage, actor); */ + /* clutter_stage_set_no_clear_hint (CLUTTER_STAGE (stage), TRUE); */ - g_signal_connect (stage, "delete-event", - G_CALLBACK (on_stage_destroyed), gst_source); - g_signal_connect (stage, "allocation-changed", - G_CALLBACK (on_stage_allocation_changed), gst_source); + /* g_signal_connect (stage, "delete-event", */ + /* G_CALLBACK (on_stage_destroyed), gst_source); */ + /* g_signal_connect (stage, "allocation-changed", */ + /* G_CALLBACK (on_stage_allocation_changed), gst_source); */ /* FIXME : We already call this above ? */ if (!clutter_gst_parse_caps (caps, gst_source->sink, TRUE)) goto negotiation_fail; - clutter_actor_set_size (stage, priv->info.width, priv->info.height); - clutter_actor_show (stage); - } else { + /* clutter_actor_set_size (stage, priv->info.width, priv->info.height); */ + /* clutter_actor_show (stage); */ + /* } else { */ /* FIXME : We already call this above ? */ - if (!clutter_gst_parse_caps (caps, gst_source->sink, TRUE)) - goto negotiation_fail; - } + /* if (!clutter_gst_parse_caps (caps, gst_source->sink, TRUE)) */ + /* goto negotiation_fail; */ + /* } */ if (priv->material_template) cogl_object_unref (priv->material_template); @@ -736,8 +745,13 @@ _create_paint_material (ClutterGstVideoSink * sink, cogl_handle_unref (tex2); } - clutter_gst_actor_set_cogl_material (CLUTTER_GST_ACTOR (priv->actor), material); - cogl_object_unref (material); + cogl_handle_unref (priv->cogl_pipeline); + priv->cogl_pipeline = material; + + g_object_notify (sink, "cogl-pipeline"); + + /* clutter_gst_actor_set_cogl_material (CLUTTER_GST_ACTOR (priv->actor), material); */ + /* cogl_object_unref (material); */ } static void @@ -1088,78 +1102,78 @@ static ClutterGstRenderer ayuv_glsl_renderer = { * HW Surfaces */ -#ifdef HAVE_HW_DECODER_SUPPORT -static CoglMaterial * -clutter_gst_hw_init (ClutterGstVideoSink * sink) -{ - ClutterGstVideoSinkPrivate *priv = sink->priv; - CoglHandle tex; - CoglHandle material; - - /* Default texture is 1x1, let's replace it with one big enough. */ - tex = cogl_texture_new_with_size (priv->info.width, priv->info.height, - CLUTTER_GST_TEXTURE_FLAGS, COGL_PIXEL_FORMAT_BGRA_8888); - - material = cogl_material_new (); - cogl_material_set_layer (material, 0, tex); - clutter_gst_actor_set_cogl_material (CLUTTER_GST_ACTOR (priv->actor), material); - - cogl_object_unref (tex); - - return material; -} - -static void -clutter_gst_hw_deinit (ClutterGstVideoSink * sink) -{ - ClutterGstVideoSinkPrivate *priv = sink->priv; - - if (priv->converter != NULL) - g_object_unref (priv->converter); - priv->converter = NULL; -} - -static void -clutter_gst_hw_upload (ClutterGstVideoSink * sink, GstBuffer * buffer) -{ - ClutterGstVideoSinkPrivate *priv = sink->priv; - GstSurfaceMeta *surface = gst_buffer_get_surface_meta (buffer); - - g_return_if_fail (surface != NULL); - - if (G_UNLIKELY (priv->converter == NULL)) { - CoglHandle tex; - unsigned int gl_texture; - unsigned int gl_target; - GValue value = { 0 }; - - tex = clutter_gst_actor_get_cogl_texture (CLUTTER_GST_ACTOR (priv->actor)); - cogl_texture_get_gl_texture (tex, &gl_texture, &gl_target); - - g_value_init (&value, G_TYPE_UINT); - g_value_set_uint (&value, gl_texture); - - priv->converter = - gst_surface_meta_create_converter (surface, "opengl", &value); - g_return_if_fail (priv->converter); - } - - gst_surface_converter_upload (priv->converter, buffer); - - /* The actor is dirty, schedule a redraw */ - clutter_actor_queue_redraw (priv->actor); -} - -static ClutterGstRenderer hw_renderer = { - "HW surface", - CLUTTER_GST_SURFACE, - 0, - GST_STATIC_CAPS ("x-video/surface, opengl=true"), - clutter_gst_hw_init, - clutter_gst_hw_deinit, - clutter_gst_hw_upload, -}; -#endif +/* #ifdef HAVE_HW_DECODER_SUPPORT */ +/* static CoglMaterial * */ +/* clutter_gst_hw_init (ClutterGstVideoSink * sink) */ +/* { */ +/* ClutterGstVideoSinkPrivate *priv = sink->priv; */ +/* CoglHandle tex; */ +/* CoglHandle material; */ + +/* /\* Default texture is 1x1, let's replace it with one big enough. *\/ */ +/* tex = cogl_texture_new_with_size (priv->info.width, priv->info.height, */ +/* CLUTTER_GST_TEXTURE_FLAGS, COGL_PIXEL_FORMAT_BGRA_8888); */ + +/* material = cogl_material_new (); */ +/* cogl_material_set_layer (material, 0, tex); */ +/* clutter_gst_actor_set_cogl_material (CLUTTER_GST_ACTOR (priv->actor), material); */ + +/* cogl_object_unref (tex); */ + +/* return material; */ +/* } */ + +/* static void */ +/* clutter_gst_hw_deinit (ClutterGstVideoSink * sink) */ +/* { */ +/* ClutterGstVideoSinkPrivate *priv = sink->priv; */ + +/* if (priv->converter != NULL) */ +/* g_object_unref (priv->converter); */ +/* priv->converter = NULL; */ +/* } */ + +/* static void */ +/* clutter_gst_hw_upload (ClutterGstVideoSink * sink, GstBuffer * buffer) */ +/* { */ +/* ClutterGstVideoSinkPrivate *priv = sink->priv; */ +/* GstSurfaceMeta *surface = gst_buffer_get_surface_meta (buffer); */ + +/* g_return_if_fail (surface != NULL); */ + +/* if (G_UNLIKELY (priv->converter == NULL)) { */ +/* CoglHandle tex; */ +/* unsigned int gl_texture; */ +/* unsigned int gl_target; */ +/* GValue value = { 0 }; */ + +/* tex = clutter_gst_actor_get_cogl_texture (CLUTTER_GST_ACTOR (priv->actor)); */ +/* cogl_texture_get_gl_texture (tex, &gl_texture, &gl_target); */ + +/* g_value_init (&value, G_TYPE_UINT); */ +/* g_value_set_uint (&value, gl_texture); */ + +/* priv->converter = */ +/* gst_surface_meta_create_converter (surface, "opengl", &value); */ +/* g_return_if_fail (priv->converter); */ +/* } */ + +/* gst_surface_converter_upload (priv->converter, buffer); */ + +/* /\* The actor is dirty, schedule a redraw *\/ */ +/* clutter_actor_queue_redraw (priv->actor); */ +/* } */ + +/* static ClutterGstRenderer hw_renderer = { */ +/* "HW surface", */ +/* CLUTTER_GST_SURFACE, */ +/* 0, */ +/* GST_STATIC_CAPS ("x-video/surface, opengl=true"), */ +/* clutter_gst_hw_init, */ +/* clutter_gst_hw_deinit, */ +/* clutter_gst_hw_upload, */ +/* }; */ +/* #endif */ static GSList * clutter_gst_build_renderers_list (void) @@ -1295,9 +1309,20 @@ navigation_event (ClutterActor * actor, } static void +_default_stage_changed (ClutterStageManager *manager, + GParamSpec *param, + ClutterGstVideoSink *self) +{ + ClutterGstVideoSinkPrivate *priv = self->priv; + + priv->default_stage = clutter_stage_manager_get_default_stage (manager); +} + +static void clutter_gst_video_sink_init (ClutterGstVideoSink * sink) { ClutterGstVideoSinkPrivate *priv; + ClutterStageManager *manager; sink->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (sink, CLUTTER_GST_TYPE_VIDEO_SINK, @@ -1312,6 +1337,11 @@ clutter_gst_video_sink_init (ClutterGstVideoSink * sink) priv->signal_handler_ids = g_array_new (FALSE, TRUE, sizeof (gulong)); priv->priority = CLUTTER_GST_DEFAULT_PRIORITY; + + manager = clutter_stage_manager_get_default (); + priv->default_stage = clutter_stage_manager_get_default_stage (manager); + g_signal_connect (manager, "notify::default-stage", + G_CALLBACK (_default_stage_changed), sink); } static GstFlowReturn @@ -1390,7 +1420,6 @@ clutter_gst_video_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) return TRUE; } - static void clutter_gst_video_sink_dispose (GObject * object) { @@ -1405,8 +1434,9 @@ clutter_gst_video_sink_dispose (GObject * object) priv->renderer = NULL; } - if (priv->actor) - clutter_gst_video_sink_set_actor (self, NULL); + g_object_notify (self, "cogl-pipeline"); + /* if (priv->actor) */ + /* clutter_gst_video_sink_set_actor (self, NULL); */ if (priv->caps) { gst_caps_unref (priv->caps); @@ -1433,52 +1463,12 @@ clutter_gst_video_sink_finalize (GObject * object) } static void -clutter_gst_video_sink_set_actor (ClutterGstVideoSink * sink, - ClutterActor * actor) -{ - const char const *events[] = { - "key-press-event", - "key-release-event", - "button-press-event", - "button-release-event", - "motion-event" - }; - ClutterGstVideoSinkPrivate *priv = sink->priv; - guint i; - - if (priv->actor) { - for (i = 0; i < priv->signal_handler_ids->len; i++) { - gulong id = g_array_index (priv->signal_handler_ids, gulong, i); - g_signal_handler_disconnect (priv->actor, id); - } - g_array_set_size (priv->signal_handler_ids, 0); - } - - priv->actor = actor; - if (priv->actor == NULL) - return; - - clutter_actor_set_reactive (priv->actor, TRUE); - g_object_add_weak_pointer (G_OBJECT (priv->actor), (gpointer *) &(priv->actor)); - - for (i = 0; i < G_N_ELEMENTS (events); i++) { - gulong id; - id = g_signal_connect (priv->actor, events[i], - G_CALLBACK (navigation_event), sink); - g_array_append_val (priv->signal_handler_ids, id); - } -} - -static void clutter_gst_video_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { ClutterGstVideoSink *sink = CLUTTER_GST_VIDEO_SINK (object); switch (prop_id) { - case PROP_ACTOR: - clutter_gst_video_sink_set_actor (sink, g_value_get_object (value)); - break; case PROP_UPDATE_PRIORITY: clutter_gst_video_sink_set_priority (sink, g_value_get_int (value)); break; @@ -1496,8 +1486,8 @@ clutter_gst_video_sink_get_property (GObject * object, ClutterGstVideoSinkPrivate *priv = sink->priv; switch (prop_id) { - case PROP_ACTOR: - g_value_set_object (value, priv->actor); + case PROP_COGL_PIPELINE: + g_value_set_object (value, priv->cogl_pipeline); break; case PROP_UPDATE_PRIORITY: g_value_set_int (value, priv->priority); @@ -1542,6 +1532,22 @@ clutter_gst_video_sink_stop (GstBaseSink * base_sink) return TRUE; } +static gboolean +_clutter_gst_point_handled_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gpointer translated_point; + + translated_point = g_value_get_boxed (handler_return); + g_value_set_boxed (return_accu, translated_point); + continue_emission = (translated_point == NULL); + + return continue_emission; +} + static void clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass * klass) { @@ -1579,35 +1585,61 @@ clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass * klass) gstbase_sink_class->get_caps = clutter_gst_video_sink_get_caps; /** - * ClutterGstVideoSink:actor: - * - * This is the actor the video is decoded into. It can be any - * #ClutterGstActor, however Cluter-Gst has a handy subclass, - * #ClutterGstVideoActor, that implements the #ClutterGstPlayer - * interface. - */ - pspec = g_param_spec_object ("actor", - "Actor", - "ClutterGstActor the video will be decoded into", - CLUTTER_GST_TYPE_ACTOR, - CLUTTER_GST_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ACTOR, pspec); - - /** * ClutterGstVideoSink:update-priority: * * Clutter-Gst installs a #GSource to signal that a new frame is ready to * the Clutter thread. This property allows to tweak the priority of the * source (Lower value is higher priority). * - * Since 1.0 + * Since 3.0 */ pspec = g_param_spec_int ("update-priority", - "Update Priority", - "Priority of video updates in the Clutter thread", - -G_MAXINT, G_MAXINT, - CLUTTER_GST_DEFAULT_PRIORITY, CLUTTER_GST_PARAM_READWRITE); + "Update Priority", + "Priority of video updates in the Clutter thread", + -G_MAXINT, G_MAXINT, + CLUTTER_GST_DEFAULT_PRIORITY, + CLUTTER_GST_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_UPDATE_PRIORITY, pspec); + + /** + * ClutterGstVideoSink:cogl-pipeline: + * + * Cogl pipeline to be used to paint a video frame + * + * Since 3.0 + */ + pspec = g_param_spec_boxed ("frame", + "Update Priority", + "Priority of video updates in the Clutter thread", + COGL_TYPE_HANDLE, + CLUTTER_GST_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_COGL_PIPELINE, pspec); + + /** + * ClutterGstVideoSink::send-event: + * @sink: the #ClutterGstVideoSink that emitted the signal + * @point: the position of the event + * + * The #ClutterGstVideoSink::send-event signal is emitted each time + * there is a need to get the coordinates of an event onto the + * screen. + * + * Return value: a #ClutterPoint if the position of the point in the + * stream can be translated into the window's coordinates, NULL + * otherwise. + * + * Since: 3.0 + */ + sink_signals[SEND_EVENT] = + g_signal_new ("send-event", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + 0, + _clutter_gst_point_handled_accumulator, + NULL, + NULL, + CLUTTER_TYPE_POINT, 1, + CLUTTER_TYPE_POINT); } static void @@ -1625,20 +1657,23 @@ clutter_gst_navigation_send_event (GstNavigation * navigation, * if the structure contains pointer coordinates */ if (gst_structure_get_double (structure, "pointer_x", &x) && gst_structure_get_double (structure, "pointer_y", &y)) { - if (clutter_actor_transform_stage_point (CLUTTER_ACTOR (priv->actor), x, - y, &x_out, &y_out) == FALSE) { - g_warning ("Failed to convert non-scaled coordinates for video-sink"); - return; - } - x = x_out * priv->info.width / - clutter_actor_get_width (CLUTTER_ACTOR (priv->actor)); - y = y_out * priv->info.height / - clutter_actor_get_height (CLUTTER_ACTOR (priv->actor)); + /* TODO: emit signal to get transformed point */ + + /* if (clutter_actor_transform_stage_point (CLUTTER_ACTOR (priv->actor), x, */ + /* y, &x_out, &y_out) == FALSE) { */ + /* g_warning ("Failed to convert non-scaled coordinates for video-sink"); */ + /* return; */ + /* } */ - gst_structure_set (structure, - "pointer_x", G_TYPE_DOUBLE, (gdouble) x, - "pointer_y", G_TYPE_DOUBLE, (gdouble) y, NULL); + /* x = x_out * priv->info.width / */ + /* clutter_actor_get_width (CLUTTER_ACTOR (priv->actor)); */ + /* y = y_out * priv->info.height / */ + /* clutter_actor_get_height (CLUTTER_ACTOR (priv->actor)); */ + + /* gst_structure_set (structure, */ + /* "pointer_x", G_TYPE_DOUBLE, (gdouble) x, */ + /* "pointer_y", G_TYPE_DOUBLE, (gdouble) y, NULL); */ } event = gst_event_new_navigation (structure); @@ -1658,16 +1693,37 @@ clutter_gst_navigation_interface_init (GstNavigationInterface * iface) iface->send_event = clutter_gst_navigation_send_event; } +/** + * clutter_gst_video_sink_get_frame: + * @self: a #ClutterGstVideoSink + * + * Retrieves the last frame from @self to present on the screen. + * + * Return value: (transfer none): A CoglHandle on a CoglPipeline + */ +ClutterGstFrame * +clutter_gst_video_sink_get_frame (ClutterGstVideoSink *self) +{ + ClutterGstVideoSinkPrivate *priv; + + g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_SINK (self), COGL_INVALID_HANDLE); + + priv = self->priv; + + return priv->cogl_pipeline; +} + + gboolean _internal_plugin_init (GstPlugin * plugin) { gboolean ret = gst_element_register (plugin, - "cluttersink", - GST_RANK_PRIMARY, - CLUTTER_GST_TYPE_VIDEO_SINK); + "coglsink", + GST_RANK_PRIMARY, + CLUTTER_GST_TYPE_VIDEO_SINK); GST_DEBUG_CATEGORY_INIT (clutter_gst_video_sink_debug, - "cluttersink", 0, "clutter video sink"); + "coglsink", 0, "cogl video sink"); return ret; } diff --git a/clutter-gst/clutter-gst-video-sink.h b/clutter-gst/clutter-gst-video-sink.h index 92eb3e4..4726267 100644 --- a/clutter-gst/clutter-gst-video-sink.h +++ b/clutter-gst/clutter-gst-video-sink.h @@ -106,6 +106,8 @@ struct _ClutterGstVideoSinkClass GType clutter_gst_video_sink_get_type (void) G_GNUC_CONST; +CoglHandle clutter_gst_video_sink_get_frame (ClutterGstVideoSink *self); + G_END_DECLS #endif /* __CLUTTER_GST_VIDEO_SINK_H__ */ diff --git a/clutter-gst/clutter-gst.h b/clutter-gst/clutter-gst.h index cb657f9..b3790dd 100644 --- a/clutter-gst/clutter-gst.h +++ b/clutter-gst/clutter-gst.h @@ -33,12 +33,11 @@ #include "clutter-gst-types.h" #include "clutter-gst-enum-types.h" #include "clutter-gst-actor.h" -#include "clutter-gst-video-sink.h" -#include "clutter-gst-video-actor.h" #include "clutter-gst-camera-device.h" -#include "clutter-gst-camera-actor.h" +#include "clutter-gst-camera.h" #include "clutter-gst-util.h" #include "clutter-gst-version.h" #include "clutter-gst-player.h" +#include "clutter-gst-playback.h" #endif /* __CLUTTER_GST_H__ */ diff --git a/configure.ac b/configure.ac index 690f8b6..cc55ef6 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,7 @@ AC_SUBST([CLUTTER_GST_RELEASE_STATUS], [clutter_gst_release_status]) # pkg-config requirements GLIB_REQ_VERSION=2.18.0 COGL_REQ_VERSION=1.10.0 +COGL_GST_REQ_VERSION=1.99.0 CLUTTER_REQ_VERSION=1.6.0 GSTREAMER_REQ_VERSION=1.0.0 GST_PLUGINS_BAD_REQ_VERSION=1.0.0 @@ -142,6 +143,7 @@ dnl ======================================================================== PKG_CHECK_MODULES(CLUTTER_GST, [clutter-1.0 >= $CLUTTER_REQ_VERSION cogl-1.0 >= $COGL_REQ_VERSION + cogl-gst >= $COGL_GST_REQ_VERSION gio-2.0 >= $GLIB_REQ_VERSION]) dnl ======================================================================== diff --git a/examples/camera-player.c b/examples/camera-player.c index 1b816a7..e02ed5e 100644 --- a/examples/camera-player.c +++ b/examples/camera-player.c @@ -33,6 +33,7 @@ typedef struct _CameraApp { ClutterActor *stage; ClutterActor *camera_actor; + ClutterGstCamera *camera_player; const GPtrArray *camera_devices; guint selected_camera_device; gboolean decrease_selected; @@ -57,24 +58,20 @@ update_gamma (CameraApp *app) { gdouble min, max, cur; - if (!clutter_gst_camera_actor_supports_gamma_correction ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + if (!clutter_gst_camera_supports_gamma_correction (app->camera_player)) { g_print ("Cannot update gamma, not supported\n"); return; } - if (!clutter_gst_camera_actor_get_gamma_range ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - &min, &max, NULL)) + if (!clutter_gst_camera_get_gamma_range (app->camera_player, + &min, &max, NULL)) { g_print ("Cannot update gamma, unable to get allowed range\n"); return; } - if (!clutter_gst_camera_actor_get_gamma ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - &cur)) + if (!clutter_gst_camera_get_gamma (app->camera_player, &cur)) { g_print ("Cannot update gamma, unable to get current value\n"); return; @@ -98,9 +95,7 @@ update_gamma (CameraApp *app) } g_print ("\tnew value: %0.2f\n", cur); - clutter_gst_camera_actor_set_gamma ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - cur); + clutter_gst_camera_set_gamma (app->camera_player, cur); } static void @@ -109,8 +104,7 @@ update_color_balance (CameraApp *app, { gdouble min, max, cur; - if (!clutter_gst_camera_actor_supports_color_balance ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + if (!clutter_gst_camera_supports_color_balance (app->camera_player)) { g_print ("Cannot update color balance property %s, " "not supported\n", @@ -118,9 +112,8 @@ update_color_balance (CameraApp *app, return; } - if (!clutter_gst_camera_actor_get_color_balance_property_range ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - property, &min, &max, NULL)) + if (!clutter_gst_camera_get_color_balance_property_range (app->camera_player, + property, &min, &max, NULL)) { g_print ("Cannot update color balance property %s, " "unable to get allowed range\n", @@ -128,9 +121,8 @@ update_color_balance (CameraApp *app, return; } - if (!clutter_gst_camera_actor_get_color_balance_property ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - property, &cur)) + if (!clutter_gst_camera_get_color_balance_property (app->camera_player, + property, &cur)) { g_print ("Cannot update color balance property %s, " "unable to get current value\n", @@ -156,9 +148,8 @@ update_color_balance (CameraApp *app, } g_print ("\tnew value: %0.2f\n", cur); - clutter_gst_camera_actor_set_color_balance_property ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - property, cur); + clutter_gst_camera_set_color_balance_property (app->camera_player, + property, cur); } static gboolean @@ -214,8 +205,7 @@ input_cb (ClutterStage *stage, g_print ("Selecting device %s (node=%s)\n", clutter_gst_camera_device_get_name (device), clutter_gst_camera_device_get_node (device)); - clutter_gst_camera_actor_set_camera_device ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), device); + clutter_gst_camera_set_camera_device (app->camera_player, device); break; } @@ -229,16 +219,13 @@ input_cb (ClutterStage *stage, gchar *filename; static guint photos_cnt = 0; - if (clutter_gst_camera_actor_is_recording_video ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + if (clutter_gst_camera_is_recording_video (app->camera_player)) { g_print ("Stopping video recording\n"); - clutter_gst_camera_actor_stop_video_recording ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor)); + clutter_gst_camera_stop_video_recording (app->camera_player); } - else if (!clutter_gst_camera_actor_is_ready_for_capture ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + else if (!clutter_gst_camera_is_ready_for_capture (app->camera_player)) g_print ("Unable to record video as the camera is not ready for capture\n"); else { @@ -246,9 +233,8 @@ input_cb (ClutterStage *stage, filename = g_strdup_printf ("camera-video-%d.ogv", photos_cnt++); - clutter_gst_camera_actor_start_video_recording ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - filename); + clutter_gst_camera_start_video_recording (app->camera_player, + filename); g_free (filename); } break; @@ -259,11 +245,9 @@ input_cb (ClutterStage *stage, gchar *filename; static guint photos_cnt = 0; - if (clutter_gst_camera_actor_is_recording_video ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + if (clutter_gst_camera_is_recording_video (app->camera_player)) g_print ("Unable to take photo as the camera is recording video\n"); - else if (!clutter_gst_camera_actor_is_ready_for_capture ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor))) + else if (!clutter_gst_camera_is_ready_for_capture (app->camera_player)) g_print ("Unable to take photo as the camera is not ready for capture\n"); else { @@ -271,9 +255,8 @@ input_cb (ClutterStage *stage, filename = g_strdup_printf ("camera-photo-%d.jpg", photos_cnt++); - clutter_gst_camera_actor_take_photo ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), - filename); + clutter_gst_camera_take_photo (app->camera_player, + filename); g_free (filename); } break; @@ -289,8 +272,7 @@ input_cb (ClutterStage *stage, { g_print ("ERROR: Unable to create 'dicetv' element, cannot set filter\n"); } - ret = clutter_gst_camera_actor_set_filter ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), filter); + ret = clutter_gst_camera_set_filter (app->camera_player, filter); if (ret) g_print ("Filter set successfully\n"); else @@ -300,8 +282,7 @@ input_cb (ClutterStage *stage, } case CLUTTER_r: - clutter_gst_camera_actor_remove_filter ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor)); + clutter_gst_camera_remove_filter (app->camera_player); break; default: @@ -316,7 +297,7 @@ input_cb (ClutterStage *stage, } static void -ready_for_capture (ClutterGstCameraActor *camera_actor, +ready_for_capture (ClutterGstCamera *camera_player, gboolean ready) { if (ready) @@ -324,13 +305,13 @@ ready_for_capture (ClutterGstCameraActor *camera_actor, } static void -photo_saved (ClutterGstCameraActor *camera_actor) +photo_saved (ClutterGstCamera *camera_player) { g_print ("Photo saved!\n"); } static void -video_saved (ClutterGstCameraActor *camera_actor) +video_saved (ClutterGstCamera *camera_player) { g_print ("Video saved!\n"); } @@ -406,16 +387,16 @@ main (int argc, char *argv[]) app = g_new0(CameraApp, 1); app->stage = stage; - app->camera_actor = clutter_gst_camera_actor_new (); + app->camera_actor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); - if (app->camera_actor == NULL) + app->camera_player = clutter_gst_camera_new (); + if (app->camera_player == NULL) { - g_error ("failed to create camera_actor"); + g_error ("failed to create camera player"); return EXIT_FAILURE; } - app->camera_devices = clutter_gst_camera_actor_get_camera_devices ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor)); + app->camera_devices = clutter_gst_camera_get_camera_devices (app->camera_player); if (!app->camera_devices) { g_error ("no suitable camera device available"); @@ -435,13 +416,13 @@ main (int argc, char *argv[]) } app->selected_camera_device = 0; - g_signal_connect (app->camera_actor, "ready-for-capture", + g_signal_connect (app->camera_player, "ready-for-capture", G_CALLBACK (ready_for_capture), app); - g_signal_connect (app->camera_actor, "photo-saved", + g_signal_connect (app->camera_player, "photo-saved", G_CALLBACK (photo_saved), app); - g_signal_connect (app->camera_actor, "video-saved", + g_signal_connect (app->camera_player, "video-saved", G_CALLBACK (video_saved), app); /* Handle it ourselves so can scale up for fullscreen better */ @@ -457,8 +438,7 @@ main (int argc, char *argv[]) /* Hook up other events */ g_signal_connect (stage, "event", G_CALLBACK (input_cb), app); - clutter_gst_camera_actor_set_playing ( - CLUTTER_GST_CAMERA_ACTOR (app->camera_actor), TRUE); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (app->camera_player), TRUE); clutter_actor_show (stage); diff --git a/examples/video-player.c b/examples/video-player.c index 1d1ebbf..69c9364 100644 --- a/examples/video-player.c +++ b/examples/video-player.c @@ -41,6 +41,7 @@ typedef struct _VideoApp ClutterActor *stage; ClutterActor *vactor; + ClutterGstPlayback *player; ClutterActor *control; ClutterActor *control_bg; @@ -163,16 +164,16 @@ toggle_pause_state (VideoApp *app) if (app->paused) { - clutter_gst_player_set_playing (CLUTTER_GST_PLAYER(app->vactor), - TRUE); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (app->player), + TRUE); app->paused = FALSE; clutter_actor_hide (app->control_play); clutter_actor_show (app->control_pause); } else { - clutter_gst_player_set_playing (CLUTTER_GST_PLAYER(app->vactor), - FALSE); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (app->player), + FALSE); app->paused = TRUE; clutter_actor_hide (app->control_pause); clutter_actor_show (app->control_play); @@ -232,8 +233,7 @@ input_cb (ClutterStage *stage, progress = (gdouble) dist / SEEK_W; - clutter_gst_player_set_progress (CLUTTER_GST_PLAYER (app->vactor), - progress); + clutter_gst_playback_set_progress (app->player, progress); } } handled = TRUE; @@ -302,10 +302,10 @@ input_cb (ClutterStage *stage, } static void -size_change (ClutterActor *actor, - gint base_width, - gint base_height, - VideoApp *app) +size_change (ClutterGstPlayer *player, + gint base_width, + gint base_height, + VideoApp *app) { ClutterActor *stage = app->stage; gfloat new_x, new_y, new_width, new_height; @@ -317,7 +317,9 @@ size_change (ClutterActor *actor, /* base_width and base_height are the actual dimensions of the buffers before * taking the pixel aspect ratio into account. We need to get the actual * size of the actor to display */ - clutter_actor_get_size (actor, &frame_width, &frame_height); + /* clutter_actor_get_size (app->vactor, &frame_width, &frame_height); */ + frame_width = base_width; + frame_height = base_height; new_height = (frame_height * stage_width) / frame_width; if (new_height <= stage_height) @@ -336,8 +338,8 @@ size_change (ClutterActor *actor, new_y = 0; } - clutter_actor_set_position (actor, new_x, new_y); - clutter_actor_set_size (actor, new_width, new_height); + clutter_actor_set_position (app->vactor, new_x, new_y); + clutter_actor_set_size (app->vactor, new_width, new_height); } static void @@ -370,8 +372,7 @@ tick (GObject *object, GParamSpec *pspec, VideoApp *app) { - ClutterGstPlayer *vactor = CLUTTER_GST_PLAYER (object); - gdouble progress = clutter_gst_player_get_progress (vactor); + gdouble progress = clutter_gst_playback_get_progress (app->player); clutter_actor_set_size (app->control_seekbar, progress * SEEK_W, @@ -384,7 +385,7 @@ on_video_actor_eos (ClutterGstPlayer *player, { if (opt_loop) { - clutter_gst_player_set_progress (player, 0.0); + clutter_gst_playback_set_progress (CLUTTER_GST_PLAYBACK (player), 0.0); clutter_gst_player_set_playing (player, TRUE); } } @@ -509,7 +510,10 @@ main (int argc, char *argv[]) app = g_new0(VideoApp, 1); app->stage = stage; - app->vactor = clutter_gst_video_actor_new (); + app->vactor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); + app->player = clutter_gst_playback_new (); + + clutter_gst_actor_set_player (CLUTTER_GST_ACTOR (app->vactor), CLUTTER_GST_PLAYER (app->player)); if (app->vactor == NULL) g_error("failed to create vactor"); @@ -519,10 +523,10 @@ main (int argc, char *argv[]) * goes to the key frame position that can be quite far from where you * clicked. Using the ACCURATE flag tells playbin2 to seek to the actual * frame */ - clutter_gst_player_set_seek_flags (CLUTTER_GST_PLAYER (app->vactor), - CLUTTER_GST_SEEK_FLAG_ACCURATE); + clutter_gst_playback_set_seek_flags (app->player, + CLUTTER_GST_SEEK_FLAG_ACCURATE); - g_signal_connect (app->vactor, + g_signal_connect (app->player, "eos", G_CALLBACK (on_video_actor_eos), app); @@ -532,13 +536,9 @@ main (int argc, char *argv[]) g_print ("Remote media detected, setting up buffering\n"); /* configure to 10 seconds of buffer duration */ - clutter_gst_player_set_buffer_duration ( - CLUTTER_GST_PLAYER (app->vactor), - 10 * GST_SECOND); - clutter_gst_player_set_buffering_mode ( - CLUTTER_GST_PLAYER (app->vactor), - CLUTTER_GST_BUFFERING_MODE_STREAM); - g_signal_connect (app->vactor, + clutter_gst_playback_set_buffer_duration (app->player, 10 * GST_SECOND); + clutter_gst_playback_set_buffering_mode (app->player, CLUTTER_GST_BUFFERING_MODE_STREAM); + g_signal_connect (app->player, "notify::buffer-fill", G_CALLBACK (on_video_actor_notify_buffer_fill), app); @@ -557,20 +557,20 @@ main (int argc, char *argv[]) NULL); /* Handle it ourselves so can scale up for fullscreen better */ - g_signal_connect_after (app->vactor, + g_signal_connect_after (app->player, "size-change", G_CALLBACK (size_change), app); /* Load up out video actor */ - clutter_gst_player_set_uri (CLUTTER_GST_PLAYER (app->vactor), uri); + clutter_gst_playback_set_filename (app->player, uri); - if (clutter_gst_player_is_live_media (CLUTTER_GST_PLAYER (app->vactor))) + if (clutter_gst_playback_is_live_media (app->player)) g_print ("Playing live media\n"); else g_print ("Playing non-live media\n"); /* Set up things so that a visualisation is played if there's no video */ - pipe = clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (app->vactor)); + pipe = clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (app->player)); if (!pipe) g_error ("Unable to get gstreamer pipeline!\n"); @@ -659,11 +659,11 @@ main (int argc, char *argv[]) /* Hook up other events */ g_signal_connect (stage, "event", G_CALLBACK (input_cb), app); - g_signal_connect (app->vactor, + g_signal_connect (app->player, "notify::progress", G_CALLBACK (tick), app); - clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (app->vactor), TRUE); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (app->player), TRUE); clutter_actor_show (stage); diff --git a/examples/video-sink.c b/examples/video-sink.c index 82030d7..9fc7ab6 100644 --- a/examples/video-sink.c +++ b/examples/video-sink.c @@ -29,10 +29,10 @@ #include <clutter-gst/clutter-gst.h> void -size_change (ClutterActor *actor, - gint width, - gint height, - gpointer user_data) +size_change (ClutterGstPlayer *player, + gint width, + gint height, + ClutterActor *actor) { ClutterActor *stage; gfloat new_x, new_y, new_width, new_height; @@ -68,14 +68,14 @@ size_change (ClutterActor *actor, int main (int argc, char *argv[]) { - ClutterTimeline *timeline; - ClutterActor *stage; - ClutterActor *actor; - GstPipeline *pipeline; - GstElement *src; - GstElement *warp; - GstElement *colorspace; - GstElement *sink; + ClutterTimeline *timeline; + ClutterActor *stage; + ClutterActor *actor; + GstElement *src; + GstElement *warp; + GstElement *bin; + GstElement *pipeline; + ClutterGstPlayback *player; if (argc < 1) { @@ -98,24 +98,23 @@ main (int argc, char *argv[]) actor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); - g_signal_connect (actor, - "size-change", - G_CALLBACK (size_change), NULL); - /* Set up pipeline */ - pipeline = GST_PIPELINE (gst_pipeline_new (NULL)); + player = clutter_gst_playback_new (); + pipeline = clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (player)); + + g_signal_connect (player, "size-change", + G_CALLBACK (size_change), actor); src = gst_element_factory_make ("videotestsrc", NULL); warp = gst_element_factory_make ("warptv", NULL); - colorspace = gst_element_factory_make ("videoconvert", NULL); - sink = gst_element_factory_make ("cluttersink", NULL); - g_object_set (sink, "actor", actor, NULL); + bin = gst_bin_new ("video-test-source"); + + gst_bin_add_many (GST_BIN (bin), src, warp, NULL); + gst_element_link_many (src, warp, NULL); - // g_object_set (src , "pattern", 10, NULL); + g_object_set (pipeline, "source", bin); - gst_bin_add_many (GST_BIN (pipeline), src, warp, colorspace, sink, NULL); - gst_element_link_many (src, warp, colorspace, sink, NULL); - gst_element_set_state (GST_ELEMENT(pipeline), GST_STATE_PLAYING); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (player), TRUE); /* start the timeline */ clutter_timeline_start (timeline); diff --git a/tests/test-start-stop.c b/tests/test-start-stop.c index 5908e4e..d9b9e7e 100644 --- a/tests/test-start-stop.c +++ b/tests/test-start-stop.c @@ -76,17 +76,17 @@ gboolean test (gpointer data) { static int count = 1; - static ClutterGstPlayer *player = NULL; + static ClutterGstPlayback *player = NULL; static char *uri[2] = {NULL, NULL}; const char *playing_uri = NULL; /* Check until we get video playing */ - if (!clutter_gst_player_is_playing (CLUTTER_GST_PLAYER (data))) + if (!clutter_gst_player_get_playing (CLUTTER_GST_PLAYER (data))) return TRUE; - if (CLUTTER_GST_PLAYER (data) != player) + if (CLUTTER_GST_PLAYBACK (data) != player) { - player = CLUTTER_GST_PLAYER (data); + player = CLUTTER_GST_PLAYBACK (data); count = 1; g_free(uri[0]); uri[0] = NULL; @@ -94,25 +94,25 @@ test (gpointer data) uri[1] = NULL; } - clutter_gst_player_set_filename (player, video_files[count & 1]); + clutter_gst_playback_set_filename (player, video_files[count & 1]); g_print ("playing %s\n", video_files[count & 1]); if (uri[count & 1] == NULL) { - uri[count & 1] = g_strdup (clutter_gst_player_get_uri (player)); + uri[count & 1] = g_strdup (clutter_gst_playback_get_uri (player)); g_assert (uri[count & 1] != NULL); } /* See if it's still playing */ - g_assert (clutter_gst_player_is_playing (player)); + g_assert (clutter_gst_player_get_playing (CLUTTER_GST_PLAYER (player))); /* See if it's already change to play correct file */ - playing_uri = clutter_gst_player_get_uri (player); + playing_uri = clutter_gst_playback_get_uri (player); g_assert_cmpstr (playing_uri, ==, uri[count & 1]); if (count ++ > 10) { - clutter_gst_player_set_playing (player, FALSE); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (player), FALSE); clutter_main_quit (); return FALSE; } @@ -124,10 +124,11 @@ test (gpointer data) int main (int argc, char *argv[]) { - ClutterInitError error; - ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 }; - ClutterActor *stage = NULL; - ClutterActor *video = NULL; + ClutterInitError error; + ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 }; + ClutterActor *stage = NULL; + ClutterActor *video = NULL; + ClutterGstPlayback *player = NULL; if (argc < 3) { @@ -144,23 +145,27 @@ main (int argc, char *argv[]) stage = clutter_stage_new (); clutter_actor_set_background_color (stage, &stage_color); - video = clutter_gst_video_actor_new (); - g_assert (CLUTTER_GST_IS_VIDEO_ACTOR(video)); + player = clutter_gst_playback_new (); + + video = /* clutter_gst_actor_new () */ g_object_new (CLUTTER_GST_TYPE_ACTOR, + NULL); + g_assert (CLUTTER_GST_IS_ACTOR (video)); + clutter_gst_actor_set_player (CLUTTER_GST_ACTOR (video), CLUTTER_GST_PLAYER (player)); + clutter_actor_add_child (stage, video); g_signal_connect (video, "size-change", G_CALLBACK(size_change), stage); - g_signal_connect (video, + g_signal_connect (player, "error", G_CALLBACK(on_error), stage); g_timeout_add (5000, test, video); - clutter_gst_player_set_filename (CLUTTER_GST_PLAYER (video), video_files[0]); - clutter_gst_player_set_audio_volume (CLUTTER_GST_PLAYER (video), 0.5); - clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (video), TRUE); + clutter_gst_playback_set_filename (player, video_files[0]); + clutter_gst_player_set_audio_volume (CLUTTER_GST_PLAYER (player), 0.5); + clutter_gst_player_set_playing (CLUTTER_GST_PLAYER (player), TRUE); - clutter_actor_add_child (stage, video); clutter_actor_show (stage); clutter_main (); diff --git a/tests/test-video-actor-new-unref-loop.c b/tests/test-video-actor-new-unref-loop.c index 5a611f2..942f56a 100644 --- a/tests/test-video-actor-new-unref-loop.c +++ b/tests/test-video-actor-new-unref-loop.c @@ -43,7 +43,7 @@ main (int argc, char *argv[]) for (i = 0; ; i++) { g_debug("VideoActor #%d", i); - vactor = clutter_gst_video_actor_new (); + vactor = g_object_new (CLUTTER_GST_TYPE_ACTOR, NULL); g_object_ref_sink (vactor); g_object_unref (vactor); } |