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