gstreamer/ext/dash/gstxmlhelper.c
Alex Ashley fd1e75900d dashdemux: copy ContentProtection element including xml namespaces
Commit bc09d8cc changed gstmpdparser to put the entire
<ContentProtection> 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: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/2487>
2021-08-27 10:47:06 +00:00

1272 lines
34 KiB
C

/* GStreamer
*
* Copyright (C) 2019 Collabora Ltd.
* Author: Stéphane Cerveau <scerveau@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "gstxmlhelper.h"
#define XML_HELPER_MINUTE_TO_SEC 60
#define XML_HELPER_HOUR_TO_SEC (60 * XML_HELPER_MINUTE_TO_SEC)
#define XML_HELPER_DAY_TO_SEC (24 * XML_HELPER_HOUR_TO_SEC)
#define XML_HELPER_MONTH_TO_SEC (30 * XML_HELPER_DAY_TO_SEC)
#define XML_HELPER_YEAR_TO_SEC (365 * XML_HELPER_DAY_TO_SEC)
#define XML_HELPER_MS_TO_SEC(time) ((time) / 1000)
/* static methods */
/* this function computes decimals * 10 ^ (3 - pos) */
static guint
_mpd_helper_convert_to_millisecs (guint decimals, gint pos)
{
guint num = 1, den = 1;
gint i = 3 - pos;
while (i < 0) {
den *= 10;
i++;
}
while (i > 0) {
num *= 10;
i--;
}
/* if i == 0 we have exactly 3 decimals and nothing to do */
return decimals * num / den;
}
static gboolean
_mpd_helper_accumulate (guint64 * v, guint64 mul, guint64 add)
{
guint64 tmp;
if (*v > G_MAXUINT64 / mul)
return FALSE;
tmp = *v * mul;
if (tmp > G_MAXUINT64 - add)
return FALSE;
*v = tmp + add;
return TRUE;
}
/*
Duration Data Type
The duration data type is used to specify a time interval.
The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
* - indicates the negative sign (optional)
* P indicates the period (required)
* nY indicates the number of years
* nM indicates the number of months
* nD indicates the number of days
* T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
* nH indicates the number of hours
* nM indicates the number of minutes
* nS indicates the number of seconds
*/
static gboolean
_mpd_helper_parse_duration (const char *str, guint64 * value)
{
gint ret, len, pos, posT;
gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
-1, decimals = -1, read;
gboolean have_ms = FALSE;
guint64 tmp_value;
len = strlen (str);
GST_TRACE ("duration: %s, len %d", str, len);
if (strspn (str, "PT0123456789., \tHMDSY") < len) {
GST_WARNING ("Invalid character found: '%s'", str);
goto error;
}
/* skip leading/trailing whitespace */
while (g_ascii_isspace (str[0])) {
str++;
len--;
}
while (len > 0 && g_ascii_isspace (str[len - 1]))
--len;
/* read "P" for period */
if (str[0] != 'P') {
GST_WARNING ("P not found at the beginning of the string!");
goto error;
}
str++;
len--;
/* read "T" for time (if present) */
posT = strcspn (str, "T");
len -= posT;
if (posT > 0) {
/* there is some room between P and T, so there must be a period section */
/* read years, months, days */
do {
GST_TRACE ("parsing substring %s", str);
pos = strcspn (str, "YMD");
ret = sscanf (str, "%u", &read);
if (ret != 1) {
GST_WARNING ("can not read integer value from string %s!", str);
goto error;
}
switch (str[pos]) {
case 'Y':
if (years != -1 || months != -1 || days != -1) {
GST_WARNING ("year, month or day was already set");
goto error;
}
years = read;
break;
case 'M':
if (months != -1 || days != -1) {
GST_WARNING ("month or day was already set");
goto error;
}
months = read;
if (months >= 12) {
GST_WARNING ("Month out of range");
goto error;
}
break;
case 'D':
if (days != -1) {
GST_WARNING ("day was already set");
goto error;
}
days = read;
if (days >= 31) {
GST_WARNING ("Day out of range");
goto error;
}
break;
default:
GST_WARNING ("unexpected char %c!", str[pos]);
goto error;
break;
}
GST_TRACE ("read number %u type %c", read, str[pos]);
str += (pos + 1);
posT -= (pos + 1);
} while (posT > 0);
}
if (years == -1)
years = 0;
if (months == -1)
months = 0;
if (days == -1)
days = 0;
GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
/* read "T" for time (if present) */
/* here T is at pos == 0 */
str++;
len--;
pos = 0;
if (pos < len) {
/* T found, there is a time section */
/* read hours, minutes, seconds, hundredths of second */
do {
GST_TRACE ("parsing substring %s", str);
pos = strcspn (str, "HMS,.");
ret = sscanf (str, "%u", &read);
if (ret != 1) {
GST_WARNING ("can not read integer value from string %s!", str);
goto error;
}
switch (str[pos]) {
case 'H':
if (hours != -1 || minutes != -1 || seconds != -1) {
GST_WARNING ("hour, minute or second was already set");
goto error;
}
hours = read;
if (hours >= 24) {
GST_WARNING ("Hour out of range");
goto error;
}
break;
case 'M':
if (minutes != -1 || seconds != -1) {
GST_WARNING ("minute or second was already set");
goto error;
}
minutes = read;
if (minutes >= 60) {
GST_WARNING ("Minute out of range");
goto error;
}
break;
case 'S':
if (have_ms) {
/* we have read the decimal part of the seconds */
decimals = _mpd_helper_convert_to_millisecs (read, pos);
GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
decimals);
} else {
if (seconds != -1) {
GST_WARNING ("second was already set");
goto error;
}
/* no decimals */
seconds = read;
}
break;
case '.':
case ',':
/* we have read the integer part of a decimal number in seconds */
if (seconds != -1) {
GST_WARNING ("second was already set");
goto error;
}
seconds = read;
have_ms = TRUE;
break;
default:
GST_WARNING ("unexpected char %c!", str[pos]);
goto error;
break;
}
GST_TRACE ("read number %u type %c", read, str[pos]);
str += pos + 1;
len -= (pos + 1);
} while (len > 0);
}
if (hours == -1)
hours = 0;
if (minutes == -1)
minutes = 0;
if (seconds == -1)
seconds = 0;
if (decimals == -1)
decimals = 0;
GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
tmp_value = 0;
if (!_mpd_helper_accumulate (&tmp_value, 1, years)
|| !_mpd_helper_accumulate (&tmp_value, 365, months * 30)
|| !_mpd_helper_accumulate (&tmp_value, 1, days)
|| !_mpd_helper_accumulate (&tmp_value, 24, hours)
|| !_mpd_helper_accumulate (&tmp_value, 60, minutes)
|| !_mpd_helper_accumulate (&tmp_value, 60, seconds)
|| !_mpd_helper_accumulate (&tmp_value, 1000, decimals))
goto error;
/* ensure it can be converted from milliseconds to nanoseconds */
if (tmp_value > G_MAXUINT64 / 1000000)
goto error;
*value = tmp_value;
return TRUE;
error:
return FALSE;
}
static gboolean
_mpd_helper_validate_no_whitespace (const char *s)
{
return !strpbrk (s, "\r\n\t ");
}
/* API */
GstXMLRange *
gst_xml_helper_clone_range (GstXMLRange * range)
{
GstXMLRange *clone = NULL;
if (range) {
clone = g_slice_new0 (GstXMLRange);
clone->first_byte_pos = range->first_byte_pos;
clone->last_byte_pos = range->last_byte_pos;
}
return clone;
}
GstXMLRatio *
gst_xml_helper_clone_ratio (GstXMLRatio * ratio)
{
GstXMLRatio *clone = NULL;
if (ratio) {
clone = g_slice_new0 (GstXMLRatio);
clone->num = ratio->num;
clone->den = ratio->den;
}
return clone;
}
GstXMLFrameRate *
gst_xml_helper_clone_frame_rate (GstXMLFrameRate * frameRate)
{
GstXMLFrameRate *clone = NULL;
if (frameRate) {
clone = g_slice_new0 (GstXMLFrameRate);
clone->num = frameRate->num;
clone->den = frameRate->den;
}
return clone;
}
/* XML property get method */
gboolean
gst_xml_helper_get_prop_validated_string (xmlNode * a_node,
const gchar * property_name, gchar ** property_value,
gboolean (*validate) (const char *))
{
xmlChar *prop_string;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (validate && !(*validate) ((const char *) prop_string)) {
GST_WARNING ("Validation failure: %s", prop_string);
xmlFree (prop_string);
return FALSE;
}
*property_value = (gchar *) prop_string;
exists = TRUE;
GST_LOG (" - %s: %s", property_name, prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_ns_prop_string (xmlNode * a_node,
const gchar * ns_name, const gchar * property_name, gchar ** property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
prop_string =
xmlGetNsProp (a_node, (const xmlChar *) property_name,
(const xmlChar *) ns_name);
if (prop_string) {
*property_value = (gchar *) prop_string;
exists = TRUE;
GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_string (xmlNode * a_node,
const gchar * property_name, gchar ** property_value)
{
return gst_xml_helper_get_prop_validated_string (a_node, property_name,
property_value, NULL);
}
gboolean
gst_xml_helper_get_prop_string_vector_type (xmlNode * a_node,
const gchar * property_name, gchar *** property_value)
{
xmlChar *prop_string;
gchar **prop_string_vector = NULL;
guint i = 0;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
if (prop_string_vector) {
exists = TRUE;
*property_value = prop_string_vector;
GST_LOG (" - %s:", property_name);
while (prop_string_vector[i]) {
GST_LOG (" %s", prop_string_vector[i]);
i++;
}
} else {
GST_WARNING ("Scan of string vector property failed!");
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_signed_integer (xmlNode * a_node,
const gchar * property_name, gint default_val, gint * property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
exists = TRUE;
GST_LOG (" - %s: %d", property_name, *property_value);
} else {
GST_WARNING
("failed to parse signed integer property %s from xml string %s",
property_name, prop_string);
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_unsigned_integer (xmlNode * a_node,
const gchar * property_name, guint default_val, guint * property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
strstr ((gchar *) prop_string, "-") == NULL) {
exists = TRUE;
GST_LOG (" - %s: %u", property_name, *property_value);
} else {
GST_WARNING
("failed to parse unsigned integer property %s from xml string %s",
property_name, prop_string);
/* sscanf might have written to *property_value. Restore to default */
*property_value = default_val;
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_unsigned_integer_64 (xmlNode * a_node,
const gchar * property_name, guint64 default_val, guint64 * property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (g_ascii_string_to_unsigned ((gchar *) prop_string, 10, 0, G_MAXUINT64,
property_value, NULL)) {
exists = TRUE;
GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
} else {
GST_WARNING
("failed to parse unsigned integer property %s from xml string %s",
property_name, prop_string);
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_uint_vector_type (xmlNode * a_node,
const gchar * property_name, guint ** property_value, guint * value_size)
{
xmlChar *prop_string;
gchar **str_vector;
guint *prop_uint_vector = NULL, i;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
if (str_vector) {
*value_size = g_strv_length (str_vector);
prop_uint_vector = g_malloc (*value_size * sizeof (guint));
if (prop_uint_vector) {
exists = TRUE;
GST_LOG (" - %s:", property_name);
for (i = 0; i < *value_size; i++) {
if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
&& strstr (str_vector[i], "-") == NULL) {
GST_LOG (" %u", prop_uint_vector[i]);
} else {
GST_WARNING
("failed to parse uint vector type property %s from xml string %s",
property_name, str_vector[i]);
/* there is no special value to put in prop_uint_vector[i] to
* signal it is invalid, so we just clean everything and return
* FALSE
*/
g_free (prop_uint_vector);
prop_uint_vector = NULL;
exists = FALSE;
break;
}
}
*property_value = prop_uint_vector;
} else {
GST_WARNING ("Array allocation failed!");
}
} else {
GST_WARNING ("Scan of uint vector property failed!");
}
xmlFree (prop_string);
g_strfreev (str_vector);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_double (xmlNode * a_node,
const gchar * property_name, gdouble * property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
exists = TRUE;
GST_LOG (" - %s: %lf", property_name, *property_value);
} else {
GST_WARNING ("failed to parse double property %s from xml string %s",
property_name, prop_string);
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_boolean (xmlNode * a_node,
const gchar * property_name, gboolean default_val,
gboolean * property_value)
{
xmlChar *prop_string;
gboolean exists = FALSE;
*property_value = default_val;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
exists = TRUE;
*property_value = FALSE;
GST_LOG (" - %s: false", property_name);
} else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
exists = TRUE;
*property_value = TRUE;
GST_LOG (" - %s: true", property_name);
} else {
GST_WARNING ("failed to parse boolean property %s from xml string %s",
property_name, prop_string);
}
xmlFree (prop_string);
}
return exists;
}
gboolean
gst_xml_helper_get_prop_range (xmlNode * a_node,
const gchar * property_name, GstXMLRange ** property_value)
{
xmlChar *prop_string;
guint64 first_byte_pos = 0, last_byte_pos = -1;
guint len, pos;
gchar *str;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
len = xmlStrlen (prop_string);
str = (gchar *) prop_string;
GST_TRACE ("range: %s, len %d", str, len);
/* find "-" */
pos = strcspn (str, "-");
if (pos >= len) {
GST_TRACE ("pos %d >= len %d", pos, len);
goto error;
}
if (pos == 0) {
GST_TRACE ("pos == 0, but first_byte_pos is not optional");
goto error;
}
/* read first_byte_pos */
/* replace str[pos] with '\0' since we only want to read the
* first_byte_pos, and g_ascii_string_to_unsigned requires the entire
* string to be a single number, which is exactly what we want */
str[pos] = '\0';
if (!g_ascii_string_to_unsigned (str, 10, 0, G_MAXUINT64, &first_byte_pos,
NULL)) {
/* restore the '-' sign */
str[pos] = '-';
goto error;
}
/* restore the '-' sign */
str[pos] = '-';
/* read last_byte_pos, which is optional */
if (pos < (len - 1) && !g_ascii_string_to_unsigned (str + pos + 1, 10, 0,
G_MAXUINT64, &last_byte_pos, NULL)) {
goto error;
}
/* malloc return data structure */
*property_value = g_slice_new0 (GstXMLRange);
exists = TRUE;
(*property_value)->first_byte_pos = first_byte_pos;
(*property_value)->last_byte_pos = last_byte_pos;
xmlFree (prop_string);
GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
property_name, first_byte_pos, last_byte_pos);
}
return exists;
error:
GST_WARNING ("failed to parse property %s from xml string %s", property_name,
prop_string);
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_prop_ratio (xmlNode * a_node,
const gchar * property_name, GstXMLRatio ** property_value)
{
xmlChar *prop_string;
guint num = 0, den = 1;
guint len, pos;
gchar *str;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
len = xmlStrlen (prop_string);
str = (gchar *) prop_string;
GST_TRACE ("ratio: %s, len %d", str, len);
/* read ":" */
pos = strcspn (str, ":");
if (pos >= len) {
GST_TRACE ("pos %d >= len %d", pos, len);
goto error;
}
/* search for negative sign */
if (strstr (str, "-") != NULL) {
goto error;
}
/* read num */
if (pos != 0) {
if (sscanf (str, "%u", &num) != 1) {
goto error;
}
}
/* read den */
if (pos < (len - 1)) {
if (sscanf (str + pos + 1, "%u", &den) != 1) {
goto error;
}
}
/* malloc return data structure */
*property_value = g_slice_new0 (GstXMLRatio);
exists = TRUE;
(*property_value)->num = num;
(*property_value)->den = den;
xmlFree (prop_string);
GST_LOG (" - %s: %u:%u", property_name, num, den);
}
return exists;
error:
GST_WARNING ("failed to parse property %s from xml string %s", property_name,
prop_string);
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_prop_framerate (xmlNode * a_node,
const gchar * property_name, GstXMLFrameRate ** property_value)
{
xmlChar *prop_string;
guint num = 0, den = 1;
guint len, pos;
gchar *str;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
len = xmlStrlen (prop_string);
str = (gchar *) prop_string;
GST_TRACE ("framerate: %s, len %d", str, len);
/* search for negative sign */
if (strstr (str, "-") != NULL) {
goto error;
}
/* read "/" if available */
pos = strcspn (str, "/");
/* read num */
if (pos != 0) {
if (sscanf (str, "%u", &num) != 1) {
goto error;
}
}
/* read den (if available) */
if (pos < (len - 1)) {
if (sscanf (str + pos + 1, "%u", &den) != 1) {
goto error;
}
}
/* alloc return data structure */
*property_value = g_slice_new0 (GstXMLFrameRate);
exists = TRUE;
(*property_value)->num = num;
(*property_value)->den = den;
xmlFree (prop_string);
if (den == 1)
GST_LOG (" - %s: %u", property_name, num);
else
GST_LOG (" - %s: %u/%u", property_name, num, den);
}
return exists;
error:
GST_WARNING ("failed to parse property %s from xml string %s", property_name,
prop_string);
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
const gchar * property_name, GstXMLConditionalUintType ** property_value)
{
xmlChar *prop_string;
gchar *str;
gboolean flag;
guint val;
gboolean exists = FALSE;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
str = (gchar *) prop_string;
GST_TRACE ("conditional uint: %s", str);
if (strcmp (str, "false") == 0) {
flag = FALSE;
val = 0;
} else if (strcmp (str, "true") == 0) {
flag = TRUE;
val = 0;
} else {
flag = TRUE;
if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
goto error;
}
/* alloc return data structure */
*property_value = g_slice_new0 (GstXMLConditionalUintType);
exists = TRUE;
(*property_value)->flag = flag;
(*property_value)->value = val;
xmlFree (prop_string);
GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
val);
}
return exists;
error:
GST_WARNING ("failed to parse property %s from xml string %s", property_name,
prop_string);
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_prop_dateTime (xmlNode * a_node,
const gchar * property_name, GstDateTime ** property_value)
{
xmlChar *prop_string;
gchar *str;
gint ret, pos;
gint year, month, day, hour, minute;
gdouble second;
gboolean exists = FALSE;
gfloat tzoffset = 0.0;
gint gmt_offset_hour = -99, gmt_offset_min = -99;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
str = (gchar *) prop_string;
GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
/* parse year */
ret = sscanf (str, "%d", &year);
if (ret != 1 || year <= 0)
goto error;
pos = strcspn (str, "-");
str += (pos + 1);
GST_TRACE (" - year %d", year);
/* parse month */
ret = sscanf (str, "%d", &month);
if (ret != 1 || month <= 0)
goto error;
pos = strcspn (str, "-");
str += (pos + 1);
GST_TRACE (" - month %d", month);
/* parse day */
ret = sscanf (str, "%d", &day);
if (ret != 1 || day <= 0)
goto error;
pos = strcspn (str, "T");
str += (pos + 1);
GST_TRACE (" - day %d", day);
/* parse hour */
ret = sscanf (str, "%d", &hour);
if (ret != 1 || hour < 0)
goto error;
pos = strcspn (str, ":");
str += (pos + 1);
GST_TRACE (" - hour %d", hour);
/* parse minute */
ret = sscanf (str, "%d", &minute);
if (ret != 1 || minute < 0)
goto error;
pos = strcspn (str, ":");
str += (pos + 1);
GST_TRACE (" - minute %d", minute);
/* parse second */
ret = sscanf (str, "%lf", &second);
if (ret != 1 || second < 0)
goto error;
GST_TRACE (" - second %lf", second);
GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
year, month, day, hour, minute, second);
if (strrchr (str, '+') || strrchr (str, '-')) {
/* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
gint gmt_offset = -1;
gchar *plus_pos = NULL;
gchar *neg_pos = NULL;
gchar *pos = NULL;
GST_LOG ("Checking for timezone information");
/* check if there is timezone info */
plus_pos = strrchr (str, '+');
neg_pos = strrchr (str, '-');
if (plus_pos)
pos = plus_pos + 1;
else if (neg_pos)
pos = neg_pos + 1;
if (pos && strlen (pos) >= 3) {
gint ret_tz;
if (pos[2] == ':')
ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
else
ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
GST_DEBUG ("Parsing timezone: %s", pos);
if (ret_tz == 2) {
if (neg_pos != NULL && neg_pos + 1 == pos) {
gmt_offset_hour *= -1;
gmt_offset_min *= -1;
}
gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
tzoffset = gmt_offset / 60.0;
GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
} else
GST_WARNING ("Failed to parse timezone information");
}
}
exists = TRUE;
*property_value =
gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
xmlFree (prop_string);
}
return exists;
error:
GST_WARNING ("failed to parse property %s from xml string %s", property_name,
prop_string);
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_prop_duration (xmlNode * a_node,
const gchar * property_name, guint64 default_value,
guint64 * property_value)
{
xmlChar *prop_string;
gchar *str;
gboolean exists = FALSE;
*property_value = default_value;
prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
if (prop_string) {
str = (gchar *) prop_string;
if (!_mpd_helper_parse_duration (str, property_value))
goto error;
GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
xmlFree (prop_string);
exists = TRUE;
}
return exists;
error:
xmlFree (prop_string);
return FALSE;
}
gboolean
gst_xml_helper_get_node_content (xmlNode * a_node, gchar ** content)
{
xmlChar *node_content = NULL;
gboolean exists = FALSE;
node_content = xmlNodeGetContent (a_node);
if (node_content) {
exists = TRUE;
*content = (gchar *) node_content;
GST_LOG (" - %s: %s", a_node->name, *content);
}
return exists;
}
gboolean
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);
/* 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) {
*content =
(gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
xmlOutputBufferGetSize (out_buf));
exists = TRUE;
}
#else
if (out_buf->conv && out_buf->conv->use > 0) {
*content =
(gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
exists = TRUE;
} else if (out_buf->buffer && out_buf->buffer->use > 0) {
*content =
(gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
exists = TRUE;
}
#endif // LIBXML2_NEW_BUFFER
xmlFreeNode (ncopy);
done:
(void) xmlOutputBufferClose (out_buf);
if (exists) {
GST_LOG (" - %s: %s", a_node->name, *content);
}
return exists;
}
gchar *
gst_xml_helper_get_node_namespace (xmlNode * a_node, const gchar * prefix)
{
xmlNs *curr_ns;
gchar *namespace = NULL;
if (prefix == NULL) {
/* return the default namespace */
if (a_node->ns) {
namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
if (namespace) {
GST_LOG (" - default namespace: %s", namespace);
}
}
} else {
/* look for the specified prefix in the namespace list */
for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
if (namespace) {
GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
}
}
}
}
return namespace;
}
gboolean
gst_xml_helper_get_prop_string_stripped (xmlNode * a_node,
const gchar * property_name, gchar ** property_value)
{
gboolean ret;
ret = gst_xml_helper_get_prop_string (a_node, property_name, property_value);
if (ret)
*property_value = g_strstrip (*property_value);
return ret;
}
gboolean
gst_xml_helper_get_prop_string_no_whitespace (xmlNode * a_node,
const gchar * property_name, gchar ** property_value)
{
return gst_xml_helper_get_prop_validated_string (a_node, property_name,
property_value, _mpd_helper_validate_no_whitespace);
}
/* XML property set method */
void
gst_xml_helper_set_prop_string (xmlNodePtr node, const gchar * name,
gchar * value)
{
if (value)
xmlSetProp (node, (xmlChar *) name, (xmlChar *) value);
}
void
gst_xml_helper_set_prop_boolean (xmlNodePtr node, const gchar * name,
gboolean value)
{
if (value)
xmlSetProp (node, (xmlChar *) name, (xmlChar *) "true");
else
xmlSetProp (node, (xmlChar *) name, (xmlChar *) "false");
}
void
gst_xml_helper_set_prop_int (xmlNodePtr node, const gchar * name, gint value)
{
gchar *text;
text = g_strdup_printf ("%d", value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
void
gst_xml_helper_set_prop_uint (xmlNodePtr node, const gchar * name, guint value)
{
gchar *text;
text = g_strdup_printf ("%d", value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
void
gst_xml_helper_set_prop_int64 (xmlNodePtr node, const gchar * name,
gint64 value)
{
gchar *text;
text = g_strdup_printf ("%" G_GINT64_FORMAT, value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
void
gst_xml_helper_set_prop_uint64 (xmlNodePtr node, const gchar * name,
guint64 value)
{
gchar *text;
text = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
void
gst_xml_helper_set_prop_double (xmlNodePtr node, const gchar * name,
gdouble value)
{
gchar *text;
text = g_strdup_printf ("%lf", value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
void
gst_xml_helper_set_prop_uint_vector_type (xmlNode * node, const gchar * name,
guint * value, guint value_size)
{
int i;
gchar *text = NULL;
gchar *prev;
gchar *temp;
for (i = 0; i < value_size; i++) {
temp = g_strdup_printf ("%d", value[i]);
prev = text;
text = g_strjoin (" ", text, prev, NULL);
g_free (prev);
g_free (temp);
}
if (text) {
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_date_time (xmlNodePtr node, const gchar * name,
GstDateTime * value)
{
gchar *text;
if (value) {
text = gst_date_time_to_iso8601_string (value);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_duration (xmlNode * node, const gchar * name,
guint64 value)
{
gchar *text;
gint years, months, days, hours, minutes, seconds, milliseconds;
if (value) {
years = (gint) (XML_HELPER_MS_TO_SEC (value) / (XML_HELPER_YEAR_TO_SEC));
months =
(gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_YEAR_TO_SEC) /
XML_HELPER_MONTH_TO_SEC);
days =
(gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_MONTH_TO_SEC) /
XML_HELPER_DAY_TO_SEC);
hours =
(gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_DAY_TO_SEC) /
XML_HELPER_HOUR_TO_SEC);
minutes =
(gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_HOUR_TO_SEC) /
XML_HELPER_MINUTE_TO_SEC);
seconds = (gint) (XML_HELPER_MS_TO_SEC (value) % XML_HELPER_MINUTE_TO_SEC);
milliseconds = value % 1000;
text =
g_strdup_printf ("P%dY%dM%dDT%dH%dM%d.%dS", years, months, days, hours,
minutes, seconds, milliseconds);
GST_LOG ("duration %" G_GUINT64_FORMAT " -> %s", value, text);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_ratio (xmlNodePtr node, const gchar * name,
GstXMLRatio * value)
{
gchar *text;
if (value) {
text = g_strdup_printf ("%d:%d", value->num, value->den);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_framerate (xmlNodePtr node, const gchar * name,
GstXMLFrameRate * value)
{
gchar *text;
if (value) {
text = g_strdup_printf ("%d/%d", value->num, value->den);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_range (xmlNodePtr node, const gchar * name,
GstXMLRange * value)
{
gchar *text;
if (value) {
text =
g_strdup_printf ("%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
value->first_byte_pos, value->last_byte_pos);
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_prop_cond_uint (xmlNodePtr node, const gchar * name,
GstXMLConditionalUintType * cond)
{
gchar *text;
if (cond) {
if (cond->flag)
if (cond->value)
text = g_strdup_printf ("%d", cond->value);
else
text = g_strdup_printf ("%s", "true");
else
text = g_strdup_printf ("%s", "false");
xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
g_free (text);
}
}
void
gst_xml_helper_set_content (xmlNodePtr node, gchar * content)
{
if (content)
xmlNodeSetContent (node, (xmlChar *) content);
}