gstmpdparser.c 194 KB
Newer Older
1
/*
2
 * DASH MPD parsing library
3
 *
4
 * gstmpdparser.c
5
 *
6
 * Copyright (C) 2012 STMicroelectronics
7
 *
8 9
 * Authors:
 *   Gianluca Gennari <gennarone@gmail.com>
10 11 12 13
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
14
 * version 2.1 of the License, or (at your option) any later version.
15 16 17 18 19 20 21
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
22
 * License along with this library (COPYING); if not, write to the
23 24 25 26 27
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <string.h>
Thiago Santos's avatar
Thiago Santos committed
28 29
#include <libxml/parser.h>
#include <libxml/tree.h>
30
#include "gstmpdparser.h"
31 32 33
#include "gstdash_debug.h"

#define GST_CAT_DEFAULT gst_dash_demux_debug
34 35

/* Property parsing */
36 37 38
static gboolean gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value,
    gboolean (*validator) (const char *));
39 40
static gboolean gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value);
41 42
static gboolean gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value);
43 44 45
static gboolean gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
    const gchar * ns_name, const gchar * property_name,
    gchar ** property_value);
46 47
static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
    const gchar * property_name, gchar *** property_value);
48 49
static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
    const gchar * property_name, gint default_val, gint * property_value);
50 51 52 53 54 55 56 57 58
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
    const gchar * property_name, guint default_val, guint * property_value);
static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
    a_node, const gchar * property_name, guint64 default_val,
    guint64 * property_value);
static gboolean gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
    const gchar * property_name, guint ** property_value, guint * value_size);
static gboolean gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
    const gchar * property_name, gdouble * property_value);
David Corvoysier's avatar
David Corvoysier committed
59
static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    const gchar * property_name, gboolean default_val,
    gboolean * property_value);
static gboolean gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
    const gchar * property_name, GstMPDFileType * property_value);
static gboolean gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
    const gchar * property_name, GstSAPType * property_value);
static gboolean gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
    const gchar * property_name, GstRange ** property_value);
static gboolean gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
    const gchar * property_name, GstRatio ** property_value);
static gboolean gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
    const gchar * property_name, GstFrameRate ** property_value);
static gboolean gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
    const gchar * property_name, GstConditionalUintType ** property_value);
static gboolean gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
    const gchar * property_name, GstDateTime ** property_value);
static gboolean gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
77 78
    const gchar * property_name, guint64 default_value,
    guint64 * property_value);
79 80
static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
    gchar ** content);
David Corvoysier's avatar
David Corvoysier committed
81 82
static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
    const gchar * prefix);
83 84
static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
    gchar ** content);
85 86 87

/* XML node parsing */
static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
David Corvoysier's avatar
David Corvoysier committed
88 89 90 91
static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
    xmlNode * a_node);
static void gst_mpdparser_parse_content_component_node (GList ** list,
    xmlNode * a_node);
92
static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
David Corvoysier's avatar
David Corvoysier committed
93 94 95 96 97 98 99
static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
    xmlNode * a_node);
static void gst_mpdparser_parse_segment_url_node (GList ** list,
    xmlNode * a_node);
static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
    xmlNode * a_node);
static void gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType **
100
    pointer, xmlNode * a_node, GstSegmentBaseType * parent);
101
static void gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node);
David Corvoysier's avatar
David Corvoysier committed
102 103
static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
    pointer, xmlNode * a_node);
104 105 106 107
static gboolean
gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
    xmlNode * a_node, GstMultSegmentBaseType * parent);
static gboolean gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
108
    pointer, xmlNode * a_node, GstSegmentListNode * parent);
David Corvoysier's avatar
David Corvoysier committed
109 110 111
static void
gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
    pointer, xmlNode * a_node);
112
static gboolean gst_mpdparser_parse_representation_node (GList ** list,
113 114
    xmlNode * a_node, GstAdaptationSetNode * parent,
    GstPeriodNode * period_node);
115
static gboolean gst_mpdparser_parse_adaptation_set_node (GList ** list,
116
    xmlNode * a_node, GstPeriodNode * parent);
117
static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
118 119 120 121 122
static gboolean
gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
    xmlNode * a_node, GstSegmentTemplateNode * parent);
static gboolean gst_mpdparser_parse_period_node (GList ** list,
    xmlNode * a_node);
