2 * DASH MPD parsing library
6 * Copyright (C) 2012 STMicroelectronics
9 * Gianluca Gennari <gennarone@gmail.com>
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.
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.
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.
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include "gstmpdparser.h"
31 #include "gstdash_debug.h"
33 #define GST_CAT_DEFAULT gst_dash_demux_debug
35 /* Property parsing */
36 static gboolean gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
37 const gchar * property_name, gchar ** property_value,
38 gboolean (*validator) (const char *));
39 static gboolean gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
40 const gchar * property_name, gchar ** property_value);
41 static gboolean gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
42 const gchar * property_name, gchar ** property_value);
43 static gboolean gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
44 const gchar * ns_name, const gchar * property_name,
45 gchar ** property_value);
46 static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
47 const gchar * property_name, gchar *** property_value);
48 static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
49 const gchar * property_name, gint default_val, gint * property_value);
50 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
51 const gchar * property_name, guint default_val, guint * property_value);
52 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
53 a_node, const gchar * property_name, guint64 default_val,
54 guint64 * property_value);
55 static gboolean gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
56 const gchar * property_name, guint ** property_value, guint * value_size);
57 static gboolean gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
58 const gchar * property_name, gdouble * property_value);
59 static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
60 const gchar * property_name, gboolean default_val,
61 gboolean * property_value);
62 static gboolean gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
63 const gchar * property_name, GstMPDFileType * property_value);
64 static gboolean gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
65 const gchar * property_name, GstSAPType * property_value);
66 static gboolean gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
67 const gchar * property_name, GstRange ** property_value);
68 static gboolean gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
69 const gchar * property_name, GstRatio ** property_value);
70 static gboolean gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
71 const gchar * property_name, GstFrameRate ** property_value);
72 static gboolean gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
73 const gchar * property_name, GstConditionalUintType ** property_value);
74 static gboolean gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
75 const gchar * property_name, GstDateTime ** property_value);
76 static gboolean gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
77 const gchar * property_name, guint64 default_value,
78 guint64 * property_value);
79 static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
81 static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
82 const gchar * prefix);
83 static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
86 /* XML node parsing */
87 static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
88 static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
90 static void gst_mpdparser_parse_content_component_node (GList ** list,
92 static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
93 static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
95 static void gst_mpdparser_parse_segment_url_node (GList ** list,
97 static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
99 static void gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType **
100 pointer, xmlNode * a_node, GstSegmentBaseType * parent);
101 static void gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node);
102 static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
103 pointer, xmlNode * a_node);
105 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
106 xmlNode * a_node, GstMultSegmentBaseType * parent);
107 static gboolean gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
108 pointer, xmlNode * a_node, GstSegmentListNode * parent);
110 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
111 pointer, xmlNode * a_node);
112 static gboolean gst_mpdparser_parse_representation_node (GList ** list,
113 xmlNode * a_node, GstAdaptationSetNode * parent,
114 GstPeriodNode * period_node);
115 static gboolean gst_mpdparser_parse_adaptation_set_node (GList ** list,
116 xmlNode * a_node, GstPeriodNode * parent);
117 static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
119 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
120 xmlNode * a_node, GstSegmentTemplateNode * parent);
121 static gboolean gst_mpdparser_parse_period_node (GList ** list,
123 static void gst_mpdparser_parse_program_info_node (GList ** list,
125 static void gst_mpdparser_parse_metrics_range_node (GList ** list,
127 static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
128 static gboolean gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
130 static void gst_mpdparser_parse_utctiming_node (GList ** list,
133 /* Helper functions */
134 static guint convert_to_millisecs (guint decimals, gint pos);
135 static int strncmp_ext (const char *s1, const char *s2);
136 static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
137 static GstSNode *gst_mpdparser_clone_s_node (GstSNode * pointer);
138 static GstSegmentTimelineNode
139 * gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer);
140 static GstRange *gst_mpdparser_clone_range (GstRange * range);
141 static GstURLType *gst_mpdparser_clone_URL (GstURLType * url);
142 static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
143 GstActiveStream * stream, gchar ** query);
144 static GstSegmentURLNode *gst_mpdparser_clone_segment_url (GstSegmentURLNode *
146 static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
147 GstSegmentURLNode * segmentURL);
148 static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream *
149 stream, GstURLType * InitializationURL);
150 static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
151 const gchar * id, guint number, guint bandwidth, guint64 time);
152 static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
153 GstSegmentURLNode * url_node, guint number, gint repeat,
154 guint64 scale_start, guint64 scale_duration, GstClockTime start,
155 GstClockTime duration);
156 static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
157 static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client,
158 GstActiveStream * stream, guint64 * scale_duration);
159 static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient *
163 static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
166 static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
168 static GstRepresentationNode
169 * gst_mpdparser_get_representation_with_max_bandwidth (GList *
170 Representations, gint max_bandwidth);
172 static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
173 Period, GstAdaptationSetNode * AdaptationSet,
174 GstRepresentationNode * Representation);
175 static GstSegmentListNode *gst_mpdparser_get_segment_list (GstMpdClient *
176 client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
177 GstRepresentationNode * Representation);
180 static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
181 GstActiveStream * stream);
183 /* Memory management */
184 static GstSegmentTimelineNode *gst_mpdparser_segment_timeline_node_new (void);
185 static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
186 static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
188 static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
189 static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
191 static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
192 static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
193 static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
194 segment_template_node);
196 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
197 representation_base);
198 static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
199 adaptation_set_node);
200 static void gst_mpdparser_free_representation_node (GstRepresentationNode *
201 representation_node);
202 static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
204 static void gst_mpdparser_free_s_node (GstSNode * s_node);
205 static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
207 static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
208 static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
210 static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
212 static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
214 static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
216 static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
217 static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
219 static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
220 content_component_node);
221 static void gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type);
222 static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
223 static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
224 static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
226 static GstUri *combine_urls (GstUri * base, GList * list, gchar ** query,
229 static GList *gst_mpd_client_fetch_external_period (GstMpdClient * client,
230 GstPeriodNode * period_node);
231 static GList *gst_mpd_client_fetch_external_adaptation_set (GstMpdClient *
232 client, GstPeriodNode * period, GstAdaptationSetNode * adapt_set);
234 struct GstMpdParserUtcTimingMethod
237 GstMPDUTCTimingType method;
240 static const struct GstMpdParserUtcTimingMethod
241 gst_mpdparser_utc_timing_methods[] = {
242 {"urn:mpeg:dash:utc:ntp:2014", GST_MPD_UTCTIMING_TYPE_NTP},
243 {"urn:mpeg:dash:utc:sntp:2014", GST_MPD_UTCTIMING_TYPE_SNTP},
244 {"urn:mpeg:dash:utc:http-head:2014", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
245 {"urn:mpeg:dash:utc:http-xsdate:2014", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
246 {"urn:mpeg:dash:utc:http-iso:2014", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
247 {"urn:mpeg:dash:utc:http-ntp:2014", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
248 {"urn:mpeg:dash:utc:direct:2014", GST_MPD_UTCTIMING_TYPE_DIRECT},
250 * Early working drafts used the :2012 namespace and this namespace is
251 * used by some DASH packagers. To work-around these packagers, we also
252 * accept the early draft scheme names.
254 {"urn:mpeg:dash:utc:ntp:2012", GST_MPD_UTCTIMING_TYPE_NTP},
255 {"urn:mpeg:dash:utc:sntp:2012", GST_MPD_UTCTIMING_TYPE_SNTP},
256 {"urn:mpeg:dash:utc:http-head:2012", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
257 {"urn:mpeg:dash:utc:http-xsdate:2012", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
258 {"urn:mpeg:dash:utc:http-iso:2012", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
259 {"urn:mpeg:dash:utc:http-ntp:2012", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
260 {"urn:mpeg:dash:utc:direct:2012", GST_MPD_UTCTIMING_TYPE_DIRECT},
264 /* functions to parse node namespaces, content and properties */
266 gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
267 const gchar * property_name, gchar ** property_value,
268 gboolean (*validate) (const char *))
270 xmlChar *prop_string;
271 gboolean exists = FALSE;
273 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
275 if (validate && !(*validate) ((const char *) prop_string)) {
276 GST_WARNING ("Validation failure: %s", prop_string);
277 xmlFree (prop_string);
280 *property_value = (gchar *) prop_string;
282 GST_LOG (" - %s: %s", property_name, prop_string);
289 gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
290 const gchar * ns_name, const gchar * property_name, gchar ** property_value)
292 xmlChar *prop_string;
293 gboolean exists = FALSE;
296 xmlGetNsProp (a_node, (const xmlChar *) property_name,
297 (const xmlChar *) ns_name);
299 *property_value = (gchar *) prop_string;
301 GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
308 gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
309 const gchar * property_name, gchar ** property_value)
311 return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
312 property_value, NULL);
316 gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
317 const gchar * property_name, gchar ** property_value)
321 gst_mpdparser_get_xml_prop_string (a_node, property_name, property_value);
323 *property_value = g_strstrip (*property_value);
328 gst_mpdparser_validate_no_whitespace (const char *s)
330 return !strpbrk (s, "\r\n\t ");
334 gst_mpdparser_get_xml_prop_string_no_whitespace (xmlNode * a_node,
335 const gchar * property_name, gchar ** property_value)
337 return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
338 property_value, gst_mpdparser_validate_no_whitespace);
342 gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
343 const gchar * property_name, gchar *** property_value)
345 xmlChar *prop_string;
346 gchar **prop_string_vector = NULL;
348 gboolean exists = FALSE;
350 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
352 prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
353 if (prop_string_vector) {
355 *property_value = prop_string_vector;
356 GST_LOG (" - %s:", property_name);
357 while (prop_string_vector[i]) {
358 GST_LOG (" %s", prop_string_vector[i]);
362 GST_WARNING ("Scan of string vector property failed!");
364 xmlFree (prop_string);
371 gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
372 const gchar * property_name, gint default_val, gint * property_value)
374 xmlChar *prop_string;
375 gboolean exists = FALSE;
377 *property_value = default_val;
378 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
380 if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
382 GST_LOG (" - %s: %d", property_name, *property_value);
385 ("failed to parse signed integer property %s from xml string %s",
386 property_name, prop_string);
388 xmlFree (prop_string);
395 gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
396 const gchar * property_name, guint default_val, guint * property_value)
398 xmlChar *prop_string;
399 gboolean exists = FALSE;
401 *property_value = default_val;
402 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
404 if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
405 strstr ((gchar *) prop_string, "-") == NULL) {
407 GST_LOG (" - %s: %u", property_name, *property_value);
410 ("failed to parse unsigned integer property %s from xml string %s",
411 property_name, prop_string);
412 /* sscanf might have written to *property_value. Restore to default */
413 *property_value = default_val;
415 xmlFree (prop_string);
422 gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
423 const gchar * property_name, guint64 default_val, guint64 * property_value)
425 xmlChar *prop_string;
426 gboolean exists = FALSE;
428 *property_value = default_val;
429 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
431 if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
432 property_value) == 1 &&
433 strstr ((gchar *) prop_string, "-") == NULL) {
435 GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
438 ("failed to parse unsigned integer property %s from xml string %s",
439 property_name, prop_string);
440 /* sscanf might have written to *property_value. Restore to default */
441 *property_value = default_val;
443 xmlFree (prop_string);
450 gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
451 const gchar * property_name, guint ** property_value, guint * value_size)
453 xmlChar *prop_string;
455 guint *prop_uint_vector = NULL, i;
456 gboolean exists = FALSE;
458 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
460 str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
462 *value_size = g_strv_length (str_vector);
463 prop_uint_vector = g_malloc (*value_size * sizeof (guint));
464 if (prop_uint_vector) {
466 GST_LOG (" - %s:", property_name);
467 for (i = 0; i < *value_size; i++) {
468 if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
469 && strstr (str_vector[i], "-") == NULL) {
470 GST_LOG (" %u", prop_uint_vector[i]);
473 ("failed to parse uint vector type property %s from xml string %s",
474 property_name, str_vector[i]);
475 /* there is no special value to put in prop_uint_vector[i] to
476 * signal it is invalid, so we just clean everything and return
479 g_free (prop_uint_vector);
480 prop_uint_vector = NULL;
485 *property_value = prop_uint_vector;
487 GST_WARNING ("Array allocation failed!");
490 GST_WARNING ("Scan of uint vector property failed!");
492 xmlFree (prop_string);
493 g_strfreev (str_vector);
500 gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
501 const gchar * property_name, gdouble * property_value)
503 xmlChar *prop_string;
504 gboolean exists = FALSE;
506 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
508 if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
510 GST_LOG (" - %s: %lf", property_name, *property_value);
512 GST_WARNING ("failed to parse double property %s from xml string %s",
513 property_name, prop_string);
515 xmlFree (prop_string);
522 gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
523 const gchar * property_name, gboolean default_val,
524 gboolean * property_value)
526 xmlChar *prop_string;
527 gboolean exists = FALSE;
529 *property_value = default_val;
530 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
532 if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
534 *property_value = FALSE;
535 GST_LOG (" - %s: false", property_name);
536 } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
538 *property_value = TRUE;
539 GST_LOG (" - %s: true", property_name);
541 GST_WARNING ("failed to parse boolean property %s from xml string %s",
542 property_name, prop_string);
544 xmlFree (prop_string);
551 gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
552 const gchar * property_name, GstMPDFileType * property_value)
554 xmlChar *prop_string;
555 gboolean exists = FALSE;
557 *property_value = GST_MPD_FILE_TYPE_STATIC; /* default */
558 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
560 if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
561 || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
563 *property_value = GST_MPD_FILE_TYPE_STATIC;
564 GST_LOG (" - %s: static", property_name);
565 } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
566 || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
568 *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
569 GST_LOG (" - %s: dynamic", property_name);
571 GST_WARNING ("failed to parse MPD type property %s from xml string %s",
572 property_name, prop_string);
574 xmlFree (prop_string);
581 gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
582 const gchar * property_name, GstSAPType * property_value)
584 xmlChar *prop_string;
585 guint prop_SAP_type = 0;
586 gboolean exists = FALSE;
588 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
590 if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
591 && prop_SAP_type <= 6) {
593 *property_value = (GstSAPType) prop_SAP_type;
594 GST_LOG (" - %s: %u", property_name, prop_SAP_type);
597 ("failed to parse unsigned integer property %s from xml string %s",
598 property_name, prop_string);
600 xmlFree (prop_string);
607 gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property_name,
608 GstRange ** property_value)
610 xmlChar *prop_string;
611 guint64 first_byte_pos = 0, last_byte_pos = -1;
614 gboolean exists = FALSE;
616 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
618 len = xmlStrlen (prop_string);
619 str = (gchar *) prop_string;
620 GST_TRACE ("range: %s, len %d", str, len);
623 pos = strcspn (str, "-");
625 GST_TRACE ("pos %d >= len %d", pos, len);
628 /* read first_byte_pos */
630 /* replace str[pos] with '\0' to allow sscanf to not be confused by
631 * the minus sign (eg " -1" (observe the space before -) would otherwise
632 * be interpreted as range -1 to 1)
635 if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
636 strstr (str, "-") != NULL) {
637 /* sscanf failed or it found a negative number */
638 /* restore the '-' sign */
642 /* restore the '-' sign */
645 /* read last_byte_pos */
646 if (pos < (len - 1)) {
647 if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
648 strstr (str + pos + 1, "-") != NULL) {
652 /* malloc return data structure */
653 *property_value = g_slice_new0 (GstRange);
655 (*property_value)->first_byte_pos = first_byte_pos;
656 (*property_value)->last_byte_pos = last_byte_pos;
657 xmlFree (prop_string);
658 GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
659 property_name, first_byte_pos, last_byte_pos);
665 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
667 xmlFree (prop_string);
672 gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
673 const gchar * property_name, GstRatio ** property_value)
675 xmlChar *prop_string;
676 guint num = 0, den = 1;
679 gboolean exists = FALSE;
681 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
683 len = xmlStrlen (prop_string);
684 str = (gchar *) prop_string;
685 GST_TRACE ("ratio: %s, len %d", str, len);
688 pos = strcspn (str, ":");
690 GST_TRACE ("pos %d >= len %d", pos, len);
693 /* search for negative sign */
694 if (strstr (str, "-") != NULL) {
699 if (sscanf (str, "%u", &num) != 1) {
704 if (pos < (len - 1)) {
705 if (sscanf (str + pos + 1, "%u", &den) != 1) {
709 /* malloc return data structure */
710 *property_value = g_slice_new0 (GstRatio);
712 (*property_value)->num = num;
713 (*property_value)->den = den;
714 xmlFree (prop_string);
715 GST_LOG (" - %s: %u:%u", property_name, num, den);
721 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
723 xmlFree (prop_string);
728 gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
729 const gchar * property_name, GstFrameRate ** property_value)
731 xmlChar *prop_string;
732 guint num = 0, den = 1;
735 gboolean exists = FALSE;
737 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
739 len = xmlStrlen (prop_string);
740 str = (gchar *) prop_string;
741 GST_TRACE ("framerate: %s, len %d", str, len);
743 /* search for negative sign */
744 if (strstr (str, "-") != NULL) {
748 /* read "/" if available */
749 pos = strcspn (str, "/");
752 if (sscanf (str, "%u", &num) != 1) {
756 /* read den (if available) */
757 if (pos < (len - 1)) {
758 if (sscanf (str + pos + 1, "%u", &den) != 1) {
762 /* alloc return data structure */
763 *property_value = g_slice_new0 (GstFrameRate);
765 (*property_value)->num = num;
766 (*property_value)->den = den;
767 xmlFree (prop_string);
769 GST_LOG (" - %s: %u", property_name, num);
771 GST_LOG (" - %s: %u/%u", property_name, num, den);
777 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
779 xmlFree (prop_string);
784 gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
785 const gchar * property_name, GstConditionalUintType ** property_value)
787 xmlChar *prop_string;
791 gboolean exists = FALSE;
793 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
795 str = (gchar *) prop_string;
796 GST_TRACE ("conditional uint: %s", str);
798 if (strcmp (str, "false") == 0) {
801 } else if (strcmp (str, "true") == 0) {
806 if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
810 /* alloc return data structure */
811 *property_value = g_slice_new0 (GstConditionalUintType);
813 (*property_value)->flag = flag;
814 (*property_value)->value = val;
815 xmlFree (prop_string);
816 GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
823 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
825 xmlFree (prop_string);
832 The dateTime data type is used to specify a date and a time.
834 The lexical form of xs:dateTime is YYYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
836 * YYYY indicates the year
837 * MM indicates the month
838 * DD indicates the day
839 * T indicates the start of the required time section
840 * hh indicates the hour
841 * mm indicates the minute
842 * ss indicates the second
844 The time zone may be specified as Z (UTC) or (+|-)hh:mm
847 gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
848 const gchar * property_name, GstDateTime ** property_value)
850 xmlChar *prop_string;
853 gint year, month, day, hour, minute;
855 gboolean exists = FALSE;
856 gfloat tzoffset = 0.0;
857 gint gmt_offset_hour = -99, gmt_offset_min = -99;
859 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
861 str = (gchar *) prop_string;
862 GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
864 ret = sscanf (str, "%d", &year);
865 if (ret != 1 || year <= 0)
867 pos = strcspn (str, "-");
869 GST_TRACE (" - year %d", year);
871 ret = sscanf (str, "%d", &month);
872 if (ret != 1 || month <= 0)
874 pos = strcspn (str, "-");
876 GST_TRACE (" - month %d", month);
878 ret = sscanf (str, "%d", &day);
879 if (ret != 1 || day <= 0)
881 pos = strcspn (str, "T");
883 GST_TRACE (" - day %d", day);
885 ret = sscanf (str, "%d", &hour);
886 if (ret != 1 || hour < 0)
888 pos = strcspn (str, ":");
890 GST_TRACE (" - hour %d", hour);
892 ret = sscanf (str, "%d", &minute);
893 if (ret != 1 || minute < 0)
895 pos = strcspn (str, ":");
897 GST_TRACE (" - minute %d", minute);
899 ret = sscanf (str, "%lf", &second);
900 if (ret != 1 || second < 0)
902 GST_TRACE (" - second %lf", second);
904 GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
905 year, month, day, hour, minute, second);
907 if (strrchr (str, '+') || strrchr (str, '-')) {
908 /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
909 gint gmt_offset = -1;
910 gchar *plus_pos = NULL;
911 gchar *neg_pos = NULL;
914 GST_LOG ("Checking for timezone information");
916 /* check if there is timezone info */
917 plus_pos = strrchr (str, '+');
918 neg_pos = strrchr (str, '-');
924 if (pos && strlen (pos) >= 3) {
927 ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
929 ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
931 GST_DEBUG ("Parsing timezone: %s", pos);
934 if (neg_pos != NULL && neg_pos + 1 == pos) {
935 gmt_offset_hour *= -1;
936 gmt_offset_min *= -1;
938 gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
940 tzoffset = gmt_offset / 60.0;
942 GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
944 GST_WARNING ("Failed to parse timezone information");
950 gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
951 xmlFree (prop_string);
957 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
959 xmlFree (prop_string);
967 The duration data type is used to specify a time interval.
969 The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
971 * - indicates the negative sign (optional)
972 * P indicates the period (required)
973 * nY indicates the number of years
974 * nM indicates the number of months
975 * nD indicates the number of days
976 * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
977 * nH indicates the number of hours
978 * nM indicates the number of minutes
979 * nS indicates the number of seconds
982 /* this function computes decimals * 10 ^ (3 - pos) */
984 convert_to_millisecs (guint decimals, gint pos)
986 guint num = 1, den = 1;
997 /* if i == 0 we have exactly 3 decimals and nothing to do */
998 return decimals * num / den;
1002 accumulate (guint64 * v, guint64 mul, guint64 add)
1006 if (*v > G_MAXUINT64 / mul)
1009 if (tmp > G_MAXUINT64 - add)
1016 gst_mpdparser_parse_duration (const char *str, guint64 * value)
1018 gint ret, len, pos, posT;
1019 gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
1020 -1, decimals = -1, read;
1021 gboolean have_ms = FALSE;
1025 GST_TRACE ("duration: %s, len %d", str, len);
1026 if (strspn (str, "PT0123456789., \tHMDSY") < len) {
1027 GST_WARNING ("Invalid character found: '%s'", str);
1030 /* skip leading/trailing whitespace */
1031 while (g_ascii_isspace (str[0])) {
1035 while (len > 0 && g_ascii_isspace (str[len - 1]))
1038 /* read "P" for period */
1039 if (str[0] != 'P') {
1040 GST_WARNING ("P not found at the beginning of the string!");
1046 /* read "T" for time (if present) */
1047 posT = strcspn (str, "T");
1050 /* there is some room between P and T, so there must be a period section */
1051 /* read years, months, days */
1053 GST_TRACE ("parsing substring %s", str);
1054 pos = strcspn (str, "YMD");
1055 ret = sscanf (str, "%u", &read);
1057 GST_WARNING ("can not read integer value from string %s!", str);
1062 if (years != -1 || months != -1 || days != -1) {
1063 GST_WARNING ("year, month or day was already set");
1069 if (months != -1 || days != -1) {
1070 GST_WARNING ("month or day was already set");
1075 GST_WARNING ("Month out of range");
1081 GST_WARNING ("day was already set");
1086 GST_WARNING ("Day out of range");
1091 GST_WARNING ("unexpected char %c!", str[pos]);
1095 GST_TRACE ("read number %u type %c", read, str[pos]);
1108 GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
1110 /* read "T" for time (if present) */
1111 /* here T is at pos == 0 */
1116 /* T found, there is a time section */
1117 /* read hours, minutes, seconds, hundredths of second */
1119 GST_TRACE ("parsing substring %s", str);
1120 pos = strcspn (str, "HMS,.");
1121 ret = sscanf (str, "%u", &read);
1123 GST_WARNING ("can not read integer value from string %s!", str);
1128 if (hours != -1 || minutes != -1 || seconds != -1) {
1129 GST_WARNING ("hour, minute or second was already set");
1134 GST_WARNING ("Hour out of range");
1139 if (minutes != -1 || seconds != -1) {
1140 GST_WARNING ("minute or second was already set");
1144 if (minutes >= 60) {
1145 GST_WARNING ("Minute out of range");
1151 /* we have read the decimal part of the seconds */
1152 decimals = convert_to_millisecs (read, pos);
1153 GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
1156 if (seconds != -1) {
1157 GST_WARNING ("second was already set");
1166 /* we have read the integer part of a decimal number in seconds */
1167 if (seconds != -1) {
1168 GST_WARNING ("second was already set");
1175 GST_WARNING ("unexpected char %c!", str[pos]);
1179 GST_TRACE ("read number %u type %c", read, str[pos]);
1193 GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
1196 if (!accumulate (&tmp_value, 1, years)
1197 || !accumulate (&tmp_value, 365, months * 30)
1198 || !accumulate (&tmp_value, 1, days)
1199 || !accumulate (&tmp_value, 24, hours)
1200 || !accumulate (&tmp_value, 60, minutes)
1201 || !accumulate (&tmp_value, 60, seconds)
1202 || !accumulate (&tmp_value, 1000, decimals))
1205 /* ensure it can be converted from milliseconds to nanoseconds */
1206 if (tmp_value > G_MAXUINT64 / 1000000)
1217 gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
1218 const gchar * property_name, guint64 default_value,
1219 guint64 * property_value)
1221 xmlChar *prop_string;
1223 gboolean exists = FALSE;
1225 *property_value = default_value;
1226 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
1228 str = (gchar *) prop_string;
1229 if (!gst_mpdparser_parse_duration (str, property_value))
1231 GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
1232 xmlFree (prop_string);
1238 xmlFree (prop_string);
1243 gst_mpdparser_get_xml_node_content (xmlNode * a_node, gchar ** content)
1245 xmlChar *node_content = NULL;
1246 gboolean exists = FALSE;
1248 node_content = xmlNodeGetContent (a_node);
1251 *content = (gchar *) node_content;
1252 GST_LOG (" - %s: %s", a_node->name, *content);
1259 gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
1261 gboolean exists = FALSE;
1262 const char *txt_encoding;
1263 xmlOutputBufferPtr out_buf;
1265 txt_encoding = (const char *) a_node->doc->encoding;
1266 out_buf = xmlAllocOutputBuffer (NULL);
1267 g_assert (out_buf != NULL);
1268 xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
1269 xmlOutputBufferFlush (out_buf);
1270 #ifdef LIBXML2_NEW_BUFFER
1271 if (xmlOutputBufferGetSize (out_buf) > 0) {
1273 (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
1274 xmlOutputBufferGetSize (out_buf));
1278 if (out_buf->conv && out_buf->conv->use > 0) {
1280 (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
1282 } else if (out_buf->buffer && out_buf->buffer->use > 0) {
1284 (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
1287 #endif // LIBXML2_NEW_BUFFER
1288 (void) xmlOutputBufferClose (out_buf);
1291 GST_LOG (" - %s: %s", a_node->name, *content);
1297 gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
1300 gchar *namespace = NULL;
1302 if (prefix == NULL) {
1303 /* return the default namespace */
1305 namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
1307 GST_LOG (" - default namespace: %s", namespace);
1311 /* look for the specified prefix in the namespace list */
1312 for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
1313 if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
1314 namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
1316 GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
1326 gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
1328 GstBaseURL *new_base_url;
1330 new_base_url = g_slice_new0 (GstBaseURL);
1331 *list = g_list_append (*list, new_base_url);
1333 GST_LOG ("content of BaseURL node:");
1334 gst_mpdparser_get_xml_node_content (a_node, &new_base_url->baseURL);
1336 GST_LOG ("attributes of BaseURL node:");
1337 gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation",
1338 &new_base_url->serviceLocation);
1339 gst_mpdparser_get_xml_prop_string (a_node, "byteRange",
1340 &new_base_url->byteRange);
1344 gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
1346 GstDescriptorType *new_descriptor;
1348 new_descriptor = g_slice_new0 (GstDescriptorType);
1349 *list = g_list_append (*list, new_descriptor);
1351 GST_LOG ("attributes of %s node:", a_node->name);
1352 gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1353 &new_descriptor->schemeIdUri);
1354 if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
1355 &new_descriptor->value)) {
1356 /* if no value attribute, use XML string representation of the node */
1357 gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
1362 gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
1365 GstContentComponentNode *new_content_component;
1367 new_content_component = g_slice_new0 (GstContentComponentNode);
1368 *list = g_list_append (*list, new_content_component);
1370 GST_LOG ("attributes of ContentComponent node:");
1371 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1372 &new_content_component->id);
1373 gst_mpdparser_get_xml_prop_string (a_node, "lang",
1374 &new_content_component->lang);
1375 gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1376 &new_content_component->contentType);
1377 gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_content_component->par);
1379 /* explore children nodes */
1380 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1381 if (cur_node->type == XML_ELEMENT_NODE) {
1382 if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
1383 gst_mpdparser_parse_descriptor_type_node
1384 (&new_content_component->Accessibility, cur_node);
1385 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
1386 gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
1388 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
1389 gst_mpdparser_parse_descriptor_type_node
1390 (&new_content_component->Rating, cur_node);
1391 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
1392 gst_mpdparser_parse_descriptor_type_node
1393 (&new_content_component->Viewpoint, cur_node);
1400 gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
1402 gchar *location = NULL;
1404 GST_LOG ("content of Location node:");
1405 if (gst_mpdparser_get_xml_node_content (a_node, &location))
1406 *list = g_list_append (*list, location);
1410 gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
1412 GstSubRepresentationNode *new_subrep;
1414 new_subrep = g_slice_new0 (GstSubRepresentationNode);
1415 *list = g_list_append (*list, new_subrep);
1417 GST_LOG ("attributes of SubRepresentation node:");
1418 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0,
1419 &new_subrep->level);
1420 gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
1421 &new_subrep->dependencyLevel, &new_subrep->size);
1422 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1423 &new_subrep->bandwidth);
1424 gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1425 "contentComponent", &new_subrep->contentComponent);
1427 /* RepresentationBase extension */
1428 gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
1432 static GstSegmentURLNode *
1433 gst_mpdparser_clone_segment_url (GstSegmentURLNode * seg_url)
1435 GstSegmentURLNode *clone = NULL;
1438 clone = g_slice_new0 (GstSegmentURLNode);
1439 clone->media = xmlMemStrdup (seg_url->media);
1440 clone->mediaRange = gst_mpdparser_clone_range (seg_url->mediaRange);
1441 clone->index = xmlMemStrdup (seg_url->index);
1442 clone->indexRange = gst_mpdparser_clone_range (seg_url->indexRange);
1449 gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
1451 GstSegmentURLNode *new_segment_url;
1453 new_segment_url = g_slice_new0 (GstSegmentURLNode);
1454 *list = g_list_append (*list, new_segment_url);
1456 GST_LOG ("attributes of SegmentURL node:");
1457 gst_mpdparser_get_xml_prop_string (a_node, "media", &new_segment_url->media);
1458 gst_mpdparser_get_xml_prop_range (a_node, "mediaRange",
1459 &new_segment_url->mediaRange);
1460 gst_mpdparser_get_xml_prop_string (a_node, "index", &new_segment_url->index);
1461 gst_mpdparser_get_xml_prop_range (a_node, "indexRange",
1462 &new_segment_url->indexRange);
1466 gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
1468 GstURLType *new_url_type;
1470 gst_mpdparser_free_url_type_node (*pointer);
1471 *pointer = new_url_type = g_slice_new0 (GstURLType);
1473 GST_LOG ("attributes of URLType node:");
1474 gst_mpdparser_get_xml_prop_string (a_node, "sourceURL",
1475 &new_url_type->sourceURL);
1476 gst_mpdparser_get_xml_prop_range (a_node, "range", &new_url_type->range);
1480 gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
1481 xmlNode * a_node, GstSegmentBaseType * parent)
1484 GstSegmentBaseType *seg_base_type;
1490 gst_mpdparser_free_seg_base_type_ext (*pointer);
1491 *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
1493 /* Initialize values that have defaults */
1494 seg_base_type->indexRangeExact = FALSE;
1495 seg_base_type->timescale = 1;
1497 /* Inherit attribute values from parent */
1499 seg_base_type->timescale = parent->timescale;
1500 seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
1501 seg_base_type->indexRange = gst_mpdparser_clone_range (parent->indexRange);
1502 seg_base_type->indexRangeExact = parent->indexRangeExact;
1503 seg_base_type->Initialization =
1504 gst_mpdparser_clone_URL (parent->Initialization);
1505 seg_base_type->RepresentationIndex =
1506 gst_mpdparser_clone_URL (parent->RepresentationIndex);
1509 /* We must retrieve each value first to see if it exists. If it does not
1510 * exist, we do not want to overwrite an inherited value */
1511 GST_LOG ("attributes of SegmentBaseType extension:");
1512 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 1,
1514 seg_base_type->timescale = intval;
1516 if (gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node,
1517 "presentationTimeOffset", 0, &int64val)) {
1518 seg_base_type->presentationTimeOffset = int64val;
1520 if (gst_mpdparser_get_xml_prop_range (a_node, "indexRange", &rangeval)) {
1521 if (seg_base_type->indexRange) {
1522 g_slice_free (GstRange, seg_base_type->indexRange);
1524 seg_base_type->indexRange = rangeval;
1526 if (gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact",
1528 seg_base_type->indexRangeExact = boolval;
1531 /* explore children nodes */
1532 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1533 if (cur_node->type == XML_ELEMENT_NODE) {
1534 if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
1535 xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
1536 /* parse will free the previous pointer to create a new one */
1537 gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
1539 } else if (xmlStrcmp (cur_node->name,
1540 (xmlChar *) "RepresentationIndex") == 0) {
1541 /* parse will free the previous pointer to create a new one */
1542 gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
1550 gst_mpdparser_clone_s_node (GstSNode * pointer)
1552 GstSNode *clone = NULL;
1555 clone = g_slice_new0 (GstSNode);
1556 clone->t = pointer->t;
1557 clone->d = pointer->d;
1558 clone->r = pointer->r;
1565 gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
1567 GstSNode *new_s_node;
1569 new_s_node = g_slice_new0 (GstSNode);
1570 g_queue_push_tail (queue, new_s_node);
1572 GST_LOG ("attributes of S node:");
1573 gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "t", 0,
1575 gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0,
1577 gst_mpdparser_get_xml_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
1580 static GstSegmentTimelineNode *
1581 gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer)
1583 GstSegmentTimelineNode *clone = NULL;
1586 clone = gst_mpdparser_segment_timeline_node_new ();
1589 for (list = g_queue_peek_head_link (&pointer->S); list;
1590 list = g_list_next (list)) {
1592 s_node = (GstSNode *) list->data;
1594 g_queue_push_tail (&clone->S, gst_mpdparser_clone_s_node (s_node));
1598 GST_WARNING ("Allocation of SegmentTimeline node failed!");
1606 gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
1610 GstSegmentTimelineNode *new_seg_timeline;
1612 gst_mpdparser_free_segment_timeline_node (*pointer);
1613 *pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ();
1614 if (new_seg_timeline == NULL) {
1615 GST_WARNING ("Allocation of SegmentTimeline node failed!");
1619 /* explore children nodes */
1620 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1621 if (cur_node->type == XML_ELEMENT_NODE) {
1622 if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
1623 gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
1630 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
1631 xmlNode * a_node, GstMultSegmentBaseType * parent)
1634 GstMultSegmentBaseType *mult_seg_base_type;
1636 gboolean has_timeline = FALSE, has_duration = FALSE;
1638 gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
1639 mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
1641 mult_seg_base_type->duration = 0;
1642 mult_seg_base_type->startNumber = 1;
1644 /* Inherit attribute values from parent */
1646 mult_seg_base_type->duration = parent->duration;
1647 mult_seg_base_type->startNumber = parent->startNumber;
1648 mult_seg_base_type->SegmentTimeline =
1649 gst_mpdparser_clone_segment_timeline (parent->SegmentTimeline);
1650 mult_seg_base_type->BitstreamSwitching =
1651 gst_mpdparser_clone_URL (parent->BitstreamSwitching);
1654 GST_LOG ("attributes of MultipleSegmentBaseType extension:");
1655 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0,
1657 mult_seg_base_type->duration = intval;
1660 /* duration might be specified from parent */
1661 if (mult_seg_base_type->duration)
1662 has_duration = TRUE;
1664 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1,
1666 mult_seg_base_type->startNumber = intval;
1669 GST_LOG ("extension of MultipleSegmentBaseType extension:");
1670 gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
1671 a_node, (parent ? parent->SegBaseType : NULL));
1673 /* explore children nodes */
1674 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1675 if (cur_node->type == XML_ELEMENT_NODE) {
1676 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
1677 /* parse frees the segmenttimeline if any */
1678 gst_mpdparser_parse_segment_timeline_node
1679 (&mult_seg_base_type->SegmentTimeline, cur_node);
1680 } else if (xmlStrcmp (cur_node->name,
1681 (xmlChar *) "BitstreamSwitching") == 0) {
1682 /* parse frees the old url before setting the new one */
1683 gst_mpdparser_parse_url_type_node
1684 (&mult_seg_base_type->BitstreamSwitching, cur_node);
1689 has_timeline = mult_seg_base_type->SegmentTimeline != NULL;
1691 /* Checking duration and timeline only at Representation's child level */
1692 if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
1693 && !has_duration && !has_timeline) {
1694 GST_ERROR ("segment has neither duration nor timeline");
1698 *pointer = mult_seg_base_type;
1702 gst_mpdparser_free_mult_seg_base_type_ext (mult_seg_base_type);
1707 gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
1708 xmlNode * a_node, GstSegmentListNode * parent)
1711 GstSegmentListNode *new_segment_list;
1713 gboolean segment_urls_inherited_from_parent = FALSE;
1715 gst_mpdparser_free_segment_list_node (*pointer);
1716 new_segment_list = g_slice_new0 (GstSegmentListNode);
1718 /* Inherit attribute values from parent */
1721 GstSegmentURLNode *seg_url;
1722 for (list = g_list_first (parent->SegmentURL); list;
1723 list = g_list_next (list)) {
1724 seg_url = (GstSegmentURLNode *) list->data;
1725 new_segment_list->SegmentURL =
1726 g_list_append (new_segment_list->SegmentURL,
1727 gst_mpdparser_clone_segment_url (seg_url));
1728 segment_urls_inherited_from_parent = TRUE;
1732 new_segment_list->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1733 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1734 "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
1735 && gst_mpdparser_get_xml_ns_prop_string (a_node,
1736 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1737 if (strcmp (actuate, "onLoad") == 0)
1738 new_segment_list->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1742 GST_LOG ("extension of SegmentList node:");
1743 if (!gst_mpdparser_parse_mult_seg_base_type_ext
1744 (&new_segment_list->MultSegBaseType, a_node,
1745 (parent ? parent->MultSegBaseType : NULL)))
1748 /* explore children nodes */
1749 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1750 if (cur_node->type == XML_ELEMENT_NODE) {
1751 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
1752 if (segment_urls_inherited_from_parent) {
1754 * SegmentBase, SegmentTemplate and SegmentList shall inherit
1755 * attributes and elements from the same element on a higher level.
1756 * If the same attribute or element is present on both levels,
1757 * the one on the lower level shall take precedence over the one
1758 * on the higher level.
1761 /* Clear the list of inherited segment URLs */
1762 g_list_free_full (new_segment_list->SegmentURL,
1763 (GDestroyNotify) gst_mpdparser_free_segment_url_node);
1764 new_segment_list->SegmentURL = NULL;
1766 /* mark the fact that we cleared the list, so that it is not tried again */
1767 segment_urls_inherited_from_parent = FALSE;
1769 gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
1775 *pointer = new_segment_list;
1779 gst_mpdparser_free_segment_list_node (new_segment_list);
1784 gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
1786 gchar *value = NULL;
1787 if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
1788 if (!g_strcmp0 (value, "MSPR 2.0")) {
1790 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1791 if (cur_node->type == XML_ELEMENT_NODE) {
1792 if (xmlStrcmp (cur_node->name, (xmlChar *) "pro") == 0) {
1793 GstDescriptorType *new_descriptor;
1794 new_descriptor = g_slice_new0 (GstDescriptorType);
1795 *list = g_list_append (*list, new_descriptor);
1797 gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1798 &new_descriptor->schemeIdUri);
1800 gst_mpdparser_get_xml_node_content (cur_node,
1801 &new_descriptor->value);
1807 gst_mpdparser_parse_descriptor_type_node (list, a_node);
1810 gst_mpdparser_parse_descriptor_type_node (list, a_node);
1818 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
1819 pointer, xmlNode * a_node)
1822 GstRepresentationBaseType *representation_base;
1824 gst_mpdparser_free_representation_base_type (*pointer);
1825 *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
1827 GST_LOG ("attributes of RepresentationBaseType extension:");
1828 gst_mpdparser_get_xml_prop_string (a_node, "profiles",
1829 &representation_base->profiles);
1830 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0,
1831 &representation_base->width);
1832 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0,
1833 &representation_base->height);
1834 gst_mpdparser_get_xml_prop_ratio (a_node, "sar", &representation_base->sar);
1835 gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate",
1836 &representation_base->frameRate);
1837 gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate",
1838 &representation_base->minFrameRate);
1839 gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate",
1840 &representation_base->maxFrameRate);
1841 gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate",
1842 &representation_base->audioSamplingRate);
1843 gst_mpdparser_get_xml_prop_string (a_node, "mimeType",
1844 &representation_base->mimeType);
1845 gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles",
1846 &representation_base->segmentProfiles);
1847 gst_mpdparser_get_xml_prop_string (a_node, "codecs",
1848 &representation_base->codecs);
1849 gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod",
1850 &representation_base->maximumSAPPeriod);
1851 gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP",
1852 &representation_base->startWithSAP);
1853 gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate",
1854 &representation_base->maxPlayoutRate);
1855 gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency",
1856 FALSE, &representation_base->codingDependency);
1857 gst_mpdparser_get_xml_prop_string (a_node, "scanType",
1858 &representation_base->scanType);
1860 /* explore children nodes */
1861 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1862 if (cur_node->type == XML_ELEMENT_NODE) {
1863 if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
1864 gst_mpdparser_parse_descriptor_type_node
1865 (&representation_base->FramePacking, cur_node);
1866 } else if (xmlStrcmp (cur_node->name,
1867 (xmlChar *) "AudioChannelConfiguration") == 0) {
1868 gst_mpdparser_parse_descriptor_type_node
1869 (&representation_base->AudioChannelConfiguration, cur_node);
1870 } else if (xmlStrcmp (cur_node->name,
1871 (xmlChar *) "ContentProtection") == 0) {
1872 gst_mpdparser_parse_content_protection_node
1873 (&representation_base->ContentProtection, cur_node);
1880 gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
1881 GstAdaptationSetNode * parent, GstPeriodNode * period_node)
1884 GstRepresentationNode *new_representation;
1886 new_representation = g_slice_new0 (GstRepresentationNode);
1888 GST_LOG ("attributes of Representation node:");
1889 if (!gst_mpdparser_get_xml_prop_string_no_whitespace (a_node, "id",
1890 &new_representation->id)) {
1891 GST_ERROR ("Cannot parse Representation id, invalid manifest");
1894 if (!gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1895 &new_representation->bandwidth)) {
1896 GST_ERROR ("Cannot parse Representation bandwidth, invalid manifest");
1899 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0,
1900 &new_representation->qualityRanking);
1901 gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId",
1902 &new_representation->dependencyId);
1903 gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1904 "mediaStreamStructureId", &new_representation->mediaStreamStructureId);
1906 /* RepresentationBase extension */
1907 gst_mpdparser_parse_representation_base_type
1908 (&new_representation->RepresentationBase, a_node);
1910 /* explore children nodes */
1911 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1912 if (cur_node->type == XML_ELEMENT_NODE) {
1913 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
1914 gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
1915 cur_node, parent->SegmentBase ?
1916 parent->SegmentBase : period_node->SegmentBase);
1917 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
1918 if (!gst_mpdparser_parse_segment_template_node
1919 (&new_representation->SegmentTemplate, cur_node,
1920 parent->SegmentTemplate ?
1921 parent->SegmentTemplate : period_node->SegmentTemplate))
1923 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
1924 if (!gst_mpdparser_parse_segment_list_node
1925 (&new_representation->SegmentList, cur_node,
1926 parent->SegmentList ? parent->
1927 SegmentList : period_node->SegmentList))
1929 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
1930 gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
1932 } else if (xmlStrcmp (cur_node->name,
1933 (xmlChar *) "SubRepresentation") == 0) {
1934 gst_mpdparser_parse_subrepresentation_node
1935 (&new_representation->SubRepresentations, cur_node);
1940 /* some sanity checking */
1942 *list = g_list_append (*list, new_representation);
1946 gst_mpdparser_free_representation_node (new_representation);
1951 gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
1952 GstPeriodNode * parent)
1955 GstAdaptationSetNode *new_adap_set;
1958 new_adap_set = g_slice_new0 (GstAdaptationSetNode);
1960 GST_LOG ("attributes of AdaptationSet node:");
1962 new_adap_set->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1963 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1964 "http://www.w3.org/1999/xlink", "href", &new_adap_set->xlink_href)
1965 && gst_mpdparser_get_xml_ns_prop_string (a_node,
1966 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1967 if (strcmp (actuate, "onLoad") == 0)
1968 new_adap_set->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1972 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1974 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0,
1975 &new_adap_set->group);
1976 gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_adap_set->lang);
1977 gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1978 &new_adap_set->contentType);
1979 gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_adap_set->par);
1980 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0,
1981 &new_adap_set->minBandwidth);
1982 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0,
1983 &new_adap_set->maxBandwidth);
1984 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0,
1985 &new_adap_set->minWidth);
1986 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0,
1987 &new_adap_set->maxWidth);
1988 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0,
1989 &new_adap_set->minHeight);
1990 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0,
1991 &new_adap_set->maxHeight);
1992 gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment",
1993 &new_adap_set->segmentAlignment);
1994 gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching",
1995 parent->bitstreamSwitching, &new_adap_set->bitstreamSwitching);
1996 if (parent->bitstreamSwitching && !new_adap_set->bitstreamSwitching) {
1997 /* according to the standard, if the Period's bitstreamSwitching attribute
1998 * is true, the AdaptationSet should not have the bitstreamSwitching
1999 * attribute set to false.
2000 * We should return a parsing error, but we are generous and ignore the
2001 * standard violation.
2003 new_adap_set->bitstreamSwitching = parent->bitstreamSwitching;
2005 gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment",
2006 &new_adap_set->subsegmentAlignment);
2007 gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP",
2008 &new_adap_set->subsegmentStartsWithSAP);
2010 /* RepresentationBase extension */
2011 gst_mpdparser_parse_representation_base_type
2012 (&new_adap_set->RepresentationBase, a_node);
2014 /* explore children nodes */
2015 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2016 if (cur_node->type == XML_ELEMENT_NODE) {
2017 if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
2018 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Accessibility,
2020 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
2021 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Role,
2023 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
2024 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Rating,
2026 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
2027 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Viewpoint,
2029 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2030 gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
2031 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2032 gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
2033 cur_node, parent->SegmentBase);
2034 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2035 if (!gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
2036 cur_node, parent->SegmentList))
2038 } else if (xmlStrcmp (cur_node->name,
2039 (xmlChar *) "ContentComponent") == 0) {
2040 gst_mpdparser_parse_content_component_node
2041 (&new_adap_set->ContentComponents, cur_node);
2042 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2043 if (!gst_mpdparser_parse_segment_template_node
2044 (&new_adap_set->SegmentTemplate, cur_node, parent->SegmentTemplate))
2050 /* We must parse Representation after everything else in the AdaptationSet
2051 * has been parsed because certain Representation child elements can inherit
2052 * attributes specified by the same element in the AdaptationSet
2054 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2055 if (cur_node->type == XML_ELEMENT_NODE) {
2056 if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
2057 if (!gst_mpdparser_parse_representation_node
2058 (&new_adap_set->Representations, cur_node, new_adap_set, parent))
2064 *list = g_list_append (*list, new_adap_set);
2068 gst_mpdparser_free_adaptation_set_node (new_adap_set);
2073 gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
2075 GstSubsetNode *new_subset;
2077 new_subset = g_slice_new0 (GstSubsetNode);
2078 *list = g_list_append (*list, new_subset);
2080 GST_LOG ("attributes of Subset node:");
2081 gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
2082 &new_subset->contains, &new_subset->size);
2086 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
2087 xmlNode * a_node, GstSegmentTemplateNode * parent)
2089 GstSegmentTemplateNode *new_segment_template;
2092 gst_mpdparser_free_segment_template_node (*pointer);
2093 new_segment_template = g_slice_new0 (GstSegmentTemplateNode);
2095 GST_LOG ("extension of SegmentTemplate node:");
2096 if (!gst_mpdparser_parse_mult_seg_base_type_ext
2097 (&new_segment_template->MultSegBaseType, a_node,
2098 (parent ? parent->MultSegBaseType : NULL)))
2101 /* Inherit attribute values from parent when the value isn't found */
2102 GST_LOG ("attributes of SegmentTemplate node:");
2103 if (gst_mpdparser_get_xml_prop_string (a_node, "media", &strval)) {
2104 new_segment_template->media = strval;
2105 } else if (parent) {
2106 new_segment_template->media = xmlMemStrdup (parent->media);
2109 if (gst_mpdparser_get_xml_prop_string (a_node, "index", &strval)) {
2110 new_segment_template->index = strval;
2111 } else if (parent) {
2112 new_segment_template->index = xmlMemStrdup (parent->index);
2115 if (gst_mpdparser_get_xml_prop_string (a_node, "initialization", &strval)) {
2116 new_segment_template->initialization = strval;
2117 } else if (parent) {
2118 new_segment_template->initialization =
2119 xmlMemStrdup (parent->initialization);
2122 if (gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching", &strval)) {
2123 new_segment_template->bitstreamSwitching = strval;
2124 } else if (parent) {
2125 new_segment_template->bitstreamSwitching =
2126 xmlMemStrdup (parent->bitstreamSwitching);
2129 *pointer = new_segment_template;
2133 gst_mpdparser_free_segment_template_node (new_segment_template);
2138 gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
2141 GstPeriodNode *new_period;
2144 new_period = g_slice_new0 (GstPeriodNode);
2146 GST_LOG ("attributes of Period node:");
2148 new_period->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
2149 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
2150 "http://www.w3.org/1999/xlink", "href", &new_period->xlink_href)
2151 && gst_mpdparser_get_xml_ns_prop_string (a_node,
2152 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
2153 if (strcmp (actuate, "onLoad") == 0)
2154 new_period->actuate = GST_XLINK_ACTUATE_ON_LOAD;
2158 gst_mpdparser_get_xml_prop_string (a_node, "id", &new_period->id);
2159 gst_mpdparser_get_xml_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
2160 &new_period->start);
2161 gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2162 GST_MPD_DURATION_NONE, &new_period->duration);
2163 gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching", FALSE,
2164 &new_period->bitstreamSwitching);
2166 /* explore children nodes */
2167 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2168 if (cur_node->type == XML_ELEMENT_NODE) {
2169 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2170 gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
2172 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2173 if (!gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
2176 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2177 if (!gst_mpdparser_parse_segment_template_node
2178 (&new_period->SegmentTemplate, cur_node, NULL))
2180 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
2181 gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
2182 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2183 gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
2188 /* We must parse AdaptationSet after everything else in the Period has been
2189 * parsed because certain AdaptationSet child elements can inherit attributes
2190 * specified by the same element in the Period
2192 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2193 if (cur_node->type == XML_ELEMENT_NODE) {
2194 if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
2195 if (!gst_mpdparser_parse_adaptation_set_node
2196 (&new_period->AdaptationSets, cur_node, new_period))
2202 *list = g_list_append (*list, new_period);
2206 gst_mpdparser_free_period_node (new_period);
2211 gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
2214 GstProgramInformationNode *new_prog_info;
2216 new_prog_info = g_slice_new0 (GstProgramInformationNode);
2217 *list = g_list_append (*list, new_prog_info);
2219 GST_LOG ("attributes of ProgramInformation node:");
2220 gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_prog_info->lang);
2221 gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL",
2222 &new_prog_info->moreInformationURL);
2224 /* explore children nodes */
2225 GST_LOG ("children of ProgramInformation node:");
2226 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2227 if (cur_node->type == XML_ELEMENT_NODE) {
2228 if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
2229 gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Title);
2230 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
2231 gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Source);
2232 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
2233 gst_mpdparser_get_xml_node_content (cur_node,
2234 &new_prog_info->Copyright);
2241 gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
2243 GstMetricsRangeNode *new_metrics_range;
2245 new_metrics_range = g_slice_new0 (GstMetricsRangeNode);
2246 *list = g_list_append (*list, new_metrics_range);
2248 GST_LOG ("attributes of Metrics Range node:");
2249 gst_mpdparser_get_xml_prop_duration (a_node, "starttime",
2250 GST_MPD_DURATION_NONE, &new_metrics_range->starttime);
2251 gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2252 GST_MPD_DURATION_NONE, &new_metrics_range->duration);
2256 gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
2259 GstMetricsNode *new_metrics;
2261 new_metrics = g_slice_new0 (GstMetricsNode);
2262 *list = g_list_append (*list, new_metrics);
2264 GST_LOG ("attributes of Metrics node:");
2265 gst_mpdparser_get_xml_prop_string (a_node, "metrics", &new_metrics->metrics);
2267 /* explore children nodes */
2268 GST_LOG ("children of Metrics node:");
2269 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2270 if (cur_node->type == XML_ELEMENT_NODE) {
2271 if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
2272 gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
2274 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
2275 /* No reporting scheme is specified in this part of ISO/IEC 23009.
2276 * It is expected that external specifications may define formats
2277 * and delivery for the reporting data. */
2278 GST_LOG (" - Reporting node found (unknown structure)");
2284 /* The UTCTiming element is defined in
2285 * ISO/IEC 23009-1:2014/PDAM 1 "Information technology — Dynamic adaptive streaming over HTTP (DASH) — Part 1: Media presentation description and segment formats / Amendment 1: High Profile and Availability Time Synchronization"
2288 gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
2290 GstUTCTimingNode *new_timing;
2291 gchar *method = NULL;
2292 gchar *value = NULL;
2294 new_timing = g_slice_new0 (GstUTCTimingNode);
2296 GST_LOG ("attributes of UTCTiming node:");
2297 if (gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri", &method)) {
2300 for (i = 0; gst_mpdparser_utc_timing_methods[i].name; ++i) {
2301 if (g_ascii_strncasecmp (gst_mpdparser_utc_timing_methods[i].name,
2302 method, strlen (gst_mpdparser_utc_timing_methods[i].name)) == 0) {
2303 new_timing->method = gst_mpdparser_utc_timing_methods[i].method;
2310 if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
2312 if (GST_MPD_UTCTIMING_TYPE_DIRECT == new_timing->method) {
2313 /* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
2314 * that is not a space separated list.
2318 new_timing->urls = g_strsplit (value, " ", max_tokens);
2322 /* append to list only if both method and urls were set */
2323 if (new_timing->method != 0 && new_timing->urls != NULL &&
2324 g_strv_length (new_timing->urls) != 0) {
2325 *list = g_list_append (*list, new_timing);
2327 gst_mpdparser_free_utctiming_node (new_timing);
2332 gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
2335 GstMPDNode *new_mpd;
2337 gst_mpdparser_free_mpd_node (*pointer);
2339 new_mpd = g_slice_new0 (GstMPDNode);
2341 GST_LOG ("namespaces of root MPD node:");
2342 new_mpd->default_namespace =
2343 gst_mpdparser_get_xml_node_namespace (a_node, NULL);
2344 new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
2345 new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
2347 GST_LOG ("attributes of root MPD node:");
2348 gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation",
2349 &new_mpd->schemaLocation);
2350 gst_mpdparser_get_xml_prop_string (a_node, "id", &new_mpd->id);
2351 gst_mpdparser_get_xml_prop_string (a_node, "profiles", &new_mpd->profiles);
2352 gst_mpdparser_get_xml_prop_type (a_node, "type", &new_mpd->type);
2353 gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime",
2354 &new_mpd->availabilityStartTime);
2355 gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime",
2356 &new_mpd->availabilityEndTime);
2357 gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration",
2358 GST_MPD_DURATION_NONE, &new_mpd->mediaPresentationDuration);
2359 gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod",
2360 GST_MPD_DURATION_NONE, &new_mpd->minimumUpdatePeriod);
2361 gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime",
2362 GST_MPD_DURATION_NONE, &new_mpd->minBufferTime);
2363 gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth",
2364 GST_MPD_DURATION_NONE, &new_mpd->timeShiftBufferDepth);
2365 gst_mpdparser_get_xml_prop_duration (a_node, "suggestedPresentationDelay",
2366 GST_MPD_DURATION_NONE, &new_mpd->suggestedPresentationDelay);
2367 gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration",
2368 GST_MPD_DURATION_NONE, &new_mpd->maxSegmentDuration);
2369 gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration",
2370 GST_MPD_DURATION_NONE, &new_mpd->maxSubsegmentDuration);
2372 /* explore children Period nodes */
2373 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2374 if (cur_node->type == XML_ELEMENT_NODE) {
2375 if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
2376 if (!gst_mpdparser_parse_period_node (&new_mpd->Periods, cur_node))
2378 } else if (xmlStrcmp (cur_node->name,
2379 (xmlChar *) "ProgramInformation") == 0) {
2380 gst_mpdparser_parse_program_info_node (&new_mpd->ProgramInfo, cur_node);
2381 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2382 gst_mpdparser_parse_baseURL_node (&new_mpd->BaseURLs, cur_node);
2383 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
2384 gst_mpdparser_parse_location_node (&new_mpd->Locations, cur_node);
2385 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
2386 gst_mpdparser_parse_metrics_node (&new_mpd->Metrics, cur_node);
2387 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "UTCTiming") == 0) {
2388 gst_mpdparser_parse_utctiming_node (&new_mpd->UTCTiming, cur_node);
2397 gst_mpdparser_free_mpd_node (new_mpd);
2401 /* comparison functions */
2403 strncmp_ext (const char *s1, const char *s2)
2405 if (s1 == NULL && s2 == NULL)
2407 if (s1 == NULL && s2 != NULL)
2409 if (s2 == NULL && s1 != NULL)
2411 return strncmp (s1, s2, strlen (s2));
2414 /* navigation functions */
2415 static GstStreamMimeType
2416 gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set,
2417 GstRepresentationNode * rep)
2420 if (rep->RepresentationBase)
2421 mime = rep->RepresentationBase->mimeType;
2422 if (mime == NULL && adapt_set->RepresentationBase) {
2423 mime = adapt_set->RepresentationBase->mimeType;
2426 if (strncmp_ext (mime, "audio") == 0)
2427 return GST_STREAM_AUDIO;
2428 if (strncmp_ext (mime, "video") == 0)
2429 return GST_STREAM_VIDEO;
2430 if (strncmp_ext (mime, "application") == 0 || strncmp_ext (mime, "text") == 0)
2431 return GST_STREAM_APPLICATION;
2433 return GST_STREAM_UNKNOWN;
2436 static GstRepresentationNode *
2437 gst_mpdparser_get_lowest_representation (GList * Representations)
2440 GstRepresentationNode *rep = NULL;
2441 GstRepresentationNode *lowest = NULL;
2443 if (Representations == NULL)
2446 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2447 rep = (GstRepresentationNode *) list->data;
2448 if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
2457 static GstRepresentationNode *
2458 gst_mpdparser_get_highest_representation (GList * Representations)
2462 if (Representations == NULL)
2465 list = g_list_last (Representations);
2467 return list ? (GstRepresentationNode *) list->data : NULL;
2470 static GstRepresentationNode *
2471 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
2475 GstRepresentationNode *representation, *best_rep = NULL;
2477 if (Representations == NULL)
2480 if (max_bandwidth <= 0) /* 0 => get highest representation available */
2481 return gst_mpdparser_get_highest_representation (Representations);
2483 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2484 representation = (GstRepresentationNode *) list->data;
2485 if (representation && representation->bandwidth <= max_bandwidth) {
2486 best_rep = representation;
2494 static GstSegmentBaseType *
2495 gst_mpdparser_get_segment_base (GstPeriodNode * Period,
2496 GstAdaptationSetNode * AdaptationSet,
2497 GstRepresentationNode * Representation)
2499 GstSegmentBaseType *SegmentBase = NULL;
2501 if (Representation && Representation->SegmentBase) {
2502 SegmentBase = Representation->SegmentBase;
2503 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
2504 SegmentBase = AdaptationSet->SegmentBase;
2505 } else if (Period && Period->SegmentBase) {
2506 SegmentBase = Period->SegmentBase;
2508 /* the SegmentBase element could be encoded also inside a SegmentList element */
2509 if (SegmentBase == NULL) {
2510 if (Representation && Representation->SegmentList
2511 && Representation->SegmentList->MultSegBaseType
2512 && Representation->SegmentList->MultSegBaseType->SegBaseType) {
2513 SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
2514 } else if (AdaptationSet && AdaptationSet->SegmentList
2515 && AdaptationSet->SegmentList->MultSegBaseType
2516 && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType) {
2517 SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
2518 } else if (Period && Period->SegmentList
2519 && Period->SegmentList->MultSegBaseType
2520 && Period->SegmentList->MultSegBaseType->SegBaseType) {
2521 SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
2529 gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations)
2531 GList *list = NULL, *lowest = NULL;
2532 GstRepresentationNode *rep = NULL;
2533 gint lowest_bandwidth = -1;
2535 if (Representations == NULL)
2538 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2539 rep = (GstRepresentationNode *) list->data;
2540 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2542 lowest_bandwidth = rep->bandwidth;
2546 return lowest ? g_list_position (Representations, lowest) : -1;
2550 gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
2551 gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
2552 max_video_framerate_n, gint max_video_framerate_d)
2554 GList *list = NULL, *best = NULL;
2555 GstRepresentationNode *representation;
2556 gint best_bandwidth = 0;
2558 GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
2560 if (Representations == NULL)
2563 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2564 return gst_mpdparser_get_rep_idx_with_min_bandwidth (Representations);
2566 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2567 GstFrameRate *framerate = NULL;
2569 representation = (GstRepresentationNode *) list->data;
2571 /* FIXME: Really? */
2572 if (!representation)
2575 framerate = representation->RepresentationBase->frameRate;
2577 framerate = representation->RepresentationBase->maxFrameRate;
2579 if (framerate && max_video_framerate_n > 0) {
2580 if (gst_util_fraction_compare (framerate->num, framerate->den,
2581 max_video_framerate_n, max_video_framerate_d) > 0)
2585 if (max_video_width > 0
2586 && representation->RepresentationBase->width > max_video_width)
2588 if (max_video_height > 0
2589 && representation->RepresentationBase->height > max_video_height)
2592 if (representation->bandwidth <= max_bandwidth &&
2593 representation->bandwidth > best_bandwidth) {
2595 best_bandwidth = representation->bandwidth;
2599 return best ? g_list_position (Representations, best) : -1;
2602 static GstSegmentListNode *
2603 gst_mpd_client_fetch_external_segment_list (GstMpdClient * client,
2604 GstPeriodNode * Period,
2605 GstAdaptationSetNode * AdaptationSet,
2606 GstRepresentationNode * Representation,
2607 GstSegmentListNode * parent, GstSegmentListNode * segment_list)
2609 GstFragment *download;
2610 GstBuffer *segment_list_buffer;
2613 xmlDocPtr doc = NULL;
2614 GstUri *base_uri, *uri;
2615 gchar *query = NULL;
2617 GstSegmentListNode *new_segment_list = NULL;
2619 /* ISO/IEC 23009-1:2014 5.5.3 4)
2620 * Remove nodes that resolve to nothing when resolving
2622 if (strcmp (segment_list->xlink_href,
2623 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
2627 if (!client->downloader) {
2631 /* Build absolute URI */
2633 /* Get base URI at the MPD level */
2635 gst_uri_from_string (client->
2636 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
2638 /* combine a BaseURL at the MPD level with the current base url */
2639 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
2641 /* combine a BaseURL at the Period level with the current base url */
2642 base_uri = combine_urls (base_uri, Period->BaseURLs, &query, 0);
2644 if (AdaptationSet) {
2645 /* combine a BaseURL at the AdaptationSet level with the current base url */
2646 base_uri = combine_urls (base_uri, AdaptationSet->BaseURLs, &query, 0);
2648 if (Representation) {
2649 /* combine a BaseURL at the Representation level with the current base url */
2650 base_uri = combine_urls (base_uri, Representation->BaseURLs, &query, 0);
2654 uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
2656 gst_uri_set_query_string (uri, query);
2658 uri_string = gst_uri_to_string (uri);
2659 gst_uri_unref (base_uri);
2660 gst_uri_unref (uri);
2663 gst_uri_downloader_fetch_uri (client->downloader,
2664 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
2665 g_free (uri_string);
2668 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
2669 segment_list->xlink_href, err->message);
2670 g_clear_error (&err);
2674 segment_list_buffer = gst_fragment_get_buffer (download);
2675 g_object_unref (download);
2677 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
2680 xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
2683 gst_buffer_unmap (segment_list_buffer, &map);
2684 gst_buffer_unref (segment_list_buffer);
2686 /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
2687 * in external xml is allowed, however, multiple SegmentList does not make sense
2688 * because Period/AdaptationSet/Representation allow only one SegmentList */
2690 xmlNode *root_element = xmlDocGetRootElement (doc);
2692 if (root_element->type != XML_ELEMENT_NODE ||
2693 xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") != 0) {
2697 gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
2707 return new_segment_list;
2710 GST_ERROR ("Failed to parse segment list node XML");
2714 static GstSegmentListNode *
2715 gst_mpdparser_get_segment_list (GstMpdClient * client, GstPeriodNode * Period,
2716 GstAdaptationSetNode * AdaptationSet,
2717 GstRepresentationNode * Representation)
2719 GstSegmentListNode **SegmentList;
2720 GstSegmentListNode *ParentSegmentList = NULL;
2722 if (Representation && Representation->SegmentList) {
2723 SegmentList = &Representation->SegmentList;
2724 ParentSegmentList = AdaptationSet->SegmentList;
2725 } else if (AdaptationSet && AdaptationSet->SegmentList) {
2726 SegmentList = &AdaptationSet->SegmentList;
2727 ParentSegmentList = Period->SegmentList;
2728 Representation = NULL;
2730 Representation = NULL;
2731 AdaptationSet = NULL;
2732 SegmentList = &Period->SegmentList;
2735 /* Resolve external segment list here. */
2736 if (*SegmentList && (*SegmentList)->xlink_href) {
2737 GstSegmentListNode *new_segment_list;
2739 /* TODO: Use SegmentList of parent if
2740 * - Parent has its own SegmentList
2741 * - Fail to get SegmentList from external xml
2744 gst_mpd_client_fetch_external_segment_list (client, Period,
2745 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
2747 gst_mpdparser_free_segment_list_node (*SegmentList);
2748 *SegmentList = new_segment_list;
2751 return *SegmentList;
2754 /* memory management functions */
2756 gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
2759 if (mpd_node->default_namespace)
2760 xmlFree (mpd_node->default_namespace);
2761 if (mpd_node->namespace_xsi)
2762 xmlFree (mpd_node->namespace_xsi);
2763 if (mpd_node->namespace_ext)
2764 xmlFree (mpd_node->namespace_ext);
2765 if (mpd_node->schemaLocation)
2766 xmlFree (mpd_node->schemaLocation);
2768 xmlFree (mpd_node->id);
2769 if (mpd_node->profiles)
2770 xmlFree (mpd_node->profiles);
2771 if (mpd_node->availabilityStartTime)
2772 gst_date_time_unref (mpd_node->availabilityStartTime);
2773 if (mpd_node->availabilityEndTime)
2774 gst_date_time_unref (mpd_node->availabilityEndTime);
2775 g_list_free_full (mpd_node->ProgramInfo,
2776 (GDestroyNotify) gst_mpdparser_free_prog_info_node);
2777 g_list_free_full (mpd_node->BaseURLs,
2778 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2779 g_list_free_full (mpd_node->Locations, (GDestroyNotify) xmlFree);
2780 g_list_free_full (mpd_node->Periods,
2781 (GDestroyNotify) gst_mpdparser_free_period_node);
2782 g_list_free_full (mpd_node->Metrics,
2783 (GDestroyNotify) gst_mpdparser_free_metrics_node);
2784 g_list_free_full (mpd_node->UTCTiming,
2785 (GDestroyNotify) gst_mpdparser_free_utctiming_node);
2786 g_slice_free (GstMPDNode, mpd_node);
2791 gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
2793 if (prog_info_node) {
2794 if (prog_info_node->lang)
2795 xmlFree (prog_info_node->lang);
2796 if (prog_info_node->moreInformationURL)
2797 xmlFree (prog_info_node->moreInformationURL);
2798 if (prog_info_node->Title)
2799 xmlFree (prog_info_node->Title);
2800 if (prog_info_node->Source)
2801 xmlFree (prog_info_node->Source);
2802 if (prog_info_node->Copyright)
2803 xmlFree (prog_info_node->Copyright);
2804 g_slice_free (GstProgramInformationNode, prog_info_node);
2809 gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
2812 if (metrics_node->metrics)
2813 xmlFree (metrics_node->metrics);
2814 g_list_free_full (metrics_node->MetricsRanges,
2815 (GDestroyNotify) gst_mpdparser_free_metrics_range_node);
2816 g_slice_free (GstMetricsNode, metrics_node);
2821 gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
2823 if (metrics_range_node) {
2824 g_slice_free (GstMetricsRangeNode, metrics_range_node);
2829 gst_mpdparser_free_period_node (GstPeriodNode * period_node)
2832 if (period_node->id)
2833 xmlFree (period_node->id);
2834 gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
2835 gst_mpdparser_free_segment_list_node (period_node->SegmentList);
2836 gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
2837 g_list_free_full (period_node->AdaptationSets,
2838 (GDestroyNotify) gst_mpdparser_free_adaptation_set_node);
2839 g_list_free_full (period_node->Subsets,
2840 (GDestroyNotify) gst_mpdparser_free_subset_node);
2841 g_list_free_full (period_node->BaseURLs,
2842 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2843 if (period_node->xlink_href)
2844 xmlFree (period_node->xlink_href);
2845 g_slice_free (GstPeriodNode, period_node);
2850 gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
2853 if (subset_node->contains)
2854 xmlFree (subset_node->contains);
2855 g_slice_free (GstSubsetNode, subset_node);
2860 gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
2861 segment_template_node)
2863 if (segment_template_node) {
2864 if (segment_template_node->media)
2865 xmlFree (segment_template_node->media);
2866 if (segment_template_node->index)
2867 xmlFree (segment_template_node->index);
2868 if (segment_template_node->initialization)
2869 xmlFree (segment_template_node->initialization);
2870 if (segment_template_node->bitstreamSwitching)
2871 xmlFree (segment_template_node->bitstreamSwitching);
2872 /* MultipleSegmentBaseType extension */
2873 gst_mpdparser_free_mult_seg_base_type_ext
2874 (segment_template_node->MultSegBaseType);
2875 g_slice_free (GstSegmentTemplateNode, segment_template_node);
2880 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
2881 representation_base)
2883 if (representation_base) {
2884 if (representation_base->profiles)
2885 xmlFree (representation_base->profiles);
2886 g_slice_free (GstRatio, representation_base->sar);
2887 g_slice_free (GstFrameRate, representation_base->frameRate);
2888 g_slice_free (GstFrameRate, representation_base->minFrameRate);
2889 g_slice_free (GstFrameRate, representation_base->maxFrameRate);
2890 if (representation_base->audioSamplingRate)
2891 xmlFree (representation_base->audioSamplingRate);
2892 if (representation_base->mimeType)
2893 xmlFree (representation_base->mimeType);
2894 if (representation_base->segmentProfiles)
2895 xmlFree (representation_base->segmentProfiles);
2896 if (representation_base->codecs)
2897 xmlFree (representation_base->codecs);
2898 if (representation_base->scanType)
2899 xmlFree (representation_base->scanType);
2900 g_list_free_full (representation_base->FramePacking,
2901 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2902 g_list_free_full (representation_base->AudioChannelConfiguration,
2903 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2904 g_list_free_full (representation_base->ContentProtection,
2905 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2906 g_slice_free (GstRepresentationBaseType, representation_base);
2911 gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
2912 adaptation_set_node)
2914 if (adaptation_set_node) {
2915 if (adaptation_set_node->lang)
2916 xmlFree (adaptation_set_node->lang);
2917 if (adaptation_set_node->contentType)
2918 xmlFree (adaptation_set_node->contentType);
2919 g_slice_free (GstRatio, adaptation_set_node->par);
2920 g_slice_free (GstConditionalUintType,
2921 adaptation_set_node->segmentAlignment);
2922 g_slice_free (GstConditionalUintType,
2923 adaptation_set_node->subsegmentAlignment);
2924 g_list_free_full (adaptation_set_node->Accessibility,
2925 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2926 g_list_free_full (adaptation_set_node->Role,
2927 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2928 g_list_free_full (adaptation_set_node->Rating,
2929 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2930 g_list_free_full (adaptation_set_node->Viewpoint,
2931 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2932 gst_mpdparser_free_representation_base_type
2933 (adaptation_set_node->RepresentationBase);
2934 gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
2935 gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
2936 gst_mpdparser_free_segment_template_node
2937 (adaptation_set_node->SegmentTemplate);
2938 g_list_free_full (adaptation_set_node->BaseURLs,
2939 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2940 g_list_free_full (adaptation_set_node->Representations,
2941 (GDestroyNotify) gst_mpdparser_free_representation_node);
2942 g_list_free_full (adaptation_set_node->ContentComponents,
2943 (GDestroyNotify) gst_mpdparser_free_content_component_node);
2944 if (adaptation_set_node->xlink_href)
2945 xmlFree (adaptation_set_node->xlink_href);
2946 g_slice_free (GstAdaptationSetNode, adaptation_set_node);
2951 gst_mpdparser_free_representation_node (GstRepresentationNode *
2952 representation_node)
2954 if (representation_node) {
2955 if (representation_node->id)
2956 xmlFree (representation_node->id);
2957 g_strfreev (representation_node->dependencyId);
2958 g_strfreev (representation_node->mediaStreamStructureId);
2959 gst_mpdparser_free_representation_base_type
2960 (representation_node->RepresentationBase);
2961 g_list_free_full (representation_node->SubRepresentations,
2962 (GDestroyNotify) gst_mpdparser_free_subrepresentation_node);
2963 gst_mpdparser_free_seg_base_type_ext (representation_node->SegmentBase);
2964 gst_mpdparser_free_segment_template_node
2965 (representation_node->SegmentTemplate);
2966 gst_mpdparser_free_segment_list_node (representation_node->SegmentList);
2967 g_list_free_full (representation_node->BaseURLs,
2968 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2969 g_slice_free (GstRepresentationNode, representation_node);
2974 gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
2978 gst_mpdparser_free_representation_base_type
2979 (subrep_node->RepresentationBase);
2980 if (subrep_node->dependencyLevel)
2981 xmlFree (subrep_node->dependencyLevel);
2982 g_strfreev (subrep_node->contentComponent);
2983 g_slice_free (GstSubRepresentationNode, subrep_node);
2988 gst_mpdparser_free_s_node (GstSNode * s_node)
2991 g_slice_free (GstSNode, s_node);
2995 static GstSegmentTimelineNode *
2996 gst_mpdparser_segment_timeline_node_new (void)
2998 GstSegmentTimelineNode *node = g_slice_new0 (GstSegmentTimelineNode);
3000 g_queue_init (&node->S);
3006 gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
3009 g_queue_foreach (&seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
3010 g_queue_clear (&seg_timeline->S);
3011 g_slice_free (GstSegmentTimelineNode, seg_timeline);
3016 gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
3018 if (url_type_node) {
3019 if (url_type_node->sourceURL)
3020 xmlFree (url_type_node->sourceURL);
3021 g_slice_free (GstRange, url_type_node->range);
3022 g_slice_free (GstURLType, url_type_node);
3027 gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
3029 if (seg_base_type) {
3030 if (seg_base_type->indexRange)
3031 g_slice_free (GstRange, seg_base_type->indexRange);
3032 gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
3033 gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
3034 g_slice_free (GstSegmentBaseType, seg_base_type);
3039 gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
3042 if (mult_seg_base_type) {
3043 /* SegmentBaseType extension */
3044 gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
3045 gst_mpdparser_free_segment_timeline_node
3046 (mult_seg_base_type->SegmentTimeline);
3047 gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
3048 g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
3053 gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
3055 if (segment_list_node) {
3056 g_list_free_full (segment_list_node->SegmentURL,
3057 (GDestroyNotify) gst_mpdparser_free_segment_url_node);
3058 /* MultipleSegmentBaseType extension */
3059 gst_mpdparser_free_mult_seg_base_type_ext
3060 (segment_list_node->MultSegBaseType);
3061 if (segment_list_node->xlink_href)
3062 xmlFree (segment_list_node->xlink_href);
3063 g_slice_free (GstSegmentListNode, segment_list_node);
3068 gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
3071 if (segment_url->media)
3072 xmlFree (segment_url->media);
3073 g_slice_free (GstRange, segment_url->mediaRange);
3074 if (segment_url->index)
3075 xmlFree (segment_url->index);
3076 g_slice_free (GstRange, segment_url->indexRange);
3077 g_slice_free (GstSegmentURLNode, segment_url);
3082 gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
3084 if (base_url_node) {
3085 if (base_url_node->baseURL)
3086 xmlFree (base_url_node->baseURL);
3087 if (base_url_node->serviceLocation)
3088 xmlFree (base_url_node->serviceLocation);
3089 if (base_url_node->byteRange)
3090 xmlFree (base_url_node->byteRange);
3091 g_slice_free (GstBaseURL, base_url_node);
3096 gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
3098 if (descriptor_type) {
3099 if (descriptor_type->schemeIdUri)
3100 xmlFree (descriptor_type->schemeIdUri);
3101 if (descriptor_type->value)
3102 xmlFree (descriptor_type->value);
3103 g_slice_free (GstDescriptorType, descriptor_type);
3108 gst_mpdparser_free_content_component_node (GstContentComponentNode *
3109 content_component_node)
3111 if (content_component_node) {
3112 if (content_component_node->lang)
3113 xmlFree (content_component_node->lang);
3114 if (content_component_node->contentType)
3115 xmlFree (content_component_node->contentType);
3116 g_slice_free (GstRatio, content_component_node->par);
3117 g_list_free_full (content_component_node->Accessibility,
3118 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3119 g_list_free_full (content_component_node->Role,
3120 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3121 g_list_free_full (content_component_node->Rating,
3122 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3123 g_list_free_full (content_component_node->Viewpoint,
3124 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3125 g_slice_free (GstContentComponentNode, content_component_node);
3130 gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type)
3133 if (timing_type->urls)
3134 g_strfreev (timing_type->urls);
3135 g_slice_free (GstUTCTimingNode, timing_type);
3140 gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
3142 if (stream_period) {
3143 g_slice_free (GstStreamPeriod, stream_period);
3148 gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
3150 if (media_segment) {
3151 g_slice_free (GstMediaSegment, media_segment);
3156 gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
3158 g_assert (stream->segments == NULL);
3159 stream->segments = g_ptr_array_new ();
3160 g_ptr_array_set_free_func (stream->segments,
3161 (GDestroyNotify) gst_mpdparser_free_media_segment);
3165 gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
3167 if (active_stream) {
3168 g_free (active_stream->baseURL);
3169 active_stream->baseURL = NULL;
3170 g_free (active_stream->queryURL);
3171 active_stream->queryURL = NULL;
3172 if (active_stream->segments)
3173 g_ptr_array_unref (active_stream->segments);
3174 g_slice_free (GstActiveStream, active_stream);
3179 gst_mpdparser_get_mediaURL (GstActiveStream * stream,
3180 GstSegmentURLNode * segmentURL)
3182 const gchar *url_prefix;
3184 g_return_val_if_fail (stream != NULL, NULL);
3185 g_return_val_if_fail (segmentURL != NULL, NULL);
3187 url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
3188 g_return_val_if_fail (url_prefix != NULL, NULL);
3190 return segmentURL->media;
3193 static const gchar *
3194 gst_mpdparser_get_initializationURL (GstActiveStream * stream,
3195 GstURLType * InitializationURL)
3197 const gchar *url_prefix;
3199 g_return_val_if_fail (stream != NULL, NULL);
3201 url_prefix = (InitializationURL
3202 && InitializationURL->sourceURL) ? InitializationURL->
3203 sourceURL : stream->baseURL;
3208 /* ISO/IEC 23009-1:2004 5.3.9.4.4 */
3210 validate_format (const gchar * format)
3212 const gchar *p = format;
3214 /* Check if it starts with % */
3215 if (!p || p[0] != '%')
3219 /* the spec mandates a format like %0[width]d */
3220 /* Following the %, we must have a 0 */
3224 /* Following the % must be a number starting with 0
3226 while (g_ascii_isdigit (*p))
3229 /* After any 0 and alphanumeric values, there must be a d.
3235 /* And then potentially more characters without any
3236 * further %, even if the spec does not mention this
3238 p = strchr (p, '%');
3246 promote_format_to_uint64 (const gchar * format)
3248 const gchar *p = format;
3249 gchar *promoted_format;
3251 /* Must be called with a validated format! */
3252 g_return_val_if_fail (validate_format (format), NULL);
3254 /* it starts with % */
3257 /* Following the % must be a 0, or any of d, x or u.
3258 * x and u are not part of the spec, but don't hurt us
3263 while (g_ascii_isdigit (*p))
3267 /* After any 0 and alphanumeric values, there must be a d.
3268 * Otherwise validation would have failed
3270 g_assert (p[0] == 'd');
3273 g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
3276 return promoted_format;
3280 gst_mpdparser_validate_rfc1738_url (const char *s)
3284 (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
3288 /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
3289 operator, so this is safe for strings ending before two hex digits */
3290 if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
3300 gst_mpdparser_build_URL_from_template (const gchar * url_template,
3301 const gchar * id, guint number, guint bandwidth, guint64 time)
3303 static const gchar default_format[] = "%01d";
3304 gchar **tokens, *token, *ret;
3305 const gchar *format;
3308 g_return_val_if_fail (url_template != NULL, NULL);
3309 tokens = g_strsplit_set (url_template, "$", -1);
3311 GST_WARNING ("Scan of URL template failed!");
3314 num_tokens = g_strv_length (tokens);
3317 * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
3318 * An even number of tokens means the string is not valid.
3320 if ((num_tokens & 1) == 0) {
3321 GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
3322 num_tokens, url_template);
3323 g_strfreev (tokens);
3327 for (i = 0; i < num_tokens; i++) {
3329 format = default_format;
3331 /* the tokens to replace must be provided between $ characters, eg $token$
3332 * For a string like token0$token1$token2$token3$token4, only the odd number
3333 * tokens (1,3,...) must be parsed.
3340 if (!g_strcmp0 (token, "RepresentationID")) {
3341 if (!gst_mpdparser_validate_rfc1738_url (id))
3342 goto invalid_representation_id;
3344 tokens[i] = g_strdup_printf ("%s", id);
3346 } else if (!strncmp (token, "Number", 6)) {
3347 if (strlen (token) > 6) {
3348 format = token + 6; /* format tag */
3350 if (!validate_format (format))
3351 goto invalid_format;
3353 tokens[i] = g_strdup_printf (format, number);
3355 } else if (!strncmp (token, "Bandwidth", 9)) {
3356 if (strlen (token) > 9) {
3357 format = token + 9; /* format tag */
3359 if (!validate_format (format))
3360 goto invalid_format;
3362 tokens[i] = g_strdup_printf (format, bandwidth);
3364 } else if (!strncmp (token, "Time", 4)) {
3365 gchar *promoted_format;
3367 if (strlen (token) > 4) {
3368 format = token + 4; /* format tag */
3370 if (!validate_format (format))
3371 goto invalid_format;
3373 promoted_format = promote_format_to_uint64 (format);
3374 tokens[i] = g_strdup_printf (promoted_format, time);
3375 g_free (promoted_format);
3377 } else if (!g_strcmp0 (token, "")) {
3378 tokens[i] = g_strdup_printf ("%s", "$");
3381 /* unexpected identifier found between $ signs
3383 * "If the URL contains unescaped $ symbols which do not enclose a valid
3384 * identifier then the result of URL formation is undefined"
3386 goto invalid_format;
3390 ret = g_strjoinv (NULL, tokens);
3392 g_strfreev (tokens);
3398 GST_ERROR ("Invalid format '%s' in '%s'", format, token);
3400 g_strfreev (tokens);
3404 invalid_representation_id:
3407 ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
3410 g_strfreev (tokens);
3417 gst_mpd_client_get_period_index_at_time (GstMpdClient * client,
3421 guint period_idx = G_MAXUINT;
3424 GstDateTime *avail_start =
3425 gst_mpd_client_get_availability_start_time (client);
3426 GstStreamPeriod *stream_period;
3428 if (avail_start == NULL)
3431 time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3432 gst_date_time_unref (avail_start);
3434 if (time_offset < 0)
3437 if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3440 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3441 stream_period = iter->data;
3442 if (stream_period->start <= time_offset
3443 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3444 || stream_period->start + stream_period->duration > time_offset)) {
3453 static GstStreamPeriod *
3454 gst_mpdparser_get_stream_period (GstMpdClient * client)
3456 g_return_val_if_fail (client != NULL, NULL);
3457 g_return_val_if_fail (client->periods != NULL, NULL);
3459 return g_list_nth_data (client->periods, client->period_idx);
3463 gst_mpdparser_clone_range (GstRange * range)
3465 GstRange *clone = NULL;
3468 clone = g_slice_new0 (GstRange);
3469 clone->first_byte_pos = range->first_byte_pos;
3470 clone->last_byte_pos = range->last_byte_pos;
3477 gst_mpdparser_clone_URL (GstURLType * url)
3480 GstURLType *clone = NULL;
3483 clone = g_slice_new0 (GstURLType);
3484 if (url->sourceURL) {
3485 clone->sourceURL = xmlMemStrdup (url->sourceURL);
3487 clone->range = gst_mpdparser_clone_range (url->range);
3494 * Combine a base url with the current stream base url from the list of
3495 * baseURLs. Takes ownership of base and returns a new base.
3498 combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
3500 GstBaseURL *baseURL;
3504 baseURL = g_list_nth_data (list, idx);
3506 baseURL = list->data;
3509 ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
3510 gst_uri_unref (base);
3514 *query = gst_uri_get_query_string (ret);
3516 ret = gst_uri_make_writable (ret);
3517 gst_uri_set_query_table (ret, NULL);
3525 /* select a stream and extract the baseURL (if present) */
3527 gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
3530 GstStreamPeriod *stream_period;
3531 static const gchar empty[] = "";
3535 g_return_val_if_fail (stream != NULL, g_strdup (empty));
3536 stream_period = gst_mpdparser_get_stream_period (client);
3537 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
3538 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
3540 /* NULLify query return before we start */
3544 /* initialise base url */
3546 gst_uri_from_string (client->
3547 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
3549 /* combine a BaseURL at the MPD level with the current base url */
3551 combine_urls (abs_url, client->mpd_node->BaseURLs, query,
3552 stream->baseURL_idx);
3554 /* combine a BaseURL at the Period level with the current base url */
3556 combine_urls (abs_url, stream_period->period->BaseURLs, query,
3557 stream->baseURL_idx);
3559 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
3560 stream->cur_adapt_set->contentType);
3561 /* combine a BaseURL at the AdaptationSet level with the current base url */
3563 combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
3564 stream->baseURL_idx);
3566 /* combine a BaseURL at the Representation level with the current base url */
3568 combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
3569 stream->baseURL_idx);
3571 ret = gst_uri_to_string (abs_url);
3572 gst_uri_unref (abs_url);
3578 gst_mpd_client_get_segment_duration (GstMpdClient * client,
3579 GstActiveStream * stream, guint64 * scale_dur)
3581 GstStreamPeriod *stream_period;
3582 GstMultSegmentBaseType *base = NULL;
3583 GstClockTime duration = 0;
3585 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
3586 stream_period = gst_mpdparser_get_stream_period (client);
3587 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
3589 if (stream->cur_segment_list) {
3590 base = stream->cur_segment_list->MultSegBaseType;
3591 } else if (stream->cur_seg_template) {
3592 base = stream->cur_seg_template->MultSegBaseType;
3595 if (base == NULL || base->SegBaseType == NULL) {
3596 /* this may happen when we have a single segment */
3597 duration = stream_period->duration;
3599 *scale_dur = duration;
3601 /* duration is guint so this cannot overflow */
3602 duration = base->duration * GST_SECOND;
3604 *scale_dur = duration;
3605 duration /= base->SegBaseType->timescale;
3611 /*****************************/
3612 /******* API functions *******/
3613 /*****************************/
3616 gst_mpd_client_new (void)
3618 GstMpdClient *client;
3620 client = g_new0 (GstMpdClient, 1);
3626 gst_active_streams_free (GstMpdClient * client)
3628 if (client->active_streams) {
3629 g_list_foreach (client->active_streams,
3630 (GFunc) gst_mpdparser_free_active_stream, NULL);
3631 g_list_free (client->active_streams);
3632 client->active_streams = NULL;
3637 gst_mpd_client_free (GstMpdClient * client)
3639 g_return_if_fail (client != NULL);
3641 if (client->mpd_node)
3642 gst_mpdparser_free_mpd_node (client->mpd_node);
3644 if (client->periods) {
3645 g_list_free_full (client->periods,
3646 (GDestroyNotify) gst_mpdparser_free_stream_period);
3649 gst_active_streams_free (client);
3651 g_free (client->mpd_uri);
3652 client->mpd_uri = NULL;
3653 g_free (client->mpd_base_uri);
3654 client->mpd_base_uri = NULL;
3656 if (client->downloader)
3657 gst_object_unref (client->downloader);
3658 client->downloader = NULL;
3664 gst_mpd_client_set_uri_downloader (GstMpdClient * client,
3665 GstUriDownloader * downloader)
3667 if (client->downloader)
3668 gst_object_unref (client->downloader);
3669 client->downloader = gst_object_ref (downloader);
3673 gst_mpd_client_check_profiles (GstMpdClient * client)
3675 GST_DEBUG ("Profiles: %s",
3676 client->mpd_node->profiles ? client->mpd_node->profiles : "<none>");
3678 if (!client->mpd_node->profiles)
3681 if (g_strstr_len (client->mpd_node->profiles, -1,
3682 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
3683 client->profile_isoff_ondemand = TRUE;
3684 GST_DEBUG ("Found ISOFF on demand profile (2011)");
3689 gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client)
3693 for (l = client->mpd_node->Periods; l; /* explicitly advanced below */ ) {
3694 GstPeriodNode *period = l->data;
3697 if (period->xlink_href && period->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3698 GList *new_periods, *prev, *next;
3700 new_periods = gst_mpd_client_fetch_external_period (client, period);
3703 client->mpd_node->Periods =
3704 g_list_delete_link (client->mpd_node->Periods, l);
3705 gst_mpdparser_free_period_node (period);
3708 /* Get new next node, we will insert before this */
3712 next = client->mpd_node->Periods;
3714 while (new_periods) {
3715 client->mpd_node->Periods =
3716 g_list_insert_before (client->mpd_node->Periods, next,
3718 new_periods = g_list_delete_link (new_periods, new_periods);
3722 /* Update our iterator to the first new period if any, or the next */
3726 l = client->mpd_node->Periods;
3731 if (period->SegmentList && period->SegmentList->xlink_href
3732 && period->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3733 GstSegmentListNode *new_segment_list;
3736 gst_mpd_client_fetch_external_segment_list (client, period, NULL,
3737 NULL, NULL, period->SegmentList);
3739 gst_mpdparser_free_segment_list_node (period->SegmentList);
3740 period->SegmentList = new_segment_list;
3743 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
3744 GstAdaptationSetNode *adapt_set = m->data;
3747 if (adapt_set->xlink_href
3748 && adapt_set->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3749 GList *new_adapt_sets, *prev, *next;
3752 gst_mpd_client_fetch_external_adaptation_set (client, period,
3756 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
3757 gst_mpdparser_free_adaptation_set_node (adapt_set);
3760 /* Get new next node, we will insert before this */
3764 next = period->AdaptationSets;
3766 while (new_adapt_sets) {
3767 period->AdaptationSets =
3768 g_list_insert_before (period->AdaptationSets, next,
3769 new_adapt_sets->data);
3770 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
3774 /* Update our iterator to the first new adapt_set if any, or the next */
3778 m = period->AdaptationSets;
3783 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
3784 && adapt_set->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3785 GstSegmentListNode *new_segment_list;
3788 gst_mpd_client_fetch_external_segment_list (client, period,
3789 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
3791 gst_mpdparser_free_segment_list_node (adapt_set->SegmentList);
3792 adapt_set->SegmentList = new_segment_list;
3795 for (n = adapt_set->Representations; n; n = n->next) {
3796 GstRepresentationNode *representation = n->data;
3798 if (representation->SegmentList
3799 && representation->SegmentList->xlink_href
3800 && representation->SegmentList->actuate ==
3801 GST_XLINK_ACTUATE_ON_LOAD) {
3803 GstSegmentListNode *new_segment_list;
3806 gst_mpd_client_fetch_external_segment_list (client, period,
3807 adapt_set, representation, adapt_set->SegmentList,
3808 representation->SegmentList);
3810 gst_mpdparser_free_segment_list_node (representation->SegmentList);
3811 representation->SegmentList = new_segment_list;
3824 gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
3826 gboolean ret = FALSE;
3830 xmlNode *root_element = NULL;
3832 GST_DEBUG ("MPD file fully buffered, start parsing...");
3834 /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
3836 /* this initialize the library and check potential ABI mismatches
3837 * between the version it was compiled for and the actual shared
3840 LIBXML_TEST_VERSION;
3842 /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
3843 doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
3845 GST_ERROR ("failed to parse the MPD file");
3848 /* get the root element node */
3849 root_element = xmlDocGetRootElement (doc);
3851 if (root_element->type != XML_ELEMENT_NODE
3852 || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
3854 ("can not find the root element MPD, failed to parse the MPD file");
3855 ret = FALSE; /* used to return TRUE before, but this seems wrong */
3857 /* now we can parse the MPD root node and all children nodes, recursively */
3858 ret = gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
3860 /* free the document */
3865 gst_mpd_client_check_profiles (client);
3866 gst_mpd_client_fetch_on_load_external_resources (client);
3874 gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream)
3876 GstActiveStream *stream;
3878 g_return_val_if_fail (client != NULL, NULL);
3879 g_return_val_if_fail (client->active_streams != NULL, NULL);
3880 stream = g_list_nth_data (client->active_streams, indexStream);
3881 g_return_val_if_fail (stream != NULL, NULL);
3883 return stream->baseURL;
3887 gst_mpdparser_get_segment_end_time (GstMpdClient * client, GPtrArray * segments,
3888 const GstMediaSegment * segment, gint index)
3890 const GstStreamPeriod *stream_period;
3893 if (segment->repeat >= 0)
3894 return segment->start + (segment->repeat + 1) * segment->duration;
3896 if (index < segments->len - 1) {
3897 const GstMediaSegment *next_segment =
3898 g_ptr_array_index (segments, index + 1);
3899 end = next_segment->start;
3901 stream_period = gst_mpdparser_get_stream_period (client);
3902 end = stream_period->start + stream_period->duration;
3908 gst_mpd_client_add_media_segment (GstActiveStream * stream,
3909 GstSegmentURLNode * url_node, guint number, gint repeat,
3910 guint64 scale_start, guint64 scale_duration,
3911 GstClockTime start, GstClockTime duration)
3913 GstMediaSegment *media_segment;
3915 g_return_val_if_fail (stream->segments != NULL, FALSE);
3917 media_segment = g_slice_new0 (GstMediaSegment);
3919 media_segment->SegmentURL = url_node;
3920 media_segment->number = number;
3921 media_segment->scale_start = scale_start;
3922 media_segment->scale_duration = scale_duration;
3923 media_segment->start = start;
3924 media_segment->duration = duration;
3925 media_segment->repeat = repeat;
3927 g_ptr_array_add (stream->segments, media_segment);
3928 GST_LOG ("Added new segment: number %d, repeat %d, "
3929 "ts: %" GST_TIME_FORMAT ", dur: %"
3930 GST_TIME_FORMAT, number, repeat,
3931 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
3937 gst_mpd_client_stream_update_presentation_time_offset (GstMpdClient * client,
3938 GstActiveStream * stream)
3940 GstSegmentBaseType *segbase = NULL;
3942 /* Find the used segbase */
3943 if (stream->cur_segment_list) {
3944 segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
3945 } else if (stream->cur_seg_template) {
3946 segbase = stream->cur_seg_template->MultSegBaseType->SegBaseType;
3947 } else if (stream->cur_segment_base) {
3948 segbase = stream->cur_segment_base;
3952 /* Avoid overflows */
3953 stream->presentationTimeOffset =
3954 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
3955 segbase->timescale);
3957 stream->presentationTimeOffset = 0;
3960 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
3961 GST_TIME_ARGS (stream->presentationTimeOffset));
3965 gst_mpd_client_setup_representation (GstMpdClient * client,
3966 GstActiveStream * stream, GstRepresentationNode * representation)
3968 GstStreamPeriod *stream_period;
3970 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
3974 if (stream->cur_adapt_set == NULL) {
3975 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
3979 rep_list = stream->cur_adapt_set->Representations;
3980 stream->cur_representation = representation;
3981 stream->representation_idx = g_list_index (rep_list, representation);
3983 /* clean the old segment list, if any */
3984 if (stream->segments) {
3985 g_ptr_array_unref (stream->segments);
3986 stream->segments = NULL;
3989 stream_period = gst_mpdparser_get_stream_period (client);
3990 g_return_val_if_fail (stream_period != NULL, FALSE);
3991 g_return_val_if_fail (stream_period->period != NULL, FALSE);
3993 PeriodStart = stream_period->start;
3994 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
3995 PeriodEnd = stream_period->start + stream_period->duration;
3997 PeriodEnd = GST_CLOCK_TIME_NONE;
3999 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
4000 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
4002 if (representation->SegmentBase != NULL
4003 || representation->SegmentList != NULL) {
4006 /* We have a fixed list of segments for any of the cases here,
4007 * init the segments list */
4008 gst_mpdparser_init_active_stream_segments (stream);
4010 /* get the first segment_base of the selected representation */
4011 if ((stream->cur_segment_base =
4012 gst_mpdparser_get_segment_base (stream_period->period,
4013 stream->cur_adapt_set, representation)) == NULL) {
4014 GST_DEBUG ("No useful SegmentBase node for the current Representation");
4017 /* get the first segment_list of the selected representation */
4018 if ((stream->cur_segment_list =
4019 gst_mpdparser_get_segment_list (client, stream_period->period,
4020 stream->cur_adapt_set, representation)) == NULL) {
4021 GST_DEBUG ("No useful SegmentList node for the current Representation");
4022 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4023 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4024 PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
4028 /* build the list of GstMediaSegment nodes from the SegmentList node */
4029 SegmentURL = stream->cur_segment_list->SegmentURL;
4030 if (SegmentURL == NULL) {
4032 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
4036 /* build segment list */
4037 i = stream->cur_segment_list->MultSegBaseType->startNumber;
4039 start_time = PeriodStart;
4041 GST_LOG ("Building media segment list using a SegmentList node");
4042 if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
4043 GstSegmentTimelineNode *timeline;
4046 GstClockTime presentationTimeOffset;
4047 GstSegmentBaseType *segbase;
4049 segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
4050 presentationTimeOffset =
4051 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
4052 segbase->timescale);
4053 GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
4054 presentationTimeOffset);
4055 timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
4056 for (list = g_queue_peek_head_link (&timeline->S); list;
4057 list = g_list_next (list)) {
4060 S = (GstSNode *) list->data;
4061 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
4062 G_GUINT64_FORMAT, S->d, S->r, S->t);
4064 stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
4065 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4069 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
4070 + PeriodStart - presentationTimeOffset;
4075 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
4079 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4080 S->r, start, S->d, start_time, duration)) {
4084 start_time += duration * (S->r + 1);
4085 start += S->d * (S->r + 1);
4086 SegmentURL = g_list_next (SegmentURL);
4092 gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
4093 if (!GST_CLOCK_TIME_IS_VALID (duration))
4096 while (SegmentURL) {
4097 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4098 0, start, scale_dur, start_time, duration)) {
4103 start_time += duration;
4104 SegmentURL = g_list_next (SegmentURL);
4109 if (representation->SegmentTemplate != NULL) {
4110 stream->cur_seg_template = representation->SegmentTemplate;
4111 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
4112 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
4113 } else if (stream_period->period->SegmentTemplate != NULL) {
4114 stream->cur_seg_template = stream_period->period->SegmentTemplate;
4117 if (stream->cur_seg_template == NULL
4118 || stream->cur_seg_template->MultSegBaseType == NULL) {
4120 gst_mpdparser_init_active_stream_segments (stream);
4121 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4122 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4123 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
4127 GstClockTime presentationTimeOffset;
4128 GstMultSegmentBaseType *mult_seg =
4129 stream->cur_seg_template->MultSegBaseType;
4131 presentationTimeOffset =
4132 gst_util_uint64_scale (mult_seg->SegBaseType->presentationTimeOffset,
4133 GST_SECOND, mult_seg->SegBaseType->timescale);
4134 GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
4135 GST_TIME_ARGS (presentationTimeOffset));
4136 /* build segment list */
4137 i = mult_seg->startNumber;
4141 GST_LOG ("Building media segment list using this template: %s",
4142 stream->cur_seg_template->media);
4144 if (mult_seg->SegmentTimeline) {
4145 GstSegmentTimelineNode *timeline;
4149 timeline = mult_seg->SegmentTimeline;
4150 gst_mpdparser_init_active_stream_segments (stream);
4151 for (list = g_queue_peek_head_link (&timeline->S); list;
4152 list = g_list_next (list)) {
4155 S = (GstSNode *) list->data;
4156 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
4157 G_GUINT64_FORMAT, S->d, S->r, S->t);
4158 timescale = mult_seg->SegBaseType->timescale;
4159 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4162 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
4163 + PeriodStart - presentationTimeOffset;
4166 if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
4167 S->d, start_time, duration)) {
4171 start += S->d * (S->r + 1);
4172 start_time += duration * (S->r + 1);
4175 /* NOP - The segment is created on demand with the template, no need
4176 * to build a list */
4181 /* clip duration of segments to stop at period end */
4182 if (stream->segments && stream->segments->len) {
4183 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
4186 for (n = 0; n < stream->segments->len; ++n) {
4187 GstMediaSegment *media_segment =
4188 g_ptr_array_index (stream->segments, n);
4189 if (media_segment) {
4190 if (media_segment->start + media_segment->duration > PeriodEnd) {
4191 GstClockTime stop = PeriodEnd;
4192 if (n < stream->segments->len - 1) {
4193 GstMediaSegment *next_segment =
4194 g_ptr_array_index (stream->segments, n + 1);
4195 if (next_segment && next_segment->start < PeriodEnd)
4196 stop = next_segment->start;
4198 media_segment->duration =
4199 media_segment->start > stop ? 0 : stop - media_segment->start;
4200 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
4201 GST_TIME_ARGS (media_segment->duration));
4203 /* If the segment was clipped entirely, we discard it and all
4204 * subsequent ones */
4205 if (media_segment->duration == 0) {
4206 GST_WARNING ("Discarding %u segments outside period",
4207 stream->segments->len - n);
4208 /* _set_size should properly unref elements */
4209 g_ptr_array_set_size (stream->segments, n);
4216 #ifndef GST_DISABLE_GST_DEBUG
4217 if (stream->segments->len > 0) {
4218 GstMediaSegment *last_media_segment =
4219 g_ptr_array_index (stream->segments, stream->segments->len - 1);
4220 GST_LOG ("Built a list of %d segments", last_media_segment->number);
4222 GST_LOG ("All media segments were clipped");
4227 g_free (stream->baseURL);
4228 g_free (stream->queryURL);
4230 gst_mpdparser_parse_baseURL (client, stream, &stream->queryURL);
4232 gst_mpd_client_stream_update_presentation_time_offset (client, stream);
4237 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
4238 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
4241 gst_mpd_client_fetch_external_period (GstMpdClient * client,
4242 GstPeriodNode * period_node)
4244 GstFragment *download;
4245 GstAdapter *adapter;
4246 GstBuffer *period_buffer;
4248 xmlDocPtr doc = NULL;
4249 GstUri *base_uri, *uri;
4250 gchar *query = NULL;
4251 gchar *uri_string, *wrapper;
4252 GList *new_periods = NULL;
4255 /* ISO/IEC 23009-1:2014 5.5.3 4)
4256 * Remove nodes that resolve to nothing when resolving
4258 if (strcmp (period_node->xlink_href,
4259 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4263 if (!client->downloader) {
4267 /* Build absolute URI */
4269 /* Get base URI at the MPD level */
4271 gst_uri_from_string (client->
4272 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4274 /* combine a BaseURL at the MPD level with the current base url */
4275 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4276 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
4278 gst_uri_set_query_string (uri, query);
4280 uri_string = gst_uri_to_string (uri);
4281 gst_uri_unref (base_uri);
4282 gst_uri_unref (uri);
4285 gst_uri_downloader_fetch_uri (client->downloader,
4286 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4287 g_free (uri_string);
4290 GST_ERROR ("Failed to download external Period node at '%s': %s",
4291 period_node->xlink_href, err->message);
4292 g_clear_error (&err);
4296 period_buffer = gst_fragment_get_buffer (download);
4297 g_object_unref (download);
4299 /* external xml could have multiple period without root xmlNode.
4300 * To avoid xml parsing error caused by no root node, wrapping it with
4301 * custom root node */
4302 adapter = gst_adapter_new ();
4304 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
4305 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
4306 gst_adapter_push (adapter,
4307 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
4309 gst_adapter_push (adapter, period_buffer);
4311 wrapper = g_strdup (CUSTOM_WRAPPER_END);
4312 gst_adapter_push (adapter,
4313 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
4315 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
4318 xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL,
4321 gst_adapter_unmap (adapter);
4322 gst_adapter_clear (adapter);
4323 gst_object_unref (adapter);
4326 xmlNode *root_element = xmlDocGetRootElement (doc);
4329 if (root_element->type != XML_ELEMENT_NODE)
4332 for (iter = root_element->children; iter; iter = iter->next) {
4333 if (iter->type == XML_ELEMENT_NODE) {
4334 if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
4335 gst_mpdparser_parse_period_node (&new_periods, iter);
4352 GST_ERROR ("Failed to parse period node XML");
4355 g_list_free_full (new_periods,
4356 (GDestroyNotify) gst_mpdparser_free_period_node);
4363 gst_mpd_client_setup_media_presentation (GstMpdClient * client,
4364 GstClockTime time, gint period_idx, const gchar * period_id)
4366 GstStreamPeriod *stream_period;
4367 GstClockTime start, duration;
4370 gboolean ret = FALSE;
4372 g_return_val_if_fail (client != NULL, FALSE);
4373 g_return_val_if_fail (client->mpd_node != NULL, FALSE);
4375 /* Check if we set up the media presentation far enough already */
4376 for (list = client->periods; list; list = list->next) {
4377 GstStreamPeriod *stream_period = list->data;
4379 if ((time != GST_CLOCK_TIME_NONE
4380 && stream_period->duration != GST_CLOCK_TIME_NONE
4381 && stream_period->start + stream_period->duration >= time)
4382 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4385 if (period_idx != -1 && stream_period->number >= period_idx)
4388 if (period_id != NULL && stream_period->period->id != NULL
4389 && strcmp (stream_period->period->id, period_id) == 0)
4394 GST_DEBUG ("Building the list of Periods in the Media Presentation");
4395 /* clean the old period list, if any */
4396 /* TODO: In theory we could reuse the ones we have so far but that
4397 * seems more complicated than the overhead caused here
4399 if (client->periods) {
4400 g_list_foreach (client->periods,
4401 (GFunc) gst_mpdparser_free_stream_period, NULL);
4402 g_list_free (client->periods);
4403 client->periods = NULL;
4408 duration = GST_CLOCK_TIME_NONE;
4410 if (client->mpd_node->mediaPresentationDuration <= 0 &&
4411 client->mpd_node->mediaPresentationDuration != -1) {
4412 /* Invalid MPD file: MPD duration is negative or zero */
4416 for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
4417 GstPeriodNode *period_node = list->data;
4418 GstPeriodNode *next_period_node = NULL;
4420 /* Download external period */
4421 if (period_node->xlink_href) {
4425 new_periods = gst_mpd_client_fetch_external_period (client, period_node);
4428 client->mpd_node->Periods =
4429 g_list_delete_link (client->mpd_node->Periods, list);
4430 gst_mpdparser_free_period_node (period_node);
4433 /* Get new next node, we will insert before this */
4437 next = client->mpd_node->Periods;
4439 while (new_periods) {
4440 client->mpd_node->Periods =
4441 g_list_insert_before (client->mpd_node->Periods, next,
4443 new_periods = g_list_delete_link (new_periods, new_periods);
4447 /* Update our iterator to the first new period if any, or the next */
4451 list = client->mpd_node->Periods;
4457 if (period_node->start != -1) {
4458 /* we have a regular period */
4459 /* start cannot be smaller than previous start */
4460 if (list != g_list_first (client->mpd_node->Periods)
4461 && start >= period_node->start * GST_MSECOND) {
4462 /* Invalid MPD file: duration would be negative or zero */
4465 start = period_node->start * GST_MSECOND;
4466 } else if (duration != GST_CLOCK_TIME_NONE) {
4467 /* start time inferred from previous period, this is still a regular period */
4469 } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
4470 /* first period of a static MPD file, start time is 0 */
4472 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4473 /* this should be a live stream, let this pass */
4475 /* this is an 'Early Available Period' */
4479 /* compute duration.
4480 If there is a start time for the next period, or this is the last period
4481 and mediaPresentationDuration was set, those values will take precedence
4482 over a configured period duration in computing this period's duration
4484 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
4485 "The Period extends until the PeriodStart of the next Period, or until
4486 the end of the Media Presentation in the case of the last Period."
4489 while ((next = g_list_next (list)) != NULL) {
4490 /* try to infer this period duration from the start time of the next period */
4491 next_period_node = next->data;
4493 if (next_period_node->xlink_href) {
4497 gst_mpd_client_fetch_external_period (client, next_period_node);
4499 client->mpd_node->Periods =
4500 g_list_delete_link (client->mpd_node->Periods, next);
4501 gst_mpdparser_free_period_node (next_period_node);
4502 next_period_node = NULL;
4503 /* Get new next node, we will insert before this */
4504 next = g_list_next (list);
4505 while (new_periods) {
4506 client->mpd_node->Periods =
4507 g_list_insert_before (client->mpd_node->Periods, next,
4509 new_periods = g_list_delete_link (new_periods, new_periods);
4512 /* And try again, getting the next list element which is now our newly
4513 * inserted nodes. If any */
4515 /* Got the next period and it doesn't have to be downloaded first */
4520 if (next_period_node) {
4521 if (next_period_node->start != -1) {
4522 if (start >= next_period_node->start * GST_MSECOND) {
4523 /* Invalid MPD file: duration would be negative or zero */
4526 duration = next_period_node->start * GST_MSECOND - start;
4527 } else if (period_node->duration != -1) {
4528 if (period_node->duration <= 0) {
4529 /* Invalid MPD file: duration would be negative or zero */
4532 duration = period_node->duration * GST_MSECOND;
4533 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4534 /* might be a live file, ignore unspecified duration */
4536 /* Invalid MPD file! */
4539 } else if (client->mpd_node->mediaPresentationDuration != -1) {
4540 /* last Period of the Media Presentation */
4541 if (client->mpd_node->mediaPresentationDuration * GST_MSECOND <= start) {
4542 /* Invalid MPD file: duration would be negative or zero */
4546 client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
4547 } else if (period_node->duration != -1) {
4548 duration = period_node->duration * GST_MSECOND;
4549 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4550 /* might be a live file, ignore unspecified duration */
4552 /* Invalid MPD file! */
4556 stream_period = g_slice_new0 (GstStreamPeriod);
4557 client->periods = g_list_append (client->periods, stream_period);
4558 stream_period->period = period_node;
4559 stream_period->number = idx++;
4560 stream_period->start = start;
4561 stream_period->duration = duration;
4563 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
4564 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
4566 if ((time != GST_CLOCK_TIME_NONE
4567 && stream_period->duration != GST_CLOCK_TIME_NONE
4568 && stream_period->start + stream_period->duration >= time)
4569 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4572 if (period_idx != -1 && stream_period->number >= period_idx)
4575 if (period_id != NULL && stream_period->period->id != NULL
4576 && strcmp (stream_period->period->id, period_id) == 0)
4583 ("Found a total of %d valid Periods in the Media Presentation up to this point",
4589 ("Found an Early Available Period, skipping the rest of the Media Presentation");
4594 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
4600 gst_mpd_client_fetch_external_adaptation_set (GstMpdClient * client,
4601 GstPeriodNode * period, GstAdaptationSetNode * adapt_set)
4603 GstFragment *download;
4604 GstBuffer *adapt_set_buffer;
4607 xmlDocPtr doc = NULL;
4608 GstUri *base_uri, *uri;
4609 gchar *query = NULL;
4611 GList *new_adapt_sets = NULL;
4613 /* ISO/IEC 23009-1:2014 5.5.3 4)
4614 * Remove nodes that resolve to nothing when resolving
4616 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4620 if (!client->downloader) {
4624 /* Build absolute URI */
4626 /* Get base URI at the MPD level */
4628 gst_uri_from_string (client->
4629 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4631 /* combine a BaseURL at the MPD level with the current base url */
4632 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4634 /* combine a BaseURL at the Period level with the current base url */
4635 base_uri = combine_urls (base_uri, period->BaseURLs, &query, 0);
4637 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
4639 gst_uri_set_query_string (uri, query);
4641 uri_string = gst_uri_to_string (uri);
4642 gst_uri_unref (base_uri);
4643 gst_uri_unref (uri);
4646 gst_uri_downloader_fetch_uri (client->downloader,
4647 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4648 g_free (uri_string);
4651 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
4652 adapt_set->xlink_href, err->message);
4653 g_clear_error (&err);
4657 adapt_set_buffer = gst_fragment_get_buffer (download);
4658 g_object_unref (download);
4660 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
4663 xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
4666 gst_buffer_unmap (adapt_set_buffer, &map);
4667 gst_buffer_unref (adapt_set_buffer);
4669 /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
4670 * in external xml is allowed */
4672 xmlNode *root_element = xmlDocGetRootElement (doc);
4674 if (root_element->type != XML_ELEMENT_NODE ||
4675 xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") != 0) {
4679 gst_mpdparser_parse_adaptation_set_node (&new_adapt_sets, root_element,
4689 return new_adapt_sets;
4692 GST_ERROR ("Failed to parse adaptation set node XML");
4697 gst_mpd_client_get_adaptation_sets_for_period (GstMpdClient * client,
4698 GstStreamPeriod * period)
4702 g_return_val_if_fail (period != NULL, NULL);
4704 /* Resolve all external adaptation sets of this period. Every user of
4705 * the adaptation sets would need to know the content of all adaptation sets
4706 * to decide which one to use, so we have to resolve them all here
4708 for (list = period->period->AdaptationSets; list;
4709 /* advanced explicitly below */ ) {
4710 GstAdaptationSetNode *adapt_set = (GstAdaptationSetNode *) list->data;
4711 GList *new_adapt_sets = NULL, *prev, *next;
4713 if (!adapt_set->xlink_href) {
4719 gst_mpd_client_fetch_external_adaptation_set (client, period->period,
4723 period->period->AdaptationSets =
4724 g_list_delete_link (period->period->AdaptationSets, list);
4725 gst_mpdparser_free_adaptation_set_node (adapt_set);
4728 /* Get new next node, we will insert before this */
4732 next = period->period->AdaptationSets;
4734 while (new_adapt_sets) {
4735 period->period->AdaptationSets =
4736 g_list_insert_before (period->period->AdaptationSets, next,
4737 new_adapt_sets->data);
4738 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
4741 /* Update our iterator to the first new adaptation set if any, or the next */
4745 list = period->period->AdaptationSets;
4748 return period->period->AdaptationSets;
4752 gst_mpd_client_get_adaptation_sets (GstMpdClient * client)
4754 GstStreamPeriod *stream_period;
4756 stream_period = gst_mpdparser_get_stream_period (client);
4757 if (stream_period == NULL || stream_period->period == NULL) {
4758 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
4762 return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
4766 gst_mpd_client_setup_streaming (GstMpdClient * client,
4767 GstAdaptationSetNode * adapt_set)
4769 GstRepresentationNode *representation;
4770 GList *rep_list = NULL;
4771 GstActiveStream *stream;
4773 rep_list = adapt_set->Representations;
4775 GST_WARNING ("Can not retrieve any representation, aborting...");
4779 stream = g_slice_new0 (GstActiveStream);
4780 gst_mpdparser_init_active_stream_segments (stream);
4782 stream->baseURL_idx = 0;
4783 stream->cur_adapt_set = adapt_set;
4785 GST_DEBUG ("0. Current stream %p", stream);
4790 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
4791 stream->max_bandwidth);
4793 if (!representation) {
4795 ("Can not retrieve a representation with the requested bandwidth");
4796 representation = gst_mpdparser_get_lowest_representation (rep_list);
4800 representation = gst_mpdparser_get_lowest_representation (rep_list);
4803 if (!representation) {
4804 GST_WARNING ("No valid representation in the MPD file, aborting...");
4805 gst_mpdparser_free_active_stream (stream);
4809 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
4810 if (stream->mimeType == GST_STREAM_UNKNOWN) {
4811 GST_WARNING ("Unknown mime type in the representation, aborting...");
4812 gst_mpdparser_free_active_stream (stream);
4816 client->active_streams = g_list_append (client->active_streams, stream);
4817 if (!gst_mpd_client_setup_representation (client, stream, representation)) {
4818 GST_WARNING ("Failed to setup the representation, aborting...");
4822 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
4829 gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
4830 gboolean forward, GstSeekFlags flags, GstClockTime ts,
4831 GstClockTime * final_ts)
4834 gint repeat_index = 0;
4835 GstMediaSegment *selectedChunk = NULL;
4837 g_return_val_if_fail (stream != NULL, 0);
4839 if (stream->segments) {
4840 for (index = 0; index < stream->segments->len; index++) {
4841 gboolean in_segment = FALSE;
4842 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
4843 GstClockTime end_time;
4845 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
4846 stream->segments->len);
4849 gst_mpdparser_get_segment_end_time (client, stream->segments,
4852 /* avoid downloading another fragment just for 1ns in reverse mode */
4854 in_segment = ts < end_time;
4856 in_segment = ts <= end_time;
4859 GstClockTime chunk_time;
4861 selectedChunk = segment;
4862 repeat_index = (ts - segment->start) / segment->duration;
4864 chunk_time = segment->start + segment->duration * repeat_index;
4866 /* At the end of a segment in reverse mode, start from the previous fragment */
4867 if (!forward && repeat_index > 0
4868 && ((ts - segment->start) % segment->duration == 0))
4871 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
4872 if (repeat_index + 1 < segment->repeat) {
4873 if (ts - chunk_time > chunk_time + segment->duration - ts)
4875 } else if (index + 1 < stream->segments->len) {
4876 GstMediaSegment *next_segment =
4877 g_ptr_array_index (stream->segments, index + 1);
4879 if (ts - chunk_time > next_segment->start - ts) {
4881 selectedChunk = next_segment;
4885 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
4886 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
4889 if (repeat_index + 1 < segment->repeat) {
4893 if (index + 1 >= stream->segments->len) {
4894 selectedChunk = NULL;
4896 selectedChunk = g_ptr_array_index (stream->segments, ++index);
4904 if (selectedChunk == NULL) {
4905 stream->segment_index = stream->segments->len;
4906 stream->segment_repeat_index = 0;
4907 GST_DEBUG ("Seek to after last segment");
4912 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
4914 GstClockTime duration =
4915 gst_mpd_client_get_segment_duration (client, stream, NULL);
4916 GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
4917 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
4918 GstClockTime index_time;
4920 g_return_val_if_fail (stream->cur_seg_template->
4921 MultSegBaseType->SegmentTimeline == NULL, FALSE);
4922 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
4926 if (ts > stream_period->start)
4927 ts -= stream_period->start;
4931 index = ts / duration;
4933 /* At the end of a segment in reverse mode, start from the previous fragment */
4934 if (!forward && index > 0 && ts % duration == 0)
4937 index_time = index * duration;
4939 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
4940 if (ts - index_time > index_time + duration - ts)
4942 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
4943 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
4944 && ts != index_time) {
4948 if (segments_count > 0 && index >= segments_count) {
4949 stream->segment_index = segments_count;
4950 stream->segment_repeat_index = 0;
4951 GST_DEBUG ("Seek to after last segment");
4955 *final_ts = index * duration;
4958 stream->segment_repeat_index = repeat_index;
4959 stream->segment_index = index;
4965 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
4966 const GstDateTime * t2)
4968 GDateTime *gdt1, *gdt2;
4971 g_assert (t1 != NULL && t2 != NULL);
4972 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
4973 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
4974 diff = g_date_time_difference (gdt2, gdt1);
4975 g_date_time_unref (gdt1);
4976 g_date_time_unref (gdt2);
4977 return diff * GST_USECOND;
4981 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
4987 g_assert (t1 != NULL);
4988 gdt = gst_date_time_to_g_date_time (t1);
4989 g_assert (gdt != NULL);
4990 gdt2 = g_date_time_add (gdt, usecs);
4991 g_assert (gdt2 != NULL);
4992 g_date_time_unref (gdt);
4993 rv = gst_date_time_new_from_g_date_time (gdt2);
4995 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
4996 * ownership of the GDateTime pointer.
5002 static GstDateTime *
5003 gst_mpd_client_get_availability_start_time (GstMpdClient * client)
5005 GstDateTime *start_time;
5008 return (GstDateTime *) NULL;
5010 start_time = client->mpd_node->availabilityStartTime;
5012 gst_date_time_ref (start_time);
5017 gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
5018 guint stream_idx, GstClockTime * ts)
5020 GstActiveStream *stream;
5022 GstMediaSegment *currentChunk;
5023 GstStreamPeriod *stream_period;
5025 GST_DEBUG ("Stream index: %i", stream_idx);
5026 stream = g_list_nth_data (client->active_streams, stream_idx);
5027 g_return_val_if_fail (stream != NULL, 0);
5029 if (!stream->segments) {
5030 stream_period = gst_mpdparser_get_stream_period (client);
5031 *ts = stream_period->start + stream_period->duration;
5033 segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
5034 if (segment_idx >= stream->segments->len) {
5035 GST_WARNING ("Segment index %d is outside of segment list of length %d",
5036 segment_idx, stream->segments->len);
5039 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
5041 if (currentChunk->repeat >= 0) {
5043 currentChunk->start + (currentChunk->duration * (1 +
5044 currentChunk->repeat));
5046 /* 5.3.9.6.1: negative repeat means repeat till the end of the
5047 * period, or the next update of the MPD (which I think is
5048 * implicit, as this will all get deleted/recreated), or the
5049 * start of the next segment, if any. */
5050 stream_period = gst_mpdparser_get_stream_period (client);
5051 *ts = stream_period->start + stream_period->duration;
5059 gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
5060 guint stream_idx, GstClockTime * ts)
5062 GstActiveStream *stream;
5063 GstMediaSegment *currentChunk;
5065 GST_DEBUG ("Stream index: %i", stream_idx);
5066 stream = g_list_nth_data (client->active_streams, stream_idx);
5067 g_return_val_if_fail (stream != NULL, 0);
5069 if (stream->segments) {
5070 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5071 stream->segment_index, stream->segments->len);
5072 if (stream->segment_index >= stream->segments->len)
5074 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5077 currentChunk->start +
5078 (currentChunk->duration * stream->segment_repeat_index);
5080 GstClockTime duration =
5081 gst_mpd_client_get_segment_duration (client, stream, NULL);
5082 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5084 g_return_val_if_fail (stream->cur_seg_template->
5085 MultSegBaseType->SegmentTimeline == NULL, FALSE);
5086 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5087 && stream->segment_index >= segments_count)) {
5090 *ts = stream->segment_index * duration;
5097 gst_mpd_parser_get_stream_presentation_offset (GstMpdClient * client,
5100 GstActiveStream *stream = NULL;
5102 g_return_val_if_fail (client != NULL, 0);
5103 g_return_val_if_fail (client->active_streams != NULL, 0);
5104 stream = g_list_nth_data (client->active_streams, stream_idx);
5105 g_return_val_if_fail (stream != NULL, 0);
5107 return stream->presentationTimeOffset;
5111 gst_mpd_parser_get_period_start_time (GstMpdClient * client)
5113 GstStreamPeriod *stream_period = NULL;
5115 g_return_val_if_fail (client != NULL, 0);
5116 stream_period = gst_mpdparser_get_stream_period (client);
5117 g_return_val_if_fail (stream_period != NULL, 0);
5119 return stream_period->start;
5123 * gst_mpd_client_get_utc_timing_sources:
5124 * @client: #GstMpdClient to check for UTCTiming elements
5125 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
5127 * @selected_method: (nullable): The selected method
5128 * Returns: (transfer none): A NULL terminated array of URLs of servers
5129 * that use @selected_method to provide a realtime clock.
5131 * Searches the UTCTiming elements found in the manifest for an element
5132 * that uses one of the UTC timing methods specified in @selected_method.
5133 * If multiple UTCTiming elements are present that support one of the
5134 * methods specified in @selected_method, the first one is returned.
5139 gst_mpd_client_get_utc_timing_sources (GstMpdClient * client,
5140 guint methods, GstMPDUTCTimingType * selected_method)
5144 g_return_val_if_fail (client != NULL, NULL);
5145 g_return_val_if_fail (client->mpd_node != NULL, NULL);
5146 for (list = g_list_first (client->mpd_node->UTCTiming); list;
5147 list = g_list_next (list)) {
5148 const GstUTCTimingNode *node = (const GstUTCTimingNode *) list->data;
5149 if (node->method & methods) {
5150 if (selected_method) {
5151 *selected_method = node->method;
5160 gst_mpd_client_get_next_fragment (GstMpdClient * client,
5161 guint indexStream, GstMediaFragmentInfo * fragment)
5163 GstActiveStream *stream = NULL;
5164 GstMediaSegment *currentChunk;
5165 gchar *mediaURL = NULL;
5166 gchar *indexURL = NULL;
5167 GstUri *base_url, *frag_url;
5170 g_return_val_if_fail (client != NULL, FALSE);
5171 g_return_val_if_fail (client->active_streams != NULL, FALSE);
5172 stream = g_list_nth_data (client->active_streams, indexStream);
5173 g_return_val_if_fail (stream != NULL, FALSE);
5174 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5176 if (stream->segments) {
5177 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5178 stream->segment_index, stream->segments->len);
5179 if (stream->segment_index >= stream->segments->len)
5182 GstClockTime duration = gst_mpd_client_get_segment_duration (client,
5184 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5186 g_return_val_if_fail (stream->cur_seg_template->
5187 MultSegBaseType->SegmentTimeline == NULL, FALSE);
5188 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5189 && stream->segment_index >= segments_count)) {
5192 fragment->duration = duration;
5195 /* FIXME rework discont checking */
5196 /* fragment->discontinuity = segment_idx != currentChunk.number; */
5197 fragment->range_start = 0;
5198 fragment->range_end = -1;
5199 fragment->index_uri = NULL;
5200 fragment->index_range_start = 0;
5201 fragment->index_range_end = -1;
5203 if (stream->segments) {
5204 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5206 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
5207 if (currentChunk->SegmentURL != NULL) {
5209 g_strdup (gst_mpdparser_get_mediaURL (stream,
5210 currentChunk->SegmentURL));
5211 indexURL = g_strdup (currentChunk->SegmentURL->index);
5212 } else if (stream->cur_seg_template != NULL) {
5214 gst_mpdparser_build_URL_from_template (stream->
5215 cur_seg_template->media, stream->cur_representation->id,
5216 currentChunk->number + stream->segment_repeat_index,
5217 stream->cur_representation->bandwidth,
5218 currentChunk->scale_start +
5219 stream->segment_repeat_index * currentChunk->scale_duration);
5220 if (stream->cur_seg_template->index) {
5222 gst_mpdparser_build_URL_from_template (stream->
5223 cur_seg_template->index, stream->cur_representation->id,
5224 currentChunk->number + stream->segment_repeat_index,
5225 stream->cur_representation->bandwidth,
5226 currentChunk->scale_start +
5227 stream->segment_repeat_index * currentChunk->scale_duration);
5230 GST_DEBUG ("mediaURL = %s", mediaURL);
5231 GST_DEBUG ("indexURL = %s", indexURL);
5233 fragment->timestamp =
5234 currentChunk->start +
5235 stream->segment_repeat_index * currentChunk->duration;
5236 fragment->duration = currentChunk->duration;
5237 if (currentChunk->SegmentURL) {
5238 if (currentChunk->SegmentURL->mediaRange) {
5239 fragment->range_start =
5240 currentChunk->SegmentURL->mediaRange->first_byte_pos;
5241 fragment->range_end =
5242 currentChunk->SegmentURL->mediaRange->last_byte_pos;
5244 if (currentChunk->SegmentURL->indexRange) {
5245 fragment->index_range_start =
5246 currentChunk->SegmentURL->indexRange->first_byte_pos;
5247 fragment->index_range_end =
5248 currentChunk->SegmentURL->indexRange->last_byte_pos;
5252 if (stream->cur_seg_template != NULL) {
5254 gst_mpdparser_build_URL_from_template (stream->
5255 cur_seg_template->media, stream->cur_representation->id,
5256 stream->segment_index +
5257 stream->cur_seg_template->MultSegBaseType->startNumber,
5258 stream->cur_representation->bandwidth,
5259 stream->segment_index * fragment->duration);
5260 if (stream->cur_seg_template->index) {
5262 gst_mpdparser_build_URL_from_template (stream->
5263 cur_seg_template->index, stream->cur_representation->id,
5264 stream->segment_index +
5265 stream->cur_seg_template->MultSegBaseType->startNumber,
5266 stream->cur_representation->bandwidth,
5267 stream->segment_index * fragment->duration);
5273 GST_DEBUG ("mediaURL = %s", mediaURL);
5274 GST_DEBUG ("indexURL = %s", indexURL);
5276 fragment->timestamp = stream->segment_index * fragment->duration;
5279 base_url = gst_uri_from_string (stream->baseURL);
5280 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
5282 if (stream->queryURL) {
5283 frag_url = gst_uri_make_writable (frag_url);
5284 gst_uri_set_query_string (frag_url, stream->queryURL);
5286 fragment->uri = gst_uri_to_string (frag_url);
5287 gst_uri_unref (frag_url);
5289 if (indexURL != NULL) {
5290 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
5292 gst_uri_set_query_string (frag_url, stream->queryURL);
5293 fragment->index_uri = gst_uri_to_string (frag_url);
5294 gst_uri_unref (frag_url);
5296 } else if (indexURL == NULL && (fragment->index_range_start
5297 || fragment->index_range_end != -1)) {
5298 /* index has no specific URL but has a range, we should only use this if
5299 * the media also has a range, otherwise we are serving some data twice
5300 * (in the media fragment and again in the index) */
5301 if (!(fragment->range_start || fragment->range_end != -1)) {
5302 GST_WARNING ("Ignoring index ranges because there isn't a media range "
5303 "and URIs would be the same");
5304 /* removing index information */
5305 fragment->index_range_start = 0;
5306 fragment->index_range_end = -1;
5310 gst_uri_unref (base_url);
5312 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
5318 gst_mpd_client_has_next_segment (GstMpdClient * client,
5319 GstActiveStream * stream, gboolean forward)
5322 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5324 if (segments_count > 0 && stream->segments
5325 && stream->segment_index + 1 == segments_count) {
5326 GstMediaSegment *segment;
5328 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5329 if (segment->repeat >= 0
5330 && stream->segment_repeat_index >= segment->repeat)
5332 } else if (segments_count > 0
5333 && stream->segment_index + 1 >= segments_count) {
5337 if (stream->segment_index < 0)
5345 gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream,
5348 GstMediaSegment *segment;
5349 GstFlowReturn ret = GST_FLOW_OK;
5350 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5352 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
5353 segments_count, stream->segment_repeat_index);
5355 /* handle special cases first */
5357 if (segments_count > 0 && stream->segment_index >= segments_count) {
5362 if (stream->segments == NULL) {
5363 if (stream->segment_index < 0) {
5364 stream->segment_index = 0;
5366 stream->segment_index++;
5367 if (segments_count > 0 && stream->segment_index >= segments_count) {
5374 /* special case for when playback direction is reverted right at *
5375 * the end of the segment list */
5376 if (stream->segment_index < 0) {
5377 stream->segment_index = 0;
5381 if (stream->segments == NULL)
5382 stream->segment_index--;
5383 if (stream->segment_index < 0) {
5384 stream->segment_index = -1;
5388 if (stream->segments == NULL)
5391 /* special case for when playback direction is reverted right at *
5392 * the end of the segment list */
5393 if (stream->segment_index >= segments_count) {
5394 stream->segment_index = segments_count - 1;
5395 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5396 if (segment->repeat >= 0) {
5397 stream->segment_repeat_index = segment->repeat;
5399 GstClockTime start = segment->start;
5401 gst_mpdparser_get_segment_end_time (client, stream->segments,
5403 stream->segment_index);
5404 stream->segment_repeat_index =
5405 (guint) (end - start) / segment->duration;
5411 /* for the normal cases we can get the segment safely here */
5412 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5414 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
5415 stream->segment_repeat_index = 0;
5416 stream->segment_index++;
5417 if (segments_count > 0 && stream->segment_index >= segments_count) {
5422 stream->segment_repeat_index++;
5425 if (stream->segment_repeat_index == 0) {
5426 stream->segment_index--;
5427 if (stream->segment_index < 0) {
5432 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5433 /* negative repeats only seem to make sense at the end of a list,
5434 * so this one will probably not be. Needs some sanity checking
5435 * when loading the XML data. */
5436 if (segment->repeat >= 0) {
5437 stream->segment_repeat_index = segment->repeat;
5439 GstClockTime start = segment->start;
5441 gst_mpdparser_get_segment_end_time (client, stream->segments,
5443 stream->segment_index);
5444 stream->segment_repeat_index =
5445 (guint) (end - start) / segment->duration;
5448 stream->segment_repeat_index--;
5453 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
5454 stream->segment_index, segments_count,
5455 stream->segment_repeat_index, gst_flow_get_name (ret));
5460 gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
5461 guint stream_idx, gint64 * range_start, gint64 * range_end)
5463 GstActiveStream *stream;
5464 GstStreamPeriod *stream_period;
5466 stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5467 g_return_val_if_fail (stream != NULL, FALSE);
5468 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5469 stream_period = gst_mpdparser_get_stream_period (client);
5470 g_return_val_if_fail (stream_period != NULL, FALSE);
5471 g_return_val_if_fail (stream_period->period != NULL, FALSE);
5476 GST_DEBUG ("Looking for current representation header");
5478 if (stream->cur_segment_base) {
5479 if (stream->cur_segment_base->Initialization) {
5481 g_strdup (gst_mpdparser_get_initializationURL (stream,
5482 stream->cur_segment_base->Initialization));
5483 if (stream->cur_segment_base->Initialization->range) {
5485 stream->cur_segment_base->Initialization->range->first_byte_pos;
5487 stream->cur_segment_base->Initialization->range->last_byte_pos;
5489 } else if (stream->cur_segment_base->indexRange) {
5491 g_strdup (gst_mpdparser_get_initializationURL (stream,
5492 stream->cur_segment_base->Initialization));
5494 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
5496 } else if (stream->cur_seg_template
5497 && stream->cur_seg_template->initialization) {
5499 gst_mpdparser_build_URL_from_template (stream->
5500 cur_seg_template->initialization, stream->cur_representation->id, 0,
5501 stream->cur_representation->bandwidth, 0);
5504 return *uri == NULL ? FALSE : TRUE;
5508 gst_mpd_client_get_next_header_index (GstMpdClient * client, gchar ** uri,
5509 guint stream_idx, gint64 * range_start, gint64 * range_end)
5511 GstActiveStream *stream;
5512 GstStreamPeriod *stream_period;
5514 stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5515 g_return_val_if_fail (stream != NULL, FALSE);
5516 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5517 stream_period = gst_mpdparser_get_stream_period (client);
5518 g_return_val_if_fail (stream_period != NULL, FALSE);
5519 g_return_val_if_fail (stream_period->period != NULL, FALSE);
5524 GST_DEBUG ("Looking for current representation index");
5526 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
5528 g_strdup (gst_mpdparser_get_initializationURL (stream,
5529 stream->cur_segment_base->RepresentationIndex));
5530 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
5531 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
5532 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
5534 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
5535 stream->cur_representation->id, 0,
5536 stream->cur_representation->bandwidth, 0);
5539 return *uri == NULL ? FALSE : TRUE;
5543 gst_mpd_client_get_next_fragment_duration (GstMpdClient * client,
5544 GstActiveStream * stream)
5546 GstMediaSegment *media_segment = NULL;
5549 g_return_val_if_fail (stream != NULL, 0);
5551 seg_idx = stream->segment_index;
5553 if (stream->segments) {
5554 if (seg_idx < stream->segments->len && seg_idx >= 0)
5555 media_segment = g_ptr_array_index (stream->segments, seg_idx);
5557 return media_segment == NULL ? 0 : media_segment->duration;
5559 GstClockTime duration =
5560 gst_mpd_client_get_segment_duration (client, stream, NULL);
5561 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5563 g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5564 SegmentTimeline == NULL, 0);
5566 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5567 && seg_idx >= segments_count)) {
5575 gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
5577 GstClockTime duration;
5579 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
5581 if (client->mpd_node->mediaPresentationDuration != -1) {
5582 duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
5584 /* We can only get the duration for on-demand streams */
5585 duration = GST_CLOCK_TIME_NONE;
5592 gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
5594 GstStreamPeriod *next_stream_period;
5595 gboolean ret = FALSE;
5599 g_return_val_if_fail (client != NULL, FALSE);
5600 g_return_val_if_fail (client->periods != NULL, FALSE);
5601 g_return_val_if_fail (period_id != NULL, FALSE);
5603 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
5607 for (period_idx = 0, iter = client->periods; iter;
5608 period_idx++, iter = g_list_next (iter)) {
5609 next_stream_period = iter->data;
5611 if (next_stream_period->period->id
5612 && strcmp (next_stream_period->period->id, period_id) == 0) {
5614 client->period_idx = period_idx;
5623 gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
5625 GstStreamPeriod *next_stream_period;
5626 gboolean ret = FALSE;
5628 g_return_val_if_fail (client != NULL, FALSE);
5629 g_return_val_if_fail (client->periods != NULL, FALSE);
5631 if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
5634 next_stream_period = g_list_nth_data (client->periods, period_idx);
5635 if (next_stream_period != NULL) {
5636 client->period_idx = period_idx;
5644 gst_mpd_client_get_period_index (GstMpdClient * client)
5648 g_return_val_if_fail (client != NULL, 0);
5649 period_idx = client->period_idx;
5655 gst_mpd_client_get_period_id (GstMpdClient * client)
5657 GstStreamPeriod *period;
5658 gchar *period_id = NULL;
5660 g_return_val_if_fail (client != NULL, 0);
5661 period = g_list_nth_data (client->periods, client->period_idx);
5662 if (period && period->period)
5663 period_id = period->period->id;
5669 gst_mpd_client_has_previous_period (GstMpdClient * client)
5671 GList *next_stream_period;
5672 g_return_val_if_fail (client != NULL, FALSE);
5673 g_return_val_if_fail (client->periods != NULL, FALSE);
5675 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5676 client->period_idx - 1, NULL))
5679 next_stream_period =
5680 g_list_nth_data (client->periods, client->period_idx - 1);
5682 return next_stream_period != NULL;
5686 gst_mpd_client_has_next_period (GstMpdClient * client)
5688 GList *next_stream_period;
5689 g_return_val_if_fail (client != NULL, FALSE);
5690 g_return_val_if_fail (client->periods != NULL, FALSE);
5692 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5693 client->period_idx + 1, NULL))
5696 next_stream_period =
5697 g_list_nth_data (client->periods, client->period_idx + 1);
5698 return next_stream_period != NULL;
5702 gst_mpd_client_seek_to_first_segment (GstMpdClient * client)
5706 g_return_if_fail (client != NULL);
5707 g_return_if_fail (client->active_streams != NULL);
5709 for (list = g_list_first (client->active_streams); list;
5710 list = g_list_next (list)) {
5711 GstActiveStream *stream = (GstActiveStream *) list->data;
5713 stream->segment_index = 0;
5714 stream->segment_repeat_index = 0;
5720 gst_mpd_client_get_segments_counts (GstMpdClient * client,
5721 GstActiveStream * stream)
5723 GstStreamPeriod *stream_period;
5725 g_return_val_if_fail (stream != NULL, 0);
5727 if (stream->segments)
5728 return stream->segments->len;
5729 g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5730 SegmentTimeline == NULL, 0);
5732 stream_period = gst_mpdparser_get_stream_period (client);
5733 if (stream_period->duration != -1)
5734 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
5735 gst_mpd_client_get_segment_duration (client, stream, NULL));
5741 gst_mpd_client_is_live (GstMpdClient * client)
5743 g_return_val_if_fail (client != NULL, FALSE);
5744 g_return_val_if_fail (client->mpd_node != NULL, FALSE);
5746 return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
5750 gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
5752 g_return_val_if_fail (client != NULL, 0);
5754 return g_list_length (client->active_streams);
5758 gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
5760 GstStreamPeriod *stream_period;
5762 stream_period = gst_mpdparser_get_stream_period (client);
5763 g_return_val_if_fail (stream_period != NULL, 0);
5764 g_return_val_if_fail (stream_period->period != NULL, 0);
5766 return g_list_length (stream_period->period->AdaptationSets);
5770 gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
5773 g_return_val_if_fail (client != NULL, NULL);
5774 g_return_val_if_fail (client->active_streams != NULL, NULL);
5776 return g_list_nth_data (client->active_streams, stream_idx);
5780 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
5782 const gchar *mimeType;
5783 const gchar *adapt_set_codecs;
5784 const gchar *rep_codecs;
5786 mimeType = stream->cur_representation->RepresentationBase->mimeType;
5788 mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5790 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
5791 g_strcmp0 (mimeType, "text/vtt") == 0)
5794 adapt_set_codecs = stream->cur_adapt_set->RepresentationBase->codecs;
5795 rep_codecs = stream->cur_representation->RepresentationBase->codecs;
5797 return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
5798 || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
5801 static const gchar *
5802 gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
5804 if (mimeType == NULL)
5806 if (strcmp (mimeType, "video/mp2t") == 0) {
5807 return "video/mpegts, systemstream=(bool) true";
5808 } else if (strcmp (mimeType, "video/mp4") == 0) {
5809 return "video/quicktime";
5810 } else if (strcmp (mimeType, "audio/mp4") == 0) {
5811 return "audio/x-m4a";
5812 } else if (strcmp (mimeType, "text/vtt") == 0) {
5813 return "application/x-subtitle-vtt";
5819 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
5821 const gchar *mimeType, *caps_string;
5822 GstCaps *ret = NULL;
5824 if (stream == NULL || stream->cur_adapt_set == NULL
5825 || stream->cur_representation == NULL)
5828 mimeType = stream->cur_representation->RepresentationBase->mimeType;
5829 if (mimeType == NULL) {
5830 mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5833 caps_string = gst_mpdparser_mimetype_to_caps (mimeType);
5835 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
5836 && gst_mpd_client_active_stream_contains_subtitles (stream))
5837 caps_string = "video/quicktime";
5840 ret = gst_caps_from_string (caps_string);
5846 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
5848 if (stream == NULL || stream->cur_adapt_set == NULL)
5851 return stream->cur_adapt_set->bitstreamSwitching;
5855 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
5859 if (stream == NULL || stream->cur_adapt_set == NULL
5860 || stream->cur_representation == NULL)
5863 width = stream->cur_representation->RepresentationBase->width;
5865 width = stream->cur_adapt_set->RepresentationBase->width;
5872 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
5876 if (stream == NULL || stream->cur_adapt_set == NULL
5877 || stream->cur_representation == NULL)
5880 height = stream->cur_representation->RepresentationBase->height;
5882 height = stream->cur_adapt_set->RepresentationBase->height;
5889 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
5890 gint * fps_num, gint * fps_den)
5895 if (stream->cur_adapt_set &&
5896 stream->cur_adapt_set->RepresentationBase->frameRate != NULL) {
5897 *fps_num = stream->cur_adapt_set->RepresentationBase->frameRate->num;
5898 *fps_den = stream->cur_adapt_set->RepresentationBase->frameRate->den;
5902 if (stream->cur_adapt_set &&
5903 stream->cur_adapt_set->RepresentationBase->maxFrameRate != NULL) {
5904 *fps_num = stream->cur_adapt_set->RepresentationBase->maxFrameRate->num;
5905 *fps_den = stream->cur_adapt_set->RepresentationBase->maxFrameRate->den;
5909 if (stream->cur_representation &&
5910 stream->cur_representation->RepresentationBase->frameRate != NULL) {
5911 *fps_num = stream->cur_representation->RepresentationBase->frameRate->num;
5912 *fps_den = stream->cur_representation->RepresentationBase->frameRate->den;
5916 if (stream->cur_representation &&
5917 stream->cur_representation->RepresentationBase->maxFrameRate != NULL) {
5919 stream->cur_representation->RepresentationBase->maxFrameRate->num;
5921 stream->cur_representation->RepresentationBase->maxFrameRate->den;
5929 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
5933 if (stream == NULL || stream->cur_adapt_set == NULL
5934 || stream->cur_representation == NULL)
5937 rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
5939 rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
5942 return rate ? atoi (rate) : 0;
5946 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
5948 if (stream == NULL || stream->cur_adapt_set == NULL
5949 || stream->cur_representation == NULL)
5951 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
5956 gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
5959 GstStreamPeriod *stream_period;
5960 GstAdaptationSetNode *adapt_set;
5961 GList *adaptation_sets, *list;
5962 const gchar *this_mimeType = "audio";
5963 gchar *mimeType = NULL;
5964 guint nb_adaptation_set = 0;
5966 stream_period = gst_mpdparser_get_stream_period (client);
5967 g_return_val_if_fail (stream_period != NULL, 0);
5968 g_return_val_if_fail (stream_period->period != NULL, 0);
5971 gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
5972 for (list = adaptation_sets; list; list = g_list_next (list)) {
5973 adapt_set = (GstAdaptationSetNode *) list->data;
5974 if (adapt_set && adapt_set->lang) {
5975 gchar *this_lang = adapt_set->lang;
5976 GstRepresentationNode *rep;
5978 gst_mpdparser_get_lowest_representation (adapt_set->Representations);
5980 if (rep->RepresentationBase)
5981 mimeType = rep->RepresentationBase->mimeType;
5982 if (!mimeType && adapt_set->RepresentationBase) {
5983 mimeType = adapt_set->RepresentationBase->mimeType;
5986 if (strncmp_ext (mimeType, this_mimeType) == 0) {
5987 nb_adaptation_set++;
5988 *lang = g_list_append (*lang, this_lang);
5993 return nb_adaptation_set;
5998 gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
5999 GstActiveStream * stream)
6001 GstDateTime *availability_start_time, *rv;
6003 GstMediaSegment *segment;
6004 GstClockTime segmentEndTime;
6005 const GstStreamPeriod *stream_period;
6006 GstClockTime period_start = 0;
6008 g_return_val_if_fail (client != NULL, NULL);
6009 g_return_val_if_fail (stream != NULL, NULL);
6011 stream_period = gst_mpdparser_get_stream_period (client);
6012 if (stream_period && stream_period->period) {
6013 period_start = stream_period->start;
6016 seg_idx = stream->segment_index;
6018 if (stream->segments) {
6019 segment = g_ptr_array_index (stream->segments, seg_idx);
6021 if (segment->repeat >= 0) {
6022 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
6024 } else if (seg_idx < stream->segments->len - 1) {
6025 const GstMediaSegment *next_segment =
6026 g_ptr_array_index (stream->segments, seg_idx + 1);
6027 segmentEndTime = next_segment->start;
6029 g_return_val_if_fail (stream_period != NULL, NULL);
6030 segmentEndTime = period_start + stream_period->duration;
6033 GstClockTime seg_duration;
6034 seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
6035 if (seg_duration == 0)
6037 segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
6040 availability_start_time = gst_mpd_client_get_availability_start_time (client);
6041 if (availability_start_time == NULL) {
6042 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
6046 rv = gst_mpd_client_add_time_difference (availability_start_time,
6047 segmentEndTime / GST_USECOND);
6048 gst_date_time_unref (availability_start_time);
6050 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
6058 gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
6061 GTimeSpan ts_microseconds;
6063 gboolean ret = TRUE;
6066 g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
6067 g_return_val_if_fail (client->mpd_node->availabilityStartTime != NULL, FALSE);
6070 gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
6072 ts_microseconds = g_date_time_difference (time, start);
6073 g_date_time_unref (start);
6075 /* Clamp to availability start time, otherwise calculations wrap around */
6076 if (ts_microseconds < 0)
6077 ts_microseconds = 0;
6079 ts = ts_microseconds * GST_USECOND;
6080 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6082 ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
6089 gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
6091 g_free (fragment->uri);
6092 g_free (fragment->index_uri);
6096 gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient * client)
6098 return client->profile_isoff_ondemand;
6102 * gst_mpd_client_parse_default_presentation_delay:
6103 * @client: #GstMpdClient that has a parsed manifest
6104 * @default_presentation_delay: A string that specifies a time period
6105 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
6107 * Returns: the parsed string in milliseconds
6112 gst_mpd_client_parse_default_presentation_delay (GstMpdClient * client,
6113 const gchar * default_presentation_delay)
6116 char *endptr = NULL;
6118 g_return_val_if_fail (client != NULL, 0);
6119 g_return_val_if_fail (default_presentation_delay != NULL, 0);
6120 value = strtol (default_presentation_delay, &endptr, 10);
6121 if (endptr == default_presentation_delay || value == 0) {
6124 while (*endptr == ' ')
6126 if (*endptr == 's' || *endptr == 'S') {
6127 value *= 1000; /* convert to ms */
6128 } else if (*endptr == 'f' || *endptr == 'F') {
6129 gint64 segment_duration;
6130 g_assert (client->mpd_node != NULL);
6131 segment_duration = client->mpd_node->maxSegmentDuration;
6132 value *= segment_duration;
6133 } else if (*endptr != 'm' && *endptr != 'M') {
6134 GST_ERROR ("Unable to parse default presentation delay: %s",
6135 default_presentation_delay);
6142 gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client)
6144 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
6147 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
6148 g_return_val_if_fail (client->mpd_node != NULL, GST_CLOCK_TIME_NONE);
6150 if (client->mpd_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
6151 return client->mpd_node->maxSegmentDuration * GST_MSECOND;
6154 /* According to the DASH specification, if maxSegmentDuration is not present:
6155 "If not present, then the maximum Segment duration shall be the maximum
6156 duration of any Segment documented in this MPD"
6158 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6159 dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
6160 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {