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