[clutter-gst] video-texture: Add support for audio stream selection



commit f68f9f67de211728f55348ee24e6a46d6753ff45
Author: Damien Lespiau <damien lespiau intel com>
Date:   Mon Jul 4 10:44:51 2011 +0100

    video-texture: Add support for audio stream selection
    
    It's basicaly a thin layer on top of playbin2. The only extra smartness
    bits are serializing all the notifications in the main thread and not
    nofifying twice the same list of available audio streams.

 clutter-gst/clutter-gst-debug.c         |    7 +-
 clutter-gst/clutter-gst-debug.h         |    6 +-
 clutter-gst/clutter-gst-video-texture.c |  325 ++++++++++++++++++++++++++++++-
 clutter-gst/clutter-gst-video-texture.h |    4 +
 configure.ac                            |    4 +-
 doc/reference/clutter-gst-sections.txt  |    3 +
 6 files changed, 342 insertions(+), 7 deletions(-)
---
diff --git a/clutter-gst/clutter-gst-debug.c b/clutter-gst/clutter-gst-debug.c
index bdba41e..f2cfef3 100644
--- a/clutter-gst/clutter-gst-debug.c
+++ b/clutter-gst/clutter-gst-debug.c
@@ -36,10 +36,11 @@ guint clutter_gst_debug_flags = 0;  /* global clutter-gst debug flag */
 static GTimer *clutter_gst_timer;
 
 static const GDebugKey clutter_gst_debug_keys[] = {
-  { "misc",         CLUTTER_GST_DEBUG_MISC },
-  { "media",        CLUTTER_GST_DEBUG_MEDIA },
+  { "misc",         CLUTTER_GST_DEBUG_MISC         },
+  { "media",        CLUTTER_GST_DEBUG_MEDIA        },
   { "aspect-ratio", CLUTTER_GST_DEBUG_ASPECT_RATIO },
-  { "buffering",    CLUTTER_GST_DEBUG_BUFFERING }
+  { "buffering",    CLUTTER_GST_DEBUG_BUFFERING    },
+  { "audio-stream", CLUTTER_GST_DEBUG_AUDIO_STREAM }
 };
 
 /**
diff --git a/clutter-gst/clutter-gst-debug.h b/clutter-gst/clutter-gst-debug.h
index 1fc5051..1077c39 100644
--- a/clutter-gst/clutter-gst-debug.h
+++ b/clutter-gst/clutter-gst-debug.h
@@ -37,9 +37,13 @@ typedef enum {
   CLUTTER_GST_DEBUG_MISC            = 1 << 0,
   CLUTTER_GST_DEBUG_MEDIA           = 1 << 1,
   CLUTTER_GST_DEBUG_ASPECT_RATIO    = 1 << 2,
-  CLUTTER_GST_DEBUG_BUFFERING       = 1 << 3
+  CLUTTER_GST_DEBUG_BUFFERING       = 1 << 3,
+  CLUTTER_GST_DEBUG_AUDIO_STREAM    = 1 << 4
 } ClutterDebugFlag;
 
+#define CLUTTER_GST_DEBUG_ENABLED(type) \
+  (clutter_gst_debug_flags & CLUTTER_GST_DEBUG_##type)
+
 #ifdef __GNUC__
 
 #define CLUTTER_GST_NOTE(type,x,a...)                         \
diff --git a/clutter-gst/clutter-gst-video-texture.c b/clutter-gst/clutter-gst-video-texture.c
index 61edf6a..5315347 100644
--- a/clutter-gst/clutter-gst-video-texture.c
+++ b/clutter-gst/clutter-gst-video-texture.c
@@ -45,6 +45,7 @@
 #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"
@@ -130,6 +131,8 @@ struct _ClutterGstVideoTexturePrivate
   GstSeekFlags seek_flags;    /* flags for the seek in set_progress(); */
 
   GstElement *download_buffering_element;
