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