David Corvoysier's avatar
David Corvoysier committed
123 124 125 126
static void gst_mpdparser_parse_program_info_node (GList ** list,
    xmlNode * a_node);
static void gst_mpdparser_parse_metrics_range_node (GList ** list,
    xmlNode * a_node);
127
static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
128
static gboolean gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
David Corvoysier's avatar
David Corvoysier committed
129
    xmlNode * a_node);
130 131
static void gst_mpdparser_parse_utctiming_node (GList ** list,
    xmlNode * a_node);
132 133

/* Helper functions */
134
static guint convert_to_millisecs (guint decimals, gint pos);
135
static int strncmp_ext (const char *s1, const char *s2);
136
static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
137 138 139 140 141
static GstSNode *gst_mpdparser_clone_s_node (GstSNode * pointer);
static GstSegmentTimelineNode
    * gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer);
static GstRange *gst_mpdparser_clone_range (GstRange * range);
static GstURLType *gst_mpdparser_clone_URL (GstURLType * url);
142
static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
143
    GstActiveStream * stream, gchar ** query);
144 145
static GstSegmentURLNode *gst_mpdparser_clone_segment_url (GstSegmentURLNode *
    seg_url);
146
static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
David Corvoysier's avatar
David Corvoysier committed
147
    GstSegmentURLNode * segmentURL);
Thiago Santos's avatar
Thiago Santos committed
148 149
static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream *
    stream, GstURLType * InitializationURL);
David Corvoysier's avatar
David Corvoysier committed
150
static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
151
    const gchar * id, guint number, guint bandwidth, guint64 time);
David Corvoysier's avatar
David Corvoysier committed
152
static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
153 154 155
    GstSegmentURLNode * url_node, guint number, gint repeat,
    guint64 scale_start, guint64 scale_duration, GstClockTime start,
    GstClockTime duration);
156
static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
157
static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client,
158
    GstActiveStream * stream, guint64 * scale_duration);
159 160
static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient *
    client);
161 162

/* Representation */
David Corvoysier's avatar
David Corvoysier committed
163 164
static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
    Representations);
165
#if 0
David Corvoysier's avatar
David Corvoysier committed
166 167 168
static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
    Representations);
static GstRepresentationNode
Thiago Santos's avatar
Thiago Santos committed
169
    * gst_mpdparser_get_representation_with_max_bandwidth (GList *
David Corvoysier's avatar
David Corvoysier committed
170
    Representations, gint max_bandwidth);
171
#endif
David Corvoysier's avatar
David Corvoysier committed
172 173 174
static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
    Period, GstAdaptationSetNode * AdaptationSet,
    GstRepresentationNode * Representation);
175 176
static GstSegmentListNode *gst_mpdparser_get_segment_list (GstMpdClient *
    client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
David Corvoysier's avatar
David Corvoysier committed
177
    GstRepresentationNode * Representation);
178

179
/* Segments */
180 181
static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
    GstActiveStream * stream);
182

183
/* Memory management */
Thiago Santos's avatar
Thiago Santos committed
184
static GstSegmentTimelineNode *gst_mpdparser_segment_timeline_node_new (void);
185
static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
David Corvoysier's avatar
David Corvoysier committed
186 187
static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
    prog_info_node);
188
static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
David Corvoysier's avatar
David Corvoysier committed
189 190
static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
    metrics_range_node);
191 192
static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
David Corvoysier's avatar
David Corvoysier committed
193 194 195 196 197 198 199 200 201 202 203
static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
    segment_template_node);
static void
gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
    representation_base);
static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
    adaptation_set_node);
static void gst_mpdparser_free_representation_node (GstRepresentationNode *
    representation_node);
static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
    * subrep_node);
204
static void gst_mpdparser_free_s_node (GstSNode * s_node);
David Corvoysier's avatar
David Corvoysier committed
205 206
static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
    seg_timeline);
207
static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
David Corvoysier's avatar
David Corvoysier committed
208 209 210 211 212 213 214 215
static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
    seg_base_type);
static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
    mult_seg_base_type);
static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
    segment_list_node);
static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
    segment_url);
216
static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
David Corvoysier's avatar
David Corvoysier committed
217 218 219 220
static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
    descriptor_type);
static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
    content_component_node);
221
static void gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type);
222
static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
223 224 225
static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);

226 227 228
static GstUri *combine_urls (GstUri * base, GList * list, gchar ** query,
    guint idx);

