discoverer: Add serialization methods.
authorMathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Sun, 3 May 2015 01:18:28 +0000 (03:18 +0200)
committerMathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Tue, 19 May 2015 16:48:07 +0000 (18:48 +0200)
[API] gst_discoverer_info_to_variant
[API] gst_discoverer_info_from_variant
[API] GstDiscovererSerializeFlags

+ Serializes as a GVariant
+ Adds a test
+ Does not serialize potential GstToc (s)

https://bugzilla.gnome.org/show_bug.cgi?id=748814

docs/libs/gst-plugins-base-libs-sections.txt
gst-libs/gst/pbutils/gstdiscoverer.c
gst-libs/gst/pbutils/gstdiscoverer.h
tests/check/libs/discoverer.c
win32/common/libgstpbutils.def

index a6fb1e3..0258250 100644 (file)
@@ -2892,6 +2892,7 @@ gst_discoverer_discover_uri_async
 <SUBSECTION>
 GstDiscovererInfo
 GstDiscovererResult
+GstDiscovererSerializeFlags
 gst_discoverer_info_get_duration
 gst_discoverer_info_get_misc
 gst_discoverer_info_get_result
@@ -2903,6 +2904,8 @@ gst_discoverer_info_get_uri
 gst_discoverer_info_get_seekable
 gst_discoverer_info_ref
 gst_discoverer_info_unref
+gst_discoverer_info_to_variant
+gst_discoverer_info_from_variant
 <SUBSECTION>
 GstDiscovererStreamInfo
 GstDiscovererContainerInfo
index dd1944f..acc3d88 100644 (file)
@@ -1637,6 +1637,276 @@ beach:
   return res;
 }
 
