documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / ext / dash / gstmpdparser.c
1 /*
2  * DASH MPD parsing library
3  *
4  * gstmpdparser.c
5  *
6  * Copyright (C) 2012 STMicroelectronics
7  *
8  * Authors:
9  *   Gianluca Gennari <gennarone@gmail.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * 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  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * 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  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 #include <string.h>
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include "gstmpdparser.h"
31 #include "gstdash_debug.h"
32
33 #define GST_CAT_DEFAULT gst_dash_demux_debug
34
35 /* Property parsing */
36 static gboolean gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
37     const gchar * property_name, gchar ** property_value,
38     gboolean (*validator) (const char *));
39 static gboolean gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
40     const gchar * property_name, gchar ** property_value);
41 static gboolean gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
42     const gchar * property_name, gchar ** property_value);
43 static gboolean gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
44     const gchar * ns_name, const gchar * property_name,
45     gchar ** property_value);
46 static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
47     const gchar * property_name, gchar *** property_value);
48 static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
49     const gchar * property_name, gint default_val, gint * property_value);
50 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
51     const gchar * property_name, guint default_val, guint * property_value);
52 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
53     a_node, const gchar * property_name, guint64 default_val,
54     guint64 * property_value);
55 static gboolean gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
56     const gchar * property_name, guint ** property_value, guint * value_size);
57 static gboolean gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
58     const gchar * property_name, gdouble * property_value);
59 static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
60     const gchar * property_name, gboolean default_val,
61     gboolean * property_value);
62 static gboolean gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
63     const gchar * property_name, GstMPDFileType * property_value);
64 static gboolean gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
65     const gchar * property_name, GstSAPType * property_value);
66 static gboolean gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
67     const gchar * property_name, GstRange ** property_value);
68 static gboolean gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
69     const gchar * property_name, GstRatio ** property_value);
70 static gboolean gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
71     const gchar * property_name, GstFrameRate ** property_value);
72 static gboolean gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
73     const gchar * property_name, GstConditionalUintType ** property_value);
74 static gboolean gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
75     const gchar * property_name, GstDateTime ** property_value);
76 static gboolean gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
77     const gchar * property_name, guint64 default_value,
78     guint64 * property_value);
79 static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
80     gchar ** content);
81 static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
82     const gchar * prefix);
83 static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
84     gchar ** content);
85
86 /* XML node parsing */
87 static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
88 static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
89     xmlNode * a_node);
90 static void gst_mpdparser_parse_content_component_node (GList ** list,
91     xmlNode * a_node);
92 static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
93 static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
94     xmlNode * a_node);
95 static void gst_mpdparser_parse_segment_url_node (GList ** list,
96     xmlNode * a_node);
97 static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
98     xmlNode * a_node);
99 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);
102 static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
103     pointer, xmlNode * a_node);
104 static gboolean
105 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
106     xmlNode * a_node, GstMultSegmentBaseType * parent);
107 static gboolean gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
108     pointer, xmlNode * a_node, GstSegmentListNode * parent);
109 static void
110 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
111     pointer, xmlNode * a_node);
112 static gboolean gst_mpdparser_parse_representation_node (GList ** list,
113     xmlNode * a_node, GstAdaptationSetNode * parent,
114     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 static gboolean
119 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
120     xmlNode * a_node, GstSegmentTemplateNode * parent);
121 static gboolean gst_mpdparser_parse_period_node (GList ** list,
122     xmlNode * a_node);
123 static void gst_mpdparser_parse_program_info_node (GList ** list,
124     xmlNode * a_node);
125 static void gst_mpdparser_parse_metrics_range_node (GList ** list,
126     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,
129     xmlNode * a_node);
130 static void gst_mpdparser_parse_utctiming_node (GList ** list,
131     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 static GstSNode *gst_mpdparser_clone_s_node (GstSNode * pointer);
138 static GstSegmentTimelineNode
139     * gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer);
140 static GstRange *gst_mpdparser_clone_range (GstRange * range);
141 static GstURLType *gst_mpdparser_clone_URL (GstURLType * url);
142 static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
143     GstActiveStream * stream, gchar ** query);
144 static GstSegmentURLNode *gst_mpdparser_clone_segment_url (GstSegmentURLNode *
145     seg_url);
146 static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
147     GstSegmentURLNode * segmentURL);
148 static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream *
149     stream, GstURLType * InitializationURL);
150 static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
151     const gchar * id, guint number, guint bandwidth, guint64 time);
152 static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
153     GstSegmentURLNode * url_node, guint number, gint repeat,
154     guint64 scale_start, guint64 scale_duration, GstClockTime start,
155     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 static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient *
160     client);
161
162 /* Representation */
163 static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
164     Representations);
165 #if 0
166 static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
167     Representations);
168 static GstRepresentationNode
169     * gst_mpdparser_get_representation_with_max_bandwidth (GList *
170     Representations, gint max_bandwidth);
171 #endif
172 static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
173     Period, GstAdaptationSetNode * AdaptationSet,
174     GstRepresentationNode * Representation);
175 static GstSegmentListNode *gst_mpdparser_get_segment_list (GstMpdClient *
176     client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
177     GstRepresentationNode * Representation);
178
179 /* Segments */
180 static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
181     GstActiveStream * stream);
182
183 /* Memory management */
184 static GstSegmentTimelineNode *gst_mpdparser_segment_timeline_node_new (void);
185 static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
186 static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
187     prog_info_node);
188 static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
189 static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
190     metrics_range_node);
191 static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
192 static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
193 static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
194     segment_template_node);
195 static void
196 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
197     representation_base);
198 static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
199     adaptation_set_node);
200 static void gst_mpdparser_free_representation_node (GstRepresentationNode *
201     representation_node);
202 static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
203     * subrep_node);
204 static void gst_mpdparser_free_s_node (GstSNode * s_node);
205 static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
206     seg_timeline);
207 static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
208 static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
209     seg_base_type);
210 static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
211     mult_seg_base_type);
212 static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
213     segment_list_node);
214 static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
215     segment_url);
216 static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
217 static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
218     descriptor_type);
219 static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
220     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 static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
224 static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
225
226 static GstUri *combine_urls (GstUri * base, GList * list, gchar ** query,
227     guint idx);
228
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 struct GstMpdParserUtcTimingMethod
235 {
236   const gchar *name;
237   GstMPDUTCTimingType method;
238 };
239
240 static const struct GstMpdParserUtcTimingMethod
241     gst_mpdparser_utc_timing_methods[] = {
242   {"urn:mpeg:dash:utc:ntp:2014", GST_MPD_UTCTIMING_TYPE_NTP},
243   {"urn:mpeg:dash:utc:sntp:2014", GST_MPD_UTCTIMING_TYPE_SNTP},
244   {"urn:mpeg:dash:utc:http-head:2014", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
245   {"urn:mpeg:dash:utc:http-xsdate:2014", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
246   {"urn:mpeg:dash:utc:http-iso:2014", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
247   {"urn:mpeg:dash:utc:http-ntp:2014", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
248   {"urn:mpeg:dash:utc:direct:2014", GST_MPD_UTCTIMING_TYPE_DIRECT},
249   /*
250    * Early working drafts used the :2012 namespace and this namespace is
251    * used by some DASH packagers. To work-around these packagers, we also
252    * accept the early draft scheme names.
253    */
254   {"urn:mpeg:dash:utc:ntp:2012", GST_MPD_UTCTIMING_TYPE_NTP},
255   {"urn:mpeg:dash:utc:sntp:2012", GST_MPD_UTCTIMING_TYPE_SNTP},
256   {"urn:mpeg:dash:utc:http-head:2012", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
257   {"urn:mpeg:dash:utc:http-xsdate:2012", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
258   {"urn:mpeg:dash:utc:http-iso:2012", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
259   {"urn:mpeg:dash:utc:http-ntp:2012", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
260   {"urn:mpeg:dash:utc:direct:2012", GST_MPD_UTCTIMING_TYPE_DIRECT},
261   {NULL, 0}
262 };
263
264 /* functions to parse node namespaces, content and properties */
265 static gboolean
266 gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
267     const gchar * property_name, gchar ** property_value,
268     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     if (validate && !(*validate) ((const char *) prop_string)) {
276       GST_WARNING ("Validation failure: %s", prop_string);
277       xmlFree (prop_string);
278       return FALSE;
279     }
280     *property_value = (gchar *) prop_string;
281     exists = TRUE;
282     GST_LOG (" - %s: %s", property_name, prop_string);
283   }
284
285   return exists;
286 }
287
288 static gboolean
289 gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
290     const gchar * ns_name, const gchar * property_name, gchar ** property_value)
291 {
292   xmlChar *prop_string;
293   gboolean exists = FALSE;
294
295   prop_string =
296       xmlGetNsProp (a_node, (const xmlChar *) property_name,
297       (const xmlChar *) ns_name);
298   if (prop_string) {
299     *property_value = (gchar *) prop_string;
300     exists = TRUE;
301     GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
302   }
303
304   return exists;
305 }
306
307 static gboolean
308 gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
309     const gchar * property_name, gchar ** property_value)
310 {
311   return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
312       property_value, NULL);
313 }
314
315 static gboolean
316 gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
317     const gchar * property_name, gchar ** property_value)
318 {
319   gboolean ret;
320   ret =
321       gst_mpdparser_get_xml_prop_string (a_node, property_name, property_value);
322   if (ret)
323     *property_value = g_strstrip (*property_value);
324   return ret;
325 }
326
327 static gboolean
328 gst_mpdparser_validate_no_whitespace (const char *s)
329 {
330   return !strpbrk (s, "\r\n\t ");
331 }
332
333 static gboolean
334 gst_mpdparser_get_xml_prop_string_no_whitespace (xmlNode * a_node,
335     const gchar * property_name, gchar ** property_value)
336 {
337   return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
338       property_value, gst_mpdparser_validate_no_whitespace);
339 }
340
341 static gboolean
342 gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
343     const gchar * property_name, gchar *** property_value)
344 {
345   xmlChar *prop_string;
346   gchar **prop_string_vector = NULL;
347   guint i = 0;
348   gboolean exists = FALSE;
349
350   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
351   if (prop_string) {
352     prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
353     if (prop_string_vector) {
354       exists = TRUE;
355       *property_value = prop_string_vector;
356       GST_LOG (" - %s:", property_name);
357       while (prop_string_vector[i]) {
358         GST_LOG ("    %s", prop_string_vector[i]);
359         i++;
360       }
361     } else {
362       GST_WARNING ("Scan of string vector property failed!");
363     }
364     xmlFree (prop_string);
365   }
366
367   return exists;
368 }
369
370 static gboolean
371 gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
372     const gchar * property_name, gint default_val, gint * property_value)
373 {
374   xmlChar *prop_string;
375   gboolean exists = FALSE;
376
377   *property_value = default_val;
378   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
379   if (prop_string) {
380     if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
381       exists = TRUE;
382       GST_LOG (" - %s: %d", property_name, *property_value);
383     } else {
384       GST_WARNING
385           ("failed to parse signed integer property %s from xml string %s",
386           property_name, prop_string);
387     }
388     xmlFree (prop_string);
389   }
390
391   return exists;
392 }
393
394 static gboolean
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   *property_value = default_val;
402   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
403   if (prop_string) {
404     if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
405         strstr ((gchar *) prop_string, "-") == NULL) {
406       exists = TRUE;
407       GST_LOG (" - %s: %u", property_name, *property_value);
408     } else {
409       GST_WARNING
410           ("failed to parse unsigned integer property %s from xml string %s",
411           property_name, prop_string);
412       /* sscanf might have written to *property_value. Restore to default */
413       *property_value = default_val;
414     }
415     xmlFree (prop_string);
416   }
417
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   *property_value = default_val;
429   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
430   if (prop_string) {
431     if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
432             property_value) == 1 &&
433         strstr ((gchar *) prop_string, "-") == NULL) {
434       exists = TRUE;
435       GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
436     } else {
437       GST_WARNING
438           ("failed to parse unsigned integer property %s from xml string %s",
439           property_name, prop_string);
440       /* sscanf might have written to *property_value. Restore to default */
441       *property_value = default_val;
442     }
443     xmlFree (prop_string);
444   }
445
446   return exists;
447 }
448
449 static gboolean
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   xmlChar *prop_string;
454   gchar **str_vector;
455   guint *prop_uint_vector = NULL, i;
456   gboolean exists = FALSE;
457
458   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
459   if (prop_string) {
460     str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
461     if (str_vector) {
462       *value_size = g_strv_length (str_vector);
463       prop_uint_vector = g_malloc (*value_size * sizeof (guint));
464       if (prop_uint_vector) {
465         exists = TRUE;
466         GST_LOG (" - %s:", property_name);
467         for (i = 0; i < *value_size; i++) {
468           if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
469               && strstr (str_vector[i], "-") == NULL) {
470             GST_LOG ("    %u", prop_uint_vector[i]);
471           } else {
472             GST_WARNING
473                 ("failed to parse uint vector type property %s from xml string %s",
474                 property_name, str_vector[i]);
475             /* there is no special value to put in prop_uint_vector[i] to
476              * signal it is invalid, so we just clean everything and return
477              * FALSE
478              */
479             g_free (prop_uint_vector);
480             prop_uint_vector = NULL;
481             exists = FALSE;
482             break;
483           }
484         }
485         *property_value = prop_uint_vector;
486       } else {
487         GST_WARNING ("Array allocation failed!");
488       }
489     } else {
490       GST_WARNING ("Scan of uint vector property failed!");
491     }
492     xmlFree (prop_string);
493     g_strfreev (str_vector);
494   }
495
496   return exists;
497 }
498
499 static gboolean
500 gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
501     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       exists = TRUE;
510       GST_LOG (" - %s: %lf", property_name, *property_value);
511     } else {
512       GST_WARNING ("failed to parse double property %s from xml string %s",
513           property_name, prop_string);
514     }
515     xmlFree (prop_string);
516   }
517
518   return exists;
519 }
520
521 static gboolean
522 gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
523     const gchar * property_name, gboolean default_val,
524     gboolean * property_value)
525 {
526   xmlChar *prop_string;
527   gboolean exists = FALSE;
528
529   *property_value = default_val;
530   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
531   if (prop_string) {
532     if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
533       exists = TRUE;
534       *property_value = FALSE;
535       GST_LOG (" - %s: false", property_name);
536     } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
537       exists = TRUE;
538       *property_value = TRUE;
539       GST_LOG (" - %s: true", property_name);
540     } else {
541       GST_WARNING ("failed to parse boolean property %s from xml string %s",
542           property_name, prop_string);
543     }
544     xmlFree (prop_string);
545   }
546
547   return exists;
548 }
549
550 static gboolean
551 gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
552     const gchar * property_name, GstMPDFileType * property_value)
553 {
554   xmlChar *prop_string;
555   gboolean exists = FALSE;
556
557   *property_value = GST_MPD_FILE_TYPE_STATIC;   /* default */
558   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
559   if (prop_string) {
560     if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
561         || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
562       exists = TRUE;
563       *property_value = GST_MPD_FILE_TYPE_STATIC;
564       GST_LOG (" - %s: static", property_name);
565     } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
566         || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
567       exists = TRUE;
568       *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
569       GST_LOG (" - %s: dynamic", property_name);
570     } else {
571       GST_WARNING ("failed to parse MPD type property %s from xml string %s",
572           property_name, prop_string);
573     }
574     xmlFree (prop_string);
575   }
576
577   return exists;
578 }
579
580 static gboolean
581 gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
582     const gchar * property_name, GstSAPType * property_value)
583 {
584   xmlChar *prop_string;
585   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       exists = TRUE;
593       *property_value = (GstSAPType) prop_SAP_type;
594       GST_LOG (" - %s: %u", property_name, prop_SAP_type);
595     } else {
596       GST_WARNING
597           ("failed to parse unsigned integer property %s from xml string %s",
598           property_name, prop_string);
599     }
600     xmlFree (prop_string);
601   }
602
603   return exists;
604 }
605
606 static gboolean
607 gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property_name,
608     GstRange ** property_value)
609 {
610   xmlChar *prop_string;
611   guint64 first_byte_pos = 0, last_byte_pos = -1;
612   guint len, pos;
613   gchar *str;
614   gboolean exists = FALSE;
615
616   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
617   if (prop_string) {
618     len = xmlStrlen (prop_string);
619     str = (gchar *) prop_string;
620     GST_TRACE ("range: %s, len %d", str, len);
621
622     /* read "-" */
623     pos = strcspn (str, "-");
624     if (pos >= len) {
625       GST_TRACE ("pos %d >= len %d", pos, len);
626       goto error;
627     }
628     /* read first_byte_pos */
629     if (pos != 0) {
630       /* replace str[pos] with '\0' to allow sscanf to not be confused by
631        * the minus sign (eg " -1" (observe the space before -) would otherwise
632        * be interpreted as range -1 to 1)
633        */
634       str[pos] = 0;
635       if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
636           strstr (str, "-") != NULL) {
637         /* sscanf failed or it found a negative number */
638         /* restore the '-' sign */
639         str[pos] = '-';
640         goto error;
641       }
642       /* restore the '-' sign */
643       str[pos] = '-';
644     }
645     /* read last_byte_pos */
646     if (pos < (len - 1)) {
647       if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
648           strstr (str + pos + 1, "-") != NULL) {
649         goto error;
650       }
651     }
652     /* malloc return data structure */
653     *property_value = g_slice_new0 (GstRange);
654     exists = TRUE;
655     (*property_value)->first_byte_pos = first_byte_pos;
656     (*property_value)->last_byte_pos = last_byte_pos;
657     xmlFree (prop_string);
658     GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
659         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 static gboolean
672 gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
673     const gchar * property_name, GstRatio ** property_value)
674 {
675   xmlChar *prop_string;
676   guint num = 0, den = 1;
677   guint len, pos;
678   gchar *str;
679   gboolean exists = FALSE;
680
681   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
682   if (prop_string) {
683     len = xmlStrlen (prop_string);
684     str = (gchar *) prop_string;
685     GST_TRACE ("ratio: %s, len %d", str, len);
686
687     /* read ":" */
688     pos = strcspn (str, ":");
689     if (pos >= len) {
690       GST_TRACE ("pos %d >= len %d", pos, len);
691       goto error;
692     }
693     /* search for negative sign */
694     if (strstr (str, "-") != NULL) {
695       goto error;
696     }
697     /* read num */
698     if (pos != 0) {
699       if (sscanf (str, "%u", &num) != 1) {
700         goto error;
701       }
702     }
703     /* read den */
704     if (pos < (len - 1)) {
705       if (sscanf (str + pos + 1, "%u", &den) != 1) {
706         goto error;
707       }
708     }
709     /* malloc return data structure */
710     *property_value = g_slice_new0 (GstRatio);
711     exists = TRUE;
712     (*property_value)->num = num;
713     (*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 static gboolean
728 gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
729     const gchar * property_name, GstFrameRate ** property_value)
730 {
731   xmlChar *prop_string;
732   guint num = 0, den = 1;
733   guint len, pos;
734   gchar *str;
735   gboolean exists = FALSE;
736
737   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
738   if (prop_string) {
739     len = xmlStrlen (prop_string);
740     str = (gchar *) prop_string;
741     GST_TRACE ("framerate: %s, len %d", str, len);
742
743     /* search for negative sign */
744     if (strstr (str, "-") != NULL) {
745       goto error;
746     }
747
748     /* read "/" if available */
749     pos = strcspn (str, "/");
750     /* read num */
751     if (pos != 0) {
752       if (sscanf (str, "%u", &num) != 1) {
753         goto error;
754       }
755     }
756     /* read den (if available) */
757     if (pos < (len - 1)) {
758       if (sscanf (str + pos + 1, "%u", &den) != 1) {
759         goto error;
760       }
761     }
762     /* alloc return data structure */
763     *property_value = g_slice_new0 (GstFrameRate);
764     exists = TRUE;
765     (*property_value)->num = num;
766     (*property_value)->den = den;
767     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 static gboolean
784 gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
785     const gchar * property_name, GstConditionalUintType ** property_value)
786 {
787   xmlChar *prop_string;
788   gchar *str;
789   gboolean flag;
790   guint val;
791   gboolean exists = FALSE;
792
793   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
794   if (prop_string) {
795     str = (gchar *) prop_string;
796     GST_TRACE ("conditional uint: %s", str);
797
798     if (strcmp (str, "false") == 0) {
799       flag = FALSE;
800       val = 0;
801     } else if (strcmp (str, "true") == 0) {
802       flag = TRUE;
803       val = 0;
804     } else {
805       flag = TRUE;
806       if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
807         goto error;
808     }
809
810     /* alloc return data structure */
811     *property_value = g_slice_new0 (GstConditionalUintType);
812     exists = TRUE;
813     (*property_value)->flag = flag;
814     (*property_value)->value = val;
815     xmlFree (prop_string);
816     GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
817         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   DateTime Data Type
831
832   The dateTime data type is used to specify a date and a time.
833
834   The lexical form of xs:dateTime is YYYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
835
836     * YYYY indicates the year
837     * MM indicates the month
838     * DD indicates the day
839     * T indicates the start of the required time section
840     * hh indicates the hour
841     * mm indicates the minute
842     * ss indicates the second
843
844   The time zone may be specified as Z (UTC) or (+|-)hh:mm
845 */
846 static gboolean
847 gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
848     const gchar * property_name, GstDateTime ** property_value)
849 {
850   xmlChar *prop_string;
851   gchar *str;
852   gint ret, pos;
853   gint year, month, day, hour, minute;
854   gdouble second;
855   gboolean exists = FALSE;
856   gfloat tzoffset = 0.0;
857   gint gmt_offset_hour = -99, gmt_offset_min = -99;
858
859   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
860   if (prop_string) {
861     str = (gchar *) prop_string;
862     GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
863     /* parse year */
864     ret = sscanf (str, "%d", &year);
865     if (ret != 1 || year <= 0)
866       goto error;
867     pos = strcspn (str, "-");
868     str += (pos + 1);
869     GST_TRACE (" - year %d", year);
870     /* parse month */
871     ret = sscanf (str, "%d", &month);
872     if (ret != 1 || month <= 0)
873       goto error;
874     pos = strcspn (str, "-");
875     str += (pos + 1);
876     GST_TRACE (" - month %d", month);
877     /* parse day */
878     ret = sscanf (str, "%d", &day);
879     if (ret != 1 || day <= 0)
880       goto error;
881     pos = strcspn (str, "T");
882     str += (pos + 1);
883     GST_TRACE (" - day %d", day);
884     /* parse hour */
885     ret = sscanf (str, "%d", &hour);
886     if (ret != 1 || hour < 0)
887       goto error;
888     pos = strcspn (str, ":");
889     str += (pos + 1);
890     GST_TRACE (" - hour %d", hour);
891     /* parse minute */
892     ret = sscanf (str, "%d", &minute);
893     if (ret != 1 || minute < 0)
894       goto error;
895     pos = strcspn (str, ":");
896     str += (pos + 1);
897     GST_TRACE (" - minute %d", minute);
898     /* parse second */
899     ret = sscanf (str, "%lf", &second);
900     if (ret != 1 || second < 0)
901       goto error;
902     GST_TRACE (" - second %lf", second);
903
904     GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
905         year, month, day, hour, minute, second);
906
907     if (strrchr (str, '+') || strrchr (str, '-')) {
908       /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
909       gint gmt_offset = -1;
910       gchar *plus_pos = NULL;
911       gchar *neg_pos = NULL;
912       gchar *pos = NULL;
913
914       GST_LOG ("Checking for timezone information");
915
916       /* check if there is timezone info */
917       plus_pos = strrchr (str, '+');
918       neg_pos = strrchr (str, '-');
919       if (plus_pos)
920         pos = plus_pos + 1;
921       else if (neg_pos)
922         pos = neg_pos + 1;
923
924       if (pos && strlen (pos) >= 3) {
925         gint ret_tz;
926         if (pos[2] == ':')
927           ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
928         else
929           ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
930
931         GST_DEBUG ("Parsing timezone: %s", pos);
932
933         if (ret_tz == 2) {
934           if (neg_pos != NULL && neg_pos + 1 == pos) {
935             gmt_offset_hour *= -1;
936             gmt_offset_min *= -1;
937           }
938           gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
939
940           tzoffset = gmt_offset / 60.0;
941
942           GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
943         } else
944           GST_WARNING ("Failed to parse timezone information");
945       }
946     }
947
948     exists = TRUE;
949     *property_value =
950         gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
951     xmlFree (prop_string);
952   }
953
954   return exists;
955
956 error:
957   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
958       prop_string);
959   xmlFree (prop_string);
960   return FALSE;
961 }
962
963
964 /*
965   Duration Data Type
966
967   The duration data type is used to specify a time interval.
968
969   The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
970
971     * - indicates the negative sign (optional)
972     * P indicates the period (required)
973     * nY indicates the number of years
974     * nM indicates the number of months
975     * nD indicates the number of days
976     * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
977     * nH indicates the number of hours
978     * nM indicates the number of minutes
979     * nS indicates the number of seconds
980 */
981
982 /* this function computes decimals * 10 ^ (3 - pos) */
983 static guint
984 convert_to_millisecs (guint decimals, gint pos)
985 {
986   guint num = 1, den = 1;
987   gint i = 3 - pos;
988
989   while (i < 0) {
990     den *= 10;
991     i++;
992   }
993   while (i > 0) {
994     num *= 10;
995     i--;
996   }
997   /* if i == 0 we have exactly 3 decimals and nothing to do */
998   return decimals * num / den;
999 }
1000
1001 static gboolean
1002 accumulate (guint64 * v, guint64 mul, guint64 add)
1003 {
1004   guint64 tmp;
1005
1006   if (*v > G_MAXUINT64 / mul)
1007     return FALSE;
1008   tmp = *v * mul;
1009   if (tmp > G_MAXUINT64 - add)
1010     return FALSE;
1011   *v = tmp + add;
1012   return TRUE;
1013 }
1014
1015 static gboolean
1016 gst_mpdparser_parse_duration (const char *str, guint64 * value)
1017 {
1018   gint ret, len, pos, posT;
1019   gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
1020       -1, decimals = -1, read;
1021   gboolean have_ms = FALSE;
1022   guint64 tmp_value;
1023
1024   len = strlen (str);
1025   GST_TRACE ("duration: %s, len %d", str, len);
1026   if (strspn (str, "PT0123456789., \tHMDSY") < len) {
1027     GST_WARNING ("Invalid character found: '%s'", str);
1028     goto error;
1029   }
1030   /* skip leading/trailing whitespace */
1031   while (g_ascii_isspace (str[0])) {
1032     str++;
1033     len--;
1034   }
1035   while (len > 0 && g_ascii_isspace (str[len - 1]))
1036     --len;
1037
1038   /* read "P" for period */
1039   if (str[0] != 'P') {
1040     GST_WARNING ("P not found at the beginning of the string!");
1041     goto error;
1042   }
1043   str++;
1044   len--;
1045
1046   /* read "T" for time (if present) */
1047   posT = strcspn (str, "T");
1048   len -= posT;
1049   if (posT > 0) {
1050     /* there is some room between P and T, so there must be a period section */
1051     /* read years, months, days */
1052     do {
1053       GST_TRACE ("parsing substring %s", str);
1054       pos = strcspn (str, "YMD");
1055       ret = sscanf (str, "%u", &read);
1056       if (ret != 1) {
1057         GST_WARNING ("can not read integer value from string %s!", str);
1058         goto error;
1059       }
1060       switch (str[pos]) {
1061         case 'Y':
1062           if (years != -1 || months != -1 || days != -1) {
1063             GST_WARNING ("year, month or day was already set");
1064             goto error;
1065           }
1066           years = read;
1067           break;
1068         case 'M':
1069           if (months != -1 || days != -1) {
1070             GST_WARNING ("month or day was already set");
1071             goto error;
1072           }
1073           months = read;
1074           if (months >= 12) {
1075             GST_WARNING ("Month out of range");
1076             goto error;
1077           }
1078           break;
1079         case 'D':
1080           if (days != -1) {
1081             GST_WARNING ("day was already set");
1082             goto error;
1083           }
1084           days = read;
1085           if (days >= 31) {
1086             GST_WARNING ("Day out of range");
1087             goto error;
1088           }
1089           break;
1090         default:
1091           GST_WARNING ("unexpected char %c!", str[pos]);
1092           goto error;
1093           break;
1094       }
1095       GST_TRACE ("read number %u type %c", read, str[pos]);
1096       str += (pos + 1);
1097       posT -= (pos + 1);
1098     } while (posT > 0);
1099   }
1100
1101   if (years == -1)
1102     years = 0;
1103   if (months == -1)
1104     months = 0;
1105   if (days == -1)
1106     days = 0;
1107
1108   GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
1109
1110   /* read "T" for time (if present) */
1111   /* here T is at pos == 0 */
1112   str++;
1113   len--;
1114   pos = 0;
1115   if (pos < len) {
1116     /* T found, there is a time section */
1117     /* read hours, minutes, seconds, hundredths of second */
1118     do {
1119       GST_TRACE ("parsing substring %s", str);
1120       pos = strcspn (str, "HMS,.");
1121       ret = sscanf (str, "%u", &read);
1122       if (ret != 1) {
1123         GST_WARNING ("can not read integer value from string %s!", str);
1124         goto error;
1125       }
1126       switch (str[pos]) {
1127         case 'H':
1128           if (hours != -1 || minutes != -1 || seconds != -1) {
1129             GST_WARNING ("hour, minute or second was already set");
1130             goto error;
1131           }
1132           hours = read;
1133           if (hours >= 24) {
1134             GST_WARNING ("Hour out of range");
1135             goto error;
1136           }
1137           break;
1138         case 'M':
1139           if (minutes != -1 || seconds != -1) {
1140             GST_WARNING ("minute or second was already set");
1141             goto error;
1142           }
1143           minutes = read;
1144           if (minutes >= 60) {
1145             GST_WARNING ("Minute out of range");
1146             goto error;
1147           }
1148           break;
1149         case 'S':
1150           if (have_ms) {
1151             /* we have read the decimal part of the seconds */
1152             decimals = convert_to_millisecs (read, pos);
1153             GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
1154                 decimals);
1155           } else {
1156             if (seconds != -1) {
1157               GST_WARNING ("second was already set");
1158               goto error;
1159             }
1160             /* no decimals */
1161             seconds = read;
1162           }
1163           break;
1164         case '.':
1165         case ',':
1166           /* we have read the integer part of a decimal number in seconds */
1167           if (seconds != -1) {
1168             GST_WARNING ("second was already set");
1169             goto error;
1170           }
1171           seconds = read;
1172           have_ms = TRUE;
1173           break;
1174         default:
1175           GST_WARNING ("unexpected char %c!", str[pos]);
1176           goto error;
1177           break;
1178       }
1179       GST_TRACE ("read number %u type %c", read, str[pos]);
1180       str += pos + 1;
1181       len -= (pos + 1);
1182     } while (len > 0);
1183   }
1184
1185   if (hours == -1)
1186     hours = 0;
1187   if (minutes == -1)
1188     minutes = 0;
1189   if (seconds == -1)
1190     seconds = 0;
1191   if (decimals == -1)
1192     decimals = 0;
1193   GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
1194
1195   tmp_value = 0;
1196   if (!accumulate (&tmp_value, 1, years)
1197       || !accumulate (&tmp_value, 365, months * 30)
1198       || !accumulate (&tmp_value, 1, days)
1199       || !accumulate (&tmp_value, 24, hours)
1200       || !accumulate (&tmp_value, 60, minutes)
1201       || !accumulate (&tmp_value, 60, seconds)
1202       || !accumulate (&tmp_value, 1000, decimals))
1203     goto error;
1204
1205   /* ensure it can be converted from milliseconds to nanoseconds */
1206   if (tmp_value > G_MAXUINT64 / 1000000)
1207     goto error;
1208
1209   *value = tmp_value;
1210   return TRUE;
1211
1212 error:
1213   return FALSE;
1214 }
1215
1216 static gboolean
1217 gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
1218     const gchar * property_name, guint64 default_value,
1219     guint64 * property_value)
1220 {
1221   xmlChar *prop_string;
1222   gchar *str;
1223   gboolean exists = FALSE;
1224
1225   *property_value = default_value;
1226   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
1227   if (prop_string) {
1228     str = (gchar *) prop_string;
1229     if (!gst_mpdparser_parse_duration (str, property_value))
1230       goto error;
1231     GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
1232     xmlFree (prop_string);
1233     exists = TRUE;
1234   }
1235   return exists;
1236
1237 error:
1238   xmlFree (prop_string);
1239   return FALSE;
1240 }
1241
1242 static gboolean
1243 gst_mpdparser_get_xml_node_content (xmlNode * a_node, gchar ** content)
1244 {
1245   xmlChar *node_content = NULL;
1246   gboolean exists = FALSE;
1247
1248   node_content = xmlNodeGetContent (a_node);
1249   if (node_content) {
1250     exists = TRUE;
1251     *content = (gchar *) node_content;
1252     GST_LOG (" - %s: %s", a_node->name, *content);
1253   }
1254
1255   return exists;
1256 }
1257
1258 static gboolean
1259 gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
1260 {
1261   gboolean exists = FALSE;
1262   const char *txt_encoding;
1263   xmlOutputBufferPtr out_buf;
1264
1265   txt_encoding = (const char *) a_node->doc->encoding;
1266   out_buf = xmlAllocOutputBuffer (NULL);
1267   g_assert (out_buf != NULL);
1268   xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
1269   xmlOutputBufferFlush (out_buf);
1270 #ifdef LIBXML2_NEW_BUFFER
1271   if (xmlOutputBufferGetSize (out_buf) > 0) {
1272     *content =
1273         (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
1274         xmlOutputBufferGetSize (out_buf));
1275     exists = TRUE;
1276   }
1277 #else
1278   if (out_buf->conv && out_buf->conv->use > 0) {
1279     *content =
1280         (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
1281     exists = TRUE;
1282   } else if (out_buf->buffer && out_buf->buffer->use > 0) {
1283     *content =
1284         (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
1285     exists = TRUE;
1286   }
1287 #endif // LIBXML2_NEW_BUFFER
1288   (void) xmlOutputBufferClose (out_buf);
1289
1290   if (exists) {
1291     GST_LOG (" - %s: %s", a_node->name, *content);
1292   }
1293   return exists;
1294 }
1295
1296 static gchar *
1297 gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
1298 {
1299   xmlNs *curr_ns;
1300   gchar *namespace = NULL;
1301
1302   if (prefix == NULL) {
1303     /* return the default namespace */
1304     if (a_node->ns) {
1305       namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
1306       if (namespace) {
1307         GST_LOG (" - default namespace: %s", namespace);
1308       }
1309     }
1310   } else {
1311     /* look for the specified prefix in the namespace list */
1312     for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
1313       if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
1314         namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
1315         if (namespace) {
1316           GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
1317         }
1318       }
1319     }
1320   }
1321
1322   return namespace;
1323 }
1324
1325 static void
1326 gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
1327 {
1328   GstBaseURL *new_base_url;
1329
1330   new_base_url = g_slice_new0 (GstBaseURL);
1331   *list = g_list_append (*list, new_base_url);
1332
1333   GST_LOG ("content of BaseURL node:");
1334   gst_mpdparser_get_xml_node_content (a_node, &new_base_url->baseURL);
1335
1336   GST_LOG ("attributes of BaseURL node:");
1337   gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation",
1338       &new_base_url->serviceLocation);
1339   gst_mpdparser_get_xml_prop_string (a_node, "byteRange",
1340       &new_base_url->byteRange);
1341 }
1342
1343 static void
1344 gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
1345 {
1346   GstDescriptorType *new_descriptor;
1347
1348   new_descriptor = g_slice_new0 (GstDescriptorType);
1349   *list = g_list_append (*list, new_descriptor);
1350
1351   GST_LOG ("attributes of %s node:", a_node->name);
1352   gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1353       &new_descriptor->schemeIdUri);
1354   if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
1355           &new_descriptor->value)) {
1356     /* if no value attribute, use XML string representation of the node */
1357     gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
1358   }
1359 }
1360
1361 static void
1362 gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
1363 {
1364   xmlNode *cur_node;
1365   GstContentComponentNode *new_content_component;
1366
1367   new_content_component = g_slice_new0 (GstContentComponentNode);
1368   *list = g_list_append (*list, new_content_component);
1369
1370   GST_LOG ("attributes of ContentComponent node:");
1371   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1372       &new_content_component->id);
1373   gst_mpdparser_get_xml_prop_string (a_node, "lang",
1374       &new_content_component->lang);
1375   gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1376       &new_content_component->contentType);
1377   gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_content_component->par);
1378
1379   /* explore children nodes */
1380   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1381     if (cur_node->type == XML_ELEMENT_NODE) {
1382       if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
1383         gst_mpdparser_parse_descriptor_type_node
1384             (&new_content_component->Accessibility, cur_node);
1385       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
1386         gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
1387             cur_node);
1388       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
1389         gst_mpdparser_parse_descriptor_type_node
1390             (&new_content_component->Rating, cur_node);
1391       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
1392         gst_mpdparser_parse_descriptor_type_node
1393             (&new_content_component->Viewpoint, cur_node);
1394       }
1395     }
1396   }
1397 }
1398
1399 static void
1400 gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
1401 {
1402   gchar *location = NULL;
1403
1404   GST_LOG ("content of Location node:");
1405   if (gst_mpdparser_get_xml_node_content (a_node, &location))
1406     *list = g_list_append (*list, location);
1407 }
1408
1409 static void
1410 gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
1411 {
1412   GstSubRepresentationNode *new_subrep;
1413
1414   new_subrep = g_slice_new0 (GstSubRepresentationNode);
1415   *list = g_list_append (*list, new_subrep);
1416
1417   GST_LOG ("attributes of SubRepresentation node:");
1418   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0,
1419       &new_subrep->level);
1420   gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
1421       &new_subrep->dependencyLevel, &new_subrep->size);
1422   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1423       &new_subrep->bandwidth);
1424   gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1425       "contentComponent", &new_subrep->contentComponent);
1426
1427   /* RepresentationBase extension */
1428   gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
1429       a_node);
1430 }
1431
1432 static GstSegmentURLNode *
1433 gst_mpdparser_clone_segment_url (GstSegmentURLNode * seg_url)
1434 {
1435   GstSegmentURLNode *clone = NULL;
1436
1437   if (seg_url) {
1438     clone = g_slice_new0 (GstSegmentURLNode);
1439     clone->media = xmlMemStrdup (seg_url->media);
1440     clone->mediaRange = gst_mpdparser_clone_range (seg_url->mediaRange);
1441     clone->index = xmlMemStrdup (seg_url->index);
1442     clone->indexRange = gst_mpdparser_clone_range (seg_url->indexRange);
1443   }
1444
1445   return clone;
1446 }
1447
1448 static void
1449 gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
1450 {
1451   GstSegmentURLNode *new_segment_url;
1452
1453   new_segment_url = g_slice_new0 (GstSegmentURLNode);
1454   *list = g_list_append (*list, new_segment_url);
1455
1456   GST_LOG ("attributes of SegmentURL node:");
1457   gst_mpdparser_get_xml_prop_string (a_node, "media", &new_segment_url->media);
1458   gst_mpdparser_get_xml_prop_range (a_node, "mediaRange",
1459       &new_segment_url->mediaRange);
1460   gst_mpdparser_get_xml_prop_string (a_node, "index", &new_segment_url->index);
1461   gst_mpdparser_get_xml_prop_range (a_node, "indexRange",
1462       &new_segment_url->indexRange);
1463 }
1464
1465 static void
1466 gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
1467 {
1468   GstURLType *new_url_type;
1469
1470   gst_mpdparser_free_url_type_node (*pointer);
1471   *pointer = new_url_type = g_slice_new0 (GstURLType);
1472
1473   GST_LOG ("attributes of URLType node:");
1474   gst_mpdparser_get_xml_prop_string (a_node, "sourceURL",
1475       &new_url_type->sourceURL);
1476   gst_mpdparser_get_xml_prop_range (a_node, "range", &new_url_type->range);
1477 }
1478
1479 static void
1480 gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
1481     xmlNode * a_node, GstSegmentBaseType * parent)
1482 {
1483   xmlNode *cur_node;
1484   GstSegmentBaseType *seg_base_type;
1485   guint intval;
1486   guint64 int64val;
1487   gboolean boolval;
1488   GstRange *rangeval;
1489
1490   gst_mpdparser_free_seg_base_type_ext (*pointer);
1491   *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
1492
1493   /* Initialize values that have defaults */
1494   seg_base_type->indexRangeExact = FALSE;
1495   seg_base_type->timescale = 1;
1496
1497   /* Inherit attribute values from parent */
1498   if (parent) {
1499     seg_base_type->timescale = parent->timescale;
1500     seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
1501     seg_base_type->indexRange = gst_mpdparser_clone_range (parent->indexRange);
1502     seg_base_type->indexRangeExact = parent->indexRangeExact;
1503     seg_base_type->Initialization =
1504         gst_mpdparser_clone_URL (parent->Initialization);
1505     seg_base_type->RepresentationIndex =
1506         gst_mpdparser_clone_URL (parent->RepresentationIndex);
1507   }
1508
1509   /* We must retrieve each value first to see if it exists.  If it does not
1510    * exist, we do not want to overwrite an inherited value */
1511   GST_LOG ("attributes of SegmentBaseType extension:");
1512   if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 1,
1513           &intval)) {
1514     seg_base_type->timescale = intval;
1515   }
1516   if (gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node,
1517           "presentationTimeOffset", 0, &int64val)) {
1518     seg_base_type->presentationTimeOffset = int64val;
1519   }
1520   if (gst_mpdparser_get_xml_prop_range (a_node, "indexRange", &rangeval)) {
1521     if (seg_base_type->indexRange) {
1522       g_slice_free (GstRange, seg_base_type->indexRange);
1523     }
1524     seg_base_type->indexRange = rangeval;
1525   }
1526   if (gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact",
1527           FALSE, &boolval)) {
1528     seg_base_type->indexRangeExact = boolval;
1529   }
1530
1531   /* explore children nodes */
1532   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1533     if (cur_node->type == XML_ELEMENT_NODE) {
1534       if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
1535           xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
1536         /* parse will free the previous pointer to create a new one */
1537         gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
1538             cur_node);
1539       } else if (xmlStrcmp (cur_node->name,
1540               (xmlChar *) "RepresentationIndex") == 0) {
1541         /* parse will free the previous pointer to create a new one */
1542         gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
1543             cur_node);
1544       }
1545     }
1546   }
1547 }
1548
1549 static GstSNode *
1550 gst_mpdparser_clone_s_node (GstSNode * pointer)
1551 {
1552   GstSNode *clone = NULL;
1553
1554   if (pointer) {
1555     clone = g_slice_new0 (GstSNode);
1556     clone->t = pointer->t;
1557     clone->d = pointer->d;
1558     clone->r = pointer->r;
1559   }
1560
1561   return clone;
1562 }
1563
1564 static void
1565 gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
1566 {
1567   GstSNode *new_s_node;
1568
1569   new_s_node = g_slice_new0 (GstSNode);
1570   g_queue_push_tail (queue, new_s_node);
1571
1572   GST_LOG ("attributes of S node:");
1573   gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "t", 0,
1574       &new_s_node->t);
1575   gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0,
1576       &new_s_node->d);
1577   gst_mpdparser_get_xml_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
1578 }
1579
1580 static GstSegmentTimelineNode *
1581 gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer)
1582 {
1583   GstSegmentTimelineNode *clone = NULL;
1584
1585   if (pointer) {
1586     clone = gst_mpdparser_segment_timeline_node_new ();
1587     if (clone) {
1588       GList *list;
1589       for (list = g_queue_peek_head_link (&pointer->S); list;
1590           list = g_list_next (list)) {
1591         GstSNode *s_node;
1592         s_node = (GstSNode *) list->data;
1593         if (s_node) {
1594           g_queue_push_tail (&clone->S, gst_mpdparser_clone_s_node (s_node));
1595         }
1596       }
1597     } else {
1598       GST_WARNING ("Allocation of SegmentTimeline node failed!");
1599     }
1600   }
1601
1602   return clone;
1603 }
1604
1605 static void
1606 gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
1607     xmlNode * a_node)
1608 {
1609   xmlNode *cur_node;
1610   GstSegmentTimelineNode *new_seg_timeline;
1611
1612   gst_mpdparser_free_segment_timeline_node (*pointer);
1613   *pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ();
1614   if (new_seg_timeline == NULL) {
1615     GST_WARNING ("Allocation of SegmentTimeline node failed!");
1616     return;
1617   }
1618
1619   /* explore children nodes */
1620   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1621     if (cur_node->type == XML_ELEMENT_NODE) {
1622       if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
1623         gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
1624       }
1625     }
1626   }
1627 }
1628
1629 static gboolean
1630 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
1631     xmlNode * a_node, GstMultSegmentBaseType * parent)
1632 {
1633   xmlNode *cur_node;
1634   GstMultSegmentBaseType *mult_seg_base_type;
1635   guint intval;
1636   gboolean has_timeline = FALSE, has_duration = FALSE;
1637
1638   gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
1639   mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
1640
1641   mult_seg_base_type->duration = 0;
1642   mult_seg_base_type->startNumber = 1;
1643
1644   /* Inherit attribute values from parent */
1645   if (parent) {
1646     mult_seg_base_type->duration = parent->duration;
1647     mult_seg_base_type->startNumber = parent->startNumber;
1648     mult_seg_base_type->SegmentTimeline =
1649         gst_mpdparser_clone_segment_timeline (parent->SegmentTimeline);
1650     mult_seg_base_type->BitstreamSwitching =
1651         gst_mpdparser_clone_URL (parent->BitstreamSwitching);
1652   }
1653
1654   GST_LOG ("attributes of MultipleSegmentBaseType extension:");
1655   if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0,
1656           &intval)) {
1657     mult_seg_base_type->duration = intval;
1658   }
1659
1660   /* duration might be specified from parent */
1661   if (mult_seg_base_type->duration)
1662     has_duration = TRUE;
1663
1664   if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1,
1665           &intval)) {
1666     mult_seg_base_type->startNumber = intval;
1667   }
1668
1669   GST_LOG ("extension of MultipleSegmentBaseType extension:");
1670   gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
1671       a_node, (parent ? parent->SegBaseType : NULL));
1672
1673   /* explore children nodes */
1674   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1675     if (cur_node->type == XML_ELEMENT_NODE) {
1676       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
1677         /* parse frees the segmenttimeline if any */
1678         gst_mpdparser_parse_segment_timeline_node
1679             (&mult_seg_base_type->SegmentTimeline, cur_node);
1680       } else if (xmlStrcmp (cur_node->name,
1681               (xmlChar *) "BitstreamSwitching") == 0) {
1682         /* parse frees the old url before setting the new one */
1683         gst_mpdparser_parse_url_type_node
1684             (&mult_seg_base_type->BitstreamSwitching, cur_node);
1685       }
1686     }
1687   }
1688
1689   has_timeline = mult_seg_base_type->SegmentTimeline != NULL;
1690
1691   /* Checking duration and timeline only at Representation's child level */
1692   if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
1693       && !has_duration && !has_timeline) {
1694     GST_ERROR ("segment has neither duration nor timeline");
1695     goto error;
1696   }
1697
1698   *pointer = mult_seg_base_type;
1699   return TRUE;
1700
1701 error:
1702   gst_mpdparser_free_mult_seg_base_type_ext (mult_seg_base_type);
1703   return FALSE;
1704 }
1705
1706 static gboolean
1707 gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
1708     xmlNode * a_node, GstSegmentListNode * parent)
1709 {
1710   xmlNode *cur_node;
1711   GstSegmentListNode *new_segment_list;
1712   gchar *actuate;
1713   gboolean segment_urls_inherited_from_parent = FALSE;
1714
1715   gst_mpdparser_free_segment_list_node (*pointer);
1716   new_segment_list = g_slice_new0 (GstSegmentListNode);
1717
1718   /* Inherit attribute values from parent */
1719   if (parent) {
1720     GList *list;
1721     GstSegmentURLNode *seg_url;
1722     for (list = g_list_first (parent->SegmentURL); list;
1723         list = g_list_next (list)) {
1724       seg_url = (GstSegmentURLNode *) list->data;
1725       new_segment_list->SegmentURL =
1726           g_list_append (new_segment_list->SegmentURL,
1727           gst_mpdparser_clone_segment_url (seg_url));
1728       segment_urls_inherited_from_parent = TRUE;
1729     }
1730   }
1731
1732   new_segment_list->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1733   if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1734           "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
1735       && gst_mpdparser_get_xml_ns_prop_string (a_node,
1736           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1737     if (strcmp (actuate, "onLoad") == 0)
1738       new_segment_list->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1739     xmlFree (actuate);
1740   }
1741
1742   GST_LOG ("extension of SegmentList node:");
1743   if (!gst_mpdparser_parse_mult_seg_base_type_ext
1744       (&new_segment_list->MultSegBaseType, a_node,
1745           (parent ? parent->MultSegBaseType : NULL)))
1746     goto error;
1747
1748   /* explore children nodes */
1749   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1750     if (cur_node->type == XML_ELEMENT_NODE) {
1751       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
1752         if (segment_urls_inherited_from_parent) {
1753           /*
1754            * SegmentBase, SegmentTemplate and SegmentList shall inherit
1755            * attributes and elements from the same element on a higher level.
1756            * If the same attribute or element is present on both levels,
1757            * the one on the lower level shall take precedence over the one
1758            * on the higher level.
1759            */
1760
1761           /* Clear the list of inherited segment URLs */
1762           g_list_free_full (new_segment_list->SegmentURL,
1763               (GDestroyNotify) gst_mpdparser_free_segment_url_node);
1764           new_segment_list->SegmentURL = NULL;
1765
1766           /* mark the fact that we cleared the list, so that it is not tried again */
1767           segment_urls_inherited_from_parent = FALSE;
1768         }
1769         gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
1770             cur_node);
1771       }
1772     }
1773   }
1774
1775   *pointer = new_segment_list;
1776   return TRUE;
1777
1778 error:
1779   gst_mpdparser_free_segment_list_node (new_segment_list);
1780   return FALSE;
1781 }
1782
1783 static void
1784 gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
1785 {
1786   gchar *value = NULL;
1787   if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
1788     if (!g_strcmp0 (value, "MSPR 2.0")) {
1789       xmlNode *cur_node;
1790       for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1791         if (cur_node->type == XML_ELEMENT_NODE) {
1792           if (xmlStrcmp (cur_node->name, (xmlChar *) "pro") == 0) {
1793             GstDescriptorType *new_descriptor;
1794             new_descriptor = g_slice_new0 (GstDescriptorType);
1795             *list = g_list_append (*list, new_descriptor);
1796
1797             gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1798                 &new_descriptor->schemeIdUri);
1799
1800             gst_mpdparser_get_xml_node_content (cur_node,
1801                 &new_descriptor->value);
1802             goto beach;
1803           }
1804         }
1805       }
1806     } else {
1807       gst_mpdparser_parse_descriptor_type_node (list, a_node);
1808     }
1809   } else {
1810     gst_mpdparser_parse_descriptor_type_node (list, a_node);
1811   }
1812 beach:
1813   if (value)
1814     g_free (value);
1815 }
1816
1817 static void
1818 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
1819     pointer, xmlNode * a_node)
1820 {
1821   xmlNode *cur_node;
1822   GstRepresentationBaseType *representation_base;
1823
1824   gst_mpdparser_free_representation_base_type (*pointer);
1825   *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
1826
1827   GST_LOG ("attributes of RepresentationBaseType extension:");
1828   gst_mpdparser_get_xml_prop_string (a_node, "profiles",
1829       &representation_base->profiles);
1830   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0,
1831       &representation_base->width);
1832   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0,
1833       &representation_base->height);
1834   gst_mpdparser_get_xml_prop_ratio (a_node, "sar", &representation_base->sar);
1835   gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate",
1836       &representation_base->frameRate);
1837   gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate",
1838       &representation_base->minFrameRate);
1839   gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate",
1840       &representation_base->maxFrameRate);
1841   gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate",
1842       &representation_base->audioSamplingRate);
1843   gst_mpdparser_get_xml_prop_string (a_node, "mimeType",
1844       &representation_base->mimeType);
1845   gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles",
1846       &representation_base->segmentProfiles);
1847   gst_mpdparser_get_xml_prop_string (a_node, "codecs",
1848       &representation_base->codecs);
1849   gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod",
1850       &representation_base->maximumSAPPeriod);
1851   gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP",
1852       &representation_base->startWithSAP);
1853   gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate",
1854       &representation_base->maxPlayoutRate);
1855   gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency",
1856       FALSE, &representation_base->codingDependency);
1857   gst_mpdparser_get_xml_prop_string (a_node, "scanType",
1858       &representation_base->scanType);
1859
1860   /* explore children nodes */
1861   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1862     if (cur_node->type == XML_ELEMENT_NODE) {
1863       if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
1864         gst_mpdparser_parse_descriptor_type_node
1865             (&representation_base->FramePacking, cur_node);
1866       } else if (xmlStrcmp (cur_node->name,
1867               (xmlChar *) "AudioChannelConfiguration") == 0) {
1868         gst_mpdparser_parse_descriptor_type_node
1869             (&representation_base->AudioChannelConfiguration, cur_node);
1870       } else if (xmlStrcmp (cur_node->name,
1871               (xmlChar *) "ContentProtection") == 0) {
1872         gst_mpdparser_parse_content_protection_node
1873             (&representation_base->ContentProtection, cur_node);
1874       }
1875     }
1876   }
1877 }
1878
1879 static gboolean
1880 gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
1881     GstAdaptationSetNode * parent, GstPeriodNode * period_node)
1882 {
1883   xmlNode *cur_node;
1884   GstRepresentationNode *new_representation;
1885
1886   new_representation = g_slice_new0 (GstRepresentationNode);
1887
1888   GST_LOG ("attributes of Representation node:");
1889   if (!gst_mpdparser_get_xml_prop_string_no_whitespace (a_node, "id",
1890           &new_representation->id)) {
1891     GST_ERROR ("Cannot parse Representation id, invalid manifest");
1892     goto error;
1893   }
1894   if (!gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1895           &new_representation->bandwidth)) {
1896     GST_ERROR ("Cannot parse Representation bandwidth, invalid manifest");
1897     goto error;
1898   }
1899   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0,
1900       &new_representation->qualityRanking);
1901   gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId",
1902       &new_representation->dependencyId);
1903   gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1904       "mediaStreamStructureId", &new_representation->mediaStreamStructureId);
1905
1906   /* RepresentationBase extension */
1907   gst_mpdparser_parse_representation_base_type
1908       (&new_representation->RepresentationBase, a_node);
1909
1910   /* explore children nodes */
1911   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1912     if (cur_node->type == XML_ELEMENT_NODE) {
1913       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
1914         gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
1915             cur_node, parent->SegmentBase ?
1916             parent->SegmentBase : period_node->SegmentBase);
1917       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
1918         if (!gst_mpdparser_parse_segment_template_node
1919             (&new_representation->SegmentTemplate, cur_node,
1920                 parent->SegmentTemplate ?
1921                 parent->SegmentTemplate : period_node->SegmentTemplate))
1922           goto error;
1923       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
1924         if (!gst_mpdparser_parse_segment_list_node
1925             (&new_representation->SegmentList, cur_node,
1926                 parent->SegmentList ? parent->
1927                 SegmentList : period_node->SegmentList))
1928           goto error;
1929       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
1930         gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
1931             cur_node);
1932       } else if (xmlStrcmp (cur_node->name,
1933               (xmlChar *) "SubRepresentation") == 0) {
1934         gst_mpdparser_parse_subrepresentation_node
1935             (&new_representation->SubRepresentations, cur_node);
1936       }
1937     }
1938   }
1939
1940   /* some sanity checking */
1941
1942   *list = g_list_append (*list, new_representation);
1943   return TRUE;
1944
1945 error:
1946   gst_mpdparser_free_representation_node (new_representation);
1947   return FALSE;
1948 }
1949
1950 static gboolean
1951 gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
1952     GstPeriodNode * parent)
1953 {
1954   xmlNode *cur_node;
1955   GstAdaptationSetNode *new_adap_set;
1956   gchar *actuate;
1957
1958   new_adap_set = g_slice_new0 (GstAdaptationSetNode);
1959
1960   GST_LOG ("attributes of AdaptationSet node:");
1961
1962   new_adap_set->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1963   if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1964           "http://www.w3.org/1999/xlink", "href", &new_adap_set->xlink_href)
1965       && gst_mpdparser_get_xml_ns_prop_string (a_node,
1966           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1967     if (strcmp (actuate, "onLoad") == 0)
1968       new_adap_set->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1969     xmlFree (actuate);
1970   }
1971
1972   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1973       &new_adap_set->id);
1974   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0,
1975       &new_adap_set->group);
1976   gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_adap_set->lang);
1977   gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1978       &new_adap_set->contentType);
1979   gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_adap_set->par);
1980   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0,
1981       &new_adap_set->minBandwidth);
1982   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0,
1983       &new_adap_set->maxBandwidth);
1984   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0,
1985       &new_adap_set->minWidth);
1986   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0,
1987       &new_adap_set->maxWidth);
1988   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0,
1989       &new_adap_set->minHeight);
1990   gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0,
1991       &new_adap_set->maxHeight);
1992   gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment",
1993       &new_adap_set->segmentAlignment);
1994   gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching",
1995       parent->bitstreamSwitching, &new_adap_set->bitstreamSwitching);
1996   if (parent->bitstreamSwitching && !new_adap_set->bitstreamSwitching) {
1997     /* according to the standard, if the Period's bitstreamSwitching attribute
1998      * is true, the AdaptationSet should not have the bitstreamSwitching
1999      * attribute set to false.
2000      * We should return a parsing error, but we are generous and ignore the
2001      * standard violation.
2002      */
2003     new_adap_set->bitstreamSwitching = parent->bitstreamSwitching;
2004   }
2005   gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment",
2006       &new_adap_set->subsegmentAlignment);
2007   gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP",
2008       &new_adap_set->subsegmentStartsWithSAP);
2009
2010   /* RepresentationBase extension */
2011   gst_mpdparser_parse_representation_base_type
2012       (&new_adap_set->RepresentationBase, a_node);
2013
2014   /* explore children nodes */
2015   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2016     if (cur_node->type == XML_ELEMENT_NODE) {
2017       if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
2018         gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Accessibility,
2019             cur_node);
2020       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
2021         gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Role,
2022             cur_node);
2023       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
2024         gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Rating,
2025             cur_node);
2026       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
2027         gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Viewpoint,
2028             cur_node);
2029       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2030         gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
2031       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2032         gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
2033             cur_node, parent->SegmentBase);
2034       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2035         if (!gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
2036                 cur_node, parent->SegmentList))
2037           goto error;
2038       } else if (xmlStrcmp (cur_node->name,
2039               (xmlChar *) "ContentComponent") == 0) {
2040         gst_mpdparser_parse_content_component_node
2041             (&new_adap_set->ContentComponents, cur_node);
2042       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2043         if (!gst_mpdparser_parse_segment_template_node
2044             (&new_adap_set->SegmentTemplate, cur_node, parent->SegmentTemplate))
2045           goto error;
2046       }
2047     }
2048   }
2049
2050   /* We must parse Representation after everything else in the AdaptationSet
2051    * has been parsed because certain Representation child elements can inherit
2052    * attributes specified by the same element in the AdaptationSet
2053    */
2054   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2055     if (cur_node->type == XML_ELEMENT_NODE) {
2056       if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
2057         if (!gst_mpdparser_parse_representation_node
2058             (&new_adap_set->Representations, cur_node, new_adap_set, parent))
2059           goto error;
2060       }
2061     }
2062   }
2063
2064   *list = g_list_append (*list, new_adap_set);
2065   return TRUE;
2066
2067 error:
2068   gst_mpdparser_free_adaptation_set_node (new_adap_set);
2069   return FALSE;
2070 }
2071
2072 static void
2073 gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
2074 {
2075   GstSubsetNode *new_subset;
2076
2077   new_subset = g_slice_new0 (GstSubsetNode);
2078   *list = g_list_append (*list, new_subset);
2079
2080   GST_LOG ("attributes of Subset node:");
2081   gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
2082       &new_subset->contains, &new_subset->size);
2083 }
2084
2085 static gboolean
2086 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
2087     xmlNode * a_node, GstSegmentTemplateNode * parent)
2088 {
2089   GstSegmentTemplateNode *new_segment_template;
2090   gchar *strval;
2091
2092   gst_mpdparser_free_segment_template_node (*pointer);
2093   new_segment_template = g_slice_new0 (GstSegmentTemplateNode);
2094
2095   GST_LOG ("extension of SegmentTemplate node:");
2096   if (!gst_mpdparser_parse_mult_seg_base_type_ext
2097       (&new_segment_template->MultSegBaseType, a_node,
2098           (parent ? parent->MultSegBaseType : NULL)))
2099     goto error;
2100
2101   /* Inherit attribute values from parent when the value isn't found */
2102   GST_LOG ("attributes of SegmentTemplate node:");
2103   if (gst_mpdparser_get_xml_prop_string (a_node, "media", &strval)) {
2104     new_segment_template->media = strval;
2105   } else if (parent) {
2106     new_segment_template->media = xmlMemStrdup (parent->media);
2107   }
2108
2109   if (gst_mpdparser_get_xml_prop_string (a_node, "index", &strval)) {
2110     new_segment_template->index = strval;
2111   } else if (parent) {
2112     new_segment_template->index = xmlMemStrdup (parent->index);
2113   }
2114
2115   if (gst_mpdparser_get_xml_prop_string (a_node, "initialization", &strval)) {
2116     new_segment_template->initialization = strval;
2117   } else if (parent) {
2118     new_segment_template->initialization =
2119         xmlMemStrdup (parent->initialization);
2120   }
2121
2122   if (gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching", &strval)) {
2123     new_segment_template->bitstreamSwitching = strval;
2124   } else if (parent) {
2125     new_segment_template->bitstreamSwitching =
2126         xmlMemStrdup (parent->bitstreamSwitching);
2127   }
2128
2129   *pointer = new_segment_template;
2130   return TRUE;
2131
2132 error:
2133   gst_mpdparser_free_segment_template_node (new_segment_template);
2134   return FALSE;
2135 }
2136
2137 static gboolean
2138 gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
2139 {
2140   xmlNode *cur_node;
2141   GstPeriodNode *new_period;
2142   gchar *actuate;
2143
2144   new_period = g_slice_new0 (GstPeriodNode);
2145
2146   GST_LOG ("attributes of Period node:");
2147
2148   new_period->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
2149   if (gst_mpdparser_get_xml_ns_prop_string (a_node,
2150           "http://www.w3.org/1999/xlink", "href", &new_period->xlink_href)
2151       && gst_mpdparser_get_xml_ns_prop_string (a_node,
2152           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
2153     if (strcmp (actuate, "onLoad") == 0)
2154       new_period->actuate = GST_XLINK_ACTUATE_ON_LOAD;
2155     xmlFree (actuate);
2156   }
2157
2158   gst_mpdparser_get_xml_prop_string (a_node, "id", &new_period->id);
2159   gst_mpdparser_get_xml_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
2160       &new_period->start);
2161   gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2162       GST_MPD_DURATION_NONE, &new_period->duration);
2163   gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching", FALSE,
2164       &new_period->bitstreamSwitching);
2165
2166   /* explore children nodes */
2167   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2168     if (cur_node->type == XML_ELEMENT_NODE) {
2169       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2170         gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
2171             cur_node, NULL);
2172       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2173         if (!gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
2174                 cur_node, NULL))
2175           goto error;
2176       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2177         if (!gst_mpdparser_parse_segment_template_node
2178             (&new_period->SegmentTemplate, cur_node, NULL))
2179           goto error;
2180       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
2181         gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
2182       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2183         gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
2184       }
2185     }
2186   }
2187
2188   /* We must parse AdaptationSet after everything else in the Period has been
2189    * parsed because certain AdaptationSet child elements can inherit attributes
2190    * specified by the same element in the Period
2191    */
2192   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2193     if (cur_node->type == XML_ELEMENT_NODE) {
2194       if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
2195         if (!gst_mpdparser_parse_adaptation_set_node
2196             (&new_period->AdaptationSets, cur_node, new_period))
2197           goto error;
2198       }
2199     }
2200   }
2201
2202   *list = g_list_append (*list, new_period);
2203   return TRUE;
2204
2205 error:
2206   gst_mpdparser_free_period_node (new_period);
2207   return FALSE;
2208 }
2209
2210 static void
2211 gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
2212 {
2213   xmlNode *cur_node;
2214   GstProgramInformationNode *new_prog_info;
2215
2216   new_prog_info = g_slice_new0 (GstProgramInformationNode);
2217   *list = g_list_append (*list, new_prog_info);
2218
2219   GST_LOG ("attributes of ProgramInformation node:");
2220   gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_prog_info->lang);
2221   gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL",
2222       &new_prog_info->moreInformationURL);
2223
2224   /* explore children nodes */
2225   GST_LOG ("children of ProgramInformation node:");
2226   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2227     if (cur_node->type == XML_ELEMENT_NODE) {
2228       if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
2229         gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Title);
2230       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
2231         gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Source);
2232       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
2233         gst_mpdparser_get_xml_node_content (cur_node,
2234             &new_prog_info->Copyright);
2235       }
2236     }
2237   }
2238 }
2239
2240 static void
2241 gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
2242 {
2243   GstMetricsRangeNode *new_metrics_range;
2244
2245   new_metrics_range = g_slice_new0 (GstMetricsRangeNode);
2246   *list = g_list_append (*list, new_metrics_range);
2247
2248   GST_LOG ("attributes of Metrics Range node:");
2249   gst_mpdparser_get_xml_prop_duration (a_node, "starttime",
2250       GST_MPD_DURATION_NONE, &new_metrics_range->starttime);
2251   gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2252       GST_MPD_DURATION_NONE, &new_metrics_range->duration);
2253 }
2254
2255 static void
2256 gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
2257 {
2258   xmlNode *cur_node;
2259   GstMetricsNode *new_metrics;
2260
2261   new_metrics = g_slice_new0 (GstMetricsNode);
2262   *list = g_list_append (*list, new_metrics);
2263
2264   GST_LOG ("attributes of Metrics node:");
2265   gst_mpdparser_get_xml_prop_string (a_node, "metrics", &new_metrics->metrics);
2266
2267   /* explore children nodes */
2268   GST_LOG ("children of Metrics node:");
2269   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2270     if (cur_node->type == XML_ELEMENT_NODE) {
2271       if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
2272         gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
2273             cur_node);
2274       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
2275         /* No reporting scheme is specified in this part of ISO/IEC 23009.
2276          * It is expected that external specifications may define formats
2277          * and delivery for the reporting data. */
2278         GST_LOG (" - Reporting node found (unknown structure)");
2279       }
2280     }
2281   }
2282 }
2283
2284 /* The UTCTiming element is defined in
2285  * ISO/IEC 23009-1:2014/PDAM 1 "Information technology â€” Dynamic adaptive streaming over HTTP (DASH) â€” Part 1: Media presentation description and segment formats / Amendment 1: High Profile and Availability Time Synchronization"
2286  */
2287 static void
2288 gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
2289 {
2290   GstUTCTimingNode *new_timing;
2291   gchar *method = NULL;
2292   gchar *value = NULL;
2293
2294   new_timing = g_slice_new0 (GstUTCTimingNode);
2295
2296   GST_LOG ("attributes of UTCTiming node:");
2297   if (gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri", &method)) {
2298     int i;
2299
2300     for (i = 0; gst_mpdparser_utc_timing_methods[i].name; ++i) {
2301       if (g_ascii_strncasecmp (gst_mpdparser_utc_timing_methods[i].name,
2302               method, strlen (gst_mpdparser_utc_timing_methods[i].name)) == 0) {
2303         new_timing->method = gst_mpdparser_utc_timing_methods[i].method;
2304         break;
2305       }
2306     }
2307     xmlFree (method);
2308   }
2309
2310   if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
2311     int max_tokens = 0;
2312     if (GST_MPD_UTCTIMING_TYPE_DIRECT == new_timing->method) {
2313       /* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
2314        * that is not a space separated list.
2315        */
2316       max_tokens = 1;
2317     }
2318     new_timing->urls = g_strsplit (value, " ", max_tokens);
2319     xmlFree (value);
2320   }
2321
2322   /* append to list only if both method and urls were set */
2323   if (new_timing->method != 0 && new_timing->urls != NULL &&
2324       g_strv_length (new_timing->urls) != 0) {
2325     *list = g_list_append (*list, new_timing);
2326   } else {
2327     gst_mpdparser_free_utctiming_node (new_timing);
2328   }
2329 }
2330
2331 static gboolean
2332 gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
2333 {
2334   xmlNode *cur_node;
2335   GstMPDNode *new_mpd;
2336
2337   gst_mpdparser_free_mpd_node (*pointer);
2338   *pointer = NULL;
2339   new_mpd = g_slice_new0 (GstMPDNode);
2340
2341   GST_LOG ("namespaces of root MPD node:");
2342   new_mpd->default_namespace =
2343       gst_mpdparser_get_xml_node_namespace (a_node, NULL);
2344   new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
2345   new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
2346
2347   GST_LOG ("attributes of root MPD node:");
2348   gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation",
2349       &new_mpd->schemaLocation);
2350   gst_mpdparser_get_xml_prop_string (a_node, "id", &new_mpd->id);
2351   gst_mpdparser_get_xml_prop_string (a_node, "profiles", &new_mpd->profiles);
2352   gst_mpdparser_get_xml_prop_type (a_node, "type", &new_mpd->type);
2353   gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime",
2354       &new_mpd->availabilityStartTime);
2355   gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime",
2356       &new_mpd->availabilityEndTime);
2357   gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration",
2358       GST_MPD_DURATION_NONE, &new_mpd->mediaPresentationDuration);
2359   gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod",
2360       GST_MPD_DURATION_NONE, &new_mpd->minimumUpdatePeriod);
2361   gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime",
2362       GST_MPD_DURATION_NONE, &new_mpd->minBufferTime);
2363   gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth",
2364       GST_MPD_DURATION_NONE, &new_mpd->timeShiftBufferDepth);
2365   gst_mpdparser_get_xml_prop_duration (a_node, "suggestedPresentationDelay",
2366       GST_MPD_DURATION_NONE, &new_mpd->suggestedPresentationDelay);
2367   gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration",
2368       GST_MPD_DURATION_NONE, &new_mpd->maxSegmentDuration);
2369   gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration",
2370       GST_MPD_DURATION_NONE, &new_mpd->maxSubsegmentDuration);
2371
2372   /* explore children Period nodes */
2373   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2374     if (cur_node->type == XML_ELEMENT_NODE) {
2375       if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
2376         if (!gst_mpdparser_parse_period_node (&new_mpd->Periods, cur_node))
2377           goto error;
2378       } else if (xmlStrcmp (cur_node->name,
2379               (xmlChar *) "ProgramInformation") == 0) {
2380         gst_mpdparser_parse_program_info_node (&new_mpd->ProgramInfo, cur_node);
2381       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2382         gst_mpdparser_parse_baseURL_node (&new_mpd->BaseURLs, cur_node);
2383       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
2384         gst_mpdparser_parse_location_node (&new_mpd->Locations, cur_node);
2385       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
2386         gst_mpdparser_parse_metrics_node (&new_mpd->Metrics, cur_node);
2387       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "UTCTiming") == 0) {
2388         gst_mpdparser_parse_utctiming_node (&new_mpd->UTCTiming, cur_node);
2389       }
2390     }
2391   }
2392
2393   *pointer = new_mpd;
2394   return TRUE;
2395
2396 error:
2397   gst_mpdparser_free_mpd_node (new_mpd);
2398   return FALSE;
2399 }
2400
2401 /* comparison functions */
2402 static int
2403 strncmp_ext (const char *s1, const char *s2)
2404 {
2405   if (s1 == NULL && s2 == NULL)
2406     return 0;
2407   if (s1 == NULL && s2 != NULL)
2408     return 1;
2409   if (s2 == NULL && s1 != NULL)
2410     return 1;
2411   return strncmp (s1, s2, strlen (s2));
2412 }
2413
2414 /* navigation functions */
2415 static GstStreamMimeType
2416 gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set,
2417     GstRepresentationNode * rep)
2418 {
2419   gchar *mime = NULL;
2420   if (rep->RepresentationBase)
2421     mime = rep->RepresentationBase->mimeType;
2422   if (mime == NULL && adapt_set->RepresentationBase) {
2423     mime = adapt_set->RepresentationBase->mimeType;
2424   }
2425
2426   if (strncmp_ext (mime, "audio") == 0)
2427     return GST_STREAM_AUDIO;
2428   if (strncmp_ext (mime, "video") == 0)
2429     return GST_STREAM_VIDEO;
2430   if (strncmp_ext (mime, "application") == 0 || strncmp_ext (mime, "text") == 0)
2431     return GST_STREAM_APPLICATION;
2432
2433   return GST_STREAM_UNKNOWN;
2434 }
2435
2436 static GstRepresentationNode *
2437 gst_mpdparser_get_lowest_representation (GList * Representations)
2438 {
2439   GList *list = NULL;
2440   GstRepresentationNode *rep = NULL;
2441   GstRepresentationNode *lowest = NULL;
2442
2443   if (Representations == NULL)
2444     return NULL;
2445
2446   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2447     rep = (GstRepresentationNode *) list->data;
2448     if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
2449       lowest = rep;
2450     }
2451   }
2452
2453   return lowest;
2454 }
2455
2456 #if 0
2457 static GstRepresentationNode *
2458 gst_mpdparser_get_highest_representation (GList * Representations)
2459 {
2460   GList *list = NULL;
2461
2462   if (Representations == NULL)
2463     return NULL;
2464
2465   list = g_list_last (Representations);
2466
2467   return list ? (GstRepresentationNode *) list->data : NULL;
2468 }
2469
2470 static GstRepresentationNode *
2471 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
2472     gint max_bandwidth)
2473 {
2474   GList *list = NULL;
2475   GstRepresentationNode *representation, *best_rep = NULL;
2476
2477   if (Representations == NULL)
2478     return NULL;
2479
2480   if (max_bandwidth <= 0)       /* 0 => get highest representation available */
2481     return gst_mpdparser_get_highest_representation (Representations);
2482
2483   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2484     representation = (GstRepresentationNode *) list->data;
2485     if (representation && representation->bandwidth <= max_bandwidth) {
2486       best_rep = representation;
2487     }
2488   }
2489
2490   return best_rep;
2491 }
2492 #endif
2493
2494 static GstSegmentBaseType *
2495 gst_mpdparser_get_segment_base (GstPeriodNode * Period,
2496     GstAdaptationSetNode * AdaptationSet,
2497     GstRepresentationNode * Representation)
2498 {
2499   GstSegmentBaseType *SegmentBase = NULL;
2500
2501   if (Representation && Representation->SegmentBase) {
2502     SegmentBase = Representation->SegmentBase;
2503   } else if (AdaptationSet && AdaptationSet->SegmentBase) {
2504     SegmentBase = AdaptationSet->SegmentBase;
2505   } else if (Period && Period->SegmentBase) {
2506     SegmentBase = Period->SegmentBase;
2507   }
2508   /* the SegmentBase element could be encoded also inside a SegmentList element */
2509   if (SegmentBase == NULL) {
2510     if (Representation && Representation->SegmentList
2511         && Representation->SegmentList->MultSegBaseType
2512         && Representation->SegmentList->MultSegBaseType->SegBaseType) {
2513       SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
2514     } else if (AdaptationSet && AdaptationSet->SegmentList
2515         && AdaptationSet->SegmentList->MultSegBaseType
2516         && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType) {
2517       SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
2518     } else if (Period && Period->SegmentList
2519         && Period->SegmentList->MultSegBaseType
2520         && Period->SegmentList->MultSegBaseType->SegBaseType) {
2521       SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
2522     }
2523   }
2524
2525   return SegmentBase;
2526 }
2527
2528 gint
2529 gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations)
2530 {
2531   GList *list = NULL, *lowest = NULL;
2532   GstRepresentationNode *rep = NULL;
2533   gint lowest_bandwidth = -1;
2534
2535   if (Representations == NULL)
2536     return -1;
2537
2538   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2539     rep = (GstRepresentationNode *) list->data;
2540     if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2541       lowest = list;
2542       lowest_bandwidth = rep->bandwidth;
2543     }
2544   }
2545
2546   return lowest ? g_list_position (Representations, lowest) : -1;
2547 }
2548
2549 gint
2550 gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
2551     gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2552     max_video_framerate_n, gint max_video_framerate_d)
2553 {
2554   GList *list = NULL, *best = NULL;
2555   GstRepresentationNode *representation;
2556   gint best_bandwidth = 0;
2557
2558   GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2559
2560   if (Representations == NULL)
2561     return -1;
2562
2563   if (max_bandwidth <= 0)       /* 0 => get lowest representation available */
2564     return gst_mpdparser_get_rep_idx_with_min_bandwidth (Representations);
2565
2566   for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2567     GstFrameRate *framerate = NULL;
2568
2569     representation = (GstRepresentationNode *) list->data;
2570
2571     /* FIXME: Really? */
2572     if (!representation)
2573       continue;
2574
2575     framerate = representation->RepresentationBase->frameRate;
2576     if (!framerate)
2577       framerate = representation->RepresentationBase->maxFrameRate;
2578
2579     if (framerate && max_video_framerate_n > 0) {
2580       if (gst_util_fraction_compare (framerate->num, framerate->den,
2581               max_video_framerate_n, max_video_framerate_d) > 0)
2582         continue;
2583     }
2584
2585     if (max_video_width > 0
2586         && representation->RepresentationBase->width > max_video_width)
2587       continue;
2588     if (max_video_height > 0
2589         && representation->RepresentationBase->height > max_video_height)
2590       continue;
2591
2592     if (representation->bandwidth <= max_bandwidth &&
2593         representation->bandwidth > best_bandwidth) {
2594       best = list;
2595       best_bandwidth = representation->bandwidth;
2596     }
2597   }
2598
2599   return best ? g_list_position (Representations, best) : -1;
2600 }
2601
2602 static GstSegmentListNode *
2603 gst_mpd_client_fetch_external_segment_list (GstMpdClient * client,
2604     GstPeriodNode * Period,
2605     GstAdaptationSetNode * AdaptationSet,
2606     GstRepresentationNode * Representation,
2607     GstSegmentListNode * parent, GstSegmentListNode * segment_list)
2608 {
2609   GstFragment *download;
2610   GstBuffer *segment_list_buffer;
2611   GstMapInfo map;
2612   GError *err = NULL;
2613   xmlDocPtr doc = NULL;
2614   GstUri *base_uri, *uri;
2615   gchar *query = NULL;
2616   gchar *uri_string;
2617   GstSegmentListNode *new_segment_list = NULL;
2618
2619   /* ISO/IEC 23009-1:2014 5.5.3 4)
2620    * Remove nodes that resolve to nothing when resolving
2621    */
2622   if (strcmp (segment_list->xlink_href,
2623           "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
2624     return NULL;
2625   }
2626
2627   if (!client->downloader) {
2628     return NULL;
2629   }
2630
2631   /* Build absolute URI */
2632
2633   /* Get base URI at the MPD level */
2634   base_uri =
2635       gst_uri_from_string (client->
2636       mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
2637
2638   /* combine a BaseURL at the MPD level with the current base url */
2639   base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
2640
2641   /* combine a BaseURL at the Period level with the current base url */
2642   base_uri = combine_urls (base_uri, Period->BaseURLs, &query, 0);
2643
2644   if (AdaptationSet) {
2645     /* combine a BaseURL at the AdaptationSet level with the current base url */
2646     base_uri = combine_urls (base_uri, AdaptationSet->BaseURLs, &query, 0);
2647
2648     if (Representation) {
2649       /* combine a BaseURL at the Representation level with the current base url */
2650       base_uri = combine_urls (base_uri, Representation->BaseURLs, &query, 0);
2651     }
2652   }
2653
2654   uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
2655   if (query)
2656     gst_uri_set_query_string (uri, query);
2657   g_free (query);
2658   uri_string = gst_uri_to_string (uri);
2659   gst_uri_unref (base_uri);
2660   gst_uri_unref (uri);
2661
2662   download =
2663       gst_uri_downloader_fetch_uri (client->downloader,
2664       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
2665   g_free (uri_string);
2666
2667   if (!download) {
2668     GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
2669         segment_list->xlink_href, err->message);
2670     g_clear_error (&err);
2671     return NULL;
2672   }
2673
2674   segment_list_buffer = gst_fragment_get_buffer (download);
2675   g_object_unref (download);
2676
2677   gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
2678
2679   doc =
2680       xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
2681       XML_PARSE_NONET);
2682
2683   gst_buffer_unmap (segment_list_buffer, &map);
2684   gst_buffer_unref (segment_list_buffer);
2685
2686   /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
2687    * in external xml is allowed, however, multiple SegmentList does not make sense
2688    * because Period/AdaptationSet/Representation allow only one SegmentList */
2689   if (doc) {
2690     xmlNode *root_element = xmlDocGetRootElement (doc);
2691
2692     if (root_element->type != XML_ELEMENT_NODE ||
2693         xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") != 0) {
2694       goto error;
2695     }
2696
2697     gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
2698         parent);
2699   } else {
2700     goto error;
2701   }
2702
2703 done:
2704   if (doc)
2705     xmlFreeDoc (doc);
2706
2707   return new_segment_list;
2708
2709 error:
2710   GST_ERROR ("Failed to parse segment list node XML");
2711   goto done;
2712 }
2713
2714 static GstSegmentListNode *
2715 gst_mpdparser_get_segment_list (GstMpdClient * client, GstPeriodNode * Period,
2716     GstAdaptationSetNode * AdaptationSet,
2717     GstRepresentationNode * Representation)
2718 {
2719   GstSegmentListNode **SegmentList;
2720   GstSegmentListNode *ParentSegmentList = NULL;
2721
2722   if (Representation && Representation->SegmentList) {
2723     SegmentList = &Representation->SegmentList;
2724     ParentSegmentList = AdaptationSet->SegmentList;
2725   } else if (AdaptationSet && AdaptationSet->SegmentList) {
2726     SegmentList = &AdaptationSet->SegmentList;
2727     ParentSegmentList = Period->SegmentList;
2728     Representation = NULL;
2729   } else {
2730     Representation = NULL;
2731     AdaptationSet = NULL;
2732     SegmentList = &Period->SegmentList;
2733   }
2734
2735   /* Resolve external segment list here. */
2736   if (*SegmentList && (*SegmentList)->xlink_href) {
2737     GstSegmentListNode *new_segment_list;
2738
2739     /* TODO: Use SegmentList of parent if
2740      * - Parent has its own SegmentList
2741      * - Fail to get SegmentList from external xml
2742      */
2743     new_segment_list =
2744         gst_mpd_client_fetch_external_segment_list (client, Period,
2745         AdaptationSet, Representation, ParentSegmentList, *SegmentList);
2746
2747     gst_mpdparser_free_segment_list_node (*SegmentList);
2748     *SegmentList = new_segment_list;
2749   }
2750
2751   return *SegmentList;
2752 }
2753
2754 /* memory management functions */
2755 static void
2756 gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
2757 {
2758   if (mpd_node) {
2759     if (mpd_node->default_namespace)
2760       xmlFree (mpd_node->default_namespace);
2761     if (mpd_node->namespace_xsi)
2762       xmlFree (mpd_node->namespace_xsi);
2763     if (mpd_node->namespace_ext)
2764       xmlFree (mpd_node->namespace_ext);
2765     if (mpd_node->schemaLocation)
2766       xmlFree (mpd_node->schemaLocation);
2767     if (mpd_node->id)
2768       xmlFree (mpd_node->id);
2769     if (mpd_node->profiles)
2770       xmlFree (mpd_node->profiles);
2771     if (mpd_node->availabilityStartTime)
2772       gst_date_time_unref (mpd_node->availabilityStartTime);
2773     if (mpd_node->availabilityEndTime)
2774       gst_date_time_unref (mpd_node->availabilityEndTime);
2775     g_list_free_full (mpd_node->ProgramInfo,
2776         (GDestroyNotify) gst_mpdparser_free_prog_info_node);
2777     g_list_free_full (mpd_node->BaseURLs,
2778         (GDestroyNotify) gst_mpdparser_free_base_url_node);
2779     g_list_free_full (mpd_node->Locations, (GDestroyNotify) xmlFree);
2780     g_list_free_full (mpd_node->Periods,
2781         (GDestroyNotify) gst_mpdparser_free_period_node);
2782     g_list_free_full (mpd_node->Metrics,
2783         (GDestroyNotify) gst_mpdparser_free_metrics_node);
2784     g_list_free_full (mpd_node->UTCTiming,
2785         (GDestroyNotify) gst_mpdparser_free_utctiming_node);
2786     g_slice_free (GstMPDNode, mpd_node);
2787   }
2788 }
2789
2790 static void
2791 gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
2792 {
2793   if (prog_info_node) {
2794     if (prog_info_node->lang)
2795       xmlFree (prog_info_node->lang);
2796     if (prog_info_node->moreInformationURL)
2797       xmlFree (prog_info_node->moreInformationURL);
2798     if (prog_info_node->Title)
2799       xmlFree (prog_info_node->Title);
2800     if (prog_info_node->Source)
2801       xmlFree (prog_info_node->Source);
2802     if (prog_info_node->Copyright)
2803       xmlFree (prog_info_node->Copyright);
2804     g_slice_free (GstProgramInformationNode, prog_info_node);
2805   }
2806 }
2807
2808 static void
2809 gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
2810 {
2811   if (metrics_node) {
2812     if (metrics_node->metrics)
2813       xmlFree (metrics_node->metrics);
2814     g_list_free_full (metrics_node->MetricsRanges,
2815         (GDestroyNotify) gst_mpdparser_free_metrics_range_node);
2816     g_slice_free (GstMetricsNode, metrics_node);
2817   }
2818 }
2819
2820 static void
2821 gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
2822 {
2823   if (metrics_range_node) {
2824     g_slice_free (GstMetricsRangeNode, metrics_range_node);
2825   }
2826 }
2827
2828 static void
2829 gst_mpdparser_free_period_node (GstPeriodNode * period_node)
2830 {
2831   if (period_node) {
2832     if (period_node->id)
2833       xmlFree (period_node->id);
2834     gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
2835     gst_mpdparser_free_segment_list_node (period_node->SegmentList);
2836     gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
2837     g_list_free_full (period_node->AdaptationSets,
2838         (GDestroyNotify) gst_mpdparser_free_adaptation_set_node);
2839     g_list_free_full (period_node->Subsets,
2840         (GDestroyNotify) gst_mpdparser_free_subset_node);
2841     g_list_free_full (period_node->BaseURLs,
2842         (GDestroyNotify) gst_mpdparser_free_base_url_node);
2843     if (period_node->xlink_href)
2844       xmlFree (period_node->xlink_href);
2845     g_slice_free (GstPeriodNode, period_node);
2846   }
2847 }
2848
2849 static void
2850 gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
2851 {
2852   if (subset_node) {
2853     if (subset_node->contains)
2854       xmlFree (subset_node->contains);
2855     g_slice_free (GstSubsetNode, subset_node);
2856   }
2857 }
2858
2859 static void
2860 gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
2861     segment_template_node)
2862 {
2863   if (segment_template_node) {
2864     if (segment_template_node->media)
2865       xmlFree (segment_template_node->media);
2866     if (segment_template_node->index)
2867       xmlFree (segment_template_node->index);
2868     if (segment_template_node->initialization)
2869       xmlFree (segment_template_node->initialization);
2870     if (segment_template_node->bitstreamSwitching)
2871       xmlFree (segment_template_node->bitstreamSwitching);
2872     /* MultipleSegmentBaseType extension */
2873     gst_mpdparser_free_mult_seg_base_type_ext
2874         (segment_template_node->MultSegBaseType);
2875     g_slice_free (GstSegmentTemplateNode, segment_template_node);
2876   }
2877 }
2878
2879 static void
2880 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
2881     representation_base)
2882 {
2883   if (representation_base) {
2884     if (representation_base->profiles)
2885       xmlFree (representation_base->profiles);
2886     g_slice_free (GstRatio, representation_base->sar);
2887     g_slice_free (GstFrameRate, representation_base->frameRate);
2888     g_slice_free (GstFrameRate, representation_base->minFrameRate);
2889     g_slice_free (GstFrameRate, representation_base->maxFrameRate);
2890     if (representation_base->audioSamplingRate)
2891       xmlFree (representation_base->audioSamplingRate);
2892     if (representation_base->mimeType)
2893       xmlFree (representation_base->mimeType);
2894     if (representation_base->segmentProfiles)
2895       xmlFree (representation_base->segmentProfiles);
2896     if (representation_base->codecs)
2897       xmlFree (representation_base->codecs);
2898     if (representation_base->scanType)
2899       xmlFree (representation_base->scanType);
2900     g_list_free_full (representation_base->FramePacking,
2901         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2902     g_list_free_full (representation_base->AudioChannelConfiguration,
2903         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2904     g_list_free_full (representation_base->ContentProtection,
2905         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2906     g_slice_free (GstRepresentationBaseType, representation_base);
2907   }
2908 }
2909
2910 static void
2911 gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
2912     adaptation_set_node)
2913 {
2914   if (adaptation_set_node) {
2915     if (adaptation_set_node->lang)
2916       xmlFree (adaptation_set_node->lang);
2917     if (adaptation_set_node->contentType)
2918       xmlFree (adaptation_set_node->contentType);
2919     g_slice_free (GstRatio, adaptation_set_node->par);
2920     g_slice_free (GstConditionalUintType,
2921         adaptation_set_node->segmentAlignment);
2922     g_slice_free (GstConditionalUintType,
2923         adaptation_set_node->subsegmentAlignment);
2924     g_list_free_full (adaptation_set_node->Accessibility,
2925         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2926     g_list_free_full (adaptation_set_node->Role,
2927         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2928     g_list_free_full (adaptation_set_node->Rating,
2929         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2930     g_list_free_full (adaptation_set_node->Viewpoint,
2931         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2932     gst_mpdparser_free_representation_base_type
2933         (adaptation_set_node->RepresentationBase);
2934     gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
2935     gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
2936     gst_mpdparser_free_segment_template_node
2937         (adaptation_set_node->SegmentTemplate);
2938     g_list_free_full (adaptation_set_node->BaseURLs,
2939         (GDestroyNotify) gst_mpdparser_free_base_url_node);
2940     g_list_free_full (adaptation_set_node->Representations,
2941         (GDestroyNotify) gst_mpdparser_free_representation_node);
2942     g_list_free_full (adaptation_set_node->ContentComponents,
2943         (GDestroyNotify) gst_mpdparser_free_content_component_node);
2944     if (adaptation_set_node->xlink_href)
2945       xmlFree (adaptation_set_node->xlink_href);
2946     g_slice_free (GstAdaptationSetNode, adaptation_set_node);
2947   }
2948 }
2949
2950 static void
2951 gst_mpdparser_free_representation_node (GstRepresentationNode *
2952     representation_node)
2953 {
2954   if (representation_node) {
2955     if (representation_node->id)
2956       xmlFree (representation_node->id);
2957     g_strfreev (representation_node->dependencyId);
2958     g_strfreev (representation_node->mediaStreamStructureId);
2959     gst_mpdparser_free_representation_base_type
2960         (representation_node->RepresentationBase);
2961     g_list_free_full (representation_node->SubRepresentations,
2962         (GDestroyNotify) gst_mpdparser_free_subrepresentation_node);
2963     gst_mpdparser_free_seg_base_type_ext (representation_node->SegmentBase);
2964     gst_mpdparser_free_segment_template_node
2965         (representation_node->SegmentTemplate);
2966     gst_mpdparser_free_segment_list_node (representation_node->SegmentList);
2967     g_list_free_full (representation_node->BaseURLs,
2968         (GDestroyNotify) gst_mpdparser_free_base_url_node);
2969     g_slice_free (GstRepresentationNode, representation_node);
2970   }
2971 }
2972
2973 static void
2974 gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
2975     subrep_node)
2976 {
2977   if (subrep_node) {
2978     gst_mpdparser_free_representation_base_type
2979         (subrep_node->RepresentationBase);
2980     if (subrep_node->dependencyLevel)
2981       xmlFree (subrep_node->dependencyLevel);
2982     g_strfreev (subrep_node->contentComponent);
2983     g_slice_free (GstSubRepresentationNode, subrep_node);
2984   }
2985 }
2986
2987 static void
2988 gst_mpdparser_free_s_node (GstSNode * s_node)
2989 {
2990   if (s_node) {
2991     g_slice_free (GstSNode, s_node);
2992   }
2993 }
2994
2995 static GstSegmentTimelineNode *
2996 gst_mpdparser_segment_timeline_node_new (void)
2997 {
2998   GstSegmentTimelineNode *node = g_slice_new0 (GstSegmentTimelineNode);
2999
3000   g_queue_init (&node->S);
3001
3002   return node;
3003 }
3004
3005 static void
3006 gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
3007 {
3008   if (seg_timeline) {
3009     g_queue_foreach (&seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
3010     g_queue_clear (&seg_timeline->S);
3011     g_slice_free (GstSegmentTimelineNode, seg_timeline);
3012   }
3013 }
3014
3015 static void
3016 gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
3017 {
3018   if (url_type_node) {
3019     if (url_type_node->sourceURL)
3020       xmlFree (url_type_node->sourceURL);
3021     g_slice_free (GstRange, url_type_node->range);
3022     g_slice_free (GstURLType, url_type_node);
3023   }
3024 }
3025
3026 static void
3027 gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
3028 {
3029   if (seg_base_type) {
3030     if (seg_base_type->indexRange)
3031       g_slice_free (GstRange, seg_base_type->indexRange);
3032     gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
3033     gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
3034     g_slice_free (GstSegmentBaseType, seg_base_type);
3035   }
3036 }
3037
3038 static void
3039 gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
3040     mult_seg_base_type)
3041 {
3042   if (mult_seg_base_type) {
3043     /* SegmentBaseType extension */
3044     gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
3045     gst_mpdparser_free_segment_timeline_node
3046         (mult_seg_base_type->SegmentTimeline);
3047     gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
3048     g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
3049   }
3050 }
3051
3052 static void
3053 gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
3054 {
3055   if (segment_list_node) {
3056     g_list_free_full (segment_list_node->SegmentURL,
3057         (GDestroyNotify) gst_mpdparser_free_segment_url_node);
3058     /* MultipleSegmentBaseType extension */
3059     gst_mpdparser_free_mult_seg_base_type_ext
3060         (segment_list_node->MultSegBaseType);
3061     if (segment_list_node->xlink_href)
3062       xmlFree (segment_list_node->xlink_href);
3063     g_slice_free (GstSegmentListNode, segment_list_node);
3064   }
3065 }
3066
3067 static void
3068 gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
3069 {
3070   if (segment_url) {
3071     if (segment_url->media)
3072       xmlFree (segment_url->media);
3073     g_slice_free (GstRange, segment_url->mediaRange);
3074     if (segment_url->index)
3075       xmlFree (segment_url->index);
3076     g_slice_free (GstRange, segment_url->indexRange);
3077     g_slice_free (GstSegmentURLNode, segment_url);
3078   }
3079 }
3080
3081 static void
3082 gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
3083 {
3084   if (base_url_node) {
3085     if (base_url_node->baseURL)
3086       xmlFree (base_url_node->baseURL);
3087     if (base_url_node->serviceLocation)
3088       xmlFree (base_url_node->serviceLocation);
3089     if (base_url_node->byteRange)
3090       xmlFree (base_url_node->byteRange);
3091     g_slice_free (GstBaseURL, base_url_node);
3092   }
3093 }
3094
3095 static void
3096 gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
3097 {
3098   if (descriptor_type) {
3099     if (descriptor_type->schemeIdUri)
3100       xmlFree (descriptor_type->schemeIdUri);
3101     if (descriptor_type->value)
3102       xmlFree (descriptor_type->value);
3103     g_slice_free (GstDescriptorType, descriptor_type);
3104   }
3105 }
3106
3107 static void
3108 gst_mpdparser_free_content_component_node (GstContentComponentNode *
3109     content_component_node)
3110 {
3111   if (content_component_node) {
3112     if (content_component_node->lang)
3113       xmlFree (content_component_node->lang);
3114     if (content_component_node->contentType)
3115       xmlFree (content_component_node->contentType);
3116     g_slice_free (GstRatio, content_component_node->par);
3117     g_list_free_full (content_component_node->Accessibility,
3118         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3119     g_list_free_full (content_component_node->Role,
3120         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3121     g_list_free_full (content_component_node->Rating,
3122         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3123     g_list_free_full (content_component_node->Viewpoint,
3124         (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3125     g_slice_free (GstContentComponentNode, content_component_node);
3126   }
3127 }
3128
3129 static void
3130 gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type)
3131 {
3132   if (timing_type) {
3133     if (timing_type->urls)
3134       g_strfreev (timing_type->urls);
3135     g_slice_free (GstUTCTimingNode, timing_type);
3136   }
3137 }
3138
3139 static void
3140 gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
3141 {
3142   if (stream_period) {
3143     g_slice_free (GstStreamPeriod, stream_period);
3144   }
3145 }
3146
3147 static void
3148 gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
3149 {
3150   if (media_segment) {
3151     g_slice_free (GstMediaSegment, media_segment);
3152   }
3153 }
3154
3155 static void
3156 gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
3157 {
3158   g_assert (stream->segments == NULL);
3159   stream->segments = g_ptr_array_new ();
3160   g_ptr_array_set_free_func (stream->segments,
3161       (GDestroyNotify) gst_mpdparser_free_media_segment);
3162 }
3163
3164 static void
3165 gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
3166 {
3167   if (active_stream) {
3168     g_free (active_stream->baseURL);
3169     active_stream->baseURL = NULL;
3170     g_free (active_stream->queryURL);
3171     active_stream->queryURL = NULL;
3172     if (active_stream->segments)
3173       g_ptr_array_unref (active_stream->segments);
3174     g_slice_free (GstActiveStream, active_stream);
3175   }
3176 }
3177
3178 static gchar *
3179 gst_mpdparser_get_mediaURL (GstActiveStream * stream,
3180     GstSegmentURLNode * segmentURL)
3181 {
3182   const gchar *url_prefix;
3183
3184   g_return_val_if_fail (stream != NULL, NULL);
3185   g_return_val_if_fail (segmentURL != NULL, NULL);
3186
3187   url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
3188   g_return_val_if_fail (url_prefix != NULL, NULL);
3189
3190   return segmentURL->media;
3191 }
3192
3193 static const gchar *
3194 gst_mpdparser_get_initializationURL (GstActiveStream * stream,
3195     GstURLType * InitializationURL)
3196 {
3197   const gchar *url_prefix;
3198
3199   g_return_val_if_fail (stream != NULL, NULL);
3200
3201   url_prefix = (InitializationURL
3202       && InitializationURL->sourceURL) ? InitializationURL->
3203       sourceURL : stream->baseURL;
3204
3205   return url_prefix;
3206 }
3207
3208 /* ISO/IEC 23009-1:2004 5.3.9.4.4 */
3209 static gboolean
3210 validate_format (const gchar * format)
3211 {
3212   const gchar *p = format;
3213
3214   /* Check if it starts with % */
3215   if (!p || p[0] != '%')
3216     return FALSE;
3217   p++;
3218
3219   /* the spec mandates a format like %0[width]d */
3220   /* Following the %, we must have a 0 */
3221   if (p[0] != '0')
3222     return FALSE;
3223
3224   /* Following the % must be a number starting with 0
3225    */
3226   while (g_ascii_isdigit (*p))
3227     p++;
3228
3229   /* After any 0 and alphanumeric values, there must be a d.
3230    */
3231   if (p[0] != 'd')
3232     return FALSE;
3233   p++;
3234
3235   /* And then potentially more characters without any
3236    * further %, even if the spec does not mention this
3237    */
3238   p = strchr (p, '%');
3239   if (p)
3240     return FALSE;
3241
3242   return TRUE;
3243 }
3244
3245 static gchar *
3246 promote_format_to_uint64 (const gchar * format)
3247 {
3248   const gchar *p = format;
3249   gchar *promoted_format;
3250
3251   /* Must be called with a validated format! */
3252   g_return_val_if_fail (validate_format (format), NULL);
3253
3254   /* it starts with % */
3255   p++;
3256
3257   /* Following the % must be a 0, or any of d, x or u.
3258    * x and u are not part of the spec, but don't hurt us
3259    */
3260   if (p[0] == '0') {
3261     p++;
3262
3263     while (g_ascii_isdigit (*p))
3264       p++;
3265   }
3266
3267   /* After any 0 and alphanumeric values, there must be a d.
3268    * Otherwise validation would have failed
3269    */
3270   g_assert (p[0] == 'd');
3271
3272   promoted_format =
3273       g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
3274       format, p);
3275
3276   return promoted_format;
3277 }
3278
3279 static gboolean
3280 gst_mpdparser_validate_rfc1738_url (const char *s)
3281 {
3282   while (*s) {
3283     if (!strchr
3284         (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
3285             *s))
3286       return FALSE;
3287     if (*s == '%') {
3288       /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
3289          operator, so this is safe for strings ending before two hex digits */
3290       if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
3291         return FALSE;
3292       s += 2;
3293     }
3294     s++;
3295   }
3296   return TRUE;
3297 }
3298
3299 static gchar *
3300 gst_mpdparser_build_URL_from_template (const gchar * url_template,
3301     const gchar * id, guint number, guint bandwidth, guint64 time)
3302 {
3303   static const gchar default_format[] = "%01d";
3304   gchar **tokens, *token, *ret;
3305   const gchar *format;
3306   gint i, num_tokens;
3307
3308   g_return_val_if_fail (url_template != NULL, NULL);
3309   tokens = g_strsplit_set (url_template, "$", -1);
3310   if (!tokens) {
3311     GST_WARNING ("Scan of URL template failed!");
3312     return NULL;
3313   }
3314   num_tokens = g_strv_length (tokens);
3315
3316   /*
3317    * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
3318    * An even number of tokens means the string is not valid.
3319    */
3320   if ((num_tokens & 1) == 0) {
3321     GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
3322         num_tokens, url_template);
3323     g_strfreev (tokens);
3324     return NULL;
3325   }
3326
3327   for (i = 0; i < num_tokens; i++) {
3328     token = tokens[i];
3329     format = default_format;
3330
3331     /* the tokens to replace must be provided between $ characters, eg $token$
3332      * For a string like token0$token1$token2$token3$token4, only the odd number
3333      * tokens (1,3,...) must be parsed.
3334      *
3335      * Skip even tokens
3336      */
3337     if ((i & 1) == 0)
3338       continue;
3339
3340     if (!g_strcmp0 (token, "RepresentationID")) {
3341       if (!gst_mpdparser_validate_rfc1738_url (id))
3342         goto invalid_representation_id;
3343
3344       tokens[i] = g_strdup_printf ("%s", id);
3345       g_free (token);
3346     } else if (!strncmp (token, "Number", 6)) {
3347       if (strlen (token) > 6) {
3348         format = token + 6;     /* format tag */
3349       }
3350       if (!validate_format (format))
3351         goto invalid_format;
3352
3353       tokens[i] = g_strdup_printf (format, number);
3354       g_free (token);
3355     } else if (!strncmp (token, "Bandwidth", 9)) {
3356       if (strlen (token) > 9) {
3357         format = token + 9;     /* format tag */
3358       }
3359       if (!validate_format (format))
3360         goto invalid_format;
3361
3362       tokens[i] = g_strdup_printf (format, bandwidth);
3363       g_free (token);
3364     } else if (!strncmp (token, "Time", 4)) {
3365       gchar *promoted_format;
3366
3367       if (strlen (token) > 4) {
3368         format = token + 4;     /* format tag */
3369       }
3370       if (!validate_format (format))
3371         goto invalid_format;
3372
3373       promoted_format = promote_format_to_uint64 (format);
3374       tokens[i] = g_strdup_printf (promoted_format, time);
3375       g_free (promoted_format);
3376       g_free (token);
3377     } else if (!g_strcmp0 (token, "")) {
3378       tokens[i] = g_strdup_printf ("%s", "$");
3379       g_free (token);
3380     } else {
3381       /* unexpected identifier found between $ signs
3382        *
3383        * "If the URL contains unescaped $ symbols which do not enclose a valid
3384        * identifier then the result of URL formation is undefined"
3385        */
3386       goto invalid_format;
3387     }
3388   }
3389
3390   ret = g_strjoinv (NULL, tokens);
3391
3392   g_strfreev (tokens);
3393
3394   return ret;
3395
3396 invalid_format:
3397   {
3398     GST_ERROR ("Invalid format '%s' in '%s'", format, token);
3399
3400     g_strfreev (tokens);
3401
3402     return NULL;
3403   }
3404 invalid_representation_id:
3405   {
3406     GST_ERROR
3407         ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
3408         id);
3409
3410     g_strfreev (tokens);
3411
3412     return NULL;
3413   }
3414 }
3415
3416 guint
3417 gst_mpd_client_get_period_index_at_time (GstMpdClient * client,
3418     GstDateTime * time)
3419 {
3420   GList *iter;
3421   guint period_idx = G_MAXUINT;
3422   guint idx;
3423   gint64 time_offset;
3424   GstDateTime *avail_start =
3425       gst_mpd_client_get_availability_start_time (client);
3426   GstStreamPeriod *stream_period;
3427
3428   if (avail_start == NULL)
3429     return 0;
3430
3431   time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3432   gst_date_time_unref (avail_start);
3433
3434   if (time_offset < 0)
3435     return 0;
3436
3437   if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3438     return 0;
3439
3440   for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3441     stream_period = iter->data;
3442     if (stream_period->start <= time_offset
3443         && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3444             || stream_period->start + stream_period->duration > time_offset)) {
3445       period_idx = idx;
3446       break;
3447     }
3448   }
3449
3450   return period_idx;
3451 }
3452
3453 static GstStreamPeriod *
3454 gst_mpdparser_get_stream_period (GstMpdClient * client)
3455 {
3456   g_return_val_if_fail (client != NULL, NULL);
3457   g_return_val_if_fail (client->periods != NULL, NULL);
3458
3459   return g_list_nth_data (client->periods, client->period_idx);
3460 }
3461
3462 static GstRange *
3463 gst_mpdparser_clone_range (GstRange * range)
3464 {
3465   GstRange *clone = NULL;
3466
3467   if (range) {
3468     clone = g_slice_new0 (GstRange);
3469     clone->first_byte_pos = range->first_byte_pos;
3470     clone->last_byte_pos = range->last_byte_pos;
3471   }
3472
3473   return clone;
3474 }
3475
3476 static GstURLType *
3477 gst_mpdparser_clone_URL (GstURLType * url)
3478 {
3479
3480   GstURLType *clone = NULL;
3481
3482   if (url) {
3483     clone = g_slice_new0 (GstURLType);
3484     if (url->sourceURL) {
3485       clone->sourceURL = xmlMemStrdup (url->sourceURL);
3486     }
3487     clone->range = gst_mpdparser_clone_range (url->range);
3488   }
3489
3490   return clone;
3491 }
3492
3493 /*
3494  * Combine a base url with the current stream base url from the list of
3495  * baseURLs. Takes ownership of base and returns a new base.
3496  */
3497 static GstUri *
3498 combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
3499 {
3500   GstBaseURL *baseURL;
3501   GstUri *ret = base;
3502
3503   if (list != NULL) {
3504     baseURL = g_list_nth_data (list, idx);
3505     if (!baseURL) {
3506       baseURL = list->data;
3507     }
3508
3509     ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
3510     gst_uri_unref (base);
3511
3512     if (ret && query) {
3513       g_free (*query);
3514       *query = gst_uri_get_query_string (ret);
3515       if (*query) {
3516         ret = gst_uri_make_writable (ret);
3517         gst_uri_set_query_table (ret, NULL);
3518       }
3519     }
3520   }
3521
3522   return ret;
3523 }
3524
3525 /* select a stream and extract the baseURL (if present) */
3526 static gchar *
3527 gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
3528     gchar ** query)
3529 {
3530   GstStreamPeriod *stream_period;
3531   static const gchar empty[] = "";
3532   gchar *ret = NULL;
3533   GstUri *abs_url;
3534
3535   g_return_val_if_fail (stream != NULL, g_strdup (empty));
3536   stream_period = gst_mpdparser_get_stream_period (client);
3537   g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
3538   g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
3539
3540   /* NULLify query return before we start */
3541   if (query)
3542     *query = NULL;
3543
3544   /* initialise base url */
3545   abs_url =
3546       gst_uri_from_string (client->
3547       mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
3548
3549   /* combine a BaseURL at the MPD level with the current base url */
3550   abs_url =
3551       combine_urls (abs_url, client->mpd_node->BaseURLs, query,
3552       stream->baseURL_idx);
3553
3554   /* combine a BaseURL at the Period level with the current base url */
3555   abs_url =
3556       combine_urls (abs_url, stream_period->period->BaseURLs, query,
3557       stream->baseURL_idx);
3558
3559   GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
3560       stream->cur_adapt_set->contentType);
3561   /* combine a BaseURL at the AdaptationSet level with the current base url */
3562   abs_url =
3563       combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
3564       stream->baseURL_idx);
3565
3566   /* combine a BaseURL at the Representation level with the current base url */
3567   abs_url =
3568       combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
3569       stream->baseURL_idx);
3570
3571   ret = gst_uri_to_string (abs_url);
3572   gst_uri_unref (abs_url);
3573
3574   return ret;
3575 }
3576
3577 static GstClockTime
3578 gst_mpd_client_get_segment_duration (GstMpdClient * client,
3579     GstActiveStream * stream, guint64 * scale_dur)
3580 {
3581   GstStreamPeriod *stream_period;
3582   GstMultSegmentBaseType *base = NULL;
3583   GstClockTime duration = 0;
3584
3585   g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
3586   stream_period = gst_mpdparser_get_stream_period (client);
3587   g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
3588
3589   if (stream->cur_segment_list) {
3590     base = stream->cur_segment_list->MultSegBaseType;
3591   } else if (stream->cur_seg_template) {
3592     base = stream->cur_seg_template->MultSegBaseType;
3593   }
3594
3595   if (base == NULL || base->SegBaseType == NULL) {
3596     /* this may happen when we have a single segment */
3597     duration = stream_period->duration;
3598     if (scale_dur)
3599       *scale_dur = duration;
3600   } else {
3601     /* duration is guint so this cannot overflow */
3602     duration = base->duration * GST_SECOND;
3603     if (scale_dur)
3604       *scale_dur = duration;
3605     duration /= base->SegBaseType->timescale;
3606   }
3607
3608   return duration;
3609 }
3610
3611 /*****************************/
3612 /******* API functions *******/
3613 /*****************************/
3614
3615 GstMpdClient *
3616 gst_mpd_client_new (void)
3617 {
3618   GstMpdClient *client;
3619
3620   client = g_new0 (GstMpdClient, 1);
3621
3622   return client;
3623 }
3624
3625 void
3626 gst_active_streams_free (GstMpdClient * client)
3627 {
3628   if (client->active_streams) {
3629     g_list_foreach (client->active_streams,
3630         (GFunc) gst_mpdparser_free_active_stream, NULL);
3631     g_list_free (client->active_streams);
3632     client->active_streams = NULL;
3633   }
3634 }
3635
3636 void
3637 gst_mpd_client_free (GstMpdClient * client)
3638 {
3639   g_return_if_fail (client != NULL);
3640
3641   if (client->mpd_node)
3642     gst_mpdparser_free_mpd_node (client->mpd_node);
3643
3644   if (client->periods) {
3645     g_list_free_full (client->periods,
3646         (GDestroyNotify) gst_mpdparser_free_stream_period);
3647   }
3648
3649   gst_active_streams_free (client);
3650
3651   g_free (client->mpd_uri);
3652   client->mpd_uri = NULL;
3653   g_free (client->mpd_base_uri);
3654   client->mpd_base_uri = NULL;
3655
3656   if (client->downloader)
3657     gst_object_unref (client->downloader);
3658   client->downloader = NULL;
3659
3660   g_free (client);
3661 }
3662
3663 void
3664 gst_mpd_client_set_uri_downloader (GstMpdClient * client,
3665     GstUriDownloader * downloader)
3666 {
3667   if (client->downloader)
3668     gst_object_unref (client->downloader);
3669   client->downloader = gst_object_ref (downloader);
3670 }
3671
3672 static void
3673 gst_mpd_client_check_profiles (GstMpdClient * client)
3674 {
3675   GST_DEBUG ("Profiles: %s",
3676       client->mpd_node->profiles ? client->mpd_node->profiles : "<none>");
3677
3678   if (!client->mpd_node->profiles)
3679     return;
3680
3681   if (g_strstr_len (client->mpd_node->profiles, -1,
3682           "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
3683     client->profile_isoff_ondemand = TRUE;
3684     GST_DEBUG ("Found ISOFF on demand profile (2011)");
3685   }
3686 }
3687
3688 static void
3689 gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client)
3690 {
3691   GList *l;
3692
3693   for (l = client->mpd_node->Periods; l; /* explicitly advanced below */ ) {
3694     GstPeriodNode *period = l->data;
3695     GList *m;
3696
3697     if (period->xlink_href && period->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3698       GList *new_periods, *prev, *next;
3699
3700       new_periods = gst_mpd_client_fetch_external_period (client, period);
3701
3702       prev = l->prev;
3703       client->mpd_node->Periods =
3704           g_list_delete_link (client->mpd_node->Periods, l);
3705       gst_mpdparser_free_period_node (period);
3706       period = NULL;
3707
3708       /* Get new next node, we will insert before this */
3709       if (prev)
3710         next = prev->next;
3711       else
3712         next = client->mpd_node->Periods;
3713
3714       while (new_periods) {
3715         client->mpd_node->Periods =
3716             g_list_insert_before (client->mpd_node->Periods, next,
3717             new_periods->data);
3718         new_periods = g_list_delete_link (new_periods, new_periods);
3719       }
3720       next = NULL;
3721
3722       /* Update our iterator to the first new period if any, or the next */
3723       if (prev)
3724         l = prev->next;
3725       else
3726         l = client->mpd_node->Periods;
3727
3728       continue;
3729     }
3730
3731     if (period->SegmentList && period->SegmentList->xlink_href
3732         && period->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3733       GstSegmentListNode *new_segment_list;
3734
3735       new_segment_list =
3736           gst_mpd_client_fetch_external_segment_list (client, period, NULL,
3737           NULL, NULL, period->SegmentList);
3738
3739       gst_mpdparser_free_segment_list_node (period->SegmentList);
3740       period->SegmentList = new_segment_list;
3741     }
3742
3743     for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
3744       GstAdaptationSetNode *adapt_set = m->data;
3745       GList *n;
3746
3747       if (adapt_set->xlink_href
3748           && adapt_set->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3749         GList *new_adapt_sets, *prev, *next;
3750
3751         new_adapt_sets =
3752             gst_mpd_client_fetch_external_adaptation_set (client, period,
3753             adapt_set);
3754
3755         prev = m->prev;
3756         period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
3757         gst_mpdparser_free_adaptation_set_node (adapt_set);
3758         adapt_set = NULL;
3759
3760         /* Get new next node, we will insert before this */
3761         if (prev)
3762           next = prev->next;
3763         else
3764           next = period->AdaptationSets;
3765
3766         while (new_adapt_sets) {
3767           period->AdaptationSets =
3768               g_list_insert_before (period->AdaptationSets, next,
3769               new_adapt_sets->data);
3770           new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
3771         }
3772         next = NULL;
3773
3774         /* Update our iterator to the first new adapt_set if any, or the next */
3775         if (prev)
3776           m = prev->next;
3777         else
3778           m = period->AdaptationSets;
3779
3780         continue;
3781       }
3782
3783       if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
3784           && adapt_set->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3785         GstSegmentListNode *new_segment_list;
3786
3787         new_segment_list =
3788             gst_mpd_client_fetch_external_segment_list (client, period,
3789             adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
3790
3791         gst_mpdparser_free_segment_list_node (adapt_set->SegmentList);
3792         adapt_set->SegmentList = new_segment_list;
3793       }
3794
3795       for (n = adapt_set->Representations; n; n = n->next) {
3796         GstRepresentationNode *representation = n->data;
3797
3798         if (representation->SegmentList
3799             && representation->SegmentList->xlink_href
3800             && representation->SegmentList->actuate ==
3801             GST_XLINK_ACTUATE_ON_LOAD) {
3802
3803           GstSegmentListNode *new_segment_list;
3804
3805           new_segment_list =
3806               gst_mpd_client_fetch_external_segment_list (client, period,
3807               adapt_set, representation, adapt_set->SegmentList,
3808               representation->SegmentList);
3809
3810           gst_mpdparser_free_segment_list_node (representation->SegmentList);
3811           representation->SegmentList = new_segment_list;
3812
3813         }
3814       }
3815
3816       m = m->next;
3817     }
3818
3819     l = l->next;
3820   }
3821 }
3822
3823 gboolean
3824 gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
3825 {
3826   gboolean ret = FALSE;
3827
3828   if (data) {
3829     xmlDocPtr doc;
3830     xmlNode *root_element = NULL;
3831
3832     GST_DEBUG ("MPD file fully buffered, start parsing...");
3833
3834     /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
3835
3836     /* this initialize the library and check potential ABI mismatches
3837      * between the version it was compiled for and the actual shared
3838      * library used
3839      */
3840     LIBXML_TEST_VERSION;
3841
3842     /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
3843     doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
3844     if (doc == NULL) {
3845       GST_ERROR ("failed to parse the MPD file");
3846       ret = FALSE;
3847     } else {
3848       /* get the root element node */
3849       root_element = xmlDocGetRootElement (doc);
3850
3851       if (root_element->type != XML_ELEMENT_NODE
3852           || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
3853         GST_ERROR
3854             ("can not find the root element MPD, failed to parse the MPD file");
3855         ret = FALSE;            /* used to return TRUE before, but this seems wrong */
3856       } else {
3857         /* now we can parse the MPD root node and all children nodes, recursively */
3858         ret = gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
3859       }
3860       /* free the document */
3861       xmlFreeDoc (doc);
3862     }
3863
3864     if (ret) {
3865       gst_mpd_client_check_profiles (client);
3866       gst_mpd_client_fetch_on_load_external_resources (client);
3867     }
3868   }
3869
3870   return ret;
3871 }
3872
3873 const gchar *
3874 gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream)
3875 {
3876   GstActiveStream *stream;
3877
3878   g_return_val_if_fail (client != NULL, NULL);
3879   g_return_val_if_fail (client->active_streams != NULL, NULL);
3880   stream = g_list_nth_data (client->active_streams, indexStream);
3881   g_return_val_if_fail (stream != NULL, NULL);
3882
3883   return stream->baseURL;
3884 }
3885
3886 static GstClockTime
3887 gst_mpdparser_get_segment_end_time (GstMpdClient * client, GPtrArray * segments,
3888     const GstMediaSegment * segment, gint index)
3889 {
3890   const GstStreamPeriod *stream_period;
3891   GstClockTime end;
3892
3893   if (segment->repeat >= 0)
3894     return segment->start + (segment->repeat + 1) * segment->duration;
3895
3896   if (index < segments->len - 1) {
3897     const GstMediaSegment *next_segment =
3898         g_ptr_array_index (segments, index + 1);
3899     end = next_segment->start;
3900   } else {
3901     stream_period = gst_mpdparser_get_stream_period (client);
3902     end = stream_period->start + stream_period->duration;
3903   }
3904   return end;
3905 }
3906
3907 static gboolean
3908 gst_mpd_client_add_media_segment (GstActiveStream * stream,
3909     GstSegmentURLNode * url_node, guint number, gint repeat,
3910     guint64 scale_start, guint64 scale_duration,
3911     GstClockTime start, GstClockTime duration)
3912 {
3913   GstMediaSegment *media_segment;
3914
3915   g_return_val_if_fail (stream->segments != NULL, FALSE);
3916
3917   media_segment = g_slice_new0 (GstMediaSegment);
3918
3919   media_segment->SegmentURL = url_node;
3920   media_segment->number = number;
3921   media_segment->scale_start = scale_start;
3922   media_segment->scale_duration = scale_duration;
3923   media_segment->start = start;
3924   media_segment->duration = duration;
3925   media_segment->repeat = repeat;
3926
3927   g_ptr_array_add (stream->segments, media_segment);
3928   GST_LOG ("Added new segment: number %d, repeat %d, "
3929       "ts: %" GST_TIME_FORMAT ", dur: %"
3930       GST_TIME_FORMAT, number, repeat,
3931       GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
3932
3933   return TRUE;
3934 }
3935
3936 static void
3937 gst_mpd_client_stream_update_presentation_time_offset (GstMpdClient * client,
3938     GstActiveStream * stream)
3939 {
3940   GstSegmentBaseType *segbase = NULL;
3941
3942   /* Find the used segbase */
3943   if (stream->cur_segment_list) {
3944     segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
3945   } else if (stream->cur_seg_template) {
3946     segbase = stream->cur_seg_template->MultSegBaseType->SegBaseType;
3947   } else if (stream->cur_segment_base) {
3948     segbase = stream->cur_segment_base;
3949   }
3950
3951   if (segbase) {
3952     /* Avoid overflows */
3953     stream->presentationTimeOffset =
3954         gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
3955         segbase->timescale);
3956   } else {
3957     stream->presentationTimeOffset = 0;
3958   }
3959
3960   GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
3961       GST_TIME_ARGS (stream->presentationTimeOffset));
3962 }
3963
3964 gboolean
3965 gst_mpd_client_setup_representation (GstMpdClient * client,
3966     GstActiveStream * stream, GstRepresentationNode * representation)
3967 {
3968   GstStreamPeriod *stream_period;
3969   GList *rep_list;
3970   GstClockTime PeriodStart, PeriodEnd, start_time, duration;
3971   guint i;
3972   guint64 start;
3973
3974   if (stream->cur_adapt_set == NULL) {
3975     GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
3976     return FALSE;
3977   }
3978
3979   rep_list = stream->cur_adapt_set->Representations;
3980   stream->cur_representation = representation;
3981   stream->representation_idx = g_list_index (rep_list, representation);
3982
3983   /* clean the old segment list, if any */
3984   if (stream->segments) {
3985     g_ptr_array_unref (stream->segments);
3986     stream->segments = NULL;
3987   }
3988
3989   stream_period = gst_mpdparser_get_stream_period (client);
3990   g_return_val_if_fail (stream_period != NULL, FALSE);
3991   g_return_val_if_fail (stream_period->period != NULL, FALSE);
3992
3993   PeriodStart = stream_period->start;
3994   if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
3995     PeriodEnd = stream_period->start + stream_period->duration;
3996   else
3997     PeriodEnd = GST_CLOCK_TIME_NONE;
3998
3999   GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
4000       GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
4001
4002   if (representation->SegmentBase != NULL
4003       || representation->SegmentList != NULL) {
4004     GList *SegmentURL;
4005
4006     /* We have a fixed list of segments for any of the cases here,
4007      * init the segments list */
4008     gst_mpdparser_init_active_stream_segments (stream);
4009
4010     /* get the first segment_base of the selected representation */
4011     if ((stream->cur_segment_base =
4012             gst_mpdparser_get_segment_base (stream_period->period,
4013                 stream->cur_adapt_set, representation)) == NULL) {
4014       GST_DEBUG ("No useful SegmentBase node for the current Representation");
4015     }
4016
4017     /* get the first segment_list of the selected representation */
4018     if ((stream->cur_segment_list =
4019             gst_mpdparser_get_segment_list (client, stream_period->period,
4020                 stream->cur_adapt_set, representation)) == NULL) {
4021       GST_DEBUG ("No useful SegmentList node for the current Representation");
4022       /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4023       if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4024               PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
4025         return FALSE;
4026       }
4027     } else {
4028       /* build the list of GstMediaSegment nodes from the SegmentList node */
4029       SegmentURL = stream->cur_segment_list->SegmentURL;
4030       if (SegmentURL == NULL) {
4031         GST_WARNING
4032             ("No valid list of SegmentURL nodes in the MPD file, aborting...");
4033         return FALSE;
4034       }
4035
4036       /* build segment list */
4037       i = stream->cur_segment_list->MultSegBaseType->startNumber;
4038       start = 0;
4039       start_time = PeriodStart;
4040
4041       GST_LOG ("Building media segment list using a SegmentList node");
4042       if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
4043         GstSegmentTimelineNode *timeline;
4044         GstSNode *S;
4045         GList *list;
4046         GstClockTime presentationTimeOffset;
4047         GstSegmentBaseType *segbase;
4048
4049         segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
4050         presentationTimeOffset =
4051             gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
4052             segbase->timescale);
4053         GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
4054             presentationTimeOffset);
4055         timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
4056         for (list = g_queue_peek_head_link (&timeline->S); list;
4057             list = g_list_next (list)) {
4058           guint timescale;
4059
4060           S = (GstSNode *) list->data;
4061           GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
4062               G_GUINT64_FORMAT, S->d, S->r, S->t);
4063           timescale =
4064               stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
4065           duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4066
4067           if (S->t > 0) {
4068             start = S->t;
4069             start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
4070                 + PeriodStart - presentationTimeOffset;
4071           }
4072
4073           if (!SegmentURL) {
4074             GST_WARNING
4075                 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
4076             return FALSE;
4077           }
4078
4079           if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4080                   S->r, start, S->d, start_time, duration)) {
4081             return FALSE;
4082           }
4083           i += S->r + 1;
4084           start_time += duration * (S->r + 1);
4085           start += S->d * (S->r + 1);
4086           SegmentURL = g_list_next (SegmentURL);
4087         }
4088       } else {
4089         guint64 scale_dur;
4090
4091         duration =
4092             gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
4093         if (!GST_CLOCK_TIME_IS_VALID (duration))
4094           return FALSE;
4095
4096         while (SegmentURL) {
4097           if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4098                   0, start, scale_dur, start_time, duration)) {
4099             return FALSE;
4100           }
4101           i++;
4102           start += scale_dur;
4103           start_time += duration;
4104           SegmentURL = g_list_next (SegmentURL);
4105         }
4106       }
4107     }
4108   } else {
4109     if (representation->SegmentTemplate != NULL) {
4110       stream->cur_seg_template = representation->SegmentTemplate;
4111     } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
4112       stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
4113     } else if (stream_period->period->SegmentTemplate != NULL) {
4114       stream->cur_seg_template = stream_period->period->SegmentTemplate;
4115     }
4116
4117     if (stream->cur_seg_template == NULL
4118         || stream->cur_seg_template->MultSegBaseType == NULL) {
4119
4120       gst_mpdparser_init_active_stream_segments (stream);
4121       /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4122       if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4123               PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
4124         return FALSE;
4125       }
4126     } else {
4127       GstClockTime presentationTimeOffset;
4128       GstMultSegmentBaseType *mult_seg =
4129           stream->cur_seg_template->MultSegBaseType;
4130
4131       presentationTimeOffset =
4132           gst_util_uint64_scale (mult_seg->SegBaseType->presentationTimeOffset,
4133           GST_SECOND, mult_seg->SegBaseType->timescale);
4134       GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
4135           GST_TIME_ARGS (presentationTimeOffset));
4136       /* build segment list */
4137       i = mult_seg->startNumber;
4138       start = 0;
4139       start_time = 0;
4140
4141       GST_LOG ("Building media segment list using this template: %s",
4142           stream->cur_seg_template->media);
4143
4144       if (mult_seg->SegmentTimeline) {
4145         GstSegmentTimelineNode *timeline;
4146         GstSNode *S;
4147         GList *list;
4148
4149         timeline = mult_seg->SegmentTimeline;
4150         gst_mpdparser_init_active_stream_segments (stream);
4151         for (list = g_queue_peek_head_link (&timeline->S); list;
4152             list = g_list_next (list)) {
4153           guint timescale;
4154
4155           S = (GstSNode *) list->data;
4156           GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
4157               G_GUINT64_FORMAT, S->d, S->r, S->t);
4158           timescale = mult_seg->SegBaseType->timescale;
4159           duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4160           if (S->t > 0) {
4161             start = S->t;
4162             start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
4163                 + PeriodStart - presentationTimeOffset;
4164           }
4165
4166           if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
4167                   S->d, start_time, duration)) {
4168             return FALSE;
4169           }
4170           i += S->r + 1;
4171           start += S->d * (S->r + 1);
4172           start_time += duration * (S->r + 1);
4173         }
4174       } else {
4175         /* NOP - The segment is created on demand with the template, no need
4176          * to build a list */
4177       }
4178     }
4179   }
4180
4181   /* clip duration of segments to stop at period end */
4182   if (stream->segments && stream->segments->len) {
4183     if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
4184       guint n;
4185
4186       for (n = 0; n < stream->segments->len; ++n) {
4187         GstMediaSegment *media_segment =
4188             g_ptr_array_index (stream->segments, n);
4189         if (media_segment) {
4190           if (media_segment->start + media_segment->duration > PeriodEnd) {
4191             GstClockTime stop = PeriodEnd;
4192             if (n < stream->segments->len - 1) {
4193               GstMediaSegment *next_segment =
4194                   g_ptr_array_index (stream->segments, n + 1);
4195               if (next_segment && next_segment->start < PeriodEnd)
4196                 stop = next_segment->start;
4197             }
4198             media_segment->duration =
4199                 media_segment->start > stop ? 0 : stop - media_segment->start;
4200             GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
4201                 GST_TIME_ARGS (media_segment->duration));
4202
4203             /* If the segment was clipped entirely, we discard it and all
4204              * subsequent ones */
4205             if (media_segment->duration == 0) {
4206               GST_WARNING ("Discarding %u segments outside period",
4207                   stream->segments->len - n);
4208               /* _set_size should properly unref elements */
4209               g_ptr_array_set_size (stream->segments, n);
4210               break;
4211             }
4212           }
4213         }
4214       }
4215     }
4216 #ifndef GST_DISABLE_GST_DEBUG
4217     if (stream->segments->len > 0) {
4218       GstMediaSegment *last_media_segment =
4219           g_ptr_array_index (stream->segments, stream->segments->len - 1);
4220       GST_LOG ("Built a list of %d segments", last_media_segment->number);
4221     } else {
4222       GST_LOG ("All media segments were clipped");
4223     }
4224 #endif
4225   }
4226
4227   g_free (stream->baseURL);
4228   g_free (stream->queryURL);
4229   stream->baseURL =
4230       gst_mpdparser_parse_baseURL (client, stream, &stream->queryURL);
4231
4232   gst_mpd_client_stream_update_presentation_time_offset (client, stream);
4233
4234   return TRUE;
4235 }
4236
4237 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
4238 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
4239
4240 static GList *
4241 gst_mpd_client_fetch_external_period (GstMpdClient * client,
4242     GstPeriodNode * period_node)
4243 {
4244   GstFragment *download;
4245   GstAdapter *adapter;
4246   GstBuffer *period_buffer;
4247   GError *err = NULL;
4248   xmlDocPtr doc = NULL;
4249   GstUri *base_uri, *uri;
4250   gchar *query = NULL;
4251   gchar *uri_string, *wrapper;
4252   GList *new_periods = NULL;
4253   const gchar *data;
4254
4255   /* ISO/IEC 23009-1:2014 5.5.3 4)
4256    * Remove nodes that resolve to nothing when resolving
4257    */
4258   if (strcmp (period_node->xlink_href,
4259           "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4260     return NULL;
4261   }
4262
4263   if (!client->downloader) {
4264     return NULL;
4265   }
4266
4267   /* Build absolute URI */
4268
4269   /* Get base URI at the MPD level */
4270   base_uri =
4271       gst_uri_from_string (client->
4272       mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4273
4274   /* combine a BaseURL at the MPD level with the current base url */
4275   base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4276   uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
4277   if (query)
4278     gst_uri_set_query_string (uri, query);
4279   g_free (query);
4280   uri_string = gst_uri_to_string (uri);
4281   gst_uri_unref (base_uri);
4282   gst_uri_unref (uri);
4283
4284   download =
4285       gst_uri_downloader_fetch_uri (client->downloader,
4286       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4287   g_free (uri_string);
4288
4289   if (!download) {
4290     GST_ERROR ("Failed to download external Period node at '%s': %s",
4291         period_node->xlink_href, err->message);
4292     g_clear_error (&err);
4293     return NULL;
4294   }
4295
4296   period_buffer = gst_fragment_get_buffer (download);
4297   g_object_unref (download);
4298
4299   /* external xml could have multiple period without root xmlNode.
4300    * To avoid xml parsing error caused by no root node, wrapping it with
4301    * custom root node */
4302   adapter = gst_adapter_new ();
4303
4304   wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
4305   memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
4306   gst_adapter_push (adapter,
4307       gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
4308
4309   gst_adapter_push (adapter, period_buffer);
4310
4311   wrapper = g_strdup (CUSTOM_WRAPPER_END);
4312   gst_adapter_push (adapter,
4313       gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
4314
4315   data = gst_adapter_map (adapter, gst_adapter_available (adapter));
4316
4317   doc =
4318       xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL,
4319       XML_PARSE_NONET);
4320
4321   gst_adapter_unmap (adapter);
4322   gst_adapter_clear (adapter);
4323   gst_object_unref (adapter);
4324
4325   if (doc) {
4326     xmlNode *root_element = xmlDocGetRootElement (doc);
4327     xmlNode *iter;
4328
4329     if (root_element->type != XML_ELEMENT_NODE)
4330       goto error;
4331
4332     for (iter = root_element->children; iter; iter = iter->next) {
4333       if (iter->type == XML_ELEMENT_NODE) {
4334         if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
4335           gst_mpdparser_parse_period_node (&new_periods, iter);
4336         } else {
4337           goto error;
4338         }
4339       }
4340     }
4341   } else {
4342     goto error;
4343   }
4344
4345 done:
4346   if (doc)
4347     xmlFreeDoc (doc);
4348
4349   return new_periods;
4350
4351 error:
4352   GST_ERROR ("Failed to parse period node XML");
4353
4354   if (new_periods) {
4355     g_list_free_full (new_periods,
4356         (GDestroyNotify) gst_mpdparser_free_period_node);
4357     new_periods = NULL;
4358   }
4359   goto done;
4360 }
4361
4362 gboolean
4363 gst_mpd_client_setup_media_presentation (GstMpdClient * client,
4364     GstClockTime time, gint period_idx, const gchar * period_id)
4365 {
4366   GstStreamPeriod *stream_period;
4367   GstClockTime start, duration;
4368   GList *list, *next;
4369   guint idx;
4370   gboolean ret = FALSE;
4371
4372   g_return_val_if_fail (client != NULL, FALSE);
4373   g_return_val_if_fail (client->mpd_node != NULL, FALSE);
4374
4375   /* Check if we set up the media presentation far enough already */
4376   for (list = client->periods; list; list = list->next) {
4377     GstStreamPeriod *stream_period = list->data;
4378
4379     if ((time != GST_CLOCK_TIME_NONE
4380             && stream_period->duration != GST_CLOCK_TIME_NONE
4381             && stream_period->start + stream_period->duration >= time)
4382         || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4383       return TRUE;
4384
4385     if (period_idx != -1 && stream_period->number >= period_idx)
4386       return TRUE;
4387
4388     if (period_id != NULL && stream_period->period->id != NULL
4389         && strcmp (stream_period->period->id, period_id) == 0)
4390       return TRUE;
4391
4392   }
4393
4394   GST_DEBUG ("Building the list of Periods in the Media Presentation");
4395   /* clean the old period list, if any */
4396   /* TODO: In theory we could reuse the ones we have so far but that
4397    * seems more complicated than the overhead caused here
4398    */
4399   if (client->periods) {
4400     g_list_foreach (client->periods,
4401         (GFunc) gst_mpdparser_free_stream_period, NULL);
4402     g_list_free (client->periods);
4403     client->periods = NULL;
4404   }
4405
4406   idx = 0;
4407   start = 0;
4408   duration = GST_CLOCK_TIME_NONE;
4409
4410   if (client->mpd_node->mediaPresentationDuration <= 0 &&
4411       client->mpd_node->mediaPresentationDuration != -1) {
4412     /* Invalid MPD file: MPD duration is negative or zero */
4413     goto syntax_error;
4414   }
4415
4416   for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
4417     GstPeriodNode *period_node = list->data;
4418     GstPeriodNode *next_period_node = NULL;
4419
4420     /* Download external period */
4421     if (period_node->xlink_href) {
4422       GList *new_periods;
4423       GList *prev;
4424
4425       new_periods = gst_mpd_client_fetch_external_period (client, period_node);
4426
4427       prev = list->prev;
4428       client->mpd_node->Periods =
4429           g_list_delete_link (client->mpd_node->Periods, list);
4430       gst_mpdparser_free_period_node (period_node);
4431       period_node = NULL;
4432
4433       /* Get new next node, we will insert before this */
4434       if (prev)
4435         next = prev->next;
4436       else
4437         next = client->mpd_node->Periods;
4438
4439       while (new_periods) {
4440         client->mpd_node->Periods =
4441             g_list_insert_before (client->mpd_node->Periods, next,
4442             new_periods->data);
4443         new_periods = g_list_delete_link (new_periods, new_periods);
4444       }
4445       next = NULL;
4446
4447       /* Update our iterator to the first new period if any, or the next */
4448       if (prev)
4449         list = prev->next;
4450       else
4451         list = client->mpd_node->Periods;
4452
4453       /* And try again */
4454       continue;
4455     }
4456
4457     if (period_node->start != -1) {
4458       /* we have a regular period */
4459       /* start cannot be smaller than previous start */
4460       if (list != g_list_first (client->mpd_node->Periods)
4461           && start >= period_node->start * GST_MSECOND) {
4462         /* Invalid MPD file: duration would be negative or zero */
4463         goto syntax_error;
4464       }
4465       start = period_node->start * GST_MSECOND;
4466     } else if (duration != GST_CLOCK_TIME_NONE) {
4467       /* start time inferred from previous period, this is still a regular period */
4468       start += duration;
4469     } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
4470       /* first period of a static MPD file, start time is 0 */
4471       start = 0;
4472     } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4473       /* this should be a live stream, let this pass */
4474     } else {
4475       /* this is an 'Early Available Period' */
4476       goto early;
4477     }
4478
4479     /* compute duration.
4480        If there is a start time for the next period, or this is the last period
4481        and mediaPresentationDuration was set, those values will take precedence
4482        over a configured period duration in computing this period's duration
4483
4484        ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
4485        "The Period extends until the PeriodStart of the next Period, or until
4486        the end of the Media Presentation in the case of the last Period."
4487      */
4488
4489     while ((next = g_list_next (list)) != NULL) {
4490       /* try to infer this period duration from the start time of the next period */
4491       next_period_node = next->data;
4492
4493       if (next_period_node->xlink_href) {
4494         GList *new_periods;
4495
4496         new_periods =
4497             gst_mpd_client_fetch_external_period (client, next_period_node);
4498
4499         client->mpd_node->Periods =
4500             g_list_delete_link (client->mpd_node->Periods, next);
4501         gst_mpdparser_free_period_node (next_period_node);
4502         next_period_node = NULL;
4503         /* Get new next node, we will insert before this */
4504         next = g_list_next (list);
4505         while (new_periods) {
4506           client->mpd_node->Periods =
4507               g_list_insert_before (client->mpd_node->Periods, next,
4508               new_periods->data);
4509           new_periods = g_list_delete_link (new_periods, new_periods);
4510         }
4511
4512         /* And try again, getting the next list element which is now our newly
4513          * inserted nodes. If any */
4514       } else {
4515         /* Got the next period and it doesn't have to be downloaded first */
4516         break;
4517       }
4518     }
4519
4520     if (next_period_node) {
4521       if (next_period_node->start != -1) {
4522         if (start >= next_period_node->start * GST_MSECOND) {
4523           /* Invalid MPD file: duration would be negative or zero */
4524           goto syntax_error;
4525         }
4526         duration = next_period_node->start * GST_MSECOND - start;
4527       } else if (period_node->duration != -1) {
4528         if (period_node->duration <= 0) {
4529           /* Invalid MPD file: duration would be negative or zero */
4530           goto syntax_error;
4531         }
4532         duration = period_node->duration * GST_MSECOND;
4533       } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4534         /* might be a live file, ignore unspecified duration */
4535       } else {
4536         /* Invalid MPD file! */
4537         goto syntax_error;
4538       }
4539     } else if (client->mpd_node->mediaPresentationDuration != -1) {
4540       /* last Period of the Media Presentation */
4541       if (client->mpd_node->mediaPresentationDuration * GST_MSECOND <= start) {
4542         /* Invalid MPD file: duration would be negative or zero */
4543         goto syntax_error;
4544       }
4545       duration =
4546           client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
4547     } else if (period_node->duration != -1) {
4548       duration = period_node->duration * GST_MSECOND;
4549     } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4550       /* might be a live file, ignore unspecified duration */
4551     } else {
4552       /* Invalid MPD file! */
4553       goto syntax_error;
4554     }
4555
4556     stream_period = g_slice_new0 (GstStreamPeriod);
4557     client->periods = g_list_append (client->periods, stream_period);
4558     stream_period->period = period_node;
4559     stream_period->number = idx++;
4560     stream_period->start = start;
4561     stream_period->duration = duration;
4562     ret = TRUE;
4563     GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
4564         GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
4565
4566     if ((time != GST_CLOCK_TIME_NONE
4567             && stream_period->duration != GST_CLOCK_TIME_NONE
4568             && stream_period->start + stream_period->duration >= time)
4569         || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4570       break;
4571
4572     if (period_idx != -1 && stream_period->number >= period_idx)
4573       break;
4574
4575     if (period_id != NULL && stream_period->period->id != NULL
4576         && strcmp (stream_period->period->id, period_id) == 0)
4577       break;
4578
4579     list = list->next;
4580   }
4581
4582   GST_DEBUG
4583       ("Found a total of %d valid Periods in the Media Presentation up to this point",
4584       idx);
4585   return ret;
4586
4587 early:
4588   GST_WARNING
4589       ("Found an Early Available Period, skipping the rest of the Media Presentation");
4590   return ret;
4591
4592 syntax_error:
4593   GST_WARNING
4594       ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
4595       idx);
4596   return ret;
4597 }
4598
4599 static GList *
4600 gst_mpd_client_fetch_external_adaptation_set (GstMpdClient * client,
4601     GstPeriodNode * period, GstAdaptationSetNode * adapt_set)
4602 {
4603   GstFragment *download;
4604   GstBuffer *adapt_set_buffer;
4605   GstMapInfo map;
4606   GError *err = NULL;
4607   xmlDocPtr doc = NULL;
4608   GstUri *base_uri, *uri;
4609   gchar *query = NULL;
4610   gchar *uri_string;
4611   GList *new_adapt_sets = NULL;
4612
4613   /* ISO/IEC 23009-1:2014 5.5.3 4)
4614    * Remove nodes that resolve to nothing when resolving
4615    */
4616   if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4617     return NULL;
4618   }
4619
4620   if (!client->downloader) {
4621     return NULL;
4622   }
4623
4624   /* Build absolute URI */
4625
4626   /* Get base URI at the MPD level */
4627   base_uri =
4628       gst_uri_from_string (client->
4629       mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4630
4631   /* combine a BaseURL at the MPD level with the current base url */
4632   base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4633
4634   /* combine a BaseURL at the Period level with the current base url */
4635   base_uri = combine_urls (base_uri, period->BaseURLs, &query, 0);
4636
4637   uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
4638   if (query)
4639     gst_uri_set_query_string (uri, query);
4640   g_free (query);
4641   uri_string = gst_uri_to_string (uri);
4642   gst_uri_unref (base_uri);
4643   gst_uri_unref (uri);
4644
4645   download =
4646       gst_uri_downloader_fetch_uri (client->downloader,
4647       uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4648   g_free (uri_string);
4649
4650   if (!download) {
4651     GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
4652         adapt_set->xlink_href, err->message);
4653     g_clear_error (&err);
4654     return NULL;
4655   }
4656
4657   adapt_set_buffer = gst_fragment_get_buffer (download);
4658   g_object_unref (download);
4659
4660   gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
4661
4662   doc =
4663       xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
4664       XML_PARSE_NONET);
4665
4666   gst_buffer_unmap (adapt_set_buffer, &map);
4667   gst_buffer_unref (adapt_set_buffer);
4668
4669   /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
4670    * in external xml is allowed */
4671   if (doc) {
4672     xmlNode *root_element = xmlDocGetRootElement (doc);
4673
4674     if (root_element->type != XML_ELEMENT_NODE ||
4675         xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") != 0) {
4676       goto error;
4677     }
4678
4679     gst_mpdparser_parse_adaptation_set_node (&new_adapt_sets, root_element,
4680         period);
4681   } else {
4682     goto error;
4683   }
4684
4685 done:
4686   if (doc)
4687     xmlFreeDoc (doc);
4688
4689   return new_adapt_sets;
4690
4691 error:
4692   GST_ERROR ("Failed to parse adaptation set node XML");
4693   goto done;
4694 }
4695
4696 static GList *
4697 gst_mpd_client_get_adaptation_sets_for_period (GstMpdClient * client,
4698     GstStreamPeriod * period)
4699 {
4700   GList *list;
4701
4702   g_return_val_if_fail (period != NULL, NULL);
4703
4704   /* Resolve all external adaptation sets of this period. Every user of
4705    * the adaptation sets would need to know the content of all adaptation sets
4706    * to decide which one to use, so we have to resolve them all here
4707    */
4708   for (list = period->period->AdaptationSets; list;
4709       /* advanced explicitly below */ ) {
4710     GstAdaptationSetNode *adapt_set = (GstAdaptationSetNode *) list->data;
4711     GList *new_adapt_sets = NULL, *prev, *next;
4712
4713     if (!adapt_set->xlink_href) {
4714       list = list->next;
4715       continue;
4716     }
4717
4718     new_adapt_sets =
4719         gst_mpd_client_fetch_external_adaptation_set (client, period->period,
4720         adapt_set);
4721
4722     prev = list->prev;
4723     period->period->AdaptationSets =
4724         g_list_delete_link (period->period->AdaptationSets, list);
4725     gst_mpdparser_free_adaptation_set_node (adapt_set);
4726     adapt_set = NULL;
4727
4728     /* Get new next node, we will insert before this */
4729     if (prev)
4730       next = prev->next;
4731     else
4732       next = period->period->AdaptationSets;
4733
4734     while (new_adapt_sets) {
4735       period->period->AdaptationSets =
4736           g_list_insert_before (period->period->AdaptationSets, next,
4737           new_adapt_sets->data);
4738       new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
4739     }
4740
4741     /* Update our iterator to the first new adaptation set if any, or the next */
4742     if (prev)
4743       list = prev->next;
4744     else
4745       list = period->period->AdaptationSets;
4746   }
4747
4748   return period->period->AdaptationSets;
4749 }
4750
4751 GList *
4752 gst_mpd_client_get_adaptation_sets (GstMpdClient * client)
4753 {
4754   GstStreamPeriod *stream_period;
4755
4756   stream_period = gst_mpdparser_get_stream_period (client);
4757   if (stream_period == NULL || stream_period->period == NULL) {
4758     GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
4759     return NULL;
4760   }
4761
4762   return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
4763 }
4764
4765 gboolean
4766 gst_mpd_client_setup_streaming (GstMpdClient * client,
4767     GstAdaptationSetNode * adapt_set)
4768 {
4769   GstRepresentationNode *representation;
4770   GList *rep_list = NULL;
4771   GstActiveStream *stream;
4772
4773   rep_list = adapt_set->Representations;
4774   if (!rep_list) {
4775     GST_WARNING ("Can not retrieve any representation, aborting...");
4776     return FALSE;
4777   }
4778
4779   stream = g_slice_new0 (GstActiveStream);
4780   gst_mpdparser_init_active_stream_segments (stream);
4781
4782   stream->baseURL_idx = 0;
4783   stream->cur_adapt_set = adapt_set;
4784
4785   GST_DEBUG ("0. Current stream %p", stream);
4786
4787 #if 0
4788   /* fast start */
4789   representation =
4790       gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
4791       stream->max_bandwidth);
4792
4793   if (!representation) {
4794     GST_WARNING
4795         ("Can not retrieve a representation with the requested bandwidth");
4796     representation = gst_mpdparser_get_lowest_representation (rep_list);
4797   }
4798 #else
4799   /* slow start */
4800   representation = gst_mpdparser_get_lowest_representation (rep_list);
4801 #endif
4802
4803   if (!representation) {
4804     GST_WARNING ("No valid representation in the MPD file, aborting...");
4805     gst_mpdparser_free_active_stream (stream);
4806     return FALSE;
4807   }
4808   stream->mimeType =
4809       gst_mpdparser_representation_get_mimetype (adapt_set, representation);
4810   if (stream->mimeType == GST_STREAM_UNKNOWN) {
4811     GST_WARNING ("Unknown mime type in the representation, aborting...");
4812     gst_mpdparser_free_active_stream (stream);
4813     return FALSE;
4814   }
4815
4816   client->active_streams = g_list_append (client->active_streams, stream);
4817   if (!gst_mpd_client_setup_representation (client, stream, representation)) {
4818     GST_WARNING ("Failed to setup the representation, aborting...");
4819     return FALSE;
4820   }
4821
4822   GST_INFO ("Successfully setup the download pipeline for mimeType %d",
4823       stream->mimeType);
4824
4825   return TRUE;
4826 }
4827
4828 gboolean
4829 gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
4830     gboolean forward, GstSeekFlags flags, GstClockTime ts,
4831     GstClockTime * final_ts)
4832 {
4833   gint index = 0;
4834   gint repeat_index = 0;
4835   GstMediaSegment *selectedChunk = NULL;
4836
4837   g_return_val_if_fail (stream != NULL, 0);
4838
4839   if (stream->segments) {
4840     for (index = 0; index < stream->segments->len; index++) {
4841       gboolean in_segment = FALSE;
4842       GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
4843       GstClockTime end_time;
4844
4845       GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
4846           stream->segments->len);
4847
4848       end_time =
4849           gst_mpdparser_get_segment_end_time (client, stream->segments,
4850           segment, index);
4851
4852       /* avoid downloading another fragment just for 1ns in reverse mode */
4853       if (forward)
4854         in_segment = ts < end_time;
4855       else
4856         in_segment = ts <= end_time;
4857
4858       if (in_segment) {
4859         GstClockTime chunk_time;
4860
4861         selectedChunk = segment;
4862         repeat_index = (ts - segment->start) / segment->duration;
4863
4864         chunk_time = segment->start + segment->duration * repeat_index;
4865
4866         /* At the end of a segment in reverse mode, start from the previous fragment */
4867         if (!forward && repeat_index > 0
4868             && ((ts - segment->start) % segment->duration == 0))
4869           repeat_index--;
4870
4871         if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
4872           if (repeat_index + 1 < segment->repeat) {
4873             if (ts - chunk_time > chunk_time + segment->duration - ts)
4874               repeat_index++;
4875           } else if (index + 1 < stream->segments->len) {
4876             GstMediaSegment *next_segment =
4877                 g_ptr_array_index (stream->segments, index + 1);
4878
4879             if (ts - chunk_time > next_segment->start - ts) {
4880               repeat_index = 0;
4881               selectedChunk = next_segment;
4882               index++;
4883             }
4884           }
4885         } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
4886                 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
4887             ts != chunk_time) {
4888
4889           if (repeat_index + 1 < segment->repeat) {
4890             repeat_index++;
4891           } else {
4892             repeat_index = 0;
4893             if (index + 1 >= stream->segments->len) {
4894               selectedChunk = NULL;
4895             } else {
4896               selectedChunk = g_ptr_array_index (stream->segments, ++index);
4897             }
4898           }
4899         }
4900         break;
4901       }
4902     }
4903
4904     if (selectedChunk == NULL) {
4905       stream->segment_index = stream->segments->len;
4906       stream->segment_repeat_index = 0;
4907       GST_DEBUG ("Seek to after last segment");
4908       return FALSE;
4909     }
4910
4911     if (final_ts)
4912       *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
4913   } else {
4914     GstClockTime duration =
4915         gst_mpd_client_get_segment_duration (client, stream, NULL);
4916     GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
4917     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
4918     GstClockTime index_time;
4919
4920     g_return_val_if_fail (stream->cur_seg_template->
4921         MultSegBaseType->SegmentTimeline == NULL, FALSE);
4922     if (!GST_CLOCK_TIME_IS_VALID (duration)) {
4923       return FALSE;
4924     }
4925
4926     if (ts > stream_period->start)
4927       ts -= stream_period->start;
4928     else
4929       ts = 0;
4930
4931     index = ts / duration;
4932
4933     /* At the end of a segment in reverse mode, start from the previous fragment */
4934     if (!forward && index > 0 && ts % duration == 0)
4935       index--;
4936
4937     index_time = index * duration;
4938
4939     if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
4940       if (ts - index_time > index_time + duration - ts)
4941         index++;
4942     } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
4943             (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
4944         && ts != index_time) {
4945       index++;
4946     }
4947
4948     if (segments_count > 0 && index >= segments_count) {
4949       stream->segment_index = segments_count;
4950       stream->segment_repeat_index = 0;
4951       GST_DEBUG ("Seek to after last segment");
4952       return FALSE;
4953     }
4954     if (final_ts)
4955       *final_ts = index * duration;
4956   }
4957
4958   stream->segment_repeat_index = repeat_index;
4959   stream->segment_index = index;
4960
4961   return TRUE;
4962 }
4963
4964 gint64
4965 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
4966     const GstDateTime * t2)
4967 {
4968   GDateTime *gdt1, *gdt2;
4969   GTimeSpan diff;
4970
4971   g_assert (t1 != NULL && t2 != NULL);
4972   gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
4973   gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
4974   diff = g_date_time_difference (gdt2, gdt1);
4975   g_date_time_unref (gdt1);
4976   g_date_time_unref (gdt2);
4977   return diff * GST_USECOND;
4978 }
4979
4980 GstDateTime *
4981 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
4982 {
4983   GDateTime *gdt;
4984   GDateTime *gdt2;
4985   GstDateTime *rv;
4986
4987   g_assert (t1 != NULL);
4988   gdt = gst_date_time_to_g_date_time (t1);
4989   g_assert (gdt != NULL);
4990   gdt2 = g_date_time_add (gdt, usecs);
4991   g_assert (gdt2 != NULL);
4992   g_date_time_unref (gdt);
4993   rv = gst_date_time_new_from_g_date_time (gdt2);
4994
4995   /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
4996    * ownership of the GDateTime pointer.
4997    */
4998
4999   return rv;
5000 }
5001
5002 static GstDateTime *
5003 gst_mpd_client_get_availability_start_time (GstMpdClient * client)
5004 {
5005   GstDateTime *start_time;
5006
5007   if (client == NULL)
5008     return (GstDateTime *) NULL;
5009
5010   start_time = client->mpd_node->availabilityStartTime;
5011   if (start_time)
5012     gst_date_time_ref (start_time);
5013   return start_time;
5014 }
5015
5016 gboolean
5017 gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
5018     guint stream_idx, GstClockTime * ts)
5019 {
5020   GstActiveStream *stream;
5021   gint segment_idx;
5022   GstMediaSegment *currentChunk;
5023   GstStreamPeriod *stream_period;
5024
5025   GST_DEBUG ("Stream index: %i", stream_idx);
5026   stream = g_list_nth_data (client->active_streams, stream_idx);
5027   g_return_val_if_fail (stream != NULL, 0);
5028
5029   if (!stream->segments) {
5030     stream_period = gst_mpdparser_get_stream_period (client);
5031     *ts = stream_period->start + stream_period->duration;
5032   } else {
5033     segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
5034     if (segment_idx >= stream->segments->len) {
5035       GST_WARNING ("Segment index %d is outside of segment list of length %d",
5036           segment_idx, stream->segments->len);
5037       return FALSE;
5038     }
5039     currentChunk = g_ptr_array_index (stream->segments, segment_idx);
5040
5041     if (currentChunk->repeat >= 0) {
5042       *ts =
5043           currentChunk->start + (currentChunk->duration * (1 +
5044               currentChunk->repeat));
5045     } else {
5046       /* 5.3.9.6.1: negative repeat means repeat till the end of the
5047        * period, or the next update of the MPD (which I think is
5048        * implicit, as this will all get deleted/recreated), or the
5049        * start of the next segment, if any. */
5050       stream_period = gst_mpdparser_get_stream_period (client);
5051       *ts = stream_period->start + stream_period->duration;
5052     }
5053   }
5054
5055   return TRUE;
5056 }
5057
5058 gboolean
5059 gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
5060     guint stream_idx, GstClockTime * ts)
5061 {
5062   GstActiveStream *stream;
5063   GstMediaSegment *currentChunk;
5064
5065   GST_DEBUG ("Stream index: %i", stream_idx);
5066   stream = g_list_nth_data (client->active_streams, stream_idx);
5067   g_return_val_if_fail (stream != NULL, 0);
5068
5069   if (stream->segments) {
5070     GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5071         stream->segment_index, stream->segments->len);
5072     if (stream->segment_index >= stream->segments->len)
5073       return FALSE;
5074     currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5075
5076     *ts =
5077         currentChunk->start +
5078         (currentChunk->duration * stream->segment_repeat_index);
5079   } else {
5080     GstClockTime duration =
5081         gst_mpd_client_get_segment_duration (client, stream, NULL);
5082     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5083
5084     g_return_val_if_fail (stream->cur_seg_template->
5085         MultSegBaseType->SegmentTimeline == NULL, FALSE);
5086     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5087             && stream->segment_index >= segments_count)) {
5088       return FALSE;
5089     }
5090     *ts = stream->segment_index * duration;
5091   }
5092
5093   return TRUE;
5094 }
5095
5096 GstClockTime
5097 gst_mpd_parser_get_stream_presentation_offset (GstMpdClient * client,
5098     guint stream_idx)
5099 {
5100   GstActiveStream *stream = NULL;
5101
5102   g_return_val_if_fail (client != NULL, 0);
5103   g_return_val_if_fail (client->active_streams != NULL, 0);
5104   stream = g_list_nth_data (client->active_streams, stream_idx);
5105   g_return_val_if_fail (stream != NULL, 0);
5106
5107   return stream->presentationTimeOffset;
5108 }
5109
5110 GstClockTime
5111 gst_mpd_parser_get_period_start_time (GstMpdClient * client)
5112 {
5113   GstStreamPeriod *stream_period = NULL;
5114
5115   g_return_val_if_fail (client != NULL, 0);
5116   stream_period = gst_mpdparser_get_stream_period (client);
5117   g_return_val_if_fail (stream_period != NULL, 0);
5118
5119   return stream_period->start;
5120 }
5121
5122 /**
5123  * gst_mpd_client_get_utc_timing_sources:
5124  * @client: #GstMpdClient to check for UTCTiming elements
5125  * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
5126  *     to search for.
5127  * @selected_method: (nullable): The selected method
5128  * Returns: (transfer none): A NULL terminated array of URLs of servers
5129  *     that use @selected_method to provide a realtime clock.
5130  *
5131  * Searches the UTCTiming elements found in the manifest for an element
5132  * that uses one of the UTC timing methods specified in @selected_method.
5133  * If multiple UTCTiming elements are present that support one of the
5134  * methods specified in @selected_method, the first one is returned.
5135  *
5136  * Since: 1.6
5137  */
5138 gchar **
5139 gst_mpd_client_get_utc_timing_sources (GstMpdClient * client,
5140     guint methods, GstMPDUTCTimingType * selected_method)
5141 {
5142   GList *list;
5143
5144   g_return_val_if_fail (client != NULL, NULL);
5145   g_return_val_if_fail (client->mpd_node != NULL, NULL);
5146   for (list = g_list_first (client->mpd_node->UTCTiming); list;
5147       list = g_list_next (list)) {
5148     const GstUTCTimingNode *node = (const GstUTCTimingNode *) list->data;
5149     if (node->method & methods) {
5150       if (selected_method) {
5151         *selected_method = node->method;
5152       }
5153       return node->urls;
5154     }
5155   }
5156   return NULL;
5157 }
5158
5159 gboolean
5160 gst_mpd_client_get_next_fragment (GstMpdClient * client,
5161     guint indexStream, GstMediaFragmentInfo * fragment)
5162 {
5163   GstActiveStream *stream = NULL;
5164   GstMediaSegment *currentChunk;
5165   gchar *mediaURL = NULL;
5166   gchar *indexURL = NULL;
5167   GstUri *base_url, *frag_url;
5168
5169   /* select stream */
5170   g_return_val_if_fail (client != NULL, FALSE);
5171   g_return_val_if_fail (client->active_streams != NULL, FALSE);
5172   stream = g_list_nth_data (client->active_streams, indexStream);
5173   g_return_val_if_fail (stream != NULL, FALSE);
5174   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5175
5176   if (stream->segments) {
5177     GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5178         stream->segment_index, stream->segments->len);
5179     if (stream->segment_index >= stream->segments->len)
5180       return FALSE;
5181   } else {
5182     GstClockTime duration = gst_mpd_client_get_segment_duration (client,
5183         stream, NULL);
5184     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5185
5186     g_return_val_if_fail (stream->cur_seg_template->
5187         MultSegBaseType->SegmentTimeline == NULL, FALSE);
5188     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5189             && stream->segment_index >= segments_count)) {
5190       return FALSE;
5191     }
5192     fragment->duration = duration;
5193   }
5194
5195   /* FIXME rework discont checking */
5196   /* fragment->discontinuity = segment_idx != currentChunk.number; */
5197   fragment->range_start = 0;
5198   fragment->range_end = -1;
5199   fragment->index_uri = NULL;
5200   fragment->index_range_start = 0;
5201   fragment->index_range_end = -1;
5202
5203   if (stream->segments) {
5204     currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5205
5206     GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
5207     if (currentChunk->SegmentURL != NULL) {
5208       mediaURL =
5209           g_strdup (gst_mpdparser_get_mediaURL (stream,
5210               currentChunk->SegmentURL));
5211       indexURL = g_strdup (currentChunk->SegmentURL->index);
5212     } else if (stream->cur_seg_template != NULL) {
5213       mediaURL =
5214           gst_mpdparser_build_URL_from_template (stream->
5215           cur_seg_template->media, stream->cur_representation->id,
5216           currentChunk->number + stream->segment_repeat_index,
5217           stream->cur_representation->bandwidth,
5218           currentChunk->scale_start +
5219           stream->segment_repeat_index * currentChunk->scale_duration);
5220       if (stream->cur_seg_template->index) {
5221         indexURL =
5222             gst_mpdparser_build_URL_from_template (stream->
5223             cur_seg_template->index, stream->cur_representation->id,
5224             currentChunk->number + stream->segment_repeat_index,
5225             stream->cur_representation->bandwidth,
5226             currentChunk->scale_start +
5227             stream->segment_repeat_index * currentChunk->scale_duration);
5228       }
5229     }
5230     GST_DEBUG ("mediaURL = %s", mediaURL);
5231     GST_DEBUG ("indexURL = %s", indexURL);
5232
5233     fragment->timestamp =
5234         currentChunk->start +
5235         stream->segment_repeat_index * currentChunk->duration;
5236     fragment->duration = currentChunk->duration;
5237     if (currentChunk->SegmentURL) {
5238       if (currentChunk->SegmentURL->mediaRange) {
5239         fragment->range_start =
5240             currentChunk->SegmentURL->mediaRange->first_byte_pos;
5241         fragment->range_end =
5242             currentChunk->SegmentURL->mediaRange->last_byte_pos;
5243       }
5244       if (currentChunk->SegmentURL->indexRange) {
5245         fragment->index_range_start =
5246             currentChunk->SegmentURL->indexRange->first_byte_pos;
5247         fragment->index_range_end =
5248             currentChunk->SegmentURL->indexRange->last_byte_pos;
5249       }
5250     }
5251   } else {
5252     if (stream->cur_seg_template != NULL) {
5253       mediaURL =
5254           gst_mpdparser_build_URL_from_template (stream->
5255           cur_seg_template->media, stream->cur_representation->id,
5256           stream->segment_index +
5257           stream->cur_seg_template->MultSegBaseType->startNumber,
5258           stream->cur_representation->bandwidth,
5259           stream->segment_index * fragment->duration);
5260       if (stream->cur_seg_template->index) {
5261         indexURL =
5262             gst_mpdparser_build_URL_from_template (stream->
5263             cur_seg_template->index, stream->cur_representation->id,
5264             stream->segment_index +
5265             stream->cur_seg_template->MultSegBaseType->startNumber,
5266             stream->cur_representation->bandwidth,
5267             stream->segment_index * fragment->duration);
5268       }
5269     } else {
5270       return FALSE;
5271     }
5272
5273     GST_DEBUG ("mediaURL = %s", mediaURL);
5274     GST_DEBUG ("indexURL = %s", indexURL);
5275
5276     fragment->timestamp = stream->segment_index * fragment->duration;
5277   }
5278
5279   base_url = gst_uri_from_string (stream->baseURL);
5280   frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
5281   g_free (mediaURL);
5282   if (stream->queryURL) {
5283     frag_url = gst_uri_make_writable (frag_url);
5284     gst_uri_set_query_string (frag_url, stream->queryURL);
5285   }
5286   fragment->uri = gst_uri_to_string (frag_url);
5287   gst_uri_unref (frag_url);
5288
5289   if (indexURL != NULL) {
5290     frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
5291             indexURL));
5292     gst_uri_set_query_string (frag_url, stream->queryURL);
5293     fragment->index_uri = gst_uri_to_string (frag_url);
5294     gst_uri_unref (frag_url);
5295     g_free (indexURL);
5296   } else if (indexURL == NULL && (fragment->index_range_start
5297           || fragment->index_range_end != -1)) {
5298     /* index has no specific URL but has a range, we should only use this if
5299      * the media also has a range, otherwise we are serving some data twice
5300      * (in the media fragment and again in the index) */
5301     if (!(fragment->range_start || fragment->range_end != -1)) {
5302       GST_WARNING ("Ignoring index ranges because there isn't a media range "
5303           "and URIs would be the same");
5304       /* removing index information */
5305       fragment->index_range_start = 0;
5306       fragment->index_range_end = -1;
5307     }
5308   }
5309
5310   gst_uri_unref (base_url);
5311
5312   GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
5313
5314   return TRUE;
5315 }
5316
5317 gboolean
5318 gst_mpd_client_has_next_segment (GstMpdClient * client,
5319     GstActiveStream * stream, gboolean forward)
5320 {
5321   if (forward) {
5322     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5323
5324     if (segments_count > 0 && stream->segments
5325         && stream->segment_index + 1 == segments_count) {
5326       GstMediaSegment *segment;
5327
5328       segment = g_ptr_array_index (stream->segments, stream->segment_index);
5329       if (segment->repeat >= 0
5330           && stream->segment_repeat_index >= segment->repeat)
5331         return FALSE;
5332     } else if (segments_count > 0
5333         && stream->segment_index + 1 >= segments_count) {
5334       return FALSE;
5335     }
5336   } else {
5337     if (stream->segment_index < 0)
5338       return FALSE;
5339   }
5340
5341   return TRUE;
5342 }
5343
5344 GstFlowReturn
5345 gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream,
5346     gboolean forward)
5347 {
5348   GstMediaSegment *segment;
5349   GstFlowReturn ret = GST_FLOW_OK;
5350   guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5351
5352   GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
5353       segments_count, stream->segment_repeat_index);
5354
5355   /* handle special cases first */
5356   if (forward) {
5357     if (segments_count > 0 && stream->segment_index >= segments_count) {
5358       ret = GST_FLOW_EOS;
5359       goto done;
5360     }
5361
5362     if (stream->segments == NULL) {
5363       if (stream->segment_index < 0) {
5364         stream->segment_index = 0;
5365       } else {
5366         stream->segment_index++;
5367         if (segments_count > 0 && stream->segment_index >= segments_count) {
5368           ret = GST_FLOW_EOS;
5369         }
5370       }
5371       goto done;
5372     }
5373
5374     /* special case for when playback direction is reverted right at *
5375      * the end of the segment list */
5376     if (stream->segment_index < 0) {
5377       stream->segment_index = 0;
5378       goto done;
5379     }
5380   } else {
5381     if (stream->segments == NULL)
5382       stream->segment_index--;
5383     if (stream->segment_index < 0) {
5384       stream->segment_index = -1;
5385       ret = GST_FLOW_EOS;
5386       goto done;
5387     }
5388     if (stream->segments == NULL)
5389       goto done;
5390
5391     /* special case for when playback direction is reverted right at *
5392      * the end of the segment list */
5393     if (stream->segment_index >= segments_count) {
5394       stream->segment_index = segments_count - 1;
5395       segment = g_ptr_array_index (stream->segments, stream->segment_index);
5396       if (segment->repeat >= 0) {
5397         stream->segment_repeat_index = segment->repeat;
5398       } else {
5399         GstClockTime start = segment->start;
5400         GstClockTime end =
5401             gst_mpdparser_get_segment_end_time (client, stream->segments,
5402             segment,
5403             stream->segment_index);
5404         stream->segment_repeat_index =
5405             (guint) (end - start) / segment->duration;
5406       }
5407       goto done;
5408     }
5409   }
5410
5411   /* for the normal cases we can get the segment safely here */
5412   segment = g_ptr_array_index (stream->segments, stream->segment_index);
5413   if (forward) {
5414     if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
5415       stream->segment_repeat_index = 0;
5416       stream->segment_index++;
5417       if (segments_count > 0 && stream->segment_index >= segments_count) {
5418         ret = GST_FLOW_EOS;
5419         goto done;
5420       }
5421     } else {
5422       stream->segment_repeat_index++;
5423     }
5424   } else {
5425     if (stream->segment_repeat_index == 0) {
5426       stream->segment_index--;
5427       if (stream->segment_index < 0) {
5428         ret = GST_FLOW_EOS;
5429         goto done;
5430       }
5431
5432       segment = g_ptr_array_index (stream->segments, stream->segment_index);
5433       /* negative repeats only seem to make sense at the end of a list,
5434        * so this one will probably not be. Needs some sanity checking
5435        * when loading the XML data. */
5436       if (segment->repeat >= 0) {
5437         stream->segment_repeat_index = segment->repeat;
5438       } else {
5439         GstClockTime start = segment->start;
5440         GstClockTime end =
5441             gst_mpdparser_get_segment_end_time (client, stream->segments,
5442             segment,
5443             stream->segment_index);
5444         stream->segment_repeat_index =
5445             (guint) (end - start) / segment->duration;
5446       }
5447     } else {
5448       stream->segment_repeat_index--;
5449     }
5450   }
5451
5452 done:
5453   GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
5454       stream->segment_index, segments_count,
5455       stream->segment_repeat_index, gst_flow_get_name (ret));
5456   return ret;
5457 }
5458
5459 gboolean
5460 gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
5461     guint stream_idx, gint64 * range_start, gint64 * range_end)
5462 {
5463   GstActiveStream *stream;
5464   GstStreamPeriod *stream_period;
5465
5466   stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5467   g_return_val_if_fail (stream != NULL, FALSE);
5468   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5469   stream_period = gst_mpdparser_get_stream_period (client);
5470   g_return_val_if_fail (stream_period != NULL, FALSE);
5471   g_return_val_if_fail (stream_period->period != NULL, FALSE);
5472
5473   *range_start = 0;
5474   *range_end = -1;
5475
5476   GST_DEBUG ("Looking for current representation header");
5477   *uri = NULL;
5478   if (stream->cur_segment_base) {
5479     if (stream->cur_segment_base->Initialization) {
5480       *uri =
5481           g_strdup (gst_mpdparser_get_initializationURL (stream,
5482               stream->cur_segment_base->Initialization));
5483       if (stream->cur_segment_base->Initialization->range) {
5484         *range_start =
5485             stream->cur_segment_base->Initialization->range->first_byte_pos;
5486         *range_end =
5487             stream->cur_segment_base->Initialization->range->last_byte_pos;
5488       }
5489     } else if (stream->cur_segment_base->indexRange) {
5490       *uri =
5491           g_strdup (gst_mpdparser_get_initializationURL (stream,
5492               stream->cur_segment_base->Initialization));
5493       *range_start = 0;
5494       *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
5495     }
5496   } else if (stream->cur_seg_template
5497       && stream->cur_seg_template->initialization) {
5498     *uri =
5499         gst_mpdparser_build_URL_from_template (stream->
5500         cur_seg_template->initialization, stream->cur_representation->id, 0,
5501         stream->cur_representation->bandwidth, 0);
5502   }
5503
5504   return *uri == NULL ? FALSE : TRUE;
5505 }
5506
5507 gboolean
5508 gst_mpd_client_get_next_header_index (GstMpdClient * client, gchar ** uri,
5509     guint stream_idx, gint64 * range_start, gint64 * range_end)
5510 {
5511   GstActiveStream *stream;
5512   GstStreamPeriod *stream_period;
5513
5514   stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5515   g_return_val_if_fail (stream != NULL, FALSE);
5516   g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5517   stream_period = gst_mpdparser_get_stream_period (client);
5518   g_return_val_if_fail (stream_period != NULL, FALSE);
5519   g_return_val_if_fail (stream_period->period != NULL, FALSE);
5520
5521   *range_start = 0;
5522   *range_end = -1;
5523
5524   GST_DEBUG ("Looking for current representation index");
5525   *uri = NULL;
5526   if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
5527     *uri =
5528         g_strdup (gst_mpdparser_get_initializationURL (stream,
5529             stream->cur_segment_base->RepresentationIndex));
5530     *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
5531     *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
5532   } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
5533     *uri =
5534         gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
5535         stream->cur_representation->id, 0,
5536         stream->cur_representation->bandwidth, 0);
5537   }
5538
5539   return *uri == NULL ? FALSE : TRUE;
5540 }
5541
5542 GstClockTime
5543 gst_mpd_client_get_next_fragment_duration (GstMpdClient * client,
5544     GstActiveStream * stream)
5545 {
5546   GstMediaSegment *media_segment = NULL;
5547   gint seg_idx;
5548
5549   g_return_val_if_fail (stream != NULL, 0);
5550
5551   seg_idx = stream->segment_index;
5552
5553   if (stream->segments) {
5554     if (seg_idx < stream->segments->len && seg_idx >= 0)
5555       media_segment = g_ptr_array_index (stream->segments, seg_idx);
5556
5557     return media_segment == NULL ? 0 : media_segment->duration;
5558   } else {
5559     GstClockTime duration =
5560         gst_mpd_client_get_segment_duration (client, stream, NULL);
5561     guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5562
5563     g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5564         SegmentTimeline == NULL, 0);
5565
5566     if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5567             && seg_idx >= segments_count)) {
5568       return 0;
5569     }
5570     return duration;
5571   }
5572 }
5573
5574 GstClockTime
5575 gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
5576 {
5577   GstClockTime duration;
5578
5579   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
5580
5581   if (client->mpd_node->mediaPresentationDuration != -1) {
5582     duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
5583   } else {
5584     /* We can only get the duration for on-demand streams */
5585     duration = GST_CLOCK_TIME_NONE;
5586   }
5587
5588   return duration;
5589 }
5590
5591 gboolean
5592 gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
5593 {
5594   GstStreamPeriod *next_stream_period;
5595   gboolean ret = FALSE;
5596   GList *iter;
5597   guint period_idx;
5598
5599   g_return_val_if_fail (client != NULL, FALSE);
5600   g_return_val_if_fail (client->periods != NULL, FALSE);
5601   g_return_val_if_fail (period_id != NULL, FALSE);
5602
5603   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
5604           period_id))
5605     return FALSE;
5606
5607   for (period_idx = 0, iter = client->periods; iter;
5608       period_idx++, iter = g_list_next (iter)) {
5609     next_stream_period = iter->data;
5610
5611     if (next_stream_period->period->id
5612         && strcmp (next_stream_period->period->id, period_id) == 0) {
5613       ret = TRUE;
5614       client->period_idx = period_idx;
5615       break;
5616     }
5617   }
5618
5619   return ret;
5620 }
5621
5622 gboolean
5623 gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
5624 {
5625   GstStreamPeriod *next_stream_period;
5626   gboolean ret = FALSE;
5627
5628   g_return_val_if_fail (client != NULL, FALSE);
5629   g_return_val_if_fail (client->periods != NULL, FALSE);
5630
5631   if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
5632     return FALSE;
5633
5634   next_stream_period = g_list_nth_data (client->periods, period_idx);
5635   if (next_stream_period != NULL) {
5636     client->period_idx = period_idx;
5637     ret = TRUE;
5638   }
5639
5640   return ret;
5641 }
5642
5643 guint
5644 gst_mpd_client_get_period_index (GstMpdClient * client)
5645 {
5646   guint period_idx;
5647
5648   g_return_val_if_fail (client != NULL, 0);
5649   period_idx = client->period_idx;
5650
5651   return period_idx;
5652 }
5653
5654 const gchar *
5655 gst_mpd_client_get_period_id (GstMpdClient * client)
5656 {
5657   GstStreamPeriod *period;
5658   gchar *period_id = NULL;
5659
5660   g_return_val_if_fail (client != NULL, 0);
5661   period = g_list_nth_data (client->periods, client->period_idx);
5662   if (period && period->period)
5663     period_id = period->period->id;
5664
5665   return period_id;
5666 }
5667
5668 gboolean
5669 gst_mpd_client_has_previous_period (GstMpdClient * client)
5670 {
5671   GList *next_stream_period;
5672   g_return_val_if_fail (client != NULL, FALSE);
5673   g_return_val_if_fail (client->periods != NULL, FALSE);
5674
5675   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5676           client->period_idx - 1, NULL))
5677     return FALSE;
5678
5679   next_stream_period =
5680       g_list_nth_data (client->periods, client->period_idx - 1);
5681
5682   return next_stream_period != NULL;
5683 }
5684
5685 gboolean
5686 gst_mpd_client_has_next_period (GstMpdClient * client)
5687 {
5688   GList *next_stream_period;
5689   g_return_val_if_fail (client != NULL, FALSE);
5690   g_return_val_if_fail (client->periods != NULL, FALSE);
5691
5692   if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5693           client->period_idx + 1, NULL))
5694     return FALSE;
5695
5696   next_stream_period =
5697       g_list_nth_data (client->periods, client->period_idx + 1);
5698   return next_stream_period != NULL;
5699 }
5700
5701 void
5702 gst_mpd_client_seek_to_first_segment (GstMpdClient * client)
5703 {
5704   GList *list;
5705
5706   g_return_if_fail (client != NULL);
5707   g_return_if_fail (client->active_streams != NULL);
5708
5709   for (list = g_list_first (client->active_streams); list;
5710       list = g_list_next (list)) {
5711     GstActiveStream *stream = (GstActiveStream *) list->data;
5712     if (stream) {
5713       stream->segment_index = 0;
5714       stream->segment_repeat_index = 0;
5715     }
5716   }
5717 }
5718
5719 static guint
5720 gst_mpd_client_get_segments_counts (GstMpdClient * client,
5721     GstActiveStream * stream)
5722 {
5723   GstStreamPeriod *stream_period;
5724
5725   g_return_val_if_fail (stream != NULL, 0);
5726
5727   if (stream->segments)
5728     return stream->segments->len;
5729   g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5730       SegmentTimeline == NULL, 0);
5731
5732   stream_period = gst_mpdparser_get_stream_period (client);
5733   if (stream_period->duration != -1)
5734     return gst_util_uint64_scale_ceil (stream_period->duration, 1,
5735         gst_mpd_client_get_segment_duration (client, stream, NULL));
5736
5737   return 0;
5738 }
5739
5740 gboolean
5741 gst_mpd_client_is_live (GstMpdClient * client)
5742 {
5743   g_return_val_if_fail (client != NULL, FALSE);
5744   g_return_val_if_fail (client->mpd_node != NULL, FALSE);
5745
5746   return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
5747 }
5748
5749 guint
5750 gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
5751 {
5752   g_return_val_if_fail (client != NULL, 0);
5753
5754   return g_list_length (client->active_streams);
5755 }
5756
5757 guint
5758 gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
5759 {
5760   GstStreamPeriod *stream_period;
5761
5762   stream_period = gst_mpdparser_get_stream_period (client);
5763   g_return_val_if_fail (stream_period != NULL, 0);
5764   g_return_val_if_fail (stream_period->period != NULL, 0);
5765
5766   return g_list_length (stream_period->period->AdaptationSets);
5767 }
5768
5769 GstActiveStream *
5770 gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
5771     guint stream_idx)
5772 {
5773   g_return_val_if_fail (client != NULL, NULL);
5774   g_return_val_if_fail (client->active_streams != NULL, NULL);
5775
5776   return g_list_nth_data (client->active_streams, stream_idx);
5777 }
5778
5779 gboolean
5780 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
5781 {
5782   const gchar *mimeType;
5783   const gchar *adapt_set_codecs;
5784   const gchar *rep_codecs;
5785
5786   mimeType = stream->cur_representation->RepresentationBase->mimeType;
5787   if (!mimeType)
5788     mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5789
5790   if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
5791       g_strcmp0 (mimeType, "text/vtt") == 0)
5792     return TRUE;
5793
5794   adapt_set_codecs = stream->cur_adapt_set->RepresentationBase->codecs;
5795   rep_codecs = stream->cur_representation->RepresentationBase->codecs;
5796
5797   return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
5798       || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
5799 }
5800
5801 static const gchar *
5802 gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
5803 {
5804   if (mimeType == NULL)
5805     return NULL;
5806   if (strcmp (mimeType, "video/mp2t") == 0) {
5807     return "video/mpegts, systemstream=(bool) true";
5808   } else if (strcmp (mimeType, "video/mp4") == 0) {
5809     return "video/quicktime";
5810   } else if (strcmp (mimeType, "audio/mp4") == 0) {
5811     return "audio/x-m4a";
5812   } else if (strcmp (mimeType, "text/vtt") == 0) {
5813     return "application/x-subtitle-vtt";
5814   } else
5815     return mimeType;
5816 }
5817
5818 GstCaps *
5819 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
5820 {
5821   const gchar *mimeType, *caps_string;
5822   GstCaps *ret = NULL;
5823
5824   if (stream == NULL || stream->cur_adapt_set == NULL
5825       || stream->cur_representation == NULL)
5826     return NULL;
5827
5828   mimeType = stream->cur_representation->RepresentationBase->mimeType;
5829   if (mimeType == NULL) {
5830     mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5831   }
5832
5833   caps_string = gst_mpdparser_mimetype_to_caps (mimeType);
5834
5835   if ((g_strcmp0 (caps_string, "application/mp4") == 0)
5836       && gst_mpd_client_active_stream_contains_subtitles (stream))
5837     caps_string = "video/quicktime";
5838
5839   if (caps_string)
5840     ret = gst_caps_from_string (caps_string);
5841
5842   return ret;
5843 }
5844
5845 gboolean
5846 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
5847 {
5848   if (stream == NULL || stream->cur_adapt_set == NULL)
5849     return FALSE;
5850
5851   return stream->cur_adapt_set->bitstreamSwitching;
5852 }
5853
5854 guint
5855 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
5856 {
5857   guint width;
5858
5859   if (stream == NULL || stream->cur_adapt_set == NULL
5860       || stream->cur_representation == NULL)
5861     return 0;
5862
5863   width = stream->cur_representation->RepresentationBase->width;
5864   if (width == 0) {
5865     width = stream->cur_adapt_set->RepresentationBase->width;
5866   }
5867
5868   return width;
5869 }
5870
5871 guint
5872 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
5873 {
5874   guint height;
5875
5876   if (stream == NULL || stream->cur_adapt_set == NULL
5877       || stream->cur_representation == NULL)
5878     return 0;
5879
5880   height = stream->cur_representation->RepresentationBase->height;
5881   if (height == 0) {
5882     height = stream->cur_adapt_set->RepresentationBase->height;
5883   }
5884
5885   return height;
5886 }
5887
5888 gboolean
5889 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
5890     gint * fps_num, gint * fps_den)
5891 {
5892   if (stream == NULL)
5893     return FALSE;
5894
5895   if (stream->cur_adapt_set &&
5896       stream->cur_adapt_set->RepresentationBase->frameRate != NULL) {
5897     *fps_num = stream->cur_adapt_set->RepresentationBase->frameRate->num;
5898     *fps_den = stream->cur_adapt_set->RepresentationBase->frameRate->den;
5899     return TRUE;
5900   }
5901
5902   if (stream->cur_adapt_set &&
5903       stream->cur_adapt_set->RepresentationBase->maxFrameRate != NULL) {
5904     *fps_num = stream->cur_adapt_set->RepresentationBase->maxFrameRate->num;
5905     *fps_den = stream->cur_adapt_set->RepresentationBase->maxFrameRate->den;
5906     return TRUE;
5907   }
5908
5909   if (stream->cur_representation &&
5910       stream->cur_representation->RepresentationBase->frameRate != NULL) {
5911     *fps_num = stream->cur_representation->RepresentationBase->frameRate->num;
5912     *fps_den = stream->cur_representation->RepresentationBase->frameRate->den;
5913     return TRUE;
5914   }
5915
5916   if (stream->cur_representation &&
5917       stream->cur_representation->RepresentationBase->maxFrameRate != NULL) {
5918     *fps_num =
5919         stream->cur_representation->RepresentationBase->maxFrameRate->num;
5920     *fps_den =
5921         stream->cur_representation->RepresentationBase->maxFrameRate->den;
5922     return TRUE;
5923   }
5924
5925   return FALSE;
5926 }
5927
5928 guint
5929 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
5930 {
5931   const gchar *rate;
5932
5933   if (stream == NULL || stream->cur_adapt_set == NULL
5934       || stream->cur_representation == NULL)
5935     return 0;
5936
5937   rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
5938   if (rate == NULL) {
5939     rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
5940   }
5941
5942   return rate ? atoi (rate) : 0;
5943 }
5944
5945 guint
5946 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
5947 {
5948   if (stream == NULL || stream->cur_adapt_set == NULL
5949       || stream->cur_representation == NULL)
5950     return 0;
5951   /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
5952   return 0;
5953 }
5954
5955 guint
5956 gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
5957     GList ** lang)
5958 {
5959   GstStreamPeriod *stream_period;
5960   GstAdaptationSetNode *adapt_set;
5961   GList *adaptation_sets, *list;
5962   const gchar *this_mimeType = "audio";
5963   gchar *mimeType = NULL;
5964   guint nb_adaptation_set = 0;
5965
5966   stream_period = gst_mpdparser_get_stream_period (client);
5967   g_return_val_if_fail (stream_period != NULL, 0);
5968   g_return_val_if_fail (stream_period->period != NULL, 0);
5969
5970   adaptation_sets =
5971       gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
5972   for (list = adaptation_sets; list; list = g_list_next (list)) {
5973     adapt_set = (GstAdaptationSetNode *) list->data;
5974     if (adapt_set && adapt_set->lang) {
5975       gchar *this_lang = adapt_set->lang;
5976       GstRepresentationNode *rep;
5977       rep =
5978           gst_mpdparser_get_lowest_representation (adapt_set->Representations);
5979       mimeType = NULL;
5980       if (rep->RepresentationBase)
5981         mimeType = rep->RepresentationBase->mimeType;
5982       if (!mimeType && adapt_set->RepresentationBase) {
5983         mimeType = adapt_set->RepresentationBase->mimeType;
5984       }
5985
5986       if (strncmp_ext (mimeType, this_mimeType) == 0) {
5987         nb_adaptation_set++;
5988         *lang = g_list_append (*lang, this_lang);
5989       }
5990     }
5991   }
5992
5993   return nb_adaptation_set;
5994 }
5995
5996
5997 GstDateTime *
5998 gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
5999     GstActiveStream * stream)
6000 {
6001   GstDateTime *availability_start_time, *rv;
6002   gint seg_idx;
6003   GstMediaSegment *segment;
6004   GstClockTime segmentEndTime;
6005   const GstStreamPeriod *stream_period;
6006   GstClockTime period_start = 0;
6007
6008   g_return_val_if_fail (client != NULL, NULL);
6009   g_return_val_if_fail (stream != NULL, NULL);
6010
6011   stream_period = gst_mpdparser_get_stream_period (client);
6012   if (stream_period && stream_period->period) {
6013     period_start = stream_period->start;
6014   }
6015
6016   seg_idx = stream->segment_index;
6017
6018   if (stream->segments) {
6019     segment = g_ptr_array_index (stream->segments, seg_idx);
6020
6021     if (segment->repeat >= 0) {
6022       segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
6023           segment->duration;
6024     } else if (seg_idx < stream->segments->len - 1) {
6025       const GstMediaSegment *next_segment =
6026           g_ptr_array_index (stream->segments, seg_idx + 1);
6027       segmentEndTime = next_segment->start;
6028     } else {
6029       g_return_val_if_fail (stream_period != NULL, NULL);
6030       segmentEndTime = period_start + stream_period->duration;
6031     }
6032   } else {
6033     GstClockTime seg_duration;
6034     seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
6035     if (seg_duration == 0)
6036       return NULL;
6037     segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
6038   }
6039
6040   availability_start_time = gst_mpd_client_get_availability_start_time (client);
6041   if (availability_start_time == NULL) {
6042     GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
6043     return NULL;
6044   }
6045
6046   rv = gst_mpd_client_add_time_difference (availability_start_time,
6047       segmentEndTime / GST_USECOND);
6048   gst_date_time_unref (availability_start_time);
6049   if (rv == NULL) {
6050     GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
6051     return NULL;
6052   }
6053
6054   return rv;
6055 }
6056
6057 gboolean
6058 gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
6059 {
6060   GDateTime *start;
6061   GTimeSpan ts_microseconds;
6062   GstClockTime ts;
6063   gboolean ret = TRUE;
6064   GList *stream;
6065
6066   g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
6067   g_return_val_if_fail (client->mpd_node->availabilityStartTime != NULL, FALSE);
6068
6069   start =
6070       gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
6071
6072   ts_microseconds = g_date_time_difference (time, start);
6073   g_date_time_unref (start);
6074
6075   /* Clamp to availability start time, otherwise calculations wrap around */
6076   if (ts_microseconds < 0)
6077     ts_microseconds = 0;
6078
6079   ts = ts_microseconds * GST_USECOND;
6080   for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6081     ret =
6082         ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
6083         NULL);
6084   }
6085   return ret;
6086 }
6087
6088 void
6089 gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
6090 {
6091   g_free (fragment->uri);
6092   g_free (fragment->index_uri);
6093 }
6094
6095 gboolean
6096 gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient * client)
6097 {
6098   return client->profile_isoff_ondemand;
6099 }
6100
6101 /**
6102  * gst_mpd_client_parse_default_presentation_delay:
6103  * @client: #GstMpdClient that has a parsed manifest
6104  * @default_presentation_delay: A string that specifies a time period
6105  * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
6106  * ("12000 ms")
6107  * Returns: the parsed string in milliseconds
6108  *
6109  * Since: 1.6
6110  */
6111 gint64
6112 gst_mpd_client_parse_default_presentation_delay (GstMpdClient * client,
6113     const gchar * default_presentation_delay)
6114 {
6115   gint64 value;
6116   char *endptr = NULL;
6117
6118   g_return_val_if_fail (client != NULL, 0);
6119   g_return_val_if_fail (default_presentation_delay != NULL, 0);
6120   value = strtol (default_presentation_delay, &endptr, 10);
6121   if (endptr == default_presentation_delay || value == 0) {
6122     return 0;
6123   }
6124   while (*endptr == ' ')
6125     endptr++;
6126   if (*endptr == 's' || *endptr == 'S') {
6127     value *= 1000;              /* convert to ms */
6128   } else if (*endptr == 'f' || *endptr == 'F') {
6129     gint64 segment_duration;
6130     g_assert (client->mpd_node != NULL);
6131     segment_duration = client->mpd_node->maxSegmentDuration;
6132     value *= segment_duration;
6133   } else if (*endptr != 'm' && *endptr != 'M') {
6134     GST_ERROR ("Unable to parse default presentation delay: %s",
6135         default_presentation_delay);
6136     value = 0;
6137   }
6138   return value;
6139 }
6140
6141 GstClockTime
6142 gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client)
6143 {
6144   GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
6145   GList *stream;
6146
6147   g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
6148   g_return_val_if_fail (client->mpd_node != NULL, GST_CLOCK_TIME_NONE);
6149
6150   if (client->mpd_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
6151     return client->mpd_node->maxSegmentDuration * GST_MSECOND;
6152   }
6153
6154   /* According to the DASH specification, if maxSegmentDuration is not present:
6155      "If not present, then the maximum Segment duration shall be the maximum
6156      duration of any Segment documented in this MPD"
6157    */
6158   for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6159     dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
6160     if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
6161       ret = dur;
6162     }
6163   }
6164   return ret;
6165 }