229
static GList *gst_mpd_client_fetch_external_period (GstMpdClient * client,
230
    GstPeriodNode * period_node);
231
static GList *gst_mpd_client_fetch_external_adaptation_set (GstMpdClient *
232
    client, GstPeriodNode * period, GstAdaptationSetNode * adapt_set);
233

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
struct GstMpdParserUtcTimingMethod
{
  const gchar *name;
  GstMPDUTCTimingType method;
};

static const struct GstMpdParserUtcTimingMethod
    gst_mpdparser_utc_timing_methods[] = {
  {"urn:mpeg:dash:utc:ntp:2014", GST_MPD_UTCTIMING_TYPE_NTP},
  {"urn:mpeg:dash:utc:sntp:2014", GST_MPD_UTCTIMING_TYPE_SNTP},
  {"urn:mpeg:dash:utc:http-head:2014", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
  {"urn:mpeg:dash:utc:http-xsdate:2014", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
  {"urn:mpeg:dash:utc:http-iso:2014", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
  {"urn:mpeg:dash:utc:http-ntp:2014", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
  {"urn:mpeg:dash:utc:direct:2014", GST_MPD_UTCTIMING_TYPE_DIRECT},
  /*
   * Early working drafts used the :2012 namespace and this namespace is
   * used by some DASH packagers. To work-around these packagers, we also
   * accept the early draft scheme names.
   */
  {"urn:mpeg:dash:utc:ntp:2012", GST_MPD_UTCTIMING_TYPE_NTP},
  {"urn:mpeg:dash:utc:sntp:2012", GST_MPD_UTCTIMING_TYPE_SNTP},
  {"urn:mpeg:dash:utc:http-head:2012", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
  {"urn:mpeg:dash:utc:http-xsdate:2012", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
  {"urn:mpeg:dash:utc:http-iso:2012", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
  {"urn:mpeg:dash:utc:http-ntp:2012", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
  {"urn:mpeg:dash:utc:direct:2012", GST_MPD_UTCTIMING_TYPE_DIRECT},
  {NULL, 0}
};

264
/* functions to parse node namespaces, content and properties */
265
static gboolean
266 267 268
gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value,
    gboolean (*validate) (const char *))
269 270
{
  xmlChar *prop_string;
271
  gboolean exists = FALSE;
272

273
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
274
  if (prop_string) {
275 276 277 278 279
    if (validate && !(*validate) ((const char *) prop_string)) {
      GST_WARNING ("Validation failure: %s", prop_string);
      xmlFree (prop_string);
      return FALSE;
    }
280 281 282
    *property_value = (gchar *) prop_string;
    exists = TRUE;
    GST_LOG (" - %s: %s", property_name, prop_string);
283 284
  }

285
  return exists;
286 287
}

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
static gboolean
gst_mpdparser_get_xml_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;
}

307 308 309 310 311 312 313 314
static gboolean
gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value)
{
  return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
      property_value, NULL);
}

315 316 317 318 319 320 321 322 323 324 325 326
static gboolean
gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value)
{
  gboolean ret;
  ret =
      gst_mpdparser_get_xml_prop_string (a_node, property_name, property_value);
  if (ret)
    *property_value = g_strstrip (*property_value);
  return ret;
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340
static gboolean
gst_mpdparser_validate_no_whitespace (const char *s)
{
  return !strpbrk (s, "\r\n\t ");
}

static gboolean
gst_mpdparser_get_xml_prop_string_no_whitespace (xmlNode * a_node,
    const gchar * property_name, gchar ** property_value)
{
  return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
      property_value, gst_mpdparser_validate_no_whitespace);
}

341
static gboolean
David Corvoysier's avatar
David Corvoysier committed
342
gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
343
    const gchar * property_name, gchar *** property_value)
344 345 346 347
{
  xmlChar *prop_string;
  gchar **prop_string_vector = NULL;
  guint i = 0;
348
  gboolean exists = FALSE;
349

350
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
351 352
  if (prop_string) {
    prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
353 354 355 356 357 358 359 360 361
    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 {
362 363 364 365 366
      GST_WARNING ("Scan of string vector property failed!");
    }
    xmlFree (prop_string);
  }

367
  return exists;
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
static gboolean
gst_mpdparser_get_xml_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;
}

394
static gboolean
David Corvoysier's avatar
David Corvoysier committed
395
gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
396
    const gchar * property_name, guint default_val, guint * property_value)
397 398
{
  xmlChar *prop_string;
399
  gboolean exists = FALSE;
400

401 402
  *property_value = default_val;
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
403
  if (prop_string) {
404 405
    if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
        strstr ((gchar *) prop_string, "-") == NULL) {
406 407
      exists = TRUE;
      GST_LOG (" - %s: %u", property_name, *property_value);
408
    } else {
David Corvoysier's avatar
David Corvoysier committed
409 410
      GST_WARNING
          ("failed to parse unsigned integer property %s from xml string %s",
411
          property_name, prop_string);
412 413
      /* sscanf might have written to *property_value. Restore to default */
      *property_value = default_val;
414 415 416 417
    }
    xmlFree (prop_string);
  }

418
  return exists;
419 420
}