+/* Serializing code */
+
+static GVariant *
+_serialize_common_stream_info (GstDiscovererStreamInfo * sinfo,
+    GstDiscovererSerializeFlags flags)
+{
+  GVariant *common;
+  gchar *caps_str = NULL, *tags_str = NULL, *misc_str = NULL;
+
+  if (sinfo->caps && (flags & GST_DISCOVERER_SERIALIZE_CAPS))
+    caps_str = gst_caps_to_string (sinfo->caps);
+
+  if (sinfo->tags && (flags & GST_DISCOVERER_SERIALIZE_TAGS))
+    tags_str = gst_tag_list_to_string (sinfo->tags);
+
+  if (sinfo->misc && (flags & GST_DISCOVERER_SERIALIZE_MISC))
+    misc_str = gst_structure_to_string (sinfo->misc);
+
+  common =
+      g_variant_new ("(msmsmsms)", sinfo->stream_id, caps_str, tags_str,
+      misc_str);
+
+  g_free (caps_str);
+  g_free (tags_str);
+  g_free (misc_str);
+
+  return common;
+}
+
+static GVariant *
+_serialize_audio_stream_info (GstDiscovererAudioInfo * ainfo)
+{
+  return g_variant_new ("(uuuuums)",
+      ainfo->channels,
+      ainfo->sample_rate,
+      ainfo->bitrate, ainfo->max_bitrate, ainfo->depth, ainfo->language);
+}
+
+static GVariant *
+_serialize_video_stream_info (GstDiscovererVideoInfo * vinfo)
+{
+  return g_variant_new ("(uuuuuuubuub)",
+      vinfo->width,
+      vinfo->height,
+      vinfo->depth,
+      vinfo->framerate_num,
+      vinfo->framerate_denom,
+      vinfo->par_num,
+      vinfo->par_denom,
+      vinfo->interlaced, vinfo->bitrate, vinfo->max_bitrate, vinfo->is_image);
+}
+
+static GVariant *
+_serialize_subtitle_stream_info (GstDiscovererSubtitleInfo * sinfo)
+{
+  return g_variant_new ("ms", sinfo->language);
+}
+
+static GVariant *
+gst_discoverer_info_to_variant_recurse (GstDiscovererStreamInfo * sinfo,
+    GstDiscovererSerializeFlags flags)
+{
+  GVariant *stream_variant = NULL;
+  GVariant *common_stream_variant =
+      _serialize_common_stream_info (sinfo, flags);
+
+  if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) {
+    GList *tmp;
+    GList *streams =
+        gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO
+        (sinfo));
+
+    if (g_list_length (streams) > 0) {
+      GVariantBuilder children;
+      GVariant *child_variant;
+      g_variant_builder_init (&children, G_VARIANT_TYPE_ARRAY);
+
+      for (tmp = streams; tmp; tmp = tmp->next) {
+        child_variant =
+            gst_discoverer_info_to_variant_recurse (tmp->data, flags);
+        g_variant_builder_add (&children, "v", child_variant);
+      }
+      stream_variant =
+          g_variant_new ("(yvav)", 'c', common_stream_variant, &children);
+    } else {
+      stream_variant =
+          g_variant_new ("(yvav)", 'c', common_stream_variant, NULL);
+    }
+
+    gst_discoverer_stream_info_list_free (streams);
+  } else if (GST_IS_DISCOVERER_AUDIO_INFO (sinfo)) {
+    GVariant *audio_stream_info =
+        _serialize_audio_stream_info (GST_DISCOVERER_AUDIO_INFO (sinfo));
+    stream_variant =
+        g_variant_new ("(yvv)", 'a', common_stream_variant, audio_stream_info);
+  } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinfo)) {
+    GVariant *video_stream_info =
+        _serialize_video_stream_info (GST_DISCOVERER_VIDEO_INFO (sinfo));
+    stream_variant =
+        g_variant_new ("(yvv)", 'v', common_stream_variant, video_stream_info);
+  } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (sinfo)) {
+    GVariant *subtitle_stream_info =
+        _serialize_subtitle_stream_info (GST_DISCOVERER_SUBTITLE_INFO (sinfo));
+    stream_variant =
+        g_variant_new ("(yvv)", 's', common_stream_variant,
+        subtitle_stream_info);
+  }
+
+  return stream_variant;
+}
+
+/* Parsing code */
+
+#define GET_FROM_TUPLE(v, t, n, val) G_STMT_START{         \
+  GVariant *child = g_variant_get_child_value (v, n); \
+  *val = g_variant_get_##t(child); \
+  g_variant_unref (child); \
+}G_STMT_END
+
+static const gchar *
+_maybe_get_string_from_tuple (GVariant * tuple, guint index)
+{
+  const gchar *ret = NULL;
+  GVariant *maybe;
+  GET_FROM_TUPLE (tuple, maybe, index, &maybe);
+  if (maybe) {
+    ret = g_variant_get_string (maybe, NULL);
+    g_variant_unref (maybe);
+  }
+
+  return ret;
+}
+
+static void
+_parse_common_stream_info (GstDiscovererStreamInfo * sinfo, GVariant * common)
+{
+  const gchar *str;
+
+  str = _maybe_get_string_from_tuple (common, 0);
+  if (str)
+    sinfo->stream_id = g_strdup (str);
+
+  str = _maybe_get_string_from_tuple (common, 1);
+  if (str)
+    sinfo->caps = gst_caps_from_string (str);
+
+  str = _maybe_get_string_from_tuple (common, 2);
+  if (str)
+    sinfo->tags = gst_tag_list_new_from_string (str);
+
+  str = _maybe_get_string_from_tuple (common, 3);
+  if (str)
+    sinfo->misc = gst_structure_new_from_string (str);
+
+  g_variant_unref (common);
+}
+
+static void
+_parse_audio_stream_info (GstDiscovererAudioInfo * ainfo, GVariant * specific)
+{
+  const gchar *str;
+
+  GET_FROM_TUPLE (specific, uint32, 0, &ainfo->channels);
+  GET_FROM_TUPLE (specific, uint32, 1, &ainfo->sample_rate);
+  GET_FROM_TUPLE (specific, uint32, 2, &ainfo->bitrate);
+  GET_FROM_TUPLE (specific, uint32, 3, &ainfo->max_bitrate);
+  GET_FROM_TUPLE (specific, uint32, 4, &ainfo->depth);
+
+  str = _maybe_get_string_from_tuple (specific, 5);
+
+  if (str)
+    ainfo->language = g_strdup (str);
+
+  g_variant_unref (specific);
+}
+
+static void
+_parse_video_stream_info (GstDiscovererVideoInfo * vinfo, GVariant * specific)
+{
+  GET_FROM_TUPLE (specific, uint32, 0, &vinfo->width);
+  GET_FROM_TUPLE (specific, uint32, 1, &vinfo->height);
+  GET_FROM_TUPLE (specific, uint32, 2, &vinfo->depth);
+  GET_FROM_TUPLE (specific, uint32, 3, &vinfo->framerate_num);
+  GET_FROM_TUPLE (specific, uint32, 4, &vinfo->framerate_denom);
+  GET_FROM_TUPLE (specific, uint32, 5, &vinfo->par_num);
+  GET_FROM_TUPLE (specific, uint32, 6, &vinfo->par_denom);
+  GET_FROM_TUPLE (specific, boolean, 7, &vinfo->interlaced);
+  GET_FROM_TUPLE (specific, uint32, 8, &vinfo->bitrate);
+  GET_FROM_TUPLE (specific, uint32, 9, &vinfo->max_bitrate);
+  GET_FROM_TUPLE (specific, boolean, 10, &vinfo->is_image);
+
+  g_variant_unref (specific);
+}
+
+static void
+_parse_subtitle_stream_info (GstDiscovererSubtitleInfo * sinfo,
+    GVariant * specific)
+{
+  GVariant *maybe;
+
+  maybe = g_variant_get_maybe (specific);
+  if (maybe) {
+    sinfo->language = g_strdup (g_variant_get_string (maybe, NULL));
+    g_variant_unref (maybe);
+  }
+
+  g_variant_unref (specific);
+}
+
+static GstDiscovererStreamInfo *
+_parse_discovery (GVariant * variant, GstDiscovererInfo * info)
+{
+  gchar type;
+  GVariant *common = g_variant_get_child_value (variant, 1);
+  GVariant *specific = g_variant_get_child_value (variant, 2);
+  GstDiscovererStreamInfo *sinfo = NULL;
+
+  GET_FROM_TUPLE (variant, byte, 0, &type);
+  switch (type) {
+    case 'c':
+      sinfo = g_object_new (GST_TYPE_DISCOVERER_CONTAINER_INFO, NULL);
+      break;
+    case 'a':
+      sinfo = g_object_new (GST_TYPE_DISCOVERER_AUDIO_INFO, NULL);
+      _parse_audio_stream_info (GST_DISCOVERER_AUDIO_INFO (sinfo),
+          g_variant_get_child_value (specific, 0));
+      break;
+    case 'v':
+      sinfo = g_object_new (GST_TYPE_DISCOVERER_VIDEO_INFO, NULL);
+      _parse_video_stream_info (GST_DISCOVERER_VIDEO_INFO (sinfo),
+          g_variant_get_child_value (specific, 0));
+      break;
+    case 's':
+      sinfo = g_object_new (GST_TYPE_DISCOVERER_SUBTITLE_INFO, NULL);
+      _parse_subtitle_stream_info (GST_DISCOVERER_SUBTITLE_INFO (sinfo),
+          g_variant_get_child_value (specific, 0));
+      break;
+    default:
+      break;
+  }
+
+  _parse_common_stream_info (sinfo, g_variant_get_child_value (common, 0));
+
+  info->stream_list = g_list_append (info->stream_list, sinfo);
+
+  if (!info->stream_info) {
+    info->stream_info = sinfo;
+  }
+
+  if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) {
+    GVariantIter iter;
+    GVariant *child;
+
+    GstDiscovererContainerInfo *cinfo = GST_DISCOVERER_CONTAINER_INFO (sinfo);
+    g_variant_iter_init (&iter, specific);
+    while ((child = g_variant_iter_next_value (&iter))) {
+      GstDiscovererStreamInfo *child_info;
+      child_info = _parse_discovery (g_variant_get_variant (child), info);
+      cinfo->streams =
+          g_list_append (cinfo->streams,
+          gst_discoverer_stream_info_ref (child_info));
+      g_variant_unref (child);
+    }
+  }
+
+  g_variant_unref (common);
+  g_variant_unref (specific);
+  g_variant_unref (variant);
+  return sinfo;
+}
 
 /**
  * gst_discoverer_start:
@@ -1854,3 +2124,59 @@ gst_discoverer_new (GstClockTime timeout, GError ** err)
   }
   return res;
 }
+
+/**
+ * gst_discoverer_info_to_variant:
+ * @info: A #GstDiscovererInfo
+ * @flags: A combination of #GstDiscovererSerializeFlags to specify
+ * what needs to be serialized.
+ *
+ * Serializes @info to a #GVariant that can be parsed again
+ * through gst_discoverer_info_from_variant().
+ *
+ * Note that any #GstToc (s) that might have been discovered will not be serialized
+ * for now.
+ *
+ * Returns: (transfer full): A newly-allocated #GVariant representing @info.
+ *
+ * Since: 1.6
+ */
+GVariant *
+gst_discoverer_info_to_variant (GstDiscovererInfo * info,
+    GstDiscovererSerializeFlags flags)
+{
+  /* FIXME: implement TOC support */
+  GVariant *variant;
+  GstDiscovererStreamInfo *sinfo = gst_discoverer_info_get_stream_info (info);
+  GVariant *wrapper;
+
+  variant = gst_discoverer_info_to_variant_recurse (sinfo, flags);
+  /* Returning a wrapper implies some small overhead, but simplifies 
+   * deserializing from bytes */
+  wrapper = g_variant_new_variant (variant);
+
+  gst_discoverer_stream_info_unref (sinfo);
+  return wrapper;
+}
+
+/**
+ * gst_discoverer_info_from_variant:
+ * @variant: A #GVariant to deserialize into a #GstDiscovererInfo.
+ *
+ * Parses a #GVariant as produced by gst_discoverer_info_to_variant()
+ * back to a #GstDiscovererInfo.
+ *
+ * Returns: (transfer full): A newly-allocated #GstDiscovererInfo.
+ *
+ * Since: 1.6
+ */
+GstDiscovererInfo *
+gst_discoverer_info_from_variant (GVariant * variant)
+{
+  GstDiscovererInfo *info = g_object_new (GST_TYPE_DISCOVERER_INFO, NULL);
+  GVariant *wrapped = g_variant_get_variant (variant);
+
+  _parse_discovery (wrapped, info);
+
+  return info;
+}
index bb3db24..8a14208 100644 (file)
@@ -173,6 +173,28 @@ typedef enum {
   GST_DISCOVERER_MISSING_PLUGINS  = 5
 } GstDiscovererResult;
 
