mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-20 23:36:38 +00:00
af78c16dd5
This provides new HLS, DASH and MSS adaptive demuxer elements as a single plugin. These elements offer many improvements over the legacy elements. They will only work within a streams-aware context (`urisourcebin`, `uridecodebin3`, `decodebin3`, `playbin3`, ...). Stream selection and buffering is handled internally, this allows them to directly manage the elementary streams and stream selection. Authors: * Edward Hervey <edward@centricular.com> * Jan Schmidt <jan@centricular.com> * Piotrek Brzeziński <piotr@centricular.com> * Tim-Philipp Müller <tim@centricular.com> Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2117>
1259 lines
34 KiB
C
1259 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 GST_CAT_DEFAULT gst_dash_demux2_debug
|
|
|
|
#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;
|
|
|
|
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);
|
|
(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
|
|
(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);
|
|
}
|