From 2e423dd129f9ea509181a9f3dc952a29fbfc7251 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Sun, 3 May 2015 03:18:28 +0200 Subject: [PATCH] discoverer: Add serialization methods. [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 | 3 + gst-libs/gst/pbutils/gstdiscoverer.c | 326 +++++++++++++++++++ gst-libs/gst/pbutils/gstdiscoverer.h | 26 ++ tests/check/libs/discoverer.c | 52 ++- win32/common/libgstpbutils.def | 2 + 5 files changed, 408 insertions(+), 1 deletion(-) diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index a6fb1e35d3..02582501c8 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -2892,6 +2892,7 @@ gst_discoverer_discover_uri_async 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 GstDiscovererStreamInfo GstDiscovererContainerInfo diff --git a/gst-libs/gst/pbutils/gstdiscoverer.c b/gst-libs/gst/pbutils/gstdiscoverer.c index dd1944f18f..acc3d889b1 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.c +++ b/gst-libs/gst/pbutils/gstdiscoverer.c @@ -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; +} diff --git a/gst-libs/gst/pbutils/gstdiscoverer.h b/gst-libs/gst/pbutils/gstdiscoverer.h index bb3db2445a..8a142086ed 100644 --- a/gst-libs/gst/pbutils/gstdiscoverer.h +++ b/gst-libs/gst/pbutils/gstdiscoverer.h @@ -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 \ diff --git a/tests/check/libs/discoverer.c b/tests/check/libs/discoverer.c index c7d2679c2f..8fe5ecf5f9 100644 --- a/tests/check/libs/discoverer.c +++ b/tests/check/libs/discoverer.c @@ -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; } diff --git a/win32/common/libgstpbutils.def b/win32/common/libgstpbutils.def index 771371e1fb..1deace307e 100644 --- a/win32/common/libgstpbutils.def +++ b/win32/common/libgstpbutils.def @@ -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