• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * Copyright (C) 2019 Collabora Ltd.
4  *   Author: Stéphane Cerveau <scerveau@collabora.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  */
21 
22 #include "gstxmlhelper.h"
23 
24 #define XML_HELPER_MINUTE_TO_SEC       60
25 #define XML_HELPER_HOUR_TO_SEC         (60 * XML_HELPER_MINUTE_TO_SEC)
26 #define XML_HELPER_DAY_TO_SEC          (24 * XML_HELPER_HOUR_TO_SEC)
27 #define XML_HELPER_MONTH_TO_SEC        (30 * XML_HELPER_DAY_TO_SEC)
28 #define XML_HELPER_YEAR_TO_SEC         (365 * XML_HELPER_DAY_TO_SEC)
29 #define XML_HELPER_MS_TO_SEC(time)     ((time) / 1000)
30 /* static methods */
31 /* this function computes decimals * 10 ^ (3 - pos) */
32 static guint
_mpd_helper_convert_to_millisecs(guint decimals,gint pos)33 _mpd_helper_convert_to_millisecs (guint decimals, gint pos)
34 {
35   guint num = 1, den = 1;
36   gint i = 3 - pos;
37 
38   while (i < 0) {
39     den *= 10;
40     i++;
41   }
42   while (i > 0) {
43     num *= 10;
44     i--;
45   }
46   /* if i == 0 we have exactly 3 decimals and nothing to do */
47   return decimals * num / den;
48 }
49 
50 static gboolean
_mpd_helper_accumulate(guint64 * v,guint64 mul,guint64 add)51 _mpd_helper_accumulate (guint64 * v, guint64 mul, guint64 add)
52 {
53   guint64 tmp;
54 
55   if (*v > G_MAXUINT64 / mul)
56     return FALSE;
57   tmp = *v * mul;
58   if (tmp > G_MAXUINT64 - add)
59     return FALSE;
60   *v = tmp + add;
61   return TRUE;
62 }
63 
64 /*
65   Duration Data Type
66 
67   The duration data type is used to specify a time interval.
68 
69   The time interval is specified in the following form "-PnYnMnDTnHnMnS" where:
70 
71     * - indicates the negative sign (optional)
72     * P indicates the period (required)
73     * nY indicates the number of years
74     * nM indicates the number of months
75     * nD indicates the number of days
76     * T indicates the start of a time section (required if you are going to specify hours, minutes, or seconds)
77     * nH indicates the number of hours
78     * nM indicates the number of minutes
79     * nS indicates the number of seconds
80 */
81 static gboolean
_mpd_helper_parse_duration(const char * str,guint64 * value)82 _mpd_helper_parse_duration (const char *str, guint64 * value)
83 {
84   gint ret, len, pos, posT;
85   gint years = -1, months = -1, days = -1, hours = -1, minutes = -1, seconds =
86       -1, decimals = -1, read;
87   gboolean have_ms = FALSE;
88   guint64 tmp_value;
89 
90   len = strlen (str);
91   GST_TRACE ("duration: %s, len %d", str, len);
92   if (strspn (str, "PT0123456789., \tHMDSY") < len) {
93     GST_WARNING ("Invalid character found: '%s'", str);
94     goto error;
95   }
96   /* skip leading/trailing whitespace */
97   while (g_ascii_isspace (str[0])) {
98     str++;
99     len--;
100   }
101   while (len > 0 && g_ascii_isspace (str[len - 1]))
102     --len;
103 
104   /* read "P" for period */
105   if (str[0] != 'P') {
106     GST_WARNING ("P not found at the beginning of the string!");
107     goto error;
108   }
109   str++;
110   len--;
111 
112   /* read "T" for time (if present) */
113   posT = strcspn (str, "T");
114   len -= posT;
115   if (posT > 0) {
116     /* there is some room between P and T, so there must be a period section */
117     /* read years, months, days */
118     do {
119       GST_TRACE ("parsing substring %s", str);
120       pos = strcspn (str, "YMD");
121       ret = sscanf (str, "%u", &read);
122       if (ret != 1) {
123         GST_WARNING ("can not read integer value from string %s!", str);
124         goto error;
125       }
126       switch (str[pos]) {
127         case 'Y':
128           if (years != -1 || months != -1 || days != -1) {
129             GST_WARNING ("year, month or day was already set");
130             goto error;
131           }
132           years = read;
133           break;
134         case 'M':
135           if (months != -1 || days != -1) {
136             GST_WARNING ("month or day was already set");
137             goto error;
138           }
139           months = read;
140           if (months >= 12) {
141             GST_WARNING ("Month out of range");
142             goto error;
143           }
144           break;
145         case 'D':
146           if (days != -1) {
147             GST_WARNING ("day was already set");
148             goto error;
149           }
150           days = read;
151           if (days >= 31) {
152             GST_WARNING ("Day out of range");
153             goto error;
154           }
155           break;
156         default:
157           GST_WARNING ("unexpected char %c!", str[pos]);
158           goto error;
159           break;
160       }
161       GST_TRACE ("read number %u type %c", read, str[pos]);
162       str += (pos + 1);
163       posT -= (pos + 1);
164     } while (posT > 0);
165   }
166 
167   if (years == -1)
168     years = 0;
169   if (months == -1)
170     months = 0;
171   if (days == -1)
172     days = 0;
173 
174   GST_TRACE ("Y:M:D=%d:%d:%d", years, months, days);
175 
176   /* read "T" for time (if present) */
177   /* here T is at pos == 0 */
178   str++;
179   len--;
180   pos = 0;
181   if (pos < len) {
182     /* T found, there is a time section */
183     /* read hours, minutes, seconds, hundredths of second */
184     do {
185       GST_TRACE ("parsing substring %s", str);
186       pos = strcspn (str, "HMS,.");
187       ret = sscanf (str, "%u", &read);
188       if (ret != 1) {
189         GST_WARNING ("can not read integer value from string %s!", str);
190         goto error;
191       }
192       switch (str[pos]) {
193         case 'H':
194           if (hours != -1 || minutes != -1 || seconds != -1) {
195             GST_WARNING ("hour, minute or second was already set");
196             goto error;
197           }
198           hours = read;
199           if (hours >= 24) {
200             GST_WARNING ("Hour out of range");
201             goto error;
202           }
203           break;
204         case 'M':
205           if (minutes != -1 || seconds != -1) {
206             GST_WARNING ("minute or second was already set");
207             goto error;
208           }
209           minutes = read;
210           if (minutes >= 60) {
211             GST_WARNING ("Minute out of range");
212             goto error;
213           }
214           break;
215         case 'S':
216           if (have_ms) {
217             /* we have read the decimal part of the seconds */
218             decimals = _mpd_helper_convert_to_millisecs (read, pos);
219             GST_TRACE ("decimal number %u (%d digits) -> %d ms", read, pos,
220                 decimals);
221           } else {
222             if (seconds != -1) {
223               GST_WARNING ("second was already set");
224               goto error;
225             }
226             /* no decimals */
227             seconds = read;
228           }
229           break;
230         case '.':
231         case ',':
232           /* we have read the integer part of a decimal number in seconds */
233           if (seconds != -1) {
234             GST_WARNING ("second was already set");
235             goto error;
236           }
237           seconds = read;
238           have_ms = TRUE;
239           break;
240         default:
241           GST_WARNING ("unexpected char %c!", str[pos]);
242           goto error;
243           break;
244       }
245       GST_TRACE ("read number %u type %c", read, str[pos]);
246       str += pos + 1;
247       len -= (pos + 1);
248     } while (len > 0);
249   }
250 
251   if (hours == -1)
252     hours = 0;
253   if (minutes == -1)
254     minutes = 0;
255   if (seconds == -1)
256     seconds = 0;
257   if (decimals == -1)
258     decimals = 0;
259   GST_TRACE ("H:M:S.MS=%d:%d:%d.%03d", hours, minutes, seconds, decimals);
260 
261   tmp_value = 0;
262   if (!_mpd_helper_accumulate (&tmp_value, 1, years)
263       || !_mpd_helper_accumulate (&tmp_value, 365, months * 30)
264       || !_mpd_helper_accumulate (&tmp_value, 1, days)
265       || !_mpd_helper_accumulate (&tmp_value, 24, hours)
266       || !_mpd_helper_accumulate (&tmp_value, 60, minutes)
267       || !_mpd_helper_accumulate (&tmp_value, 60, seconds)
268       || !_mpd_helper_accumulate (&tmp_value, 1000, decimals))
269     goto error;
270 
271   /* ensure it can be converted from milliseconds to nanoseconds */
272   if (tmp_value > G_MAXUINT64 / 1000000)
273     goto error;
274 
275   *value = tmp_value;
276   return TRUE;
277 
278 error:
279   return FALSE;
280 }
281 
282 static gboolean
_mpd_helper_validate_no_whitespace(const char * s)283 _mpd_helper_validate_no_whitespace (const char *s)
284 {
285   return !strpbrk (s, "\r\n\t ");
286 }
287 
288 /* API */
289 
290 GstXMLRange *
gst_xml_helper_clone_range(GstXMLRange * range)291 gst_xml_helper_clone_range (GstXMLRange * range)
292 {
293   GstXMLRange *clone = NULL;
294 
295   if (range) {
296     clone = g_slice_new0 (GstXMLRange);
297     clone->first_byte_pos = range->first_byte_pos;
298     clone->last_byte_pos = range->last_byte_pos;
299   }
300 
301   return clone;
302 }
303 
304 GstXMLRatio *
gst_xml_helper_clone_ratio(GstXMLRatio * ratio)305 gst_xml_helper_clone_ratio (GstXMLRatio * ratio)
306 {
307   GstXMLRatio *clone = NULL;
308 
309   if (ratio) {
310     clone = g_slice_new0 (GstXMLRatio);
311     clone->num = ratio->num;
312     clone->den = ratio->den;
313   }
314 
315   return clone;
316 }
317 
318 GstXMLFrameRate *
gst_xml_helper_clone_frame_rate(GstXMLFrameRate * frameRate)319 gst_xml_helper_clone_frame_rate (GstXMLFrameRate * frameRate)
320 {
321   GstXMLFrameRate *clone = NULL;
322 
323   if (frameRate) {
324     clone = g_slice_new0 (GstXMLFrameRate);
325     clone->num = frameRate->num;
326     clone->den = frameRate->den;
327   }
328 
329   return clone;
330 }
331 
332 /* XML property get method */
333 gboolean
gst_xml_helper_get_prop_validated_string(xmlNode * a_node,const gchar * property_name,gchar ** property_value,gboolean (* validate)(const char *))334 gst_xml_helper_get_prop_validated_string (xmlNode * a_node,
335     const gchar * property_name, gchar ** property_value,
336     gboolean (*validate) (const char *))
337 {
338   xmlChar *prop_string;
339   gboolean exists = FALSE;
340 
341   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
342   if (prop_string) {
343     if (validate && !(*validate) ((const char *) prop_string)) {
344       GST_WARNING ("Validation failure: %s", prop_string);
345       xmlFree (prop_string);
346       return FALSE;
347     }
348     *property_value = (gchar *) prop_string;
349     exists = TRUE;
350     GST_LOG (" - %s: %s", property_name, prop_string);
351   }
352 
353   return exists;
354 }
355 
356 gboolean
gst_xml_helper_get_ns_prop_string(xmlNode * a_node,const gchar * ns_name,const gchar * property_name,gchar ** property_value)357 gst_xml_helper_get_ns_prop_string (xmlNode * a_node,
358     const gchar * ns_name, const gchar * property_name, gchar ** property_value)
359 {
360   xmlChar *prop_string;
361   gboolean exists = FALSE;
362 
363   prop_string =
364       xmlGetNsProp (a_node, (const xmlChar *) property_name,
365       (const xmlChar *) ns_name);
366   if (prop_string) {
367     *property_value = (gchar *) prop_string;
368     exists = TRUE;
369     GST_LOG (" - %s:%s: %s", ns_name, property_name, prop_string);
370   }
371 
372   return exists;
373 }
374 
375 gboolean
gst_xml_helper_get_prop_string(xmlNode * a_node,const gchar * property_name,gchar ** property_value)376 gst_xml_helper_get_prop_string (xmlNode * a_node,
377     const gchar * property_name, gchar ** property_value)
378 {
379   return gst_xml_helper_get_prop_validated_string (a_node, property_name,
380       property_value, NULL);
381 }
382 
383 gboolean
gst_xml_helper_get_prop_string_vector_type(xmlNode * a_node,const gchar * property_name,gchar *** property_value)384 gst_xml_helper_get_prop_string_vector_type (xmlNode * a_node,
385     const gchar * property_name, gchar *** property_value)
386 {
387   xmlChar *prop_string;
388   gchar **prop_string_vector = NULL;
389   guint i = 0;
390   gboolean exists = FALSE;
391 
392   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
393   if (prop_string) {
394     prop_string_vector = g_strsplit ((gchar *) prop_string, " ", -1);
395     if (prop_string_vector) {
396       exists = TRUE;
397       *property_value = prop_string_vector;
398       GST_LOG (" - %s:", property_name);
399       while (prop_string_vector[i]) {
400         GST_LOG ("    %s", prop_string_vector[i]);
401         i++;
402       }
403     } else {
404       GST_WARNING ("Scan of string vector property failed!");
405     }
406     xmlFree (prop_string);
407   }
408 
409   return exists;
410 }
411 
412 gboolean
gst_xml_helper_get_prop_signed_integer(xmlNode * a_node,const gchar * property_name,gint default_val,gint * property_value)413 gst_xml_helper_get_prop_signed_integer (xmlNode * a_node,
414     const gchar * property_name, gint default_val, gint * property_value)
415 {
416   xmlChar *prop_string;
417   gboolean exists = FALSE;
418 
419   *property_value = default_val;
420   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
421   if (prop_string) {
422     if (sscanf ((const gchar *) prop_string, "%d", property_value) == 1) {
423       exists = TRUE;
424       GST_LOG (" - %s: %d", property_name, *property_value);
425     } else {
426       GST_WARNING
427           ("failed to parse signed integer property %s from xml string %s",
428           property_name, prop_string);
429     }
430     xmlFree (prop_string);
431   }
432 
433   return exists;
434 }
435 
436 gboolean
gst_xml_helper_get_prop_unsigned_integer(xmlNode * a_node,const gchar * property_name,guint default_val,guint * property_value)437 gst_xml_helper_get_prop_unsigned_integer (xmlNode * a_node,
438     const gchar * property_name, guint default_val, guint * property_value)
439 {
440   xmlChar *prop_string;
441   gboolean exists = FALSE;
442 
443   *property_value = default_val;
444   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
445   if (prop_string) {
446     if (sscanf ((gchar *) prop_string, "%u", property_value) == 1 &&
447         strstr ((gchar *) prop_string, "-") == NULL) {
448       exists = TRUE;
449       GST_LOG (" - %s: %u", property_name, *property_value);
450     } else {
451       GST_WARNING
452           ("failed to parse unsigned integer property %s from xml string %s",
453           property_name, prop_string);
454       /* sscanf might have written to *property_value. Restore to default */
455       *property_value = default_val;
456     }
457     xmlFree (prop_string);
458   }
459 
460   return exists;
461 }
462 
463 gboolean
gst_xml_helper_get_prop_unsigned_integer_64(xmlNode * a_node,const gchar * property_name,guint64 default_val,guint64 * property_value)464 gst_xml_helper_get_prop_unsigned_integer_64 (xmlNode * a_node,
465     const gchar * property_name, guint64 default_val, guint64 * property_value)
466 {
467   xmlChar *prop_string;
468   gboolean exists = FALSE;
469 
470   *property_value = default_val;
471   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
472   if (prop_string) {
473     if (g_ascii_string_to_unsigned ((gchar *) prop_string, 10, 0, G_MAXUINT64,
474             property_value, NULL)) {
475       exists = TRUE;
476       GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
477     } else {
478       GST_WARNING
479           ("failed to parse unsigned integer property %s from xml string %s",
480           property_name, prop_string);
481     }
482     xmlFree (prop_string);
483   }
484 
485   return exists;
486 }
487 
488 gboolean
gst_xml_helper_get_prop_uint_vector_type(xmlNode * a_node,const gchar * property_name,guint ** property_value,guint * value_size)489 gst_xml_helper_get_prop_uint_vector_type (xmlNode * a_node,
490     const gchar * property_name, guint ** property_value, guint * value_size)
491 {
492   xmlChar *prop_string;
493   gchar **str_vector;
494   guint *prop_uint_vector = NULL, i;
495   gboolean exists = FALSE;
496 
497   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
498   if (prop_string) {
499     str_vector = g_strsplit ((gchar *) prop_string, " ", -1);
500     if (str_vector) {
501       *value_size = g_strv_length (str_vector);
502       prop_uint_vector = g_malloc (*value_size * sizeof (guint));
503       if (prop_uint_vector) {
504         exists = TRUE;
505         GST_LOG (" - %s:", property_name);
506         for (i = 0; i < *value_size; i++) {
507           if (sscanf ((gchar *) str_vector[i], "%u", &prop_uint_vector[i]) == 1
508               && strstr (str_vector[i], "-") == NULL) {
509             GST_LOG ("    %u", prop_uint_vector[i]);
510           } else {
511             GST_WARNING
512                 ("failed to parse uint vector type property %s from xml string %s",
513                 property_name, str_vector[i]);
514             /* there is no special value to put in prop_uint_vector[i] to
515              * signal it is invalid, so we just clean everything and return
516              * FALSE
517              */
518             g_free (prop_uint_vector);
519             prop_uint_vector = NULL;
520             exists = FALSE;
521             break;
522           }
523         }
524         *property_value = prop_uint_vector;
525       } else {
526         GST_WARNING ("Array allocation failed!");
527       }
528     } else {
529       GST_WARNING ("Scan of uint vector property failed!");
530     }
531     xmlFree (prop_string);
532     g_strfreev (str_vector);
533   }
534 
535   return exists;
536 }
537 
538 gboolean
gst_xml_helper_get_prop_double(xmlNode * a_node,const gchar * property_name,gdouble * property_value)539 gst_xml_helper_get_prop_double (xmlNode * a_node,
540     const gchar * property_name, gdouble * property_value)
541 {
542   xmlChar *prop_string;
543   gboolean exists = FALSE;
544 
545   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
546   if (prop_string) {
547     if (sscanf ((gchar *) prop_string, "%lf", property_value) == 1) {
548       exists = TRUE;
549       GST_LOG (" - %s: %lf", property_name, *property_value);
550     } else {
551       GST_WARNING ("failed to parse double property %s from xml string %s",
552           property_name, prop_string);
553     }
554     xmlFree (prop_string);
555   }
556 
557   return exists;
558 }
559 
560 gboolean
gst_xml_helper_get_prop_boolean(xmlNode * a_node,const gchar * property_name,gboolean default_val,gboolean * property_value)561 gst_xml_helper_get_prop_boolean (xmlNode * a_node,
562     const gchar * property_name, gboolean default_val,
563     gboolean * property_value)
564 {
565   xmlChar *prop_string;
566   gboolean exists = FALSE;
567 
568   *property_value = default_val;
569   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
570   if (prop_string) {
571     if (xmlStrcmp (prop_string, (xmlChar *) "false") == 0) {
572       exists = TRUE;
573       *property_value = FALSE;
574       GST_LOG (" - %s: false", property_name);
575     } else if (xmlStrcmp (prop_string, (xmlChar *) "true") == 0) {
576       exists = TRUE;
577       *property_value = TRUE;
578       GST_LOG (" - %s: true", property_name);
579     } else {
580       GST_WARNING ("failed to parse boolean property %s from xml string %s",
581           property_name, prop_string);
582     }
583     xmlFree (prop_string);
584   }
585 
586   return exists;
587 }
588 
589 gboolean
gst_xml_helper_get_prop_range(xmlNode * a_node,const gchar * property_name,GstXMLRange ** property_value)590 gst_xml_helper_get_prop_range (xmlNode * a_node,
591     const gchar * property_name, GstXMLRange ** property_value)
592 {
593   xmlChar *prop_string;
594   guint64 first_byte_pos = 0, last_byte_pos = -1;
595   guint len, pos;
596   gchar *str;
597   gboolean exists = FALSE;
598 
599   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
600   if (prop_string) {
601     len = xmlStrlen (prop_string);
602     str = (gchar *) prop_string;
603     GST_TRACE ("range: %s, len %d", str, len);
604 
605     /* find "-" */
606     pos = strcspn (str, "-");
607     if (pos >= len) {
608       GST_TRACE ("pos %d >= len %d", pos, len);
609       goto error;
610     }
611     if (pos == 0) {
612       GST_TRACE ("pos == 0, but first_byte_pos is not optional");
613       goto error;
614     }
615 
616     /* read first_byte_pos */
617 
618     /* replace str[pos] with '\0' since we only want to read the
619      * first_byte_pos, and g_ascii_string_to_unsigned requires the entire
620      * string to be a single number, which is exactly what we want */
621     str[pos] = '\0';
622     if (!g_ascii_string_to_unsigned (str, 10, 0, G_MAXUINT64, &first_byte_pos,
623             NULL)) {
624       /* restore the '-' sign */
625       str[pos] = '-';
626       goto error;
627     }
628     /* restore the '-' sign */
629     str[pos] = '-';
630 
631     /* read last_byte_pos, which is optional */
632     if (pos < (len - 1) && !g_ascii_string_to_unsigned (str + pos + 1, 10, 0,
633             G_MAXUINT64, &last_byte_pos, NULL)) {
634       goto error;
635     }
636     /* malloc return data structure */
637     *property_value = g_slice_new0 (GstXMLRange);
638     exists = TRUE;
639     (*property_value)->first_byte_pos = first_byte_pos;
640     (*property_value)->last_byte_pos = last_byte_pos;
641     xmlFree (prop_string);
642     GST_LOG (" - %s: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
643         property_name, first_byte_pos, last_byte_pos);
644   }
645 
646   return exists;
647 
648 error:
649   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
650       prop_string);
651   xmlFree (prop_string);
652   return FALSE;
653 }
654 
655 gboolean
gst_xml_helper_get_prop_ratio(xmlNode * a_node,const gchar * property_name,GstXMLRatio ** property_value)656 gst_xml_helper_get_prop_ratio (xmlNode * a_node,
657     const gchar * property_name, GstXMLRatio ** property_value)
658 {
659   xmlChar *prop_string;
660   guint num = 0, den = 1;
661   guint len, pos;
662   gchar *str;
663   gboolean exists = FALSE;
664 
665   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
666   if (prop_string) {
667     len = xmlStrlen (prop_string);
668     str = (gchar *) prop_string;
669     GST_TRACE ("ratio: %s, len %d", str, len);
670 
671     /* read ":" */
672     pos = strcspn (str, ":");
673     if (pos >= len) {
674       GST_TRACE ("pos %d >= len %d", pos, len);
675       goto error;
676     }
677     /* search for negative sign */
678     if (strstr (str, "-") != NULL) {
679       goto error;
680     }
681     /* read num */
682     if (pos != 0) {
683       if (sscanf (str, "%u", &num) != 1) {
684         goto error;
685       }
686     }
687     /* read den */
688     if (pos < (len - 1)) {
689       if (sscanf (str + pos + 1, "%u", &den) != 1) {
690         goto error;
691       }
692     }
693     /* malloc return data structure */
694     *property_value = g_slice_new0 (GstXMLRatio);
695     exists = TRUE;
696     (*property_value)->num = num;
697     (*property_value)->den = den;
698     xmlFree (prop_string);
699     GST_LOG (" - %s: %u:%u", property_name, num, den);
700   }
701 
702   return exists;
703 
704 error:
705   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
706       prop_string);
707   xmlFree (prop_string);
708   return FALSE;
709 }
710 
711 gboolean
gst_xml_helper_get_prop_framerate(xmlNode * a_node,const gchar * property_name,GstXMLFrameRate ** property_value)712 gst_xml_helper_get_prop_framerate (xmlNode * a_node,
713     const gchar * property_name, GstXMLFrameRate ** property_value)
714 {
715   xmlChar *prop_string;
716   guint num = 0, den = 1;
717   guint len, pos;
718   gchar *str;
719   gboolean exists = FALSE;
720 
721   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
722   if (prop_string) {
723     len = xmlStrlen (prop_string);
724     str = (gchar *) prop_string;
725     GST_TRACE ("framerate: %s, len %d", str, len);
726 
727     /* search for negative sign */
728     if (strstr (str, "-") != NULL) {
729       goto error;
730     }
731 
732     /* read "/" if available */
733     pos = strcspn (str, "/");
734     /* read num */
735     if (pos != 0) {
736       if (sscanf (str, "%u", &num) != 1) {
737         goto error;
738       }
739     }
740     /* read den (if available) */
741     if (pos < (len - 1)) {
742       if (sscanf (str + pos + 1, "%u", &den) != 1) {
743         goto error;
744       }
745     }
746     /* alloc return data structure */
747     *property_value = g_slice_new0 (GstXMLFrameRate);
748     exists = TRUE;
749     (*property_value)->num = num;
750     (*property_value)->den = den;
751     xmlFree (prop_string);
752     if (den == 1)
753       GST_LOG (" - %s: %u", property_name, num);
754     else
755       GST_LOG (" - %s: %u/%u", property_name, num, den);
756   }
757 
758   return exists;
759 
760 error:
761   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
762       prop_string);
763   xmlFree (prop_string);
764   return FALSE;
765 }
766 
767 gboolean
gst_xml_helper_get_prop_cond_uint(xmlNode * a_node,const gchar * property_name,GstXMLConditionalUintType ** property_value)768 gst_xml_helper_get_prop_cond_uint (xmlNode * a_node,
769     const gchar * property_name, GstXMLConditionalUintType ** property_value)
770 {
771   xmlChar *prop_string;
772   gchar *str;
773   gboolean flag;
774   guint val;
775   gboolean exists = FALSE;
776 
777   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
778   if (prop_string) {
779     str = (gchar *) prop_string;
780     GST_TRACE ("conditional uint: %s", str);
781 
782     if (strcmp (str, "false") == 0) {
783       flag = FALSE;
784       val = 0;
785     } else if (strcmp (str, "true") == 0) {
786       flag = TRUE;
787       val = 0;
788     } else {
789       flag = TRUE;
790       if (sscanf (str, "%u", &val) != 1 || strstr (str, "-") != NULL)
791         goto error;
792     }
793 
794     /* alloc return data structure */
795     *property_value = g_slice_new0 (GstXMLConditionalUintType);
796     exists = TRUE;
797     (*property_value)->flag = flag;
798     (*property_value)->value = val;
799     xmlFree (prop_string);
800     GST_LOG (" - %s: flag=%s val=%u", property_name, flag ? "true" : "false",
801         val);
802   }
803 
804   return exists;
805 
806 error:
807   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
808       prop_string);
809   xmlFree (prop_string);
810   return FALSE;
811 }
812 
813 gboolean
gst_xml_helper_get_prop_dateTime(xmlNode * a_node,const gchar * property_name,GstDateTime ** property_value)814 gst_xml_helper_get_prop_dateTime (xmlNode * a_node,
815     const gchar * property_name, GstDateTime ** property_value)
816 {
817   xmlChar *prop_string;
818   gchar *str;
819   gint ret, pos;
820   gint year, month, day, hour, minute;
821   gdouble second;
822   gboolean exists = FALSE;
823   gfloat tzoffset = 0.0;
824   gint gmt_offset_hour = -99, gmt_offset_min = -99;
825 
826   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
827   if (prop_string) {
828     str = (gchar *) prop_string;
829     GST_TRACE ("dateTime: %s, len %d", str, xmlStrlen (prop_string));
830     /* parse year */
831     ret = sscanf (str, "%d", &year);
832     if (ret != 1 || year <= 0)
833       goto error;
834     pos = strcspn (str, "-");
835     str += (pos + 1);
836     GST_TRACE (" - year %d", year);
837     /* parse month */
838     ret = sscanf (str, "%d", &month);
839     if (ret != 1 || month <= 0)
840       goto error;
841     pos = strcspn (str, "-");
842     str += (pos + 1);
843     GST_TRACE (" - month %d", month);
844     /* parse day */
845     ret = sscanf (str, "%d", &day);
846     if (ret != 1 || day <= 0)
847       goto error;
848     pos = strcspn (str, "T");
849     str += (pos + 1);
850     GST_TRACE (" - day %d", day);
851     /* parse hour */
852     ret = sscanf (str, "%d", &hour);
853     if (ret != 1 || hour < 0)
854       goto error;
855     pos = strcspn (str, ":");
856     str += (pos + 1);
857     GST_TRACE (" - hour %d", hour);
858     /* parse minute */
859     ret = sscanf (str, "%d", &minute);
860     if (ret != 1 || minute < 0)
861       goto error;
862     pos = strcspn (str, ":");
863     str += (pos + 1);
864     GST_TRACE (" - minute %d", minute);
865     /* parse second */
866     ret = sscanf (str, "%lf", &second);
867     if (ret != 1 || second < 0)
868       goto error;
869     GST_TRACE (" - second %lf", second);
870 
871     GST_LOG (" - %s: %4d/%02d/%02d %02d:%02d:%09.6lf", property_name,
872         year, month, day, hour, minute, second);
873 
874     if (strrchr (str, '+') || strrchr (str, '-')) {
875       /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
876       gint gmt_offset = -1;
877       gchar *plus_pos = NULL;
878       gchar *neg_pos = NULL;
879       gchar *pos = NULL;
880 
881       GST_LOG ("Checking for timezone information");
882 
883       /* check if there is timezone info */
884       plus_pos = strrchr (str, '+');
885       neg_pos = strrchr (str, '-');
886       if (plus_pos)
887         pos = plus_pos + 1;
888       else if (neg_pos)
889         pos = neg_pos + 1;
890 
891       if (pos && strlen (pos) >= 3) {
892         gint ret_tz;
893         if (pos[2] == ':')
894           ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
895         else
896           ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
897 
898         GST_DEBUG ("Parsing timezone: %s", pos);
899 
900         if (ret_tz == 2) {
901           if (neg_pos != NULL && neg_pos + 1 == pos) {
902             gmt_offset_hour *= -1;
903             gmt_offset_min *= -1;
904           }
905           gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
906 
907           tzoffset = gmt_offset / 60.0;
908 
909           GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
910         } else
911           GST_WARNING ("Failed to parse timezone information");
912       }
913     }
914 
915     exists = TRUE;
916     *property_value =
917         gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
918     xmlFree (prop_string);
919   }
920 
921   return exists;
922 
923 error:
924   GST_WARNING ("failed to parse property %s from xml string %s", property_name,
925       prop_string);
926   xmlFree (prop_string);
927   return FALSE;
928 }
929 
930 gboolean
gst_xml_helper_get_prop_duration(xmlNode * a_node,const gchar * property_name,guint64 default_value,guint64 * property_value)931 gst_xml_helper_get_prop_duration (xmlNode * a_node,
932     const gchar * property_name, guint64 default_value,
933     guint64 * property_value)
934 {
935   xmlChar *prop_string;
936   gchar *str;
937   gboolean exists = FALSE;
938 
939   *property_value = default_value;
940   prop_string = xmlGetProp (a_node, (const xmlChar *) property_name);
941   if (prop_string) {
942     str = (gchar *) prop_string;
943     if (!_mpd_helper_parse_duration (str, property_value))
944       goto error;
945     GST_LOG (" - %s: %" G_GUINT64_FORMAT, property_name, *property_value);
946     xmlFree (prop_string);
947     exists = TRUE;
948   }
949   return exists;
950 
951 error:
952   xmlFree (prop_string);
953   return FALSE;
954 }
955 
956 gboolean
gst_xml_helper_get_node_content(xmlNode * a_node,gchar ** content)957 gst_xml_helper_get_node_content (xmlNode * a_node, gchar ** content)
958 {
959   xmlChar *node_content = NULL;
960   gboolean exists = FALSE;
961 
962   node_content = xmlNodeGetContent (a_node);
963   if (node_content) {
964     exists = TRUE;
965     *content = (gchar *) node_content;
966     GST_LOG (" - %s: %s", a_node->name, *content);
967   }
968 
969   return exists;
970 }
971 
972 gboolean
gst_xml_helper_get_node_as_string(xmlNode * a_node,gchar ** content)973 gst_xml_helper_get_node_as_string (xmlNode * a_node, gchar ** content)
974 {
975   gboolean exists = FALSE;
976   const char *txt_encoding;
977   xmlOutputBufferPtr out_buf;
978   xmlNode *ncopy = NULL;
979 
980   txt_encoding = (const char *) a_node->doc->encoding;
981   out_buf = xmlAllocOutputBuffer (NULL);
982   g_assert (out_buf != NULL);
983 
984   /* Need to make a copy of XML element so that it includes namespaces
985      in the output, so that the resulting string can be parsed by an XML parser
986      that is namespace aware.
987      Use extended=1 for recursive copy (properties, namespaces and children) */
988   ncopy = xmlDocCopyNode (a_node, a_node->doc, 1);
989 
990   if (!ncopy) {
991     GST_WARNING ("Failed to clone XML node");
992     goto done;
993   }
994   xmlNodeDumpOutput (out_buf, ncopy->doc, ncopy, 0, 0, txt_encoding);
995 
996   (void) xmlOutputBufferFlush (out_buf);
997 #ifdef LIBXML2_NEW_BUFFER
998   if (xmlOutputBufferGetSize (out_buf) > 0) {
999     *content =
1000         (gchar *) xmlStrndup (xmlOutputBufferGetContent (out_buf),
1001         xmlOutputBufferGetSize (out_buf));
1002     exists = TRUE;
1003   }
1004 #else
1005   if (out_buf->conv && out_buf->conv->use > 0) {
1006     *content =
1007         (gchar *) xmlStrndup (out_buf->conv->content, out_buf->conv->use);
1008     exists = TRUE;
1009   } else if (out_buf->buffer && out_buf->buffer->use > 0) {
1010     *content =
1011         (gchar *) xmlStrndup (out_buf->buffer->content, out_buf->buffer->use);
1012     exists = TRUE;
1013   }
1014 #endif // LIBXML2_NEW_BUFFER
1015   xmlFreeNode (ncopy);
1016 done:
1017   (void) xmlOutputBufferClose (out_buf);
1018 
1019   if (exists) {
1020     GST_LOG (" - %s: %s", a_node->name, *content);
1021   }
1022   return exists;
1023 }
1024 
1025 gchar *
gst_xml_helper_get_node_namespace(xmlNode * a_node,const gchar * prefix)1026 gst_xml_helper_get_node_namespace (xmlNode * a_node, const gchar * prefix)
1027 {
1028   xmlNs *curr_ns;
1029   gchar *namespace = NULL;
1030 
1031   if (prefix == NULL) {
1032     /* return the default namespace */
1033     if (a_node->ns) {
1034       namespace = xmlMemStrdup ((const gchar *) a_node->ns->href);
1035       if (namespace) {
1036         GST_LOG (" - default namespace: %s", namespace);
1037       }
1038     }
1039   } else {
1040     /* look for the specified prefix in the namespace list */
1041     for (curr_ns = a_node->ns; curr_ns; curr_ns = curr_ns->next) {
1042       if (xmlStrcmp (curr_ns->prefix, (xmlChar *) prefix) == 0) {
1043         namespace = xmlMemStrdup ((const gchar *) curr_ns->href);
1044         if (namespace) {
1045           GST_LOG (" - %s namespace: %s", curr_ns->prefix, curr_ns->href);
1046         }
1047       }
1048     }
1049   }
1050 
1051   return namespace;
1052 }
1053 
1054 gboolean
gst_xml_helper_get_prop_string_stripped(xmlNode * a_node,const gchar * property_name,gchar ** property_value)1055 gst_xml_helper_get_prop_string_stripped (xmlNode * a_node,
1056     const gchar * property_name, gchar ** property_value)
1057 {
1058   gboolean ret;
1059   ret = gst_xml_helper_get_prop_string (a_node, property_name, property_value);
1060   if (ret)
1061     *property_value = g_strstrip (*property_value);
1062   return ret;
1063 }
1064 
1065 gboolean
gst_xml_helper_get_prop_string_no_whitespace(xmlNode * a_node,const gchar * property_name,gchar ** property_value)1066 gst_xml_helper_get_prop_string_no_whitespace (xmlNode * a_node,
1067     const gchar * property_name, gchar ** property_value)
1068 {
1069   return gst_xml_helper_get_prop_validated_string (a_node, property_name,
1070       property_value, _mpd_helper_validate_no_whitespace);
1071 }
1072 
1073 
1074 /* XML property set method */
1075 
1076 void
gst_xml_helper_set_prop_string(xmlNodePtr node,const gchar * name,gchar * value)1077 gst_xml_helper_set_prop_string (xmlNodePtr node, const gchar * name,
1078     gchar * value)
1079 {
1080   if (value)
1081     xmlSetProp (node, (xmlChar *) name, (xmlChar *) value);
1082 }
1083 
1084 void
gst_xml_helper_set_prop_boolean(xmlNodePtr node,const gchar * name,gboolean value)1085 gst_xml_helper_set_prop_boolean (xmlNodePtr node, const gchar * name,
1086     gboolean value)
1087 {
1088   if (value)
1089     xmlSetProp (node, (xmlChar *) name, (xmlChar *) "true");
1090   else
1091     xmlSetProp (node, (xmlChar *) name, (xmlChar *) "false");
1092 }
1093 
1094 void
gst_xml_helper_set_prop_int(xmlNodePtr node,const gchar * name,gint value)1095 gst_xml_helper_set_prop_int (xmlNodePtr node, const gchar * name, gint value)
1096 {
1097   gchar *text;
1098   text = g_strdup_printf ("%d", value);
1099   xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1100   g_free (text);
1101 }
1102 
1103 void
gst_xml_helper_set_prop_uint(xmlNodePtr node,const gchar * name,guint value)1104 gst_xml_helper_set_prop_uint (xmlNodePtr node, const gchar * name, guint value)
1105 {
1106   gchar *text;
1107   text = g_strdup_printf ("%d", value);
1108   xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1109   g_free (text);
1110 }
1111 
1112 void
gst_xml_helper_set_prop_int64(xmlNodePtr node,const gchar * name,gint64 value)1113 gst_xml_helper_set_prop_int64 (xmlNodePtr node, const gchar * name,
1114     gint64 value)
1115 {
1116   gchar *text;
1117   text = g_strdup_printf ("%" G_GINT64_FORMAT, value);
1118   xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1119   g_free (text);
1120 }
1121 
1122 void
gst_xml_helper_set_prop_uint64(xmlNodePtr node,const gchar * name,guint64 value)1123 gst_xml_helper_set_prop_uint64 (xmlNodePtr node, const gchar * name,
1124     guint64 value)
1125 {
1126   gchar *text;
1127   text = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1128   xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1129   g_free (text);
1130 }
1131 
1132 void
gst_xml_helper_set_prop_double(xmlNodePtr node,const gchar * name,gdouble value)1133 gst_xml_helper_set_prop_double (xmlNodePtr node, const gchar * name,
1134     gdouble value)
1135 {
1136   gchar *text;
1137   text = g_strdup_printf ("%lf", value);
1138   xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1139   g_free (text);
1140 }
1141 
1142 void
gst_xml_helper_set_prop_uint_vector_type(xmlNode * node,const gchar * name,guint * value,guint value_size)1143 gst_xml_helper_set_prop_uint_vector_type (xmlNode * node, const gchar * name,
1144     guint * value, guint value_size)
1145 {
1146   int i;
1147   gchar *text = NULL;
1148   gchar *prev;
1149   gchar *temp;
1150 
1151   for (i = 0; i < value_size; i++) {
1152     temp = g_strdup_printf ("%d", value[i]);
1153     prev = text;
1154     text = g_strjoin (" ", text, prev, NULL);
1155     g_free (prev);
1156     g_free (temp);
1157   }
1158 
1159   if (text) {
1160     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1161     g_free (text);
1162   }
1163 }
1164 
1165 void
gst_xml_helper_set_prop_date_time(xmlNodePtr node,const gchar * name,GstDateTime * value)1166 gst_xml_helper_set_prop_date_time (xmlNodePtr node, const gchar * name,
1167     GstDateTime * value)
1168 {
1169   gchar *text;
1170   if (value) {
1171     text = gst_date_time_to_iso8601_string (value);
1172     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1173     g_free (text);
1174   }
1175 }
1176 
1177 void
gst_xml_helper_set_prop_duration(xmlNode * node,const gchar * name,guint64 value)1178 gst_xml_helper_set_prop_duration (xmlNode * node, const gchar * name,
1179     guint64 value)
1180 {
1181   gchar *text;
1182   gint years, months, days, hours, minutes, seconds, milliseconds;
1183   if (value) {
1184     years = (gint) (XML_HELPER_MS_TO_SEC (value) / (XML_HELPER_YEAR_TO_SEC));
1185     months =
1186         (gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_YEAR_TO_SEC) /
1187         XML_HELPER_MONTH_TO_SEC);
1188     days =
1189         (gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_MONTH_TO_SEC) /
1190         XML_HELPER_DAY_TO_SEC);
1191     hours =
1192         (gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_DAY_TO_SEC) /
1193         XML_HELPER_HOUR_TO_SEC);
1194     minutes =
1195         (gint) ((XML_HELPER_MS_TO_SEC (value) % XML_HELPER_HOUR_TO_SEC) /
1196         XML_HELPER_MINUTE_TO_SEC);
1197     seconds = (gint) (XML_HELPER_MS_TO_SEC (value) % XML_HELPER_MINUTE_TO_SEC);
1198     milliseconds = value % 1000;
1199 
1200     text =
1201         g_strdup_printf ("P%dY%dM%dDT%dH%dM%d.%dS", years, months, days, hours,
1202         minutes, seconds, milliseconds);
1203     GST_LOG ("duration %" G_GUINT64_FORMAT " -> %s", value, text);
1204     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1205     g_free (text);
1206   }
1207 }
1208 
1209 void
gst_xml_helper_set_prop_ratio(xmlNodePtr node,const gchar * name,GstXMLRatio * value)1210 gst_xml_helper_set_prop_ratio (xmlNodePtr node, const gchar * name,
1211     GstXMLRatio * value)
1212 {
1213   gchar *text;
1214   if (value) {
1215     text = g_strdup_printf ("%d:%d", value->num, value->den);
1216     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1217     g_free (text);
1218   }
1219 }
1220 
1221 
1222 void
gst_xml_helper_set_prop_framerate(xmlNodePtr node,const gchar * name,GstXMLFrameRate * value)1223 gst_xml_helper_set_prop_framerate (xmlNodePtr node, const gchar * name,
1224     GstXMLFrameRate * value)
1225 {
1226   gchar *text;
1227   if (value) {
1228     text = g_strdup_printf ("%d/%d", value->num, value->den);
1229     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1230     g_free (text);
1231   }
1232 }
1233 
1234 void
gst_xml_helper_set_prop_range(xmlNodePtr node,const gchar * name,GstXMLRange * value)1235 gst_xml_helper_set_prop_range (xmlNodePtr node, const gchar * name,
1236     GstXMLRange * value)
1237 {
1238   gchar *text;
1239   if (value) {
1240     text =
1241         g_strdup_printf ("%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
1242         value->first_byte_pos, value->last_byte_pos);
1243     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1244     g_free (text);
1245   }
1246 }
1247 
1248 void
gst_xml_helper_set_prop_cond_uint(xmlNodePtr node,const gchar * name,GstXMLConditionalUintType * cond)1249 gst_xml_helper_set_prop_cond_uint (xmlNodePtr node, const gchar * name,
1250     GstXMLConditionalUintType * cond)
1251 {
1252   gchar *text;
1253   if (cond) {
1254     if (cond->flag)
1255       if (cond->value)
1256         text = g_strdup_printf ("%d", cond->value);
1257       else
1258         text = g_strdup_printf ("%s", "true");
1259     else
1260       text = g_strdup_printf ("%s", "false");
1261 
1262     xmlSetProp (node, (xmlChar *) name, (xmlChar *) text);
1263     g_free (text);
1264   }
1265 }
1266 
1267 void
gst_xml_helper_set_content(xmlNodePtr node,gchar * content)1268 gst_xml_helper_set_content (xmlNodePtr node, gchar * content)
1269 {
1270   if (content)
1271     xmlNodeSetContent (node, (xmlChar *) content);
1272 }
1273