421
static gboolean
422
gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
423
    const gchar * property_name, guint64 default_val, guint64 * property_value)
424 425
{
  xmlChar *prop_string;
426
  gboolean exists = FALSE;
427

428 429
  *property_value = default_val;
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
430
  if (prop_string) {
431
    if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
432 433
            property_value) == 1 &&
        strstr ((gchar *) prop_string, "-") == NULL) {
434 435
      exists = TRUE;
      GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
436 437 438
    } else {
      GST_WARNING
          ("failed to parse unsigned integer property %s from xml string %s",
439
          property_name, prop_string);
440 441
      /* sscanf might have written to *property_value. Restore to default */
      *property_value = default_val;
442 443 444 445
    }
    xmlFree (prop_string);
  }

446
  return exists;
447 448
}

449
static gboolean
David Corvoysier's avatar
David Corvoysier committed
450
gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
451
    const gchar * property_name, guint ** property_value, guint * value_size)
452 453 454 455
{
  xmlChar *prop_string;
  gchar **str_vector;
  guint *prop_uint_vector = NULL, i;
456
  gboolean exists = FALSE;
457

458
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
459 460
  if (prop_string) {
    str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
461 462 463 464 465 466 467
    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++) {
468 469
          if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
              && strstr (str_vector[i], "-") == NULL) {
470 471 472 473 474
            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]);
475 476 477 478 479 480 481 482
            /* 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;
483
          }
484
        }
485 486 487
        *property_value = prop_uint_vector;
      } else {
        GST_WARNING ("Array allocation failed!");
488
      }
489 490
    } else {
      GST_WARNING ("Scan of uint vector property failed!");
491 492 493 494 495
    }
    xmlFree (prop_string);
    g_strfreev (str_vector);
  }

496
  return exists;
497 498
}

499 500 501
static gboolean
gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
    const gchar * property_name, gdouble * property_value)
502 503
{
  xmlChar *prop_string;
504
  gboolean exists = FALSE;
505

506
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
507
  if (prop_string) {
508
    if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
509 510
      exists = TRUE;
      GST_LOG (" - %s: %lf", property_name, *property_value);
511 512
    } else {
      GST_WARNING ("failed to parse double property %s from xml string %s",
513
          property_name, prop_string);
514 515 516 517
    }
    xmlFree (prop_string);
  }

518
  return exists;
519 520 521
}

static gboolean
522 523 524
gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
    const gchar * property_name, gboolean default_val,
    gboolean * property_value)
525 526
{
  xmlChar *prop_string;
527
  gboolean exists = FALSE;
528

529 530
  *property_value = default_val;
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
531 532
  if (prop_string) {
    if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
533 534 535
      exists = TRUE;
      *property_value = FALSE;
      GST_LOG (" - %s: false", property_name);
536
    } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
537 538 539
      exists = TRUE;
      *property_value = TRUE;
      GST_LOG (" - %s: true", property_name);
540 541
    } else {
      GST_WARNING ("failed to parse boolean property %s from xml string %s",
542
          property_name, prop_string);
543 544 545 546
    }
    xmlFree (prop_string);
  }

547
  return exists;
548 549
}

550 551 552
static gboolean
gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
    const gchar * property_name, GstMPDFileType * property_value)
553 554
{
  xmlChar *prop_string;
555
  gboolean exists = FALSE;
556

557 558
  *property_value = GST_MPD_FILE_TYPE_STATIC;   /* default */
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
559 560 561
  if (prop_string) {
    if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
        || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
562 563 564
      exists = TRUE;
      *property_value = GST_MPD_FILE_TYPE_STATIC;
      GST_LOG (" - %s: static", property_name);
565 566
    } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
        || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
567 568 569
      exists = TRUE;
      *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
      GST_LOG (" - %s: dynamic", property_name);
570 571
    } else {
      GST_WARNING ("failed to parse MPD type property %s from xml string %s",
572
          property_name, prop_string);
573 574 575 576
    }
    xmlFree (prop_string);
  }

577
  return exists;
578 579
}

580 581 582
static gboolean
gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
    const gchar * property_name, GstSAPType * property_value)
583 584 585
{
  xmlChar *prop_string;
  guint prop_SAP_type = 0;
586
  gboolean exists = FALSE;
587

588
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
589
  if (prop_string) {
590
    if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
591
        && prop_SAP_type <= 6) {
592 593 594
      exists = TRUE;
      *property_value = (GstSAPType) prop_SAP_type;
      GST_LOG (" - %s: %u", property_name, prop_SAP_type);
595
    } else {
David Corvoysier's avatar
David Corvoysier committed
596 597
      GST_WARNING
          ("failed to parse unsigned integer property %s from xml string %s",
598
          property_name, prop_string);
599 600 601 602
    }
    xmlFree (prop_string);
  }

603
  return exists;
604 605
}

606 607 608
static gboolean
gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property_name,
    GstRange ** property_value)
609 610
{
  xmlChar *prop_string;
611
  guint64 first_byte_pos = 0, last_byte_pos = -1;
612 613
  guint len, pos;
  gchar *str;
614
  gboolean exists = FALSE;
615

616
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
617 618 619 620 621 622 623 624 625 626 627 628 629
  if (prop_string) {
    len = xmlStrlen (prop_string);
    str = (gchar *) prop_string;
    GST_TRACE ("range: %s, len %d", str, len);

    /* read "-" */
    pos = strcspn (str, "-");
    if (pos >= len) {
      GST_TRACE ("pos %d >= len %d", pos, len);
      goto error;
    }
    /* read first_byte_pos */
    if (pos != 0) {
630 631 632 633 634 635 636 637 638 639
      /* replace str[pos] with '\0' to allow sscanf to not be confused by
       * the minus sign (eg " -1" (observe the space before -) would otherwise
       * be interpreted as range -1 to 1)
       */
      str[pos] = 0;
      if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
          strstr (str, "-") != NULL) {
        /* sscanf failed or it found a negative number */
        /* restore the '-' sign */
        str[pos] = '-';
640 641
        goto error;
      }
642 643
      /* restore the '-' sign */
      str[pos] = '-';
644 645 646
    }
    /* read last_byte_pos */
    if (pos < (len - 1)) {
647 648
      if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
          strstr (str + pos + 1, "-") != NULL) {
649 650 651 652
        goto error;
      }
    }
    /* malloc return data structure */
653 654 655 656
    *property_value = g_slice_new0 (GstRange);
    exists = TRUE;
    (*property_value)->first_byte_pos = first_byte_pos;
    (*property_value)->last_byte_pos = last_byte_pos;
657
    xmlFree (prop_string);
658 659
    GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
        property_name, first_byte_pos, last_byte_pos);
660 661
  }

662
  return exists;
663 664

error:
665
  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
666
      prop_string);
667
  xmlFree (prop_string);
668
  return FALSE;
669 670
}

671 672 673
static gboolean
gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
    const gchar * property_name, GstRatio ** property_value)
674 675 676 677 678
{
  xmlChar *prop_string;
  guint num = 0, den = 1;
  guint len, pos;
  gchar *str;
679
  gboolean exists = FALSE;
680

681
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
682 683 684 685 686 687 688 689 690 691 692
  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;
    }
693 694 695 696
    /* search for negative sign */
    if (strstr (str, "-") != NULL) {
      goto error;
    }
697 698 699 700 701 702 703 704 705 706 707 708 709
    /* 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 */
710 711 712 713
    *property_value = g_slice_new0 (GstRatio);
    exists = TRUE;
    (*property_value)->num = num;
    (*property_value)->den = den;
714
    xmlFree (prop_string);
715
    GST_LOG (" - %s: %u:%u", property_name, num, den);
716 717
  }

718
  return exists;
719 720

error:
721
  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
722
      prop_string);
723
  xmlFree (prop_string);
724
  return FALSE;
725 726
}

727 728 729
static gboolean
gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
    const gchar * property_name, GstFrameRate ** property_value)
