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
34 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
35 #define DEFAULT_ADAPTIVE_VARIANT -1
38 /* Property parsing */
39 static gboolean gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
40 const gchar * property_name, gchar ** property_value,
41 gboolean (*validator) (const char *));
42 static gboolean gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
43 const gchar * property_name, gchar ** property_value);
44 static gboolean gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
45 const gchar * property_name, gchar ** property_value);
46 static gboolean gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
47 const gchar * ns_name, const gchar * property_name,
48 gchar ** property_value);
49 static gboolean gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
50 const gchar * property_name, gchar *** property_value);
51 static gboolean gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
52 const gchar * property_name, gint default_val, gint * property_value);
53 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
54 const gchar * property_name, guint default_val, guint * property_value);
55 static gboolean gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode *
56 a_node, const gchar * property_name, guint64 default_val,
57 guint64 * property_value);
58 static gboolean gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
59 const gchar * property_name, guint ** property_value, guint * value_size);
60 static gboolean gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
61 const gchar * property_name, gdouble * property_value);
62 static gboolean gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
63 const gchar * property_name, gboolean default_val,
64 gboolean * property_value);
65 static gboolean gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
66 const gchar * property_name, GstMPDFileType * property_value);
67 static gboolean gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
68 const gchar * property_name, GstSAPType * property_value);
69 static gboolean gst_mpdparser_get_xml_prop_range (xmlNode * a_node,
70 const gchar * property_name, GstRange ** property_value);
71 static gboolean gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
72 const gchar * property_name, GstRatio ** property_value);
73 static gboolean gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
74 const gchar * property_name, GstFrameRate ** property_value);
75 static gboolean gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
76 const gchar * property_name, GstConditionalUintType ** property_value);
77 static gboolean gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
78 const gchar * property_name, GstDateTime ** property_value);
79 static gboolean gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
80 const gchar * property_name, guint64 default_value,
81 guint64 * property_value);
82 static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
84 static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
85 const gchar * prefix);
86 static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
89 /* XML node parsing */
90 static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
91 static void gst_mpdparser_parse_descriptor_type_node (GList ** list,
93 static void gst_mpdparser_parse_content_component_node (GList ** list,
95 static void gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node);
96 static void gst_mpdparser_parse_subrepresentation_node (GList ** list,
98 static void gst_mpdparser_parse_segment_url_node (GList ** list,
100 static void gst_mpdparser_parse_url_type_node (GstURLType ** pointer,
102 static void gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType **
103 pointer, xmlNode * a_node, GstSegmentBaseType * parent);
104 static void gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node);
105 static void gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode **
106 pointer, xmlNode * a_node);
108 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
109 xmlNode * a_node, GstMultSegmentBaseType * parent);
110 static gboolean gst_mpdparser_parse_segment_list_node (GstSegmentListNode **
111 pointer, xmlNode * a_node, GstSegmentListNode * parent);
113 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
114 pointer, xmlNode * a_node);
115 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
116 static gboolean gst_mpdparser_parse_representation_node (GList ** list,
117 GList ** info, xmlNode * a_node, GstAdaptationSetNode * parent,
118 GstPeriodNode * period_node);
120 static gboolean gst_mpdparser_parse_representation_node (GList ** list,
121 xmlNode * a_node, GstAdaptationSetNode * parent,
122 GstPeriodNode * period_node);
124 static gboolean gst_mpdparser_parse_adaptation_set_node (GList ** list,
125 xmlNode * a_node, GstPeriodNode * parent);
126 static void gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node);
128 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
129 xmlNode * a_node, GstSegmentTemplateNode * parent);
130 static gboolean gst_mpdparser_parse_period_node (GList ** list,
132 static void gst_mpdparser_parse_program_info_node (GList ** list,
134 static void gst_mpdparser_parse_metrics_range_node (GList ** list,
136 static void gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node);
137 static gboolean gst_mpdparser_parse_root_node (GstMPDNode ** pointer,
139 static void gst_mpdparser_parse_utctiming_node (GList ** list,
142 /* Helper functions */
143 static guint convert_to_millisecs (guint decimals, gint pos);
144 static int strncmp_ext (const char *s1, const char *s2);
145 static GstStreamPeriod *gst_mpdparser_get_stream_period (GstMpdClient * client);
146 static GstSNode *gst_mpdparser_clone_s_node (GstSNode * pointer);
147 static GstSegmentTimelineNode
148 * gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer);
149 static GstRange *gst_mpdparser_clone_range (GstRange * range);
150 static GstURLType *gst_mpdparser_clone_URL (GstURLType * url);
151 static gchar *gst_mpdparser_parse_baseURL (GstMpdClient * client,
152 GstActiveStream * stream, gchar ** query);
153 static GstSegmentURLNode *gst_mpdparser_clone_segment_url (GstSegmentURLNode *
155 static gchar *gst_mpdparser_get_mediaURL (GstActiveStream * stream,
156 GstSegmentURLNode * segmentURL);
157 static const gchar *gst_mpdparser_get_initializationURL (GstActiveStream *
158 stream, GstURLType * InitializationURL);
159 static gchar *gst_mpdparser_build_URL_from_template (const gchar * url_template,
160 const gchar * id, guint number, guint bandwidth, guint64 time);
161 static gboolean gst_mpd_client_add_media_segment (GstActiveStream * stream,
162 GstSegmentURLNode * url_node, guint number, gint repeat,
163 guint64 scale_start, guint64 scale_duration, GstClockTime start,
164 GstClockTime duration);
165 static const gchar *gst_mpdparser_mimetype_to_caps (const gchar * mimeType);
166 static GstClockTime gst_mpd_client_get_segment_duration (GstMpdClient * client,
167 GstActiveStream * stream, guint64 * scale_duration);
168 static GstDateTime *gst_mpd_client_get_availability_start_time (GstMpdClient *
172 static GstRepresentationNode *gst_mpdparser_get_lowest_representation (GList *
175 static GstRepresentationNode *gst_mpdparser_get_highest_representation (GList *
177 static GstRepresentationNode
178 * gst_mpdparser_get_representation_with_max_bandwidth (GList *
179 Representations, gint max_bandwidth);
181 static GstSegmentBaseType *gst_mpdparser_get_segment_base (GstPeriodNode *
182 Period, GstAdaptationSetNode * AdaptationSet,
183 GstRepresentationNode * Representation);
184 static GstSegmentListNode *gst_mpdparser_get_segment_list (GstMpdClient *
185 client, GstPeriodNode * Period, GstAdaptationSetNode * AdaptationSet,
186 GstRepresentationNode * Representation);
189 static guint gst_mpd_client_get_segments_counts (GstMpdClient * client,
190 GstActiveStream * stream);
192 /* Memory management */
193 static GstSegmentTimelineNode *gst_mpdparser_segment_timeline_node_new (void);
194 static void gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node);
195 static void gst_mpdparser_free_prog_info_node (GstProgramInformationNode *
197 static void gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node);
198 static void gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode *
200 static void gst_mpdparser_free_period_node (GstPeriodNode * period_node);
201 static void gst_mpdparser_free_subset_node (GstSubsetNode * subset_node);
202 static void gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
203 segment_template_node);
205 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
206 representation_base);
207 static void gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
208 adaptation_set_node);
209 static void gst_mpdparser_free_representation_node (GstRepresentationNode *
210 representation_node);
211 static void gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode
213 static void gst_mpdparser_free_s_node (GstSNode * s_node);
214 static void gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode *
216 static void gst_mpdparser_free_url_type_node (GstURLType * url_type_node);
217 static void gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType *
219 static void gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
221 static void gst_mpdparser_free_segment_list_node (GstSegmentListNode *
223 static void gst_mpdparser_free_segment_url_node (GstSegmentURLNode *
225 static void gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node);
226 static void gst_mpdparser_free_descriptor_type_node (GstDescriptorType *
228 static void gst_mpdparser_free_content_component_node (GstContentComponentNode *
229 content_component_node);
230 static void gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type);
231 static void gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period);
232 static void gst_mpdparser_free_media_segment (GstMediaSegment * media_segment);
233 static void gst_mpdparser_free_active_stream (GstActiveStream * active_stream);
235 static GstUri *combine_urls (GstUri * base, GList * list, gchar ** query,
238 static GList *gst_mpd_client_fetch_external_period (GstMpdClient * client,
239 GstPeriodNode * period_node);
240 static GList *gst_mpd_client_fetch_external_adaptation_set (GstMpdClient *
241 client, GstPeriodNode * period, GstAdaptationSetNode * adapt_set);
243 struct GstMpdParserUtcTimingMethod
246 GstMPDUTCTimingType method;
249 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
250 struct GstVideoVariantInfo
258 static const struct GstMpdParserUtcTimingMethod
259 gst_mpdparser_utc_timing_methods[] = {
260 {"urn:mpeg:dash:utc:ntp:2014", GST_MPD_UTCTIMING_TYPE_NTP},
261 {"urn:mpeg:dash:utc:sntp:2014", GST_MPD_UTCTIMING_TYPE_SNTP},
262 {"urn:mpeg:dash:utc:http-head:2014", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
263 {"urn:mpeg:dash:utc:http-xsdate:2014", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
264 {"urn:mpeg:dash:utc:http-iso:2014", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
265 {"urn:mpeg:dash:utc:http-ntp:2014", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
266 {"urn:mpeg:dash:utc:direct:2014", GST_MPD_UTCTIMING_TYPE_DIRECT},
268 * Early working drafts used the :2012 namespace and this namespace is
269 * used by some DASH packagers. To work-around these packagers, we also
270 * accept the early draft scheme names.
272 {"urn:mpeg:dash:utc:ntp:2012", GST_MPD_UTCTIMING_TYPE_NTP},
273 {"urn:mpeg:dash:utc:sntp:2012", GST_MPD_UTCTIMING_TYPE_SNTP},
274 {"urn:mpeg:dash:utc:http-head:2012", GST_MPD_UTCTIMING_TYPE_HTTP_HEAD},
275 {"urn:mpeg:dash:utc:http-xsdate:2012", GST_MPD_UTCTIMING_TYPE_HTTP_XSDATE},
276 {"urn:mpeg:dash:utc:http-iso:2012", GST_MPD_UTCTIMING_TYPE_HTTP_ISO},
277 {"urn:mpeg:dash:utc:http-ntp:2012", GST_MPD_UTCTIMING_TYPE_HTTP_NTP},
278 {"urn:mpeg:dash:utc:direct:2012", GST_MPD_UTCTIMING_TYPE_DIRECT},
282 /* functions to parse node namespaces, content and properties */
284 gst_mpdparser_get_xml_prop_validated_string (xmlNode * a_node,
285 const gchar * property_name, gchar ** property_value,
286 gboolean (*validate) (const char *))
288 xmlChar *prop_string;
289 gboolean exists = FALSE;
291 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
293 if (validate && !(*validate) ((const char *) prop_string)) {
294 GST_WARNING ("Validation failure: %s", prop_string);
295 xmlFree (prop_string);
298 *property_value = (gchar *) prop_string;
300 GST_LOG (" - %s: %s", property_name, prop_string);
307 gst_mpdparser_get_xml_ns_prop_string (xmlNode * a_node,
308 const gchar * ns_name, const gchar * property_name, gchar ** property_value)
310 xmlChar *prop_string;
311 gboolean exists = FALSE;
314 xmlGetNsProp (a_node, (const xmlChar *) property_name,
315 (const xmlChar *) ns_name);
317 *property_value = (gchar *) prop_string;
319 GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
326 gst_mpdparser_get_xml_prop_string (xmlNode * a_node,
327 const gchar * property_name, gchar ** property_value)
329 return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
330 property_value, NULL);
334 gst_mpdparser_get_xml_prop_string_stripped (xmlNode * a_node,
335 const gchar * property_name, gchar ** property_value)
339 gst_mpdparser_get_xml_prop_string (a_node, property_name, property_value);
341 *property_value = g_strstrip (*property_value);
346 gst_mpdparser_validate_no_whitespace (const char *s)
348 return !strpbrk (s, "\r\n\t ");
352 gst_mpdparser_get_xml_prop_string_no_whitespace (xmlNode * a_node,
353 const gchar * property_name, gchar ** property_value)
355 return gst_mpdparser_get_xml_prop_validated_string (a_node, property_name,
356 property_value, gst_mpdparser_validate_no_whitespace);
360 gst_mpdparser_get_xml_prop_string_vector_type (xmlNode * a_node,
361 const gchar * property_name, gchar *** property_value)
363 xmlChar *prop_string;
364 gchar **prop_string_vector = NULL;
366 gboolean exists = FALSE;
368 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
370 prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
371 if (prop_string_vector) {
373 *property_value = prop_string_vector;
374 GST_LOG (" - %s:", property_name);
375 while (prop_string_vector[i]) {
376 GST_LOG (" %s", prop_string_vector[i]);
380 GST_WARNING ("Scan of string vector property failed!");
382 xmlFree (prop_string);
389 gst_mpdparser_get_xml_prop_signed_integer (xmlNode * a_node,
390 const gchar * property_name, gint default_val, gint * property_value)
392 xmlChar *prop_string;
393 gboolean exists = FALSE;
395 *property_value = default_val;
396 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
398 if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
400 GST_LOG (" - %s: %d", property_name, *property_value);
403 ("failed to parse signed integer property %s from xml string %s",
404 property_name, prop_string);
406 xmlFree (prop_string);
413 gst_mpdparser_get_xml_prop_unsigned_integer (xmlNode * a_node,
414 const gchar * property_name, guint default_val, guint * property_value)
416 xmlChar *prop_string;
417 gboolean exists = FALSE;
419 *property_value = default_val;
420 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
422 if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
423 strstr ((gchar *) prop_string, "-") == NULL) {
425 GST_LOG (" - %s: %u", property_name, *property_value);
428 ("failed to parse unsigned integer property %s from xml string %s",
429 property_name, prop_string);
430 /* sscanf might have written to *property_value. Restore to default */
431 *property_value = default_val;
433 xmlFree (prop_string);
440 gst_mpdparser_get_xml_prop_unsigned_integer_64 (xmlNode * a_node,
441 const gchar * property_name, guint64 default_val, guint64 * property_value)
443 xmlChar *prop_string;
444 gboolean exists = FALSE;
446 *property_value = default_val;
447 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
449 if (sscanf ((gchar *) prop_string, "%" G_GUINT64_FORMAT,
450 property_value) == 1 &&
451 strstr ((gchar *) prop_string, "-") == NULL) {
453 GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
456 ("failed to parse unsigned integer property %s from xml string %s",
457 property_name, prop_string);
458 /* sscanf might have written to *property_value. Restore to default */
459 *property_value = default_val;
461 xmlFree (prop_string);
468 gst_mpdparser_get_xml_prop_uint_vector_type (xmlNode * a_node,
469 const gchar * property_name, guint ** property_value, guint * value_size)
471 xmlChar *prop_string;
473 guint *prop_uint_vector = NULL, i;
474 gboolean exists = FALSE;
476 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
478 str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
480 *value_size = g_strv_length (str_vector);
481 prop_uint_vector = g_malloc (*value_size * sizeof (guint));
482 if (prop_uint_vector) {
484 GST_LOG (" - %s:", property_name);
485 for (i = 0; i < *value_size; i++) {
486 if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
487 && strstr (str_vector[i], "-") == NULL) {
488 GST_LOG (" %u", prop_uint_vector[i]);
491 ("failed to parse uint vector type property %s from xml string %s",
492 property_name, str_vector[i]);
493 /* there is no special value to put in prop_uint_vector[i] to
494 * signal it is invalid, so we just clean everything and return
497 g_free (prop_uint_vector);
498 prop_uint_vector = NULL;
503 *property_value = prop_uint_vector;
505 GST_WARNING ("Array allocation failed!");
508 GST_WARNING ("Scan of uint vector property failed!");
510 xmlFree (prop_string);
511 g_strfreev (str_vector);
518 gst_mpdparser_get_xml_prop_double (xmlNode * a_node,
519 const gchar * property_name, gdouble * property_value)
521 xmlChar *prop_string;
522 gboolean exists = FALSE;
524 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
526 if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
528 GST_LOG (" - %s: %lf", property_name, *property_value);
530 GST_WARNING ("failed to parse double property %s from xml string %s",
531 property_name, prop_string);
533 xmlFree (prop_string);
540 gst_mpdparser_get_xml_prop_boolean (xmlNode * a_node,
541 const gchar * property_name, gboolean default_val,
542 gboolean * property_value)
544 xmlChar *prop_string;
545 gboolean exists = FALSE;
547 *property_value = default_val;
548 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
550 if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
552 *property_value = FALSE;
553 GST_LOG (" - %s: false", property_name);
554 } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
556 *property_value = TRUE;
557 GST_LOG (" - %s: true", property_name);
559 GST_WARNING ("failed to parse boolean property %s from xml string %s",
560 property_name, prop_string);
562 xmlFree (prop_string);
569 gst_mpdparser_get_xml_prop_type (xmlNode * a_node,
570 const gchar * property_name, GstMPDFileType * property_value)
572 xmlChar *prop_string;
573 gboolean exists = FALSE;
575 *property_value = GST_MPD_FILE_TYPE_STATIC; /* default */
576 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
578 if (xmlStrcmp (prop_string, (xmlChar *) "OnDemand") == 0
579 || xmlStrcmp (prop_string, (xmlChar *) "static") == 0) {
581 *property_value = GST_MPD_FILE_TYPE_STATIC;
582 GST_LOG (" - %s: static", property_name);
583 } else if (xmlStrcmp (prop_string, (xmlChar *) "Live") == 0
584 || xmlStrcmp (prop_string, (xmlChar *) "dynamic") == 0) {
586 *property_value = GST_MPD_FILE_TYPE_DYNAMIC;
587 GST_LOG (" - %s: dynamic", property_name);
589 GST_WARNING ("failed to parse MPD type property %s from xml string %s",
590 property_name, prop_string);
592 xmlFree (prop_string);
599 gst_mpdparser_get_xml_prop_SAP_type (xmlNode * a_node,
600 const gchar * property_name, GstSAPType * property_value)
602 xmlChar *prop_string;
603 guint prop_SAP_type = 0;
604 gboolean exists = FALSE;
606 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
608 if (sscanf ((gchar *) prop_string, "%u", &prop_SAP_type) == 1
609 && prop_SAP_type <= 6) {
611 *property_value = (GstSAPType) prop_SAP_type;
612 GST_LOG (" - %s: %u", property_name, prop_SAP_type);
615 ("failed to parse unsigned integer property %s from xml string %s",
616 property_name, prop_string);
618 xmlFree (prop_string);
625 gst_mpdparser_get_xml_prop_range (xmlNode * a_node, const gchar * property_name,
626 GstRange ** property_value)
628 xmlChar *prop_string;
629 guint64 first_byte_pos = 0, last_byte_pos = -1;
632 gboolean exists = FALSE;
634 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
636 len = xmlStrlen (prop_string);
637 str = (gchar *) prop_string;
638 GST_TRACE ("range: %s, len %d", str, len);
641 pos = strcspn (str, "-");
643 GST_TRACE ("pos %d >= len %d", pos, len);
646 /* read first_byte_pos */
648 /* replace str[pos] with '\0' to allow sscanf to not be confused by
649 * the minus sign (eg " -1" (observe the space before -) would otherwise
650 * be interpreted as range -1 to 1)
653 if (sscanf (str, "%" G_GUINT64_FORMAT, &first_byte_pos) != 1 ||
654 strstr (str, "-") != NULL) {
655 /* sscanf failed or it found a negative number */
656 /* restore the '-' sign */
660 /* restore the '-' sign */
663 /* read last_byte_pos */
664 if (pos < (len - 1)) {
665 if (sscanf (str + pos + 1, "%" G_GUINT64_FORMAT, &last_byte_pos) != 1 ||
666 strstr (str + pos + 1, "-") != NULL) {
670 /* malloc return data structure */
671 *property_value = g_slice_new0 (GstRange);
673 (*property_value)->first_byte_pos = first_byte_pos;
674 (*property_value)->last_byte_pos = last_byte_pos;
675 xmlFree (prop_string);
676 GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
677 property_name, first_byte_pos, last_byte_pos);
683 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
685 xmlFree (prop_string);
690 gst_mpdparser_get_xml_prop_ratio (xmlNode * a_node,
691 const gchar * property_name, GstRatio ** property_value)
693 xmlChar *prop_string;
694 guint num = 0, den = 1;
697 gboolean exists = FALSE;
699 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
701 len = xmlStrlen (prop_string);
702 str = (gchar *) prop_string;
703 GST_TRACE ("ratio: %s, len %d", str, len);
706 pos = strcspn (str, ":");
708 GST_TRACE ("pos %d >= len %d", pos, len);
711 /* search for negative sign */
712 if (strstr (str, "-") != NULL) {
717 if (sscanf (str, "%u", &num) != 1) {
722 if (pos < (len - 1)) {
723 if (sscanf (str + pos + 1, "%u", &den) != 1) {
727 /* malloc return data structure */
728 *property_value = g_slice_new0 (GstRatio);
730 (*property_value)->num = num;
731 (*property_value)->den = den;
732 xmlFree (prop_string);
733 GST_LOG (" - %s: %u:%u", property_name, num, den);
739 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
741 xmlFree (prop_string);
746 gst_mpdparser_get_xml_prop_framerate (xmlNode * a_node,
747 const gchar * property_name, GstFrameRate ** property_value)
749 xmlChar *prop_string;
750 guint num = 0, den = 1;
753 gboolean exists = FALSE;
755 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
757 len = xmlStrlen (prop_string);
758 str = (gchar *) prop_string;
759 GST_TRACE ("framerate: %s, len %d", str, len);
761 /* search for negative sign */
762 if (strstr (str, "-") != NULL) {
766 /* read "/" if available */
767 pos = strcspn (str, "/");
770 if (sscanf (str, "%u", &num) != 1) {
774 /* read den (if available) */
775 if (pos < (len - 1)) {
776 if (sscanf (str + pos + 1, "%u", &den) != 1) {
780 /* alloc return data structure */
781 *property_value = g_slice_new0 (GstFrameRate);
783 (*property_value)->num = num;
784 (*property_value)->den = den;
785 xmlFree (prop_string);
787 GST_LOG (" - %s: %u", property_name, num);
789 GST_LOG (" - %s: %u/%u", property_name, num, den);
795 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
797 xmlFree (prop_string);
802 gst_mpdparser_get_xml_prop_cond_uint (xmlNode * a_node,
803 const gchar * property_name, GstConditionalUintType ** property_value)
805 xmlChar *prop_string;
809 gboolean exists = FALSE;
811 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
813 str = (gchar *) prop_string;
814 GST_TRACE ("conditional uint: %s", str);
816 if (strcmp (str, "false") == 0) {
819 } else if (strcmp (str, "true") == 0) {
824 if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
828 /* alloc return data structure */
829 *property_value = g_slice_new0 (GstConditionalUintType);
831 (*property_value)->flag = flag;
832 (*property_value)->value = val;
833 xmlFree (prop_string);
834 GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
841 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
843 xmlFree (prop_string);
850 The dateTime data type is used to specify a date and a time.
852 The dateTime is specified in the following form "YYYY-MM-DDThh:mm:ss" where:
854 * YYYY indicates the year
855 * MM indicates the month
856 * DD indicates the day
857 * T indicates the start of the required time section
858 * hh indicates the hour
859 * mm indicates the minute
860 * ss indicates the second
862 Note: All components are required!
865 gst_mpdparser_get_xml_prop_dateTime (xmlNode * a_node,
866 const gchar * property_name, GstDateTime ** property_value)
868 xmlChar *prop_string;
871 gint year, month, day, hour, minute;
873 gboolean exists = FALSE;
875 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
877 str = (gchar *) prop_string;
878 GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
880 ret = sscanf (str, "%d", &year);
881 if (ret != 1 || year <= 0)
883 pos = strcspn (str, "-");
885 GST_TRACE (" - year %d", year);
887 ret = sscanf (str, "%d", &month);
888 if (ret != 1 || month <= 0)
890 pos = strcspn (str, "-");
892 GST_TRACE (" - month %d", month);
894 ret = sscanf (str, "%d", &day);
895 if (ret != 1 || day <= 0)
897 pos = strcspn (str, "T");
899 GST_TRACE (" - day %d", day);
901 ret = sscanf (str, "%d", &hour);
902 if (ret != 1 || hour < 0)
904 pos = strcspn (str, ":");
906 GST_TRACE (" - hour %d", hour);
908 ret = sscanf (str, "%d", &minute);
909 if (ret != 1 || minute < 0)
911 pos = strcspn (str, ":");
913 GST_TRACE (" - minute %d", minute);
915 ret = sscanf (str, "%lf", &second);
916 if (ret != 1 || second < 0)
918 GST_TRACE (" - second %lf", second);
920 GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
921 year, month, day, hour, minute, second);
925 gst_date_time_new (0, year, month, day, hour, minute, second);
926 xmlFree (prop_string);
932 GST_WARNING ("failed to parse property %s from xml string %s", property_name,
934 xmlFree (prop_string);
941 The duration data type is used to specify a time interval.
943 The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
945 * - indicates the negative sign (optional)
946 * P indicates the period (required)
947 * nY indicates the number of years
948 * nM indicates the number of months
949 * nD indicates the number of days
950 * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
951 * nH indicates the number of hours
952 * nM indicates the number of minutes
953 * nS indicates the number of seconds
956 /* this function computes decimals * 10 ^ (3 - pos) */
958 convert_to_millisecs (guint decimals, gint pos)
960 guint num = 1, den = 1;
971 /* if i == 0 we have exactly 3 decimals and nothing to do */
972 return decimals * num / den;
976 accumulate (guint64 * v, guint64 mul, guint64 add)
980 if (*v > G_MAXUINT64 / mul)
983 if (tmp > G_MAXUINT64 - add)
990 gst_mpdparser_parse_duration (const char *str, guint64 * value)
992 gint ret, len, pos, posT;
993 gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
994 -1, decimals = -1, read;
995 gboolean have_ms = FALSE;
999 GST_TRACE ("duration: %s, len %d", str, len);
1000 if (strspn (str, "PT0123456789., \tHMDSY") < len) {
1001 GST_WARNING ("Invalid character found: '%s'", str);
1004 /* skip leading/trailing whitespace */
1005 while (g_ascii_isspace (str[0])) {
1009 while (len > 0 && g_ascii_isspace (str[len - 1]))
1012 /* read "P" for period */
1013 if (str[0] != 'P') {
1014 GST_WARNING ("P not found at the beginning of the string!");
1020 /* read "T" for time (if present) */
1021 posT = strcspn (str, "T");
1024 /* there is some room between P and T, so there must be a period section */
1025 /* read years, months, days */
1027 GST_TRACE ("parsing substring %s", str);
1028 pos = strcspn (str, "YMD");
1029 ret = sscanf (str, "%u", &read);
1031 GST_WARNING ("can not read integer value from string %s!", str);
1036 if (years != -1 || months != -1 || days != -1) {
1037 GST_WARNING ("year, month or day was already set");
1043 if (months != -1 || days != -1) {
1044 GST_WARNING ("month or day was already set");
1049 GST_WARNING ("Month out of range");
1055 GST_WARNING ("day was already set");
1060 GST_WARNING ("Day out of range");
1065 GST_WARNING ("unexpected char %c!", str[pos]);
1069 GST_TRACE ("read number %u type %c", read, str[pos]);
1082 GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
1084 /* read "T" for time (if present) */
1085 /* here T is at pos == 0 */
1090 /* T found, there is a time section */
1091 /* read hours, minutes, seconds, hundredths of second */
1093 GST_TRACE ("parsing substring %s", str);
1094 pos = strcspn (str, "HMS,.");
1095 ret = sscanf (str, "%u", &read);
1097 GST_WARNING ("can not read integer value from string %s!", str);
1102 if (hours != -1 || minutes != -1 || seconds != -1) {
1103 GST_WARNING ("hour, minute or second was already set");
1108 GST_WARNING ("Hour out of range");
1113 if (minutes != -1 || seconds != -1) {
1114 GST_WARNING ("minute or second was already set");
1118 if (minutes >= 60) {
1119 GST_WARNING ("Minute out of range");
1125 /* we have read the decimal part of the seconds */
1126 decimals = convert_to_millisecs (read, pos);
1127 GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
1130 if (seconds != -1) {
1131 GST_WARNING ("second was already set");
1140 /* we have read the integer part of a decimal number in seconds */
1141 if (seconds != -1) {
1142 GST_WARNING ("second was already set");
1149 GST_WARNING ("unexpected char %c!", str[pos]);
1153 GST_TRACE ("read number %u type %c", read, str[pos]);
1167 GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
1170 if (!accumulate (&tmp_value, 1, years)
1171 || !accumulate (&tmp_value, 365, months * 30)
1172 || !accumulate (&tmp_value, 1, days)
1173 || !accumulate (&tmp_value, 24, hours)
1174 || !accumulate (&tmp_value, 60, minutes)
1175 || !accumulate (&tmp_value, 60, seconds)
1176 || !accumulate (&tmp_value, 1000, decimals))
1179 /* ensure it can be converted from milliseconds to nanoseconds */
1180 if (tmp_value > G_MAXUINT64 / 1000000)
1191 gst_mpdparser_get_xml_prop_duration (xmlNode * a_node,
1192 const gchar * property_name, guint64 default_value,
1193 guint64 * property_value)
1195 xmlChar *prop_string;
1197 gboolean exists = FALSE;
1199 *property_value = default_value;
1200 prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
1202 str = (gchar *) prop_string;
1203 if (!gst_mpdparser_parse_duration (str, property_value))
1205 GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
1206 xmlFree (prop_string);
1212 xmlFree (prop_string);
1217 gst_mpdparser_get_xml_node_content (xmlNode * a_node, gchar ** content)
1219 xmlChar *node_content = NULL;
1220 gboolean exists = FALSE;
1222 node_content = xmlNodeGetContent (a_node);
1225 *content = (gchar *) node_content;
1226 GST_LOG (" - %s: %s", a_node->name, *content);
1233 gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
1235 gboolean exists = FALSE;
1236 const char *txt_encoding;
1237 xmlOutputBufferPtr out_buf;
1239 txt_encoding = (const char *) a_node->doc->encoding;
1240 out_buf = xmlAllocOutputBuffer (NULL);
1241 g_assert (out_buf != NULL);
1242 xmlNodeDumpOutput (out_buf, a_node->doc, a_node, 0, 0, txt_encoding);
1243 xmlOutputBufferFlush (out_buf);
1244 #ifdef LIBXML2_NEW_BUFFER
1245 if (xmlOutputBufferGetSize (out_buf) > 0) {
1247 (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
1248 xmlOutputBufferGetSize (out_buf));
1252 if (out_buf->conv && out_buf->conv->use > 0) {
1254 (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
1256 } else if (out_buf->buffer && out_buf->buffer->use > 0) {
1258 (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
1261 #endif // LIBXML2_NEW_BUFFER
1262 (void) xmlOutputBufferClose (out_buf);
1265 GST_LOG (" - %s: %s", a_node->name, *content);
1271 gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
1274 gchar *namespace = NULL;
1276 if (prefix == NULL) {
1277 /* return the default namespace */
1279 namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
1281 GST_LOG (" - default namespace: %s", namespace);
1285 /* look for the specified prefix in the namespace list */
1286 for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
1287 if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
1288 namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
1290 GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
1300 gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node)
1302 GstBaseURL *new_base_url;
1304 new_base_url = g_slice_new0 (GstBaseURL);
1305 *list = g_list_append (*list, new_base_url);
1307 GST_LOG ("content of BaseURL node:");
1308 gst_mpdparser_get_xml_node_content (a_node, &new_base_url->baseURL);
1310 GST_LOG ("attributes of BaseURL node:");
1311 gst_mpdparser_get_xml_prop_string (a_node, "serviceLocation",
1312 &new_base_url->serviceLocation);
1313 gst_mpdparser_get_xml_prop_string (a_node, "byteRange",
1314 &new_base_url->byteRange);
1318 gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
1320 GstDescriptorType *new_descriptor;
1322 new_descriptor = g_slice_new0 (GstDescriptorType);
1323 *list = g_list_append (*list, new_descriptor);
1325 GST_LOG ("attributes of %s node:", a_node->name);
1326 gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1327 &new_descriptor->schemeIdUri);
1328 if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
1329 &new_descriptor->value)) {
1330 /* if no value attribute, use XML string representation of the node */
1331 gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
1336 gst_mpdparser_parse_content_component_node (GList ** list, xmlNode * a_node)
1339 GstContentComponentNode *new_content_component;
1341 new_content_component = g_slice_new0 (GstContentComponentNode);
1342 *list = g_list_append (*list, new_content_component);
1344 GST_LOG ("attributes of ContentComponent node:");
1345 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1346 &new_content_component->id);
1347 gst_mpdparser_get_xml_prop_string (a_node, "lang",
1348 &new_content_component->lang);
1349 gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1350 &new_content_component->contentType);
1351 gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_content_component->par);
1353 /* explore children nodes */
1354 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1355 if (cur_node->type == XML_ELEMENT_NODE) {
1356 if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
1357 gst_mpdparser_parse_descriptor_type_node
1358 (&new_content_component->Accessibility, cur_node);
1359 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
1360 gst_mpdparser_parse_descriptor_type_node (&new_content_component->Role,
1362 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
1363 gst_mpdparser_parse_descriptor_type_node
1364 (&new_content_component->Rating, cur_node);
1365 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
1366 gst_mpdparser_parse_descriptor_type_node
1367 (&new_content_component->Viewpoint, cur_node);
1374 gst_mpdparser_parse_location_node (GList ** list, xmlNode * a_node)
1376 gchar *location = NULL;
1378 GST_LOG ("content of Location node:");
1379 if (gst_mpdparser_get_xml_node_content (a_node, &location))
1380 *list = g_list_append (*list, location);
1384 gst_mpdparser_parse_subrepresentation_node (GList ** list, xmlNode * a_node)
1386 GstSubRepresentationNode *new_subrep;
1388 new_subrep = g_slice_new0 (GstSubRepresentationNode);
1389 *list = g_list_append (*list, new_subrep);
1391 GST_LOG ("attributes of SubRepresentation node:");
1392 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "level", 0,
1393 &new_subrep->level);
1394 gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "dependencyLevel",
1395 &new_subrep->dependencyLevel, &new_subrep->size);
1396 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1397 &new_subrep->bandwidth);
1398 gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1399 "contentComponent", &new_subrep->contentComponent);
1401 /* RepresentationBase extension */
1402 gst_mpdparser_parse_representation_base_type (&new_subrep->RepresentationBase,
1406 static GstSegmentURLNode *
1407 gst_mpdparser_clone_segment_url (GstSegmentURLNode * seg_url)
1409 GstSegmentURLNode *clone = NULL;
1412 clone = g_slice_new0 (GstSegmentURLNode);
1413 clone->media = xmlMemStrdup (seg_url->media);
1414 clone->mediaRange = gst_mpdparser_clone_range (seg_url->mediaRange);
1415 clone->index = xmlMemStrdup (seg_url->index);
1416 clone->indexRange = gst_mpdparser_clone_range (seg_url->indexRange);
1423 gst_mpdparser_parse_segment_url_node (GList ** list, xmlNode * a_node)
1425 GstSegmentURLNode *new_segment_url;
1427 new_segment_url = g_slice_new0 (GstSegmentURLNode);
1428 *list = g_list_append (*list, new_segment_url);
1430 GST_LOG ("attributes of SegmentURL node:");
1431 gst_mpdparser_get_xml_prop_string (a_node, "media", &new_segment_url->media);
1432 gst_mpdparser_get_xml_prop_range (a_node, "mediaRange",
1433 &new_segment_url->mediaRange);
1434 gst_mpdparser_get_xml_prop_string (a_node, "index", &new_segment_url->index);
1435 gst_mpdparser_get_xml_prop_range (a_node, "indexRange",
1436 &new_segment_url->indexRange);
1440 gst_mpdparser_parse_url_type_node (GstURLType ** pointer, xmlNode * a_node)
1442 GstURLType *new_url_type;
1444 gst_mpdparser_free_url_type_node (*pointer);
1445 *pointer = new_url_type = g_slice_new0 (GstURLType);
1447 GST_LOG ("attributes of URLType node:");
1448 gst_mpdparser_get_xml_prop_string (a_node, "sourceURL",
1449 &new_url_type->sourceURL);
1450 gst_mpdparser_get_xml_prop_range (a_node, "range", &new_url_type->range);
1454 gst_mpdparser_parse_seg_base_type_ext (GstSegmentBaseType ** pointer,
1455 xmlNode * a_node, GstSegmentBaseType * parent)
1458 GstSegmentBaseType *seg_base_type;
1464 gst_mpdparser_free_seg_base_type_ext (*pointer);
1465 *pointer = seg_base_type = g_slice_new0 (GstSegmentBaseType);
1467 /* Initialize values that have defaults */
1468 seg_base_type->indexRangeExact = FALSE;
1469 seg_base_type->timescale = 1;
1471 /* Inherit attribute values from parent */
1473 seg_base_type->timescale = parent->timescale;
1474 seg_base_type->presentationTimeOffset = parent->presentationTimeOffset;
1475 seg_base_type->indexRange = gst_mpdparser_clone_range (parent->indexRange);
1476 seg_base_type->indexRangeExact = parent->indexRangeExact;
1477 seg_base_type->Initialization =
1478 gst_mpdparser_clone_URL (parent->Initialization);
1479 seg_base_type->RepresentationIndex =
1480 gst_mpdparser_clone_URL (parent->RepresentationIndex);
1483 /* We must retrieve each value first to see if it exists. If it does not
1484 * exist, we do not want to overwrite an inherited value */
1485 GST_LOG ("attributes of SegmentBaseType extension:");
1486 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "timescale", 1,
1488 seg_base_type->timescale = intval;
1490 if (gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node,
1491 "presentationTimeOffset", 0, &int64val)) {
1492 seg_base_type->presentationTimeOffset = int64val;
1494 if (gst_mpdparser_get_xml_prop_range (a_node, "indexRange", &rangeval)) {
1495 if (seg_base_type->indexRange) {
1496 g_slice_free (GstRange, seg_base_type->indexRange);
1498 seg_base_type->indexRange = rangeval;
1500 if (gst_mpdparser_get_xml_prop_boolean (a_node, "indexRangeExact",
1502 seg_base_type->indexRangeExact = boolval;
1505 /* explore children nodes */
1506 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1507 if (cur_node->type == XML_ELEMENT_NODE) {
1508 if (xmlStrcmp (cur_node->name, (xmlChar *) "Initialization") == 0 ||
1509 xmlStrcmp (cur_node->name, (xmlChar *) "Initialisation") == 0) {
1510 /* parse will free the previous pointer to create a new one */
1511 gst_mpdparser_parse_url_type_node (&seg_base_type->Initialization,
1513 } else if (xmlStrcmp (cur_node->name,
1514 (xmlChar *) "RepresentationIndex") == 0) {
1515 /* parse will free the previous pointer to create a new one */
1516 gst_mpdparser_parse_url_type_node (&seg_base_type->RepresentationIndex,
1524 gst_mpdparser_clone_s_node (GstSNode * pointer)
1526 GstSNode *clone = NULL;
1529 clone = g_slice_new0 (GstSNode);
1530 clone->t = pointer->t;
1531 clone->d = pointer->d;
1532 clone->r = pointer->r;
1539 gst_mpdparser_parse_s_node (GQueue * queue, xmlNode * a_node)
1541 GstSNode *new_s_node;
1543 new_s_node = g_slice_new0 (GstSNode);
1544 g_queue_push_tail (queue, new_s_node);
1546 GST_LOG ("attributes of S node:");
1547 gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "t", 0,
1549 gst_mpdparser_get_xml_prop_unsigned_integer_64 (a_node, "d", 0,
1551 gst_mpdparser_get_xml_prop_signed_integer (a_node, "r", 0, &new_s_node->r);
1554 static GstSegmentTimelineNode *
1555 gst_mpdparser_clone_segment_timeline (GstSegmentTimelineNode * pointer)
1557 GstSegmentTimelineNode *clone = NULL;
1560 clone = gst_mpdparser_segment_timeline_node_new ();
1563 for (list = g_queue_peek_head_link (&pointer->S); list;
1564 list = g_list_next (list)) {
1566 s_node = (GstSNode *) list->data;
1568 g_queue_push_tail (&clone->S, gst_mpdparser_clone_s_node (s_node));
1572 GST_WARNING ("Allocation of SegmentTimeline node failed!");
1580 gst_mpdparser_parse_segment_timeline_node (GstSegmentTimelineNode ** pointer,
1584 GstSegmentTimelineNode *new_seg_timeline;
1586 gst_mpdparser_free_segment_timeline_node (*pointer);
1587 *pointer = new_seg_timeline = gst_mpdparser_segment_timeline_node_new ();
1588 if (new_seg_timeline == NULL) {
1589 GST_WARNING ("Allocation of SegmentTimeline node failed!");
1593 /* explore children nodes */
1594 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1595 if (cur_node->type == XML_ELEMENT_NODE) {
1596 if (xmlStrcmp (cur_node->name, (xmlChar *) "S") == 0) {
1597 gst_mpdparser_parse_s_node (&new_seg_timeline->S, cur_node);
1604 gst_mpdparser_parse_mult_seg_base_type_ext (GstMultSegmentBaseType ** pointer,
1605 xmlNode * a_node, GstMultSegmentBaseType * parent)
1608 GstMultSegmentBaseType *mult_seg_base_type;
1610 gboolean has_timeline = FALSE, has_duration = FALSE;
1612 gst_mpdparser_free_mult_seg_base_type_ext (*pointer);
1613 mult_seg_base_type = g_slice_new0 (GstMultSegmentBaseType);
1615 mult_seg_base_type->duration = 0;
1616 mult_seg_base_type->startNumber = 1;
1618 /* Inherit attribute values from parent */
1620 mult_seg_base_type->duration = parent->duration;
1621 mult_seg_base_type->startNumber = parent->startNumber;
1622 mult_seg_base_type->SegmentTimeline =
1623 gst_mpdparser_clone_segment_timeline (parent->SegmentTimeline);
1624 mult_seg_base_type->BitstreamSwitching =
1625 gst_mpdparser_clone_URL (parent->BitstreamSwitching);
1628 GST_LOG ("attributes of MultipleSegmentBaseType extension:");
1629 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "duration", 0,
1631 mult_seg_base_type->duration = intval;
1634 /* duration might be specified from parent */
1635 if (mult_seg_base_type->duration)
1636 has_duration = TRUE;
1638 if (gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "startNumber", 1,
1640 mult_seg_base_type->startNumber = intval;
1643 GST_LOG ("extension of MultipleSegmentBaseType extension:");
1644 gst_mpdparser_parse_seg_base_type_ext (&mult_seg_base_type->SegBaseType,
1645 a_node, (parent ? parent->SegBaseType : NULL));
1647 /* explore children nodes */
1648 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1649 if (cur_node->type == XML_ELEMENT_NODE) {
1650 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTimeline") == 0) {
1651 /* parse frees the segmenttimeline if any */
1652 gst_mpdparser_parse_segment_timeline_node
1653 (&mult_seg_base_type->SegmentTimeline, cur_node);
1654 } else if (xmlStrcmp (cur_node->name,
1655 (xmlChar *) "BitstreamSwitching") == 0) {
1656 /* parse frees the old url before setting the new one */
1657 gst_mpdparser_parse_url_type_node
1658 (&mult_seg_base_type->BitstreamSwitching, cur_node);
1663 has_timeline = mult_seg_base_type->SegmentTimeline != NULL;
1665 /* Checking duration and timeline only at Representation's child level */
1666 if (xmlStrcmp (a_node->parent->name, (xmlChar *) "Representation") == 0
1667 && !has_duration && !has_timeline) {
1668 GST_ERROR ("segment has neither duration nor timeline");
1672 *pointer = mult_seg_base_type;
1676 gst_mpdparser_free_mult_seg_base_type_ext (mult_seg_base_type);
1681 gst_mpdparser_parse_segment_list_node (GstSegmentListNode ** pointer,
1682 xmlNode * a_node, GstSegmentListNode * parent)
1685 GstSegmentListNode *new_segment_list;
1687 gboolean segment_urls_inherited_from_parent = FALSE;
1689 gst_mpdparser_free_segment_list_node (*pointer);
1690 new_segment_list = g_slice_new0 (GstSegmentListNode);
1692 /* Inherit attribute values from parent */
1695 GstSegmentURLNode *seg_url;
1696 for (list = g_list_first (parent->SegmentURL); list;
1697 list = g_list_next (list)) {
1698 seg_url = (GstSegmentURLNode *) list->data;
1699 new_segment_list->SegmentURL =
1700 g_list_append (new_segment_list->SegmentURL,
1701 gst_mpdparser_clone_segment_url (seg_url));
1702 segment_urls_inherited_from_parent = TRUE;
1706 new_segment_list->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1707 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1708 "http://www.w3.org/1999/xlink", "href", &new_segment_list->xlink_href)
1709 && gst_mpdparser_get_xml_ns_prop_string (a_node,
1710 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1711 if (strcmp (actuate, "onLoad") == 0)
1712 new_segment_list->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1716 GST_LOG ("extension of SegmentList node:");
1717 if (!gst_mpdparser_parse_mult_seg_base_type_ext
1718 (&new_segment_list->MultSegBaseType, a_node,
1719 (parent ? parent->MultSegBaseType : NULL)))
1722 /* explore children nodes */
1723 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1724 if (cur_node->type == XML_ELEMENT_NODE) {
1725 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentURL") == 0) {
1726 if (segment_urls_inherited_from_parent) {
1728 * SegmentBase, SegmentTemplate and SegmentList shall inherit
1729 * attributes and elements from the same element on a higher level.
1730 * If the same attribute or element is present on both levels,
1731 * the one on the lower level shall take precedence over the one
1732 * on the higher level.
1735 /* Clear the list of inherited segment URLs */
1736 g_list_free_full (new_segment_list->SegmentURL,
1737 (GDestroyNotify) gst_mpdparser_free_segment_url_node);
1738 new_segment_list->SegmentURL = NULL;
1740 /* mark the fact that we cleared the list, so that it is not tried again */
1741 segment_urls_inherited_from_parent = FALSE;
1743 gst_mpdparser_parse_segment_url_node (&new_segment_list->SegmentURL,
1749 *pointer = new_segment_list;
1753 gst_mpdparser_free_segment_list_node (new_segment_list);
1758 gst_mpdparser_parse_content_protection_node (GList ** list, xmlNode * a_node)
1760 gchar *value = NULL;
1761 if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
1762 if (!g_strcmp0 (value, "MSPR 2.0")) {
1764 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1765 if (cur_node->type == XML_ELEMENT_NODE) {
1766 if (xmlStrcmp (cur_node->name, (xmlChar *) "pro") == 0) {
1767 GstDescriptorType *new_descriptor;
1768 new_descriptor = g_slice_new0 (GstDescriptorType);
1769 *list = g_list_append (*list, new_descriptor);
1771 gst_mpdparser_get_xml_prop_string_stripped (a_node, "schemeIdUri",
1772 &new_descriptor->schemeIdUri);
1774 gst_mpdparser_get_xml_node_content (cur_node,
1775 &new_descriptor->value);
1781 gst_mpdparser_parse_descriptor_type_node (list, a_node);
1784 gst_mpdparser_parse_descriptor_type_node (list, a_node);
1792 gst_mpdparser_parse_representation_base_type (GstRepresentationBaseType **
1793 pointer, xmlNode * a_node)
1796 GstRepresentationBaseType *representation_base;
1798 gst_mpdparser_free_representation_base_type (*pointer);
1799 *pointer = representation_base = g_slice_new0 (GstRepresentationBaseType);
1801 GST_LOG ("attributes of RepresentationBaseType extension:");
1802 gst_mpdparser_get_xml_prop_string (a_node, "profiles",
1803 &representation_base->profiles);
1804 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "width", 0,
1805 &representation_base->width);
1806 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "height", 0,
1807 &representation_base->height);
1808 gst_mpdparser_get_xml_prop_ratio (a_node, "sar", &representation_base->sar);
1809 gst_mpdparser_get_xml_prop_framerate (a_node, "frameRate",
1810 &representation_base->frameRate);
1811 gst_mpdparser_get_xml_prop_framerate (a_node, "minFrameRate",
1812 &representation_base->minFrameRate);
1813 gst_mpdparser_get_xml_prop_framerate (a_node, "maxFrameRate",
1814 &representation_base->maxFrameRate);
1815 gst_mpdparser_get_xml_prop_string (a_node, "audioSamplingRate",
1816 &representation_base->audioSamplingRate);
1817 gst_mpdparser_get_xml_prop_string (a_node, "mimeType",
1818 &representation_base->mimeType);
1819 gst_mpdparser_get_xml_prop_string (a_node, "segmentProfiles",
1820 &representation_base->segmentProfiles);
1821 gst_mpdparser_get_xml_prop_string (a_node, "codecs",
1822 &representation_base->codecs);
1823 gst_mpdparser_get_xml_prop_double (a_node, "maximumSAPPeriod",
1824 &representation_base->maximumSAPPeriod);
1825 gst_mpdparser_get_xml_prop_SAP_type (a_node, "startWithSAP",
1826 &representation_base->startWithSAP);
1827 gst_mpdparser_get_xml_prop_double (a_node, "maxPlayoutRate",
1828 &representation_base->maxPlayoutRate);
1829 gst_mpdparser_get_xml_prop_boolean (a_node, "codingDependency",
1830 FALSE, &representation_base->codingDependency);
1831 gst_mpdparser_get_xml_prop_string (a_node, "scanType",
1832 &representation_base->scanType);
1834 /* explore children nodes */
1835 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1836 if (cur_node->type == XML_ELEMENT_NODE) {
1837 if (xmlStrcmp (cur_node->name, (xmlChar *) "FramePacking") == 0) {
1838 gst_mpdparser_parse_descriptor_type_node
1839 (&representation_base->FramePacking, cur_node);
1840 } else if (xmlStrcmp (cur_node->name,
1841 (xmlChar *) "AudioChannelConfiguration") == 0) {
1842 gst_mpdparser_parse_descriptor_type_node
1843 (&representation_base->AudioChannelConfiguration, cur_node);
1844 } else if (xmlStrcmp (cur_node->name,
1845 (xmlChar *) "ContentProtection") == 0) {
1846 gst_mpdparser_parse_content_protection_node
1847 (&representation_base->ContentProtection, cur_node);
1853 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
1855 gst_mpdparser_parse_representation_node (GList ** list, GList ** info, xmlNode * a_node,
1856 GstAdaptationSetNode * parent, GstPeriodNode * period_node)
1859 gst_mpdparser_parse_representation_node (GList ** list, xmlNode * a_node,
1860 GstAdaptationSetNode * parent, GstPeriodNode * period_node)
1864 GstRepresentationNode *new_representation;
1865 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
1866 struct GstVideoVariantInfo *variant_info;
1870 new_representation = g_slice_new0 (GstRepresentationNode);
1872 GST_LOG ("attributes of Representation node:");
1873 if (!gst_mpdparser_get_xml_prop_string_no_whitespace (a_node, "id",
1874 &new_representation->id)) {
1875 GST_ERROR ("Cannot parse Representation id, invalid manifest");
1878 if (!gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "bandwidth", 0,
1879 &new_representation->bandwidth)) {
1880 GST_ERROR ("Cannot parse Representation bandwidth, invalid manifest");
1883 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "qualityRanking", 0,
1884 &new_representation->qualityRanking);
1885 gst_mpdparser_get_xml_prop_string_vector_type (a_node, "dependencyId",
1886 &new_representation->dependencyId);
1887 gst_mpdparser_get_xml_prop_string_vector_type (a_node,
1888 "mediaStreamStructureId", &new_representation->mediaStreamStructureId);
1890 /* RepresentationBase extension */
1891 gst_mpdparser_parse_representation_base_type
1892 (&new_representation->RepresentationBase, a_node);
1894 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
1895 mime = new_representation->RepresentationBase->mimeType;
1896 if (strncmp_ext(mime, "video") == 0) {
1897 variant_info = g_new0(struct GstVideoVariantInfo, 1);
1899 GST_LOG ("video variant info %d, %d x %d", new_representation->bandwidth,
1900 new_representation->RepresentationBase->width,
1901 new_representation->RepresentationBase->height);
1903 variant_info->bandwidth = new_representation->bandwidth;
1904 variant_info->width = new_representation->RepresentationBase->width;
1905 variant_info->height = new_representation->RepresentationBase->height;
1907 *info = g_list_append(*info, variant_info);
1911 /* explore children nodes */
1912 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
1913 if (cur_node->type == XML_ELEMENT_NODE) {
1914 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
1915 gst_mpdparser_parse_seg_base_type_ext (&new_representation->SegmentBase,
1916 cur_node, parent->SegmentBase ?
1917 parent->SegmentBase : period_node->SegmentBase);
1918 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
1919 if (!gst_mpdparser_parse_segment_template_node
1920 (&new_representation->SegmentTemplate, cur_node,
1921 parent->SegmentTemplate ?
1922 parent->SegmentTemplate : period_node->SegmentTemplate))
1924 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
1925 if (!gst_mpdparser_parse_segment_list_node
1926 (&new_representation->SegmentList, cur_node,
1927 parent->SegmentList ? parent->
1928 SegmentList : period_node->SegmentList))
1930 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
1931 gst_mpdparser_parse_baseURL_node (&new_representation->BaseURLs,
1933 } else if (xmlStrcmp (cur_node->name,
1934 (xmlChar *) "SubRepresentation") == 0) {
1935 gst_mpdparser_parse_subrepresentation_node
1936 (&new_representation->SubRepresentations, cur_node);
1941 /* some sanity checking */
1943 *list = g_list_append (*list, new_representation);
1947 gst_mpdparser_free_representation_node (new_representation);
1952 gst_mpdparser_parse_adaptation_set_node (GList ** list, xmlNode * a_node,
1953 GstPeriodNode * parent)
1956 GstAdaptationSetNode *new_adap_set;
1959 new_adap_set = g_slice_new0 (GstAdaptationSetNode);
1961 GST_LOG ("attributes of AdaptationSet node:");
1963 new_adap_set->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
1964 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
1965 "http://www.w3.org/1999/xlink", "href", &new_adap_set->xlink_href)
1966 && gst_mpdparser_get_xml_ns_prop_string (a_node,
1967 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
1968 if (strcmp (actuate, "onLoad") == 0)
1969 new_adap_set->actuate = GST_XLINK_ACTUATE_ON_LOAD;
1973 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "id", 0,
1975 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "group", 0,
1976 &new_adap_set->group);
1977 gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_adap_set->lang);
1978 gst_mpdparser_get_xml_prop_string (a_node, "contentType",
1979 &new_adap_set->contentType);
1980 gst_mpdparser_get_xml_prop_ratio (a_node, "par", &new_adap_set->par);
1981 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minBandwidth", 0,
1982 &new_adap_set->minBandwidth);
1983 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxBandwidth", 0,
1984 &new_adap_set->maxBandwidth);
1985 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minWidth", 0,
1986 &new_adap_set->minWidth);
1987 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxWidth", 0,
1988 &new_adap_set->maxWidth);
1989 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "minHeight", 0,
1990 &new_adap_set->minHeight);
1991 gst_mpdparser_get_xml_prop_unsigned_integer (a_node, "maxHeight", 0,
1992 &new_adap_set->maxHeight);
1993 gst_mpdparser_get_xml_prop_cond_uint (a_node, "segmentAlignment",
1994 &new_adap_set->segmentAlignment);
1995 gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching",
1996 parent->bitstreamSwitching, &new_adap_set->bitstreamSwitching);
1997 if (parent->bitstreamSwitching && !new_adap_set->bitstreamSwitching) {
1998 /* according to the standard, if the Period's bitstreamSwitching attribute
1999 * is true, the AdaptationSet should not have the bitstreamSwitching
2000 * attribute set to false.
2001 * We should return a parsing error, but we are generous and ignore the
2002 * standard violation.
2004 new_adap_set->bitstreamSwitching = parent->bitstreamSwitching;
2006 gst_mpdparser_get_xml_prop_cond_uint (a_node, "subsegmentAlignment",
2007 &new_adap_set->subsegmentAlignment);
2008 gst_mpdparser_get_xml_prop_SAP_type (a_node, "subsegmentStartsWithSAP",
2009 &new_adap_set->subsegmentStartsWithSAP);
2011 /* RepresentationBase extension */
2012 gst_mpdparser_parse_representation_base_type
2013 (&new_adap_set->RepresentationBase, a_node);
2015 /* explore children nodes */
2016 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2017 if (cur_node->type == XML_ELEMENT_NODE) {
2018 if (xmlStrcmp (cur_node->name, (xmlChar *) "Accessibility") == 0) {
2019 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Accessibility,
2021 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Role") == 0) {
2022 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Role,
2024 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Rating") == 0) {
2025 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Rating,
2027 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Viewpoint") == 0) {
2028 gst_mpdparser_parse_descriptor_type_node (&new_adap_set->Viewpoint,
2030 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2031 gst_mpdparser_parse_baseURL_node (&new_adap_set->BaseURLs, cur_node);
2032 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2033 gst_mpdparser_parse_seg_base_type_ext (&new_adap_set->SegmentBase,
2034 cur_node, parent->SegmentBase);
2035 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2036 if (!gst_mpdparser_parse_segment_list_node (&new_adap_set->SegmentList,
2037 cur_node, parent->SegmentList))
2039 } else if (xmlStrcmp (cur_node->name,
2040 (xmlChar *) "ContentComponent") == 0) {
2041 gst_mpdparser_parse_content_component_node
2042 (&new_adap_set->ContentComponents, cur_node);
2043 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2044 if (!gst_mpdparser_parse_segment_template_node
2045 (&new_adap_set->SegmentTemplate, cur_node, parent->SegmentTemplate))
2051 /* We must parse Representation after everything else in the AdaptationSet
2052 * has been parsed because certain Representation child elements can inherit
2053 * attributes specified by the same element in the AdaptationSet
2055 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2056 if (cur_node->type == XML_ELEMENT_NODE) {
2057 if (xmlStrcmp (cur_node->name, (xmlChar *) "Representation") == 0) {
2058 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
2059 if (!gst_mpdparser_parse_representation_node
2060 (&new_adap_set->Representations, &new_adap_set->VariantInfo, cur_node, new_adap_set, parent))
2063 if (!gst_mpdparser_parse_representation_node
2064 (&new_adap_set->Representations, cur_node, new_adap_set, parent))
2071 *list = g_list_append (*list, new_adap_set);
2075 gst_mpdparser_free_adaptation_set_node (new_adap_set);
2080 gst_mpdparser_parse_subset_node (GList ** list, xmlNode * a_node)
2082 GstSubsetNode *new_subset;
2084 new_subset = g_slice_new0 (GstSubsetNode);
2085 *list = g_list_append (*list, new_subset);
2087 GST_LOG ("attributes of Subset node:");
2088 gst_mpdparser_get_xml_prop_uint_vector_type (a_node, "contains",
2089 &new_subset->contains, &new_subset->size);
2093 gst_mpdparser_parse_segment_template_node (GstSegmentTemplateNode ** pointer,
2094 xmlNode * a_node, GstSegmentTemplateNode * parent)
2096 GstSegmentTemplateNode *new_segment_template;
2099 gst_mpdparser_free_segment_template_node (*pointer);
2100 new_segment_template = g_slice_new0 (GstSegmentTemplateNode);
2102 GST_LOG ("extension of SegmentTemplate node:");
2103 if (!gst_mpdparser_parse_mult_seg_base_type_ext
2104 (&new_segment_template->MultSegBaseType, a_node,
2105 (parent ? parent->MultSegBaseType : NULL)))
2108 /* Inherit attribute values from parent when the value isn't found */
2109 GST_LOG ("attributes of SegmentTemplate node:");
2110 if (gst_mpdparser_get_xml_prop_string (a_node, "media", &strval)) {
2111 new_segment_template->media = strval;
2112 } else if (parent) {
2113 new_segment_template->media = xmlMemStrdup (parent->media);
2116 if (gst_mpdparser_get_xml_prop_string (a_node, "index", &strval)) {
2117 new_segment_template->index = strval;
2118 } else if (parent) {
2119 new_segment_template->index = xmlMemStrdup (parent->index);
2122 if (gst_mpdparser_get_xml_prop_string (a_node, "initialization", &strval)) {
2123 new_segment_template->initialization = strval;
2124 } else if (parent) {
2125 new_segment_template->initialization =
2126 xmlMemStrdup (parent->initialization);
2129 if (gst_mpdparser_get_xml_prop_string (a_node, "bitstreamSwitching", &strval)) {
2130 new_segment_template->bitstreamSwitching = strval;
2131 } else if (parent) {
2132 new_segment_template->bitstreamSwitching =
2133 xmlMemStrdup (parent->bitstreamSwitching);
2136 *pointer = new_segment_template;
2140 gst_mpdparser_free_segment_template_node (new_segment_template);
2145 gst_mpdparser_parse_period_node (GList ** list, xmlNode * a_node)
2148 GstPeriodNode *new_period;
2151 new_period = g_slice_new0 (GstPeriodNode);
2153 GST_LOG ("attributes of Period node:");
2155 new_period->actuate = GST_XLINK_ACTUATE_ON_REQUEST;
2156 if (gst_mpdparser_get_xml_ns_prop_string (a_node,
2157 "http://www.w3.org/1999/xlink", "href", &new_period->xlink_href)
2158 && gst_mpdparser_get_xml_ns_prop_string (a_node,
2159 "http://www.w3.org/1999/xlink", "actuate", &actuate)) {
2160 if (strcmp (actuate, "onLoad") == 0)
2161 new_period->actuate = GST_XLINK_ACTUATE_ON_LOAD;
2165 gst_mpdparser_get_xml_prop_string (a_node, "id", &new_period->id);
2166 gst_mpdparser_get_xml_prop_duration (a_node, "start", GST_MPD_DURATION_NONE,
2167 &new_period->start);
2168 gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2169 GST_MPD_DURATION_NONE, &new_period->duration);
2170 gst_mpdparser_get_xml_prop_boolean (a_node, "bitstreamSwitching", FALSE,
2171 &new_period->bitstreamSwitching);
2173 /* explore children nodes */
2174 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2175 if (cur_node->type == XML_ELEMENT_NODE) {
2176 if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentBase") == 0) {
2177 gst_mpdparser_parse_seg_base_type_ext (&new_period->SegmentBase,
2179 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentList") == 0) {
2180 if (!gst_mpdparser_parse_segment_list_node (&new_period->SegmentList,
2183 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "SegmentTemplate") == 0) {
2184 if (!gst_mpdparser_parse_segment_template_node
2185 (&new_period->SegmentTemplate, cur_node, NULL))
2187 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Subset") == 0) {
2188 gst_mpdparser_parse_subset_node (&new_period->Subsets, cur_node);
2189 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2190 gst_mpdparser_parse_baseURL_node (&new_period->BaseURLs, cur_node);
2195 /* We must parse AdaptationSet after everything else in the Period has been
2196 * parsed because certain AdaptationSet child elements can inherit attributes
2197 * specified by the same element in the Period
2199 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2200 if (cur_node->type == XML_ELEMENT_NODE) {
2201 if (xmlStrcmp (cur_node->name, (xmlChar *) "AdaptationSet") == 0) {
2202 if (!gst_mpdparser_parse_adaptation_set_node
2203 (&new_period->AdaptationSets, cur_node, new_period))
2209 *list = g_list_append (*list, new_period);
2213 gst_mpdparser_free_period_node (new_period);
2218 gst_mpdparser_parse_program_info_node (GList ** list, xmlNode * a_node)
2221 GstProgramInformationNode *new_prog_info;
2223 new_prog_info = g_slice_new0 (GstProgramInformationNode);
2224 *list = g_list_append (*list, new_prog_info);
2226 GST_LOG ("attributes of ProgramInformation node:");
2227 gst_mpdparser_get_xml_prop_string (a_node, "lang", &new_prog_info->lang);
2228 gst_mpdparser_get_xml_prop_string (a_node, "moreInformationURL",
2229 &new_prog_info->moreInformationURL);
2231 /* explore children nodes */
2232 GST_LOG ("children of ProgramInformation node:");
2233 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2234 if (cur_node->type == XML_ELEMENT_NODE) {
2235 if (xmlStrcmp (cur_node->name, (xmlChar *) "Title") == 0) {
2236 gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Title);
2237 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Source") == 0) {
2238 gst_mpdparser_get_xml_node_content (cur_node, &new_prog_info->Source);
2239 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Copyright") == 0) {
2240 gst_mpdparser_get_xml_node_content (cur_node,
2241 &new_prog_info->Copyright);
2248 gst_mpdparser_parse_metrics_range_node (GList ** list, xmlNode * a_node)
2250 GstMetricsRangeNode *new_metrics_range;
2252 new_metrics_range = g_slice_new0 (GstMetricsRangeNode);
2253 *list = g_list_append (*list, new_metrics_range);
2255 GST_LOG ("attributes of Metrics Range node:");
2256 gst_mpdparser_get_xml_prop_duration (a_node, "starttime",
2257 GST_MPD_DURATION_NONE, &new_metrics_range->starttime);
2258 gst_mpdparser_get_xml_prop_duration (a_node, "duration",
2259 GST_MPD_DURATION_NONE, &new_metrics_range->duration);
2263 gst_mpdparser_parse_metrics_node (GList ** list, xmlNode * a_node)
2266 GstMetricsNode *new_metrics;
2268 new_metrics = g_slice_new0 (GstMetricsNode);
2269 *list = g_list_append (*list, new_metrics);
2271 GST_LOG ("attributes of Metrics node:");
2272 gst_mpdparser_get_xml_prop_string (a_node, "metrics", &new_metrics->metrics);
2274 /* explore children nodes */
2275 GST_LOG ("children of Metrics node:");
2276 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2277 if (cur_node->type == XML_ELEMENT_NODE) {
2278 if (xmlStrcmp (cur_node->name, (xmlChar *) "Range") == 0) {
2279 gst_mpdparser_parse_metrics_range_node (&new_metrics->MetricsRanges,
2281 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Reporting") == 0) {
2282 /* No reporting scheme is specified in this part of ISO/IEC 23009.
2283 * It is expected that external specifications may define formats
2284 * and delivery for the reporting data. */
2285 GST_LOG (" - Reporting node found (unknown structure)");
2291 /* The UTCTiming element is defined in
2292 * ISO/IEC 23009-1:2014/PDAM 1 "Information technology — Dynamic adaptive streaming over HTTP (DASH) — Part 1: Media presentation description and segment formats / Amendment 1: High Profile and Availability Time Synchronization"
2295 gst_mpdparser_parse_utctiming_node (GList ** list, xmlNode * a_node)
2297 GstUTCTimingNode *new_timing;
2298 gchar *method = NULL;
2299 gchar *value = NULL;
2301 new_timing = g_slice_new0 (GstUTCTimingNode);
2303 GST_LOG ("attributes of UTCTiming node:");
2304 if (gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri", &method)) {
2307 for (i = 0; gst_mpdparser_utc_timing_methods[i].name; ++i) {
2308 if (g_ascii_strncasecmp (gst_mpdparser_utc_timing_methods[i].name,
2309 method, strlen (gst_mpdparser_utc_timing_methods[i].name)) == 0) {
2310 new_timing->method = gst_mpdparser_utc_timing_methods[i].method;
2317 if (gst_mpdparser_get_xml_prop_string (a_node, "value", &value)) {
2319 if (GST_MPD_UTCTIMING_TYPE_DIRECT == new_timing->method) {
2320 /* The GST_MPD_UTCTIMING_TYPE_DIRECT method is a special case
2321 * that is not a space separated list.
2325 new_timing->urls = g_strsplit (value, " ", max_tokens);
2329 /* append to list only if both method and urls were set */
2330 if (new_timing->method != 0 && new_timing->urls != NULL &&
2331 g_strv_length (new_timing->urls) != 0) {
2332 *list = g_list_append (*list, new_timing);
2334 gst_mpdparser_free_utctiming_node (new_timing);
2339 gst_mpdparser_parse_root_node (GstMPDNode ** pointer, xmlNode * a_node)
2342 GstMPDNode *new_mpd;
2344 gst_mpdparser_free_mpd_node (*pointer);
2346 new_mpd = g_slice_new0 (GstMPDNode);
2348 GST_LOG ("namespaces of root MPD node:");
2349 new_mpd->default_namespace =
2350 gst_mpdparser_get_xml_node_namespace (a_node, NULL);
2351 new_mpd->namespace_xsi = gst_mpdparser_get_xml_node_namespace (a_node, "xsi");
2352 new_mpd->namespace_ext = gst_mpdparser_get_xml_node_namespace (a_node, "ext");
2354 GST_LOG ("attributes of root MPD node:");
2355 gst_mpdparser_get_xml_prop_string (a_node, "schemaLocation",
2356 &new_mpd->schemaLocation);
2357 gst_mpdparser_get_xml_prop_string (a_node, "id", &new_mpd->id);
2358 gst_mpdparser_get_xml_prop_string (a_node, "profiles", &new_mpd->profiles);
2359 gst_mpdparser_get_xml_prop_type (a_node, "type", &new_mpd->type);
2360 gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityStartTime",
2361 &new_mpd->availabilityStartTime);
2362 gst_mpdparser_get_xml_prop_dateTime (a_node, "availabilityEndTime",
2363 &new_mpd->availabilityEndTime);
2364 gst_mpdparser_get_xml_prop_duration (a_node, "mediaPresentationDuration",
2365 GST_MPD_DURATION_NONE, &new_mpd->mediaPresentationDuration);
2366 gst_mpdparser_get_xml_prop_duration (a_node, "minimumUpdatePeriod",
2367 GST_MPD_DURATION_NONE, &new_mpd->minimumUpdatePeriod);
2368 gst_mpdparser_get_xml_prop_duration (a_node, "minBufferTime",
2369 GST_MPD_DURATION_NONE, &new_mpd->minBufferTime);
2370 gst_mpdparser_get_xml_prop_duration (a_node, "timeShiftBufferDepth",
2371 GST_MPD_DURATION_NONE, &new_mpd->timeShiftBufferDepth);
2372 gst_mpdparser_get_xml_prop_duration (a_node, "suggestedPresentationDelay",
2373 GST_MPD_DURATION_NONE, &new_mpd->suggestedPresentationDelay);
2374 gst_mpdparser_get_xml_prop_duration (a_node, "maxSegmentDuration",
2375 GST_MPD_DURATION_NONE, &new_mpd->maxSegmentDuration);
2376 gst_mpdparser_get_xml_prop_duration (a_node, "maxSubsegmentDuration",
2377 GST_MPD_DURATION_NONE, &new_mpd->maxSubsegmentDuration);
2379 /* explore children Period nodes */
2380 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
2381 if (cur_node->type == XML_ELEMENT_NODE) {
2382 if (xmlStrcmp (cur_node->name, (xmlChar *) "Period") == 0) {
2383 if (!gst_mpdparser_parse_period_node (&new_mpd->Periods, cur_node))
2385 } else if (xmlStrcmp (cur_node->name,
2386 (xmlChar *) "ProgramInformation") == 0) {
2387 gst_mpdparser_parse_program_info_node (&new_mpd->ProgramInfo, cur_node);
2388 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "BaseURL") == 0) {
2389 gst_mpdparser_parse_baseURL_node (&new_mpd->BaseURLs, cur_node);
2390 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Location") == 0) {
2391 gst_mpdparser_parse_location_node (&new_mpd->Locations, cur_node);
2392 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "Metrics") == 0) {
2393 gst_mpdparser_parse_metrics_node (&new_mpd->Metrics, cur_node);
2394 } else if (xmlStrcmp (cur_node->name, (xmlChar *) "UTCTiming") == 0) {
2395 gst_mpdparser_parse_utctiming_node (&new_mpd->UTCTiming, cur_node);
2404 gst_mpdparser_free_mpd_node (new_mpd);
2408 /* comparison functions */
2410 strncmp_ext (const char *s1, const char *s2)
2412 if (s1 == NULL && s2 == NULL)
2414 if (s1 == NULL && s2 != NULL)
2416 if (s2 == NULL && s1 != NULL)
2418 return strncmp (s1, s2, strlen (s2));
2421 /* navigation functions */
2422 static GstStreamMimeType
2423 gst_mpdparser_representation_get_mimetype (GstAdaptationSetNode * adapt_set,
2424 GstRepresentationNode * rep)
2427 if (rep->RepresentationBase)
2428 mime = rep->RepresentationBase->mimeType;
2429 if (mime == NULL && adapt_set->RepresentationBase) {
2430 mime = adapt_set->RepresentationBase->mimeType;
2433 if (strncmp_ext (mime, "audio") == 0)
2434 return GST_STREAM_AUDIO;
2435 if (strncmp_ext (mime, "video") == 0)
2436 return GST_STREAM_VIDEO;
2437 if (strncmp_ext (mime, "application") == 0)
2438 return GST_STREAM_APPLICATION;
2440 return GST_STREAM_UNKNOWN;
2443 static GstRepresentationNode *
2444 gst_mpdparser_get_lowest_representation (GList * Representations)
2447 GstRepresentationNode *rep = NULL;
2448 GstRepresentationNode *lowest = NULL;
2450 if (Representations == NULL)
2453 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2454 rep = (GstRepresentationNode *) list->data;
2455 if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
2464 static GstRepresentationNode *
2465 gst_mpdparser_get_highest_representation (GList * Representations)
2469 if (Representations == NULL)
2472 list = g_list_last (Representations);
2474 return list ? (GstRepresentationNode *) list->data : NULL;
2477 static GstRepresentationNode *
2478 gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
2482 GstRepresentationNode *representation, *best_rep = NULL;
2484 if (Representations == NULL)
2487 if (max_bandwidth <= 0) /* 0 => get highest representation available */
2488 return gst_mpdparser_get_highest_representation (Representations);
2490 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2491 representation = (GstRepresentationNode *) list->data;
2492 if (representation && representation->bandwidth <= max_bandwidth) {
2493 best_rep = representation;
2501 static GstSegmentBaseType *
2502 gst_mpdparser_get_segment_base (GstPeriodNode * Period,
2503 GstAdaptationSetNode * AdaptationSet,
2504 GstRepresentationNode * Representation)
2506 GstSegmentBaseType *SegmentBase = NULL;
2508 if (Representation && Representation->SegmentBase) {
2509 SegmentBase = Representation->SegmentBase;
2510 } else if (AdaptationSet && AdaptationSet->SegmentBase) {
2511 SegmentBase = AdaptationSet->SegmentBase;
2512 } else if (Period && Period->SegmentBase) {
2513 SegmentBase = Period->SegmentBase;
2515 /* the SegmentBase element could be encoded also inside a SegmentList element */
2516 if (SegmentBase == NULL) {
2517 if (Representation && Representation->SegmentList
2518 && Representation->SegmentList->MultSegBaseType
2519 && Representation->SegmentList->MultSegBaseType->SegBaseType) {
2520 SegmentBase = Representation->SegmentList->MultSegBaseType->SegBaseType;
2521 } else if (AdaptationSet && AdaptationSet->SegmentList
2522 && AdaptationSet->SegmentList->MultSegBaseType
2523 && AdaptationSet->SegmentList->MultSegBaseType->SegBaseType) {
2524 SegmentBase = AdaptationSet->SegmentList->MultSegBaseType->SegBaseType;
2525 } else if (Period && Period->SegmentList
2526 && Period->SegmentList->MultSegBaseType
2527 && Period->SegmentList->MultSegBaseType->SegBaseType) {
2528 SegmentBase = Period->SegmentList->MultSegBaseType->SegBaseType;
2536 gst_mpdparser_get_rep_idx_with_min_bandwidth (GList * Representations)
2538 GList *list = NULL, *lowest = NULL;
2539 GstRepresentationNode *rep = NULL;
2540 gint lowest_bandwidth = -1;
2542 if (Representations == NULL)
2545 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2546 rep = (GstRepresentationNode *) list->data;
2547 if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
2549 lowest_bandwidth = rep->bandwidth;
2553 return lowest ? g_list_position (Representations, lowest) : -1;
2556 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
2558 gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
2559 gint max_bandwidth, gint max_video_width, gint max_video_height)
2561 GList *list = NULL, *best = NULL, *min_rep = NULL;
2562 GstRepresentationNode *representation;
2563 gint best_bandwidth = 0, min_bandwidth = 0;
2565 GST_DEBUG ("[b]%d [w]%d [h]%d", max_bandwidth, max_video_width, max_video_height);
2567 if (Representations == NULL)
2570 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2571 representation = (GstRepresentationNode *) list->data;
2572 if (!representation)
2575 if (max_video_width > DEFAULT_ADAPTIVE_VARIANT
2576 && representation->RepresentationBase->width > max_video_width)
2579 if (max_video_height > DEFAULT_ADAPTIVE_VARIANT
2580 && representation->RepresentationBase->height > max_video_height)
2583 if (representation->bandwidth <= max_bandwidth &&
2584 representation->bandwidth > best_bandwidth) {
2586 best_bandwidth = representation->bandwidth;
2588 if ((!min_rep) || (min_bandwidth == 0) ||
2589 (min_bandwidth >= representation->bandwidth)) {
2591 min_bandwidth = representation->bandwidth;
2599 return best ? g_list_position (Representations, best) : -1;
2603 gst_mpdparser_get_rep_idx_with_max_bandwidth (GList * Representations,
2604 gint max_bandwidth, gint max_video_width, gint max_video_height, gint
2605 max_video_framerate_n, gint max_video_framerate_d)
2607 GList *list = NULL, *best = NULL;
2608 GstRepresentationNode *representation;
2609 gint best_bandwidth = 0;
2611 GST_DEBUG ("max_bandwidth = %i", max_bandwidth);
2613 if (Representations == NULL)
2616 if (max_bandwidth <= 0) /* 0 => get lowest representation available */
2617 return gst_mpdparser_get_rep_idx_with_min_bandwidth (Representations);
2619 for (list = g_list_first (Representations); list; list = g_list_next (list)) {
2620 GstFrameRate *framerate = NULL;
2622 representation = (GstRepresentationNode *) list->data;
2624 /* FIXME: Really? */
2625 if (!representation)
2628 framerate = representation->RepresentationBase->frameRate;
2630 framerate = representation->RepresentationBase->maxFrameRate;
2632 if (framerate && max_video_framerate_n > 0) {
2633 if (gst_util_fraction_compare (framerate->num, framerate->den,
2634 max_video_framerate_n, max_video_framerate_d) > 0)
2638 if (max_video_width > 0
2639 && representation->RepresentationBase->width > max_video_width)
2641 if (max_video_height > 0
2642 && representation->RepresentationBase->height > max_video_height)
2645 if (representation->bandwidth <= max_bandwidth &&
2646 representation->bandwidth > best_bandwidth) {
2648 best_bandwidth = representation->bandwidth;
2652 return best ? g_list_position (Representations, best) : -1;
2655 static GstSegmentListNode *
2656 gst_mpd_client_fetch_external_segment_list (GstMpdClient * client,
2657 GstPeriodNode * Period,
2658 GstAdaptationSetNode * AdaptationSet,
2659 GstRepresentationNode * Representation,
2660 GstSegmentListNode * parent, GstSegmentListNode * segment_list)
2662 GstFragment *download;
2663 GstBuffer *segment_list_buffer;
2666 xmlDocPtr doc = NULL;
2667 GstUri *base_uri, *uri;
2668 gchar *query = NULL;
2670 GstSegmentListNode *new_segment_list = NULL;
2672 /* ISO/IEC 23009-1:2014 5.5.3 4)
2673 * Remove nodes that resolve to nothing when resolving
2675 if (strcmp (segment_list->xlink_href,
2676 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
2680 if (!client->downloader) {
2684 /* Build absolute URI */
2686 /* Get base URI at the MPD level */
2688 gst_uri_from_string (client->
2689 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
2691 /* combine a BaseURL at the MPD level with the current base url */
2692 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
2694 /* combine a BaseURL at the Period level with the current base url */
2695 base_uri = combine_urls (base_uri, Period->BaseURLs, &query, 0);
2697 if (AdaptationSet) {
2698 /* combine a BaseURL at the AdaptationSet level with the current base url */
2699 base_uri = combine_urls (base_uri, AdaptationSet->BaseURLs, &query, 0);
2701 if (Representation) {
2702 /* combine a BaseURL at the Representation level with the current base url */
2703 base_uri = combine_urls (base_uri, Representation->BaseURLs, &query, 0);
2707 uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
2709 gst_uri_set_query_string (uri, query);
2711 uri_string = gst_uri_to_string (uri);
2712 gst_uri_unref (base_uri);
2713 gst_uri_unref (uri);
2715 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
2717 gst_uri_downloader_fetch_uri (client->downloader,
2718 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
2719 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
2722 gst_uri_downloader_fetch_uri (client->downloader,
2723 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
2725 g_free (uri_string);
2728 GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
2729 segment_list->xlink_href, err->message);
2730 g_clear_error (&err);
2734 segment_list_buffer = gst_fragment_get_buffer (download);
2735 g_object_unref (download);
2737 gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
2740 xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
2743 gst_buffer_unmap (segment_list_buffer, &map);
2744 gst_buffer_unref (segment_list_buffer);
2746 /* NOTE: ISO/IEC 23009-1:2014 5.3.9.3.2 is saying that one or multiple SegmentList
2747 * in external xml is allowed, however, multiple SegmentList does not make sense
2748 * because Period/AdaptationSet/Representation allow only one SegmentList */
2750 xmlNode *root_element = xmlDocGetRootElement (doc);
2752 if (root_element->type != XML_ELEMENT_NODE ||
2753 xmlStrcmp (root_element->name, (xmlChar *) "SegmentList") != 0) {
2757 gst_mpdparser_parse_segment_list_node (&new_segment_list, root_element,
2767 return new_segment_list;
2770 GST_ERROR ("Failed to parse segment list node XML");
2774 static GstSegmentListNode *
2775 gst_mpdparser_get_segment_list (GstMpdClient * client, GstPeriodNode * Period,
2776 GstAdaptationSetNode * AdaptationSet,
2777 GstRepresentationNode * Representation)
2779 GstSegmentListNode **SegmentList;
2780 GstSegmentListNode *ParentSegmentList = NULL;
2782 if (Representation && Representation->SegmentList) {
2783 SegmentList = &Representation->SegmentList;
2784 ParentSegmentList = AdaptationSet->SegmentList;
2785 } else if (AdaptationSet && AdaptationSet->SegmentList) {
2786 SegmentList = &AdaptationSet->SegmentList;
2787 ParentSegmentList = Period->SegmentList;
2788 Representation = NULL;
2790 Representation = NULL;
2791 AdaptationSet = NULL;
2792 SegmentList = &Period->SegmentList;
2795 /* Resolve external segment list here. */
2796 if (*SegmentList && (*SegmentList)->xlink_href) {
2797 GstSegmentListNode *new_segment_list;
2799 /* TODO: Use SegmentList of parent if
2800 * - Parent has its own SegmentList
2801 * - Fail to get SegmentList from external xml
2804 gst_mpd_client_fetch_external_segment_list (client, Period,
2805 AdaptationSet, Representation, ParentSegmentList, *SegmentList);
2807 gst_mpdparser_free_segment_list_node (*SegmentList);
2808 *SegmentList = new_segment_list;
2811 return *SegmentList;
2814 /* memory management functions */
2816 gst_mpdparser_free_mpd_node (GstMPDNode * mpd_node)
2819 if (mpd_node->default_namespace)
2820 xmlFree (mpd_node->default_namespace);
2821 if (mpd_node->namespace_xsi)
2822 xmlFree (mpd_node->namespace_xsi);
2823 if (mpd_node->namespace_ext)
2824 xmlFree (mpd_node->namespace_ext);
2825 if (mpd_node->schemaLocation)
2826 xmlFree (mpd_node->schemaLocation);
2828 xmlFree (mpd_node->id);
2829 if (mpd_node->profiles)
2830 xmlFree (mpd_node->profiles);
2831 if (mpd_node->availabilityStartTime)
2832 gst_date_time_unref (mpd_node->availabilityStartTime);
2833 if (mpd_node->availabilityEndTime)
2834 gst_date_time_unref (mpd_node->availabilityEndTime);
2835 g_list_free_full (mpd_node->ProgramInfo,
2836 (GDestroyNotify) gst_mpdparser_free_prog_info_node);
2837 g_list_free_full (mpd_node->BaseURLs,
2838 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2839 g_list_free_full (mpd_node->Locations, (GDestroyNotify) xmlFree);
2840 g_list_free_full (mpd_node->Periods,
2841 (GDestroyNotify) gst_mpdparser_free_period_node);
2842 g_list_free_full (mpd_node->Metrics,
2843 (GDestroyNotify) gst_mpdparser_free_metrics_node);
2844 g_list_free_full (mpd_node->UTCTiming,
2845 (GDestroyNotify) gst_mpdparser_free_utctiming_node);
2846 g_slice_free (GstMPDNode, mpd_node);
2851 gst_mpdparser_free_prog_info_node (GstProgramInformationNode * prog_info_node)
2853 if (prog_info_node) {
2854 if (prog_info_node->lang)
2855 xmlFree (prog_info_node->lang);
2856 if (prog_info_node->moreInformationURL)
2857 xmlFree (prog_info_node->moreInformationURL);
2858 if (prog_info_node->Title)
2859 xmlFree (prog_info_node->Title);
2860 if (prog_info_node->Source)
2861 xmlFree (prog_info_node->Source);
2862 if (prog_info_node->Copyright)
2863 xmlFree (prog_info_node->Copyright);
2864 g_slice_free (GstProgramInformationNode, prog_info_node);
2869 gst_mpdparser_free_metrics_node (GstMetricsNode * metrics_node)
2872 if (metrics_node->metrics)
2873 xmlFree (metrics_node->metrics);
2874 g_list_free_full (metrics_node->MetricsRanges,
2875 (GDestroyNotify) gst_mpdparser_free_metrics_range_node);
2876 g_slice_free (GstMetricsNode, metrics_node);
2881 gst_mpdparser_free_metrics_range_node (GstMetricsRangeNode * metrics_range_node)
2883 if (metrics_range_node) {
2884 g_slice_free (GstMetricsRangeNode, metrics_range_node);
2889 gst_mpdparser_free_period_node (GstPeriodNode * period_node)
2892 if (period_node->id)
2893 xmlFree (period_node->id);
2894 gst_mpdparser_free_seg_base_type_ext (period_node->SegmentBase);
2895 gst_mpdparser_free_segment_list_node (period_node->SegmentList);
2896 gst_mpdparser_free_segment_template_node (period_node->SegmentTemplate);
2897 g_list_free_full (period_node->AdaptationSets,
2898 (GDestroyNotify) gst_mpdparser_free_adaptation_set_node);
2899 g_list_free_full (period_node->Subsets,
2900 (GDestroyNotify) gst_mpdparser_free_subset_node);
2901 g_list_free_full (period_node->BaseURLs,
2902 (GDestroyNotify) gst_mpdparser_free_base_url_node);
2903 if (period_node->xlink_href)
2904 xmlFree (period_node->xlink_href);
2905 g_slice_free (GstPeriodNode, period_node);
2910 gst_mpdparser_free_subset_node (GstSubsetNode * subset_node)
2913 if (subset_node->contains)
2914 xmlFree (subset_node->contains);
2915 g_slice_free (GstSubsetNode, subset_node);
2920 gst_mpdparser_free_segment_template_node (GstSegmentTemplateNode *
2921 segment_template_node)
2923 if (segment_template_node) {
2924 if (segment_template_node->media)
2925 xmlFree (segment_template_node->media);
2926 if (segment_template_node->index)
2927 xmlFree (segment_template_node->index);
2928 if (segment_template_node->initialization)
2929 xmlFree (segment_template_node->initialization);
2930 if (segment_template_node->bitstreamSwitching)
2931 xmlFree (segment_template_node->bitstreamSwitching);
2932 /* MultipleSegmentBaseType extension */
2933 gst_mpdparser_free_mult_seg_base_type_ext
2934 (segment_template_node->MultSegBaseType);
2935 g_slice_free (GstSegmentTemplateNode, segment_template_node);
2940 gst_mpdparser_free_representation_base_type (GstRepresentationBaseType *
2941 representation_base)
2943 if (representation_base) {
2944 if (representation_base->profiles)
2945 xmlFree (representation_base->profiles);
2946 g_slice_free (GstRatio, representation_base->sar);
2947 g_slice_free (GstFrameRate, representation_base->frameRate);
2948 g_slice_free (GstFrameRate, representation_base->minFrameRate);
2949 g_slice_free (GstFrameRate, representation_base->maxFrameRate);
2950 if (representation_base->audioSamplingRate)
2951 xmlFree (representation_base->audioSamplingRate);
2952 if (representation_base->mimeType)
2953 xmlFree (representation_base->mimeType);
2954 if (representation_base->segmentProfiles)
2955 xmlFree (representation_base->segmentProfiles);
2956 if (representation_base->codecs)
2957 xmlFree (representation_base->codecs);
2958 if (representation_base->scanType)
2959 xmlFree (representation_base->scanType);
2960 g_list_free_full (representation_base->FramePacking,
2961 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2962 g_list_free_full (representation_base->AudioChannelConfiguration,
2963 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2964 g_list_free_full (representation_base->ContentProtection,
2965 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2966 g_slice_free (GstRepresentationBaseType, representation_base);
2971 gst_mpdparser_free_adaptation_set_node (GstAdaptationSetNode *
2972 adaptation_set_node)
2974 if (adaptation_set_node) {
2975 if (adaptation_set_node->lang)
2976 xmlFree (adaptation_set_node->lang);
2977 if (adaptation_set_node->contentType)
2978 xmlFree (adaptation_set_node->contentType);
2979 g_slice_free (GstRatio, adaptation_set_node->par);
2980 g_slice_free (GstConditionalUintType,
2981 adaptation_set_node->segmentAlignment);
2982 g_slice_free (GstConditionalUintType,
2983 adaptation_set_node->subsegmentAlignment);
2984 g_list_free_full (adaptation_set_node->Accessibility,
2985 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2986 g_list_free_full (adaptation_set_node->Role,
2987 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2988 g_list_free_full (adaptation_set_node->Rating,
2989 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2990 g_list_free_full (adaptation_set_node->Viewpoint,
2991 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
2992 gst_mpdparser_free_representation_base_type
2993 (adaptation_set_node->RepresentationBase);
2994 gst_mpdparser_free_seg_base_type_ext (adaptation_set_node->SegmentBase);
2995 gst_mpdparser_free_segment_list_node (adaptation_set_node->SegmentList);
2996 gst_mpdparser_free_segment_template_node
2997 (adaptation_set_node->SegmentTemplate);
2998 g_list_free_full (adaptation_set_node->BaseURLs,
2999 (GDestroyNotify) gst_mpdparser_free_base_url_node);
3000 g_list_free_full (adaptation_set_node->Representations,
3001 (GDestroyNotify) gst_mpdparser_free_representation_node);
3002 g_list_free_full (adaptation_set_node->ContentComponents,
3003 (GDestroyNotify) gst_mpdparser_free_content_component_node);
3004 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
3005 g_list_free_full (adaptation_set_node->VariantInfo,
3006 (GDestroyNotify) g_free);
3008 if (adaptation_set_node->xlink_href)
3009 xmlFree (adaptation_set_node->xlink_href);
3010 g_slice_free (GstAdaptationSetNode, adaptation_set_node);
3015 gst_mpdparser_free_representation_node (GstRepresentationNode *
3016 representation_node)
3018 if (representation_node) {
3019 if (representation_node->id)
3020 xmlFree (representation_node->id);
3021 g_strfreev (representation_node->dependencyId);
3022 g_strfreev (representation_node->mediaStreamStructureId);
3023 gst_mpdparser_free_representation_base_type
3024 (representation_node->RepresentationBase);
3025 g_list_free_full (representation_node->SubRepresentations,
3026 (GDestroyNotify) gst_mpdparser_free_subrepresentation_node);
3027 gst_mpdparser_free_seg_base_type_ext (representation_node->SegmentBase);
3028 gst_mpdparser_free_segment_template_node
3029 (representation_node->SegmentTemplate);
3030 gst_mpdparser_free_segment_list_node (representation_node->SegmentList);
3031 g_list_free_full (representation_node->BaseURLs,
3032 (GDestroyNotify) gst_mpdparser_free_base_url_node);
3033 g_slice_free (GstRepresentationNode, representation_node);
3038 gst_mpdparser_free_subrepresentation_node (GstSubRepresentationNode *
3042 gst_mpdparser_free_representation_base_type
3043 (subrep_node->RepresentationBase);
3044 if (subrep_node->dependencyLevel)
3045 xmlFree (subrep_node->dependencyLevel);
3046 g_strfreev (subrep_node->contentComponent);
3047 g_slice_free (GstSubRepresentationNode, subrep_node);
3052 gst_mpdparser_free_s_node (GstSNode * s_node)
3055 g_slice_free (GstSNode, s_node);
3059 static GstSegmentTimelineNode *
3060 gst_mpdparser_segment_timeline_node_new (void)
3062 GstSegmentTimelineNode *node = g_slice_new0 (GstSegmentTimelineNode);
3064 g_queue_init (&node->S);
3070 gst_mpdparser_free_segment_timeline_node (GstSegmentTimelineNode * seg_timeline)
3073 g_queue_foreach (&seg_timeline->S, (GFunc) gst_mpdparser_free_s_node, NULL);
3074 g_queue_clear (&seg_timeline->S);
3075 g_slice_free (GstSegmentTimelineNode, seg_timeline);
3080 gst_mpdparser_free_url_type_node (GstURLType * url_type_node)
3082 if (url_type_node) {
3083 if (url_type_node->sourceURL)
3084 xmlFree (url_type_node->sourceURL);
3085 g_slice_free (GstRange, url_type_node->range);
3086 g_slice_free (GstURLType, url_type_node);
3091 gst_mpdparser_free_seg_base_type_ext (GstSegmentBaseType * seg_base_type)
3093 if (seg_base_type) {
3094 if (seg_base_type->indexRange)
3095 g_slice_free (GstRange, seg_base_type->indexRange);
3096 gst_mpdparser_free_url_type_node (seg_base_type->Initialization);
3097 gst_mpdparser_free_url_type_node (seg_base_type->RepresentationIndex);
3098 g_slice_free (GstSegmentBaseType, seg_base_type);
3103 gst_mpdparser_free_mult_seg_base_type_ext (GstMultSegmentBaseType *
3106 if (mult_seg_base_type) {
3107 /* SegmentBaseType extension */
3108 gst_mpdparser_free_seg_base_type_ext (mult_seg_base_type->SegBaseType);
3109 gst_mpdparser_free_segment_timeline_node
3110 (mult_seg_base_type->SegmentTimeline);
3111 gst_mpdparser_free_url_type_node (mult_seg_base_type->BitstreamSwitching);
3112 g_slice_free (GstMultSegmentBaseType, mult_seg_base_type);
3117 gst_mpdparser_free_segment_list_node (GstSegmentListNode * segment_list_node)
3119 if (segment_list_node) {
3120 g_list_free_full (segment_list_node->SegmentURL,
3121 (GDestroyNotify) gst_mpdparser_free_segment_url_node);
3122 /* MultipleSegmentBaseType extension */
3123 gst_mpdparser_free_mult_seg_base_type_ext
3124 (segment_list_node->MultSegBaseType);
3125 if (segment_list_node->xlink_href)
3126 xmlFree (segment_list_node->xlink_href);
3127 g_slice_free (GstSegmentListNode, segment_list_node);
3132 gst_mpdparser_free_segment_url_node (GstSegmentURLNode * segment_url)
3135 if (segment_url->media)
3136 xmlFree (segment_url->media);
3137 g_slice_free (GstRange, segment_url->mediaRange);
3138 if (segment_url->index)
3139 xmlFree (segment_url->index);
3140 g_slice_free (GstRange, segment_url->indexRange);
3141 g_slice_free (GstSegmentURLNode, segment_url);
3146 gst_mpdparser_free_base_url_node (GstBaseURL * base_url_node)
3148 if (base_url_node) {
3149 if (base_url_node->baseURL)
3150 xmlFree (base_url_node->baseURL);
3151 if (base_url_node->serviceLocation)
3152 xmlFree (base_url_node->serviceLocation);
3153 if (base_url_node->byteRange)
3154 xmlFree (base_url_node->byteRange);
3155 g_slice_free (GstBaseURL, base_url_node);
3160 gst_mpdparser_free_descriptor_type_node (GstDescriptorType * descriptor_type)
3162 if (descriptor_type) {
3163 if (descriptor_type->schemeIdUri)
3164 xmlFree (descriptor_type->schemeIdUri);
3165 if (descriptor_type->value)
3166 xmlFree (descriptor_type->value);
3167 g_slice_free (GstDescriptorType, descriptor_type);
3172 gst_mpdparser_free_content_component_node (GstContentComponentNode *
3173 content_component_node)
3175 if (content_component_node) {
3176 if (content_component_node->lang)
3177 xmlFree (content_component_node->lang);
3178 if (content_component_node->contentType)
3179 xmlFree (content_component_node->contentType);
3180 g_slice_free (GstRatio, content_component_node->par);
3181 g_list_free_full (content_component_node->Accessibility,
3182 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3183 g_list_free_full (content_component_node->Role,
3184 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3185 g_list_free_full (content_component_node->Rating,
3186 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3187 g_list_free_full (content_component_node->Viewpoint,
3188 (GDestroyNotify) gst_mpdparser_free_descriptor_type_node);
3189 g_slice_free (GstContentComponentNode, content_component_node);
3194 gst_mpdparser_free_utctiming_node (GstUTCTimingNode * timing_type)
3197 if (timing_type->urls)
3198 g_strfreev (timing_type->urls);
3199 g_slice_free (GstUTCTimingNode, timing_type);
3204 gst_mpdparser_free_stream_period (GstStreamPeriod * stream_period)
3206 if (stream_period) {
3207 g_slice_free (GstStreamPeriod, stream_period);
3212 gst_mpdparser_free_media_segment (GstMediaSegment * media_segment)
3214 if (media_segment) {
3215 g_slice_free (GstMediaSegment, media_segment);
3220 gst_mpdparser_init_active_stream_segments (GstActiveStream * stream)
3222 g_assert (stream->segments == NULL);
3223 stream->segments = g_ptr_array_new ();
3224 g_ptr_array_set_free_func (stream->segments,
3225 (GDestroyNotify) gst_mpdparser_free_media_segment);
3229 gst_mpdparser_free_active_stream (GstActiveStream * active_stream)
3231 if (active_stream) {
3232 g_free (active_stream->baseURL);
3233 active_stream->baseURL = NULL;
3234 g_free (active_stream->queryURL);
3235 active_stream->queryURL = NULL;
3236 if (active_stream->segments)
3237 g_ptr_array_unref (active_stream->segments);
3238 g_slice_free (GstActiveStream, active_stream);
3243 gst_mpdparser_get_mediaURL (GstActiveStream * stream,
3244 GstSegmentURLNode * segmentURL)
3246 const gchar *url_prefix;
3248 g_return_val_if_fail (stream != NULL, NULL);
3249 g_return_val_if_fail (segmentURL != NULL, NULL);
3251 url_prefix = segmentURL->media ? segmentURL->media : stream->baseURL;
3252 g_return_val_if_fail (url_prefix != NULL, NULL);
3254 return segmentURL->media;
3257 static const gchar *
3258 gst_mpdparser_get_initializationURL (GstActiveStream * stream,
3259 GstURLType * InitializationURL)
3261 const gchar *url_prefix;
3263 g_return_val_if_fail (stream != NULL, NULL);
3265 url_prefix = (InitializationURL
3266 && InitializationURL->sourceURL) ? InitializationURL->
3267 sourceURL : stream->baseURL;
3272 /* ISO/IEC 23009-1:2004 5.3.9.4.4 */
3274 validate_format (const gchar * format)
3276 const gchar *p = format;
3278 /* Check if it starts with % */
3279 if (!p || p[0] != '%')
3283 /* the spec mandates a format like %0[width]d */
3284 /* Following the %, we must have a 0 */
3288 /* Following the % must be a number starting with 0
3290 while (g_ascii_isdigit (*p))
3293 /* After any 0 and alphanumeric values, there must be a d.
3299 /* And then potentially more characters without any
3300 * further %, even if the spec does not mention this
3302 p = strchr (p, '%');
3310 promote_format_to_uint64 (const gchar * format)
3312 const gchar *p = format;
3313 gchar *promoted_format;
3315 /* Must be called with a validated format! */
3316 g_return_val_if_fail (validate_format (format), NULL);
3318 /* it starts with % */
3321 /* Following the % must be a 0, or any of d, x or u.
3322 * x and u are not part of the spec, but don't hurt us
3327 while (g_ascii_isdigit (*p))
3331 /* After any 0 and alphanumeric values, there must be a d.
3332 * Otherwise validation would have failed
3334 g_assert (p[0] == 'd');
3337 g_strdup_printf ("%.*s" G_GINT64_MODIFIER "%s", (gint) (p - format),
3340 return promoted_format;
3344 gst_mpdparser_validate_rfc1738_url (const char *s)
3348 (";:@&=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789$-_.+!*'(),%/",
3352 /* g_ascii_isdigit returns FALSE for NUL, and || is a short circuiting
3353 operator, so this is safe for strings ending before two hex digits */
3354 if (!g_ascii_isxdigit (s[1]) || !g_ascii_isxdigit (s[2]))
3364 gst_mpdparser_build_URL_from_template (const gchar * url_template,
3365 const gchar * id, guint number, guint bandwidth, guint64 time)
3367 static const gchar default_format[] = "%01d";
3368 gchar **tokens, *token, *ret;
3369 const gchar *format;
3372 g_return_val_if_fail (url_template != NULL, NULL);
3373 tokens = g_strsplit_set (url_template, "$", -1);
3375 GST_WARNING ("Scan of URL template failed!");
3378 num_tokens = g_strv_length (tokens);
3381 * each identifier is guarded by 2 $, which means that we must have an odd number of tokens
3382 * An even number of tokens means the string is not valid.
3384 if ((num_tokens & 1) == 0) {
3385 GST_ERROR ("Invalid number of tokens (%d). url_template is '%s'",
3386 num_tokens, url_template);
3387 g_strfreev (tokens);
3391 for (i = 0; i < num_tokens; i++) {
3393 format = default_format;
3395 /* the tokens to replace must be provided between $ characters, eg $token$
3396 * For a string like token0$token1$token2$token3$token4, only the odd number
3397 * tokens (1,3,...) must be parsed.
3404 if (!g_strcmp0 (token, "RepresentationID")) {
3405 if (!gst_mpdparser_validate_rfc1738_url (id))
3406 goto invalid_representation_id;
3408 tokens[i] = g_strdup_printf ("%s", id);
3410 } else if (!strncmp (token, "Number", 6)) {
3411 if (strlen (token) > 6) {
3412 format = token + 6; /* format tag */
3414 if (!validate_format (format))
3415 goto invalid_format;
3417 tokens[i] = g_strdup_printf (format, number);
3419 } else if (!strncmp (token, "Bandwidth", 9)) {
3420 if (strlen (token) > 9) {
3421 format = token + 9; /* format tag */
3423 if (!validate_format (format))
3424 goto invalid_format;
3426 tokens[i] = g_strdup_printf (format, bandwidth);
3428 } else if (!strncmp (token, "Time", 4)) {
3429 gchar *promoted_format;
3431 if (strlen (token) > 4) {
3432 format = token + 4; /* format tag */
3434 if (!validate_format (format))
3435 goto invalid_format;
3437 promoted_format = promote_format_to_uint64 (format);
3438 tokens[i] = g_strdup_printf (promoted_format, time);
3439 g_free (promoted_format);
3441 } else if (!g_strcmp0 (token, "")) {
3442 tokens[i] = g_strdup_printf ("%s", "$");
3445 /* unexpected identifier found between $ signs
3447 * "If the URL contains unescaped $ symbols which do not enclose a valid
3448 * identifier then the result of URL formation is undefined"
3450 goto invalid_format;
3454 ret = g_strjoinv (NULL, tokens);
3456 g_strfreev (tokens);
3462 GST_ERROR ("Invalid format '%s' in '%s'", format, token);
3464 g_strfreev (tokens);
3468 invalid_representation_id:
3471 ("Representation ID string '%s' has characters invalid in an RFC 1738 URL",
3474 g_strfreev (tokens);
3481 gst_mpd_client_get_period_index_at_time (GstMpdClient * client,
3485 guint period_idx = G_MAXUINT;
3488 GstDateTime *avail_start =
3489 gst_mpd_client_get_availability_start_time (client);
3490 GstStreamPeriod *stream_period;
3492 if (avail_start == NULL)
3495 time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
3496 gst_date_time_unref (avail_start);
3498 if (time_offset < 0)
3501 if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
3504 for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
3505 stream_period = iter->data;
3506 if (stream_period->start <= time_offset
3507 && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
3508 || stream_period->start + stream_period->duration > time_offset)) {
3517 static GstStreamPeriod *
3518 gst_mpdparser_get_stream_period (GstMpdClient * client)
3520 g_return_val_if_fail (client != NULL, NULL);
3521 g_return_val_if_fail (client->periods != NULL, NULL);
3523 return g_list_nth_data (client->periods, client->period_idx);
3527 gst_mpdparser_clone_range (GstRange * range)
3529 GstRange *clone = NULL;
3532 clone = g_slice_new0 (GstRange);
3533 clone->first_byte_pos = range->first_byte_pos;
3534 clone->last_byte_pos = range->last_byte_pos;
3541 gst_mpdparser_clone_URL (GstURLType * url)
3544 GstURLType *clone = NULL;
3547 clone = g_slice_new0 (GstURLType);
3548 if (url->sourceURL) {
3549 clone->sourceURL = xmlMemStrdup (url->sourceURL);
3551 clone->range = gst_mpdparser_clone_range (url->range);
3558 * Combine a base url with the current stream base url from the list of
3559 * baseURLs. Takes ownership of base and returns a new base.
3562 combine_urls (GstUri * base, GList * list, gchar ** query, guint idx)
3564 GstBaseURL *baseURL;
3568 baseURL = g_list_nth_data (list, idx);
3570 baseURL = list->data;
3573 ret = gst_uri_from_string_with_base (base, baseURL->baseURL);
3574 gst_uri_unref (base);
3578 *query = gst_uri_get_query_string (ret);
3580 ret = gst_uri_make_writable (ret);
3581 gst_uri_set_query_table (ret, NULL);
3589 /* select a stream and extract the baseURL (if present) */
3591 gst_mpdparser_parse_baseURL (GstMpdClient * client, GstActiveStream * stream,
3594 GstStreamPeriod *stream_period;
3595 static const gchar empty[] = "";
3599 g_return_val_if_fail (stream != NULL, g_strdup (empty));
3600 stream_period = gst_mpdparser_get_stream_period (client);
3601 g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
3602 g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
3604 /* NULLify query return before we start */
3608 /* initialise base url */
3610 gst_uri_from_string (client->
3611 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
3613 /* combine a BaseURL at the MPD level with the current base url */
3615 combine_urls (abs_url, client->mpd_node->BaseURLs, query,
3616 stream->baseURL_idx);
3618 /* combine a BaseURL at the Period level with the current base url */
3620 combine_urls (abs_url, stream_period->period->BaseURLs, query,
3621 stream->baseURL_idx);
3623 GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
3624 stream->cur_adapt_set->contentType);
3625 /* combine a BaseURL at the AdaptationSet level with the current base url */
3627 combine_urls (abs_url, stream->cur_adapt_set->BaseURLs, query,
3628 stream->baseURL_idx);
3630 /* combine a BaseURL at the Representation level with the current base url */
3632 combine_urls (abs_url, stream->cur_representation->BaseURLs, query,
3633 stream->baseURL_idx);
3635 ret = gst_uri_to_string (abs_url);
3636 gst_uri_unref (abs_url);
3642 gst_mpd_client_get_segment_duration (GstMpdClient * client,
3643 GstActiveStream * stream, guint64 * scale_dur)
3645 GstStreamPeriod *stream_period;
3646 GstMultSegmentBaseType *base = NULL;
3647 GstClockTime duration = 0;
3649 g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
3650 stream_period = gst_mpdparser_get_stream_period (client);
3651 g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
3653 if (stream->cur_segment_list) {
3654 base = stream->cur_segment_list->MultSegBaseType;
3655 } else if (stream->cur_seg_template) {
3656 base = stream->cur_seg_template->MultSegBaseType;
3659 if (base == NULL || base->SegBaseType == NULL) {
3660 /* this may happen when we have a single segment */
3661 duration = stream_period->duration;
3663 *scale_dur = duration;
3665 /* duration is guint so this cannot overflow */
3666 duration = base->duration * GST_SECOND;
3668 *scale_dur = duration;
3669 duration /= base->SegBaseType->timescale;
3675 /*****************************/
3676 /******* API functions *******/
3677 /*****************************/
3680 gst_mpd_client_new (void)
3682 GstMpdClient *client;
3684 client = g_new0 (GstMpdClient, 1);
3690 gst_active_streams_free (GstMpdClient * client)
3692 if (client->active_streams) {
3693 g_list_foreach (client->active_streams,
3694 (GFunc) gst_mpdparser_free_active_stream, NULL);
3695 g_list_free (client->active_streams);
3696 client->active_streams = NULL;
3701 gst_mpd_client_free (GstMpdClient * client)
3703 g_return_if_fail (client != NULL);
3705 if (client->mpd_node)
3706 gst_mpdparser_free_mpd_node (client->mpd_node);
3708 if (client->periods) {
3709 g_list_free_full (client->periods,
3710 (GDestroyNotify) gst_mpdparser_free_stream_period);
3713 gst_active_streams_free (client);
3715 g_free (client->mpd_uri);
3716 client->mpd_uri = NULL;
3717 g_free (client->mpd_base_uri);
3718 client->mpd_base_uri = NULL;
3720 if (client->downloader)
3721 gst_object_unref (client->downloader);
3722 client->downloader = NULL;
3728 gst_mpd_client_set_uri_downloader (GstMpdClient * client,
3729 GstUriDownloader * downloader)
3731 if (client->downloader)
3732 gst_object_unref (client->downloader);
3733 client->downloader = gst_object_ref (downloader);
3737 gst_mpd_client_check_profiles (GstMpdClient * client)
3739 GST_DEBUG ("Profiles: %s",
3740 client->mpd_node->profiles ? client->mpd_node->profiles : "<none>");
3742 if (!client->mpd_node->profiles)
3745 if (g_strstr_len (client->mpd_node->profiles, -1,
3746 "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
3747 client->profile_isoff_ondemand = TRUE;
3748 GST_DEBUG ("Found ISOFF on demand profile (2011)");
3753 gst_mpd_client_fetch_on_load_external_resources (GstMpdClient * client)
3757 for (l = client->mpd_node->Periods; l; /* explicitly advanced below */ ) {
3758 GstPeriodNode *period = l->data;
3761 if (period->xlink_href && period->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3762 GList *new_periods, *prev, *next;
3764 new_periods = gst_mpd_client_fetch_external_period (client, period);
3767 client->mpd_node->Periods =
3768 g_list_delete_link (client->mpd_node->Periods, l);
3769 gst_mpdparser_free_period_node (period);
3772 /* Get new next node, we will insert before this */
3776 next = client->mpd_node->Periods;
3778 while (new_periods) {
3779 client->mpd_node->Periods =
3780 g_list_insert_before (client->mpd_node->Periods, next,
3782 new_periods = g_list_delete_link (new_periods, new_periods);
3786 /* Update our iterator to the first new period if any, or the next */
3790 l = client->mpd_node->Periods;
3795 if (period->SegmentList && period->SegmentList->xlink_href
3796 && period->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3797 GstSegmentListNode *new_segment_list;
3800 gst_mpd_client_fetch_external_segment_list (client, period, NULL,
3801 NULL, NULL, period->SegmentList);
3803 gst_mpdparser_free_segment_list_node (period->SegmentList);
3804 period->SegmentList = new_segment_list;
3807 for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
3808 GstAdaptationSetNode *adapt_set = m->data;
3811 if (adapt_set->xlink_href
3812 && adapt_set->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3813 GList *new_adapt_sets, *prev, *next;
3816 gst_mpd_client_fetch_external_adaptation_set (client, period,
3820 period->AdaptationSets = g_list_delete_link (period->AdaptationSets, l);
3821 gst_mpdparser_free_adaptation_set_node (adapt_set);
3824 /* Get new next node, we will insert before this */
3828 next = period->AdaptationSets;
3830 while (new_adapt_sets) {
3831 period->AdaptationSets =
3832 g_list_insert_before (period->AdaptationSets, next,
3833 new_adapt_sets->data);
3834 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
3838 /* Update our iterator to the first new adapt_set if any, or the next */
3842 l = period->AdaptationSets;
3847 if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
3848 && adapt_set->SegmentList->actuate == GST_XLINK_ACTUATE_ON_LOAD) {
3849 GstSegmentListNode *new_segment_list;
3852 gst_mpd_client_fetch_external_segment_list (client, period,
3853 adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
3855 gst_mpdparser_free_segment_list_node (adapt_set->SegmentList);
3856 adapt_set->SegmentList = new_segment_list;
3859 for (n = adapt_set->Representations; n; n = n->next) {
3860 GstRepresentationNode *representation = n->data;
3862 if (representation->SegmentList
3863 && representation->SegmentList->xlink_href
3864 && representation->SegmentList->actuate ==
3865 GST_XLINK_ACTUATE_ON_LOAD) {
3867 GstSegmentListNode *new_segment_list;
3870 gst_mpd_client_fetch_external_segment_list (client, period,
3871 adapt_set, representation, adapt_set->SegmentList,
3872 representation->SegmentList);
3874 gst_mpdparser_free_segment_list_node (representation->SegmentList);
3875 representation->SegmentList = new_segment_list;
3888 gst_mpd_parse (GstMpdClient * client, const gchar * data, gint size)
3890 gboolean ret = FALSE;
3894 xmlNode *root_element = NULL;
3896 GST_DEBUG ("MPD file fully buffered, start parsing...");
3898 /* parse the complete MPD file into a tree (using the libxml2 default parser API) */
3900 /* this initialize the library and check potential ABI mismatches
3901 * between the version it was compiled for and the actual shared
3904 LIBXML_TEST_VERSION;
3906 /* parse "data" into a document (which is a libxml2 tree structure xmlDoc) */
3907 doc = xmlReadMemory (data, size, "noname.xml", NULL, XML_PARSE_NONET);
3909 GST_ERROR ("failed to parse the MPD file");
3912 /* get the root element node */
3913 root_element = xmlDocGetRootElement (doc);
3915 if (root_element->type != XML_ELEMENT_NODE
3916 || xmlStrcmp (root_element->name, (xmlChar *) "MPD") != 0) {
3918 ("can not find the root element MPD, failed to parse the MPD file");
3919 ret = FALSE; /* used to return TRUE before, but this seems wrong */
3921 /* now we can parse the MPD root node and all children nodes, recursively */
3922 ret = gst_mpdparser_parse_root_node (&client->mpd_node, root_element);
3924 /* free the document */
3929 gst_mpd_client_check_profiles (client);
3930 gst_mpd_client_fetch_on_load_external_resources (client);
3938 gst_mpdparser_get_baseURL (GstMpdClient * client, guint indexStream)
3940 GstActiveStream *stream;
3942 g_return_val_if_fail (client != NULL, NULL);
3943 g_return_val_if_fail (client->active_streams != NULL, NULL);
3944 stream = g_list_nth_data (client->active_streams, indexStream);
3945 g_return_val_if_fail (stream != NULL, NULL);
3947 return stream->baseURL;
3951 gst_mpdparser_get_segment_end_time (GstMpdClient * client, GPtrArray * segments,
3952 const GstMediaSegment * segment, gint index)
3954 const GstStreamPeriod *stream_period;
3957 if (segment->repeat >= 0)
3958 return segment->start + (segment->repeat + 1) * segment->duration;
3960 if (index < segments->len - 1) {
3961 const GstMediaSegment *next_segment =
3962 g_ptr_array_index (segments, index + 1);
3963 end = next_segment->start;
3965 stream_period = gst_mpdparser_get_stream_period (client);
3966 end = stream_period->start + stream_period->duration;
3972 gst_mpd_client_add_media_segment (GstActiveStream * stream,
3973 GstSegmentURLNode * url_node, guint number, gint repeat,
3974 guint64 scale_start, guint64 scale_duration,
3975 GstClockTime start, GstClockTime duration)
3977 GstMediaSegment *media_segment;
3979 g_return_val_if_fail (stream->segments != NULL, FALSE);
3981 media_segment = g_slice_new0 (GstMediaSegment);
3983 media_segment->SegmentURL = url_node;
3984 media_segment->number = number;
3985 media_segment->scale_start = scale_start;
3986 media_segment->scale_duration = scale_duration;
3987 media_segment->start = start;
3988 media_segment->duration = duration;
3989 media_segment->repeat = repeat;
3991 g_ptr_array_add (stream->segments, media_segment);
3992 GST_LOG ("Added new segment: number %d, repeat %d, "
3993 "ts: %" GST_TIME_FORMAT ", dur: %"
3994 GST_TIME_FORMAT, number, repeat,
3995 GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
4001 gst_mpd_client_stream_update_presentation_time_offset (GstMpdClient * client,
4002 GstActiveStream * stream)
4004 GstSegmentBaseType *segbase = NULL;
4006 /* Find the used segbase */
4007 if (stream->cur_segment_list) {
4008 segbase = stream->cur_segment_list->MultSegBaseType->SegBaseType;
4009 } else if (stream->cur_seg_template) {
4010 segbase = stream->cur_seg_template->MultSegBaseType->SegBaseType;
4011 } else if (stream->cur_segment_base) {
4012 segbase = stream->cur_segment_base;
4016 /* Avoid overflows */
4017 stream->presentationTimeOffset =
4018 gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
4019 segbase->timescale);
4021 stream->presentationTimeOffset = 0;
4024 GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
4025 GST_TIME_ARGS (stream->presentationTimeOffset));
4029 gst_mpd_client_setup_representation (GstMpdClient * client,
4030 GstActiveStream * stream, GstRepresentationNode * representation)
4032 GstStreamPeriod *stream_period;
4034 GstClockTime PeriodStart, PeriodEnd, start_time, duration;
4038 if (stream->cur_adapt_set == NULL) {
4039 GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
4043 rep_list = stream->cur_adapt_set->Representations;
4044 stream->cur_representation = representation;
4045 stream->representation_idx = g_list_index (rep_list, representation);
4047 /* clean the old segment list, if any */
4048 if (stream->segments) {
4049 g_ptr_array_unref (stream->segments);
4050 stream->segments = NULL;
4053 stream_period = gst_mpdparser_get_stream_period (client);
4054 g_return_val_if_fail (stream_period != NULL, FALSE);
4055 g_return_val_if_fail (stream_period->period != NULL, FALSE);
4057 PeriodStart = stream_period->start;
4058 if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
4059 PeriodEnd = stream_period->start + stream_period->duration;
4061 PeriodEnd = GST_CLOCK_TIME_NONE;
4063 GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
4064 GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
4066 if (representation->SegmentBase != NULL
4067 || representation->SegmentList != NULL) {
4070 /* We have a fixed list of segments for any of the cases here,
4071 * init the segments list */
4072 gst_mpdparser_init_active_stream_segments (stream);
4074 /* get the first segment_base of the selected representation */
4075 if ((stream->cur_segment_base =
4076 gst_mpdparser_get_segment_base (stream_period->period,
4077 stream->cur_adapt_set, representation)) == NULL) {
4078 GST_DEBUG ("No useful SegmentBase node for the current Representation");
4081 /* get the first segment_list of the selected representation */
4082 if ((stream->cur_segment_list =
4083 gst_mpdparser_get_segment_list (client, stream_period->period,
4084 stream->cur_adapt_set, representation)) == NULL) {
4085 GST_DEBUG ("No useful SegmentList node for the current Representation");
4086 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4087 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4088 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
4092 /* build the list of GstMediaSegment nodes from the SegmentList node */
4093 SegmentURL = stream->cur_segment_list->SegmentURL;
4094 if (SegmentURL == NULL) {
4096 ("No valid list of SegmentURL nodes in the MPD file, aborting...");
4100 /* build segment list */
4101 i = stream->cur_segment_list->MultSegBaseType->startNumber;
4105 GST_LOG ("Building media segment list using a SegmentList node");
4106 if (stream->cur_segment_list->MultSegBaseType->SegmentTimeline) {
4107 GstSegmentTimelineNode *timeline;
4111 timeline = stream->cur_segment_list->MultSegBaseType->SegmentTimeline;
4112 for (list = g_queue_peek_head_link (&timeline->S); list;
4113 list = g_list_next (list)) {
4116 S = (GstSNode *) list->data;
4117 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
4118 G_GUINT64_FORMAT, S->d, S->r, S->t);
4120 stream->cur_segment_list->MultSegBaseType->SegBaseType->timescale;
4121 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4125 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
4130 ("SegmentTimeline does not have a matching SegmentURL, aborting...");
4134 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4135 S->r, start, S->d, start_time, duration)) {
4139 start_time += duration * (S->r + 1);
4140 start += S->d * (S->r + 1);
4141 SegmentURL = g_list_next (SegmentURL);
4147 gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
4148 if (!GST_CLOCK_TIME_IS_VALID (duration))
4151 while (SegmentURL) {
4152 if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
4153 0, start, scale_dur, start_time, duration)) {
4158 start_time += duration;
4159 SegmentURL = g_list_next (SegmentURL);
4164 if (representation->SegmentTemplate != NULL) {
4165 stream->cur_seg_template = representation->SegmentTemplate;
4166 } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
4167 stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
4168 } else if (stream_period->period->SegmentTemplate != NULL) {
4169 stream->cur_seg_template = stream_period->period->SegmentTemplate;
4172 if (stream->cur_seg_template == NULL
4173 || stream->cur_seg_template->MultSegBaseType == NULL) {
4175 gst_mpdparser_init_active_stream_segments (stream);
4176 /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
4177 if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
4178 PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
4182 GstMultSegmentBaseType *mult_seg =
4183 stream->cur_seg_template->MultSegBaseType;
4184 /* build segment list */
4185 i = mult_seg->startNumber;
4189 GST_LOG ("Building media segment list using this template: %s",
4190 stream->cur_seg_template->media);
4192 if (mult_seg->SegmentTimeline) {
4193 GstSegmentTimelineNode *timeline;
4197 timeline = mult_seg->SegmentTimeline;
4198 gst_mpdparser_init_active_stream_segments (stream);
4199 for (list = g_queue_peek_head_link (&timeline->S); list;
4200 list = g_list_next (list)) {
4203 S = (GstSNode *) list->data;
4204 GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
4205 G_GUINT64_FORMAT, S->d, S->r, S->t);
4206 timescale = mult_seg->SegBaseType->timescale;
4207 duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
4210 start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale);
4213 if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
4214 S->d, start_time, duration)) {
4218 start += S->d * (S->r + 1);
4219 start_time += duration * (S->r + 1);
4222 /* NOP - The segment is created on demand with the template, no need
4223 * to build a list */
4228 /* clip duration of segments to stop at period end */
4229 if (stream->segments && stream->segments->len) {
4230 if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
4233 for (n = 0; n < stream->segments->len; ++n) {
4234 GstMediaSegment *media_segment =
4235 g_ptr_array_index (stream->segments, n);
4236 if (media_segment) {
4237 if (media_segment->start + media_segment->duration >
4238 PeriodEnd - PeriodStart) {
4239 GstClockTime stop = PeriodEnd - PeriodStart;
4240 if (n < stream->segments->len - 1) {
4241 GstMediaSegment *next_segment =
4242 g_ptr_array_index (stream->segments, n + 1);
4243 if (next_segment && next_segment->start < PeriodEnd - PeriodStart)
4244 stop = next_segment->start;
4246 media_segment->duration =
4247 media_segment->start > stop ? 0 : stop - media_segment->start;
4248 GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
4249 GST_TIME_ARGS (media_segment->duration));
4251 /* If the segment was clipped entirely, we discard it and all
4252 * subsequent ones */
4253 if (media_segment->duration == 0) {
4254 GST_WARNING ("Discarding %u segments outside period",
4255 stream->segments->len - n);
4256 /* _set_size should properly unref elements */
4257 g_ptr_array_set_size (stream->segments, n);
4264 #ifndef GST_DISABLE_GST_DEBUG
4265 if (stream->segments->len > 0) {
4266 GstMediaSegment *last_media_segment =
4267 g_ptr_array_index (stream->segments, stream->segments->len - 1);
4268 GST_LOG ("Built a list of %d segments", last_media_segment->number);
4270 GST_LOG ("All media segments were clipped");
4275 g_free (stream->baseURL);
4276 g_free (stream->queryURL);
4278 gst_mpdparser_parse_baseURL (client, stream, &stream->queryURL);
4280 gst_mpd_client_stream_update_presentation_time_offset (client, stream);
4285 #define CUSTOM_WRAPPER_START "<custom_wrapper>"
4286 #define CUSTOM_WRAPPER_END "</custom_wrapper>"
4289 gst_mpd_client_fetch_external_period (GstMpdClient * client,
4290 GstPeriodNode * period_node)
4292 GstFragment *download;
4293 GstAdapter *adapter;
4294 GstBuffer *period_buffer;
4296 xmlDocPtr doc = NULL;
4297 GstUri *base_uri, *uri;
4298 gchar *query = NULL;
4299 gchar *uri_string, *wrapper;
4300 GList *new_periods = NULL;
4303 /* ISO/IEC 23009-1:2014 5.5.3 4)
4304 * Remove nodes that resolve to nothing when resolving
4306 if (strcmp (period_node->xlink_href,
4307 "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4311 if (!client->downloader) {
4315 /* Build absolute URI */
4317 /* Get base URI at the MPD level */
4319 gst_uri_from_string (client->
4320 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4322 /* combine a BaseURL at the MPD level with the current base url */
4323 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4324 uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
4326 gst_uri_set_query_string (uri, query);
4328 uri_string = gst_uri_to_string (uri);
4329 gst_uri_unref (base_uri);
4330 gst_uri_unref (uri);
4332 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
4334 gst_uri_downloader_fetch_uri (client->downloader,
4335 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
4336 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
4339 gst_uri_downloader_fetch_uri (client->downloader,
4340 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4342 g_free (uri_string);
4345 GST_ERROR ("Failed to download external Period node at '%s': %s",
4346 period_node->xlink_href, err->message);
4347 g_clear_error (&err);
4351 period_buffer = gst_fragment_get_buffer (download);
4352 g_object_unref (download);
4354 /* external xml could have multiple period without root xmlNode.
4355 * To avoid xml parsing error caused by no root node, wrapping it with
4356 * custom root node */
4357 adapter = gst_adapter_new ();
4359 wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
4360 memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
4361 gst_adapter_push (adapter,
4362 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
4364 gst_adapter_push (adapter, period_buffer);
4366 wrapper = g_strdup (CUSTOM_WRAPPER_END);
4367 gst_adapter_push (adapter,
4368 gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
4370 data = gst_adapter_map (adapter, gst_adapter_available (adapter));
4373 xmlReadMemory (data, gst_adapter_available (adapter), "noname.xml", NULL,
4376 gst_adapter_unmap (adapter);
4377 gst_adapter_clear (adapter);
4378 gst_object_unref (adapter);
4381 xmlNode *root_element = xmlDocGetRootElement (doc);
4384 if (root_element->type != XML_ELEMENT_NODE)
4387 for (iter = root_element->children; iter; iter = iter->next) {
4388 if (iter->type == XML_ELEMENT_NODE) {
4389 if (xmlStrcmp (iter->name, (xmlChar *) "Period") == 0) {
4390 gst_mpdparser_parse_period_node (&new_periods, iter);
4407 GST_ERROR ("Failed to parse period node XML");
4410 g_list_free_full (new_periods,
4411 (GDestroyNotify) gst_mpdparser_free_period_node);
4418 gst_mpd_client_setup_media_presentation (GstMpdClient * client,
4419 GstClockTime time, gint period_idx, const gchar * period_id)
4421 GstStreamPeriod *stream_period;
4422 GstClockTime start, duration;
4425 gboolean ret = FALSE;
4427 g_return_val_if_fail (client != NULL, FALSE);
4428 g_return_val_if_fail (client->mpd_node != NULL, FALSE);
4430 /* Check if we set up the media presentation far enough already */
4431 for (list = client->periods; list; list = list->next) {
4432 GstStreamPeriod *stream_period = list->data;
4434 if ((time != GST_CLOCK_TIME_NONE
4435 && stream_period->duration != GST_CLOCK_TIME_NONE
4436 && stream_period->start + stream_period->duration >= time)
4437 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4440 if (period_idx != -1 && stream_period->number >= period_idx)
4443 if (period_id != NULL && stream_period->period->id != NULL
4444 && strcmp (stream_period->period->id, period_id) == 0)
4449 GST_DEBUG ("Building the list of Periods in the Media Presentation");
4450 /* clean the old period list, if any */
4451 /* TODO: In theory we could reuse the ones we have so far but that
4452 * seems more complicated than the overhead caused here
4454 if (client->periods) {
4455 g_list_foreach (client->periods,
4456 (GFunc) gst_mpdparser_free_stream_period, NULL);
4457 g_list_free (client->periods);
4458 client->periods = NULL;
4463 duration = GST_CLOCK_TIME_NONE;
4465 if (client->mpd_node->mediaPresentationDuration <= 0 &&
4466 client->mpd_node->mediaPresentationDuration != -1) {
4467 /* Invalid MPD file: MPD duration is negative or zero */
4471 for (list = client->mpd_node->Periods; list; /* explicitly advanced below */ ) {
4472 GstPeriodNode *period_node = list->data;
4473 GstPeriodNode *next_period_node = NULL;
4475 /* Download external period */
4476 if (period_node->xlink_href) {
4480 new_periods = gst_mpd_client_fetch_external_period (client, period_node);
4483 client->mpd_node->Periods =
4484 g_list_delete_link (client->mpd_node->Periods, list);
4485 gst_mpdparser_free_period_node (period_node);
4488 /* Get new next node, we will insert before this */
4492 next = client->mpd_node->Periods;
4494 while (new_periods) {
4495 client->mpd_node->Periods =
4496 g_list_insert_before (client->mpd_node->Periods, next,
4498 new_periods = g_list_delete_link (new_periods, new_periods);
4502 /* Update our iterator to the first new period if any, or the next */
4506 list = client->mpd_node->Periods;
4512 if (period_node->start != -1) {
4513 /* we have a regular period */
4514 /* start cannot be smaller than previous start */
4515 if (list != g_list_first (client->mpd_node->Periods)
4516 && start >= period_node->start * GST_MSECOND) {
4517 /* Invalid MPD file: duration would be negative or zero */
4520 start = period_node->start * GST_MSECOND;
4521 } else if (duration != GST_CLOCK_TIME_NONE) {
4522 /* start time inferred from previous period, this is still a regular period */
4524 } else if (idx == 0 && client->mpd_node->type == GST_MPD_FILE_TYPE_STATIC) {
4525 /* first period of a static MPD file, start time is 0 */
4527 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4528 /* this should be a live stream, let this pass */
4530 /* this is an 'Early Available Period' */
4534 /* compute duration.
4535 If there is a start time for the next period, or this is the last period
4536 and mediaPresentationDuration was set, those values will take precedence
4537 over a configured period duration in computing this period's duration
4539 ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
4540 "The Period extends until the PeriodStart of the next Period, or until
4541 the end of the Media Presentation in the case of the last Period."
4544 while ((next = g_list_next (list)) != NULL) {
4545 /* try to infer this period duration from the start time of the next period */
4546 next_period_node = next->data;
4548 if (next_period_node->xlink_href) {
4552 gst_mpd_client_fetch_external_period (client, next_period_node);
4554 client->mpd_node->Periods =
4555 g_list_delete_link (client->mpd_node->Periods, next);
4556 gst_mpdparser_free_period_node (next_period_node);
4557 next_period_node = NULL;
4558 /* Get new next node, we will insert before this */
4559 next = g_list_next (list);
4560 while (new_periods) {
4561 client->mpd_node->Periods =
4562 g_list_insert_before (client->mpd_node->Periods, next,
4564 new_periods = g_list_delete_link (new_periods, new_periods);
4567 /* And try again, getting the next list element which is now our newly
4568 * inserted nodes. If any */
4570 /* Got the next period and it doesn't have to be downloaded first */
4575 if (next_period_node) {
4576 if (next_period_node->start != -1) {
4577 if (start >= next_period_node->start * GST_MSECOND) {
4578 /* Invalid MPD file: duration would be negative or zero */
4581 duration = next_period_node->start * GST_MSECOND - start;
4582 } else if (period_node->duration != -1) {
4583 if (period_node->duration <= 0) {
4584 /* Invalid MPD file: duration would be negative or zero */
4587 duration = period_node->duration * GST_MSECOND;
4588 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4589 /* might be a live file, ignore unspecified duration */
4591 /* Invalid MPD file! */
4594 } else if (client->mpd_node->mediaPresentationDuration != -1) {
4595 /* last Period of the Media Presentation */
4596 if (client->mpd_node->mediaPresentationDuration * GST_MSECOND <= start) {
4597 /* Invalid MPD file: duration would be negative or zero */
4601 client->mpd_node->mediaPresentationDuration * GST_MSECOND - start;
4602 } else if (period_node->duration != -1) {
4603 duration = period_node->duration * GST_MSECOND;
4604 } else if (client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
4605 /* might be a live file, ignore unspecified duration */
4607 /* Invalid MPD file! */
4611 stream_period = g_slice_new0 (GstStreamPeriod);
4612 client->periods = g_list_append (client->periods, stream_period);
4613 stream_period->period = period_node;
4614 stream_period->number = idx++;
4615 stream_period->start = start;
4616 stream_period->duration = duration;
4618 GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
4619 GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
4621 if ((time != GST_CLOCK_TIME_NONE
4622 && stream_period->duration != GST_CLOCK_TIME_NONE
4623 && stream_period->start + stream_period->duration >= time)
4624 || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
4627 if (period_idx != -1 && stream_period->number >= period_idx)
4630 if (period_id != NULL && stream_period->period->id != NULL
4631 && strcmp (stream_period->period->id, period_id) == 0)
4638 ("Found a total of %d valid Periods in the Media Presentation up to this point",
4644 ("Found an Early Available Period, skipping the rest of the Media Presentation");
4649 ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
4655 gst_mpd_client_fetch_external_adaptation_set (GstMpdClient * client,
4656 GstPeriodNode * period, GstAdaptationSetNode * adapt_set)
4658 GstFragment *download;
4659 GstBuffer *adapt_set_buffer;
4662 xmlDocPtr doc = NULL;
4663 GstUri *base_uri, *uri;
4664 gchar *query = NULL;
4666 GList *new_adapt_sets = NULL;
4668 /* ISO/IEC 23009-1:2014 5.5.3 4)
4669 * Remove nodes that resolve to nothing when resolving
4671 if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
4675 if (!client->downloader) {
4679 /* Build absolute URI */
4681 /* Get base URI at the MPD level */
4683 gst_uri_from_string (client->
4684 mpd_base_uri ? client->mpd_base_uri : client->mpd_uri);
4686 /* combine a BaseURL at the MPD level with the current base url */
4687 base_uri = combine_urls (base_uri, client->mpd_node->BaseURLs, &query, 0);
4689 /* combine a BaseURL at the Period level with the current base url */
4690 base_uri = combine_urls (base_uri, period->BaseURLs, &query, 0);
4692 uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
4694 gst_uri_set_query_string (uri, query);
4696 uri_string = gst_uri_to_string (uri);
4697 gst_uri_unref (base_uri);
4698 gst_uri_unref (uri);
4700 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
4702 gst_uri_downloader_fetch_uri (client->downloader,
4703 uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
4704 DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
4707 gst_uri_downloader_fetch_uri (client->downloader,
4708 uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
4710 g_free (uri_string);
4713 GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
4714 adapt_set->xlink_href, err->message);
4715 g_clear_error (&err);
4719 adapt_set_buffer = gst_fragment_get_buffer (download);
4720 g_object_unref (download);
4722 gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
4725 xmlReadMemory ((const gchar *) map.data, map.size, "noname.xml", NULL,
4728 gst_buffer_unmap (adapt_set_buffer, &map);
4729 gst_buffer_unref (adapt_set_buffer);
4731 /* NOTE: ISO/IEC 23009-1:2014 5.3.3.2 is saying that exactly one AdaptationSet
4732 * in external xml is allowed */
4734 xmlNode *root_element = xmlDocGetRootElement (doc);
4736 if (root_element->type != XML_ELEMENT_NODE ||
4737 xmlStrcmp (root_element->name, (xmlChar *) "AdaptationSet") != 0) {
4741 gst_mpdparser_parse_adaptation_set_node (&new_adapt_sets, root_element,
4751 return new_adapt_sets;
4754 GST_ERROR ("Failed to parse adaptation set node XML");
4759 gst_mpd_client_get_adaptation_sets_for_period (GstMpdClient * client,
4760 GstStreamPeriod * period)
4764 g_return_val_if_fail (period != NULL, NULL);
4766 /* Resolve all external adaptation sets of this period. Every user of
4767 * the adaptation sets would need to know the content of all adaptation sets
4768 * to decide which one to use, so we have to resolve them all here
4770 for (list = period->period->AdaptationSets; list;
4771 /* advanced explicitely below */ ) {
4772 GstAdaptationSetNode *adapt_set = (GstAdaptationSetNode *) list->data;
4773 GList *new_adapt_sets = NULL, *prev, *next;
4775 if (!adapt_set->xlink_href) {
4781 gst_mpd_client_fetch_external_adaptation_set (client, period->period,
4785 period->period->AdaptationSets =
4786 g_list_delete_link (period->period->AdaptationSets, list);
4787 gst_mpdparser_free_adaptation_set_node (adapt_set);
4790 /* Get new next node, we will insert before this */
4794 next = period->period->AdaptationSets;
4796 while (new_adapt_sets) {
4797 period->period->AdaptationSets =
4798 g_list_insert_before (period->period->AdaptationSets, next,
4799 new_adapt_sets->data);
4800 new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
4803 /* Update our iterator to the first new adaptation set if any, or the next */
4807 list = period->period->AdaptationSets;
4810 return period->period->AdaptationSets;
4814 gst_mpd_client_get_adaptation_sets (GstMpdClient * client)
4816 GstStreamPeriod *stream_period;
4818 stream_period = gst_mpdparser_get_stream_period (client);
4819 if (stream_period == NULL || stream_period->period == NULL) {
4820 GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
4824 return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
4828 gst_mpd_client_setup_streaming (GstMpdClient * client,
4829 GstAdaptationSetNode * adapt_set)
4831 GstRepresentationNode *representation;
4832 GList *rep_list = NULL;
4833 GstActiveStream *stream;
4835 rep_list = adapt_set->Representations;
4837 GST_WARNING ("Can not retrieve any representation, aborting...");
4841 stream = g_slice_new0 (GstActiveStream);
4842 gst_mpdparser_init_active_stream_segments (stream);
4844 stream->baseURL_idx = 0;
4845 stream->cur_adapt_set = adapt_set;
4847 GST_DEBUG ("0. Current stream %p", stream);
4849 /* retrieve representation list */
4850 if (stream->cur_adapt_set != NULL)
4851 rep_list = stream->cur_adapt_set->Representations;
4856 gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
4857 stream->max_bandwidth);
4859 if (!representation) {
4861 ("Can not retrieve a representation with the requested bandwidth");
4862 representation = gst_mpdparser_get_lowest_representation (rep_list);
4866 representation = gst_mpdparser_get_lowest_representation (rep_list);
4869 if (!representation) {
4870 GST_WARNING ("No valid representation in the MPD file, aborting...");
4871 g_slice_free (GstActiveStream, stream);
4875 gst_mpdparser_representation_get_mimetype (adapt_set, representation);
4876 if (stream->mimeType == GST_STREAM_UNKNOWN) {
4877 GST_WARNING ("Unknown mime type in the representation, aborting...");
4878 g_slice_free (GstActiveStream, stream);
4882 client->active_streams = g_list_append (client->active_streams, stream);
4883 if (!gst_mpd_client_setup_representation (client, stream, representation)) {
4884 GST_WARNING ("Failed to setup the representation, aborting...");
4888 GST_INFO ("Successfully setup the download pipeline for mimeType %d",
4895 gst_mpd_client_stream_seek (GstMpdClient * client, GstActiveStream * stream,
4896 gboolean forward, GstSeekFlags flags, GstClockTime ts,
4897 GstClockTime * final_ts)
4900 gint repeat_index = 0;
4901 GstMediaSegment *selectedChunk = NULL;
4903 g_return_val_if_fail (stream != NULL, 0);
4905 if (stream->segments) {
4906 for (index = 0; index < stream->segments->len; index++) {
4907 gboolean in_segment = FALSE;
4908 GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
4909 GstClockTime end_time;
4911 GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
4912 stream->segments->len);
4915 gst_mpdparser_get_segment_end_time (client, stream->segments,
4918 /* avoid downloading another fragment just for 1ns in reverse mode */
4920 in_segment = ts < end_time;
4922 in_segment = ts <= end_time;
4925 GstClockTime chunk_time;
4927 selectedChunk = segment;
4928 repeat_index = (ts - segment->start) / segment->duration;
4930 chunk_time = segment->start + segment->duration * repeat_index;
4932 /* At the end of a segment in reverse mode, start from the previous fragment */
4933 if (!forward && repeat_index > 0
4934 && ((ts - segment->start) % segment->duration == 0))
4937 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
4938 if (repeat_index + 1 < segment->repeat) {
4939 if (ts - chunk_time > chunk_time + segment->duration - ts)
4941 } else if (index + 1 < stream->segments->len) {
4942 GstMediaSegment *next_segment =
4943 g_ptr_array_index (stream->segments, index + 1);
4945 if (ts - chunk_time > next_segment->start - ts) {
4947 selectedChunk = next_segment;
4951 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
4952 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
4955 if (repeat_index + 1 < segment->repeat) {
4959 if (index + 1 >= stream->segments->len) {
4960 selectedChunk = NULL;
4962 selectedChunk = g_ptr_array_index (stream->segments, ++index);
4970 if (selectedChunk == NULL) {
4971 stream->segment_index = stream->segments->len;
4972 stream->segment_repeat_index = 0;
4973 GST_DEBUG ("Seek to after last segment");
4978 *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
4980 GstClockTime duration =
4981 gst_mpd_client_get_segment_duration (client, stream, NULL);
4982 GstStreamPeriod *stream_period = gst_mpdparser_get_stream_period (client);
4983 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
4984 GstClockTime index_time;
4986 g_return_val_if_fail (stream->cur_seg_template->
4987 MultSegBaseType->SegmentTimeline == NULL, FALSE);
4988 if (!GST_CLOCK_TIME_IS_VALID (duration)) {
4992 if (ts > stream_period->start)
4993 ts -= stream_period->start;
4997 index = ts / duration;
4999 /* At the end of a segment in reverse mode, start from the previous fragment */
5000 if (!forward && index > 0 && ts % duration == 0)
5003 index_time = index * duration;
5005 if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
5006 if (ts - index_time > index_time + duration - ts)
5008 } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
5009 (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
5010 && ts != index_time) {
5014 if (segments_count > 0 && index >= segments_count) {
5015 stream->segment_index = segments_count;
5016 stream->segment_repeat_index = 0;
5017 GST_DEBUG ("Seek to after last segment");
5021 *final_ts = index * duration;
5024 stream->segment_repeat_index = repeat_index;
5025 stream->segment_index = index;
5031 gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
5032 const GstDateTime * t2)
5034 GDateTime *gdt1, *gdt2;
5037 g_assert (t1 != NULL && t2 != NULL);
5038 gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
5039 gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
5040 diff = g_date_time_difference (gdt2, gdt1);
5041 g_date_time_unref (gdt1);
5042 g_date_time_unref (gdt2);
5043 return diff * GST_USECOND;
5047 gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
5053 #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
5054 g_return_val_if_fail (t1 != NULL, NULL);
5055 gdt = gst_date_time_to_g_date_time (t1);
5056 g_return_val_if_fail (gdt != NULL, NULL);
5057 gdt2 = g_date_time_add (gdt, usecs);
5058 g_return_val_if_fail (gdt2 != NULL, NULL);
5060 g_assert (t1 != NULL);
5061 gdt = gst_date_time_to_g_date_time (t1);
5062 g_assert (gdt != NULL);
5063 gdt2 = g_date_time_add (gdt, usecs);
5064 g_assert (gdt2 != NULL);
5066 g_date_time_unref (gdt);
5067 rv = gst_date_time_new_from_g_date_time (gdt2);
5069 /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
5070 * ownership of the GDateTime pointer.
5076 static GstDateTime *
5077 gst_mpd_client_get_availability_start_time (GstMpdClient * client)
5079 GstDateTime *start_time;
5082 return (GstDateTime *) NULL;
5084 start_time = client->mpd_node->availabilityStartTime;
5086 gst_date_time_ref (start_time);
5091 gst_mpd_client_get_last_fragment_timestamp_end (GstMpdClient * client,
5092 guint stream_idx, GstClockTime * ts)
5094 GstActiveStream *stream;
5096 GstMediaSegment *currentChunk;
5097 GstStreamPeriod *stream_period;
5099 GST_DEBUG ("Stream index: %i", stream_idx);
5100 stream = g_list_nth_data (client->active_streams, stream_idx);
5101 g_return_val_if_fail (stream != NULL, 0);
5103 if (!stream->segments) {
5104 stream_period = gst_mpdparser_get_stream_period (client);
5105 *ts = stream_period->start + stream_period->duration;
5107 segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
5108 currentChunk = g_ptr_array_index (stream->segments, segment_idx);
5110 if (currentChunk->repeat >= 0) {
5112 currentChunk->start + (currentChunk->duration * (1 +
5113 currentChunk->repeat));
5115 /* 5.3.9.6.1: negative repeat means repeat till the end of the
5116 * period, or the next update of the MPD (which I think is
5117 * implicit, as this will all get deleted/recreated), or the
5118 * start of the next segment, if any. */
5119 stream_period = gst_mpdparser_get_stream_period (client);
5120 *ts = stream_period->start + stream_period->duration;
5128 gst_mpd_client_get_next_fragment_timestamp (GstMpdClient * client,
5129 guint stream_idx, GstClockTime * ts)
5131 GstActiveStream *stream;
5132 GstMediaSegment *currentChunk;
5134 GST_DEBUG ("Stream index: %i", stream_idx);
5135 stream = g_list_nth_data (client->active_streams, stream_idx);
5136 g_return_val_if_fail (stream != NULL, 0);
5138 if (stream->segments) {
5139 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5140 stream->segment_index, stream->segments->len);
5141 if (stream->segment_index >= stream->segments->len)
5143 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5146 currentChunk->start +
5147 (currentChunk->duration * stream->segment_repeat_index);
5149 GstClockTime duration =
5150 gst_mpd_client_get_segment_duration (client, stream, NULL);
5151 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5153 g_return_val_if_fail (stream->cur_seg_template->
5154 MultSegBaseType->SegmentTimeline == NULL, FALSE);
5155 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5156 && stream->segment_index >= segments_count)) {
5159 *ts = stream->segment_index * duration;
5166 gst_mpd_parser_get_stream_presentation_offset (GstMpdClient * client,
5169 GstActiveStream *stream = NULL;
5171 g_return_val_if_fail (client != NULL, 0);
5172 g_return_val_if_fail (client->active_streams != NULL, 0);
5173 stream = g_list_nth_data (client->active_streams, stream_idx);
5174 g_return_val_if_fail (stream != NULL, 0);
5176 return stream->presentationTimeOffset;
5180 gst_mpd_parser_get_period_start_time (GstMpdClient * client)
5182 GstStreamPeriod *stream_period = NULL;
5184 g_return_val_if_fail (client != NULL, 0);
5185 stream_period = gst_mpdparser_get_stream_period (client);
5186 g_return_val_if_fail (stream_period != NULL, 0);
5188 return stream_period->start;
5192 * gst_mpd_client_get_utc_timing_sources:
5193 * @client: #GstMpdClient to check for UTCTiming elements
5194 * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
5196 * @selected_method: (nullable): The selected method
5197 * Returns: (transfer none): A NULL terminated array of URLs of servers
5198 * that use @selected_method to provide a realtime clock.
5200 * Searches the UTCTiming elements found in the manifest for an element
5201 * that uses one of the UTC timing methods specified in @selected_method.
5202 * If multiple UTCTiming elements are present that support one of the
5203 * methods specified in @selected_method, the first one is returned.
5208 gst_mpd_client_get_utc_timing_sources (GstMpdClient * client,
5209 guint methods, GstMPDUTCTimingType * selected_method)
5213 g_return_val_if_fail (client != NULL, NULL);
5214 g_return_val_if_fail (client->mpd_node != NULL, NULL);
5215 for (list = g_list_first (client->mpd_node->UTCTiming); list;
5216 list = g_list_next (list)) {
5217 const GstUTCTimingNode *node = (const GstUTCTimingNode *) list->data;
5218 if (node->method & methods) {
5219 if (selected_method) {
5220 *selected_method = node->method;
5229 gst_mpd_client_get_next_fragment (GstMpdClient * client,
5230 guint indexStream, GstMediaFragmentInfo * fragment)
5232 GstActiveStream *stream = NULL;
5233 GstMediaSegment *currentChunk;
5234 gchar *mediaURL = NULL;
5235 gchar *indexURL = NULL;
5236 GstUri *base_url, *frag_url;
5239 g_return_val_if_fail (client != NULL, FALSE);
5240 g_return_val_if_fail (client->active_streams != NULL, FALSE);
5241 stream = g_list_nth_data (client->active_streams, indexStream);
5242 g_return_val_if_fail (stream != NULL, FALSE);
5243 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5245 if (stream->segments) {
5246 GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
5247 stream->segment_index, stream->segments->len);
5248 if (stream->segment_index >= stream->segments->len)
5251 GstClockTime duration = gst_mpd_client_get_segment_duration (client,
5253 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5255 g_return_val_if_fail (stream->cur_seg_template->
5256 MultSegBaseType->SegmentTimeline == NULL, FALSE);
5257 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5258 && stream->segment_index >= segments_count)) {
5261 fragment->duration = duration;
5264 /* FIXME rework discont checking */
5265 /* fragment->discontinuity = segment_idx != currentChunk.number; */
5266 fragment->range_start = 0;
5267 fragment->range_end = -1;
5268 fragment->index_uri = NULL;
5269 fragment->index_range_start = 0;
5270 fragment->index_range_end = -1;
5272 if (stream->segments) {
5273 currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
5275 GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
5276 if (currentChunk->SegmentURL != NULL) {
5278 g_strdup (gst_mpdparser_get_mediaURL (stream,
5279 currentChunk->SegmentURL));
5280 indexURL = g_strdup (currentChunk->SegmentURL->index);
5281 } else if (stream->cur_seg_template != NULL) {
5283 gst_mpdparser_build_URL_from_template (stream->
5284 cur_seg_template->media, stream->cur_representation->id,
5285 currentChunk->number + stream->segment_repeat_index,
5286 stream->cur_representation->bandwidth,
5287 currentChunk->scale_start +
5288 stream->segment_repeat_index * currentChunk->scale_duration);
5289 if (stream->cur_seg_template->index) {
5291 gst_mpdparser_build_URL_from_template (stream->
5292 cur_seg_template->index, stream->cur_representation->id,
5293 currentChunk->number + stream->segment_repeat_index,
5294 stream->cur_representation->bandwidth,
5295 currentChunk->scale_start +
5296 stream->segment_repeat_index * currentChunk->scale_duration);
5299 GST_DEBUG ("mediaURL = %s", mediaURL);
5300 GST_DEBUG ("indexURL = %s", indexURL);
5302 fragment->timestamp =
5303 currentChunk->start +
5304 stream->segment_repeat_index * currentChunk->duration;
5305 fragment->duration = currentChunk->duration;
5306 if (currentChunk->SegmentURL) {
5307 if (currentChunk->SegmentURL->mediaRange) {
5308 fragment->range_start =
5309 currentChunk->SegmentURL->mediaRange->first_byte_pos;
5310 fragment->range_end =
5311 currentChunk->SegmentURL->mediaRange->last_byte_pos;
5313 if (currentChunk->SegmentURL->indexRange) {
5314 fragment->index_range_start =
5315 currentChunk->SegmentURL->indexRange->first_byte_pos;
5316 fragment->index_range_end =
5317 currentChunk->SegmentURL->indexRange->last_byte_pos;
5321 if (stream->cur_seg_template != NULL) {
5323 gst_mpdparser_build_URL_from_template (stream->
5324 cur_seg_template->media, stream->cur_representation->id,
5325 stream->segment_index +
5326 stream->cur_seg_template->MultSegBaseType->startNumber,
5327 stream->cur_representation->bandwidth,
5328 stream->segment_index * fragment->duration);
5329 if (stream->cur_seg_template->index) {
5331 gst_mpdparser_build_URL_from_template (stream->
5332 cur_seg_template->index, stream->cur_representation->id,
5333 stream->segment_index +
5334 stream->cur_seg_template->MultSegBaseType->startNumber,
5335 stream->cur_representation->bandwidth,
5336 stream->segment_index * fragment->duration);
5342 GST_DEBUG ("mediaURL = %s", mediaURL);
5343 GST_DEBUG ("indexURL = %s", indexURL);
5345 fragment->timestamp = stream->segment_index * fragment->duration;
5348 base_url = gst_uri_from_string (stream->baseURL);
5349 frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
5351 if (stream->queryURL) {
5352 frag_url = gst_uri_make_writable (frag_url);
5353 gst_uri_set_query_string (frag_url, stream->queryURL);
5355 fragment->uri = gst_uri_to_string (frag_url);
5356 gst_uri_unref (frag_url);
5358 if (indexURL != NULL) {
5359 frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
5361 gst_uri_set_query_string (frag_url, stream->queryURL);
5362 fragment->index_uri = gst_uri_to_string (frag_url);
5363 gst_uri_unref (frag_url);
5365 } else if (indexURL == NULL && (fragment->index_range_start
5366 || fragment->index_range_end != -1)) {
5367 /* index has no specific URL but has a range, we should only use this if
5368 * the media also has a range, otherwise we are serving some data twice
5369 * (in the media fragment and again in the index) */
5370 if (!(fragment->range_start || fragment->range_end != -1)) {
5371 GST_WARNING ("Ignoring index ranges because there isn't a media range "
5372 "and URIs would be the same");
5373 /* removing index information */
5374 fragment->index_range_start = 0;
5375 fragment->index_range_end = -1;
5379 gst_uri_unref (base_url);
5381 GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
5387 gst_mpd_client_has_next_segment (GstMpdClient * client,
5388 GstActiveStream * stream, gboolean forward)
5391 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5393 if (segments_count > 0 && stream->segments
5394 && stream->segment_index + 1 == segments_count) {
5395 GstMediaSegment *segment;
5397 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5398 if (segment->repeat >= 0
5399 && stream->segment_repeat_index >= segment->repeat)
5401 } else if (segments_count > 0
5402 && stream->segment_index + 1 >= segments_count) {
5406 if (stream->segment_index < 0)
5414 gst_mpd_client_advance_segment (GstMpdClient * client, GstActiveStream * stream,
5417 GstMediaSegment *segment;
5418 GstFlowReturn ret = GST_FLOW_OK;
5419 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5421 GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
5422 segments_count, stream->segment_repeat_index);
5424 /* handle special cases first */
5426 if (segments_count > 0 && stream->segment_index >= segments_count) {
5431 if (stream->segments == NULL) {
5432 if (stream->segment_index < 0) {
5433 stream->segment_index = 0;
5435 stream->segment_index++;
5436 if (segments_count > 0 && stream->segment_index >= segments_count) {
5443 /* special case for when playback direction is reverted right at *
5444 * the end of the segment list */
5445 if (stream->segment_index < 0) {
5446 stream->segment_index = 0;
5450 if (stream->segments == NULL)
5451 stream->segment_index--;
5452 if (stream->segment_index < 0) {
5453 stream->segment_index = -1;
5457 if (stream->segments == NULL)
5460 /* special case for when playback direction is reverted right at *
5461 * the end of the segment list */
5462 if (stream->segment_index >= segments_count) {
5463 stream->segment_index = segments_count - 1;
5464 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5465 if (segment->repeat >= 0) {
5466 stream->segment_repeat_index = segment->repeat;
5468 GstClockTime start = segment->start;
5470 gst_mpdparser_get_segment_end_time (client, stream->segments,
5472 stream->segment_index);
5473 stream->segment_repeat_index =
5474 (guint) (end - start) / segment->duration;
5480 /* for the normal cases we can get the segment safely here */
5481 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5483 if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
5484 stream->segment_repeat_index = 0;
5485 stream->segment_index++;
5486 if (segments_count > 0 && stream->segment_index >= segments_count) {
5491 stream->segment_repeat_index++;
5494 if (stream->segment_repeat_index == 0) {
5495 stream->segment_index--;
5496 if (stream->segment_index < 0) {
5501 segment = g_ptr_array_index (stream->segments, stream->segment_index);
5502 /* negative repeats only seem to make sense at the end of a list,
5503 * so this one will probably not be. Needs some sanity checking
5504 * when loading the XML data. */
5505 if (segment->repeat >= 0) {
5506 stream->segment_repeat_index = segment->repeat;
5508 GstClockTime start = segment->start;
5510 gst_mpdparser_get_segment_end_time (client, stream->segments,
5512 stream->segment_index);
5513 stream->segment_repeat_index =
5514 (guint) (end - start) / segment->duration;
5517 stream->segment_repeat_index--;
5522 GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
5523 stream->segment_index, segments_count,
5524 stream->segment_repeat_index, gst_flow_get_name (ret));
5529 gst_mpd_client_get_next_header (GstMpdClient * client, gchar ** uri,
5530 guint stream_idx, gint64 * range_start, gint64 * range_end)
5532 GstActiveStream *stream;
5533 GstStreamPeriod *stream_period;
5535 stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5536 g_return_val_if_fail (stream != NULL, FALSE);
5537 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5538 stream_period = gst_mpdparser_get_stream_period (client);
5539 g_return_val_if_fail (stream_period != NULL, FALSE);
5540 g_return_val_if_fail (stream_period->period != NULL, FALSE);
5545 GST_DEBUG ("Looking for current representation header");
5547 if (stream->cur_segment_base) {
5548 if (stream->cur_segment_base->Initialization) {
5550 g_strdup (gst_mpdparser_get_initializationURL (stream,
5551 stream->cur_segment_base->Initialization));
5552 if (stream->cur_segment_base->Initialization->range) {
5554 stream->cur_segment_base->Initialization->range->first_byte_pos;
5556 stream->cur_segment_base->Initialization->range->last_byte_pos;
5558 } else if (stream->cur_segment_base->indexRange) {
5560 g_strdup (gst_mpdparser_get_initializationURL (stream,
5561 stream->cur_segment_base->Initialization));
5563 *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
5565 } else if (stream->cur_seg_template
5566 && stream->cur_seg_template->initialization) {
5568 gst_mpdparser_build_URL_from_template (stream->
5569 cur_seg_template->initialization, stream->cur_representation->id, 0,
5570 stream->cur_representation->bandwidth, 0);
5573 return *uri == NULL ? FALSE : TRUE;
5577 gst_mpd_client_get_next_header_index (GstMpdClient * client, gchar ** uri,
5578 guint stream_idx, gint64 * range_start, gint64 * range_end)
5580 GstActiveStream *stream;
5581 GstStreamPeriod *stream_period;
5583 stream = gst_mpdparser_get_active_stream_by_index (client, stream_idx);
5584 g_return_val_if_fail (stream != NULL, FALSE);
5585 g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
5586 stream_period = gst_mpdparser_get_stream_period (client);
5587 g_return_val_if_fail (stream_period != NULL, FALSE);
5588 g_return_val_if_fail (stream_period->period != NULL, FALSE);
5593 GST_DEBUG ("Looking for current representation index");
5595 if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
5597 g_strdup (gst_mpdparser_get_initializationURL (stream,
5598 stream->cur_segment_base->RepresentationIndex));
5599 *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
5600 *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
5601 } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
5603 gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
5604 stream->cur_representation->id, 0,
5605 stream->cur_representation->bandwidth, 0);
5608 return *uri == NULL ? FALSE : TRUE;
5612 gst_mpd_client_get_next_fragment_duration (GstMpdClient * client,
5613 GstActiveStream * stream)
5615 GstMediaSegment *media_segment = NULL;
5618 g_return_val_if_fail (stream != NULL, 0);
5620 seg_idx = stream->segment_index;
5622 if (stream->segments) {
5623 if (seg_idx < stream->segments->len && seg_idx >= 0)
5624 media_segment = g_ptr_array_index (stream->segments, seg_idx);
5626 return media_segment == NULL ? 0 : media_segment->duration;
5628 GstClockTime duration =
5629 gst_mpd_client_get_segment_duration (client, stream, NULL);
5630 guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
5632 g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5633 SegmentTimeline == NULL, 0);
5635 if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
5636 && seg_idx >= segments_count)) {
5644 gst_mpd_client_get_media_presentation_duration (GstMpdClient * client)
5646 GstClockTime duration;
5648 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
5650 if (client->mpd_node->mediaPresentationDuration != -1) {
5651 duration = client->mpd_node->mediaPresentationDuration * GST_MSECOND;
5653 /* We can only get the duration for on-demand streams */
5654 duration = GST_CLOCK_TIME_NONE;
5661 gst_mpd_client_set_period_id (GstMpdClient * client, const gchar * period_id)
5663 GstStreamPeriod *next_stream_period;
5664 gboolean ret = FALSE;
5668 g_return_val_if_fail (client != NULL, FALSE);
5669 g_return_val_if_fail (client->periods != NULL, FALSE);
5670 g_return_val_if_fail (period_id != NULL, FALSE);
5672 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
5676 for (period_idx = 0, iter = client->periods; iter;
5677 period_idx++, iter = g_list_next (iter)) {
5678 next_stream_period = iter->data;
5680 if (next_stream_period->period->id
5681 && strcmp (next_stream_period->period->id, period_id) == 0) {
5683 client->period_idx = period_idx;
5692 gst_mpd_client_set_period_index (GstMpdClient * client, guint period_idx)
5694 GstStreamPeriod *next_stream_period;
5695 gboolean ret = FALSE;
5697 g_return_val_if_fail (client != NULL, FALSE);
5698 g_return_val_if_fail (client->periods != NULL, FALSE);
5700 if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
5703 next_stream_period = g_list_nth_data (client->periods, period_idx);
5704 if (next_stream_period != NULL) {
5705 client->period_idx = period_idx;
5713 gst_mpd_client_get_period_index (GstMpdClient * client)
5717 g_return_val_if_fail (client != NULL, 0);
5718 period_idx = client->period_idx;
5724 gst_mpd_client_get_period_id (GstMpdClient * client)
5726 GstStreamPeriod *period;
5727 gchar *period_id = NULL;
5729 g_return_val_if_fail (client != NULL, 0);
5730 period = g_list_nth_data (client->periods, client->period_idx);
5731 if (period && period->period)
5732 period_id = period->period->id;
5738 gst_mpd_client_has_previous_period (GstMpdClient * client)
5740 GList *next_stream_period;
5741 g_return_val_if_fail (client != NULL, FALSE);
5742 g_return_val_if_fail (client->periods != NULL, FALSE);
5744 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5745 client->period_idx - 1, NULL))
5748 next_stream_period =
5749 g_list_nth_data (client->periods, client->period_idx - 1);
5751 return next_stream_period != NULL;
5755 gst_mpd_client_has_next_period (GstMpdClient * client)
5757 GList *next_stream_period;
5758 g_return_val_if_fail (client != NULL, FALSE);
5759 g_return_val_if_fail (client->periods != NULL, FALSE);
5761 if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
5762 client->period_idx + 1, NULL))
5765 next_stream_period =
5766 g_list_nth_data (client->periods, client->period_idx + 1);
5767 return next_stream_period != NULL;
5771 gst_mpd_client_seek_to_first_segment (GstMpdClient * client)
5775 g_return_if_fail (client != NULL);
5776 g_return_if_fail (client->active_streams != NULL);
5778 for (list = g_list_first (client->active_streams); list;
5779 list = g_list_next (list)) {
5780 GstActiveStream *stream = (GstActiveStream *) list->data;
5782 stream->segment_index = 0;
5783 stream->segment_repeat_index = 0;
5789 gst_mpd_client_get_segments_counts (GstMpdClient * client,
5790 GstActiveStream * stream)
5792 GstStreamPeriod *stream_period;
5794 g_return_val_if_fail (stream != NULL, 0);
5796 if (stream->segments)
5797 return stream->segments->len;
5798 g_return_val_if_fail (stream->cur_seg_template->MultSegBaseType->
5799 SegmentTimeline == NULL, 0);
5801 stream_period = gst_mpdparser_get_stream_period (client);
5802 if (stream_period->duration != -1)
5803 return gst_util_uint64_scale_ceil (stream_period->duration, 1,
5804 gst_mpd_client_get_segment_duration (client, stream, NULL));
5810 gst_mpd_client_is_live (GstMpdClient * client)
5812 g_return_val_if_fail (client != NULL, FALSE);
5813 g_return_val_if_fail (client->mpd_node != NULL, FALSE);
5815 return client->mpd_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
5819 gst_mpdparser_get_nb_active_stream (GstMpdClient * client)
5821 g_return_val_if_fail (client != NULL, 0);
5823 return g_list_length (client->active_streams);
5827 gst_mpdparser_get_nb_adaptationSet (GstMpdClient * client)
5829 GstStreamPeriod *stream_period;
5831 stream_period = gst_mpdparser_get_stream_period (client);
5832 g_return_val_if_fail (stream_period != NULL, 0);
5833 g_return_val_if_fail (stream_period->period != NULL, 0);
5835 return g_list_length (stream_period->period->AdaptationSets);
5839 gst_mpdparser_get_active_stream_by_index (GstMpdClient * client,
5842 g_return_val_if_fail (client != NULL, NULL);
5843 g_return_val_if_fail (client->active_streams != NULL, NULL);
5845 return g_list_nth_data (client->active_streams, stream_idx);
5849 gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
5851 const gchar *mimeType;
5852 const gchar *adapt_set_codecs;
5853 const gchar *rep_codecs;
5855 mimeType = stream->cur_representation->RepresentationBase->mimeType;
5857 mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5859 if (g_strcmp0 (mimeType, "application/ttml+xml") == 0)
5862 adapt_set_codecs = stream->cur_adapt_set->RepresentationBase->codecs;
5863 rep_codecs = stream->cur_representation->RepresentationBase->codecs;
5865 return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
5866 || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
5869 static const gchar *
5870 gst_mpdparser_mimetype_to_caps (const gchar * mimeType)
5872 if (mimeType == NULL)
5874 if (strcmp (mimeType, "video/mp2t") == 0) {
5875 return "video/mpegts, systemstream=(bool) true";
5876 } else if (strcmp (mimeType, "video/mp4") == 0) {
5877 return "video/quicktime";
5878 } else if (strcmp (mimeType, "audio/mp4") == 0) {
5879 return "audio/x-m4a";
5885 gst_mpd_client_get_stream_caps (GstActiveStream * stream)
5887 const gchar *mimeType, *caps_string;
5888 GstCaps *ret = NULL;
5890 if (stream == NULL || stream->cur_adapt_set == NULL
5891 || stream->cur_representation == NULL)
5894 mimeType = stream->cur_representation->RepresentationBase->mimeType;
5895 if (mimeType == NULL) {
5896 mimeType = stream->cur_adapt_set->RepresentationBase->mimeType;
5899 caps_string = gst_mpdparser_mimetype_to_caps (mimeType);
5901 if ((g_strcmp0 (caps_string, "application/mp4") == 0)
5902 && gst_mpd_client_active_stream_contains_subtitles (stream))
5903 caps_string = "video/quicktime";
5906 ret = gst_caps_from_string (caps_string);
5912 gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
5914 if (stream == NULL || stream->cur_adapt_set == NULL)
5917 return stream->cur_adapt_set->bitstreamSwitching;
5921 gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
5925 if (stream == NULL || stream->cur_adapt_set == NULL
5926 || stream->cur_representation == NULL)
5929 width = stream->cur_representation->RepresentationBase->width;
5931 width = stream->cur_adapt_set->RepresentationBase->width;
5938 gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
5942 if (stream == NULL || stream->cur_adapt_set == NULL
5943 || stream->cur_representation == NULL)
5946 height = stream->cur_representation->RepresentationBase->height;
5948 height = stream->cur_adapt_set->RepresentationBase->height;
5955 gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
5956 gint * fps_num, gint * fps_den)
5961 if (stream->cur_adapt_set &&
5962 stream->cur_adapt_set->RepresentationBase->frameRate != NULL) {
5963 *fps_num = stream->cur_adapt_set->RepresentationBase->frameRate->num;
5964 *fps_den = stream->cur_adapt_set->RepresentationBase->frameRate->den;
5968 if (stream->cur_adapt_set &&
5969 stream->cur_adapt_set->RepresentationBase->maxFrameRate != NULL) {
5970 *fps_num = stream->cur_adapt_set->RepresentationBase->maxFrameRate->num;
5971 *fps_den = stream->cur_adapt_set->RepresentationBase->maxFrameRate->den;
5975 if (stream->cur_representation &&
5976 stream->cur_representation->RepresentationBase->frameRate != NULL) {
5977 *fps_num = stream->cur_representation->RepresentationBase->frameRate->num;
5978 *fps_den = stream->cur_representation->RepresentationBase->frameRate->den;
5982 if (stream->cur_representation &&
5983 stream->cur_representation->RepresentationBase->maxFrameRate != NULL) {
5985 stream->cur_representation->RepresentationBase->maxFrameRate->num;
5987 stream->cur_representation->RepresentationBase->maxFrameRate->den;
5995 gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
5999 if (stream == NULL || stream->cur_adapt_set == NULL
6000 || stream->cur_representation == NULL)
6003 rate = stream->cur_representation->RepresentationBase->audioSamplingRate;
6005 rate = stream->cur_adapt_set->RepresentationBase->audioSamplingRate;
6008 return rate ? atoi (rate) : 0;
6012 gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
6014 if (stream == NULL || stream->cur_adapt_set == NULL
6015 || stream->cur_representation == NULL)
6017 /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
6022 gst_mpdparser_get_list_and_nb_of_audio_language (GstMpdClient * client,
6025 GstStreamPeriod *stream_period;
6026 GstAdaptationSetNode *adapt_set;
6027 GList *adaptation_sets, *list;
6028 const gchar *this_mimeType = "audio";
6029 gchar *mimeType = NULL;
6030 guint nb_adaptation_set = 0;
6032 stream_period = gst_mpdparser_get_stream_period (client);
6033 g_return_val_if_fail (stream_period != NULL, 0);
6034 g_return_val_if_fail (stream_period->period != NULL, 0);
6037 gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
6038 for (list = adaptation_sets; list; list = g_list_next (list)) {
6039 adapt_set = (GstAdaptationSetNode *) list->data;
6040 if (adapt_set && adapt_set->lang) {
6041 gchar *this_lang = adapt_set->lang;
6042 GstRepresentationNode *rep;
6044 gst_mpdparser_get_lowest_representation (adapt_set->Representations);
6046 if (rep->RepresentationBase)
6047 mimeType = rep->RepresentationBase->mimeType;
6048 if (!mimeType && adapt_set->RepresentationBase) {
6049 mimeType = adapt_set->RepresentationBase->mimeType;
6052 if (strncmp_ext (mimeType, this_mimeType) == 0) {
6053 nb_adaptation_set++;
6054 *lang = g_list_append (*lang, this_lang);
6059 return nb_adaptation_set;
6064 gst_mpd_client_get_next_segment_availability_start_time (GstMpdClient * client,
6065 GstActiveStream * stream)
6067 GstDateTime *availability_start_time, *rv;
6069 GstStreamPeriod *stream_period;
6070 GstMediaSegment *segment;
6071 GstClockTime segmentEndTime;
6073 g_return_val_if_fail (client != NULL, NULL);
6074 g_return_val_if_fail (stream != NULL, NULL);
6076 stream_period = gst_mpdparser_get_stream_period (client);
6078 seg_idx = stream->segment_index;
6080 if (stream->segments) {
6081 segment = g_ptr_array_index (stream->segments, seg_idx);
6083 if (segment->repeat >= 0) {
6084 segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
6086 } else if (seg_idx < stream->segments->len - 1) {
6087 const GstMediaSegment *next_segment =
6088 g_ptr_array_index (stream->segments, seg_idx + 1);
6089 segmentEndTime = next_segment->start;
6091 const GstStreamPeriod *stream_period;
6092 stream_period = gst_mpdparser_get_stream_period (client);
6093 segmentEndTime = stream_period->start + stream_period->duration;
6096 GstClockTime seg_duration;
6097 seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
6098 if (seg_duration == 0)
6100 segmentEndTime = (1 + seg_idx) * seg_duration;
6103 availability_start_time = gst_mpd_client_get_availability_start_time (client);
6104 if (availability_start_time == NULL) {
6105 GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
6109 if (stream_period && stream_period->period) {
6111 gst_mpd_client_add_time_difference (availability_start_time,
6112 stream_period->start / GST_USECOND);
6113 gst_date_time_unref (availability_start_time);
6114 availability_start_time = t;
6116 if (availability_start_time == NULL) {
6117 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
6122 rv = gst_mpd_client_add_time_difference (availability_start_time,
6123 segmentEndTime / GST_USECOND);
6124 gst_date_time_unref (availability_start_time);
6126 GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
6134 gst_mpd_client_seek_to_time (GstMpdClient * client, GDateTime * time)
6137 GTimeSpan ts_microseconds;
6139 gboolean ret = TRUE;
6142 g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
6143 g_return_val_if_fail (client->mpd_node->availabilityStartTime != NULL, FALSE);
6146 gst_date_time_to_g_date_time (client->mpd_node->availabilityStartTime);
6148 ts_microseconds = g_date_time_difference (time, start);
6149 g_date_time_unref (start);
6151 /* Clamp to availability start time, otherwise calculations wrap around */
6152 if (ts_microseconds < 0)
6153 ts_microseconds = 0;
6155 ts = ts_microseconds * GST_USECOND;
6156 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6158 ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
6165 gst_media_fragment_info_clear (GstMediaFragmentInfo * fragment)
6167 g_free (fragment->uri);
6168 g_free (fragment->index_uri);
6172 gst_mpd_client_has_isoff_ondemand_profile (GstMpdClient * client)
6174 return client->profile_isoff_ondemand;
6178 * gst_mpd_client_parse_default_presentation_delay:
6179 * @client: #GstMpdClient that has a parsed manifest
6180 * @default_presentation_delay: A string that specifies a time period
6181 * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
6183 * Returns: the parsed string in milliseconds
6188 gst_mpd_client_parse_default_presentation_delay (GstMpdClient * client,
6189 const gchar * default_presentation_delay)
6192 char *endptr = NULL;
6194 g_return_val_if_fail (client != NULL, 0);
6195 g_return_val_if_fail (default_presentation_delay != NULL, 0);
6196 value = strtol (default_presentation_delay, &endptr, 10);
6197 if (endptr == default_presentation_delay || value == 0) {
6200 while (*endptr == ' ')
6202 if (*endptr == 's' || *endptr == 'S') {
6203 value *= 1000; /* convert to ms */
6204 } else if (*endptr == 'f' || *endptr == 'F') {
6205 gint64 segment_duration;
6206 g_assert (client->mpd_node != NULL);
6207 segment_duration = client->mpd_node->maxSegmentDuration;
6208 value *= segment_duration;
6209 } else if (*endptr != 'm' && *endptr != 'M') {
6210 GST_ERROR ("Unable to parse default presentation delay: %s",
6211 default_presentation_delay);
6218 gst_mpd_client_get_maximum_segment_duration (GstMpdClient * client)
6220 GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
6223 g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
6224 g_return_val_if_fail (client->mpd_node != NULL, GST_CLOCK_TIME_NONE);
6226 if (client->mpd_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
6227 return client->mpd_node->maxSegmentDuration * GST_MSECOND;
6230 /* According to the DASH specification, if maxSegmentDuration is not present:
6231 "If not present, then the maximum Segment duration shall be the maximum
6232 duration of any Segment documented in this MPD"
6234 for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
6235 dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
6236 if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {