• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 #include "gstmpdparser.h"
30 #include "gstdash_debug.h"
31 
32 #define GST_CAT_DEFAULT gst_dash_demux_debug
33 
34 
35 /* XML node parsing */
36 static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
37 static void gst_mpdparser_parse_descriptor_type (GList ** list,
38     xmlNode * a_node);
39 static void gst_mpdparser_parse_content_component_node (GList ** list,
40     xmlNode * a_node);
41 static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
42 static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
43     xmlNode * a_node);
44 static void gst_mpdparser_parse_segment_url_node (GList ** list,
45     xmlNode * a_node);
46 static void gst_mpdparser_parse_url_type_node (GstMPDURLTypeNode ** pointer,
47     xmlNode * a_node);
48 static void gst_mpdparser_parse_seg_base_type_ext (GstMPDSegmentBaseNode **
49     pointer, xmlNode * a_node, GstMPDSegmentBaseNode * parent);
50 static void gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node);
51 static void gst_mpdparser_parse_segment_timeline_node (GstMPDSegmentTimelineNode
52     ** pointer, xmlNode * a_node);
53 static gboolean
54 gst_mpdparser_parse_mult_seg_base_node (GstMPDMultSegmentBaseNode *
55     pointer, xmlNode * a_node, GstMPDMultSegmentBaseNode * parent);
56 static gboolean gst_mpdparser_parse_segment_list_node (GstMPDSegmentListNode **
57     pointer, xmlNode * a_node, GstMPDSegmentListNode * parent);
58 static void
59 gst_mpdparser_parse_representation_base (GstMPDRepresentationBaseNode *
60     pointer, xmlNode * a_node);
61 static gboolean gst_mpdparser_parse_representation_node (GList ** list,
62     xmlNode * a_node, GstMPDAdaptationSetNode * parent,
63     GstMPDPeriodNode * period_node);
64 static gboolean gst_mpdparser_parse_adaptation_set_node (GList ** list,
65     xmlNode * a_node, GstMPDPeriodNode * parent);
66 static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
67 static gboolean
68 gst_mpdparser_parse_segment_template_node (GstMPDSegmentTemplateNode ** pointer,
69     xmlNode * a_node, GstMPDSegmentTemplateNode * parent);
70 static gboolean gst_mpdparser_parse_period_node (GList ** list,
71     xmlNode * a_node);
72 static void gst_mpdparser_parse_program_info_node (GList ** list,
73     xmlNode * a_node);
74 static void gst_mpdparser_parse_metrics_range_node (GList ** list,
75     xmlNode * a_node);
76 static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
77 static gboolean gst_mpdparser_parse_root_node (GstMPDRootNode ** pointer,
78     xmlNode * a_node);
79 static void gst_mpdparser_parse_utctiming_node (GList ** list,
80     xmlNode * a_node);
81 
82 /*
83   Duration Data Type
84 
85   The duration data type is used to specify a time interval.
86 
87   The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
88 
89     * - indicates the negative sign (optional)
90     * P indicates the period (required)
91     * nY indicates the number of years
92     * nM indicates the number of months
93     * nD indicates the number of days
94     * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
95     * nH indicates the number of hours
96     * nM indicates the number of minutes
97     * nS indicates the number of seconds
98 */
99 
100 
101 
102 
103 static void
gst_mpdparser_parse_baseURL_node(GList ** list,xmlNode * a_node)104 gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
105 {
106   GstMPDBaseURLNode *new_base_url;
107 
108   new_base_url = gst_mpd_baseurl_node_new ();
109   *list = g_list_append (*list, new_base_url);
110 
111   GST_LOG ("content of BaseURL node:");
112   gst_xml_helper_get_node_content (a_node, &new_base_url->baseURL);
113 
114   GST_LOG ("attributes of BaseURL node:");
115   gst_xml_helper_get_prop_string (a_node, "serviceLocation",
116       &new_base_url->serviceLocation);
117   gst_xml_helper_get_prop_string (a_node, "byteRange",
118       &new_base_url->byteRange);
119 }
120 
121 static void
gst_mpdparser_parse_descriptor_type(GList ** list,xmlNode * a_node)122 gst_mpdparser_parse_descriptor_type (GList ** list, xmlNode * a_node)
123 {
124   GstMPDDescriptorTypeNode *new_descriptor;
125 
126   new_descriptor =
127       gst_mpd_descriptor_type_node_new ((const gchar *) a_node->name);
128   *list = g_list_append (*list, new_descriptor);
129 
130   GST_LOG ("attributes of %s node:", a_node->name);
131   gst_xml_helper_get_prop_string_stripped (a_node, "schemeIdUri",
132       &new_descriptor->schemeIdUri);
133   if (!gst_xml_helper_get_prop_string (a_node, "value", &new_descriptor->value)) {
134     /* if no value attribute, use XML string representation of the node */
135     gst_xml_helper_get_node_as_string (a_node, &new_descriptor->value);
136   }
137 }
138 
139 static void
gst_mpdparser_parse_content_component_node(GList ** list,xmlNode * a_node)140 gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
141 {
142   xmlNode *cur_node;
143   GstMPDContentComponentNode *new_content_component;
144 
145   new_content_component = gst_mpd_content_component_node_new ();
146   *list = g_list_append (*list, new_content_component);
147 
148   GST_LOG ("attributes of ContentComponent node:");
149   gst_xml_helper_get_prop_unsigned_integer (a_node, "id", 0,
150       &new_content_component->id);
151   gst_xml_helper_get_prop_string (a_node, "lang", &new_content_component->lang);
152   gst_xml_helper_get_prop_string (a_node, "contentType",
153       &new_content_component->contentType);
154   gst_xml_helper_get_prop_ratio (a_node, "par", &new_content_component->par);
155 
156   /* explore children nodes */
157   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
158     if (cur_node->type == XML_ELEMENT_NODE) {
159       if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
160         gst_mpdparser_parse_descriptor_type
161             (&new_content_component->Accessibility, cur_node);
162       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
163         gst_mpdparser_parse_descriptor_type (&new_content_component->Role,
164             cur_node);
165       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
166         gst_mpdparser_parse_descriptor_type
167             (&new_content_component->Rating, cur_node);
168       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
169         gst_mpdparser_parse_descriptor_type
170             (&new_content_component->Viewpoint, cur_node);
171       }
172     }
173   }
174 }
175 
176 static void
gst_mpdparser_parse_location_node(GList ** list,xmlNode * a_node)177 gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
178 {
179   gchar *location = NULL;
180   GstMPDLocationNode *locationNode;
181 
182   GST_LOG ("content of Location node:");
183   if (gst_xml_helper_get_node_content (a_node, &location)) {
184     locationNode = gst_mpd_location_node_new ();
185     locationNode->location = location;
186     *list = g_list_append (*list, locationNode);
187   }
188 }
189 
190 static void
gst_mpdparser_parse_subrepresentation_node(GList ** list,xmlNode * a_node)191 gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
192 {
193   GstMPDSubRepresentationNode *new_subrep;
194 
195   new_subrep = gst_mpd_sub_representation_node_new ();
196   *list = g_list_append (*list, new_subrep);
197 
198   GST_LOG ("attributes of SubRepresentation node:");
199   gst_xml_helper_get_prop_unsigned_integer (a_node, "level", 0,
200       &new_subrep->level);
201   gst_xml_helper_get_prop_uint_vector_type (a_node, "dependencyLevel",
202       &new_subrep->dependencyLevel, &new_subrep->dependencyLevel_size);
203   gst_xml_helper_get_prop_unsigned_integer (a_node, "bandwidth", 0,
204       &new_subrep->bandwidth);
205   gst_xml_helper_get_prop_string_vector_type (a_node,
206       "contentComponent", &new_subrep->contentComponent);
207 
208   /* RepresentationBase extension */
209   gst_mpdparser_parse_representation_base (GST_MPD_REPRESENTATION_BASE_NODE
210       (new_subrep), a_node);
211 }
212 
213 
214 
215 static void
gst_mpdparser_parse_segment_url_node(GList ** list,xmlNode * a_node)216 gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
217 {
218   GstMPDSegmentURLNode *new_segment_url;
219 
220   new_segment_url = gst_mpd_segment_url_node_new ();
221   *list = g_list_append (*list, new_segment_url);
222 
223   GST_LOG ("attributes of SegmentURL node:");
224   gst_xml_helper_get_prop_string (a_node, "media", &new_segment_url->media);
225   gst_xml_helper_get_prop_range (a_node, "mediaRange",
226       &new_segment_url->mediaRange);
227   gst_xml_helper_get_prop_string (a_node, "index", &new_segment_url->index);
228   gst_xml_helper_get_prop_range (a_node, "indexRange",
229       &new_segment_url->indexRange);
230 }
231 
232 static void
gst_mpdparser_parse_url_type_node(GstMPDURLTypeNode ** pointer,xmlNode * a_node)233 gst_mpdparser_parse_url_type_node (GstMPDURLTypeNode ** pointer,
234     xmlNode * a_node)
235 {
236   GstMPDURLTypeNode *new_url_type;
237 
238   gst_mpd_url_type_node_free (*pointer);
239   *pointer = new_url_type =
240       gst_mpd_url_type_node_new ((const gchar *) a_node->name);
241 
242   GST_LOG ("attributes of URLType node:");
243   gst_xml_helper_get_prop_string (a_node, "sourceURL",
244       &new_url_type->sourceURL);
245   gst_xml_helper_get_prop_range (a_node, "range", &new_url_type->range);
246 }
247 
248 static void
gst_mpdparser_parse_seg_base_type_ext(GstMPDSegmentBaseNode ** pointer,xmlNode * a_node,GstMPDSegmentBaseNode * parent)249 gst_mpdparser_parse_seg_base_type_ext (GstMPDSegmentBaseNode ** pointer,
250     xmlNode * a_node, GstMPDSegmentBaseNode * parent)
251 {
252   xmlNode *cur_node;
253   GstMPDSegmentBaseNode *seg_base_type;
254   guint intval;
255   guint64 int64val;
256   gboolean boolval;
257   GstXMLRange *rangeval;
258 
259   gst_mpd_segment_base_node_free (*pointer);
260   *pointer = seg_base_type = gst_mpd_segment_base_node_new ();
261 
262   /* Initialize values that have defaults */
263   seg_base_type->indexRangeExact = FALSE;
264   seg_base_type->timescale = 1;
265 
266   /* Inherit attribute values from parent */
267   if (parent) {
268     seg_base_type->timescale = parent->timescale;
269     seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
270     seg_base_type->indexRange = gst_xml_helper_clone_range (parent->indexRange);
271     seg_base_type->indexRangeExact = parent->indexRangeExact;
272     seg_base_type->Initialization =
273         gst_mpd_url_type_node_clone (parent->Initialization);
274     seg_base_type->RepresentationIndex =
275         gst_mpd_url_type_node_clone (parent->RepresentationIndex);
276   }
277 
278   /* We must retrieve each value first to see if it exists.  If it does not
279    * exist, we do not want to overwrite an inherited value */
280   GST_LOG ("attributes of SegmentBaseType extension:");
281   if (gst_xml_helper_get_prop_unsigned_integer (a_node, "timescale", 1,
282           &intval)) {
283     seg_base_type->timescale = intval;
284   }
285   if (gst_xml_helper_get_prop_unsigned_integer_64 (a_node,
286           "presentationTimeOffset", 0, &int64val)) {
287     seg_base_type->presentationTimeOffset = int64val;
288   }
289   if (gst_xml_helper_get_prop_range (a_node, "indexRange", &rangeval)) {
290     if (seg_base_type->indexRange) {
291       g_slice_free (GstXMLRange, seg_base_type->indexRange);
292     }
293     seg_base_type->indexRange = rangeval;
294   }
295   if (gst_xml_helper_get_prop_boolean (a_node, "indexRangeExact",
296           FALSE, &boolval)) {
297     seg_base_type->indexRangeExact = boolval;
298   }
299 
300   /* explore children nodes */
301   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
302     if (cur_node->type == XML_ELEMENT_NODE) {
303       if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
304           xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
305         /* parse will free the previous pointer to create a new one */
306         gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
307             cur_node);
308       } else if (xmlStrcmp (cur_node->name,
309               (xmlChar *) "RepresentationIndex") == 0) {
310         /* parse will free the previous pointer to create a new one */
311         gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
312             cur_node);
313       }
314     }
315   }
316 }
317 
318 
319 
320 static void
gst_mpdparser_parse_s_node(GQueue * queue,xmlNode * a_node)321 gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
322 {
323   GstMPDSNode *new_s_node;
324 
325   new_s_node = gst_mpd_s_node_new ();
326   g_queue_push_tail (queue, new_s_node);
327 
328   GST_LOG ("attributes of S node:");
329   gst_xml_helper_get_prop_unsigned_integer_64 (a_node, "t", 0, &new_s_node->t);
330   gst_xml_helper_get_prop_unsigned_integer_64 (a_node, "d", 0, &new_s_node->d);
331   gst_xml_helper_get_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
332 }
333 
334 
335 
336 static void
gst_mpdparser_parse_segment_timeline_node(GstMPDSegmentTimelineNode ** pointer,xmlNode * a_node)337 gst_mpdparser_parse_segment_timeline_node (GstMPDSegmentTimelineNode ** pointer,
338     xmlNode * a_node)
339 {
340   xmlNode *cur_node;
341   GstMPDSegmentTimelineNode *new_seg_timeline;
342 
343   gst_mpd_segment_timeline_node_free (*pointer);
344   *pointer = new_seg_timeline = gst_mpd_segment_timeline_node_new ();
345   if (new_seg_timeline == NULL) {
346     GST_WARNING ("Allocation of SegmentTimeline node failed!");
347     return;
348   }
349 
350   /* explore children nodes */
351   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
352     if (cur_node->type == XML_ELEMENT_NODE) {
353       if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
354         gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
355       }
356     }
357   }
358 }
359 
360 static gboolean
gst_mpdparser_parse_mult_seg_base_node(GstMPDMultSegmentBaseNode * mult_seg_base_node,xmlNode * a_node,GstMPDMultSegmentBaseNode * parent)361 gst_mpdparser_parse_mult_seg_base_node (GstMPDMultSegmentBaseNode *
362     mult_seg_base_node, xmlNode * a_node, GstMPDMultSegmentBaseNode * parent)
363 {
364   xmlNode *cur_node;
365 
366   guint intval;
367   gboolean has_timeline = FALSE, has_duration = FALSE;
368 
369   mult_seg_base_node->duration = 0;
370   mult_seg_base_node->startNumber = 1;
371 
372   /* Inherit attribute values from parent */
373   if (parent) {
374     mult_seg_base_node->duration = parent->duration;
375     mult_seg_base_node->startNumber = parent->startNumber;
376     mult_seg_base_node->SegmentTimeline =
377         gst_mpd_segment_timeline_node_clone (parent->SegmentTimeline);
378     mult_seg_base_node->BitstreamSwitching =
379         gst_mpd_url_type_node_clone (parent->BitstreamSwitching);
380   }
381   GST_LOG ("attributes of MultipleSegmentBaseType extension:");
382   if (gst_xml_helper_get_prop_unsigned_integer (a_node, "duration", 0, &intval)) {
383     mult_seg_base_node->duration = intval;
384   }
385 
386   /* duration might be specified from parent */
387   if (mult_seg_base_node->duration)
388     has_duration = TRUE;
389 
390   if (gst_xml_helper_get_prop_unsigned_integer (a_node, "startNumber", 1,
391           &intval)) {
392     mult_seg_base_node->startNumber = intval;
393   }
394 
395   GST_LOG ("extension of MultipleSegmentBaseType extension:");
396   gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_node->SegmentBase,
397       a_node, (parent ? parent->SegmentBase : NULL));
398 
399   /* explore children nodes */
400   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
401     if (cur_node->type == XML_ELEMENT_NODE) {
402       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
403         /* parse frees the segmenttimeline if any */
404         gst_mpdparser_parse_segment_timeline_node
405             (&mult_seg_base_node->SegmentTimeline, cur_node);
406       } else if (xmlStrcmp (cur_node->name,
407               (xmlChar *) "BitstreamSwitching") == 0) {
408         /* parse frees the old url before setting the new one */
409         gst_mpdparser_parse_url_type_node
410             (&mult_seg_base_node->BitstreamSwitching, cur_node);
411       }
412     }
413   }
414 
415   has_timeline = mult_seg_base_node->SegmentTimeline != NULL;
416 
417   /* Checking duration and timeline only at Representation's child level */
418   if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
419       && !has_duration && !has_timeline) {
420     GST_ERROR ("segment has neither duration nor timeline");
421   }
422 
423   return TRUE;
424 }
425 
426 static gboolean
gst_mpdparser_parse_segment_list_node(GstMPDSegmentListNode ** pointer,xmlNode * a_node,GstMPDSegmentListNode * parent)427 gst_mpdparser_parse_segment_list_node (GstMPDSegmentListNode ** pointer,
428     xmlNode * a_node, GstMPDSegmentListNode * parent)
429 {
430   xmlNode *cur_node;
431   GstMPDSegmentListNode *new_segment_list;
432   gchar *actuate;
433   gboolean segment_urls_inherited_from_parent = FALSE;
434 
435   gst_mpd_segment_list_node_free (*pointer);
436   new_segment_list = gst_mpd_segment_list_node_new ();
437 
438   /* Inherit attribute values from parent */
439   if (parent) {
440     GList *list;
441     GstMPDSegmentURLNode *seg_url;
442     for (list = g_list_first (parent->SegmentURL); list;
443         list = g_list_next (list)) {
444       seg_url = (GstMPDSegmentURLNode *) list->data;
445       new_segment_list->SegmentURL =
446           g_list_append (new_segment_list->SegmentURL,
447           gst_mpd_segment_url_node_clone (seg_url));
448       segment_urls_inherited_from_parent = TRUE;
449     }
450   }
451 
452   new_segment_list->actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST;
453   if (gst_xml_helper_get_ns_prop_string (a_node,
454           "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
455       && gst_xml_helper_get_ns_prop_string (a_node,
456           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
457     if (strcmp (actuate, GST_MPD_XLINK_ACTUATE_ON_LOAD_STR) == 0)
458       new_segment_list->actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD;
459     xmlFree (actuate);
460   }
461 
462   GST_LOG ("extension of SegmentList node:");
463   if (!gst_mpdparser_parse_mult_seg_base_node
464       (GST_MPD_MULT_SEGMENT_BASE_NODE (new_segment_list), a_node,
465           (parent ? GST_MPD_MULT_SEGMENT_BASE_NODE (parent) : NULL)))
466     goto error;
467 
468   /* explore children nodes */
469   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
470     if (cur_node->type == XML_ELEMENT_NODE) {
471       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
472         if (segment_urls_inherited_from_parent) {
473           /*
474            * SegmentBase, SegmentTemplate and SegmentList shall inherit
475            * attributes and elements from the same element on a higher level.
476            * If the same attribute or element is present on both levels,
477            * the one on the lower level shall take precedence over the one
478            * on the higher level.
479            */
480 
481           /* Clear the list of inherited segment URLs */
482           g_list_free_full (new_segment_list->SegmentURL,
483               (GDestroyNotify) gst_mpd_segment_url_node_free);
484           new_segment_list->SegmentURL = NULL;
485 
486           /* mark the fact that we cleared the list, so that it is not tried again */
487           segment_urls_inherited_from_parent = FALSE;
488         }
489         gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
490             cur_node);
491       }
492     }
493   }
494 
495   *pointer = new_segment_list;
496   return TRUE;
497 
498 error:
499   gst_mpd_segment_list_node_free (new_segment_list);
500   return FALSE;
501 }
502 
503 static void
gst_mpdparser_parse_content_protection_node(GList ** list,xmlNode * a_node)504 gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
505 {
506   GstMPDDescriptorTypeNode *new_descriptor;
507   new_descriptor = gst_mpd_descriptor_type_node_new ((const gchar *)
508       a_node->name);
509   *list = g_list_append (*list, new_descriptor);
510 
511   gst_xml_helper_get_prop_string_stripped (a_node, "schemeIdUri",
512       &new_descriptor->schemeIdUri);
513   gst_xml_helper_get_node_as_string (a_node, &new_descriptor->value);
514 }
515 
516 static void
gst_mpdparser_parse_representation_base(GstMPDRepresentationBaseNode * representation_base,xmlNode * a_node)517 gst_mpdparser_parse_representation_base (GstMPDRepresentationBaseNode *
518     representation_base, xmlNode * a_node)
519 {
520   xmlNode *cur_node;
521 
522   GST_LOG ("attributes of RepresentationBaseType extension:");
523   gst_xml_helper_get_prop_string (a_node, "profiles",
524       &representation_base->profiles);
525   gst_xml_helper_get_prop_unsigned_integer (a_node, "width", 0,
526       &representation_base->width);
527   gst_xml_helper_get_prop_unsigned_integer (a_node, "height", 0,
528       &representation_base->height);
529   gst_xml_helper_get_prop_ratio (a_node, "sar", &representation_base->sar);
530   gst_xml_helper_get_prop_framerate (a_node, "frameRate",
531       &representation_base->frameRate);
532   gst_xml_helper_get_prop_framerate (a_node, "minFrameRate",
533       &representation_base->minFrameRate);
534   gst_xml_helper_get_prop_framerate (a_node, "maxFrameRate",
535       &representation_base->maxFrameRate);
536   gst_xml_helper_get_prop_string (a_node, "audioSamplingRate",
537       &representation_base->audioSamplingRate);
538   gst_xml_helper_get_prop_string (a_node, "mimeType",
539       &representation_base->mimeType);
540   gst_xml_helper_get_prop_string (a_node, "segmentProfiles",
541       &representation_base->segmentProfiles);
542   gst_xml_helper_get_prop_string (a_node, "codecs",
543       &representation_base->codecs);
544   gst_xml_helper_get_prop_double (a_node, "maximumSAPPeriod",
545       &representation_base->maximumSAPPeriod);
546   gst_mpd_helper_get_SAP_type (a_node, "startWithSAP",
547       &representation_base->startWithSAP);
548   gst_xml_helper_get_prop_double (a_node, "maxPlayoutRate",
549       &representation_base->maxPlayoutRate);
550   gst_xml_helper_get_prop_boolean (a_node, "codingDependency",
551       FALSE, &representation_base->codingDependency);
552   gst_xml_helper_get_prop_string (a_node, "scanType",
553       &representation_base->scanType);
554 
555   /* explore children nodes */
556   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
557     if (cur_node->type == XML_ELEMENT_NODE) {
558       if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
559         gst_mpdparser_parse_descriptor_type
560             (&representation_base->FramePacking, cur_node);
561       } else if (xmlStrcmp (cur_node->name,
562               (xmlChar *) "AudioChannelConfiguration") == 0) {
563         gst_mpdparser_parse_descriptor_type
564             (&representation_base->AudioChannelConfiguration, cur_node);
565       } else if (xmlStrcmp (cur_node->name,
566               (xmlChar *) "ContentProtection") == 0) {
567         gst_mpdparser_parse_content_protection_node
568             (&representation_base->ContentProtection, cur_node);
569       }
570     }
571   }
572 }
573 
574 static gboolean
gst_mpdparser_parse_representation_node(GList ** list,xmlNode * a_node,GstMPDAdaptationSetNode * parent,GstMPDPeriodNode * period_node)575 gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
576     GstMPDAdaptationSetNode * parent, GstMPDPeriodNode * period_node)
577 {
578   xmlNode *cur_node;
579   GstMPDRepresentationNode *new_representation;
580 
581   new_representation = gst_mpd_representation_node_new ();
582 
583   GST_LOG ("attributes of Representation node:");
584   if (!gst_xml_helper_get_prop_string_no_whitespace (a_node, "id",
585           &new_representation->id)) {
586     GST_ERROR ("Cannot parse Representation id, invalid manifest");
587     goto error;
588   }
589   if (!gst_xml_helper_get_prop_unsigned_integer (a_node, "bandwidth", 0,
590           &new_representation->bandwidth)) {
591     GST_ERROR ("Cannot parse Representation bandwidth, invalid manifest");
592     goto error;
593   }
594   gst_xml_helper_get_prop_unsigned_integer (a_node, "qualityRanking", 0,
595       &new_representation->qualityRanking);
596   gst_xml_helper_get_prop_string_vector_type (a_node, "dependencyId",
597       &new_representation->dependencyId);
598   gst_xml_helper_get_prop_string_vector_type (a_node,
599       "mediaStreamStructureId", &new_representation->mediaStreamStructureId);
600   /* RepresentationBase extension */
601   gst_mpdparser_parse_representation_base
602       (GST_MPD_REPRESENTATION_BASE_NODE (new_representation), a_node);
603 
604   /* explore children nodes */
605   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
606     if (cur_node->type == XML_ELEMENT_NODE) {
607       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
608         gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
609             cur_node, parent->SegmentBase ?
610             parent->SegmentBase : period_node->SegmentBase);
611       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
612         if (!gst_mpdparser_parse_segment_template_node
613             (&new_representation->SegmentTemplate, cur_node,
614                 parent->SegmentTemplate ?
615                 parent->SegmentTemplate : period_node->SegmentTemplate))
616           goto error;
617       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
618         if (!gst_mpdparser_parse_segment_list_node
619             (&new_representation->SegmentList, cur_node,
620                 parent->SegmentList ? parent->SegmentList : period_node->
621                 SegmentList))
622           goto error;
623       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
624         gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
625             cur_node);
626       } else if (xmlStrcmp (cur_node->name,
627               (xmlChar *) "SubRepresentation") == 0) {
628         gst_mpdparser_parse_subrepresentation_node
629             (&new_representation->SubRepresentations, cur_node);
630       }
631     }
632   }
633 
634   /* some sanity checking */
635 
636   *list = g_list_append (*list, new_representation);
637   return TRUE;
638 
639 error:
640   gst_mpd_representation_node_free (new_representation);
641   return FALSE;
642 }
643 
644 static gboolean
gst_mpdparser_parse_adaptation_set_node(GList ** list,xmlNode * a_node,GstMPDPeriodNode * parent)645 gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
646     GstMPDPeriodNode * parent)
647 {
648   xmlNode *cur_node;
649   GstMPDAdaptationSetNode *new_adap_set;
650   gchar *actuate;
651 
652   new_adap_set = gst_mpd_adaptation_set_node_new ();
653 
654   GST_LOG ("attributes of AdaptationSet node:");
655 
656   new_adap_set->actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST;
657   if (gst_xml_helper_get_ns_prop_string (a_node,
658           "http://www.w3.org/1999/xlink", "href", &new_adap_set->xlink_href)
659       && gst_xml_helper_get_ns_prop_string (a_node,
660           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
661     if (strcmp (actuate, "onLoad") == 0)
662       new_adap_set->actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD;
663     xmlFree (actuate);
664   }
665 
666   gst_xml_helper_get_prop_unsigned_integer (a_node, "id", 0, &new_adap_set->id);
667   gst_xml_helper_get_prop_unsigned_integer (a_node, "group", 0,
668       &new_adap_set->group);
669   gst_xml_helper_get_prop_string (a_node, "lang", &new_adap_set->lang);
670   gst_xml_helper_get_prop_string (a_node, "contentType",
671       &new_adap_set->contentType);
672   gst_xml_helper_get_prop_ratio (a_node, "par", &new_adap_set->par);
673   gst_xml_helper_get_prop_unsigned_integer (a_node, "minBandwidth", 0,
674       &new_adap_set->minBandwidth);
675   gst_xml_helper_get_prop_unsigned_integer (a_node, "maxBandwidth", 0,
676       &new_adap_set->maxBandwidth);
677   gst_xml_helper_get_prop_unsigned_integer (a_node, "minWidth", 0,
678       &new_adap_set->minWidth);
679   gst_xml_helper_get_prop_unsigned_integer (a_node, "maxWidth", 0,
680       &new_adap_set->maxWidth);
681   gst_xml_helper_get_prop_unsigned_integer (a_node, "minHeight", 0,
682       &new_adap_set->minHeight);
683   gst_xml_helper_get_prop_unsigned_integer (a_node, "maxHeight", 0,
684       &new_adap_set->maxHeight);
685   gst_xml_helper_get_prop_cond_uint (a_node, "segmentAlignment",
686       &new_adap_set->segmentAlignment);
687   gst_xml_helper_get_prop_boolean (a_node, "bitstreamSwitching",
688       parent->bitstreamSwitching, &new_adap_set->bitstreamSwitching);
689   if (parent->bitstreamSwitching && !new_adap_set->bitstreamSwitching) {
690     /* according to the standard, if the Period's bitstreamSwitching attribute
691      * is true, the AdaptationSet should not have the bitstreamSwitching
692      * attribute set to false.
693      * We should return a parsing error, but we are generous and ignore the
694      * standard violation.
695      */
696     new_adap_set->bitstreamSwitching = parent->bitstreamSwitching;
697   }
698   gst_xml_helper_get_prop_cond_uint (a_node, "subsegmentAlignment",
699       &new_adap_set->subsegmentAlignment);
700   gst_mpd_helper_get_SAP_type (a_node, "subsegmentStartsWithSAP",
701       &new_adap_set->subsegmentStartsWithSAP);
702 
703   /* RepresentationBase extension */
704   gst_mpdparser_parse_representation_base
705       (GST_MPD_REPRESENTATION_BASE_NODE (new_adap_set), a_node);
706 
707   /* explore children nodes */
708   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
709     if (cur_node->type == XML_ELEMENT_NODE) {
710       if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
711         gst_mpdparser_parse_descriptor_type (&new_adap_set->Accessibility,
712             cur_node);
713       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
714         gst_mpdparser_parse_descriptor_type (&new_adap_set->Role, cur_node);
715       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
716         gst_mpdparser_parse_descriptor_type (&new_adap_set->Rating, cur_node);
717       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
718         gst_mpdparser_parse_descriptor_type (&new_adap_set->Viewpoint,
719             cur_node);
720       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
721         gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
722       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
723         gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
724             cur_node, parent->SegmentBase);
725       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
726         if (!gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
727                 cur_node, parent->SegmentList))
728           goto error;
729       } else if (xmlStrcmp (cur_node->name,
730               (xmlChar *) "ContentComponent") == 0) {
731         gst_mpdparser_parse_content_component_node
732             (&new_adap_set->ContentComponents, cur_node);
733       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
734         if (!gst_mpdparser_parse_segment_template_node
735             (&new_adap_set->SegmentTemplate, cur_node, parent->SegmentTemplate))
736           goto error;
737       }
738     }
739   }
740 
741   /* We must parse Representation after everything else in the AdaptationSet
742    * has been parsed because certain Representation child elements can inherit
743    * attributes specified by the same element in the AdaptationSet
744    */
745   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
746     if (cur_node->type == XML_ELEMENT_NODE) {
747       if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
748         if (!gst_mpdparser_parse_representation_node
749             (&new_adap_set->Representations, cur_node, new_adap_set, parent))
750           goto error;
751       }
752     }
753   }
754 
755   *list = g_list_append (*list, new_adap_set);
756   return TRUE;
757 
758 error:
759   gst_mpd_adaptation_set_node_free (new_adap_set);
760   return FALSE;
761 }
762 
763 static void
gst_mpdparser_parse_subset_node(GList ** list,xmlNode * a_node)764 gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
765 {
766   GstMPDSubsetNode *new_subset;
767 
768   new_subset = gst_mpd_subset_node_new ();
769   *list = g_list_append (*list, new_subset);
770 
771   GST_LOG ("attributes of Subset node:");
772   gst_xml_helper_get_prop_uint_vector_type (a_node, "contains",
773       &new_subset->contains, &new_subset->contains_size);
774 }
775 
776 static gboolean
gst_mpdparser_parse_segment_template_node(GstMPDSegmentTemplateNode ** pointer,xmlNode * a_node,GstMPDSegmentTemplateNode * parent)777 gst_mpdparser_parse_segment_template_node (GstMPDSegmentTemplateNode ** pointer,
778     xmlNode * a_node, GstMPDSegmentTemplateNode * parent)
779 {
780   GstMPDSegmentTemplateNode *new_segment_template;
781   gchar *strval;
782 
783   gst_mpd_segment_template_node_free (*pointer);
784   new_segment_template = gst_mpd_segment_template_node_new ();
785 
786   GST_LOG ("extension of SegmentTemplate node:");
787   if (!gst_mpdparser_parse_mult_seg_base_node
788       (GST_MPD_MULT_SEGMENT_BASE_NODE (new_segment_template), a_node,
789           (parent ? GST_MPD_MULT_SEGMENT_BASE_NODE (parent) : NULL)))
790     goto error;
791 
792   /* Inherit attribute values from parent when the value isn't found */
793   GST_LOG ("attributes of SegmentTemplate node:");
794   if (gst_xml_helper_get_prop_string (a_node, "media", &strval)) {
795     new_segment_template->media = strval;
796   } else if (parent) {
797     new_segment_template->media = xmlMemStrdup (parent->media);
798   }
799 
800   if (gst_xml_helper_get_prop_string (a_node, "index", &strval)) {
801     new_segment_template->index = strval;
802   } else if (parent) {
803     new_segment_template->index = xmlMemStrdup (parent->index);
804   }
805 
806   if (gst_xml_helper_get_prop_string (a_node, "initialization", &strval)) {
807     new_segment_template->initialization = strval;
808   } else if (parent) {
809     new_segment_template->initialization =
810         xmlMemStrdup (parent->initialization);
811   }
812 
813   if (gst_xml_helper_get_prop_string (a_node, "bitstreamSwitching", &strval)) {
814     new_segment_template->bitstreamSwitching = strval;
815   } else if (parent) {
816     new_segment_template->bitstreamSwitching =
817         xmlMemStrdup (parent->bitstreamSwitching);
818   }
819 
820   *pointer = new_segment_template;
821   return TRUE;
822 
823 error:
824   gst_mpd_segment_template_node_free (new_segment_template);
825   return FALSE;
826 }
827 
828 static gboolean
gst_mpdparser_parse_period_node(GList ** list,xmlNode * a_node)829 gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
830 {
831   xmlNode *cur_node;
832   GstMPDPeriodNode *new_period;
833   gchar *actuate;
834 
835   new_period = gst_mpd_period_node_new ();
836 
837   GST_LOG ("attributes of Period node:");
838 
839   new_period->actuate = GST_MPD_XLINK_ACTUATE_ON_REQUEST;
840   if (gst_xml_helper_get_ns_prop_string (a_node,
841           "http://www.w3.org/1999/xlink", "href", &new_period->xlink_href)
842       && gst_xml_helper_get_ns_prop_string (a_node,
843           "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
844     if (strcmp (actuate, "onLoad") == 0)
845       new_period->actuate = GST_MPD_XLINK_ACTUATE_ON_LOAD;
846     xmlFree (actuate);
847   }
848 
849   gst_xml_helper_get_prop_string (a_node, "id", &new_period->id);
850   gst_xml_helper_get_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
851       &new_period->start);
852   gst_xml_helper_get_prop_duration (a_node, "duration",
853       GST_MPD_DURATION_NONE, &new_period->duration);
854   gst_xml_helper_get_prop_boolean (a_node, "bitstreamSwitching", FALSE,
855       &new_period->bitstreamSwitching);
856 
857   /* explore children nodes */
858   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
859     if (cur_node->type == XML_ELEMENT_NODE) {
860       if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
861         gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
862             cur_node, NULL);
863       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
864         if (!gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
865                 cur_node, NULL))
866           goto error;
867       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
868         if (!gst_mpdparser_parse_segment_template_node
869             (&new_period->SegmentTemplate, cur_node, NULL))
870           goto error;
871       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
872         gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
873       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
874         gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
875       }
876     }
877   }
878 
879   /* We must parse AdaptationSet after everything else in the Period has been
880    * parsed because certain AdaptationSet child elements can inherit attributes
881    * specified by the same element in the Period
882    */
883   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
884     if (cur_node->type == XML_ELEMENT_NODE) {
885       if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
886         if (!gst_mpdparser_parse_adaptation_set_node
887             (&new_period->AdaptationSets, cur_node, new_period))
888           goto error;
889       }
890     }
891   }
892 
893   *list = g_list_append (*list, new_period);
894   return TRUE;
895 
896 error:
897   gst_mpd_period_node_free (new_period);
898   return FALSE;
899 }
900 
901 static void
gst_mpdparser_parse_program_info_node(GList ** list,xmlNode * a_node)902 gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
903 {
904   xmlNode *cur_node;
905   GstMPDProgramInformationNode *new_prog_info;
906 
907   new_prog_info = gst_mpd_program_information_node_new ();
908   *list = g_list_append (*list, new_prog_info);
909 
910   GST_LOG ("attributes of ProgramInformation node:");
911   gst_xml_helper_get_prop_string (a_node, "lang", &new_prog_info->lang);
912   gst_xml_helper_get_prop_string (a_node, "moreInformationURL",
913       &new_prog_info->moreInformationURL);
914 
915   /* explore children nodes */
916   GST_LOG ("children of ProgramInformation node:");
917   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
918     if (cur_node->type == XML_ELEMENT_NODE) {
919       if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
920         gst_xml_helper_get_node_content (cur_node, &new_prog_info->Title);
921       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
922         gst_xml_helper_get_node_content (cur_node, &new_prog_info->Source);
923       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
924         gst_xml_helper_get_node_content (cur_node, &new_prog_info->Copyright);
925       }
926     }
927   }
928 }
929 
930 static void
gst_mpdparser_parse_metrics_range_node(GList ** list,xmlNode * a_node)931 gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
932 {
933   GstMPDMetricsRangeNode *new_metrics_range;
934 
935   new_metrics_range = gst_mpd_metrics_range_node_new ();
936   *list = g_list_append (*list, new_metrics_range);
937 
938   GST_LOG ("attributes of Metrics Range node:");
939   gst_xml_helper_get_prop_duration (a_node, "starttime",
940       GST_MPD_DURATION_NONE, &new_metrics_range->starttime);
941   gst_xml_helper_get_prop_duration (a_node, "duration",
942       GST_MPD_DURATION_NONE, &new_metrics_range->duration);
943 }
944 
945 static void
gst_mpdparser_parse_metrics_node(GList ** list,xmlNode * a_node)946 gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
947 {
948   xmlNode *cur_node;
949   GstMPDMetricsNode *new_metrics;
950 
951   new_metrics = gst_mpd_metrics_node_new ();
952   *list = g_list_append (*list, new_metrics);
953 
954   GST_LOG ("attributes of Metrics node:");
955   gst_xml_helper_get_prop_string (a_node, "metrics", &new_metrics->metrics);
956 
957   /* explore children nodes */
958   GST_LOG ("children of Metrics node:");
959   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
960     if (cur_node->type == XML_ELEMENT_NODE) {
961       if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
962         gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
963             cur_node);
964       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
965         /* No reporting scheme is specified in this part of ISO/IEC 23009.
966          * It is expected that external specifications may define formats
967          * and delivery for the reporting data. */
968         GST_LOG (" - Reporting node found (unknown structure)");
969       }
970     }
971   }
972 }
973 
974 /* The UTCTiming element is defined in
975  * 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"
976  */
977 static void
gst_mpdparser_parse_utctiming_node(GList ** list,xmlNode * a_node)978 gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
979 {
980   GstMPDUTCTimingNode *new_timing;
981   gchar *method = NULL;
982   gchar *value = NULL;
983 
984   new_timing = gst_mpd_utctiming_node_new ();
985 
986   GST_LOG ("attributes of UTCTiming node:");
987   if (gst_xml_helper_get_prop_string (a_node, "schemeIdUri", &method)) {
988     new_timing->method = gst_mpd_utctiming_get_method (method);
989     xmlFree (method);
990   }
991 
992   if (gst_xml_helper_get_prop_string (a_node, "value", &value)) {
993     int max_tokens = 0;
994     if (GST_MPD_UTCTIMING_TYPE_DIRECT == new_timing->method) {
995       /* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
996        * that is not a space separated list.
997        */
998       max_tokens = 1;
999     }
1000     new_timing->urls = g_strsplit (value, " ", max_tokens);
1001     xmlFree (value);
1002   }
1003 
1004   /* append to list only if both method and urls were set */
1005   if (new_timing->method != 0 && new_timing->urls != NULL &&
1006       g_strv_length (new_timing->urls) != 0) {
1007     *list = g_list_append (*list, new_timing);
1008   } else {
1009     gst_mpd_utctiming_node_free (new_timing);
1010   }
1011 }
1012 
1013 static gboolean
gst_mpdparser_parse_root_node(GstMPDRootNode ** pointer,xmlNode * a_node)1014 gst_mpdparser_parse_root_node (GstMPDRootNode ** pointer, xmlNode * a_node)
1015 {
1016   xmlNode *cur_node;
1017   GstMPDRootNode *new_mpd_root;
1018 
1019   gst_mpd_root_node_free (*pointer);
1020   *pointer = NULL;
1021   new_mpd_root = gst_mpd_root_node_new ();
1022 
1023   GST_LOG ("namespaces of root MPD node:");
1024   new_mpd_root->default_namespace =
1025       gst_xml_helper_get_node_namespace (a_node, NULL);
1026   new_mpd_root->namespace_xsi =
1027       gst_xml_helper_get_node_namespace (a_node, "xsi");
1028   new_mpd_root->namespace_ext =
1029       gst_xml_helper_get_node_namespace (a_node, "ext");
1030 
1031   GST_LOG ("attributes of root MPD node:");
1032   gst_xml_helper_get_prop_string (a_node, "schemaLocation",
1033       &new_mpd_root->schemaLocation);
1034   gst_xml_helper_get_prop_string (a_node, "id", &new_mpd_root->id);
1035   gst_xml_helper_get_prop_string (a_node, "profiles", &new_mpd_root->profiles);
1036   gst_mpd_helper_get_mpd_type (a_node, "type", &new_mpd_root->type);
1037   gst_xml_helper_get_prop_dateTime (a_node, "availabilityStartTime",
1038       &new_mpd_root->availabilityStartTime);
1039   gst_xml_helper_get_prop_dateTime (a_node, "availabilityEndTime",
1040       &new_mpd_root->availabilityEndTime);
1041   gst_xml_helper_get_prop_duration (a_node, "mediaPresentationDuration",
1042       GST_MPD_DURATION_NONE, &new_mpd_root->mediaPresentationDuration);
1043   gst_xml_helper_get_prop_duration (a_node, "minimumUpdatePeriod",
1044       GST_MPD_DURATION_NONE, &new_mpd_root->minimumUpdatePeriod);
1045   gst_xml_helper_get_prop_duration (a_node, "minBufferTime",
1046       GST_MPD_DURATION_NONE, &new_mpd_root->minBufferTime);
1047   gst_xml_helper_get_prop_duration (a_node, "timeShiftBufferDepth",
1048       GST_MPD_DURATION_NONE, &new_mpd_root->timeShiftBufferDepth);
1049   gst_xml_helper_get_prop_duration (a_node, "suggestedPresentationDelay",
1050       GST_MPD_DURATION_NONE, &new_mpd_root->suggestedPresentationDelay);
1051   gst_xml_helper_get_prop_duration (a_node, "maxSegmentDuration",
1052       GST_MPD_DURATION_NONE, &new_mpd_root->maxSegmentDuration);
1053   gst_xml_helper_get_prop_duration (a_node, "maxSubsegmentDuration",
1054       GST_MPD_DURATION_NONE, &new_mpd_root->maxSubsegmentDuration);
1055 
1056   /* explore children Period nodes */
1057   for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1058     if (cur_node->type == XML_ELEMENT_NODE) {
1059       if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
1060         if (!gst_mpdparser_parse_period_node (&new_mpd_root->Periods, cur_node))
1061           goto error;
1062       } else if (xmlStrcmp (cur_node->name,
1063               (xmlChar *) "ProgramInformation") == 0) {
1064         gst_mpdparser_parse_program_info_node (&new_mpd_root->ProgramInfos,
1065             cur_node);
1066       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
1067         gst_mpdparser_parse_baseURL_node (&new_mpd_root->BaseURLs, cur_node);
1068       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
1069         gst_mpdparser_parse_location_node (&new_mpd_root->Locations, cur_node);
1070       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
1071         gst_mpdparser_parse_metrics_node (&new_mpd_root->Metrics, cur_node);
1072       } else if (xmlStrcmp (cur_node->name, (xmlChar *) "UTCTiming") == 0) {
1073         gst_mpdparser_parse_utctiming_node (&new_mpd_root->UTCTimings,
1074             cur_node);
1075       }
1076     }
1077   }
1078 
1079   *pointer = new_mpd_root;
1080   return TRUE;
1081 
1082 error:
1083   gst_mpd_root_node_free (new_mpd_root);
1084   return FALSE;
1085 }
1086 
1087 /* internal memory management functions */
1088 
1089 /* ISO/IEC 23009-1:2004 5.3.9.4.4 */
1090 static gboolean
validate_format(const gchar * format)1091 validate_format (const gchar * format)
1092 {
1093   const gchar *p = format;
1094 
1095   /* Check if it starts with % */
1096   if (!p || p[0] != '%')
1097     return FALSE;
1098   p++;
1099 
1100   /* the spec mandates a format like %0[width]d */
1101   /* Following the %, we must have a 0 */
1102   if (p[0] != '0')
1103     return FALSE;
1104 
1105   /* Following the % must be a number starting with 0
1106    */
1107   while (g_ascii_isdigit (*p))
1108     p++;
1109 
1110   /* After any 0 and alphanumeric values, there must be a d.
1111    */
1112   if (p[0] != 'd')
1113     return FALSE;
1114   p++;
1115 
1116   /* And then potentially more characters without any
1117    * further %, even if the spec does not mention this
1118    */
1119   p = strchr (p, '%');
1120   if (p)
1121     return FALSE;
1122 
1123   return TRUE;
1124 }
1125 
1126 static gchar *
promote_format_to_uint64(const gchar * format)1127 promote_format_to_uint64 (const gchar * format)
1128 {
1129   const gchar *p = format;
1130   gchar *promoted_format;
1131 
1132   /* Must be called with a validated format! */
1133   g_return_val_if_fail (validate_format (format), NULL);
1134 
1135   /* it starts with % */
1136   p++;
1137 
1138   /* Following the % must be a 0, or any of d, x or u.
1139    * x and u are not part of the spec, but don't hurt us
1140    */
1141   if (p[0] == '0') {
1142     p++;
1143 
1144     while (g_ascii_isdigit (*p))
1145       p++;
1146   }
1147 
1148   /* After any 0 and alphanumeric values, there must be a d.
1149    * Otherwise validation would have failed
1150    */
1151   g_assert (p[0] == 'd');
1152 
1153   promoted_format =
1154       g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
1155       format, p);
1156 
1157   return promoted_format;
1158 }
1159 
1160 static gboolean
gst_mpdparser_validate_rfc1738_url(const char * s)1161 gst_mpdparser_validate_rfc1738_url (const char *s)
1162 {
1163   while (*s) {
1164     if (!strchr
1165         (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
1166             *s))
1167       return FALSE;
1168     if (*s == '%') {
1169       /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
1170          operator, so this is safe for strings ending before two hex digits */
1171       if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
1172         return FALSE;
1173       s += 2;
1174     }
1175     s++;
1176   }
1177   return TRUE;
1178 }
1179 
1180 void
gst_mpdparser_media_fragment_info_clear(GstMediaFragmentInfo * fragment)1181 gst_mpdparser_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
1182 {
1183   g_free (fragment->uri);
1184   g_free (fragment->index_uri);
1185 }
1186 
1187 /* API */
1188 gboolean
gst_mpdparser_get_mpd_root_node(GstMPDRootNode ** mpd_root_node,const gchar * data,gint size)1189 gst_mpdparser_get_mpd_root_node (GstMPDRootNode ** mpd_root_node,
1190     const gchar * data, gint size)
1191 {
1192   gboolean ret = FALSE;
1193 
1194   if (data) {
1195     xmlDocPtr doc;
1196     xmlNode *root_element = NULL;
1197 
1198     GST_DEBUG ("MPD file fully buffered, start parsing...");
1199 
1200     /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
1201 
1202     /* this initialize the library and check potential ABI mismatches
1203      * between the version it was compiled for and the actual shared
1204      * library used
1205      */
1206     LIBXML_TEST_VERSION;
1207 
1208     /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
1209     doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
1210     if (doc == NULL) {
1211       GST_ERROR ("failed to parse the MPD file");
1212       ret = FALSE;
1213     } else {
1214       /* get the root element node */
1215       root_element = xmlDocGetRootElement (doc);
1216 
1217       if (root_element->type != XML_ELEMENT_NODE
1218           || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
1219         GST_ERROR
1220             ("can not find the root element MPD, failed to parse the MPD file");
1221         ret = FALSE;            /* used to return TRUE before, but this seems wrong */
1222       } else {
1223         /* now we can parse the MPD root node and all children nodes, recursively */
1224         ret = gst_mpdparser_parse_root_node (mpd_root_node, root_element);
1225       }
1226       /* free the document */
1227       xmlFreeDoc (doc);
1228     }
1229   }
1230 
1231   return ret;
1232 }
1233 
1234 GstMPDSegmentListNode *
gst_mpdparser_get_external_segment_list(const gchar * data,gint size,GstMPDSegmentListNode * parent)1235 gst_mpdparser_get_external_segment_list (const gchar * data, gint size,
1236     GstMPDSegmentListNode * parent)
1237 {
1238   xmlDocPtr doc = NULL;
1239   GstMPDSegmentListNode *new_segment_list = NULL;
1240 
1241   doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
1242 
1243 
1244   /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
1245    * in external xml is allowed, however, multiple SegmentList does not make sense
1246    * because Period/AdaptationSet/Representation allow only one SegmentList */
1247   if (doc) {
1248     xmlNode *root_element = xmlDocGetRootElement (doc);
1249 
1250 
1251     if (root_element->type == XML_ELEMENT_NODE &&
1252         xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") == 0) {
1253       gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
1254           parent);
1255     }
1256   }
1257 
1258   if (doc)
1259     xmlFreeDoc (doc);
1260 
1261   return new_segment_list;
1262 }
1263 
1264 GList *
gst_mpdparser_get_external_periods(const gchar * data,gint size)1265 gst_mpdparser_get_external_periods (const gchar * data, gint size)
1266 {
1267   xmlDocPtr doc = NULL;
1268   GList *new_periods = NULL;
1269 
1270   doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
1271 
1272 
1273   if (doc) {
1274     xmlNode *root_element = xmlDocGetRootElement (doc);
1275     xmlNode *iter;
1276 
1277     for (iter = root_element->children; iter; iter = iter->next) {
1278       if (iter->type == XML_ELEMENT_NODE) {
1279         if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
1280           gst_mpdparser_parse_period_node (&new_periods, iter);
1281         } else {
1282           goto error;
1283         }
1284       }
1285     }
1286   }
1287 
1288 done:
1289   if (doc)
1290     xmlFreeDoc (doc);
1291 
1292   return new_periods;
1293 
1294 error:
1295   GST_ERROR ("Failed to parse period node XML");
1296 
1297   if (new_periods) {
1298     g_list_free_full (new_periods, (GDestroyNotify) gst_mpd_period_node_free);
1299     new_periods = NULL;
1300   }
1301   goto done;
1302 }
1303 
1304 GList *
gst_mpdparser_get_external_adaptation_sets(const gchar * data,gint size,GstMPDPeriodNode * period)1305 gst_mpdparser_get_external_adaptation_sets (const gchar * data, gint size,
1306     GstMPDPeriodNode * period)
1307 {
1308   xmlDocPtr doc = NULL;
1309   GList *new_adaptation_sets = NULL;
1310 
1311   doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
1312 
1313   /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
1314    * in external xml is allowed */
1315   if (doc) {
1316     xmlNode *root_element = xmlDocGetRootElement (doc);
1317     if (root_element->type == XML_ELEMENT_NODE &&
1318         xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") == 0) {
1319       gst_mpdparser_parse_adaptation_set_node (&new_adaptation_sets,
1320           root_element, period);
1321     }
1322   }
1323 
1324   if (doc)
1325     xmlFreeDoc (doc);
1326 
1327   return new_adaptation_sets;
1328 }
1329 
1330 void
gst_mpdparser_free_stream_period(GstStreamPeriod * stream_period)1331 gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
1332 {
1333   if (stream_period) {
1334     g_slice_free (GstStreamPeriod, stream_period);
1335   }
1336 }
1337 
1338 void
gst_mpdparser_free_media_segment(GstMediaSegment * media_segment)1339 gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
1340 {
1341   if (media_segment) {
1342     g_slice_free (GstMediaSegment, media_segment);
1343   }
1344 }
1345 
1346 void
gst_mpdparser_init_active_stream_segments(GstActiveStream * stream)1347 gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
1348 {
1349   g_assert (stream->segments == NULL);
1350   stream->segments = g_ptr_array_new ();
1351   g_ptr_array_set_free_func (stream->segments,
1352       (GDestroyNotify) gst_mpdparser_free_media_segment);
1353 }
1354 
1355 void
gst_mpdparser_free_active_stream(GstActiveStream * active_stream)1356 gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
1357 {
1358   if (active_stream) {
1359     g_free (active_stream->baseURL);
1360     active_stream->baseURL = NULL;
1361     g_free (active_stream->queryURL);
1362     active_stream->queryURL = NULL;
1363     if (active_stream->segments)
1364       g_ptr_array_unref (active_stream->segments);
1365     g_slice_free (GstActiveStream, active_stream);
1366   }
1367 }
1368 
1369 const gchar *
gst_mpdparser_get_initializationURL(GstActiveStream * stream,GstMPDURLTypeNode * InitializationURL)1370 gst_mpdparser_get_initializationURL (GstActiveStream * stream,
1371     GstMPDURLTypeNode * InitializationURL)
1372 {
1373   const gchar *url_prefix;
1374 
1375   g_return_val_if_fail (stream != NULL, NULL);
1376 
1377   url_prefix = (InitializationURL
1378       && InitializationURL->sourceURL) ? InitializationURL->sourceURL : stream->
1379       baseURL;
1380 
1381   return url_prefix;
1382 }
1383 
1384 gchar *
gst_mpdparser_get_mediaURL(GstActiveStream * stream,GstMPDSegmentURLNode * segmentURL)1385 gst_mpdparser_get_mediaURL (GstActiveStream * stream,
1386     GstMPDSegmentURLNode * segmentURL)
1387 {
1388   const gchar *url_prefix;
1389 
1390   g_return_val_if_fail (stream != NULL, NULL);
1391   g_return_val_if_fail (segmentURL != NULL, NULL);
1392 
1393   url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
1394   g_return_val_if_fail (url_prefix != NULL, NULL);
1395 
1396   return segmentURL->media;
1397 }
1398 
1399 /* navigation functions */
1400 GstStreamMimeType
gst_mpdparser_representation_get_mimetype(GstMPDAdaptationSetNode * adapt_set,GstMPDRepresentationNode * rep)1401 gst_mpdparser_representation_get_mimetype (GstMPDAdaptationSetNode * adapt_set,
1402     GstMPDRepresentationNode * rep)
1403 {
1404   gchar *mime = NULL;
1405   if (rep)
1406     mime = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
1407   if (mime == NULL) {
1408     mime = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
1409   }
1410 
1411   if (gst_mpd_helper_strncmp_ext (mime, "audio") == 0)
1412     return GST_STREAM_AUDIO;
1413   if (gst_mpd_helper_strncmp_ext (mime, "video") == 0)
1414     return GST_STREAM_VIDEO;
1415   if (gst_mpd_helper_strncmp_ext (mime, "application") == 0
1416       || gst_mpd_helper_strncmp_ext (mime, "text") == 0)
1417     return GST_STREAM_APPLICATION;
1418 
1419   return GST_STREAM_UNKNOWN;
1420 }
1421 
1422 /* Helper methods */
1423 gchar *
gst_mpdparser_build_URL_from_template(const gchar * url_template,const gchar * id,guint number,guint bandwidth,guint64 time)1424 gst_mpdparser_build_URL_from_template (const gchar * url_template,
1425     const gchar * id, guint number, guint bandwidth, guint64 time)
1426 {
1427   static const gchar default_format[] = "%01d";
1428   gchar **tokens, *token, *ret;
1429   const gchar *format;
1430   gint i, num_tokens;
1431 
1432   g_return_val_if_fail (url_template != NULL, NULL);
1433   tokens = g_strsplit_set (url_template, "$", -1);
1434   if (!tokens) {
1435     GST_WARNING ("Scan of URL template failed!");
1436     return NULL;
1437   }
1438   num_tokens = g_strv_length (tokens);
1439 
1440   /*
1441    * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
1442    * An even number of tokens means the string is not valid.
1443    */
1444   if ((num_tokens & 1) == 0) {
1445     GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
1446         num_tokens, url_template);
1447     g_strfreev (tokens);
1448     return NULL;
1449   }
1450 
1451   for (i = 0; i < num_tokens; i++) {
1452     token = tokens[i];
1453     format = default_format;
1454 
1455     /* the tokens to replace must be provided between $ characters, eg $token$
1456      * For a string like token0$token1$token2$token3$token4, only the odd number
1457      * tokens (1,3,...) must be parsed.
1458      *
1459      * Skip even tokens
1460      */
1461     if ((i & 1) == 0)
1462       continue;
1463 
1464     if (!g_strcmp0 (token, "RepresentationID")) {
1465       if (!gst_mpdparser_validate_rfc1738_url (id))
1466         goto invalid_representation_id;
1467 
1468       tokens[i] = g_strdup_printf ("%s", id);
1469       g_free (token);
1470     } else if (!strncmp (token, "Number", 6)) {
1471       if (strlen (token) > 6) {
1472         format = token + 6;     /* format tag */
1473       }
1474       if (!validate_format (format))
1475         goto invalid_format;
1476 
1477       tokens[i] = g_strdup_printf (format, number);
1478       g_free (token);
1479     } else if (!strncmp (token, "Bandwidth", 9)) {
1480       if (strlen (token) > 9) {
1481         format = token + 9;     /* format tag */
1482       }
1483       if (!validate_format (format))
1484         goto invalid_format;
1485 
1486       tokens[i] = g_strdup_printf (format, bandwidth);
1487       g_free (token);
1488     } else if (!strncmp (token, "Time", 4)) {
1489       gchar *promoted_format;
1490 
1491       if (strlen (token) > 4) {
1492         format = token + 4;     /* format tag */
1493       }
1494       if (!validate_format (format))
1495         goto invalid_format;
1496 
1497       promoted_format = promote_format_to_uint64 (format);
1498       tokens[i] = g_strdup_printf (promoted_format, time);
1499       g_free (promoted_format);
1500       g_free (token);
1501     } else if (!g_strcmp0 (token, "")) {
1502       tokens[i] = g_strdup_printf ("%s", "$");
1503       g_free (token);
1504     } else {
1505       /* unexpected identifier found between $ signs
1506        *
1507        * "If the URL contains unescaped $ symbols which do not enclose a valid
1508        * identifier then the result of URL formation is undefined"
1509        */
1510       goto invalid_format;
1511     }
1512   }
1513 
1514   ret = g_strjoinv (NULL, tokens);
1515 
1516   g_strfreev (tokens);
1517 
1518   return ret;
1519 
1520 invalid_format:
1521   {
1522     GST_ERROR ("Invalid format '%s' in '%s'", format, token);
1523 
1524     g_strfreev (tokens);
1525 
1526     return NULL;
1527   }
1528 invalid_representation_id:
1529   {
1530     GST_ERROR
1531         ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
1532         id);
1533 
1534     g_strfreev (tokens);
1535 
1536     return NULL;
1537   }
1538 }
1539