730 731 732 733 734
{
  xmlChar *prop_string;
  guint num = 0, den = 1;
  guint len, pos;
  gchar *str;
735
  gboolean exists = FALSE;
736

737
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
738 739 740 741 742
  if (prop_string) {
    len = xmlStrlen (prop_string);
    str = (gchar *) prop_string;
    GST_TRACE ("framerate: %s, len %d", str, len);

743 744 745 746 747
    /* search for negative sign */
    if (strstr (str, "-") != NULL) {
      goto error;
    }

748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
    /* 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 */
763 764 765 766 767
    *property_value = g_slice_new0 (GstFrameRate);
    exists = TRUE;
    (*property_value)->num = num;
    (*property_value)->den = den;
    xmlFree (prop_string);
768
    if (den == 1)
769
      GST_LOG (" - %s: %u", property_name, num);
770
    else
771
      GST_LOG (" - %s: %u/%u", property_name, num, den);
772 773
  }

774
  return exists;
775 776

error:
777
  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
778
      prop_string);
779
  xmlFree (prop_string);
780
  return FALSE;
781 782
}

783 784 785
static gboolean
gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
    const gchar * property_name, GstConditionalUintType ** property_value)
786 787 788 789 790
{
  xmlChar *prop_string;
  gchar *str;
  gboolean flag;
  guint val;
791
  gboolean exists = FALSE;
792

793
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
794 795 796 797 798 799 800 801 802 803 804 805
  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;
806
      if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
807 808 809 810
        goto error;
    }

    /* alloc return data structure */
811 812 813 814
    *property_value = g_slice_new0 (GstConditionalUintType);
    exists = TRUE;
    (*property_value)->flag = flag;
    (*property_value)->value = val;
815
    xmlFree (prop_string);
816 817
    GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
        val);
818 819
  }

820
  return exists;
821 822

error:
823
  GST_WARNING ("failed to parse property %s from xml string %s", property_name,
824
      prop_string);
825
  xmlFree (prop_string);
826
  return FALSE;
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
}

/*
  DateTime Data Type

  The dateTime data type is used to specify a date and a time.

  The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:

    * YYYY indicates the year
    * MM indicates the month
    * DD indicates the day
    * T indicates the start of the required time section
    * hh indicates the hour
    * mm indicates the minute
    * ss indicates the second

  Note: All components are required!
*/
846 847 848
static gboolean
gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
    const gchar * property_name, GstDateTime ** property_value)
849 850 851
{
  xmlChar *prop_string;
  gchar *str;
852
  gint ret, pos;
853 854
  gint year, month, day, hour, minute;
  gdouble second;
855
  gboolean exists = FALSE;
856

857
  prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
858 859
  if (prop_string) {
    str = (gchar *) prop_string;
860
    GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
861 862
    /* parse year */
    ret = sscanf (str, "%d", &year);
863
    if (ret != 1 || year <= 0)
864 865 866 867 868 869
      goto error;
    pos = strcspn (str, "-");
    str += (pos + 1);
    GST_TRACE (" - year %d", year);
    /* parse month */
    ret = sscanf (str, "%d", &month);
870
    if (ret != 1 || month <= 0)
871 872 873 874 875 876
      goto error;
    pos = strcspn (str, "-");
    str += (pos + 1);
    GST_TRACE (" - month %d", month);
    /* parse day */
    ret = sscanf (str, "%d", &day);
877
    if (ret != 1 || day <= 0)
878 879 880 881 882 883
      goto error;
    pos = strcspn (str, "T");
    str += (pos + 1);
    GST_TRACE (" - day %d", day);
    /* parse hour */
    ret = sscanf (str, "%d", &hour);
884
    if (ret != 1 || hour < 0)
885 886 887 888 889 890
      goto error;
    pos = strcspn (str, ":");
    str += (pos + 1);
    GST_TRACE (" - hour %d", hour);
    /* parse minute */
    ret = sscanf (str, "%d", &minute);
891
    if (ret != 1 || minute < 0)
892 893 894 895 896
      goto error;
    pos = strcspn (str, ":");
    str += (pos + 1);
    GST_TRACE (" - minute %d", minute);
    /* parse second */
897
    ret = sscanf (str, "%lf", &second);
898
    if (ret != 1 || second < 0)
899
      goto error;
900
    GST_TRACE (" - second %lf", second);
901