+/**
+ * GstDiscovererSerializeFlags:
+ * @GST_DISCOVERER_SERIALIZE_BASIC: Serialize only basic information, excluding
+ * caps, tags and miscellaneous information
+ * @GST_DISCOVERER_SERIALIZE_CAPS: Serialize the caps for each stream
+ * @GST_DISCOVERER_SERIALIZE_TAGS: Serialize the tags for each stream
+ * @GST_DISCOVERER_SERIALIZE_MISC: Serialize miscellaneous information for each stream
+ * @GST_DISCOVERER_SERIALIZE_ALL: Serialize all the available info, including
+ * caps, tags and miscellaneous information
+ *
+ * You can use these flags to control what is serialized by
+ * gst_discoverer_info_to_variant()
+ *
+ */
+
+typedef enum {
+  GST_DISCOVERER_SERIALIZE_BASIC = 0,
+  GST_DISCOVERER_SERIALIZE_CAPS  = 1 << 0,
+  GST_DISCOVERER_SERIALIZE_TAGS  = 1 << 1,
+  GST_DISCOVERER_SERIALIZE_MISC  = 1 << 2,
+  GST_DISCOVERER_SERIALIZE_ALL   = GST_DISCOVERER_SERIALIZE_CAPS | GST_DISCOVERER_SERIALIZE_TAGS | GST_DISCOVERER_SERIALIZE_MISC
+} GstDiscovererSerializeFlags;
 
 /**
  * GstDiscovererInfo:
@@ -213,6 +235,10 @@ GList *                   gst_discoverer_info_get_video_streams (GstDiscovererIn
 GList *                   gst_discoverer_info_get_subtitle_streams (GstDiscovererInfo *info);
 GList *                   gst_discoverer_info_get_container_streams (GstDiscovererInfo *info);
 
+GVariant *                gst_discoverer_info_to_variant (GstDiscovererInfo *info,
+                                                          GstDiscovererSerializeFlags flags);
+GstDiscovererInfo *       gst_discoverer_info_from_variant (GVariant *variant);
+
 void                      gst_discoverer_stream_info_list_free (GList *infos);
 
 #define GST_TYPE_DISCOVERER \
index c7d2679..8fe5ecf 100644 (file)
@@ -44,6 +44,56 @@ GST_START_TEST (test_disco_init)
 
 GST_END_TEST;
 
+GST_START_TEST (test_disco_serializing)
+{
+  GError *err = NULL;
+  GstDiscoverer *dc;
+  GstDiscovererInfo *info, *dinfo;
+  gchar *uri;
+  GVariant *serialized, *reserialized;
+  GList *audio_streams;
+  gchar *path =
+      g_build_filename (GST_TEST_FILES_PATH, "theora-vorbis.ogg", NULL);
+
+  /* high timeout, in case we're running under valgrind */
+  dc = gst_discoverer_new (5 * GST_SECOND, &err);
+  fail_unless (dc != NULL);
+  fail_unless (err == NULL);
+
+  uri = gst_filename_to_uri (path, &err);
+  g_free (path);
+  fail_unless (err == NULL);
+
+  info = gst_discoverer_discover_uri (dc, uri, &err);
+  fail_unless (info);
+  serialized =
+      gst_discoverer_info_to_variant (info, GST_DISCOVERER_SERIALIZE_ALL);
+
+
+  fail_unless (serialized);
+  dinfo = gst_discoverer_info_from_variant (serialized);
+
+  fail_unless (dinfo);
+  audio_streams = gst_discoverer_info_get_audio_streams (dinfo);
+  fail_unless_equals_int (g_list_length (audio_streams), 1);
+  gst_discoverer_stream_info_list_free (audio_streams);
+
+  reserialized =
+      gst_discoverer_info_to_variant (dinfo, GST_DISCOVERER_SERIALIZE_ALL);
+
+  fail_unless (g_variant_equal (serialized, reserialized));
+
+  gst_discoverer_info_unref (info);
+  gst_discoverer_info_unref (dinfo);
+  g_free (uri);
+  g_variant_unref (serialized);
+  g_variant_unref (reserialized);
+
+  g_object_unref (dc);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_disco_sync)
 {
   GError *err = NULL;
@@ -78,7 +128,6 @@ GST_START_TEST (test_disco_sync)
 }
 
 GST_END_TEST;
-
 static void
 test_disco_sync_reuse (const gchar * test_fn, guint num, GstClockTime timeout)
 {
@@ -195,6 +244,7 @@ discoverer_suite (void)
   tcase_add_test (tc_chain, test_disco_sync_reuse_mp3);
   tcase_add_test (tc_chain, test_disco_sync_reuse_timeout);
   tcase_add_test (tc_chain, test_disco_missing_plugins);
+  tcase_add_test (tc_chain, test_disco_serializing);
   return s;
 }
 
index 771371e..1deace3 100644 (file)
@@ -45,6 +45,8 @@ EXPORTS
        gst_discoverer_info_get_type
        gst_discoverer_info_get_uri
        gst_discoverer_info_get_video_streams
+       gst_discoverer_info_to_variant
+       gst_discoverer_info_from_variant
        gst_discoverer_new
        gst_discoverer_result_get_type
        gst_discoverer_start