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