[clutter-gst/wip/separate-logic] video-texture: Separate out the playbin2 logic.



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, &current_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, &current_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, &current_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, &current_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]