1 /* GStreamer
2 *
3 * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
4 *
5 * gst-validate-utils.c - Some utility functions
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23 #include "config.h"
24
25 #include <math.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <glib.h>
31
32 #ifdef G_OS_UNIX
33 #include <glib-unix.h>
34 #include <sys/wait.h>
35 #endif
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 #include "gst-validate-utils.h"
42 #include <gst/gst.h>
43
44 #define PARSER_BOOLEAN_EQUALITY_THRESHOLD (1e-10)
45 #define PARSER_MAX_TOKEN_SIZE 256
46 #define PARSER_MAX_ARGUMENT_COUNT 10
47
48 static GRegex *_clean_structs_lines = NULL;
49
50 typedef struct
51 {
52 const gchar *str;
53 gint len;
54 gint pos;
55 jmp_buf err_jmp_buf;
56 const gchar *error;
57 void *user_data;
58 GstValidateParseVariableFunc variable_func;
59 } MathParser;
60
61 static gdouble _read_power (MathParser * parser);
62
63 static void
_error(MathParser * parser,const gchar * err)64 _error (MathParser * parser, const gchar * err)
65 {
66 parser->error = err;
67 longjmp (parser->err_jmp_buf, 1);
68 }
69
70 static gchar
_peek(MathParser * parser)71 _peek (MathParser * parser)
72 {
73 if (parser->pos < parser->len)
74 return parser->str[parser->pos];
75 _error (parser, "Tried to read past end of string!");
76 return '\0';
77 }
78
79 static gchar
_peek_n(MathParser * parser,gint n)80 _peek_n (MathParser * parser, gint n)
81 {
82 if (parser->pos + n < parser->len)
83 return parser->str[parser->pos + n];
84 _error (parser, "Tried to read past end of string!");
85 return '\0';
86 }
87
88 static gchar
_next(MathParser * parser)89 _next (MathParser * parser)
90 {
91 if (parser->pos < parser->len)
92 return parser->str[parser->pos++];
93 _error (parser, "Tried to read past end of string!");
94 return '\0';
95 }
96
97 static gdouble
_read_double(MathParser * parser)98 _read_double (MathParser * parser)
99 {
100 gchar c, token[PARSER_MAX_TOKEN_SIZE];
101 gint pos = 0;
102 gdouble val = 0.0;
103
104 c = _peek (parser);
105 if (c == '+' || c == '-')
106 token[pos++] = _next (parser);
107
108 while (isdigit (_peek (parser)))
109 token[pos++] = _next (parser);
110
111 c = _peek (parser);
112 if (c == '.')
113 token[pos++] = _next (parser);
114
115 while (isdigit (_peek (parser)))
116 token[pos++] = _next (parser);
117
118 c = _peek (parser);
119 if (c == 'e' || c == 'E') {
120 token[pos++] = _next (parser);
121
122 c = _peek (parser);
123 if (c == '+' || c == '-') {
124 token[pos++] = _next (parser);
125 }
126 }
127
128 while (isdigit (_peek (parser)))
129 token[pos++] = _next (parser);
130
131 token[pos] = '\0';
132
133 if (pos == 0 || sscanf (token, "%lf", &val) != 1)
134 _error (parser, "Failed to read real number");
135
136 return val;
137 }
138
139 static gdouble
_read_term(MathParser * parser)140 _read_term (MathParser * parser)
141 {
142 gdouble v0;
143 gchar c;
144
145 v0 = _read_power (parser);
146 c = _peek (parser);
147
148 while (c == '*' || c == '/') {
149 _next (parser);
150 if (c == '*') {
151 v0 *= _read_power (parser);
152 } else if (c == '/') {
153 v0 /= _read_power (parser);
154 }
155 c = _peek (parser);
156 }
157 return v0;
158 }
159
160 static gdouble
_read_expr(MathParser * parser)161 _read_expr (MathParser * parser)
162 {
163 gdouble v0 = 0.0;
164 gchar c;
165
166 c = _peek (parser);
167 if (c == '+' || c == '-') {
168 _next (parser);
169 if (c == '+')
170 v0 += _read_term (parser);
171 else if (c == '-')
172 v0 -= _read_term (parser);
173 } else {
174 v0 = _read_term (parser);
175 }
176
177 c = _peek (parser);
178 while (c == '+' || c == '-') {
179 _next (parser);
180 if (c == '+') {
181 v0 += _read_term (parser);
182 } else if (c == '-') {
183 v0 -= _read_term (parser);
184 }
185
186 c = _peek (parser);
187 }
188
189 return v0;
190 }
191
192 static gdouble
_read_boolean_comparison(MathParser * parser)193 _read_boolean_comparison (MathParser * parser)
194 {
195 gchar c, oper[] = { '\0', '\0', '\0' };
196 gdouble v0, v1;
197
198
199 v0 = _read_expr (parser);
200 c = _peek (parser);
201 if (c == '>' || c == '<') {
202 oper[0] = _next (parser);
203 c = _peek (parser);
204 if (c == '=')
205 oper[1] = _next (parser);
206
207
208 v1 = _read_expr (parser);
209
210 if (g_strcmp0 (oper, "<") == 0) {
211 v0 = (v0 < v1) ? 1.0 : 0.0;
212 } else if (g_strcmp0 (oper, ">") == 0) {
213 v0 = (v0 > v1) ? 1.0 : 0.0;
214 } else if (g_strcmp0 (oper, "<=") == 0) {
215 v0 = (v0 <= v1) ? 1.0 : 0.0;
216 } else if (g_strcmp0 (oper, ">=") == 0) {
217 v0 = (v0 >= v1) ? 1.0 : 0.0;
218 } else {
219 _error (parser, "Unknown operation!");
220 }
221 }
222 return v0;
223 }
224
225 static gdouble
_read_boolean_equality(MathParser * parser)226 _read_boolean_equality (MathParser * parser)
227 {
228 gchar c, oper[] = { '\0', '\0', '\0' };
229 gdouble v0, v1;
230
231 v0 = _read_boolean_comparison (parser);
232 c = _peek (parser);
233 if (c == '=' || c == '!') {
234 if (c == '!') {
235 if (_peek_n (parser, 1) == '=') {
236 oper[0] = _next (parser);
237 oper[1] = _next (parser);
238 } else {
239 return v0;
240 }
241 } else {
242 oper[0] = _next (parser);
243 c = _peek (parser);
244 if (c != '=')
245 _error (parser, "Expected a '=' for boolean '==' operator!");
246 oper[1] = _next (parser);
247 }
248 v1 = _read_boolean_comparison (parser);
249 if (g_strcmp0 (oper, "==") == 0) {
250 v0 = (fabs (v0 - v1) < PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0;
251 } else if (g_strcmp0 (oper, "!=") == 0) {
252 v0 = (fabs (v0 - v1) > PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0;
253 } else {
254 _error (parser, "Unknown operation!");
255 }
256 }
257 return v0;
258 }
259
260 static gdouble
_read_boolean_and(MathParser * parser)261 _read_boolean_and (MathParser * parser)
262 {
263 gchar c;
264 gdouble v0, v1;
265
266 v0 = _read_boolean_equality (parser);
267
268 c = _peek (parser);
269 while (c == '&') {
270 _next (parser);
271
272 c = _peek (parser);
273 if (c != '&')
274 _error (parser, "Expected '&' to follow '&' in logical and operation!");
275 _next (parser);
276
277 v1 = _read_boolean_equality (parser);
278 v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD
279 && fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0;
280
281 c = _peek (parser);
282 }
283
284 return v0;
285 }
286
287 static gdouble
_read_boolean_or(MathParser * parser)288 _read_boolean_or (MathParser * parser)
289 {
290 gchar c;
291 gdouble v0, v1;
292
293 v0 = _read_boolean_and (parser);
294
295 c = _peek (parser);
296 while (c == '|') {
297 _next (parser);
298 c = _peek (parser);
299 if (c != '|')
300 _error (parser, "Expected '|' to follow '|' in logical or operation!");
301 _next (parser);
302 v1 = _read_boolean_and (parser);
303 v0 = (fabs (v0) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD
304 || fabs (v1) >= PARSER_BOOLEAN_EQUALITY_THRESHOLD) ? 1.0 : 0.0;
305 c = _peek (parser);
306 }
307
308 return v0;
309 }
310
311 static gboolean
_init(MathParser * parser,const gchar * str,GstValidateParseVariableFunc variable_func,void * user_data)312 _init (MathParser * parser, const gchar * str,
313 GstValidateParseVariableFunc variable_func, void *user_data)
314 {
315 parser->str = str;
316 parser->len = strlen (str) + 1;
317 parser->pos = 0;
318 parser->error = NULL;
319 parser->user_data = user_data;
320 parser->variable_func = variable_func;
321
322 return TRUE;
323 }
324
325 static gdouble
_parse(MathParser * parser)326 _parse (MathParser * parser)
327 {
328 gdouble result = 0.0;
329
330 if (!setjmp (parser->err_jmp_buf)) {
331 result = _read_expr (parser);
332 if (parser->pos < parser->len - 1) {
333 _error (parser,
334 "Failed to reach end of input expression, likely malformed input");
335 } else
336 return result;
337 }
338
339 return -1.0;
340 }
341
342 static gdouble
_read_argument(MathParser * parser)343 _read_argument (MathParser * parser)
344 {
345 gchar c;
346 gdouble val;
347
348 val = _read_expr (parser);
349 c = _peek (parser);
350 if (c == ',')
351 _next (parser);
352
353 return val;
354 }
355
356 static gdouble
_read_builtin(MathParser * parser)357 _read_builtin (MathParser * parser)
358 {
359 gdouble v0 = 0.0, v1 = 0.0;
360 gchar c, token[PARSER_MAX_TOKEN_SIZE];
361 gint pos = 0;
362
363 c = _peek (parser);
364 if (isalpha (c) || c == '_' || c == '$') {
365 while (isalpha (c) || isdigit (c) || c == '_' || c == '$') {
366 token[pos++] = _next (parser);
367 c = _peek (parser);
368 }
369 token[pos] = '\0';
370
371 if (_peek (parser) == '(') {
372 _next (parser);
373 if (g_strcmp0 (token, "min") == 0) {
374 v0 = _read_argument (parser);
375 v1 = _read_argument (parser);
376 v0 = MIN (v0, v1);
377 } else if (g_strcmp0 (token, "max") == 0) {
378 v0 = _read_argument (parser);
379 v1 = _read_argument (parser);
380 v0 = MAX (v0, v1);
381 } else {
382 _error (parser, "Tried to call unknown built-in function!");
383 }
384
385 if (_next (parser) != ')')
386 _error (parser, "Expected ')' in built-in call!");
387 } else {
388 if (parser->variable_func != NULL
389 && parser->variable_func (token, &v1, parser->user_data)) {
390 v0 = v1;
391 } else {
392 gchar *err =
393 g_strdup_printf ("Could not look up value for variable %s!", token);
394 _error (parser, err);
395 g_free (err);
396 }
397 }
398 } else {
399 v0 = _read_double (parser);
400 }
401
402 return v0;
403 }
404
405 static gdouble
_read_parenthesis(MathParser * parser)406 _read_parenthesis (MathParser * parser)
407 {
408 gdouble val;
409
410 if (_peek (parser) == '(') {
411 _next (parser);
412 val = _read_boolean_or (parser);
413 if (_peek (parser) != ')')
414 _error (parser, "Expected ')'!");
415 _next (parser);
416 } else {
417 val = _read_builtin (parser);
418 }
419
420 return val;
421 }
422
423 static gdouble
_read_unary(MathParser * parser)424 _read_unary (MathParser * parser)
425 {
426 gchar c;
427 gdouble v0 = 0.0;
428
429 c = _peek (parser);
430 if (c == '!') {
431 _error (parser, "Expected '+' or '-' for unary expression, got '!'");
432 } else if (c == '-') {
433 _next (parser);
434 v0 = -_read_parenthesis (parser);
435 } else if (c == '+') {
436 _next (parser);
437 v0 = _read_parenthesis (parser);
438 } else {
439 v0 = _read_parenthesis (parser);
440 }
441 return v0;
442 }
443
444 static gdouble
_read_power(MathParser * parser)445 _read_power (MathParser * parser)
446 {
447 gdouble v0, v1 = 1.0, s = 1.0;
448
449 v0 = _read_unary (parser);
450
451 while (_peek (parser) == '^') {
452 _next (parser);
453 if (_peek (parser) == '-') {
454 _next (parser);
455 s = -1.0;
456 }
457 v1 = s * _read_power (parser);
458 v0 = pow (v0, v1);
459 }
460
461 return v0;
462 }
463
464 /**
465 * gst_validate_utils_parse_expression: (skip):
466 */
467 gdouble
gst_validate_utils_parse_expression(const gchar * expr,GstValidateParseVariableFunc variable_func,gpointer user_data,gchar ** error)468 gst_validate_utils_parse_expression (const gchar * expr,
469 GstValidateParseVariableFunc variable_func, gpointer user_data,
470 gchar ** error)
471 {
472 gdouble val;
473 MathParser parser;
474 gchar **spl = g_strsplit (expr, " ", -1);
475 gchar *expr_nospace = g_strjoinv ("", spl);
476
477 _init (&parser, expr_nospace, variable_func, user_data);
478 val = _parse (&parser);
479 g_strfreev (spl);
480 g_free (expr_nospace);
481
482 if (error) {
483 if (parser.error)
484 *error = g_strdup (parser.error);
485 else
486 *error = NULL;
487 }
488 return val;
489 }
490
491 /**
492 * gst_validate_utils_flags_from_str:
493 * @type: The #GType of the flags we are trying to retrieve the flags from
494 * @str_flags: The string representation of the value
495 *
496 * Returns: The flags set in @str_flags
497 */
498 guint
gst_validate_utils_flags_from_str(GType type,const gchar * str_flags)499 gst_validate_utils_flags_from_str (GType type, const gchar * str_flags)
500 {
501 guint flags;
502 GValue value = G_VALUE_INIT;
503 g_value_init (&value, type);
504
505 if (!gst_value_deserialize (&value, str_flags)) {
506 g_error ("Invalid flags: %s", str_flags);
507
508 return 0;
509 }
510
511 flags = g_value_get_flags (&value);
512 g_value_unset (&value);
513
514 return flags;
515 }
516
517 /**
518 * gst_validate_utils_enum_from_str:
519 * @type: The #GType of the enum we are trying to retrieve the enum value from
520 * @str_enum: The string representation of the value
521 * @enum_value: (out): The value of the enum
522 *
523 * Returns: %TRUE on success %FALSE otherwise
524 */
525 gboolean
gst_validate_utils_enum_from_str(GType type,const gchar * str_enum,guint * enum_value)526 gst_validate_utils_enum_from_str (GType type, const gchar * str_enum,
527 guint * enum_value)
528 {
529 GValue value = G_VALUE_INIT;
530 g_value_init (&value, type);
531
532 if (!gst_value_deserialize (&value, str_enum)) {
533 g_error ("Invalid enum: %s", str_enum);
534
535 return FALSE;
536 }
537
538 *enum_value = g_value_get_enum (&value);
539 g_value_unset (&value);
540
541 return TRUE;
542 }
543
544 /* Parse file that contains a list of GStructures */
545 static gchar **
_file_get_lines(GFile * file)546 _file_get_lines (GFile * file)
547 {
548 gsize size;
549
550 GError *err = NULL;
551 gchar *content = NULL, *escaped_content = NULL, **lines = NULL;
552
553 /* TODO Handle GCancellable */
554 if (!g_file_load_contents (file, NULL, &content, &size, NULL, &err)) {
555 GST_WARNING ("Failed to load contents: %d %s", err->code, err->message);
556 g_error_free (err);
557 return NULL;
558 }
559 if (g_strcmp0 (content, "") == 0) {
560 g_free (content);
561 return NULL;
562 }
563
564 if (_clean_structs_lines == NULL)
565 _clean_structs_lines =
566 g_regex_new ("\\\\\n|#.*\n", G_REGEX_CASELESS, 0, NULL);
567
568 escaped_content =
569 g_regex_replace (_clean_structs_lines, content, -1, 0, "", 0, NULL);
570 g_free (content);
571
572 lines = g_strsplit (escaped_content, "\n", 0);
573 g_free (escaped_content);
574
575 return lines;
576 }
577
578 static gchar **
_get_lines(const gchar * scenario_file)579 _get_lines (const gchar * scenario_file)
580 {
581 GFile *file = NULL;
582 gchar **lines = NULL;
583
584 GST_DEBUG ("Trying to load %s", scenario_file);
585 if ((file = g_file_new_for_path (scenario_file)) == NULL) {
586 GST_WARNING ("%s wrong uri", scenario_file);
587 return NULL;
588 }
589
590 lines = _file_get_lines (file);
591
592 g_object_unref (file);
593
594 return lines;
595 }
596
597 /* Returns: (transfer full): a #GList of #GstStructure */
598 static GList *
_lines_get_structures(gchar ** lines)599 _lines_get_structures (gchar ** lines)
600 {
601 gint i;
602 GList *structures = NULL;
603
604 if (lines == NULL)
605 return NULL;
606
607 for (i = 0; lines[i]; i++) {
608 GstStructure *structure;
609
610 if (g_strcmp0 (lines[i], "") == 0)
611 continue;
612
613 structure = gst_structure_from_string (lines[i], NULL);
614 if (structure == NULL) {
615 GST_ERROR ("Could not parse action %s", lines[i]);
616 goto failed;
617 }
618
619 structures = g_list_append (structures, structure);
620 }
621
622 done:
623 g_strfreev (lines);
624
625 return structures;
626
627 failed:
628 if (structures)
629 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
630 structures = NULL;
631
632 goto done;
633 }
634
635 /**
636 * gst_validate_utils_structs_parse_from_filename: (skip):
637 */
638 GList *
gst_validate_utils_structs_parse_from_filename(const gchar * scenario_file)639 gst_validate_utils_structs_parse_from_filename (const gchar * scenario_file)
640 {
641 gchar **lines;
642
643 lines = _get_lines (scenario_file);
644
645 if (lines == NULL) {
646 GST_DEBUG ("Got no line for file: %s", scenario_file);
647 return NULL;
648 }
649
650 return _lines_get_structures (lines);
651 }
652
653 /**
654 * gst_validate_structs_parse_from_gfile: (skip):
655 */
656 GList *
gst_validate_structs_parse_from_gfile(GFile * scenario_file)657 gst_validate_structs_parse_from_gfile (GFile * scenario_file)
658 {
659 gchar **lines;
660
661 lines = _file_get_lines (scenario_file);
662
663 if (lines == NULL)
664 return NULL;
665
666 return _lines_get_structures (lines);
667 }
668
669 static gboolean
strv_contains(GStrv strv,const gchar * str)670 strv_contains (GStrv strv, const gchar * str)
671 {
672 guint i;
673
674 for (i = 0; strv[i] != NULL; i++)
675 if (g_strcmp0 (strv[i], str) == 0)
676 return TRUE;
677
678 return FALSE;
679 }
680
681 gboolean
gst_validate_element_has_klass(GstElement * element,const gchar * klass)682 gst_validate_element_has_klass (GstElement * element, const gchar * klass)
683 {
684 const gchar *tmp;
685 gchar **a, **b;
686 gboolean result = FALSE;
687 guint i;
688
689 tmp = gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element),
690 GST_ELEMENT_METADATA_KLASS);
691
692 a = g_strsplit (klass, "/", -1);
693 b = g_strsplit (tmp, "/", -1);
694
695 /* All the elements in 'a' have to be in 'b' */
696 for (i = 0; a[i] != NULL; i++)
697 if (!strv_contains (b, a[i]))
698 goto done;
699 result = TRUE;
700
701 done:
702 g_strfreev (a);
703 g_strfreev (b);
704 return result;
705 }
706
707 /**
708 * gst_validate_utils_get_clocktime:
709 * @structure: A #GstStructure to retrieve @name as a GstClockTime.
710 * @name: The name of the field containing a #GstClockTime
711 * @retval: (out): The clocktime contained in @structure
712 *
713 * Get @name from @structure as a #GstClockTime, it handles various types
714 * for the value, if it is a double, it considers the value to be in second
715 * it can be a gint, gint64 a guint, a gint64.
716 *
717 * Return: %TRUE in case of success, %FALSE otherwise.
718 */
719 gboolean
gst_validate_utils_get_clocktime(GstStructure * structure,const gchar * name,GstClockTime * retval)720 gst_validate_utils_get_clocktime (GstStructure * structure, const gchar * name,
721 GstClockTime * retval)
722 {
723 gdouble val;
724 const GValue *gvalue = gst_structure_get_value (structure, name);
725
726 *retval = GST_CLOCK_TIME_NONE;
727 if (gvalue == NULL) {
728 return FALSE;
729 }
730
731 if (G_VALUE_TYPE (gvalue) == GST_TYPE_CLOCK_TIME) {
732 *retval = g_value_get_uint64 (gvalue);
733
734 return TRUE;
735 }
736
737 if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT64) {
738 *retval = g_value_get_uint64 (gvalue);
739
740 return TRUE;
741 }
742
743 if (G_VALUE_TYPE (gvalue) == G_TYPE_UINT) {
744 *retval = (GstClockTime) g_value_get_uint (gvalue);
745
746 return TRUE;
747 }
748
749 if (G_VALUE_TYPE (gvalue) == G_TYPE_INT) {
750 *retval = (GstClockTime) g_value_get_int (gvalue);
751
752 return TRUE;
753 }
754
755 if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) {
756 *retval = (GstClockTime) g_value_get_int64 (gvalue);
757
758 return TRUE;
759 }
760
761 if (!gst_structure_get_double (structure, name, &val)) {
762 return FALSE;
763 }
764
765 if (val == -1.0)
766 *retval = GST_CLOCK_TIME_NONE;
767 else {
768 *retval = val * GST_SECOND;
769 *retval = GST_ROUND_UP_4 (*retval);
770 }
771
772 return TRUE;
773 }
774
775 GstValidateActionReturn
gst_validate_object_set_property(GstValidateReporter * reporter,GObject * object,const gchar * property,const GValue * value,gboolean optional)776 gst_validate_object_set_property (GstValidateReporter * reporter,
777 GObject * object, const gchar * property,
778 const GValue * value, gboolean optional)
779 {
780 GParamSpec *paramspec;
781 GObjectClass *klass = G_OBJECT_GET_CLASS (object);
782 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
783 GValue cvalue = G_VALUE_INIT, nvalue = G_VALUE_INIT;
784
785 paramspec = g_object_class_find_property (klass, property);
786 if (paramspec == NULL) {
787 if (optional)
788 return TRUE;
789 GST_ERROR ("Target doesn't have property %s", property);
790 return FALSE;
791 }
792
793 g_value_init (&cvalue, paramspec->value_type);
794 if (paramspec->value_type != G_VALUE_TYPE (value) &&
795 (G_VALUE_TYPE (value) == G_TYPE_STRING)) {
796 if (!gst_value_deserialize (&cvalue, g_value_get_string (value))) {
797 GST_VALIDATE_REPORT (reporter, SCENARIO_ACTION_EXECUTION_ERROR,
798 "Could not set %" GST_PTR_FORMAT "::%s as value %s"
799 " could not be deserialize to %s", object, property,
800 g_value_get_string (value), G_PARAM_SPEC_TYPE_NAME (paramspec));
801
802 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
803 }
804 } else {
805 if (!g_value_transform (value, &cvalue)) {
806 GST_VALIDATE_REPORT (reporter, SCENARIO_ACTION_EXECUTION_ERROR,
807 "Could not set %" GST_PTR_FORMAT " property %s to type %s"
808 " (wanted type %s)", object, property, G_VALUE_TYPE_NAME (value),
809 G_PARAM_SPEC_TYPE_NAME (paramspec));
810
811 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
812 }
813
814 }
815
816 g_object_set_property (object, property, &cvalue);
817
818 g_value_init (&nvalue, paramspec->value_type);
819 g_object_get_property (object, property, &nvalue);
820
821 if (gst_value_compare (&cvalue, &nvalue) != GST_VALUE_EQUAL) {
822 gchar *nvalstr = gst_value_serialize (&nvalue);
823 gchar *cvalstr = gst_value_serialize (&cvalue);
824 GST_VALIDATE_REPORT (reporter, SCENARIO_ACTION_EXECUTION_ERROR,
825 "Setting value %" GST_PTR_FORMAT "::%s failed, expected value: %s"
826 " value after setting %s", object, property, cvalstr, nvalstr);
827
828 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
829 g_free (nvalstr);
830 g_free (cvalstr);
831 }
832
833 g_value_reset (&cvalue);
834 g_value_reset (&nvalue);
835 return res;
836 }
837
838 #ifdef G_OS_UNIX
839 static void
fault_restore(void)840 fault_restore (void)
841 {
842 struct sigaction action;
843
844 memset (&action, 0, sizeof (action));
845 action.sa_handler = SIG_DFL;
846
847 sigaction (SIGSEGV, &action, NULL);
848 sigaction (SIGQUIT, &action, NULL);
849 }
850
851 static void
fault_spin(void)852 fault_spin (void)
853 {
854 int spinning = TRUE;
855
856 g_on_error_stack_trace ("GstValidate");
857
858 wait (NULL);
859
860 g_printerr ("Please run 'gdb <process-name> %d' to "
861 "continue debugging, Ctrl-C to quit, or Ctrl-\\ to dump core.\n",
862 (gint) getpid ());
863
864 while (spinning)
865 g_usleep (1000000);
866 }
867
868 static void
fault_handler_sighandler(int signum)869 fault_handler_sighandler (int signum)
870 {
871 fault_restore ();
872
873 /* printf is used instead of g_print(), since it's less likely to
874 * deadlock */
875 switch (signum) {
876 case SIGSEGV:
877 g_printerr ("<Caught SIGNAL: SIGSEGV>\n");
878 break;
879 case SIGQUIT:
880 g_print ("<Caught SIGNAL: SIGQUIT>\n");
881 break;
882 default:
883 g_printerr ("<Caught SIGNAL: %d>\n", signum);
884 break;
885 }
886
887 fault_spin ();
888 }
889
890 static void
fault_setup(void)891 fault_setup (void)
892 {
893 struct sigaction action;
894
895 memset (&action, 0, sizeof (action));
896 action.sa_handler = fault_handler_sighandler;
897
898 sigaction (SIGSEGV, &action, NULL);
899 sigaction (SIGQUIT, &action, NULL);
900 }
901 #endif /* G_OS_UNIX */
902
903 void
gst_validate_spin_on_fault_signals(void)904 gst_validate_spin_on_fault_signals (void)
905 {
906 #ifdef G_OS_UNIX
907 fault_setup ();
908 #endif
909 }
910
911 /**
912 * gst_validate_element_matches_target:
913 * @element: a #GstElement to check
914 * @s: a #GstStructure to use for matching
915 *
916 * Check if @element matches one of the 'target-element-name',
917 * 'target-element-klass' or 'target-element-factory-name' defined in @s.
918 *
919 * Return: %TRUE if it matches, %FALSE otherwise or if @s doesn't contain any
920 * target-element field.
921 */
922 gboolean
gst_validate_element_matches_target(GstElement * element,GstStructure * s)923 gst_validate_element_matches_target (GstElement * element, GstStructure * s)
924 {
925 const gchar *tmp;
926
927 tmp = gst_structure_get_string (s, "target-element-name");
928 if (tmp != NULL && !g_strcmp0 (tmp, GST_ELEMENT_NAME (element)))
929 return TRUE;
930
931 tmp = gst_structure_get_string (s, "target-element-klass");
932 if (tmp != NULL && gst_validate_element_has_klass (element, tmp))
933 return TRUE;
934
935 tmp = gst_structure_get_string (s, "target-element-factory-name");
936 if (tmp != NULL && gst_element_get_factory (element)
937 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)), tmp))
938 return TRUE;
939
940 return FALSE;
941 }
942