validate: Add a mock decryptor element.
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-runner.c
1 /* GStreamer
2  *
3  * Copyright (C) 2013-2016 Collabora Ltd.
4  *  Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
5  *  Author: Thibault Saunier <thibault.saunier@collabora.com>
6  *
7  * gst-validate-runner.c - Validate Runner class
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <string.h>
26 #include <stdlib.h>
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include "validate.h"
33 #include "gst-validate-internal.h"
34 #include "gst-validate-report.h"
35 #include "gst-validate-monitor-factory.h"
36 #include "gst-validate-override-registry.h"
37 #include "gst-validate-runner.h"
38 #include "gst-validate-reporter.h"
39 #include "gst-validate-mockdecryptor.h"
40
41 GST_DEBUG_CATEGORY_STATIC (gst_validate_runner_debug);
42 #undef GST_CAT_DEFAULT
43 #define GST_CAT_DEFAULT gst_validate_runner_debug
44
45 static gboolean element_created = FALSE;
46
47 /* We create a GstValidateRunner on _init ()
48  * so that we keep backward compatibility when
49  * the user create a Runner after creating the pipeline
50  * but the runner was actually already ready to be used.
51  */
52 static GstValidateRunner *first_runner = NULL;
53
54 /**
55  * SECTION:gst-validate-runner
56  * @short_description: Class that runs Gst Validate tests for a pipeline
57  *
58  * Allows you to test a pipeline within GstValidate. It is the object where
59  * all issue reporting is done.
60  *
61  * In the tools using GstValidate the only minimal code to be able to monitor
62  * your pipelines is:
63  *
64  * |[
65  *  GstPipeline *pipeline = gst_pipeline_new ("monitored-pipeline");
66  *  GstValidateRunner *runner = gst_validate_runner_new ();
67  *  GstValidateMonitor *monitor = gst_validate_monitor_factory_create (
68  *          GST_OBJECT (pipeline), runner, NULL);
69  *
70  *  // Run the pipeline and do whatever you want with it
71  *
72  *  // In that same order
73  *  gst_object_unref (pipeline);
74  *  gst_object_unref (runner);
75  *  gst_object_unref (monitor);
76  * ]|
77  */
78
79 struct _GstValidateRunnerPrivate
80 {
81   GMutex mutex;
82   GList *reports;
83   GstValidateReportingDetails default_level;
84   GHashTable *reports_by_type;
85
86   /* A list of PatternLevel */
87   GList *report_pattern_levels;
88
89   /* Whether the runner was create with GST_TRACERS=validate or not) */
90   gboolean user_created;
91
92   gchar *pipeline_names;
93   gchar **pipeline_names_strv;
94 };
95
96 /* Describes the reporting level to apply to a name pattern */
97 typedef struct _PatternLevel
98 {
99   GPatternSpec *pattern;
100   GstValidateReportingDetails level;
101 } PatternLevel;
102
103 #define GST_VALIDATE_RUNNER_LOCK(r)                     \
104   G_STMT_START {                                        \
105   GST_LOG_OBJECT (r, "About to lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
106   (g_mutex_lock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));           \
107   GST_LOG_OBJECT (r, "Acquired lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
108   } G_STMT_END
109
110 #define GST_VALIDATE_RUNNER_UNLOCK(r)                   \
111   G_STMT_START {                                        \
112   GST_LOG_OBJECT (r, "About to unlock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
113   (g_mutex_unlock (&GST_VALIDATE_RUNNER_CAST(r)->priv->mutex));         \
114   GST_LOG_OBJECT (r, "Released lock %p", &GST_VALIDATE_RUNNER_CAST(r)->priv->mutex); \
115   } G_STMT_END
116
117 #define gst_validate_runner_parent_class parent_class
118 G_DEFINE_TYPE_WITH_PRIVATE (GstValidateRunner, gst_validate_runner,
119     GST_TYPE_TRACER);
120
121 /* signals */
122 enum
123 {
124   REPORT_ADDED_SIGNAL,
125   STOPPING_SIGNAL,
126   /* add more above */
127   LAST_SIGNAL
128 };
129
130 enum
131 {
132   PROP_0,
133   PROP_PARAMS,
134   PROP_LAST
135 };
136
137 static GParamSpec *properties[PROP_LAST];
138
139 static guint _signals[LAST_SIGNAL] = { 0, };
140
141 static gboolean
142 gst_validate_runner_should_monitor (GstValidateRunner * self,
143     GstElement * element)
144 {
145   gint i;
146   GstValidateMonitor *monitor;
147
148   if (!GST_IS_PIPELINE (element)) {
149     return FALSE;
150   }
151
152   if (self->priv->user_created)
153     return FALSE;
154
155   if (!self->priv->pipeline_names_strv)
156     return TRUE;
157
158   monitor = gst_validate_get_monitor (G_OBJECT (element));
159
160   if (monitor) {
161     GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
162         " monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
163         " NOT monitoring again.",
164         element, monitor,
165         gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
166   }
167
168   for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
169     if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
170             GST_OBJECT_NAME (element)))
171       return TRUE;
172   }
173
174   return FALSE;
175 }
176
177 static void
178 do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
179 {
180   element_created = TRUE;
181   if (gst_validate_runner_should_monitor (self, element)) {
182     /* the reference to the monitor is lost */
183     gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
184   }
185 }
186
187 static gboolean
188 _parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
189 {
190   if (!str)
191     return FALSE;
192
193   /* works in place */
194   g_strstrip (str);
195
196   if (g_ascii_isdigit (str[0])) {
197     unsigned long l;
198     char *endptr;
199     l = strtoul (str, &endptr, 10);
200     if (endptr > str && endptr[0] == 0) {
201       *level = (GstValidateReportingDetails) l;
202     } else {
203       return FALSE;
204     }
205   } else if (g_ascii_strcasecmp (str, "smart") == 0) {
206     *level = GST_VALIDATE_SHOW_SMART;
207   } else if (g_ascii_strcasecmp (str, "none") == 0) {
208     *level = GST_VALIDATE_SHOW_NONE;
209   } else if (g_ascii_strcasecmp (str, "synthetic") == 0) {
210     *level = GST_VALIDATE_SHOW_SYNTHETIC;
211   } else if (g_ascii_strcasecmp (str, "subchain") == 0) {
212     *level = GST_VALIDATE_SHOW_SUBCHAIN;
213   } else if (g_ascii_strcasecmp (str, "monitor") == 0) {
214     *level = GST_VALIDATE_SHOW_MONITOR;
215   } else if (g_ascii_strcasecmp (str, "all") == 0) {
216     *level = GST_VALIDATE_SHOW_ALL;
217   } else
218     return FALSE;
219
220   return TRUE;
221 }
222
223 static void
224 _free_report_pattern_level (PatternLevel * pattern_level)
225 {
226   g_pattern_spec_free (pattern_level->pattern);
227   g_free (pattern_level);
228 }
229
230 static void
231 _set_reporting_level_for_name (GstValidateRunner * runner,
232     const gchar * pattern, GstValidateReportingDetails level)
233 {
234   PatternLevel *pattern_level = g_malloc (sizeof (PatternLevel));
235   GPatternSpec *pattern_spec = g_pattern_spec_new (pattern);
236
237   pattern_level->pattern = pattern_spec;
238   pattern_level->level = level;
239
240   /* Allow the user to single out a pad with the "element-name__pad-name" syntax
241    */
242   if (g_strrstr (pattern, "__"))
243     runner->priv->report_pattern_levels =
244         g_list_prepend (runner->priv->report_pattern_levels, pattern_level);
245   else
246     runner->priv->report_pattern_levels =
247         g_list_append (runner->priv->report_pattern_levels, pattern_level);
248 }
249
250 static void
251 _replace_double_colons (gchar * word)
252 {
253   while (word) {
254     word = strstr (word, "::");
255     if (word) {
256       word[0] = '_';
257       word[1] = '_';
258     }
259   }
260 }
261
262 static void
263 _set_report_levels_from_string (GstValidateRunner * self, const gchar * list)
264 {
265   gchar **split;
266   gchar **walk;
267
268   g_assert (list);
269
270   GST_DEBUG_OBJECT (self, "setting report levels from string [%s]", list);
271
272   split = g_strsplit (list, ",", 0);
273
274   for (walk = split; *walk; walk++) {
275     _replace_double_colons (*walk);
276     if (strchr (*walk, ':')) {
277       gchar **values = g_strsplit (*walk, ":", 2);
278
279       if (values[0] && values[1]) {
280         GstValidateReportingDetails level;
281
282         if (_parse_reporting_level (values[1], &level))
283           _set_reporting_level_for_name (self, values[0], level);
284       }
285
286       g_strfreev (values);
287     } else {
288       GstValidateReportingDetails level;
289
290       if (_parse_reporting_level (*walk, &level))
291         self->priv->default_level = level;
292     }
293   }
294
295   g_strfreev (split);
296 }
297
298 static void
299 _init_report_levels (GstValidateRunner * self)
300 {
301   const gchar *env;
302
303   env = g_getenv ("GST_VALIDATE_REPORTING_DETAILS");
304   if (env)
305     _set_report_levels_from_string (self, env);
306 }
307
308 static void
309 _unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
310 {
311   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
312 }
313
314 static void
315 gst_validate_runner_finalize (GObject * object)
316 {
317   GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
318
319   if (!runner->priv->user_created)
320     gst_validate_runner_exit (runner, TRUE);
321
322   g_list_free_full (runner->priv->reports,
323       (GDestroyNotify) gst_validate_report_unref);
324
325   g_list_free_full (runner->priv->report_pattern_levels,
326       (GDestroyNotify) _free_report_pattern_level);
327
328   g_mutex_clear (&runner->priv->mutex);
329
330   g_free (runner->priv->pipeline_names);
331   g_strfreev (runner->priv->pipeline_names_strv);
332
333   g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
334       _unref_report_list, NULL);
335   g_hash_table_destroy (runner->priv->reports_by_type);
336
337   G_OBJECT_CLASS (parent_class)->finalize (object);
338
339   if (!runner->priv->user_created)
340     gst_validate_deinit ();
341 }
342
343 static GObject *
344 gst_validate_runner_constructor (GType type, guint n_construct_params,
345     GObjectConstructParam * construct_params)
346 {
347   GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
348       n_construct_params, construct_params);
349
350   if (!gst_validate_is_initialized ()) {
351     first_runner = GST_VALIDATE_RUNNER (runner);
352     gst_validate_init ();
353     first_runner = NULL;
354
355     return runner;
356   }
357
358   return runner;
359 }
360
361
362 static void
363 gst_validate_runner_get_property (GObject * object, guint prop_id,
364     GValue * value, GParamSpec * pspec)
365 {
366   GstValidateRunner *runner;
367
368   runner = GST_VALIDATE_RUNNER (object);
369   switch (prop_id) {
370     case PROP_PARAMS:
371     {
372       g_value_set_string (value, runner->priv->pipeline_names);
373       break;
374     }
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378   }
379 }
380
381 static void
382 gst_validate_runner_set_property (GObject * object, guint prop_id,
383     const GValue * value, GParamSpec * pspec)
384 {
385   GstValidateRunner *runner;
386
387   runner = GST_VALIDATE_RUNNER (object);
388   switch (prop_id) {
389     case PROP_PARAMS:
390     {
391       g_free (runner->priv->pipeline_names);
392       g_strfreev (runner->priv->pipeline_names_strv);
393
394       runner->priv->pipeline_names = g_value_dup_string (value);
395       if (runner->priv->pipeline_names)
396         runner->priv->pipeline_names_strv =
397             g_strsplit (runner->priv->pipeline_names, ",", -1);
398       break;
399     }
400     default:
401       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402       break;
403   }
404 }
405
406 static void
407 gst_validate_runner_class_init (GstValidateRunnerClass * klass)
408 {
409   GObjectClass *gobject_class;
410
411   gobject_class = G_OBJECT_CLASS (klass);
412
413   gobject_class->finalize = gst_validate_runner_finalize;
414
415   gobject_class->set_property = gst_validate_runner_set_property;
416   gobject_class->get_property = gst_validate_runner_get_property;
417   gobject_class->constructor = gst_validate_runner_constructor;
418
419   properties[PROP_PARAMS] =
420       g_param_spec_string ("params", "Params", "Extra configuration parameters",
421       NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
422
423   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
424
425   _signals[REPORT_ADDED_SIGNAL] =
426       g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
427       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
428       GST_TYPE_VALIDATE_REPORT);
429
430   _signals[STOPPING_SIGNAL] =
431       g_signal_new ("stopping", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0,
432       NULL, NULL, NULL, G_TYPE_NONE, 0);
433
434   GST_DEBUG_CATEGORY_INIT (gst_validate_runner_debug, "gstvalidaterunner",
435       GST_DEBUG_FG_YELLOW, "Gst validate runner");
436 }
437
438 static void
439 gst_validate_runner_init (GstValidateRunner * runner)
440 {
441   runner->priv = gst_validate_runner_get_instance_private (runner);
442
443   runner->priv->reports_by_type = g_hash_table_new (g_direct_hash,
444       g_direct_equal);
445
446   runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
447   _init_report_levels (runner);
448
449   gst_tracing_register_hook (GST_TRACER (runner), "element-new",
450       G_CALLBACK (do_element_new));
451
452   gst_element_register (NULL, GST_MOCKDECRYPTOR_NAME, GST_RANK_MARGINAL,
453       GST_TYPE_MOCKDECRYPTOR);
454 }
455
456 /**
457  * gst_validate_runner_new:
458  *
459  * Create a new #GstValidateRunner
460  *
461  * Returns: A newly created #GstValidateRunner
462  */
463 GstValidateRunner *
464 gst_validate_runner_new (void)
465 {
466   GstValidateRunner *runner;
467
468   if (first_runner) {
469     runner = first_runner;
470     first_runner = NULL;
471   } else if (element_created) {
472     g_error ("Should never create a GstValidateRunner after a GstElement "
473         "has been created in the same process.");
474
475     return NULL;
476   } else {
477     runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
478     runner->priv->user_created = TRUE;
479   }
480
481   {
482     GstValidateOverrideRegistry *registry =
483         gst_validate_override_registry_get ();
484     GList *all_overrides =
485         gst_validate_override_registry_get_override_list (registry);
486     GList *i;
487     for (i = all_overrides; i; i = i->next) {
488       GstValidateOverride *override = (GstValidateOverride *) i->data;
489       gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (override),
490           runner);
491     }
492     g_list_free (all_overrides);
493   }
494
495   return runner;
496 }
497
498 /*
499  * gst_validate_runner_get_default_reporting_level:
500  *
501  * Returns: the default #GstValidateReportingDetails used to output a report.
502  */
503 GstValidateReportingDetails
504 gst_validate_runner_get_default_reporting_level (GstValidateRunner * runner)
505 {
506   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
507       GST_VALIDATE_SHOW_UNKNOWN);
508
509   return runner->priv->default_level;
510 }
511
512 /*
513  * gst_validate_runner_get_reporting_level_for_name:
514  *
515  * Returns: the #GstValidateReportingDetails that will be applied for a given name.
516  * If no pattern was set for such a name, this function will return
517  * #GST_VALIDATE_SHOW_UNKNOWN, and reporting for that name will
518  * default to the global reporting level.
519  */
520 GstValidateReportingDetails
521 gst_validate_runner_get_reporting_level_for_name (GstValidateRunner * runner,
522     const gchar * name)
523 {
524   GList *tmp;
525   gchar *fixed_name;
526
527   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
528       GST_VALIDATE_SHOW_UNKNOWN);
529
530   fixed_name = g_strdup (name);
531   _replace_double_colons (fixed_name);
532   for (tmp = runner->priv->report_pattern_levels; tmp; tmp = tmp->next) {
533     PatternLevel *pattern_level = (PatternLevel *) tmp->data;
534     if (g_pattern_match_string (pattern_level->pattern, fixed_name)) {
535       g_free (fixed_name);
536
537       return pattern_level->level;
538     }
539   }
540
541   g_free (fixed_name);
542   return GST_VALIDATE_SHOW_UNKNOWN;
543 }
544
545 static void
546 synthesize_reports (GstValidateRunner * runner, GstValidateReport * report)
547 {
548   GstValidateIssueId issue_id;
549   GList *reports;
550
551   issue_id = report->issue->issue_id;
552
553   GST_VALIDATE_RUNNER_LOCK (runner);
554   reports =
555       g_hash_table_lookup (runner->priv->reports_by_type,
556       (gconstpointer) issue_id);
557   reports = g_list_append (reports, gst_validate_report_ref (report));
558   g_hash_table_insert (runner->priv->reports_by_type, (gpointer) issue_id,
559       reports);
560   GST_VALIDATE_RUNNER_UNLOCK (runner);
561 }
562
563 static void
564 _dot_pipeline (GstValidateReport * report, GstStructure * config)
565 {
566   GstPipeline *pipeline = gst_validate_reporter_get_pipeline (report->reporter);
567
568   if (pipeline) {
569     gint details = GST_DEBUG_GRAPH_SHOW_ALL;
570     gchar *reporter_basename =
571         g_path_get_basename (gst_validate_reporter_get_name (report->reporter));
572     report->dotfile_name =
573         g_strdup_printf ("%" GST_TIME_FORMAT "-validate-report-%s-on-%s-%s",
574         GST_TIME_ARGS (GST_CLOCK_DIFF (_priv_start_time,
575                 gst_util_get_timestamp ())),
576         gst_validate_report_level_get_name (report->level), reporter_basename,
577         g_quark_to_string (report->issue->issue_id));
578
579     g_free (reporter_basename);
580
581     if (config)
582       gst_structure_get_int (config, "details", &details);
583
584     GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pipeline),
585         GST_DEBUG_GRAPH_SHOW_ALL, report->dotfile_name);
586
587     gst_object_unref (pipeline);
588   }
589
590 }
591
592 static void
593 gst_validate_runner_maybe_dot_pipeline (GstValidateRunner * runner,
594     GstValidateReport * report)
595 {
596   GList *config;
597
598   if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL ||
599       gst_validate_report_check_abort (report)) {
600
601     _dot_pipeline (report, NULL);
602     return;
603   }
604
605   for (config = gst_validate_plugin_get_config (NULL);
606       config; config = config->next) {
607
608     if (gst_structure_has_name (config->data, "core")) {
609       GstValidateReportLevel level;
610       const gchar *level_str,
611           *action = gst_structure_get_string (config->data, "action");
612
613       if (g_strcmp0 (action, "dot-pipeline"))
614         continue;
615
616       level_str = gst_structure_get_string (config->data, "report-level");
617       level = level_str ? gst_validate_report_level_from_name (level_str) :
618           GST_VALIDATE_REPORT_LEVEL_CRITICAL;
619
620       if (level >= report->level) {
621         _dot_pipeline (report, config->data);
622
623         return;
624       }
625     }
626   }
627 }
628
629 void
630 gst_validate_runner_add_report (GstValidateRunner * runner,
631     GstValidateReport * report)
632 {
633   GstValidateReportingDetails details, reporter_details, issue_type_details;
634
635   g_return_if_fail (GST_IS_VALIDATE_RUNNER (runner));
636
637   gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE (report),
638           report));
639   gst_validate_runner_maybe_dot_pipeline (runner, report);
640
641   details = reporter_details =
642       gst_validate_reporter_get_reporting_level (report->reporter);
643   issue_type_details =
644       gst_validate_runner_get_reporting_level_for_name (runner,
645       g_quark_to_string (report->issue->issue_id));
646
647   if (reporter_details == GST_VALIDATE_SHOW_UNKNOWN)
648     details = issue_type_details;
649
650   /* Let's use our own reporting strategy */
651   if (details == GST_VALIDATE_SHOW_UNKNOWN) {
652     gst_validate_report_set_reporting_level (report,
653         runner->priv->default_level);
654     switch (runner->priv->default_level) {
655       case GST_VALIDATE_SHOW_NONE:
656         return;
657       case GST_VALIDATE_SHOW_SMART:
658         if (!gst_validate_report_check_abort (report) &&
659             report->level != GST_VALIDATE_REPORT_LEVEL_CRITICAL &&
660             !report->trace) {
661           synthesize_reports (runner, report);
662           return;
663         }
664         break;
665       case GST_VALIDATE_SHOW_SYNTHETIC:
666         if (!report->trace) {
667           synthesize_reports (runner, report);
668           return;
669         }
670       default:
671         break;
672     }
673   } else if (details == GST_VALIDATE_SHOW_NONE) {
674     GST_DEBUG ("Not reporting.");
675     return;
676   }
677
678   GST_VALIDATE_RUNNER_LOCK (runner);
679   runner->priv->reports =
680       g_list_append (runner->priv->reports, gst_validate_report_ref (report));
681   GST_VALIDATE_RUNNER_UNLOCK (runner);
682
683   g_signal_emit (runner, _signals[REPORT_ADDED_SIGNAL], 0, report);
684 }
685
686 /**
687  * gst_validate_runner_get_reports_count:
688  * @runner: The $GstValidateRunner to get the number of reports from
689  *
690  * Get the number of reports present in the runner:
691  *
692  * Returns: The number of reports present in the runner.
693  */
694 guint
695 gst_validate_runner_get_reports_count (GstValidateRunner * runner)
696 {
697   GList *tmp;
698   guint l;
699
700   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 0);
701
702   GST_VALIDATE_RUNNER_LOCK (runner);
703   l = g_list_length (runner->priv->reports);
704   for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
705     GstValidateReport *report = (GstValidateReport *) tmp->data;
706     l += g_list_length (report->repeated_reports);
707   }
708   l += g_hash_table_size (runner->priv->reports_by_type);
709   GST_VALIDATE_RUNNER_UNLOCK (runner);
710
711   return l;
712 }
713
714 /**
715  * gst_validate_runner_get_reports:
716  * @runner: The #GstValidateRunner
717  *
718  * Return: (element-type GstValidateReport)(transfer full): all the reports
719  */
720 GList *
721 gst_validate_runner_get_reports (GstValidateRunner * runner)
722 {
723   GList *ret;
724
725   GST_VALIDATE_RUNNER_LOCK (runner);
726   ret =
727       g_list_copy_deep (runner->priv->reports,
728       (GCopyFunc) gst_validate_report_ref, NULL);
729   GST_VALIDATE_RUNNER_UNLOCK (runner);
730
731   return ret;
732 }
733
734 static GList *
735 _do_report_synthesis (GstValidateRunner * runner)
736 {
737   GHashTableIter iter;
738   GList *reports, *tmp;
739   gpointer key, value;
740   GList *criticals = NULL;
741
742   /* Take the lock so the hash table won't be modified while we are iterating
743    * over it */
744   GST_VALIDATE_RUNNER_LOCK (runner);
745   g_hash_table_iter_init (&iter, runner->priv->reports_by_type);
746   while (g_hash_table_iter_next (&iter, &key, &value)) {
747     GstValidateReport *report;
748     reports = (GList *) value;
749
750     if (!reports)
751       continue;
752
753     report = (GstValidateReport *) (reports->data);
754
755     gst_validate_report_print_level (report);
756     gst_validate_report_print_detected_on (report);
757
758     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
759       criticals = g_list_append (criticals, report);
760       gst_validate_report_print_details (report);
761     }
762
763     for (tmp = g_list_next (reports); tmp; tmp = tmp->next) {
764       report = (GstValidateReport *) tmp->data;
765       gst_validate_report_print_detected_on (report);
766
767       if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
768         criticals = g_list_append (criticals, report);
769         gst_validate_report_print_details (report);
770       }
771     }
772     report = (GstValidateReport *) (reports->data);
773     gst_validate_report_print_description (report);
774     gst_validate_printf (NULL, "\n");
775   }
776   GST_VALIDATE_RUNNER_UNLOCK (runner);
777
778   return criticals;
779 }
780
781 /**
782  * gst_validate_runner_printf:
783  * @runner: The #GstValidateRunner to print all the reports for
784  *
785  * Prints all the reports on the terminal or on wherever is set
786  * in the #GST_VALIDATE_FILE env variable.
787  *
788  * Returns: 0 if no critical error has been found and 18 if a critical
789  * error has been detected. That return value is usually to be used as
790  * exit code of the application.
791  */
792 int
793 gst_validate_runner_printf (GstValidateRunner * runner)
794 {
795   GList *reports, *tmp;
796   int ret = 0;
797   GList *criticals = NULL;
798
799   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
800
801   criticals = _do_report_synthesis (runner);
802   reports = gst_validate_runner_get_reports (runner);
803   for (tmp = reports; tmp; tmp = tmp->next) {
804     GstValidateReport *report = (GstValidateReport *) tmp->data;
805
806     if (gst_validate_report_should_print (report))
807       gst_validate_report_printf (report);
808
809     if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL) {
810       criticals = g_list_append (criticals, report);
811     }
812   }
813
814   if (criticals) {
815     GList *iter;
816     g_printerr ("\n\n==== Got criticals. Return value set to 18 ====\n");
817     ret = 18;
818     for (iter = criticals; iter; iter = iter->next) {
819       g_printerr ("     Critical error %s\n",
820           ((GstValidateReport *) (iter->data))->message);
821     }
822     g_printerr ("\n");
823   }
824
825   g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
826   g_list_free (criticals);
827   gst_validate_printf (NULL, "Issues found: %u\n",
828       gst_validate_runner_get_reports_count (runner));
829   return ret;
830 }
831
832 int
833 gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
834 {
835   gint ret = 0;
836   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner), 1);
837   g_signal_emit (runner, _signals[STOPPING_SIGNAL], 0);
838   if (print_result) {
839     ret = gst_validate_runner_printf (runner);
840   } else {
841     GList *tmp;
842     for (tmp = runner->priv->reports; tmp; tmp = tmp->next) {
843       GstValidateReport *report = (GstValidateReport *) tmp->data;
844       if (report->level == GST_VALIDATE_REPORT_LEVEL_CRITICAL)
845         ret = 18;
846     }
847   }
848
849   return ret;
850 }
851
852 void
853 gst_validate_init_runner (void)
854 {
855   if (!first_runner) {
856     first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
857     first_runner->priv->user_created = TRUE;
858   }                             /* else the first runner has been created through the GST_TRACERS system */
859 }
860
861 void
862 gst_validate_deinit_runner (void)
863 {
864   g_clear_object (&first_runner);
865 }
866
867 GstValidateReportingDetails
868 gst_validate_runner_get_default_reporting_details (GstValidateRunner * runner)
869 {
870   g_return_val_if_fail (GST_IS_VALIDATE_RUNNER (runner),
871       GST_VALIDATE_SHOW_UNKNOWN);
872
873   return runner->priv->default_level;
874 }
875
876 #ifdef __GST_VALIDATE_PLUGIN
877 static gboolean
878 plugin_init (GstPlugin * plugin)
879 {
880   if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
881     return FALSE;
882
883   return TRUE;
884 }
885
886 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
887     "GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
888     GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
889 #endif /* __GST_VALIDATE_PLUGIN */