mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
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
This commit is contained in:
parent
71df82db63
commit
68e4f919a0
6 changed files with 163 additions and 19 deletions
|
@ -4186,19 +4186,23 @@ gst_mpd_client_setup_representation (GstMpdClient * client,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CUSTOM_WRAPPER_START "<custom_wrapper>"
|
||||||
|
#define CUSTOM_WRAPPER_END "</custom_wrapper>"
|
||||||
|
|
||||||
static GList *
|
static GList *
|
||||||
gst_mpd_client_fetch_external_period (GstMpdClient * client,
|
gst_mpd_client_fetch_external_period (GstMpdClient * client,
|
||||||
GstPeriodNode * period_node, gboolean * error)
|
GstPeriodNode * period_node, gboolean * error)
|
||||||
{
|
{
|
||||||
GstFragment *download;
|
GstFragment *download;
|
||||||
|
GstAdapter *adapter;
|
||||||
GstBuffer *period_buffer;
|
GstBuffer *period_buffer;
|
||||||
GstMapInfo map;
|
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
xmlDocPtr doc;
|
xmlDocPtr doc;
|
||||||
GstUri *base_uri, *uri;
|
GstUri *base_uri, *uri;
|
||||||
gchar *query = NULL;
|
gchar *query = NULL;
|
||||||
gchar *uri_string;
|
gchar *uri_string, *wrapper;
|
||||||
GList *new_periods = NULL;
|
GList *new_periods = NULL;
|
||||||
|
const gchar *data;
|
||||||
|
|
||||||
*error = FALSE;
|
*error = FALSE;
|
||||||
|
|
||||||
|
@ -4248,34 +4252,65 @@ gst_mpd_client_fetch_external_period (GstMpdClient * client,
|
||||||
period_buffer = gst_fragment_get_buffer (download);
|
period_buffer = gst_fragment_get_buffer (download);
|
||||||
g_object_unref (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 =
|
doc =
|
||||||
xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
|
xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL,
|
||||||
XML_PARSE_NONET);
|
XML_PARSE_NONET);
|
||||||
if (doc) {
|
if (doc) {
|
||||||
xmlNode *root_element = xmlDocGetRootElement (doc);
|
xmlNode *root_element = xmlDocGetRootElement (doc);
|
||||||
if (root_element->type != XML_ELEMENT_NODE ||
|
xmlNode *iter;
|
||||||
xmlStrcmp (root_element->name, (xmlChar *) "Period") != 0) {
|
|
||||||
xmlFreeDoc (doc);
|
|
||||||
gst_buffer_unmap (period_buffer, &map);
|
|
||||||
gst_buffer_unref (period_buffer);
|
|
||||||
*error = TRUE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_mpdparser_parse_period_node (&new_periods, root_element);
|
if (root_element->type != XML_ELEMENT_NODE)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
GST_ERROR ("Failed to parse period node XML");
|
GST_ERROR ("Failed to parse period node XML");
|
||||||
gst_buffer_unmap (period_buffer, &map);
|
gst_adapter_unmap (adapter);
|
||||||
gst_buffer_unref (period_buffer);
|
gst_adapter_clear (adapter);
|
||||||
|
gst_object_unref (adapter);
|
||||||
*error = TRUE;
|
*error = TRUE;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
gst_buffer_unmap (period_buffer, &map);
|
xmlFreeDoc (doc);
|
||||||
gst_buffer_unref (period_buffer);
|
gst_adapter_unmap (adapter);
|
||||||
|
gst_adapter_clear (adapter);
|
||||||
|
gst_object_unref (adapter);
|
||||||
|
|
||||||
return new_periods;
|
return new_periods;
|
||||||
|
|
||||||
|
error:
|
||||||
|
xmlFreeDoc (doc);
|
||||||
|
gst_adapter_unmap (adapter);
|
||||||
|
gst_adapter_clear (adapter);
|
||||||
|
gst_object_unref (adapter);
|
||||||
|
*error = TRUE;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/uridownloader/gsturidownloader.h>
|
#include <gst/uridownloader/gsturidownloader.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|
|
@ -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" \
|
elements_uvch264demux_CFLAGS = -DUVCH264DEMUX_DATADIR="$(srcdir)/elements/uvch264demux_data" \
|
||||||
$(AM_CFLAGS)
|
$(AM_CFLAGS)
|
||||||
|
|
||||||
elements_dash_mpd_CFLAGS = $(AM_CFLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(LIBXML2_CFLAGS)
|
elements_dash_mpd_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS) $(GST_PLUGINS_BAD_CFLAGS) $(LIBXML2_CFLAGS) \
|
||||||
elements_dash_mpd_LDADD = $(LDADD) $(LIBXML2_LIBS) \
|
-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
|
$(top_builddir)/gst-libs/gst/uridownloader/libgsturidownloader-@GST_API_VERSION@.la
|
||||||
elements_dash_mpd_SOURCES = elements/dash_mpd.c
|
elements_dash_mpd_SOURCES = elements/dash_mpd.c
|
||||||
|
|
||||||
|
|
|
@ -5528,6 +5528,107 @@ GST_START_TEST (dash_mpdparser_maximum_segment_duration)
|
||||||
|
|
||||||
GST_END_TEST;
|
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 =
|
||||||
|
"<?xml version=\"1.0\"?>"
|
||||||
|
"<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\""
|
||||||
|
" profiles=\"urn:mpeg:dash:profile:isoff-main:2011\">"
|
||||||
|
" <Period id=\"Period0\"" "duration=\"PT5S\"></Period>";
|
||||||
|
|
||||||
|
const gchar *xml_uri_front = " <Period xlink:href=\"";
|
||||||
|
|
||||||
|
const gchar *xml_uri_rear =
|
||||||
|
"\""
|
||||||
|
" xlink:actuate=\"onRequest\""
|
||||||
|
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"></Period>";
|
||||||
|
|
||||||
|
const gchar *xml_frag_end = "</MPD>";
|
||||||
|
|
||||||
|
/* XLINK_ONE_PERIOD_FILENAME
|
||||||
|
*
|
||||||
|
* <Period id="xlink-single-period-Period1" duration="PT10S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* XLINK_TWO_PERIODS_FILENAME
|
||||||
|
*
|
||||||
|
* <Period id="xlink-double-period-Period1" duration="PT10S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
||||||
|
* <Period id="xlink-double-period-Period2" duration="PT20S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
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
|
* 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_various_duration_formats);
|
||||||
tcase_add_test (tc_simpleMPD, dash_mpdparser_default_presentation_delay);
|
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
|
/* tests checking the MPD management
|
||||||
* (eg. setting active streams, obtaining attributes values)
|
* (eg. setting active streams, obtaining attributes values)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<Period id="xlink-double-period-Period1" duration="PT10S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
||||||
|
<Period id="xlink-double-period-Period2" duration="PT20S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
|
@ -0,0 +1 @@
|
||||||
|
<Period id="xlink-single-period-Period1" duration="PT10S" xmlns="urn:mpeg:dash:schema:mpd:2011"></Period>
|
Loading…
Reference in a new issue