From 68e4f919a004937f1a35be35a447d4de55f612b9 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Mon, 14 Nov 2016 23:31:37 +0900 Subject: mpdparser: Support multiple Period elements in external xml External xml could have empty, one or multiple top-level "Period" elements. Because xml parser cannot parse the multiple top-level elements (i.e., no root element), we need to wrap a xml in order to make root element. See also ISO/IEC 23009-1:2014 5.3.2.2 https://bugzilla.gnome.org/show_bug.cgi?id=774357 --- ext/dash/gstmpdparser.c | 69 ++++++++++---- ext/dash/gstmpdparser.h | 1 + tests/check/Makefile.am | 5 +- tests/check/elements/dash_mpd.c | 104 +++++++++++++++++++++ .../dash_mpd_data/xlink_double_period.period | 2 + .../dash_mpd_data/xlink_single_period.period | 1 + 6 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 tests/check/elements/dash_mpd_data/xlink_double_period.period create mode 100644 tests/check/elements/dash_mpd_data/xlink_single_period.period diff --git a/ext/dash/gstmpdparser.c b/ext/dash/gstmpdparser.c index 1e757bd59..6ab8032eb 100644 --- a/ext/dash/gstmpdparser.c +++ b/ext/dash/gstmpdparser.c @@ -4186,19 +4186,23 @@ gst_mpd_client_setup_representation (GstMpdClient * client, return TRUE; } +#define CUSTOM_WRAPPER_START "" +#define CUSTOM_WRAPPER_END "" + static GList * gst_mpd_client_fetch_external_period (GstMpdClient * client, GstPeriodNode * period_node, gboolean * error) { GstFragment *download; + GstAdapter *adapter; GstBuffer *period_buffer; - GstMapInfo map; GError *err = NULL; xmlDocPtr doc; GstUri *base_uri, *uri; gchar *query = NULL; - gchar *uri_string; + gchar *uri_string, *wrapper; GList *new_periods = NULL; + const gchar *data; *error = FALSE; @@ -4248,34 +4252,65 @@ gst_mpd_client_fetch_external_period (GstMpdClient * client, period_buffer = gst_fragment_get_buffer (download); g_object_unref (download); - gst_buffer_map (period_buffer, &map, GST_MAP_READ); + /* external xml could have multiple period without root xmlNode. + * To avoid xml parsing error caused by no root node, wrapping it with + * custom root node */ + adapter = gst_adapter_new (); + + wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START)); + memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START)); + gst_adapter_push (adapter, + gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START))); + + gst_adapter_push (adapter, period_buffer); + + wrapper = g_strdup (CUSTOM_WRAPPER_END); + gst_adapter_push (adapter, + gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1)); + + data = gst_adapter_map (adapter, gst_adapter_available (adapter)); doc = - xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL, + xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL, XML_PARSE_NONET); if (doc) { xmlNode *root_element = xmlDocGetRootElement (doc); - if (root_element->type != XML_ELEMENT_NODE || - xmlStrcmp (root_element->name, (xmlChar *) "Period") != 0) { - xmlFreeDoc (doc); - gst_buffer_unmap (period_buffer, &map); - gst_buffer_unref (period_buffer); - *error = TRUE; - return NULL; - } + xmlNode *iter; + + if (root_element->type != XML_ELEMENT_NODE) + goto error; - gst_mpdparser_parse_period_node (&new_periods, root_element); + for (iter = root_element->children; iter; iter = iter->next) { + if (iter->type == XML_ELEMENT_NODE) { + if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) { + gst_mpdparser_parse_period_node (&new_periods, iter); + } else { + goto error; + } + } + } } else { GST_ERROR ("Failed to parse period node XML"); - gst_buffer_unmap (period_buffer, &map); - gst_buffer_unref (period_buffer); + gst_adapter_unmap (adapter); + gst_adapter_clear (adapter); + gst_object_unref (adapter); *error = TRUE; return NULL; } - gst_buffer_unmap (period_buffer, &map); - gst_buffer_unref (period_buffer); + xmlFreeDoc (doc); + gst_adapter_unmap (adapter); + gst_adapter_clear (adapter); + gst_object_unref (adapter); return new_periods; + +error: + xmlFreeDoc (doc); + gst_adapter_unmap (adapter); + gst_adapter_clear (adapter); + gst_object_unref (adapter); + *error = TRUE; + return NULL; } gboolean diff --git a/ext/dash/gstmpdparser.h b/ext/dash/gstmpdparser.h index 85b97ea2a..04b935e3a 100644 --- a/ext/dash/gstmpdparser.h +++ b/ext/dash/gstmpdparser.h @@ -29,6 +29,7 @@ #include #include +#include G_BEGIN_DECLS diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 35a4a251d..90b216520 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -470,8 +470,9 @@ elements_mpegtsmux_LDADD = $(GST_PLUGINS_BASE_LIBS) $(GST_VIDEO_LIBS) $(GST_BASE elements_uvch264demux_CFLAGS = -DUVCH264DEMUX_DATADIR="$(srcdir)/elements/uvch264demux_data" \ $(AM_CFLAGS) -elements_dash_mpd_CFLAGS = $(AM_CFLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(LIBXML2_CFLAGS) -elements_dash_mpd_LDADD = $(LDADD) $(LIBXML2_LIBS) \ +elements_dash_mpd_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(LIBXML2_CFLAGS) \ + -DDASH_MPD_DATADIR="$(srcdir)/elements/dash_mpd_data" +elements_dash_mpd_LDADD = $(GST_BASE_LIBS) $(LDADD) $(LIBXML2_LIBS) \ $(top_builddir)/gst-libs/gst/uridownloader/libgsturidownloader-@GST_API_VERSION@.la elements_dash_mpd_SOURCES = elements/dash_mpd.c diff --git a/tests/check/elements/dash_mpd.c b/tests/check/elements/dash_mpd.c index 5585f4980..946921217 100644 --- a/tests/check/elements/dash_mpd.c +++ b/tests/check/elements/dash_mpd.c @@ -5528,6 +5528,107 @@ GST_START_TEST (dash_mpdparser_maximum_segment_duration) GST_END_TEST; +/* + * Test parsing of Perioud using @xlink:href attribute + */ + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_ (x) +#define REMOTEDIR STRINGIFY (DASH_MPD_DATADIR) +#define XLINK_SINGLE_PERIOD_FILENAME REMOTEDIR "/xlink_single_period.period" +#define XLINK_DOUBLE_PERIOD_FILENAME REMOTEDIR "/xlink_double_period.period" + +GST_START_TEST (dash_mpdparser_xlink_period) +{ + GstPeriodNode *periodNode; + GstUriDownloader *downloader; + GstMpdClient *mpdclient; + GList *period_list, *iter; + gboolean ret; + gchar *xml_joined, *file_uri_single_period, *file_uri_double_period; + const gchar *xml_frag_start = + "" + "" + " "; + + const gchar *xml_uri_front = " "; + + const gchar *xml_frag_end = ""; + + /* XLINK_ONE_PERIOD_FILENAME + * + * + */ + + /* XLINK_TWO_PERIODS_FILENAME + * + * + * + */ + + + mpdclient = gst_mpd_client_new (); + downloader = gst_uri_downloader_new (); + + gst_mpd_client_set_uri_downloader (mpdclient, downloader); + + file_uri_single_period = + gst_filename_to_uri (XLINK_SINGLE_PERIOD_FILENAME, NULL); + file_uri_double_period = + gst_filename_to_uri (XLINK_DOUBLE_PERIOD_FILENAME, NULL); + + /* constructs inital mpd using external xml uri */ + xml_joined = g_strjoin ("", xml_frag_start, + xml_uri_front, (const char *) file_uri_single_period, xml_uri_rear, + xml_uri_front, (const char *) file_uri_double_period, xml_uri_rear, + xml_frag_end, NULL); + + ret = gst_mpd_parse (mpdclient, xml_joined, (gint) strlen (xml_joined)); + assert_equals_int (ret, TRUE); + + period_list = mpdclient->mpd_node->Periods; + /* only count periods on initial mpd (external xml does not parsed yet) */ + assert_equals_int (g_list_length (period_list), 3); + + /* process the xml data */ + ret = gst_mpd_client_setup_media_presentation (mpdclient, GST_CLOCK_TIME_NONE, + -1, NULL); + assert_equals_int (ret, TRUE); + + period_list = mpdclient->mpd_node->Periods; + assert_equals_int (g_list_length (period_list), 4); + + iter = period_list; + periodNode = (GstPeriodNode *) iter->data; + assert_equals_string (periodNode->id, "Period0"); + + iter = iter->next; + periodNode = (GstPeriodNode *) iter->data; + assert_equals_string (periodNode->id, "xlink-single-period-Period1"); + + iter = iter->next; + periodNode = (GstPeriodNode *) iter->data; + assert_equals_string (periodNode->id, "xlink-double-period-Period1"); + + iter = iter->next; + periodNode = (GstPeriodNode *) iter->data; + assert_equals_string (periodNode->id, "xlink-double-period-Period2"); + + gst_mpd_client_free (mpdclient); + g_object_unref (downloader); + g_free (file_uri_single_period); + g_free (file_uri_double_period); + g_free (xml_joined); +} + +GST_END_TEST; + /* * create a test suite containing all dash testcases */ @@ -5654,6 +5755,9 @@ dash_suite (void) tcase_add_test (tc_simpleMPD, dash_mpdparser_various_duration_formats); tcase_add_test (tc_simpleMPD, dash_mpdparser_default_presentation_delay); + /* tests checking xlink attributes */ + tcase_add_test (tc_simpleMPD, dash_mpdparser_xlink_period); + /* tests checking the MPD management * (eg. setting active streams, obtaining attributes values) */ diff --git a/tests/check/elements/dash_mpd_data/xlink_double_period.period b/tests/check/elements/dash_mpd_data/xlink_double_period.period new file mode 100644 index 000000000..cb656b953 --- /dev/null +++ b/tests/check/elements/dash_mpd_data/xlink_double_period.period @@ -0,0 +1,2 @@ + + diff --git a/tests/check/elements/dash_mpd_data/xlink_single_period.period b/tests/check/elements/dash_mpd_data/xlink_single_period.period new file mode 100644 index 000000000..dd85543af --- /dev/null +++ b/tests/check/elements/dash_mpd_data/xlink_single_period.period @@ -0,0 +1 @@ + -- cgit v1.2.3