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