From 9afe83e6955321d33d42a4b94ed8ae04946ee5ad Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Mon, 12 Jun 2006 20:38:57 +0000 Subject: [PATCH] 2006-06-12 Matthew Allum * clutter/Makefile.am: * clutter/clutter-media.c: * clutter/clutter-media.h: * clutter/clutter-video-texture.c: * clutter/clutter-video-texture.h: * clutter/clutter.h: * examples/test-video.c: (main): * examples/video-cube.c: (main): Add new 'media' interface. Drop Totem based video playback code replace with newly rewritten supper Jorn based code. Clutter is now fully LGPL. * clutter/clutter-texture.c: (tile_dimension): Remove uneeded overlap code. --- ChangeLog | 17 + clutter/Makefile.am | 2 + clutter/clutter-label.c | 10 + clutter/clutter-media.c | 282 ++++ clutter/clutter-media.h | 124 ++ clutter/clutter-texture.c | 6 +- clutter/clutter-video-texture.c | 2288 +++++++++---------------------- clutter/clutter-video-texture.h | 201 +-- clutter/clutter.h | 1 + examples/test-video.c | 82 +- examples/video-cube.c | 9 +- 11 files changed, 1160 insertions(+), 1862 deletions(-) create mode 100644 clutter/clutter-media.c create mode 100644 clutter/clutter-media.h diff --git a/ChangeLog b/ChangeLog index 408de3210..c28d37010 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2006-06-12 Matthew Allum + + * clutter/Makefile.am: + * clutter/clutter-media.c: + * clutter/clutter-media.h: + * clutter/clutter-video-texture.c: + * clutter/clutter-video-texture.h: + * clutter/clutter.h: + * examples/test-video.c: (main): + * examples/video-cube.c: (main): + Add new 'media' interface. + Drop Totem based video playback code replace with newly + rewritten supper Jorn based code. Clutter is now fully LGPL. + + * clutter/clutter-texture.c: (tile_dimension): + Remove uneeded overlap code. + 2006-06-08 Iain Holmes * clutter/clutter-texture.c: diff --git a/clutter/Makefile.am b/clutter/Makefile.am index b1caab12a..00dcf8f25 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -7,6 +7,7 @@ BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES) source_h = clutter-keysyms.h \ clutter-util.h \ + clutter-media.h \ clutter-event.h \ clutter-color.h \ clutter-timeline.h \ @@ -65,6 +66,7 @@ CLEANFILES = $(BUILT_SOURCES) stamp-clutter-enum-types.h source_c = clutter-main.c \ clutter-util.c \ + clutter-media.c \ clutter-event.c \ clutter-color.c \ clutter-timeline.c \ diff --git a/clutter/clutter-label.c b/clutter/clutter-label.c index 4467cab84..83b406c27 100644 --- a/clutter/clutter-label.c +++ b/clutter/clutter-label.c @@ -143,6 +143,16 @@ clutter_label_make_pixbuf (ClutterLabel *label) g_object_unref (pixbuf); } +static void +clutter_label_allocate_coords (ClutterElement *element, + ClutterElementBox *box) +{ + + + +} + + static void clutter_label_set_property (GObject *object, guint prop_id, diff --git a/clutter/clutter-media.c b/clutter/clutter-media.c new file mode 100644 index 000000000..3885cbb50 --- /dev/null +++ b/clutter/clutter-media.c @@ -0,0 +1,282 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * + * 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 "config.h" + +#include "clutter-media.h" +#include "clutter-main.h" +#include "clutter-enum-types.h" +#include "clutter-private.h" /* for DBG */ + +static void clutter_media_base_init (gpointer g_class); + +GType +clutter_media_get_type (void) +{ + static GType media_type = 0; + + if (!media_type) + { + static const GTypeInfo media_info = + { + sizeof (ClutterMediaInterface), + clutter_media_base_init, + NULL, + }; + + media_type = g_type_register_static (G_TYPE_INTERFACE, "ClutterMedia", + &media_info, 0); + } + + return media_type; +} + +static void +clutter_media_base_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (!initialized) + { + initialized = TRUE; + + /* props */ + + g_object_interface_install_property + (g_iface, + g_param_spec_string + ("uri", + "URI", + "The loaded URI.", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boolean + ("playing", + "Playing", + "TRUE if playing.", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_int + ("position", + "Position", + "The position in the current stream in seconds.", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_double + ("volume", + "Volume", + "The audio volume.", + 0, 100, 50, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_boolean + ("can-seek", + "Can seek", + "TRUE if the current stream is seekable.", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_int + ("buffer-percent", + "Buffer percent", + "The percentage the current stream buffer is filled.", + 0, 100, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_object_interface_install_property + (g_iface, + g_param_spec_int + ("duration", + "Duration", + "The duration of the current stream in seconds.", + 0, G_MAXINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + /* signals */ + + g_signal_new ("metadata-available", + CLUTTER_TYPE_MEDIA, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterMediaInterface, + metadata_available), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + g_signal_new ("eos", + CLUTTER_TYPE_MEDIA, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterMediaInterface, + eos), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_signal_new ("error", + CLUTTER_TYPE_MEDIA, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterMediaInterface, + error), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + } +} + +void +clutter_media_set_uri (ClutterMedia *media, + const char *uri) +{ + g_return_if_fail (CLUTTER_IS_MEDIA(media)); + + CLUTTER_MEDIA_GET_INTERFACE (media)->set_uri (media, uri); +} + +const char* +clutter_media_get_uri (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), NULL); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_uri (media); +} + +void +clutter_media_set_playing (ClutterMedia *media, + gboolean playing) +{ + g_return_if_fail (CLUTTER_IS_MEDIA(media)); + + CLUTTER_MEDIA_GET_INTERFACE (media)->set_playing (media, playing); +} + +gboolean +clutter_media_get_playing (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), FALSE); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_playing (media); +} + +void +clutter_media_set_position (ClutterMedia *media, + int position) +{ + g_return_if_fail (CLUTTER_IS_MEDIA(media)); + + CLUTTER_MEDIA_GET_INTERFACE (media)->set_position (media, position); +} + +int +clutter_media_get_position (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_position (media); +} + +void +clutter_media_set_volume (ClutterMedia *media, + double volume) +{ + g_return_if_fail (CLUTTER_IS_MEDIA(media)); + + CLUTTER_MEDIA_GET_INTERFACE (media)->set_position (media, volume); +} + +double +clutter_media_get_volume (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0.0); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_volume (media); +} + +gboolean +clutter_media_get_can_seek (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), FALSE); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->can_seek (media); +} + +int +clutter_media_get_buffer_percent (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_buffer_percent (media); +} + +int +clutter_media_get_duration (ClutterMedia *media) +{ + g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0); + + return CLUTTER_MEDIA_GET_INTERFACE (media)->get_duration (media); +} + +/* helper funcs */ + +void +clutter_media_set_filename (ClutterMedia *media, const gchar *filename) +{ + gchar *uri; + + if (filename[0] != '/') + uri = g_strdup_printf ("file://%s/%s", g_get_current_dir (), filename); + else + uri = g_strdup_printf ("file://%s", filename); + + clutter_media_set_uri (media, uri); + + g_free(uri); +} diff --git a/clutter/clutter-media.h b/clutter/clutter-media.h new file mode 100644 index 000000000..293030b30 --- /dev/null +++ b/clutter/clutter-media.h @@ -0,0 +1,124 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _HAVE_CLUTTER_MEDIA_H +#define _HAVE_CLUTTER_MEDIA_H + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_MEDIA clutter_media_get_type() + +#define CLUTTER_MEDIA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + CLUTTER_TYPE_MEDIA, ClutterMedia)) + +#define CLUTTER_IS_MEDIA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + CLUTTER_TYPE_MEDIA)) + +#define CLUTTER_MEDIA_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ + CLUTTER_TYPE_MEDIA, ClutterMediaInterface)) + +typedef struct _ClutterMedia ClutterMedia; +typedef struct _ClutterMediaInterface ClutterMediaInterface; + +struct _ClutterMediaInterface +{ + GTypeInterface base_iface; + void (*set_uri) (ClutterMedia *media, + const char *uri); + const char *(*get_uri) (ClutterMedia *media); + void (*set_playing) (ClutterMedia *media, + gboolean playing); + gboolean (*get_playing) (ClutterMedia *media); + void (*set_position) (ClutterMedia *media, + int position); + int (*get_position) (ClutterMedia *media); + void (*set_volume) (ClutterMedia *media, + double volume); + double (*get_volume) (ClutterMedia *media); + gboolean (*can_seek) (ClutterMedia *media); + int (*get_buffer_percent) (ClutterMedia *media); + int (*get_duration) (ClutterMedia *media); + + /* signals */ + + void (* metadata_available) (ClutterMedia *media, + GstTagList *tag_list); + void (* eos) (ClutterMedia *media); + void (* error) (ClutterMedia *media, + GError *error); +}; + + +GType clutter_media_get_type (void); + +void +clutter_media_set_uri (ClutterMedia *media, + const char *uri); +const char * +clutter_media_get_uri (ClutterMedia *media); + +void +clutter_media_set_playing (ClutterMedia *media, + gboolean playing); + +gboolean +clutter_media_get_playing (ClutterMedia *media); + +void +clutter_media_set_position (ClutterMedia *media, + int position); + +int +clutter_media_get_position (ClutterMedia *media); + +void +clutter_media_set_volume (ClutterMedia *media, + double volume); + +double +clutter_media_get_volume (ClutterMedia *media); + +gboolean +clutter_media_get_can_seek (ClutterMedia *media); + +int +clutter_media_get_buffer_percent (ClutterMedia *media); + +int +clutter_media_get_duration (ClutterMedia *media); + +void +clutter_media_set_filename (ClutterMedia *media, + const gchar *filename); + +G_END_DECLS + +#endif diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 0c755e6da..bb0950563 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -40,9 +40,6 @@ G_DEFINE_TYPE (ClutterTexture, clutter_texture, CLUTTER_TYPE_ELEMENT); #define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV #endif -#define OVERLAP 0 /* Dont need as CLAMP_TO_EDGE */ - -/* FIXME: actually use */ typedef struct ClutterTextureTileDimention { gint pos, size, waste; @@ -144,8 +141,7 @@ tile_dimension (int to_fill, } else { - to_fill -= (size - OVERLAP); - pos += size - OVERLAP; + to_fill -= size; pos += size; while (size >= 2 * to_fill || size - to_fill > waste) size /= 2; } diff --git a/clutter/clutter-video-texture.c b/clutter/clutter-video-texture.c index a2fa630d5..f6575eb8b 100644 --- a/clutter/clutter-video-texture.c +++ b/clutter/clutter-video-texture.c @@ -1,4 +1,4 @@ -/* Heavily based on totems bacon-video-widget .. */ + #include "clutter-video-texture.h" #include "clutter-main.h" @@ -14,798 +14,643 @@ #include -static void -got_time_tick (GstElement *play, - gint64 time_nanos, - ClutterVideoTexture *video_texture); -static void -stop_play_pipeline (ClutterVideoTexture *video_texture); - -G_DEFINE_TYPE (ClutterVideoTexture, \ - clutter_video_texture, \ - CLUTTER_TYPE_TEXTURE); - -enum -{ - SIGNAL_ERROR, - SIGNAL_EOS, - SIGNAL_REDIRECT, - SIGNAL_TITLE_CHANGE, - SIGNAL_CHANNELS_CHANGE, - SIGNAL_TICK, - SIGNAL_GOT_METADATA, - SIGNAL_BUFFERING, - SIGNAL_SPEED_WARNING, - LAST_SIGNAL -}; - -/* Properties */ -enum -{ - PROP_0, - PROP_POSITION, - PROP_CURRENT_TIME, - PROP_STREAM_LENGTH, - PROP_PLAYING, - PROP_SEEKABLE, -}; - struct ClutterVideoTexturePrivate { - GstElement *play, *video_sink, *audio_sink; - gboolean has_video, has_audio; - gint64 stream_length; - gint64 current_time_nanos; - gint64 current_time; - float current_position; - gchar *mrl; - - gboolean got_redirect; - gint eos_id; - guint update_id; - - GstTagList *tagcache, *audiotags, *videotags; - - const GValue *movie_par; /* Movie pixel aspect ratio */ - gint video_fps_d, video_fps_n; - gint video_width, video_height; - ClutterVideoTextureAspectRatio ratio_type; - - GstMessageType ignore_messages_mask; - GstBus *bus; - gulong sig_bus_async; + GstElement *playbin; + char *uri; + gboolean can_seek; + int buffer_percent; + int duration; + gboolean force_aspect_ratio; + guint tick_timeout_id; }; -static int cvt_signals[LAST_SIGNAL] = { 0 }; +enum { + PROP_0, + /* ClutterMedia proprs */ + PROP_URI, + PROP_PLAYING, + PROP_POSITION, + PROP_VOLUME, + PROP_CAN_SEEK, + PROP_BUFFER_PERCENT, + PROP_DURATION, -GQuark -clutter_video_texture_error_quark (void) + /* Extra texture props */ + PROP_FORCE_ASPECT_RATIO +}; + + +#define TICK_TIMEOUT 0.5 + +static void clutter_media_init (ClutterMediaInterface *iface); + +static gboolean tick_timeout (ClutterVideoTexture *video_texture); + +G_DEFINE_TYPE_EXTENDED (ClutterVideoTexture, \ + clutter_video_texture, \ + CLUTTER_TYPE_TEXTURE, \ + 0, \ + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MEDIA, \ + clutter_media_init)); + +/* Interface implementation */ + +static void +set_uri (ClutterMedia *media, + const char *uri) { - return g_quark_from_static_string ("clutter-video-texture-error-quark"); + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + GstState state, pending; + + g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); + + priv = video_texture->priv; + + if (!priv->playbin) + return; + + if (priv->uri) + g_free (priv->uri); + + 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 * 1000, + (GSourceFunc) tick_timeout, + video_texture); + } + } + else + { + priv->uri = NULL; + + if (priv->tick_timeout_id > 0) + { + g_source_remove (priv->tick_timeout_id); + priv->tick_timeout_id = 0; + } + } + + priv->can_seek = FALSE; + priv->duration = 0; + + gst_element_get_state (priv->playbin, &state, &pending, 0); + + if (pending) + state = pending; + + gst_element_set_state (priv->playbin, GST_STATE_NULL); + + g_object_set (priv->playbin, + "uri", uri, + NULL); + + /** + * Restore state. + **/ + if (uri) + gst_element_set_state (priv->playbin, state); + + /** + * Emit notififications for all these to make sure UI is not showing + * any properties of the old URI. + **/ + g_object_notify (G_OBJECT (video_texture), "uri"); + g_object_notify (G_OBJECT (video_texture), "can-seek"); + g_object_notify (G_OBJECT (video_texture), "duration"); + g_object_notify (G_OBJECT (video_texture), "position"); + } -/* This is a hack to avoid doing poll_for_state_change() indirectly - * from the bus message callback (via EOS => totem => close => wait for READY) - * and deadlocking there. We need something like a - * gst_bus_set_auto_flushing(bus, FALSE) ... */ -static gboolean -signal_eos_delayed (gpointer user_data) +static const char * +get_uri (ClutterMedia *media) { - ClutterVideoTexture *video_texture = (ClutterVideoTexture*)user_data; + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; - g_signal_emit (video_texture, cvt_signals[SIGNAL_EOS], 0, NULL); + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), NULL); - video_texture->priv->eos_id = 0; + priv = video_texture->priv; - return FALSE; + return priv->uri; +} + +static void +set_playing (ClutterMedia *media, + gboolean playing) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + + g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); + + priv = video_texture->priv; + + if (!priv->playbin) + return; + + if (priv->uri) + { + GstState state; + + if (playing) + state = GST_STATE_PLAYING; + else + state = GST_STATE_PAUSED; + + gst_element_set_state (video_texture->priv->playbin, state); + } + else + { + if (playing) + g_warning ("Tried to play, but no URI is loaded."); + } + + g_object_notify (G_OBJECT (video_texture), "playing"); + g_object_notify (G_OBJECT (video_texture), "position"); } static gboolean -query_timeout (ClutterVideoTexture *video_texture) +get_playing (ClutterMedia *media) { - ClutterVideoTexturePrivate *priv; - GstFormat fmt = GST_FORMAT_TIME; - gint64 prev_len = -1, pos = -1, len = -1; + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + GstState state, pending; + + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); + + priv = video_texture->priv; + + if (!priv->playbin) + return FALSE; + + gst_element_get_state (priv->playbin, &state, &pending, 0); + + if (pending) + return (pending == GST_STATE_PLAYING); + else + return (state == GST_STATE_PLAYING); +} + +static void +set_position (ClutterMedia *media, + int position) /* seconds */ +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + + g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); + + priv = video_texture->priv; + + if (!priv->playbin) + return; + + gst_element_seek (priv->playbin, + 1.0, + GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, + position * GST_SECOND, + 0, 0); +} + +static int +get_position (ClutterMedia *media) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + GstQuery *query; + gint64 position; + + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); + + priv = video_texture->priv; + + if (!priv->playbin) + return -1; + + query = gst_query_new_position (GST_FORMAT_TIME); + + if (gst_element_query (priv->playbin, query)) + gst_query_parse_position (query, NULL, &position); + else + position = 0; + + gst_query_unref (query); + + return (position / GST_SECOND); +} + +static void +set_volume (ClutterMedia *media, + double volume) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + + g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); + // g_return_if_fail (volume >= 0.0 && volume <= GST_VOL_MAX); priv = video_texture->priv; - /* check length/pos of stream */ - prev_len = priv->stream_length; - - if (gst_element_query_duration (priv->play, &fmt, &len)) - { - if (len != -1 && fmt == GST_FORMAT_TIME) - { - priv->stream_length = len / GST_MSECOND; - if (priv->stream_length != prev_len) - { - g_signal_emit (video_texture, - cvt_signals[SIGNAL_GOT_METADATA], 0, NULL); - } - } - } - else - { - CLUTTER_DBG ("could not get duration"); - } - - if (gst_element_query_position (priv->play, &fmt, &pos)) - { - if (pos != -1 && fmt == GST_FORMAT_TIME) - { - got_time_tick (GST_ELEMENT (priv->play), pos, video_texture); - } - } - else - CLUTTER_DBG ("could not get position"); - - return TRUE; -} - -static void -got_video_size (ClutterVideoTexture *video_texture) -{ - GstMessage *msg; - - g_return_if_fail (video_texture != NULL); - - /* Do we even care about this info as comes from texture sizing ? */ - CLUTTER_DBG("%ix%i", - video_texture->priv->video_width, - video_texture->priv->video_height); - - msg = gst_message_new_application - (GST_OBJECT (video_texture->priv->play), - gst_structure_new ("video-size", - "width", G_TYPE_INT, - video_texture->priv->video_width, - "height", G_TYPE_INT, - video_texture->priv->video_height, NULL)); - - gst_element_post_message (video_texture->priv->play, msg); -} - - -static void -caps_set (GObject *obj, - GParamSpec *pspec, - ClutterVideoTexture *video_texture) -{ - ClutterVideoTexturePrivate *priv; - GstPad *pad = GST_PAD (obj); - GstStructure *s; - GstCaps *caps; - - priv = video_texture->priv; - - if (!(caps = gst_pad_get_negotiated_caps (pad))) + if (!priv->playbin) return; - - /* Get video decoder caps */ - s = gst_caps_get_structure (caps, 0); - - /* Again do we even need this - sizing info from texture signal.. */ - - if (s) - { - /* We need at least width/height and framerate */ - if (!(gst_structure_get_fraction (s, "framerate", - &priv->video_fps_n, - &priv->video_fps_d) - && - gst_structure_get_int (s, "width", &priv->video_width) && - gst_structure_get_int (s, "height", &priv->video_height))) - return; - - /* Get the movie PAR if available */ - priv->movie_par = gst_structure_get_value (s, "pixel-aspect-ratio"); - - /* Now set for real */ - clutter_video_texture_set_aspect_ratio (video_texture, priv->ratio_type); - } - - gst_caps_unref (caps); + + g_object_set (G_OBJECT (video_texture->priv->playbin), + "volume", volume, + NULL); + + g_object_notify (G_OBJECT (video_texture), "volume"); } -static void -parse_stream_info (ClutterVideoTexture *video_texture) +static double +get_volume (ClutterMedia *media) { - ClutterVideoTexturePrivate *priv; - GList *streaminfo = NULL; - GstPad *videopad = NULL; + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); + ClutterVideoTexturePrivate *priv; + double volume; + + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), 0.0); priv = video_texture->priv; + + if (!priv->playbin) + return 0.0; - g_object_get (priv->play, "stream-info", &streaminfo, NULL); + g_object_get (priv->playbin, + "volume", &volume, + NULL); + + return volume; +} - streaminfo = g_list_copy (streaminfo); +static gboolean +can_seek (ClutterMedia *media) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); - g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL); + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); + + return video_texture->priv->can_seek; +} - for ( ; streaminfo != NULL; streaminfo = streaminfo->next) - { - GObject *info = streaminfo->data; - gint type; - GParamSpec *pspec; - GEnumValue *val; +static int +get_buffer_percent (ClutterMedia *media) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); - if (!info) - continue; + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); + + return video_texture->priv->buffer_percent; +} - g_object_get (info, "type", &type, NULL); +static int +get_duration (ClutterMedia *media) +{ + ClutterVideoTexture *video_texture = CLUTTER_VIDEO_TEXTURE(media); - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info), "type"); - val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type); + g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); - if (!g_strcasecmp (val->value_nick, "audio")) - { - priv->has_audio = TRUE; - } - else if (!g_strcasecmp (val->value_nick, "video")) - { - priv->has_video = TRUE; - - if (!videopad) - g_object_get (info, "object", &videopad, NULL); - } - } - - if (videopad) - { - GstCaps *caps; - - if ((caps = gst_pad_get_negotiated_caps (videopad))) - { - caps_set (G_OBJECT (videopad), NULL, video_texture); - gst_caps_unref (caps); - } - - g_signal_connect (videopad, "notify::caps", - G_CALLBACK (caps_set), video_texture); - } - - g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL); - g_list_free (streaminfo); + return video_texture->priv->duration; } static void -handle_element_message (ClutterVideoTexture *video_texture, GstMessage *msg) +clutter_media_init (ClutterMediaInterface *iface) { - const gchar *type_name = NULL; - gchar *src_name; - - CLUTTER_MARK(); - - src_name = gst_object_get_name (msg->src); - - if (msg->structure) - type_name = gst_structure_get_name (msg->structure); - - if (type_name == NULL) - goto unhandled; - - if (strcmp (type_name, "redirect") == 0) - { - const gchar *new_location; - - new_location = gst_structure_get_string (msg->structure, "new-location"); - - CLUTTER_DBG ("Got redirect to '%s'", GST_STR_NULL (new_location)); - - if (new_location && *new_location) - { - g_signal_emit (video_texture, - cvt_signals[SIGNAL_REDIRECT], - 0, - new_location); - goto done; - } - } - - unhandled: - CLUTTER_DBG ("Unhandled element message '%s' from element '%s'", - GST_STR_NULL (type_name), src_name); - done: - g_free (src_name); + iface->set_uri = set_uri; + iface->get_uri = get_uri; + iface->set_playing = set_playing; + iface->get_playing = get_playing; + iface->set_position = set_position; + iface->get_position = get_position; + iface->set_volume = set_volume; + iface->get_volume = get_volume; + iface->can_seek = can_seek; + iface->get_buffer_percent = get_buffer_percent; + iface->get_duration = get_duration; } static void -handle_application_message (ClutterVideoTexture *video_texture, - GstMessage *msg) +clutter_video_texture_dispose (GObject *object) { - const gchar *msg_name; + ClutterVideoTexture *self; + ClutterVideoTexturePrivate *priv; - msg_name = gst_structure_get_name (msg->structure); + self = CLUTTER_VIDEO_TEXTURE(object); + priv = self->priv; - g_return_if_fail (msg_name != NULL); + /* FIXME: flush an errors off bus ? */ + /* gst_bus_set_flushing (priv->bus, TRUE); */ - CLUTTER_DBG ("Handling application message"); - - if (strcmp (msg_name, "notify-streaminfo") == 0) + if (priv->playbin) { - g_signal_emit (video_texture, cvt_signals[SIGNAL_GOT_METADATA], 0, NULL); - g_signal_emit (video_texture, cvt_signals[SIGNAL_CHANNELS_CHANGE], 0); + gst_element_set_state (priv->playbin, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (priv->playbin)); + priv->playbin = NULL; } - else if (strcmp (msg_name, "video-size") == 0) - { - g_signal_emit (video_texture, cvt_signals[SIGNAL_GOT_METADATA], 0, NULL); - CLUTTER_DBG("Got video size"); + if (priv->tick_timeout_id > 0) + { + g_source_remove (priv->tick_timeout_id); + priv->tick_timeout_id = 0; } - else - CLUTTER_DBG ("Unhandled application message %s", msg_name); + + G_OBJECT_CLASS (clutter_video_texture_parent_class)->dispose (object); } static void -bus_message_cb (GstBus *bus, GstMessage *message, gpointer data) +clutter_video_texture_finalize (GObject *object) { - ClutterVideoTexturePrivate *priv; - ClutterVideoTexture *video_texture = (ClutterVideoTexture*)data; - GstMessageType msg_type; + ClutterVideoTexture *self; + ClutterVideoTexturePrivate *priv; - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE(video_texture)); + self = CLUTTER_VIDEO_TEXTURE(object); + priv = self->priv; - priv = video_texture->priv; + if (priv->uri) + g_free(priv->uri); - msg_type = GST_MESSAGE_TYPE (message); + G_OBJECT_CLASS (clutter_video_texture_parent_class)->finalize (object); +} - /* somebody else is handling the message, - probably in poll_for_state_change */ - if (priv->ignore_messages_mask & msg_type) +static void +clutter_video_texture_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterVideoTexture *video_texture; + + video_texture = CLUTTER_VIDEO_TEXTURE(object); + + switch (property_id) { - gchar *src_name = gst_object_get_name (message->src); - CLUTTER_DBG ("Ignoring %s message from element %s as requested", - gst_message_type_get_name (msg_type), src_name); - g_free (src_name); - return; - } - - if (msg_type != GST_MESSAGE_STATE_CHANGED && clutter_want_debug()) - { - gchar *src_name = gst_object_get_name (message->src); - CLUTTER_DBG ("Handling %s message from element %s", - gst_message_type_get_name (msg_type), src_name); - g_free (src_name); - } - - switch (msg_type) - { - case GST_MESSAGE_ERROR: - { - GError *error = NULL; - gchar *debug = NULL; - - gst_message_parse_error (message, &error, &debug); - - CLUTTER_DBG ("Error message: %s [%s]", - GST_STR_NULL (error->message), - GST_STR_NULL (debug)); - - g_signal_emit (video_texture, - cvt_signals[SIGNAL_ERROR], - 0, - error->message, - TRUE, - FALSE); - - g_error_free (error); - - if (priv->play) - gst_element_set_state (priv->play, GST_STATE_NULL); - - g_free (debug); - break; - } - case GST_MESSAGE_WARNING: - { - GError *error = NULL; - gchar *debug = NULL; - - gst_message_parse_warning (message, &error, &debug); - - g_warning ("%s [%s]", - GST_STR_NULL (error->message), - GST_STR_NULL (debug)); - - g_error_free (error); - g_free (debug); - break; - } - case GST_MESSAGE_TAG: - { - GstTagList *tag_list, *result; - GstElementFactory *f; - - gst_message_parse_tag (message, &tag_list); - - CLUTTER_DBG ("Tags: %p", tag_list); - - /* all tags */ - result = gst_tag_list_merge (priv->tagcache, - tag_list, - GST_TAG_MERGE_KEEP); - - if (priv->tagcache) - gst_tag_list_free (priv->tagcache); - priv->tagcache = result; - - /* media-type-specific tags */ - if (GST_IS_ELEMENT (message->src) && - (f = gst_element_get_factory (GST_ELEMENT (message->src)))) - { - const gchar *klass = gst_element_factory_get_klass (f); - GstTagList **cache = NULL; - - if (g_strrstr (klass, "Video")) - { - cache = &priv->videotags; - } - else if (g_strrstr (klass, "Audio")) - { - cache = &priv->audiotags; - } - - if (cache) - { - result = gst_tag_list_merge (*cache, tag_list, - GST_TAG_MERGE_KEEP); - if (*cache) - gst_tag_list_free (*cache); - *cache = result; - } - } - - gst_tag_list_free (tag_list); - g_signal_emit (video_texture, cvt_signals[SIGNAL_GOT_METADATA], 0); - break; - } - case GST_MESSAGE_EOS: - CLUTTER_DBG ("GST_MESSAGE_EOS"); - - if (priv->eos_id == 0) - priv->eos_id = g_idle_add (signal_eos_delayed, video_texture); + case PROP_URI: + clutter_media_set_uri (CLUTTER_MEDIA(video_texture), + g_value_get_string (value)); break; - case GST_MESSAGE_BUFFERING: - { - gint percent = 0; - gst_structure_get_int (message->structure, "buffer-percent", &percent); - - CLUTTER_DBG ("Buffering message (%u%%)", percent); - - g_signal_emit (video_texture, - cvt_signals[SIGNAL_BUFFERING], - 0, - percent); - break; - } - - case GST_MESSAGE_APPLICATION: - handle_application_message (video_texture, message); + case PROP_PLAYING: + clutter_media_set_playing (CLUTTER_MEDIA(video_texture), + g_value_get_boolean (value)); break; - - case GST_MESSAGE_STATE_CHANGED: - { - GstState old_state, new_state; - gchar *src_name; - - CLUTTER_DBG ("GST_MESSAGE_STATE_CHANGED"); - - gst_message_parse_state_changed (message, - &old_state, &new_state, NULL); - - if (old_state == new_state) - break; - - /* we only care about playbin (pipeline) state changes */ - if (GST_MESSAGE_SRC (message) != GST_OBJECT (priv->play)) - break; - - src_name = gst_object_get_name (message->src); - - CLUTTER_DBG ("%s changed state from %s to %s", src_name, - gst_element_state_get_name (old_state), - gst_element_state_get_name (new_state)); - - g_free (src_name); - - if (new_state <= GST_STATE_PAUSED) - { - if (priv->update_id != 0) - { - CLUTTER_DBG ("removing tick timeout"); - g_source_remove (priv->update_id); - priv->update_id = 0; - } - } - else if (new_state > GST_STATE_PAUSED) - { - if (priv->update_id == 0) - { - CLUTTER_DBG ("starting tick timeout"); - - priv->update_id = g_timeout_add (200, - (GSourceFunc) query_timeout, - video_texture); - } - } - - if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED) - { - parse_stream_info (video_texture); - } - else if (old_state == GST_STATE_PAUSED && new_state == GST_STATE_READY) - { - priv->has_video = FALSE; - priv->has_audio = FALSE; - - /* clean metadata cache */ - if (priv->tagcache) - { - gst_tag_list_free (priv->tagcache); - priv->tagcache = NULL; - } - - if (priv->audiotags) - { - gst_tag_list_free (priv->audiotags); - priv->audiotags = NULL; - } - - if (priv->videotags) - { - gst_tag_list_free (priv->videotags); - priv->videotags = NULL; - } - - priv->video_width = 0; - priv->video_height = 0; - } + case PROP_POSITION: + clutter_media_set_position (CLUTTER_MEDIA(video_texture), + g_value_get_int (value)); break; - } - - case GST_MESSAGE_ELEMENT: - handle_element_message (video_texture, message); - break; - - case GST_MESSAGE_DURATION: - { - CLUTTER_DBG ("GST_MESSAGE_DURATION"); - /* force _get_stream_length() to do new duration query */ - priv->stream_length = 0; - if (clutter_video_texture_get_stream_length (video_texture) == 0) - CLUTTER_DBG ("Failed to query duration after DURATION message?!"); - break; - } - - case GST_MESSAGE_CLOCK_PROVIDE: - case GST_MESSAGE_CLOCK_LOST: - case GST_MESSAGE_NEW_CLOCK: - case GST_MESSAGE_STATE_DIRTY: + case PROP_VOLUME: + clutter_media_set_volume (CLUTTER_MEDIA(video_texture), + g_value_get_double (value)); break; default: - CLUTTER_DBG ("Unhandled message of type '%s' (0x%x)", - gst_message_type_get_name (msg_type), msg_type); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +clutter_video_texture_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + ClutterVideoTexture *video_texture; + ClutterMedia *media; + + video_texture = CLUTTER_VIDEO_TEXTURE (object); + media = CLUTTER_MEDIA (video_texture); + + switch (property_id) + { + case PROP_URI: + g_value_set_string (value, clutter_media_get_uri (media)); break; + case PROP_PLAYING: + g_value_set_boolean (value, clutter_media_get_playing (media)); + break; + case PROP_POSITION: + g_value_set_int (value, clutter_media_get_position (media)); + break; + case PROP_VOLUME: + g_value_set_double (value, clutter_media_get_volume (media)); + break; + case PROP_CAN_SEEK: + g_value_set_boolean (value, clutter_media_get_can_seek (media)); + break; + case PROP_BUFFER_PERCENT: + g_value_set_int (value, clutter_media_get_buffer_percent (media)); + break; + case PROP_DURATION: + g_value_set_int (value, clutter_media_get_duration (media)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void -got_time_tick (GstElement *play, - gint64 time_nanos, - ClutterVideoTexture *video_texture) +clutter_video_texture_class_init (ClutterVideoTextureClass *klass) { - gboolean seekable; - ClutterVideoTexturePrivate *priv; + GObjectClass *object_class; + ClutterElementClass *element_class; - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE(video_texture)); + object_class = (GObjectClass*)klass; + element_class = (ClutterElementClass*)klass; - priv = video_texture->priv; + object_class->dispose = clutter_video_texture_dispose; + object_class->finalize = clutter_video_texture_finalize; + object_class->set_property = clutter_video_texture_set_property; + object_class->get_property = clutter_video_texture_get_property; - priv->current_time_nanos = time_nanos; - priv->current_time = (gint64) time_nanos / GST_MSECOND; + /* Interface props */ - if (priv->stream_length == 0) - { - priv->current_position = 0; - seekable = clutter_video_texture_is_seekable (video_texture); - } - else - { - priv->current_position = - (gfloat) priv->current_time / priv->stream_length; - seekable = TRUE; - } - - g_signal_emit (video_texture, - cvt_signals[SIGNAL_TICK], - 0, - priv->current_time, - priv->stream_length, - priv->current_position, - seekable); + 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_POSITION, "position"); + g_object_class_override_property (object_class, PROP_VOLUME, "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_PERCENT, + "buffer-percent" ); } static void -playbin_got_source (GObject *play, - GParamSpec *pspec, - ClutterVideoTexture *video_texture) +bus_message_error_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) { - /* Called via notify::source on playbin */ + GError *error; - ClutterVideoTexturePrivate *priv; - GObject *source = NULL; + error = NULL; + gst_message_parse_error (message, &error, NULL); + + g_signal_emit_by_name (CLUTTER_MEDIA(video_texture), "error", error); - priv = video_texture->priv; + g_error_free (error); +} - if (priv->tagcache) - { - gst_tag_list_free (priv->tagcache); - priv->tagcache = NULL; - } +static void +bus_message_eos_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) +{ + g_object_notify (G_OBJECT (video_texture), "position"); - if (priv->audiotags) - { - gst_tag_list_free (priv->audiotags); - priv->audiotags = NULL; - } + g_signal_emit_by_name (CLUTTER_MEDIA(video_texture), "eos"); +} - if (priv->videotags) - { - gst_tag_list_free (priv->videotags); - priv->videotags = NULL; - } +static void +bus_message_tag_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) +{ + GstTagList *tag_list; - g_object_get (play, "source", &source, NULL); + gst_message_parse_tag (message, &tag_list); - if (!source) + g_signal_emit_by_name (CLUTTER_MEDIA(video_texture), + "metadata-available", + tag_list); + + gst_tag_list_free (tag_list); +} + +static void +bus_message_buffering_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) +{ + const GstStructure *str; + + str = gst_message_get_structure (message); + if (!str) return; - g_object_unref (source); + if (!gst_structure_get_int (str, + "buffer-percent", + &video_texture->priv->buffer_percent)) + return; + + g_object_notify (G_OBJECT (video_texture), "buffer-percent"); } static void -playbin_stream_info_set (GObject *obj, - GParamSpec *pspec, - ClutterVideoTexture *video_texture) +bus_message_duration_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) { - ClutterVideoTexturePrivate *priv; - GstMessage *msg; + GstFormat format; + gint64 duration; - priv = video_texture->priv; + gst_message_parse_duration (message, + &format, + &duration); - parse_stream_info (video_texture); - - msg = gst_message_new_application (GST_OBJECT (priv->play), - gst_structure_new ("notify-streaminfo", - NULL)); - gst_element_post_message (priv->play, msg); -} - - -static gboolean -poll_for_state_change_full (ClutterVideoTexture *video_texture, - GstElement *element, - GstState state, - GError **error, - gint64 timeout) -{ - GstBus *bus; - GstMessageType events, saved_events; - ClutterVideoTexturePrivate *priv; - - priv = video_texture->priv; - bus = gst_element_get_bus (element); - saved_events = priv->ignore_messages_mask; - - events = GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS; - - if (element != NULL && element == priv->play) - { - /* we do want the main handler to process state changed messages for - * playbin as well, otherwise it won't hook up the timeout etc. */ - priv->ignore_messages_mask |= (events ^ GST_MESSAGE_STATE_CHANGED); - } - else - priv->ignore_messages_mask |= events; - - while (TRUE) - { - GstMessage *message; - GstElement *src; - - message = gst_bus_poll (bus, events, timeout); - - if (!message) - goto timed_out; - - src = (GstElement*)GST_MESSAGE_SRC (message); - - switch (GST_MESSAGE_TYPE (message)) - { - case GST_MESSAGE_STATE_CHANGED: - { - GstState old, new, pending; - - if (src == element) - { - gst_message_parse_state_changed (message, - &old, &new, &pending); - if (new == state) - { - gst_message_unref (message); - goto success; - } - } - } - break; - case GST_MESSAGE_ERROR: - { - gchar *debug = NULL; - GError *gsterror = NULL; - - gst_message_parse_error (message, &gsterror, &debug); - - g_warning ("Error: %s (%s)", gsterror->message, debug); - - gst_message_unref (message); - g_error_free (gsterror); - g_free (debug); - goto error; - } - break; - case GST_MESSAGE_EOS: - g_set_error (error, CLUTTER_VIDEO_TEXTURE_ERROR, - CLUTTER_VIDEO_TEXTURE_ERROR_FILE_GENERIC, - "Media file could not be played."); - gst_message_unref (message); - goto error; - break; - default: - g_assert_not_reached (); - break; - } - gst_message_unref (message); - } + if (format != GST_FORMAT_TIME) + return; - g_assert_not_reached (); + video_texture->priv->duration = duration / GST_SECOND; -success: - /* state change succeeded */ - CLUTTER_DBG ("state change to %s succeeded", - gst_element_state_get_name (state)); + g_object_notify (G_OBJECT (video_texture), "duration"); +} - priv->ignore_messages_mask = saved_events; - return TRUE; +static void +bus_message_element_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) +{ + const GstStructure *str; -timed_out: - /* it's taking a long time to open -- just tell totem it was ok, this allows - * the user to stop the loading process with the normal stop button */ - CLUTTER_DBG ("state change to %s timed out, returning success and handling " - "errors asynchroneously", gst_element_state_get_name (state)); - priv->ignore_messages_mask = saved_events; - return TRUE; + str = gst_message_get_structure (message); + if (!str) + return; +} -error: - CLUTTER_DBG ("error while waiting for state change to %s: %s", - gst_element_state_get_name (state), - (error && *error) ? (*error)->message : "unknown"); - priv->ignore_messages_mask = saved_events; - return FALSE; +static void +bus_message_state_change_cb (GstBus *bus, + GstMessage *message, + ClutterVideoTexture *video_texture) +{ + gpointer src; + GstState old_state, new_state; + + src = GST_MESSAGE_SRC (message); + + if (src != video_texture->priv->playbin) + return; + + gst_message_parse_state_changed (message, &old_state, &new_state, NULL); + + 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 (video_texture->priv->playbin, query)) { + gst_query_parse_seeking (query, + NULL, + &video_texture->priv->can_seek, + NULL, + NULL); + } else { + /** + * Could not query for ability to seek. Determine + * using URI. + **/ + + if (g_str_has_prefix (video_texture->priv->uri, + "http://")) { + video_texture->priv->can_seek = FALSE; + } else { + video_texture->priv->can_seek = TRUE; + } + } + + gst_query_unref (query); + + g_object_notify (G_OBJECT (video_texture), "can-seek"); + + /** + * Determine the duration. + **/ + query = gst_query_new_duration (GST_FORMAT_TIME); + + if (gst_element_query (video_texture->priv->playbin, query)) + { + gint64 duration; + + gst_query_parse_duration (query, NULL, &duration); + + video_texture->priv->duration = duration / GST_SECOND; + + g_object_notify (G_OBJECT (video_texture), "duration"); + } + + gst_query_unref (query); + } } static gboolean -poll_for_state_change (ClutterVideoTexture *video_texture, - GstElement *element, - GstState state, - GError **error) +tick_timeout (ClutterVideoTexture *video_texture) { - return poll_for_state_change_full (video_texture, - element, - state, - error, - GST_SECOND/4 ); + g_object_notify (G_OBJECT (video_texture), "position"); + + return TRUE; } static void @@ -814,7 +659,6 @@ fakesink_handoff_cb (GstElement *fakesrc, GstPad *pad, gpointer user_data) { - GstStructure *structure; int width, height; GdkPixbuf *pixb; @@ -835,7 +679,7 @@ fakesink_handoff_cb (GstElement *fakesrc, (3 * width + 3) &~ 3, NULL, NULL); - + if (pixb) { clutter_texture_set_pixbuf (CLUTTER_TEXTURE(user_data), pixb); @@ -843,298 +687,61 @@ fakesink_handoff_cb (GstElement *fakesrc, } } -static void -clutter_video_texture_finalize (GObject *object) -{ - ClutterVideoTexture *self; - ClutterVideoTexturePrivate *priv; - - self = CLUTTER_VIDEO_TEXTURE(object); - priv = self->priv; - - if (priv->bus) - { - /* make bus drop all messages to make sure none of our callbacks is ever - * called again (main loop might be run again to display error dialog) */ - gst_bus_set_flushing (priv->bus, TRUE); - - if (priv->sig_bus_async) - g_signal_handler_disconnect (priv->bus, priv->sig_bus_async); - - gst_object_unref (priv->bus); - priv->bus = NULL; - } - - if (priv->mrl) - g_free (priv->mrl); - priv->mrl = NULL; - - if (priv->play != NULL && GST_IS_ELEMENT (priv->play)) - { - gst_element_set_state (priv->play, GST_STATE_NULL); - gst_object_unref (priv->play); - priv->play = NULL; - } - - if (priv->update_id) - { - g_source_remove (priv->update_id); - priv->update_id = 0; - } - - if (priv->tagcache) - { - gst_tag_list_free (priv->tagcache); - priv->tagcache = NULL; - } - - if (priv->audiotags) - { - gst_tag_list_free (priv->audiotags); - priv->audiotags = NULL; - } - - if (priv->videotags) - { - gst_tag_list_free (priv->videotags); - priv->videotags = NULL; - } - - if (priv->eos_id != 0) - g_source_remove (priv->eos_id); - - g_free (priv); - - self->priv = NULL; - - G_OBJECT_CLASS (clutter_video_texture_parent_class)->finalize (object); -} - -static void -clutter_video_texture_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) - { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -clutter_video_texture_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - ClutterVideoTexture *video_texture; - - video_texture = CLUTTER_VIDEO_TEXTURE (object); - - switch (property_id) - { - case PROP_POSITION: - g_value_set_int64 (value, - clutter_video_texture_get_position (video_texture)); - break; - case PROP_STREAM_LENGTH: - g_value_set_int64 (value, - clutter_video_texture_get_stream_length (video_texture)); - break; - case PROP_PLAYING: - g_value_set_boolean (value, - clutter_video_texture_is_playing (video_texture)); - break; - case PROP_SEEKABLE: - g_value_set_boolean (value, - clutter_video_texture_is_seekable (video_texture)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - - -static void -clutter_video_texture_class_init (ClutterVideoTextureClass *klass) -{ - GObjectClass *object_class; - ClutterElementClass *element_class; - - object_class = (GObjectClass*)klass; - element_class = (ClutterElementClass*)klass; - - object_class->finalize = clutter_video_texture_finalize; - object_class->set_property = clutter_video_texture_set_property; - object_class->get_property = clutter_video_texture_get_property; - - /* Properties */ - g_object_class_install_property (object_class, PROP_POSITION, - g_param_spec_int ("position", NULL, NULL, - 0, G_MAXINT, 0, - G_PARAM_READABLE)); - g_object_class_install_property (object_class, PROP_STREAM_LENGTH, - g_param_spec_int64 ("stream_length", NULL, - NULL, 0, G_MAXINT64, 0, - G_PARAM_READABLE)); - g_object_class_install_property (object_class, PROP_PLAYING, - g_param_spec_boolean ("playing", NULL, - NULL, FALSE, - G_PARAM_READABLE)); - g_object_class_install_property (object_class, PROP_SEEKABLE, - g_param_spec_boolean ("seekable", NULL, - NULL, FALSE, - G_PARAM_READABLE)); - - /* Signals */ - cvt_signals[SIGNAL_ERROR] = - g_signal_new ("error", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, error), - NULL, NULL, - clutter_marshal_VOID__STRING_BOOLEAN_BOOLEAN, - G_TYPE_NONE, 3, G_TYPE_STRING, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); - - cvt_signals[SIGNAL_EOS] = - g_signal_new ("eos", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, eos), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - cvt_signals[SIGNAL_GOT_METADATA] = - g_signal_new ("got-metadata", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, got_metadata), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - cvt_signals[SIGNAL_REDIRECT] = - g_signal_new ("got-redirect", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, got_redirect), - NULL, NULL, g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); - - cvt_signals[SIGNAL_TITLE_CHANGE] = - g_signal_new ("title-change", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, title_change), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); - - cvt_signals[SIGNAL_CHANNELS_CHANGE] = - g_signal_new ("channels-change", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, channels_change), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - cvt_signals[SIGNAL_TICK] = - g_signal_new ("tick", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, tick), - NULL, NULL, - clutter_marshal_VOID__INT64_INT64_FLOAT_BOOLEAN, - G_TYPE_NONE, 4, G_TYPE_INT64, G_TYPE_INT64, G_TYPE_FLOAT, - G_TYPE_BOOLEAN); - - cvt_signals[SIGNAL_BUFFERING] = - g_signal_new ("buffering", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, buffering), - NULL, NULL, - g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); - - cvt_signals[SIGNAL_SPEED_WARNING] = - g_signal_new ("speed-warning", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterVideoTextureClass, speed_warning), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - -} - - -static void -clutter_video_texture_init (ClutterVideoTexture *video_texture) +static gboolean +lay_pipeline (ClutterVideoTexture *video_texture) { ClutterVideoTexturePrivate *priv; - GstElement *audio_sink, *video_sink, *bin, *capsfilter; - GstCaps *filtercaps; - GstPad *ghost_pad; + GstElement *audio_sink = NULL; + GstElement *video_sink, *video_bin, *video_capsfilter; + GstCaps *video_filtercaps; + GstPad *video_ghost_pad; - priv = g_new0 (ClutterVideoTexturePrivate, 1); - video_texture->priv = priv; + priv = video_texture->priv; - priv->ratio_type = CLUTTER_VIDEO_TEXTURE_AUTO; + priv->playbin = gst_element_factory_make ("playbin", "playbin"); - priv->play = gst_element_factory_make ("playbin", "play"); - - if (!priv->play) + if (!priv->playbin) { - g_warning ("Could not create element 'playbin'"); - return; + g_warning ("Unable to create playbin GST element."); + return FALSE; } - priv->bus = gst_element_get_bus (priv->play); - gst_bus_add_signal_watch (priv->bus); - priv->sig_bus_async = g_signal_connect (priv->bus, - "message", - G_CALLBACK (bus_message_cb), - video_texture); - audio_sink = gst_element_factory_make ("gconfaudiosink", "audio-sink"); - - if (audio_sink == NULL) + if (!audio_sink) { - g_warning ("Could not create element 'gconfaudiosink' trying autosink"); audio_sink = gst_element_factory_make ("autoaudiosink", "audio-sink"); - - if (audio_sink == NULL) + if (!audio_sink) { - g_warning ("Could not create element 'autoaudiosink' " - "trying fakesink"); - audio_sink = gst_element_factory_make ("fakesink", - "audio-fake-sink"); - if (audio_sink == NULL) - { - g_warning ("Could not create element 'fakesink' for audio, giving up. "); - } + audio_sink = gst_element_factory_make ("alsasink", "audio-sink"); + g_warning ("Could not create a GST audio_sink. " + "Audio unavailable."); + + if (!audio_sink) /* Need to bother ? */ + audio_sink = gst_element_factory_make ("fakesink", "audio-sink"); } } - - priv->audio_sink = audio_sink; video_sink = gst_element_factory_make ("fakesink", "fakesink"); if (video_sink == NULL) { g_warning ("Could not create element 'fakesink' for video playback"); - return; + priv->playbin = NULL; + return FALSE; } - bin = gst_bin_new ("video-bin"); + video_bin = gst_bin_new ("video-bin"); - capsfilter = gst_element_factory_make ("capsfilter", "vfilter"); + video_capsfilter = gst_element_factory_make ("capsfilter", + "video-capsfilter"); - filtercaps + video_filtercaps = gst_caps_new_simple("video/x-raw-rgb", "bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24, "endianness", G_TYPE_INT, G_BIG_ENDIAN, - "red_mask", G_TYPE_INT, 0xff0000 /* >> 8 for 24bpp */, + /* >> 8 for 24bpp */ + "red_mask", G_TYPE_INT, 0xff0000, "green_mask", G_TYPE_INT, 0xff00, "blue_mask", G_TYPE_INT, 0xff, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, @@ -1143,38 +750,101 @@ clutter_video_texture_init (ClutterVideoTexture *video_texture) 0, 1, G_MAXINT, 1, NULL); - g_object_set(G_OBJECT(capsfilter), "caps", filtercaps, NULL); + g_object_set(G_OBJECT(video_capsfilter), + "caps", video_filtercaps, + NULL); - gst_bin_add(GST_BIN(bin), capsfilter); - gst_bin_add(GST_BIN(bin), video_sink); + gst_bin_add(GST_BIN(video_bin), video_capsfilter); + gst_bin_add(GST_BIN(video_bin), video_sink); - gst_element_link (capsfilter, video_sink); + gst_element_link (video_capsfilter, video_sink); - ghost_pad = gst_ghost_pad_new ("sink", - gst_element_get_pad (capsfilter, "sink")); - - gst_element_add_pad (bin, ghost_pad); + video_ghost_pad = gst_ghost_pad_new ("sink", + gst_element_get_pad (video_capsfilter, + "sink")); + gst_element_add_pad (video_bin, video_ghost_pad); g_object_set (G_OBJECT(video_sink), "signal-handoffs", TRUE, "sync", TRUE, NULL); - g_signal_connect(G_OBJECT (video_sink), "handoff", - G_CALLBACK(fakesink_handoff_cb), video_texture); + g_signal_connect(G_OBJECT (video_sink), + "handoff", + G_CALLBACK(fakesink_handoff_cb), + video_texture); - priv->video_sink = bin; + g_object_set (G_OBJECT (priv->playbin), + "video-sink", video_bin, + "audio-sink", audio_sink, + NULL); - if (priv->video_sink) - g_object_set (priv->play, "video-sink", bin, NULL); + return TRUE; +} + +static void +clutter_video_texture_init (ClutterVideoTexture *video_texture) +{ + ClutterVideoTexturePrivate *priv; + GstBus *bus; + + priv = g_new0 (ClutterVideoTexturePrivate, 1); + video_texture->priv = priv; + + if (!lay_pipeline(video_texture)) + { + g_warning("Failed to initiate suitable playback pipeline."); + return; + } + + bus = gst_pipeline_get_bus (GST_PIPELINE (priv->playbin)); + + gst_bus_add_signal_watch (bus); + + g_signal_connect_object (bus, + "message::error", + G_CALLBACK (bus_message_error_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::eos", + G_CALLBACK (bus_message_eos_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::tag", + G_CALLBACK (bus_message_tag_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::buffering", + G_CALLBACK (bus_message_buffering_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::duration", + G_CALLBACK (bus_message_duration_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::element", + G_CALLBACK (bus_message_element_cb), + video_texture, + 0); + + g_signal_connect_object (bus, + "message::state-changed", + G_CALLBACK (bus_message_state_change_cb), + video_texture, + 0); + + gst_object_unref (GST_OBJECT (bus)); - if (priv->audio_sink) - g_object_set (priv->play, "audio-sink", audio_sink, NULL); - - g_signal_connect (priv->play, "notify::source", - G_CALLBACK (playbin_got_source), video_texture); - g_signal_connect (priv->play, "notify::stream-info", - G_CALLBACK (playbin_stream_info_set), video_texture); return; } @@ -1191,663 +861,3 @@ clutter_video_texture_new (void) return CLUTTER_ELEMENT(video_texture); } -gboolean -clutter_video_texture_open (ClutterVideoTexture *video_texture, - const gchar *mrl, - const gchar *subtitle_uri, - GError **error) -{ - ClutterVideoTexturePrivate *priv; - gboolean ret; - - priv = video_texture->priv; - - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (mrl != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE(video_texture), FALSE); - g_return_val_if_fail (priv->play != NULL, FALSE); - - if (priv->mrl && strcmp (priv->mrl, mrl) == 0) - return TRUE; - - /* this allows non-URI type of files in the thumbnailer and so on */ - if (priv->mrl) - g_free (priv->mrl); - - if (mrl[0] == '/') - { - priv->mrl = g_strdup_printf ("file://%s", mrl); - } - else - { - if (strchr (mrl, ':')) - { - priv->mrl = g_strdup (mrl); - } - else - { - gchar *cur_dir = g_get_current_dir (); - if (!cur_dir) - { - g_set_error (error, CLUTTER_VIDEO_TEXTURE_ERROR, - CLUTTER_VIDEO_TEXTURE_ERROR_GENERIC, - "Failed to retrieve working directory"); - return FALSE; - } - priv->mrl = g_strdup_printf ("file://%s/%s", cur_dir, mrl); - g_free (cur_dir); - } - } - - priv->got_redirect = FALSE; - priv->has_video = FALSE; - priv->has_audio = FALSE; - priv->stream_length = 0; - - if (g_strrstr (priv->mrl, "#subtitle:")) - { - gchar **uris; - gchar *subtitle_uri; - - uris = g_strsplit (priv->mrl, "#subtitle:", 2); - /* Try to fix subtitle uri if needed */ - if (uris[1][0] == '/') - { - subtitle_uri = g_strdup_printf ("file://%s", uris[1]); - } - else - { - if (strchr (uris[1], ':')) - { - subtitle_uri = g_strdup (uris[1]); - } - else - { - gchar *cur_dir = g_get_current_dir (); - if (!cur_dir) - { - g_set_error (error, CLUTTER_VIDEO_TEXTURE_ERROR, - CLUTTER_VIDEO_TEXTURE_ERROR_GENERIC, - "Failed to retrieve working directory"); - return FALSE; - } - - subtitle_uri = g_strdup_printf ("file://%s/%s", - cur_dir, uris[1]); - g_free (cur_dir); - } - } - - g_object_set (priv->play, - "uri", priv->mrl, - "suburi", subtitle_uri, - NULL); - g_free (subtitle_uri); - g_strfreev (uris); - } - else - { - g_object_set (priv->play, - "uri", priv->mrl, - "suburi", subtitle_uri, - NULL); - } - - gst_element_set_state (priv->play, GST_STATE_PAUSED); - - ret = poll_for_state_change (video_texture, - priv->play, - GST_STATE_PAUSED, - NULL); - - if (!ret) - { - priv->ignore_messages_mask |= GST_MESSAGE_ERROR; - stop_play_pipeline (video_texture); - g_free (priv->mrl); - priv->mrl = NULL; - } - else - g_signal_emit (video_texture, cvt_signals[SIGNAL_CHANNELS_CHANGE], 0); - - return ret; -} - -gboolean -clutter_video_texture_play (ClutterVideoTexture *video_texture, - GError ** error) -{ - ClutterVideoTexturePrivate *priv; - gboolean ret; - - priv = video_texture->priv; - - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE(video_texture), FALSE); - g_return_val_if_fail (priv->play != NULL, FALSE); - - gst_element_set_state (priv->play, GST_STATE_PLAYING); - - ret = poll_for_state_change (video_texture, - priv->play, - GST_STATE_PLAYING, - error); - return ret; -} - -void -clutter_video_texture_pause (ClutterVideoTexture *video_texture) -{ - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); - g_return_if_fail (GST_IS_ELEMENT (video_texture->priv->play)); - - CLUTTER_DBG ("Pausing"); - - gst_element_set_state (GST_ELEMENT (video_texture->priv->play), - GST_STATE_PAUSED); -} - - -gboolean -clutter_video_texture_can_direct_seek (ClutterVideoTexture *video_texture) -{ - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - if (!video_texture->priv->mrl) - return FALSE; - - /* (instant seeking only make sense with video, hence no cdda:// here) */ - if (g_str_has_prefix (video_texture->priv->mrl, "file://") || - g_str_has_prefix (video_texture->priv->mrl, "dvd://") || - g_str_has_prefix (video_texture->priv->mrl, "vcd://")) - return TRUE; - - return FALSE; -} - -gboolean -clutter_video_texture_seek_time (ClutterVideoTexture *video_texture, - gint64 time, - GError **gerror) -{ - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - got_time_tick (video_texture->priv->play, time * GST_MSECOND, video_texture); - - gst_element_seek (video_texture->priv->play, - 1.0, - GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, - GST_SEEK_TYPE_SET, - time * GST_MSECOND, - GST_SEEK_TYPE_NONE, - GST_CLOCK_TIME_NONE); - - gst_element_get_state (video_texture->priv->play, - NULL, NULL, 100 * GST_MSECOND); - return TRUE; -} - -gboolean -clutter_video_texture_seek (ClutterVideoTexture *video_texture, - float position, - GError **error) -{ - gint64 seek_time, length_nanos; - - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - length_nanos = (gint64) (video_texture->priv->stream_length * GST_MSECOND); - seek_time = (gint64) (length_nanos * position); - - return clutter_video_texture_seek_time (video_texture, - seek_time / GST_MSECOND, error); -} - -static void -stop_play_pipeline (ClutterVideoTexture *video_texture) -{ - GstElement *playbin = video_texture->priv->play; - GstState current_state; - - /* first go to ready, that way our state change handler gets to see - * our state change messages (before the bus goes to flushing) and - * cleans up */ - gst_element_get_state (playbin, ¤t_state, NULL, 0); - - if (current_state > GST_STATE_READY) - { - GError *err = NULL; - - gst_element_set_state (playbin, GST_STATE_READY); - poll_for_state_change_full (video_texture, - playbin, - GST_STATE_READY, &err, -1); - if (err) - g_error_free (err); - } - - /* now finally go to null state */ - gst_element_set_state (playbin, GST_STATE_NULL); - gst_element_get_state (playbin, NULL, NULL, -1); -} - -void -clutter_video_texture_stop (ClutterVideoTexture *video_texture) -{ - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); - g_return_if_fail (GST_IS_ELEMENT (video_texture->priv->play)); - - stop_play_pipeline (video_texture); - - /* Reset position to 0 when stopping */ - got_time_tick (GST_ELEMENT (video_texture->priv->play), 0, video_texture); -} - -gboolean -clutter_video_texture_can_set_volume (ClutterVideoTexture *video_texture) -{ - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - return TRUE; -} - -void -clutter_video_texture_set_volume (ClutterVideoTexture *video_texture, - int volume) -{ - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); - g_return_if_fail (GST_IS_ELEMENT (video_texture->priv->play)); - - if (clutter_video_texture_can_set_volume (video_texture) != FALSE) - { - volume = CLAMP (volume, 0, 100); - g_object_set (video_texture->priv->play, "volume", - (gdouble) (1. * volume / 100), NULL); - } -} - -int -clutter_video_texture_get_volume (ClutterVideoTexture *video_texture) -{ - gdouble vol; - - g_return_val_if_fail (video_texture != NULL, -1); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), -1); - - g_object_get (G_OBJECT (video_texture->priv->play), "volume", &vol, NULL); - - return (gint) (vol * 100 + 0.5); -} - -gint64 -clutter_video_texture_get_current_time (ClutterVideoTexture *video_texture) -{ - g_return_val_if_fail (video_texture != NULL, -1); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); - - return video_texture->priv->current_time; -} - -gint64 -clutter_video_texture_get_stream_length (ClutterVideoTexture *video_texture) -{ - ClutterVideoTexturePrivate *priv; - - g_return_val_if_fail (video_texture != NULL, -1); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); - - priv = video_texture->priv; - - if (priv->stream_length == 0 && priv->play != NULL) - { - GstFormat fmt = GST_FORMAT_TIME; - gint64 len = -1; - - if (gst_element_query_duration (priv->play, &fmt, &len) && len != -1) - priv->stream_length = len / GST_MSECOND; - } - - return priv->stream_length; -} - -gboolean -clutter_video_texture_is_playing (ClutterVideoTexture *video_texture) -{ - GstState cur, pending; - - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - gst_element_get_state (video_texture->priv->play, &cur, &pending, 0); - - if (cur == GST_STATE_PLAYING || pending == GST_STATE_PLAYING) - return TRUE; - - return FALSE; -} - -gboolean -clutter_video_texture_is_seekable (ClutterVideoTexture *video_texture) -{ - gboolean res; - - g_return_val_if_fail (video_texture != NULL, FALSE); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (video_texture->priv->play), FALSE); - - if (video_texture->priv->stream_length == 0) - res = (clutter_video_texture_get_stream_length (video_texture) > 0); - else - res = (video_texture->priv->stream_length > 0); - - return res; -} - -float -clutter_video_texture_get_position (ClutterVideoTexture *video_texture) -{ - g_return_val_if_fail (video_texture != NULL, -1); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), -1); - - return video_texture->priv->current_position; -} - -void -clutter_video_texture_set_aspect_ratio (ClutterVideoTexture *video_texture, - ClutterVideoTextureAspectRatio ratio) -{ - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); - - video_texture->priv->ratio_type = ratio; - got_video_size (video_texture); -} - -ClutterVideoTextureAspectRatio -clutter_video_texture_get_aspect_ratio (ClutterVideoTexture *video_texture) -{ - g_return_val_if_fail (video_texture != NULL, 0); - g_return_val_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture), 0); - - return video_texture->priv->ratio_type; -} - -/* Metadata */ - -static const struct _metadata_map_info -{ - ClutterVideoTextureMetadataType type; - const gchar *str; -} metadata_str_map[] = { - { CLUTTER_INFO_TITLE, "title" }, - { CLUTTER_INFO_ARTIST, "artist" }, - { CLUTTER_INFO_YEAR, "year" }, - { CLUTTER_INFO_ALBUM, "album" }, - { CLUTTER_INFO_DURATION, "duration" }, - { CLUTTER_INFO_TRACK_NUMBER, "track-number" }, - { CLUTTER_INFO_HAS_VIDEO, "has-video" }, - { CLUTTER_INFO_DIMENSION_X, "dimension-x" }, - { CLUTTER_INFO_DIMENSION_Y, "dimension-y" }, - { CLUTTER_INFO_VIDEO_BITRATE,"video-bitrate" }, - { CLUTTER_INFO_VIDEO_CODEC, "video-codec" }, - { CLUTTER_INFO_FPS, "fps" }, - { CLUTTER_INFO_HAS_AUDIO, "has-audio" }, - { CLUTTER_INFO_AUDIO_BITRATE,"audio-bitrate" }, - { CLUTTER_INFO_AUDIO_CODEC, "audio-codec" } -}; - -static const gchar* -get_metadata_type_name (ClutterVideoTextureMetadataType type) -{ - guint i; - for (i = 0; i < G_N_ELEMENTS (metadata_str_map); ++i) - { - if (metadata_str_map[i].type == type) - return metadata_str_map[i].str; - } - return "unknown"; -} - -static void -get_metadata_string (ClutterVideoTexture *video_texture, - ClutterVideoTextureMetadataType type, - GValue *value) -{ - ClutterVideoTexturePrivate *priv; - char *string = NULL; - gboolean res = FALSE; - - priv = video_texture->priv; - - g_value_init (value, G_TYPE_STRING); - - if (priv->play == NULL || priv->tagcache == NULL) - { - g_value_set_string (value, NULL); - return; - } - - switch (type) - { - case CLUTTER_INFO_TITLE: - res = gst_tag_list_get_string_index (priv->tagcache, - GST_TAG_TITLE, 0, &string); - break; - case CLUTTER_INFO_ARTIST: - res = gst_tag_list_get_string_index (priv->tagcache, - GST_TAG_ARTIST, 0, &string); - break; - case CLUTTER_INFO_YEAR: - { - GDate *date; - if ((res = gst_tag_list_get_date (priv->tagcache, - GST_TAG_DATE, &date))) - { - string = g_strdup_printf ("%d", g_date_get_year (date)); - g_date_free (date); - } - break; - } - case CLUTTER_INFO_ALBUM: - res = gst_tag_list_get_string_index (priv->tagcache, - GST_TAG_ALBUM, 0, &string); - break; - case CLUTTER_INFO_VIDEO_CODEC: - res = gst_tag_list_get_string (priv->tagcache, - GST_TAG_VIDEO_CODEC, &string); - break; - case CLUTTER_INFO_AUDIO_CODEC: - res = gst_tag_list_get_string (priv->tagcache, - GST_TAG_AUDIO_CODEC, &string); - break; - default: - g_assert_not_reached (); - } - - if (res) - { - g_value_take_string (value, string); - CLUTTER_DBG ("%s = '%s'", get_metadata_type_name (type), string); - } - else - g_value_set_string (value, NULL); - - return; -} - -static void -get_metadata_int (ClutterVideoTexture *video_texture, - ClutterVideoTextureMetadataType type, - GValue *value) -{ - ClutterVideoTexturePrivate *priv; - int integer = 0; - - priv = video_texture->priv; - - g_value_init (value, G_TYPE_INT); - - if (priv->play == NULL) - { - g_value_set_int (value, 0); - return; - } - - switch (type) - { - case CLUTTER_INFO_DURATION: - integer = clutter_video_texture_get_stream_length (video_texture) / 1000; - break; - case CLUTTER_INFO_TRACK_NUMBER: - if (!gst_tag_list_get_uint (priv->tagcache, - GST_TAG_TRACK_NUMBER, (guint *) &integer)) - integer = 0; - break; - case CLUTTER_INFO_DIMENSION_X: - integer = priv->video_width; - break; - case CLUTTER_INFO_DIMENSION_Y: - integer = priv->video_height; - break; - case CLUTTER_INFO_FPS: - if (priv->video_fps_d > 0) - { - /* Round up/down to the nearest integer framerate */ - integer = (priv->video_fps_n + priv->video_fps_d/2) / - priv->video_fps_d; - } - else - integer = 0; - break; - case CLUTTER_INFO_AUDIO_BITRATE: - if (priv->audiotags == NULL) - break; - if (gst_tag_list_get_uint (priv->audiotags, GST_TAG_BITRATE, - (guint *)&integer) || - gst_tag_list_get_uint (priv->audiotags, GST_TAG_NOMINAL_BITRATE, - (guint *)&integer)) { - integer /= 1000; - } - break; - case CLUTTER_INFO_VIDEO_BITRATE: - if (priv->videotags == NULL) - break; - if (gst_tag_list_get_uint (priv->videotags, GST_TAG_BITRATE, - (guint *)&integer) || - gst_tag_list_get_uint (priv->videotags, GST_TAG_NOMINAL_BITRATE, - (guint *)&integer)) { - integer /= 1000; - } - break; - default: - g_assert_not_reached (); - } - - g_value_set_int (value, integer); - CLUTTER_DBG ("%s = %d", get_metadata_type_name (type), integer); - - return; -} - -static void -get_metadata_bool (ClutterVideoTexture *video_texture, - ClutterVideoTextureMetadataType type, - GValue *value) -{ - ClutterVideoTexturePrivate *priv; - gboolean boolean = FALSE; - - priv = video_texture->priv; - - g_value_init (value, G_TYPE_BOOLEAN); - - if (priv->play == NULL) - { - g_value_set_boolean (value, FALSE); - return; - } - - switch (type) - { - case CLUTTER_INFO_HAS_VIDEO: - boolean = priv->has_video; - /* if properties dialog, show the metadata we - * have even if we cannot decode the stream */ - if (!boolean - && priv->tagcache != NULL - && gst_structure_has_field ((GstStructure *) priv->tagcache, - GST_TAG_VIDEO_CODEC)) - boolean = TRUE; - break; - case CLUTTER_INFO_HAS_AUDIO: - boolean = priv->has_audio; - /* if properties dialog, show the metadata we - * have even if we cannot decode the stream */ - if (!boolean - && priv->tagcache != NULL - && gst_structure_has_field ((GstStructure *) priv->tagcache, - GST_TAG_AUDIO_CODEC)) - boolean = TRUE; - break; - default: - g_assert_not_reached (); - } - - g_value_set_boolean (value, boolean); - CLUTTER_DBG ("%s = %s", get_metadata_type_name (type), - (boolean) ? "yes" : "no"); - return; -} - -void -clutter_video_texture_get_metadata (ClutterVideoTexture *video_texture, - ClutterVideoTextureMetadataType type, - GValue *value) -{ - g_return_if_fail (video_texture != NULL); - g_return_if_fail (CLUTTER_IS_VIDEO_TEXTURE (video_texture)); - g_return_if_fail (GST_IS_ELEMENT (video_texture->priv->play)); - - switch (type) - { - case CLUTTER_INFO_TITLE: - case CLUTTER_INFO_ARTIST: - case CLUTTER_INFO_YEAR: - case CLUTTER_INFO_ALBUM: - case CLUTTER_INFO_VIDEO_CODEC: - case CLUTTER_INFO_AUDIO_CODEC: - get_metadata_string (video_texture, type, value); - break; - case CLUTTER_INFO_DURATION: - case CLUTTER_INFO_DIMENSION_X: - case CLUTTER_INFO_DIMENSION_Y: - case CLUTTER_INFO_FPS: - case CLUTTER_INFO_AUDIO_BITRATE: - case CLUTTER_INFO_VIDEO_BITRATE: - case CLUTTER_INFO_TRACK_NUMBER: - get_metadata_int (video_texture, type, value); - break; - case CLUTTER_INFO_HAS_VIDEO: - case CLUTTER_INFO_HAS_AUDIO: - get_metadata_bool (video_texture, type, value); - break; - default: - g_return_if_reached (); - } - - return; -} diff --git a/clutter/clutter-video-texture.h b/clutter/clutter-video-texture.h index 94e6b63e3..29cbd706c 100644 --- a/clutter/clutter-video-texture.h +++ b/clutter/clutter-video-texture.h @@ -1,9 +1,35 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + #ifndef _HAVE_CLUTTER_VIDEO_TEXTURE_H #define _HAVE_CLUTTER_VIDEO_TEXTURE_H #include #include #include +#include #include G_BEGIN_DECLS @@ -31,58 +57,10 @@ G_BEGIN_DECLS CLUTTER_TYPE_VIDEO_TEXTURE, ClutterVideoTextureClass)) typedef struct ClutterVideoTexturePrivate ClutterVideoTexturePrivate ; -typedef struct _ClutterVideoTexture ClutterVideoTexture; -typedef struct _ClutterVideoTextureClass ClutterVideoTextureClass; +typedef struct _ClutterVideoTexture ClutterVideoTexture; +typedef struct _ClutterVideoTextureClass ClutterVideoTextureClass; - -#define CLUTTER_VIDEO_TEXTURE_ERROR clutter_video_texture_error_quark() - -typedef enum -{ - /* Plugins */ - CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_PLUGIN, - CLUTTER_VIDEO_TEXTURE_ERROR_NO_PLUGIN_FOR_FILE, - CLUTTER_VIDEO_TEXTURE_ERROR_VIDEO_PLUGIN, - CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_BUSY, - - /* File */ - CLUTTER_VIDEO_TEXTURE_ERROR_BROKEN_FILE, - CLUTTER_VIDEO_TEXTURE_ERROR_FILE_GENERIC, - CLUTTER_VIDEO_TEXTURE_ERROR_FILE_PERMISSION, - CLUTTER_VIDEO_TEXTURE_ERROR_FILE_ENCRYPTED, - CLUTTER_VIDEO_TEXTURE_ERROR_FILE_NOT_FOUND, - - /* Devices */ - CLUTTER_VIDEO_TEXTURE_ERROR_DVD_ENCRYPTED, - CLUTTER_VIDEO_TEXTURE_ERROR_INVALID_DEVICE, - - /* Network */ - CLUTTER_VIDEO_TEXTURE_ERROR_UNKNOWN_HOST, - CLUTTER_VIDEO_TEXTURE_ERROR_NETWORK_UNREACHABLE, - CLUTTER_VIDEO_TEXTURE_ERROR_CONNECTION_REFUSED, - - /* Generic */ - CLUTTER_VIDEO_TEXTURE_ERROR_UNVALID_LOCATION, - CLUTTER_VIDEO_TEXTURE_ERROR_GENERIC, - CLUTTER_VIDEO_TEXTURE_ERROR_CODEC_NOT_HANDLED, - CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_ONLY, - CLUTTER_VIDEO_TEXTURE_ERROR_CANNOT_CAPTURE, - CLUTTER_VIDEO_TEXTURE_ERROR_READ_ERROR, - CLUTTER_VIDEO_TEXTURE_ERROR_PLUGIN_LOAD, - CLUTTER_VIDEO_TEXTURE_ERROR_STILL_IMAGE, - CLUTTER_VIDEO_TEXTURE_ERROR_EMPTY_FILE -} ClutterVideoTextureError; - -GQuark clutter_video_texture_error_quark (void); - -typedef enum -{ - CLUTTER_VIDEO_TEXTURE_AUTO, - CLUTTER_VIDEO_TEXTURE_SQUARE, - CLUTTER_VIDEO_TEXTURE_FOURBYTHREE, - CLUTTER_VIDEO_TEXTURE_ANAMORPHIC, - CLUTTER_VIDEO_TEXTURE_DVB -} ClutterVideoTextureAspectRatio; +/* #define CLUTTER_VIDEO_TEXTURE_ERROR clutter_video_texture_error_quark() */ struct _ClutterVideoTexture { @@ -94,20 +72,20 @@ struct _ClutterVideoTextureClass { ClutterTextureClass parent_class; - void (*error) (ClutterVideoTexture *cvt, const char *message, - gboolean playback_stopped, gboolean fatal); - void (*eos) (ClutterVideoTexture *cvt); - void (*got_metadata) (ClutterVideoTexture *cvt); - void (*got_redirect) (ClutterVideoTexture *cvt, const char *mrl); - void (*title_change) (ClutterVideoTexture *cvt, const char *title); - void (*channels_change) (ClutterVideoTexture *cvt); - void (*tick) (ClutterVideoTexture *cvt, - gint64 current_time, - gint64 stream_length, - float current_position, - gboolean seekable); - void (*buffering) (ClutterVideoTexture *cvt, guint progress); - void (*speed_warning) (ClutterVideoTexture *cvt); + /* Signals */ + void (* tag_list_available) (ClutterVideoTexture *video_texture, + GstTagList *tag_list); + void (* eos) (ClutterVideoTexture *video_texture); + void (* error) (ClutterVideoTexture *video_texture, + GError *error); + + /* 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_video_texture_get_type (void); @@ -115,99 +93,6 @@ GType clutter_video_texture_get_type (void); ClutterElement* clutter_video_texture_new (void); -gboolean -clutter_video_texture_open (ClutterVideoTexture *video_texture, - const gchar *mrl, - const gchar *subtitle_uri, - GError **error); - -gboolean -clutter_video_texture_play (ClutterVideoTexture *video_texture, - GError ** error); - -void -clutter_video_texture_pause (ClutterVideoTexture *video_texture); - -gboolean -clutter_video_texture_can_direct_seek (ClutterVideoTexture *video_texture); - -gboolean -clutter_video_texture_seek_time (ClutterVideoTexture *video_texture, - gint64 time, - GError **gerror); - -gboolean -clutter_video_texture_seek (ClutterVideoTexture *video_texture, - float position, - GError **error); - -void -clutter_video_texture_stop (ClutterVideoTexture *video_texture); - -gboolean -clutter_video_texture_can_set_volume (ClutterVideoTexture *video_texture); - -void -clutter_video_texture_set_volume (ClutterVideoTexture *video_texture, - int volume); -int -clutter_video_texture_get_volume (ClutterVideoTexture *video_texture); - -gint64 -clutter_video_texture_get_current_time (ClutterVideoTexture *video_texture); - -gint64 -clutter_video_texture_get_stream_length (ClutterVideoTexture *video_texture); - -gboolean -clutter_video_texture_is_playing (ClutterVideoTexture *video_texture); - -gboolean -clutter_video_texture_is_seekable (ClutterVideoTexture * video_texture); - -float -clutter_video_texture_get_position (ClutterVideoTexture *video_texture); - -void -clutter_video_texture_set_aspect_ratio (ClutterVideoTexture *video_texture, - ClutterVideoTextureAspectRatio ratio); - -ClutterVideoTextureAspectRatio -clutter_video_texture_get_aspect_ratio (ClutterVideoTexture *video_texture); - -/* Metadata - * FIXME: This should probably go in some kind of genric 'Media' class - * You would open the media, get a media object and then pass - * that to the video texture. media object could handle being - * just a metadata reader etc... -*/ -typedef enum -{ - CLUTTER_INFO_TITLE, - CLUTTER_INFO_ARTIST, - CLUTTER_INFO_YEAR, - CLUTTER_INFO_ALBUM, - CLUTTER_INFO_DURATION, - CLUTTER_INFO_TRACK_NUMBER, - /* Video */ - CLUTTER_INFO_HAS_VIDEO, - CLUTTER_INFO_DIMENSION_X, - CLUTTER_INFO_DIMENSION_Y, - CLUTTER_INFO_VIDEO_BITRATE, - CLUTTER_INFO_VIDEO_CODEC, - CLUTTER_INFO_FPS, - /* Audio */ - CLUTTER_INFO_HAS_AUDIO, - CLUTTER_INFO_AUDIO_BITRATE, - CLUTTER_INFO_AUDIO_CODEC, -} ClutterVideoTextureMetadataType; - - -void -clutter_video_texture_get_metadata (ClutterVideoTexture *video_texture, - ClutterVideoTextureMetadataType type, - GValue *value); - G_END_DECLS #endif diff --git a/clutter/clutter.h b/clutter/clutter.h index 9b6cd6486..51103213f 100644 --- a/clutter/clutter.h +++ b/clutter/clutter.h @@ -3,6 +3,7 @@ #include "clutter-keysyms.h" #include "clutter-main.h" +#include "clutter-media.h" #include "clutter-color.h" #include "clutter-util.h" #include "clutter-event.h" diff --git a/examples/test-video.c b/examples/test-video.c index e2f2fe112..a6631ef2b 100644 --- a/examples/test-video.c +++ b/examples/test-video.c @@ -2,19 +2,6 @@ ClutterElement *rect; /* um... */ -gboolean -foo (gpointer data) -{ - static int i = 0; - - clutter_element_set_opacity (CLUTTER_ELEMENT(data), i); - - i += 10; - - if (i>255) i = 0; - - return TRUE; -} void input_cb (ClutterStage *stage, ClutterEvent *event, @@ -27,12 +14,12 @@ void input_cb (ClutterStage *stage, { if (paused) { - clutter_video_texture_play (vtex, NULL); + clutter_media_set_playing (CLUTTER_MEDIA(vtex), TRUE); paused = 0; } else { - clutter_video_texture_pause (vtex); + clutter_media_set_playing (CLUTTER_MEDIA(vtex), FALSE); paused = 1; } } @@ -67,29 +54,30 @@ size_change (ClutterTexture *texture, stage_geom.width, new_height); - clutter_element_set_opacity (CLUTTER_ELEMENT (texture), 50); + // clutter_element_set_opacity (CLUTTER_ELEMENT (texture), 50); printf("*** Pos set to +%i+%i , %ix%i ***\n", 0, new_y, stage_geom.width, new_height); } void -tick (ClutterVideoTexture *cvt, - gint64 current_time, - gint64 stream_length, - float current_position, - gboolean seekable, - gpointer userdata) +tick (GObject *object, + GParamSpec *pspec, + ClutterLabel *label) { - gint w, h; - gchar buf[256]; - ClutterLabel *label = CLUTTER_LABEL(userdata); + ClutterVideoTexture *vtex; + gint w, h, position, duration; + gchar buf[256]; - g_snprintf(buf, 256, "%lli/%lli secs", - current_time / 1000, - stream_length / 1000); + vtex = CLUTTER_VIDEO_TEXTURE(object); + + position = clutter_media_get_position (CLUTTER_MEDIA(vtex)); + duration = clutter_media_get_duration (CLUTTER_MEDIA(vtex)); + + g_snprintf(buf, 256, "%i / %i", position, duration); clutter_label_set_text (label, buf); + clutter_texture_get_base_size (CLUTTER_TEXTURE(label), &w, &h); clutter_element_set_size(rect, w+10, h+10); } @@ -99,8 +87,8 @@ main (int argc, char *argv[]) { ClutterElement *label, *vtexture, *ctexture; ClutterElement *stage; - ClutterColor rect_color = { 0xde, 0xde, 0xdf, 0xaa }; - ClutterColor stage_color = { 0xff, 0xff, 0xff, 0x00 }; + ClutterColor rect_color = { 0xde, 0xde, 0xdf, 0xaa }; + ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 }; GError *err = NULL; if (argc < 2) @@ -110,6 +98,8 @@ main (int argc, char *argv[]) vtexture = clutter_video_texture_new (); + clutter_media_set_filename(CLUTTER_MEDIA(vtexture), argv[1]); + stage = clutter_stage_get_default (); /* Broken.. @@ -117,8 +107,6 @@ main (int argc, char *argv[]) g_object_set(vtexture, "repeat-y", TRUE, NULL); */ - printf("tiled okey\n"); - if (vtexture == NULL || err != NULL) { g_error("failed to create vtexture, err: %s", err->message); @@ -133,39 +121,27 @@ main (int argc, char *argv[]) clutter_element_set_position(rect, 5, 5); ctexture = clutter_clone_texture_new (CLUTTER_TEXTURE(vtexture)); + clutter_element_set_opacity (CLUTTER_ELEMENT (ctexture), 100); clutter_element_set_size (ctexture, 640, 50); clutter_element_set_position (ctexture, 0, 430); - /* - clutter_element_set_clip (CLUTTER_ELEMENT(clutter_stage()), - 0, 0, 100, 100); - */ - - clutter_video_texture_open(CLUTTER_VIDEO_TEXTURE(vtexture), - argv[1], - NULL, - NULL); - - clutter_group_add (CLUTTER_GROUP (stage), vtexture); - clutter_group_add (CLUTTER_GROUP (stage), rect); - clutter_group_add (CLUTTER_GROUP (stage), label); - clutter_group_add (CLUTTER_GROUP (stage), ctexture); + clutter_group_add_many (CLUTTER_GROUP (stage), + vtexture, rect, label, ctexture, NULL); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); g_signal_connect (stage, "input-event", G_CALLBACK (input_cb), vtexture); - + clutter_group_show_all (CLUTTER_GROUP (stage)); + clutter_media_set_playing (CLUTTER_MEDIA(vtexture), TRUE); - if (!clutter_video_texture_play(CLUTTER_VIDEO_TEXTURE(vtexture), NULL)) - g_error("failed to play vtexture"); - - g_signal_connect (vtexture, "tick", - G_CALLBACK (tick), + g_signal_connect (CLUTTER_MEDIA(vtexture), + "notify::position", + G_CALLBACK (tick), label); g_object_set (G_OBJECT(vtexture), "sync-size", FALSE, NULL); @@ -174,8 +150,6 @@ main (int argc, char *argv[]) "size-change", G_CALLBACK (size_change), NULL); - /* g_timeout_add (100, foo, vtexture); */ - clutter_main(); g_object_unref (stage); diff --git a/examples/video-cube.c b/examples/video-cube.c index f57f3a448..ab4fd857c 100644 --- a/examples/video-cube.c +++ b/examples/video-cube.c @@ -254,17 +254,14 @@ main (int argc, char *argv[]) g_error("failed to create vtexture, err: %s", err->message); } - clutter_video_texture_open(CLUTTER_VIDEO_TEXTURE(vtexture), - argv[1], - NULL, - NULL); + + clutter_media_set_filename (CLUTTER_MEDIA(vtexture), argv[1]); clutter_group_add (CLUTTER_GROUP (stage), texture); clutter_group_add (CLUTTER_GROUP (stage), vtexture); clutter_group_show_all (CLUTTER_GROUP (stage)); - if (!clutter_video_texture_play(CLUTTER_VIDEO_TEXTURE(vtexture), NULL)) - g_error("failed to play vtexture"); + clutter_media_set_playing(CLUTTER_MEDIA(vtexture), TRUE); clutter_main();