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