+
+  GList *audio_streams;
 };
 
 enum {
@@ -148,7 +151,9 @@ enum {
 
   PROP_IDLE_MATERIAL,
   PROP_USER_AGENT,
-  PROP_SEEK_FLAGS
+  PROP_SEEK_FLAGS,
+  PROP_AUDIO_STREAMS,
+  PROP_AUDIO_STREAM
 };
 
 /* idle timeouts (in ms) */
@@ -189,6 +194,21 @@ static void configure_buffering_timeout (ClutterGstVideoTexture *video_texture,
                                          guint                   ms);
 
 static void
+free_string_list (GList **listp)
+{
+  GList *l;
+
+  l = *listp;
+  while (l)
+    {
+      g_free (l->data);
+      l = g_list_delete_link (l, l);
+    }
+
+  *listp = NULL;
+}
+
+static void
 clear_download_buffering (ClutterGstVideoTexture *video_texture)
 {
   ClutterGstVideoTexturePrivate *priv = video_texture->priv;
@@ -624,6 +644,10 @@ set_uri (ClutterGstVideoTexture *video_texture,
   g_object_notify (self, "can-seek");
   g_object_notify (self, "duration");
   g_object_notify (self, "progress");
+
+  free_string_list (&priv->audio_streams);
+  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
+  g_object_notify (self, "audio-streams");
 }
 
 static void
@@ -1152,6 +1176,8 @@ clutter_gst_video_texture_finalize (GObject *object)
   if (priv->idle_material != COGL_INVALID_HANDLE)
     cogl_handle_unref (priv->idle_material);
 
+  free_string_list (&priv->audio_streams);
+
   G_OBJECT_CLASS (clutter_gst_video_texture_parent_class)->finalize (object);
 }
 
@@ -1204,6 +1230,11 @@ clutter_gst_video_texture_set_property (GObject      *object,
                                                 g_value_get_flags (value));
       break;
 
+    case PROP_AUDIO_STREAM:
+      clutter_gst_video_texture_set_audio_stream (video_texture,
+                                                  g_value_get_int (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -1283,6 +1314,19 @@ clutter_gst_video_texture_get_property (GObject    *object,
       }
       break;
 
+    case PROP_AUDIO_STREAMS:
+      g_value_set_pointer (value, priv->audio_streams);
+      break;
+
+    case PROP_AUDIO_STREAM:
+      {
+        gint index_;
+
+        index_ = clutter_gst_video_texture_get_audio_stream (video_texture);
+        g_value_set_int (value, index_);
+      }
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -1353,6 +1397,19 @@ clutter_gst_video_texture_class_init (ClutterGstVideoTextureClass *klass)
                               CLUTTER_GST_PARAM_READWRITE);
   g_object_class_install_property (object_class, PROP_SEEK_FLAGS, pspec);
 
+  pspec = g_param_spec_pointer ("audio-streams",
+                                "Audio Streams",
+                                "List of the audio streams of the media",
+                                CLUTTER_GST_PARAM_READABLE);
+  g_object_class_install_property (object_class, PROP_AUDIO_STREAMS, pspec);
+
+  pspec = g_param_spec_int ("audio-stream",
+                            "Audio Stream",
+                            "Index of the current audio stream",
+                            -1, G_MAXINT, -1,
+                            CLUTTER_GST_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_AUDIO_STREAM, pspec);
+
   /**
    * ClutterGstVideoTexture::download-buffering:
    * @start: Start of the buffer (between 0.0 and 1.0)
@@ -1611,6 +1668,15 @@ bus_message_async_done_cb (GstBus                 *bus,
 }
 
 static void
+bus_message_application_cb (GstBus                 *bus,
+                            GstMessage             *message,
+                            ClutterGstVideoTexture *video_texture)
+{
+  g_message ("application message");
+
+}
+
+static void
 on_source_changed (GstElement             *pipeline,
                    GParamSpec             *pspec,
                    ClutterGstVideoTexture *video_texture)
@@ -1646,6 +1712,147 @@ on_volume_changed (GstElement             *pipeline,
   g_idle_add (on_volume_changed_main_context, video_texture);
 }
 
+static GList *
+get_tags (GstElement  *pipeline,
+          const gchar *property_name,
+          const gchar *action_signal)
+{
+  GList *ret = NULL;
+  gint num = 1, i, n;
+
+  g_object_get (G_OBJECT (pipeline), property_name, &n, NULL);
+  if (n == 0)
+    return NULL;
+
+  for (i = 0; i < n; i++)
+    {
+      GstTagList *tags = NULL;
+      gchar *description = NULL;
+
+      g_signal_emit_by_name (G_OBJECT (pipeline), action_signal, i, &tags);
+
+      if (tags)
+        {
+
+          gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description);
+
+          if (description)
+            {
+              const gchar *language = gst_tag_get_language_name (description);
+
+              if (language)
+                {
+                  g_free (description);
+                  description = g_strdup (language);
+                }
+            }
+
+          if (!description)
+            gst_tag_list_get_string (tags, GST_TAG_CODEC, &description);
+
+          gst_tag_list_free (tags);
+        }
+
+      if (!description)
+        description = g_strdup_printf ("Audio Track #%d", num++);
+
+      ret = g_list_prepend (ret, description);
+
+    }
+
+  return g_list_reverse (ret);
+}
+
+static gboolean
+are_lists_equal (GList *list1,
+                 GList *list2)
+{
+  GList *l1, *l2;
+
+  l1 = list1;
+  l2 = list2;
+
+  while (l1)
+    {
+      const gchar *str1, *str2;
+
+      if (l2 == NULL)
+        return FALSE;
+
+      str1 = l1->data;
+      str2 = l2->data;
+
+      if (g_strcmp0 (str1, str2) != 0)
+        return FALSE;
+
+      l1 = g_list_next (l1);
+      l2 = g_list_next (l2);
+    }
+
+  return l2 == NULL;
+}
+
+static gboolean
+on_audio_changed_main_context (gpointer data)
+{
+  ClutterGstVideoTexture *video_texture = CLUTTER_GST_VIDEO_TEXTURE (data);
+  ClutterGstVideoTexturePrivate *priv = video_texture->priv;
+  GList *audio_streams;
+
+  audio_streams = get_tags (priv->pipeline, "n-audio", "get-audio-tags");
+
+  if (!are_lists_equal (priv->audio_streams, audio_streams))
+    {
+      free_string_list (&priv->audio_streams);
+      priv->audio_streams = audio_streams;
+
+      CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
+
+      g_object_notify (G_OBJECT (video_texture), "audio-streams");
+    }
+  else
+    {
+      free_string_list (&audio_streams);
+    }
+
+  return FALSE;
+}
+
+/* same explanation as for notify::volume's usage of g_idle_add() */
+static void
+on_audio_changed (GstElement             *pipeline,
+                  ClutterGstVideoTexture *video_texture)
+{
+  g_idle_add (on_audio_changed_main_context, video_texture);
+}
+
+static void
+on_audio_tags_changed (GstElement             *pipeline,
+                       gint                    stream,
+                       ClutterGstVideoTexture *video_texture)
+{
+  g_idle_add (on_audio_changed_main_context, video_texture);
+}
+
+static gboolean
+on_current_audio_changed_main_context (gpointer data)
+{
+  ClutterGstVideoTexture *video_texture = CLUTTER_GST_VIDEO_TEXTURE (data);
+
+  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream changed");
+  g_object_notify (G_OBJECT (video_texture), "audio-stream");
+
+  return FALSE;
+}
+
+static void
+on_current_audio_changed (GstElement             *pipeline,
+                          GParamSpec             *pspec,
+                          ClutterGstVideoTexture *video_texture)
+{
+  g_idle_add (on_current_audio_changed_main_context, video_texture);
+}
+
 static gboolean
 lay_pipeline (ClutterGstVideoTexture *video_texture)
 {
@@ -1748,10 +1955,20 @@ clutter_gst_video_texture_init (ClutterGstVideoTexture *video_texture)
   g_signal_connect_object (bus, "message::async-done",
                            G_CALLBACK (bus_message_async_done_cb),
                            video_texture, 0);
+  g_signal_connect_object (bus, "message::application",
+                           G_CALLBACK (bus_message_application_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);
+
   gst_object_unref (GST_OBJECT (bus));
 }
 
@@ -2049,3 +2266,109 @@ clutter_gst_video_texture_set_buffering_mode (ClutterGstVideoTexture *texture,
 
   g_object_set (G_OBJECT (priv->pipeline), "flags", flags, NULL);
 }
+
+#ifdef CLUTTER_GST_ENABLE_DEBUG
+gchar *
+list_to_string (GList *list)
+{
+  GString *string;
+  GList *l;
+  gint n, i;
+
+  if (!list)
+    return g_strdup ("<empty list>");
+
+  string = g_string_new (NULL);
+  n = g_list_length (list);
+  for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l))
+    g_string_append_printf (string, "%s, ", (gchar *) l->data);
+
+  g_string_append_printf (string, "%s", (gchar *) l->data);
+
+
+  return g_string_free (string, FALSE);
+}
+#endif
+
+/**
+ * clutter_gst_video_texture_get_audio_streams:
+ * @texture: a #ClutterGstVideoTexture
+ *
+ * Get the list of audio streams of the current media.
+ *
+ * Return value: a list of strings describing the available audio streams
+ *
+ * Since: 1.4
+ */
+GList *
+clutter_gst_video_texture_get_audio_streams (ClutterGstVideoTexture *texture)
+{
+  ClutterGstVideoTexturePrivate *priv = texture->priv;
+
+  g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture), NULL);
+
+  if (CLUTTER_GST_DEBUG_ENABLED (AUDIO_STREAM))
+    {
+      gchar *streams;
+
+      streams = list_to_string (priv->audio_streams);
+      CLUTTER_GST_NOTE (AUDIO_STREAM, "audio streams: %s", streams);
+      g_free (streams);
+    }
+
+  return priv->audio_streams;
+}
+
+/**
+ * clutter_gst_video_texture_get_audio_stream:
+ * @texture: a #ClutterGstVideoTexture
+ *
+ * Get the current audio stream. The number returned in the index of the
+ * audio stream playing in the list returned by
+ * clutter_gst_video_texture_get_audio_streams().
+ *
+ * Return value: the index of the current audio stream, -1 if the media has no
+ * audio stream
+ *
+ * Since: 1.4
+ */
+gint
+clutter_gst_video_texture_get_audio_stream (ClutterGstVideoTexture *texture)
+{
+  gint index_ = -1;
+
+  g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture), -1);
+
+  g_object_get (G_OBJECT (texture->priv->pipeline),
+                "current-audio", &index_,
+                NULL);
+
+  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream is #%d", index_);
+
+  return index_;
+}
+
+/**
+ * clutter_gst_video_texture_set_audio_stream:
+ * @texture: a #ClutterGstVideoTexture
+ * @index_: the index of the audio stream
+ *
+ * Set the audio stream to play. @index_ is the index of the stream
+ * in the list returned by clutter_gst_video_texture_get_audio_streams().
+ *
+ * Since: 1.4
+ */
+void
+clutter_gst_video_texture_set_audio_stream (ClutterGstVideoTexture *texture,
+                                            gint                    index_)
+{
+  ClutterGstVideoTexturePrivate *priv = texture->priv;
+
+  g_return_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture));
+  g_return_if_fail (index_ >= 0 &&
+                    index_ < g_list_length (priv->audio_streams));
+
+  CLUTTER_GST_NOTE (AUDIO_STREAM, "set audio audio stream to #%d", index_);
+
+  g_object_set (G_OBJECT (priv->pipeline), "current-audio", index_, NULL);
+}
diff --git a/clutter-gst/clutter-gst-video-texture.h b/clutter-gst/clutter-gst-video-texture.h
index 05fc58b..8b3e879 100644
--- a/clutter-gst/clutter-gst-video-texture.h
+++ b/clutter-gst/clutter-gst-video-texture.h
@@ -148,6 +148,10 @@ void			  clutter_gst_video_texture_set_seek_flags      (ClutterGstVideoTexture *
 ClutterGstBufferingMode	  clutter_gst_video_texture_get_buffering_mode	(ClutterGstVideoTexture *texture);
 void			  clutter_gst_video_texture_set_buffering_mode	(ClutterGstVideoTexture *texture,
 									 ClutterGstBufferingMode mode);
+GList *                   clutter_gst_video_texture_get_audio_streams   (ClutterGstVideoTexture *texture);
+gint                      clutter_gst_video_texture_get_audio_stream    (ClutterGstVideoTexture *texture);
+void                      clutter_gst_video_texture_set_audio_stream    (ClutterGstVideoTexture *texture,
+                                                                         gint                    index_);
 
 G_END_DECLS
 
diff --git a/configure.ac b/configure.ac
index 2581abb..a0d2fdb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,7 +78,7 @@ AC_SUBST([CLUTTER_GST_RELEASE_STATUS], [clutter_gst_release_status])
 # pkg-config requirements
 GLIB_REQ_VERSION=2.18.0
 CLUTTER_REQ_VERSION=1.4.0
-GSTREAMER_REQ_VERSION=0.10.20
+GSTREAMER_REQ_VERSION=0.10.26
 
 AC_SUBST(GLIB_REQ_VERSION)
 AC_SUBST(CLUTTER_REQ_VERSION)
@@ -143,7 +143,7 @@ dnl ========================================================================
 
 GST_MAJORMINOR=0.10
 
-pkg_modules="gstreamer-$GST_MAJORMINOR >= $GSTREAMER_REQ_VERSION gstreamer-plugins-base-$GST_MAJORMINOR gstreamer-base-$GST_MAJORMINOR gstreamer-interfaces-$GST_MAJORMINOR gstreamer-video-$GST_MAJORMINOR gstreamer-audio-$GST_MAJORMINOR"
+pkg_modules="gstreamer-$GST_MAJORMINOR >= $GSTREAMER_REQ_VERSION gstreamer-plugins-base-$GST_MAJORMINOR gstreamer-base-$GST_MAJORMINOR gstreamer-interfaces-$GST_MAJORMINOR gstreamer-video-$GST_MAJORMINOR gstreamer-audio-$GST_MAJORMINOR gstreamer-tag-$GST_MAJORMINOR"
 PKG_CHECK_MODULES(GST, [$pkg_modules])
 
 dnl ========================================================================
diff --git a/doc/reference/clutter-gst-sections.txt b/doc/reference/clutter-gst-sections.txt
index dedb149..44f62c9 100644
--- a/doc/reference/clutter-gst-sections.txt
+++ b/doc/reference/clutter-gst-sections.txt
@@ -15,6 +15,9 @@ clutter_gst_video_texture_set_seek_flags
 clutter_gst_video_texture_get_seek_flags
 clutter_gst_video_texture_get_buffering_mode
 clutter_gst_video_texture_set_buffering_mode
+clutter_gst_video_texture_get_audio_streams
+clutter_gst_video_texture_get_audio_stream
+clutter_gst_video_texture_set_audio_stream
 <SUBSECTION Standard>
 clutter_gst_seek_flags_get_type
 CLUTTER_GST_TYPE_SEEK_FLAGS



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]