From fd1e75900db48fc008899c5712a39ca16a0681b0 Mon Sep 17 00:00:00 2001 From: Alex Ashley Date: Tue, 24 Aug 2021 14:33:42 +0100 Subject: [PATCH] dashdemux: copy ContentProtection element including xml namespaces Commit bc09d8cc changed gstmpdparser to put the entire element in the "value" field, so that DRMs other than PlayReady could make use of the data inside this element. However, the data in the "value" field does not include any XML namespace declarations that are used within the element. This causes problems for a namespace aware XML parser that wants to make use of this data. This commit modifies the way the XML is converted to a string so that XML namespaces are preserved in the output. Part-of: --- ext/dash/gstxmlhelper.c | 17 +++++- tests/check/elements/dash_mpd.c | 94 ++++++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/ext/dash/gstxmlhelper.c b/ext/dash/gstxmlhelper.c index ee7aa661cc..d4db559c8e 100644 --- a/ext/dash/gstxmlhelper.c +++ b/ext/dash/gstxmlhelper.c @@ -975,11 +975,24 @@ gst_xml_helper_get_node_as_string (xmlNode * a_node, gchar ** content) gboolean exists = FALSE; const char *txt_encoding; xmlOutputBufferPtr out_buf; + xmlNode *ncopy = NULL; txt_encoding = (const char *) a_node->doc->encoding; out_buf = xmlAllocOutputBuffer (NULL); g_assert (out_buf != NULL); - xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding); + + /* Need to make a copy of XML element so that it includes namespaces + in the output, so that the resulting string can be parsed by an XML parser + that is namespace aware. + Use extended=1 for recursive copy (properties, namespaces and children) */ + ncopy = xmlDocCopyNode (a_node, a_node->doc, 1); + + if (!ncopy) { + GST_WARNING ("Failed to clone XML node"); + goto done; + } + xmlNodeDumpOutput (out_buf, ncopy->doc, ncopy, 0, 0, txt_encoding); + (void) xmlOutputBufferFlush (out_buf); #ifdef LIBXML2_NEW_BUFFER if (xmlOutputBufferGetSize (out_buf) > 0) { @@ -999,6 +1012,8 @@ gst_xml_helper_get_node_as_string (xmlNode * a_node, gchar ** content) exists = TRUE; } #endif // LIBXML2_NEW_BUFFER + xmlFreeNode (ncopy); +done: (void) xmlOutputBufferClose (out_buf); if (exists) { diff --git a/tests/check/elements/dash_mpd.c b/tests/check/elements/dash_mpd.c index 51448e69aa..c7adab8f34 100644 --- a/tests/check/elements/dash_mpd.c +++ b/tests/check/elements/dash_mpd.c @@ -51,6 +51,9 @@ #include +#include +#include + GST_DEBUG_CATEGORY (gst_dash_demux_debug); /* @@ -1434,7 +1437,7 @@ GST_START_TEST "" "" - " customns=\"foo\"" + " xmlns:customns=\"foo\"" " " " " " " @@ -1578,6 +1581,93 @@ GST_START_TEST (dash_mpdparser_contentProtection_no_value_no_encoding) GST_END_TEST; +/* + * Test parsing Period AdaptationSet RepresentationBase ContentProtection + * attributes + */ +GST_START_TEST + (dash_mpdparser_period_adaptationSet_representationBase_contentProtection_xml_namespaces) +{ + const gchar *xml = + "" + "" + " " " " + " " + " " + " " + " https://drm.test.example/AcquireLicense" + " "; + GstMPDPeriodNode *periodNode; + GstMPDAdaptationSetNode *adaptationSet; + GstMPDRepresentationBaseNode *representationBase; + GstMPDDescriptorTypeNode *contentProtection; + gboolean ret; + GstMPDClient *mpdclient = gst_mpd_client_new (); + xmlDocPtr doc; + xmlNode *root_element = NULL, *node; + xmlChar *property = NULL; + + ret = gst_mpd_client_parse (mpdclient, xml, (gint) strlen (xml)); + assert_equals_int (ret, TRUE); + + periodNode = (GstMPDPeriodNode *) mpdclient->mpd_root_node->Periods->data; + adaptationSet = (GstMPDAdaptationSetNode *) periodNode->AdaptationSets->data; + representationBase = GST_MPD_REPRESENTATION_BASE_NODE (adaptationSet); + assert_equals_int (g_list_length (representationBase->ContentProtection), 2); + contentProtection = (GstMPDDescriptorTypeNode *) + g_list_nth_data (representationBase->ContentProtection, 0); + assert_equals_string (contentProtection->schemeIdUri, + "urn:mpeg:dash:mp4protection:2011"); + + contentProtection = (GstMPDDescriptorTypeNode *) + g_list_nth_data (representationBase->ContentProtection, 1); + assert_equals_string (contentProtection->schemeIdUri, + "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e"); + + /* We can't do a simple string compare of value, because the whitespace + formatting from xmlDump might differ between versions of libxml */ + LIBXML_TEST_VERSION; + doc = + xmlReadMemory (contentProtection->value, + strlen (contentProtection->value), "ContentProtection.xml", NULL, + XML_PARSE_NONET); + fail_if (!doc); + root_element = xmlDocGetRootElement (doc); + fail_if (root_element->type != XML_ELEMENT_NODE); + fail_if (xmlStrcmp (root_element->name, + (xmlChar *) "ContentProtection") != 0); + fail_if ((property = + xmlGetNoNsProp (root_element, (const xmlChar *) "value")) == NULL); + fail_if (xmlStrcmp (property, (xmlChar *) "ClearKey1.0") != 0); + xmlFree (property); + fail_if ((property = + xmlGetNoNsProp (root_element, + (const xmlChar *) "schemeIdUri")) == NULL); + assert_equals_string ((const gchar *) property, + "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e"); + xmlFree (property); + + for (node = root_element->children; node; node = node->next) { + if (node->type == XML_ELEMENT_NODE) + break; + } + assert_equals_string ((const gchar *) node->name, "Laurl"); + assert_equals_string ((const gchar *) node->children->content, + "https://drm.test.example/AcquireLicense"); + + xmlFreeDoc (doc); + + gst_mpd_client_free (mpdclient); +} + +GST_END_TEST; + /* * Test parsing Period AdaptationSet Accessibility attributes * @@ -6454,6 +6544,8 @@ dash_suite (void) dash_mpdparser_contentProtection_no_value_no_encoding); tcase_add_test (tc_simpleMPD, dash_mpdparser_period_adaptationSet_representationBase_contentProtection_with_content); + tcase_add_test (tc_simpleMPD, + dash_mpdparser_period_adaptationSet_representationBase_contentProtection_xml_namespaces); tcase_add_test (tc_simpleMPD, dash_mpdparser_period_adaptationSet_accessibility); tcase_add_test (tc_simpleMPD, dash_mpdparser_period_adaptationSet_role);