[clutter-gst/wip/separate-logic] video-texture: Separate out the playbin2 logic.
- From: Damien Lespiau <dlespiau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter-gst/wip/separate-logic] video-texture: Separate out the playbin2 logic.
- Date: Fri, 19 Aug 2011 10:18:03 +0000 (UTC)
commit 79a275252b0f8c02c4a3db84643d8c236b299690
Author: Damien Lespiau <damien lespiau intel com>
Date: Thu Aug 18 14:25:04 2011 +0100
video-texture: Separate out the playbin2 logic.
The end goal is to be able to reuse the logic around playbin2 in a
player that has nothing to do with Clutter.
clutter-gst/Makefile.am | 2 +
clutter-gst/clutter-gst-player.c | 409 ++++++++++++++++++++
clutter-gst/clutter-gst-player.h | 117 ++++++
clutter-gst/clutter-gst-video-texture.c | 618 ++++++++-----------------------
4 files changed, 690 insertions(+), 456 deletions(-)
---
diff --git a/clutter-gst/Makefile.am b/clutter-gst/Makefile.am
index 15e4122..49575d7 100644
--- a/clutter-gst/Makefile.am
+++ b/clutter-gst/Makefile.am
@@ -31,6 +31,7 @@ source_h = \
source_priv_h = \
$(srcdir)/clutter-gst-debug.h \
$(srcdir)/clutter-gst-marshal.h \
+ $(srcdir)/clutter-gst-player.h \
$(srcdir)/clutter-gst-private.h \
$(srcdir)/clutter-gst-shaders.h \
$(NULL)
@@ -38,6 +39,7 @@ source_priv_h = \
source_c = \
$(srcdir)/clutter-gst-debug.c \
$(srcdir)/clutter-gst-marshal.c \
+ $(srcdir)/clutter-gst-player.c \
$(srcdir)/clutter-gst-video-sink.c \
$(srcdir)/clutter-gst-video-texture.c \
$(srcdir)/clutter-gst-util.c \
diff --git a/clutter-gst/clutter-gst-player.c b/clutter-gst/clutter-gst-player.c
new file mode 100644
index 0000000..a577093
--- /dev/null
+++ b/clutter-gst/clutter-gst-player.c
@@ -0,0 +1,409 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * clutter-gst-player.c - Wrap some convenience functions around playbin2
+ *
+ * Authored By Damien Lespiau <damien lespiau intel com>
+ *
+ * Copyright (C) 2011 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 <gst/video/video.h>
+#include <gst/tag/tag.h>
+#include <gst/interfaces/streamvolume.h>
+
+#include "clutter-gst-debug.h"
+#include "clutter-gst-marshal.h"
+#include "clutter-gst-player.h"
+
+G_DEFINE_TYPE (ClutterGstPlayer, clutter_gst_player, G_TYPE_OBJECT)
+
+#define PLAYER_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ CLUTTER_GST_TYPE_PLAYER, \
+ ClutterGstPlayerPrivate))
+
+static void player_configure_buffering_timeout (Player *player,
+ guint ms);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+enum
+{
+ DOWNLOAD_BUFFERING,
+
+ LAST_SIGNAL
+};
+
+struct _ClutterGstPlayerPrivate
+{
+ int dummy;
+};
+
+static void
+player_clear_download_buffering (Player *player)
+{
+ if (player->download_buffering_element)
+ {
+ g_object_unref (player->download_buffering_element);
+ player->download_buffering_element = NULL;
+ }
+ player_configure_buffering_timeout (player, 0);
+ player->in_download_buffering = FALSE;
+ player->virtual_stream_buffer_signalled = 0;
+}
+
+static gboolean
+player_buffering_timeout (gpointer data)
+{
+ Player *player = (Player *) data;
+ gdouble start_d, stop_d, seconds_buffered;
+ gint64 start, stop, left;
+ GstState current_state;
+ GstElement *element;
+ GstQuery *query;
+ gboolean res;
+
+ element = player->download_buffering_element;
+ if (element == NULL)
+ element = player->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)
+ {
+ player->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], 0, start_d, stop_d);
+
+ /* handle the "virtual stream buffer" and the associated pipeline state.
+ * We pause the pipeline until 2s of content is buffered. With the current
+ * implementation of queue2, start is always 0, so even when we seek in
+ * the stream the start position of the download-buffering signal is
+ * always 0.0. FIXME: look at gst_query_parse_nth_buffering_range () */
+ seconds_buffered = player->duration * (stop_d - start_d);
+ player->buffer_fill = seconds_buffered / 2.0;
+ player->buffer_fill = CLAMP (player->buffer_fill, 0.0, 1.0);
+
+ if (player->buffer_fill != 1.0 || !player->virtual_stream_buffer_signalled)
+ {
+ CLUTTER_GST_NOTE (BUFFERING, "buffer holds %0.2fs of data, buffer-fill "
+ "is %.02f", seconds_buffered, player->buffer_fill);
+
+ g_object_notify (G_OBJECT (video_texture), "buffer-fill");
+
+ if (player->buffer_fill == 1.0)
+ player->virtual_stream_buffer_signalled = 1;
+ }
+
+ gst_element_get_state (player->pipeline, ¤t_state, NULL, 0);
+ if (player->buffer_fill < 1.0)
+ {
+ if (current_state != GST_STATE_PAUSED)
+ {
+ CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline");
+ gst_element_set_state (player->pipeline, GST_STATE_PAUSED);
+ }
+ }
+ else
+ {
+ if (current_state != player->target_state)
+ {
+ CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline");
+ gst_element_set_state (player->pipeline, player->target_state);
+ }
+ }
+
+ /* the file has finished downloading */
+ if (left == G_GINT64_CONSTANT (0))
+ {
+ player->buffering_timeout_id = 0;
+
+ player_clear_download_buffering (player);
+ gst_query_unref (query);
+ return FALSE;
+ }
+
+ gst_query_unref (query);
+ return TRUE;
+}
+
+static void
+player_configure_buffering_timeout (Player *player,
+ guint ms)
+{
+ if (player->buffering_timeout_id)
+ {
+ g_source_remove (player->buffering_timeout_id);
+ player->buffering_timeout_id = 0;
+ }
+
+ if (ms)
+ {
+ player->buffering_timeout_id =
+ g_timeout_add (ms, player_buffering_timeout, player);
+ }
+}
+
+static void
+bus_message_error_cb (GstBus *bus,
+ GstMessage *message,
+ ClutterGstPlayer *player)
+{
+ gst_element_set_state(player->pipeline, GST_STATE_NULL);
+}
+
+static void
+bus_message_eos_cb (GstBus *bus,
+ GstMessage *message,
+ ClutterGstPlayer *player)
+{
+ gst_element_set_state(player->pipeline, GST_STATE_READY);
+ player->in_eos = TRUE;
+}
+
+static void
+bus_message_buffering_cb (GstBus *bus,
+ GstMessage *message,
+ ClutterGstPlayer *player)
+{
+ GstBufferingMode mode;
+ GstState current_state;
+ gint buffer_percent;
+
+ gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL);
+
+ if (mode != GST_BUFFERING_DOWNLOAD)
+ player->in_download_buffering = FALSE;
+
+ switch (mode)
+ {
+ case GST_BUFFERING_STREAM:
+ gst_message_parse_buffering (message, &buffer_percent);
+ player->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0);
+
+ CLUTTER_GST_NOTE (BUFFERING, "buffer-fill: %.02f", player->buffer_fill);
+
+ /* The playbin2 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 (player->pipeline, ¤t_state, NULL, 0);
+
+ if (player->buffer_fill < 1.0)
+ {
+ if (current_state != GST_STATE_PAUSED)
+ {
+ CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline");
+ gst_element_set_state (player->pipeline, GST_STATE_PAUSED);
+ }
+ }
+ else
+ {
+ if (current_state != player->target_state)
+ {
+ CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline");
+ gst_element_set_state (player->pipeline, player->target_state);
+ }
+ }
+
+ g_object_notify (G_OBJECT (video_texture), "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 (player->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 (player->pipeline, GST_STATE_PAUSED);
+ player->buffer_fill = 0.0;
+ g_object_notify (G_OBJECT (video_texture), "buffer-fill");
+
+ player->download_buffering_element = g_object_ref (message->src);
+ player->in_download_buffering = TRUE;
+ player->virtual_stream_buffer_signalled = 0;
+ break;
+
+ case GST_BUFFERING_TIMESHIFT:
+ case GST_BUFFERING_LIVE:
+ default:
+ g_warning ("Buffering mode %d not handled", mode);
+ break;
+ }
+}
+
+static void
+on_source_changed (GstElement *pipeline,
+ GParamSpec *pspec,
+ ClutterGstPlayer *player)
+{
+ player_set_user_agent (player, player->user_agent);
+}
+
+/*
+ * GObject implementation
+ */
+
+static void
+clutter_gst_player_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+clutter_gst_player_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_gst_player_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (clutter_gst_player_parent_class)->finalize (object);
+}
+
+static void
+clutter_gst_player_class_init (ClutterGstPlayerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ClutterGstPlayerPrivate));
+
+ object_class->get_property = clutter_gst_player_get_property;
+ object_class->set_property = clutter_gst_player_set_property;
+ object_class->finalize = clutter_gst_player_finalize;
+
+ signals[DOWNLOAD_BUFFERING] =
+ g_signal_new ("download-buffering",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterGstVideoTextureClass,
+ download_buffering),
+ NULL, NULL,
+ _clutter_gst_marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+}
+
+static void
+clutter_gst_player_init (ClutterGstPlayer *self)
+{
+ self->priv = PLAYER_PRIVATE (self);
+
+ self->in_seek = FALSE;
+ self->is_changing_uri = FALSE;
+ self->in_download_buffering = FALSE;
+
+ self->pipeline = gst_element_factory_make ("playbin2", "pipeline");
+ if (!self->pipeline)
+ {
+ g_critical ("Unable to create playbin2 element");
+ return FALSE;
+ }
+
+ g_signal_connect (self->pipeline, "notify::source",
+ G_CALLBACK (on_source_changed), self);
+
+ /* We default to not playing until someone calls set_playing(TRUE) */
+ self->target_state = GST_STATE_PAUSED;
+
+ /* Default to a fast seek, ie. same effect than set_seek_flags (NONE); */
+ self->seek_flags = GST_SEEK_FLAG_KEY_UNIT;
+
+ self->bus = gst_pipeline_get_bus (GST_PIPELINE (self->pipeline));
+
+ gst_bus_add_signal_watch (self->bus);
+
+ g_signal_connect_object (self->bus, "message::error",
+ G_CALLBACK (bus_message_error_cb),
+ self, 0);
+ g_signal_connect_object (self->bus, "message::eos",
+ G_CALLBACK (bus_message_eos_cb),
+ self, 0);
+ g_signal_connect_object (self->bus, "message::buffering",
+ G_CALLBACK (bus_message_buffering_cb),
+ video_texture, 0);
+ g_signal_connect_object (self->bus, "message::duration",
+ G_CALLBACK (bus_message_duration_cb),
+ video_texture, 0);
+ g_signal_connect_object (self->bus, "message::state-changed",
+ G_CALLBACK (bus_message_state_change_cb),
+ video_texture, 0);
+ g_signal_connect_object (self->bus, "message::async-done",
+ G_CALLBACK (bus_message_async_done_cb),
+ video_texture, 0);
+
+ g_signal_connect (self->pipeline, "notify::volume",
+ G_CALLBACK (on_volume_changed), video_texture);
+
+ g_signal_connect (self->pipeline, "audio-changed",
+ G_CALLBACK (on_audio_changed), video_texture);
+ g_signal_connect (self->pipeline, "audio-tags-changed",
+ G_CALLBACK (on_audio_tags_changed), video_texture);
+ g_signal_connect (self->pipeline, "notify::current-audio",
+ G_CALLBACK (on_current_audio_changed), video_texture);
+
+ gst_object_unref (GST_OBJECT (self->bus));
+}
+
+ClutterGstPlayer *
+clutter_gst_player_new (void)
+{
+ return g_object_new (CLUTTER_GST_TYPE_PLAYER, NULL);
+}
diff --git a/clutter-gst/clutter-gst-player.h b/clutter-gst/clutter-gst-player.h
new file mode 100644
index 0000000..7a8632d
--- /dev/null
+++ b/clutter-gst/clutter-gst-player.h
@@ -0,0 +1,117 @@
+/*
+ * Clutter-GStreamer.
+ *
+ * GStreamer integration library for Clutter.
+ *
+ * clutter-gst-player.c - Wrap some convenience functions around playbin2
+ *
+ * Authored By Damien Lespiau <damien lespiau intel com>
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef __CLUTTER_GST_PLAYER_H__
+#define __CLUTTER_GST_PLAYER_H__
+
+#include <glib-object.h>
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_GST_TYPE_PLAYER clutter_gst_player_get_type()
+
+#define CLUTTER_GST_PLAYER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ CLUTTER_GST_TYPE_PLAYER, ClutterGstPlayer))
+
+#define CLUTTER_GST_PLAYER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ CLUTTER_GST_TYPE_PLAYER, ClutterGstPlayerClass))
+
+#define CLUTTER_GST_IS_PLAYER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ CLUTTER_GST_TYPE_PLAYER))
+
+#define CLUTTER_GST_IS_PLAYER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ CLUTTER_GST_TYPE_PLAYER))
+
+#define CLUTTER_GST_PLAYER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ CLUTTER_GST_TYPE_PLAYER, ClutterGstPlayerClass))
+
+typedef struct _ClutterGstPlayer ClutterGstPlayer;
+typedef struct _ClutterGstPlayerClass ClutterGstPlayerClass;
+typedef struct _ClutterGstPlayerPrivate ClutterGstPlayerPrivate;
+
+struct _ClutterGstPlayer
+{
+ GObject parent;
+
+ GstElement *pipeline;
+ GstBus *bus;
+
+ gchar *uri;
+
+ 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;
+};
+
+struct _ClutterGstPlayerClass
+{
+ GObjectClass parent_class;
+};
+
+GType clutter_gst_player_get_type (void) G_GNUC_CONST;
+
+ClutterGstPlayer *clutter_gst_player_new (void);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_GST_PLAYER_H__ */
diff --git a/clutter-gst/clutter-gst-video-texture.c b/clutter-gst/clutter-gst-video-texture.c
index 6f56cf7..dce54e1 100644
--- a/clutter-gst/clutter-gst-video-texture.c
+++ b/clutter-gst/clutter-gst-video-texture.c
@@ -43,14 +43,11 @@
#include <glib.h>
#include <gio/gio.h>
-#include <gst/gst.h>
-#include <gst/video/video.h>
-#include <gst/tag/tag.h>
-#include <gst/interfaces/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"
#include "clutter-gst-video-sink.h"
#include "clutter-gst-video-texture.h"
@@ -80,31 +77,9 @@ static guint signals[LAST_SIGNAL] = { 0, };
struct _ClutterGstVideoTexturePrivate
{
- GstElement *pipeline;
- GstBus *bus;
+ ClutterGstPlayer *player;
- gchar *uri;
-
- guint can_seek : 1;
- guint in_seek : 1;
guint is_idle : 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;
/* width / height (in pixels) of the frame data before applying the pixel
* aspect ratio */
@@ -118,22 +93,8 @@ struct _ClutterGstVideoTexturePrivate
guint texture_width;
guint texture_height;
- /* 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;
-
CoglHandle idle_material;
CoglColor idle_color_unpre;
-
- GstSeekFlags seek_flags; /* flags for the seek in set_progress(); */
-
- GstElement *download_buffering_element;
-
- GList *audio_streams;
};
enum {
@@ -191,8 +152,6 @@ gst_state_to_string (GstState state)
static void set_subtitle_uri (ClutterGstVideoTexture *video_texture,
const gchar *uri);
-static void configure_buffering_timeout (ClutterGstVideoTexture *video_texture,
- guint ms);
static void
free_string_list (GList **listp)
@@ -209,133 +168,6 @@ free_string_list (GList **listp)
*listp = NULL;
}
-static void
-clear_download_buffering (ClutterGstVideoTexture *video_texture)
-{
- ClutterGstVideoTexturePrivate *priv = video_texture->priv;
-
- if (priv->download_buffering_element)
- {
- g_object_unref (priv->download_buffering_element);
- priv->download_buffering_element = NULL;
- }
- configure_buffering_timeout (video_texture, 0);
- priv->in_download_buffering = FALSE;
- priv->virtual_stream_buffer_signalled = 0;
-}
-
-static gboolean
-buffering_timeout (gpointer data)
-{
- ClutterGstVideoTexture *video_texture = CLUTTER_GST_VIDEO_TEXTURE (data);
- ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- gdouble start_d, stop_d, seconds_buffered;
- gint64 start, stop, left;
- 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;
- clear_download_buffering (video_texture);
- 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 (video_texture,
- signals[DOWNLOAD_BUFFERING], 0, start_d, stop_d);
-
- /* handle the "virtual stream buffer" and the associated pipeline state.
- * We pause the pipeline until 2s of content is buffered. With the current
- * implementation of queue2, start is always 0, so even when we seek in
- * the stream the start position of the download-buffering signal is
- * always 0.0. FIXME: look at gst_query_parse_nth_buffering_range () */
- seconds_buffered = priv->duration * (stop_d - start_d);
- priv->buffer_fill = seconds_buffered / 2.0;
- priv->buffer_fill = CLAMP (priv->buffer_fill, 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 (video_texture), "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;
-
- clear_download_buffering (video_texture);
- gst_query_unref (query);
- return FALSE;
- }
-
- gst_query_unref (query);
- return TRUE;
-}
-
-static void
-configure_buffering_timeout (ClutterGstVideoTexture *video_texture,
- guint ms)
-{
- ClutterGstVideoTexturePrivate *priv = video_texture->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, buffering_timeout, video_texture);
- }
-}
-
/* Clutter 1.4 has this symbol, we don't want to depend on 1.4 just for that
* just yet */
static void
@@ -400,17 +232,16 @@ create_black_idle_material (ClutterGstVideoTexture *video_texture)
}
static void
-set_user_agent (ClutterGstVideoTexture *video_texture,
- const gchar *user_agent)
+player_set_user_agent (Player *player,
+ const gchar *user_agent)
{
- ClutterGstVideoTexturePrivate *priv = video_texture->priv;
GstElement *source;
GParamSpec *pspec;
if (user_agent == NULL)
return;
- g_object_get (priv->pipeline, "source", &source, NULL);
+ g_object_get (player->pipeline, "source", &source, NULL);
if (source == NULL)
return;
@@ -499,7 +330,7 @@ autoload_subtitle (ClutterGstVideoTexture *video_texture,
CLUTTER_GST_NOTE (MEDIA, "found subtitle: %s", suburi);
- g_object_set (priv->pipeline, "suburi", suburi, NULL);
+ g_object_set (priv->player.pipeline, "suburi", suburi, NULL);
g_free (suburi);
g_object_unref (candidate);
@@ -522,7 +353,9 @@ query_duration (ClutterGstVideoTexture *video_texture)
gint64 duration;
gdouble new_duration, difference;
- success = gst_element_query_duration (priv->pipeline, &format, &duration);
+ success = gst_element_query_duration (priv->player.pipeline,
+ &format,
+ &duration);
if (G_UNLIKELY (success != TRUE))
return;
@@ -532,11 +365,11 @@ query_duration (ClutterGstVideoTexture *video_texture)
* 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);
+ difference = ABS (priv->player.duration - new_duration);
if (difference > 1e-3)
{
CLUTTER_GST_NOTE (MEDIA, "duration: %.02f", new_duration);
- priv->duration = new_duration;
+ priv->player.duration = new_duration;
if (difference > 1.0)
g_object_notify (G_OBJECT (video_texture), "duration");
@@ -553,26 +386,26 @@ set_uri (ClutterGstVideoTexture *video_texture,
CLUTTER_GST_NOTE (MEDIA, "setting uri %s", uri);
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return;
- g_free (priv->uri);
+ g_free (priv->player.uri);
- priv->in_eos = FALSE;
- priv->in_error = FALSE;
+ priv->player.in_eos = FALSE;
+ priv->player.in_error = FALSE;
if (uri)
{
- priv->uri = g_strdup (uri);
+ priv->player.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)
+ if (priv->player.tick_timeout_id == 0)
{
- priv->tick_timeout_id =
+ priv->player.tick_timeout_id =
g_timeout_add (TICK_TIMEOUT, tick_timeout, self);
}
@@ -581,59 +414,59 @@ set_uri (ClutterGstVideoTexture *video_texture,
autoload_subtitle (video_texture, uri);
/* reset the states of download buffering */
- clear_download_buffering (video_texture);
+ player_clear_download_buffering (&priv->player);
}
else
{
- priv->uri = NULL;
+ priv->player.uri = NULL;
set_subtitle_uri (video_texture, NULL);
- if (priv->tick_timeout_id)
+ if (priv->player.tick_timeout_id)
{
- g_source_remove (priv->tick_timeout_id);
- priv->tick_timeout_id = 0;
+ g_source_remove (priv->player.tick_timeout_id);
+ priv->player.tick_timeout_id = 0;
}
- if (priv->buffering_timeout_id)
+ if (priv->player.buffering_timeout_id)
{
- g_source_remove (priv->buffering_timeout_id);
- priv->buffering_timeout_id = 0;
+ g_source_remove (priv->player.buffering_timeout_id);
+ priv->player.buffering_timeout_id = 0;
}
- if (priv->download_buffering_element)
+ if (priv->player.download_buffering_element)
{
- g_object_unref (priv->download_buffering_element);
- priv->download_buffering_element = NULL;
+ g_object_unref (priv->player.download_buffering_element);
+ priv->player.download_buffering_element = NULL;
}
}
- priv->can_seek = FALSE;
- priv->duration = 0.0;
- priv->stacked_progress = 0.0;
- priv->target_progress = 0.0;
+ priv->player.can_seek = FALSE;
+ priv->player.duration = 0.0;
+ priv->player.stacked_progress = 0.0;
+ priv->player.target_progress = 0.0;
CLUTTER_GST_NOTE (MEDIA, "setting URI: %s", uri);
if (uri)
{
- gst_element_get_state (priv->pipeline, &state, &pending, 0);
+ gst_element_get_state (priv->player.pipeline, &state, &pending, 0);
if (pending)
state = pending;
- gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+ gst_element_set_state (priv->player.pipeline, GST_STATE_NULL);
- g_object_set (priv->pipeline, "uri", uri, NULL);
+ g_object_set (priv->player.pipeline, "uri", uri, NULL);
- gst_element_set_state (priv->pipeline, state);
+ gst_element_set_state (priv->player.pipeline, state);
- priv->is_changing_uri = TRUE;
+ priv->player.is_changing_uri = TRUE;
}
else
{
priv->is_idle = TRUE;
- gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+ gst_element_set_state (priv->player.pipeline, GST_STATE_NULL);
clutter_actor_queue_redraw (CLUTTER_ACTOR (video_texture));
}
@@ -646,7 +479,7 @@ set_uri (ClutterGstVideoTexture *video_texture,
g_object_notify (self, "duration");
g_object_notify (self, "progress");
- free_string_list (&priv->audio_streams);
+ free_string_list (&priv->player.audio_streams);
CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
g_object_notify (self, "audio-streams");
}
@@ -657,21 +490,21 @@ set_playing (ClutterGstVideoTexture *video_texture,
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return;
CLUTTER_GST_NOTE (MEDIA, "set playing: %d", playing);
- priv->in_error = FALSE;
- priv->in_eos = FALSE;
+ priv->player.in_error = FALSE;
+ priv->player.in_eos = FALSE;
- priv->target_state = playing ? GST_STATE_PLAYING : GST_STATE_PAUSED;
+ priv->player.target_state = playing ? GST_STATE_PLAYING : GST_STATE_PAUSED;
- if (priv->uri)
+ if (priv->player.uri)
{
- priv->in_seek = FALSE;
+ priv->player.in_seek = FALSE;
- gst_element_set_state (priv->pipeline, priv->target_state);
+ gst_element_set_state (priv->player.pipeline, priv->player.target_state);
}
else
{
@@ -690,10 +523,10 @@ get_playing (ClutterGstVideoTexture *video_texture)
GstState state, pending;
gboolean playing;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return FALSE;
- gst_element_get_state (priv->pipeline, &state, &pending, 0);
+ gst_element_get_state (priv->player.pipeline, &state, &pending, 0);
if (pending)
playing = (pending == GST_STATE_PLAYING);
@@ -713,34 +546,34 @@ set_progress (ClutterGstVideoTexture *video_texture,
GstQuery *duration_q;
gint64 position;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return;
CLUTTER_GST_NOTE (MEDIA, "set progress: %.02f", progress);
- priv->in_eos = FALSE;
- priv->target_progress = progress;
+ priv->player.in_eos = FALSE;
+ priv->player.target_progress = progress;
- if (priv->in_download_buffering)
+ if (priv->player.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;
+ priv->player.virtual_stream_buffer_signalled = 0;
}
- if (priv->in_seek || priv->is_idle || priv->is_changing_uri)
+ if (priv->player.in_seek || priv->is_idle || priv->player.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;
+ priv->player.stacked_progress = progress;
return;
}
duration_q = gst_query_new_duration (GST_FORMAT_TIME);
- if (gst_element_query (priv->pipeline, duration_q))
+ if (gst_element_query (priv->player.pipeline, duration_q))
{
gint64 duration = 0;
@@ -753,16 +586,16 @@ set_progress (ClutterGstVideoTexture *video_texture,
gst_query_unref (duration_q);
- gst_element_seek (priv->pipeline,
+ gst_element_seek (priv->player.pipeline,
1.0,
GST_FORMAT_TIME,
- GST_SEEK_FLAG_FLUSH | priv->seek_flags,
+ GST_SEEK_FLAG_FLUSH | priv->player.seek_flags,
GST_SEEK_TYPE_SET,
position,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
- priv->in_seek = TRUE;
- priv->stacked_progress = 0.0;
+ priv->player.in_seek = TRUE;
+ priv->player.stacked_progress = 0.0;
CLUTTER_GST_NOTE (MEDIA, "set progress (seeked): %.02f", progress);
}
@@ -774,19 +607,19 @@ get_progress (ClutterGstVideoTexture *video_texture)
GstQuery *position_q, *duration_q;
gdouble progress;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return 0.0;
/* when hitting an error or after an EOS, playbin2 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)
+ if (priv->player.in_error)
{
CLUTTER_GST_NOTE (MEDIA, "get progress (error): 0.0");
return 0.0;
}
- if (priv->in_eos)
+ if (priv->player.in_eos)
{
CLUTTER_GST_NOTE (MEDIA, "get progress (eos): 1.0");
return 1.0;
@@ -795,18 +628,18 @@ get_progress (ClutterGstVideoTexture *video_texture)
/* When seeking, the progress returned by playbin2 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)
+ if (priv->player.in_seek || priv->player.is_changing_uri)
{
CLUTTER_GST_NOTE (MEDIA, "get progress (target): %.02f",
- priv->target_progress);
- return priv->target_progress;
+ priv->player.target_progress);
+ return priv->player.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))
+ if (gst_element_query (priv->player.pipeline, position_q) &&
+ gst_element_query (priv->player.pipeline, duration_q))
{
gint64 position, duration;
@@ -834,12 +667,12 @@ set_subtitle_uri (ClutterGstVideoTexture *video_texture,
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return;
CLUTTER_GST_NOTE (MEDIA, "setting subtitle URI: %s", uri);
- g_object_set (priv->pipeline, "suburi", uri, NULL);
+ g_object_set (priv->player.pipeline, "suburi", uri, NULL);
}
static void
@@ -848,14 +681,14 @@ set_subtitle_font_name (ClutterGstVideoTexture *video_texture,
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (!priv->pipeline)
+ if (!priv->player.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);
+ g_free (priv->player.font_name);
+ priv->player.font_name = g_strdup (font_name);
+ g_object_set (priv->player.pipeline, "subtitle-font-desc", font_name, NULL);
}
static void
@@ -864,13 +697,13 @@ set_audio_volume (ClutterGstVideoTexture *video_texture,
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (!priv->pipeline)
- return;
+ if (!priv->player.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_set_volume (GST_STREAM_VOLUME (priv->player.pipeline),
GST_STREAM_VOLUME_FORMAT_CUBIC,
volume);
g_object_notify (G_OBJECT (video_texture), "audio-volume");
@@ -881,12 +714,12 @@ get_audio_volume (ClutterGstVideoTexture *video_texture)
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (!priv->pipeline)
+ if (!priv->player.pipeline)
return 0.0;
- CLUTTER_GST_NOTE (MEDIA, "get volume: %.02f", priv->volume);
+ CLUTTER_GST_NOTE (MEDIA, "get volume: %.02f", priv->player.volume);
- return priv->volume;
+ return priv->player.volume;
}
static void
@@ -1151,16 +984,16 @@ clutter_gst_video_texture_dispose (GObject *object)
/* start by doing the usual clean up when not wanting to play an URI */
set_uri (self, NULL);
- if (priv->bus)
+ if (priv->player.bus)
{
- gst_bus_remove_signal_watch (priv->bus);
- priv->bus = NULL;
+ gst_bus_remove_signal_watch (priv->player.bus);
+ priv->player.bus = NULL;
}
- if (priv->pipeline)
+ if (priv->player.pipeline)
{
- gst_object_unref (GST_OBJECT (priv->pipeline));
- priv->pipeline = NULL;
+ gst_object_unref (GST_OBJECT (priv->player.pipeline));
+ priv->player.pipeline = NULL;
}
G_OBJECT_CLASS (clutter_gst_video_texture_parent_class)->dispose (object);
@@ -1175,12 +1008,12 @@ clutter_gst_video_texture_finalize (GObject *object)
self = CLUTTER_GST_VIDEO_TEXTURE (object);
priv = self->priv;
- g_free (priv->uri);
- g_free (priv->font_name);
+ g_free (priv->player.uri);
+ g_free (priv->player.font_name);
if (priv->idle_material != COGL_INVALID_HANDLE)
cogl_handle_unref (priv->idle_material);
- free_string_list (&priv->audio_streams);
+ free_string_list (&priv->player.audio_streams);
G_OBJECT_CLASS (clutter_gst_video_texture_parent_class)->finalize (object);
}
@@ -1260,7 +1093,7 @@ clutter_gst_video_texture_get_property (GObject *object,
switch (property_id)
{
case PROP_URI:
- g_value_set_string (value, priv->uri);
+ g_value_set_string (value, priv->player.uri);
break;
case PROP_PLAYING:
@@ -1272,12 +1105,12 @@ clutter_gst_video_texture_get_property (GObject *object,
break;
case PROP_SUBTITLE_URI:
- g_object_get (priv->pipeline, "suburi", &str, NULL);
+ g_object_get (priv->player.pipeline, "suburi", &str, NULL);
g_value_take_string (value, str);
break;
case PROP_SUBTITLE_FONT_NAME:
- g_value_set_string (value, priv->font_name);
+ g_value_set_string (value, priv->player.font_name);
break;
case PROP_AUDIO_VOLUME:
@@ -1285,15 +1118,15 @@ clutter_gst_video_texture_get_property (GObject *object,
break;
case PROP_CAN_SEEK:
- g_value_set_boolean (value, priv->can_seek);
+ g_value_set_boolean (value, priv->player.can_seek);
break;
case PROP_BUFFER_FILL:
- g_value_set_double (value, priv->buffer_fill);
+ g_value_set_double (value, priv->player.buffer_fill);
break;
case PROP_DURATION:
- g_value_set_double (value, priv->duration);
+ g_value_set_double (value, priv->player.duration);
break;
case PROP_IDLE_MATERIAL:
@@ -1319,7 +1152,7 @@ clutter_gst_video_texture_get_property (GObject *object,
break;
case PROP_AUDIO_STREAMS:
- g_value_set_pointer (value, priv->audio_streams);
+ g_value_set_pointer (value, priv->player.audio_streams);
break;
case PROP_AUDIO_STREAM:
@@ -1439,11 +1272,8 @@ bus_message_error_cb (GstBus *bus,
GstMessage *message,
ClutterGstVideoTexture *video_texture)
{
- GError *error = NULL;
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- gst_element_set_state(priv->pipeline, GST_STATE_NULL);
-
gst_message_parse_error (message, &error, NULL);
/* restore the idle material so we don't just display the last frame */
@@ -1464,10 +1294,6 @@ bus_message_eos_cb (GstBus *bus,
CLUTTER_GST_NOTE (MEDIA, "EOS");
- priv->in_eos = TRUE;
-
- gst_element_set_state(priv->pipeline, GST_STATE_READY);
-
/* restore the idle material so we don't just display the last frame */
priv->is_idle = TRUE;
clutter_actor_queue_redraw (CLUTTER_ACTOR (video_texture));
@@ -1477,87 +1303,6 @@ bus_message_eos_cb (GstBus *bus,
}
static void
-bus_message_buffering_cb (GstBus *bus,
- GstMessage *message,
- ClutterGstVideoTexture *video_texture)
-{
- ClutterGstVideoTexturePrivate *priv = video_texture->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_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);
-
- /* The playbin2 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 (video_texture), "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 */
- configure_buffering_timeout (video_texture, 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 (video_texture), "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:
- case GST_BUFFERING_LIVE:
- default:
- g_warning ("Buffering mode %d not handled", mode);
- break;
- }
-}
-
-static void
bus_message_duration_cb (GstBus *bus,
GstMessage *message,
ClutterGstVideoTexture *video_texture)
@@ -1583,7 +1328,7 @@ bus_message_state_change_cb (GstBus *bus,
gpointer src;
src = GST_MESSAGE_SRC (message);
- if (src != priv->pipeline)
+ if (src != priv->player.pipeline)
return;
gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
@@ -1603,7 +1348,7 @@ bus_message_state_change_cb (GstBus *bus,
/* Determine whether we can seek */
query = gst_query_new_seeking (GST_FORMAT_TIME);
- if (gst_element_query (priv->pipeline, query))
+ if (gst_element_query (priv->player.pipeline, query))
{
gboolean can_seek = FALSE;
@@ -1611,22 +1356,22 @@ bus_message_state_change_cb (GstBus *bus,
NULL,
NULL);
- priv->can_seek = (can_seek == TRUE) ? TRUE : FALSE;
+ priv->player.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;
+ if (priv->player.uri && g_str_has_prefix (priv->player.uri, "http://"))
+ priv->player.can_seek = FALSE;
else
- priv->can_seek = TRUE;
+ priv->player.can_seek = TRUE;
}
gst_query_unref (query);
- CLUTTER_GST_NOTE (MEDIA, "can-seek: %d", priv->can_seek);
+ CLUTTER_GST_NOTE (MEDIA, "can-seek: %d", priv->player.can_seek);
g_object_notify (G_OBJECT (video_texture), "can-seek");
@@ -1639,14 +1384,14 @@ bus_message_state_change_cb (GstBus *bus,
else if (new_state == GST_STATE_PLAYING)
{
priv->is_idle = FALSE;
- priv->is_changing_uri = FALSE;
+ priv->player.is_changing_uri = FALSE;
}
if (!priv->is_idle)
{
- if (priv->stacked_progress)
+ if (priv->player.stacked_progress)
{
- set_progress (video_texture, priv->stacked_progress);
+ set_progress (video_texture, priv->player.stacked_progress);
}
}
}
@@ -1658,27 +1403,19 @@ bus_message_async_done_cb (GstBus *bus,
{
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
- if (priv->in_seek)
+ if (priv->player.in_seek)
{
g_object_notify (G_OBJECT (video_texture), "progress");
- priv->in_seek = FALSE;
+ priv->player.in_seek = FALSE;
- if (priv->stacked_progress)
+ if (priv->player.stacked_progress)
{
- set_progress (video_texture, priv->stacked_progress);
+ set_progress (video_texture, priv->player.stacked_progress);
}
}
}
-static void
-on_source_changed (GstElement *pipeline,
- GParamSpec *pspec,
- ClutterGstVideoTexture *video_texture)
-{
- set_user_agent (video_texture, video_texture->priv->user_agent);
-}
-
static gboolean
on_volume_changed_main_context (gpointer data)
{
@@ -1686,9 +1423,10 @@ on_volume_changed_main_context (gpointer data)
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
gdouble volume;
- volume = gst_stream_volume_get_volume (GST_STREAM_VOLUME (priv->pipeline),
- GST_STREAM_VOLUME_FORMAT_CUBIC);
- priv->volume = volume;
+ volume =
+ gst_stream_volume_get_volume (GST_STREAM_VOLUME (priv->player.pipeline),
+ GST_STREAM_VOLUME_FORMAT_CUBIC);
+ priv->player.volume = volume;
g_object_notify (G_OBJECT (video_texture), "audio-volume");
@@ -1794,12 +1532,12 @@ on_audio_changed_main_context (gpointer data)
ClutterGstVideoTexturePrivate *priv = video_texture->priv;
GList *audio_streams;
- audio_streams = get_tags (priv->pipeline, "n-audio", "get-audio-tags");
+ audio_streams = get_tags (priv->player.pipeline, "n-audio", "get-audio-tags");
- if (!are_lists_equal (priv->audio_streams, audio_streams))
+ if (!are_lists_equal (priv->player.audio_streams, audio_streams))
{
- free_string_list (&priv->audio_streams);
- priv->audio_streams = audio_streams;
+ free_string_list (&priv->player.audio_streams);
+ priv->player.audio_streams = audio_streams;
CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
@@ -1848,23 +1586,21 @@ on_current_audio_changed (GstElement *pipeline,
g_idle_add (on_current_audio_changed_main_context, video_texture);
}
+static void
+on_player_download_buffering (ClutterGstPlayer *player,
+ gdouble start,
+ gdouble stop,
+ ClutterGstVideoTexture *video_texture)
+{
+ g_signal_emit (video_texture, signals[DOWNLOAD_BUFFERING], 0, start, stop);
+}
+
static gboolean
-lay_pipeline (ClutterGstVideoTexture *video_texture)
+setup_pipeline (GstElement *pipeline)
{
- ClutterGstVideoTexturePrivate *priv = video_texture->priv;
GstElement *audio_sink = NULL;
GstElement *video_sink = NULL;
- priv->pipeline = gst_element_factory_make ("playbin2", "pipeline");
- if (!priv->pipeline)
- {
- g_critical ("Unable to create playbin2 element");
- return FALSE;
- }
-
- g_signal_connect (priv->pipeline, "notify::source",
- G_CALLBACK (on_source_changed), video_texture);
-
/* ugh - let's go through the audio sinks
*
* FIXME - there must be a way to ask gstreamer to do this for us
@@ -1887,7 +1623,7 @@ lay_pipeline (ClutterGstVideoTexture *video_texture)
video_sink = clutter_gst_video_sink_new (CLUTTER_TEXTURE (video_texture));
g_object_set (G_OBJECT (video_sink), "qos", TRUE, "sync", TRUE, NULL);
- g_object_set (G_OBJECT (priv->pipeline),
+ g_object_set (G_OBJECT (pipeline),
"video-sink", video_sink,
"audio-sink", audio_sink,
"subtitle-font-desc", "Sans 16",
@@ -1906,61 +1642,28 @@ clutter_gst_video_texture_init (ClutterGstVideoTexture *video_texture)
CLUTTER_GST_TYPE_VIDEO_TEXTURE,
ClutterGstVideoTexturePrivate);
- if (!lay_pipeline (video_texture))
+ self->is_idle = TRUE;
+
+ priv->player = clutter_gst_player_new ();
+ if (!setup_pipeline (priv->player->pipeline))
{
g_warning ("Failed to initiate suitable playback pipeline.");
return;
}
- create_black_idle_material (video_texture);
-
- priv->is_idle = TRUE;
- priv->in_seek = FALSE;
- priv->is_changing_uri = FALSE;
- priv->in_download_buffering = FALSE;
-
- priv->par_n = priv->par_d = 1;
-
- /* 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));
+ g_signal_connect (priv->player->pipeline, "download-buffering",
+ G_CALLBACK (on_player_download_buffering), video_texture);
- gst_bus_add_signal_watch (priv->bus);
-
- g_signal_connect_object (priv->bus, "message::error",
+ g_signal_connect_object (priv->player->bus, "message::error",
G_CALLBACK (bus_message_error_cb),
video_texture, 0);
- g_signal_connect_object (priv->bus, "message::eos",
+ g_signal_connect_object (self->bus, "message::eos",
G_CALLBACK (bus_message_eos_cb),
video_texture, 0);
- g_signal_connect_object (priv->bus, "message::buffering",
- G_CALLBACK (bus_message_buffering_cb),
- video_texture, 0);
- g_signal_connect_object (priv->bus, "message::duration",
- G_CALLBACK (bus_message_duration_cb),
- video_texture, 0);
- g_signal_connect_object (priv->bus, "message::state-changed",
- G_CALLBACK (bus_message_state_change_cb),
- video_texture, 0);
- g_signal_connect_object (priv->bus, "message::async-done",
- G_CALLBACK (bus_message_async_done_cb),
- video_texture, 0);
- g_signal_connect (priv->pipeline, "notify::volume",
- G_CALLBACK (on_volume_changed), video_texture);
-
- g_signal_connect (priv->pipeline, "audio-changed",
- G_CALLBACK (on_audio_changed), video_texture);
- g_signal_connect (priv->pipeline, "audio-tags-changed",
- G_CALLBACK (on_audio_tags_changed), video_texture);
- g_signal_connect (priv->pipeline, "notify::current-audio",
- G_CALLBACK (on_current_audio_changed), video_texture);
+ create_black_idle_material (video_texture);
- gst_object_unref (GST_OBJECT (priv->bus));
+ priv->par_n = priv->par_d = 1;
}
/*
@@ -2015,7 +1718,7 @@ clutter_gst_video_texture_get_pipeline (ClutterGstVideoTexture *texture)
{
g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture), NULL);
- return texture->priv->pipeline;
+ return texture->priv->player.pipeline;
}
/**
@@ -2102,11 +1805,11 @@ clutter_gst_video_texture_get_user_agent (ClutterGstVideoTexture *texture)
/* 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 (priv->player.user_agent)
+ return g_strdup (priv->player.user_agent);
/* If not, we try to retrieve the user agent used by the current source */
- g_object_get (priv->pipeline, "source", &source, NULL);
+ g_object_get (priv->player.pipeline, "source", &source, NULL);
if (source == NULL)
return NULL;
@@ -2142,13 +1845,13 @@ clutter_gst_video_texture_set_user_agent (ClutterGstVideoTexture *texture,
g_return_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture));
priv = texture->priv;
- g_free (priv->user_agent);
+ g_free (priv->player.user_agent);
if (user_agent)
- priv->user_agent = g_strdup (user_agent);
+ priv->player.user_agent = g_strdup (user_agent);
else
- priv->user_agent = NULL;
+ priv->player.user_agent = NULL;
- set_user_agent (texture, user_agent);
+ player_set_user_agent (&priv->player, user_agent);
}
/**
@@ -2167,7 +1870,7 @@ clutter_gst_video_texture_get_seek_flags (ClutterGstVideoTexture *texture)
g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture),
CLUTTER_GST_SEEK_FLAG_NONE);
- if (texture->priv->seek_flags == GST_SEEK_FLAG_ACCURATE)
+ if (texture->priv->player.seek_flags == GST_SEEK_FLAG_ACCURATE)
return CLUTTER_GST_SEEK_FLAG_ACCURATE;
else
return CLUTTER_GST_SEEK_FLAG_NONE;
@@ -2193,9 +1896,9 @@ clutter_gst_video_texture_set_seek_flags (ClutterGstVideoTexture *texture,
priv = texture->priv;
if (flags == CLUTTER_GST_SEEK_FLAG_NONE)
- priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT;
+ priv->player.seek_flags = GST_SEEK_FLAG_KEY_UNIT;
else if (flags & CLUTTER_GST_SEEK_FLAG_ACCURATE)
- priv->seek_flags = GST_SEEK_FLAG_ACCURATE;
+ priv->player.seek_flags = GST_SEEK_FLAG_ACCURATE;
}
/**
@@ -2216,7 +1919,7 @@ clutter_gst_video_texture_get_buffering_mode (ClutterGstVideoTexture *texture)
CLUTTER_GST_BUFFERING_MODE_STREAM);
priv = texture->priv;
- g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL);
+ g_object_get (G_OBJECT (priv->player.pipeline), "flags", &flags, NULL);
if (flags & GST_PLAY_FLAG_DOWNLOAD)
return CLUTTER_GST_BUFFERING_MODE_DOWNLOAD;
@@ -2240,7 +1943,7 @@ clutter_gst_video_texture_set_buffering_mode (ClutterGstVideoTexture *texture,
g_return_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture));
priv = texture->priv;
- g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL);
+ g_object_get (G_OBJECT (priv->player.pipeline), "flags", &flags, NULL);
switch (mode)
{
@@ -2255,7 +1958,7 @@ clutter_gst_video_texture_set_buffering_mode (ClutterGstVideoTexture *texture,
break;
}
- g_object_set (G_OBJECT (priv->pipeline), "flags", flags, NULL);
+ g_object_set (G_OBJECT (priv->player.pipeline), "flags", flags, NULL);
}
#ifdef CLUTTER_GST_ENABLE_DEBUG
@@ -2302,12 +2005,12 @@ clutter_gst_video_texture_get_audio_streams (ClutterGstVideoTexture *texture)
{
gchar *streams;
- streams = list_to_string (priv->audio_streams);
+ streams = list_to_string (priv->player.audio_streams);
CLUTTER_GST_NOTE (AUDIO_STREAM, "audio streams: %s", streams);
g_free (streams);
}
- return priv->audio_streams;
+ return priv->player.audio_streams;
}
/**
@@ -2330,7 +2033,7 @@ clutter_gst_video_texture_get_audio_stream (ClutterGstVideoTexture *texture)
g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture), -1);
- g_object_get (G_OBJECT (texture->priv->pipeline),
+ g_object_get (G_OBJECT (texture->priv->player.pipeline),
"current-audio", &index_,
NULL);
@@ -2357,9 +2060,12 @@ clutter_gst_video_texture_set_audio_stream (ClutterGstVideoTexture *texture,
g_return_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture));
g_return_if_fail (index_ >= 0 &&
- index_ < g_list_length (priv->audio_streams));
+ index_ < g_list_length (priv->player.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);
+ g_object_set (G_OBJECT (priv->player.pipeline),
+ "current-audio",
+ index_,
+ NULL);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]