f6d9dee0d2b9131146df2f6ba22a7b3f1282b5f5
[platform/upstream/gstreamer.git] / validate / gst / validate / gst-validate-scenario.c
1 /* GStreamer
2  *
3  * Copyright (C) 2013 Collabora Ltd.
4  *  Author: Thibault Saunier <thibault.saunier@collabora.com>
5  * Copyright (C) 2018 Thibault Saunier <tsaunier@igalia.com>
6  *
7  * gst-validate-scenario.c - Validate Scenario 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  * SECTION:gst-validate-scenario
26  * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
27  *
28  * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
29  * It is basically an ordered list of #GstValidateAction that will be executed during the
30  * execution of the pipeline.
31  *
32  * Possible configurations (see #GST_VALIDATE_CONFIG):
33  *  * scenario-action-execution-interval: Sets the interval in
34  *    milliseconds (1/1000ths of a second), between which actions
35  *    will be executed, setting it to 0 means "execute in idle".
36  *    The default value is 10ms.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <gst/gst.h>
44 #include <gio/gio.h>
45 #include <string.h>
46 #include <errno.h>
47
48 #include "gst-validate-internal.h"
49 #include "gst-validate-scenario.h"
50 #include "gst-validate-reporter.h"
51 #include "gst-validate-report.h"
52 #include "gst-validate-utils.h"
53 #include "validate.h"
54 #include <gst/validate/gst-validate-override.h>
55 #include <gst/validate/gst-validate-override-registry.h>
56 #include <gst/validate/gst-validate-pipeline-monitor.h>
57
58 #define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
59 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
60
61 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND)        /* tolerance seek interval
62                                                            TODO make it overridable  */
63
64 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
65 #undef GST_CAT_DEFAULT
66 #define GST_CAT_DEFAULT gst_validate_scenario_debug
67
68 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
69   gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
70 } G_STMT_END
71
72 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
73
74 #define SCENARIO_LOCK(scenario) (g_mutex_lock(&scenario->priv->lock))
75 #define SCENARIO_UNLOCK(scenario) (g_mutex_unlock(&scenario->priv->lock))
76
77 #define DECLARE_AND_GET_PIPELINE(s,a) \
78   GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
79   if (pipeline == NULL) { \
80     GST_VALIDATE_REPORT (s, SCENARIO_ACTION_EXECUTION_ERROR, \
81             "Can't execute a '%s' action after the pipeline " \
82             "has been destroyed.", a->type); \
83     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
84   }
85
86 enum
87 {
88   PROP_0,
89   PROP_RUNNER,
90   PROP_HANDLES_STATE,
91   PROP_EXECUTE_ON_IDLE,
92   PROP_LAST
93 };
94
95 enum
96 {
97   DONE,
98   LAST_SIGNAL
99 };
100
101 static guint scenario_signals[LAST_SIGNAL] = { 0 };
102
103 static GList *action_types = NULL;
104 static void gst_validate_scenario_dispose (GObject * object);
105 static void gst_validate_scenario_finalize (GObject * object);
106 static GstValidateActionType *_find_action_type (const gchar * type_name);
107
108 /* GstValidateScenario is not really thread safe and
109  * everything should be done from the thread GstValidate
110  * was inited from, unless stated otherwise.
111  */
112 struct _GstValidateScenarioPrivate
113 {
114   GstBus *bus;
115   GstValidateRunner *runner;
116   gboolean execute_on_idle;
117
118   GMutex lock;
119
120   GList *actions;
121   GList *interlaced_actions;    /* MT safe. Protected with SCENARIO_LOCK */
122   GList *on_addition_actions;   /* MT safe. Protected with SCENARIO_LOCK */
123
124   /*  List of action that need parsing when reaching ASYNC_DONE
125    *  most probably to be able to query duration */
126   GList *needs_parsing;
127
128   GstEvent *last_seek;
129   GstSeekFlags seek_flags;
130   GstClockTime segment_start;
131   GstClockTime segment_stop;
132   GstClockTime seek_pos_tol;
133
134   /* If we seeked in paused the position should be exactly what
135    * the seek value was (if accurate) */
136   gboolean seeked_in_pause;
137
138   guint num_actions;
139
140   gboolean handles_state;
141
142   guint execute_actions_source_id;      /* MT safe. Protect with SCENARIO_LOCK */
143   guint wait_id;
144   guint signal_handler_id;
145   guint action_execution_interval;
146
147   /* Name of message the wait action is waiting for */
148   const gchar *message_type;
149
150   gboolean buffering;
151
152   gboolean got_eos;
153   gboolean changing_state;
154   gboolean needs_async_done;
155   GstState target_state;
156
157   GList *overrides;
158
159   gchar *pipeline_name;
160   GstClockTime max_latency;
161   gint dropped;
162   gint max_dropped;
163
164   /* 'switch-track action' currently waiting for
165    * GST_MESSAGE_STREAMS_SELECTED to be completed. */
166   GstValidateAction *pending_switch_track;
167
168   GstStructure *vars;
169
170   GWeakRef ref_pipeline;
171 };
172
173 typedef struct KeyFileGroupName
174 {
175   GKeyFile *kf;
176   gchar *group_name;
177 } KeyFileGroupName;
178
179 static GstValidateInterceptionReturn
180 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
181     GstValidateReport * report)
182 {
183   GList *tmp;
184
185   for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
186       tmp = tmp->next) {
187     GstValidateOverride *override = (GstValidateOverride *) tmp->data;
188     report->level =
189         gst_validate_override_get_severity (override,
190         gst_validate_issue_get_id (report->issue), report->level);
191   }
192
193   return GST_VALIDATE_REPORTER_REPORT;
194 }
195
196 /**
197  * gst_validate_scenario_get_pipeline:
198  * @scenario: The scenario to retrieve a pipeline from
199  *
200  * Returns: (transfer full): The #GstPipeline the scenario is running
201  * against
202  */
203 GstElement *
204 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
205 {
206   return g_weak_ref_get (&scenario->priv->ref_pipeline);
207 }
208
209 static GstPipeline *
210 _get_pipeline (GstValidateReporter * reporter)
211 {
212   return
213       GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
214       (GST_VALIDATE_SCENARIO (reporter)));
215 }
216
217 static void
218 _reporter_iface_init (GstValidateReporterInterface * iface)
219 {
220   iface->intercept_report = gst_validate_scenario_intercept_report;
221   iface->get_pipeline = _get_pipeline;
222 }
223
224 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
225     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
226     G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init));
227
228 /* GstValidateAction implementation */
229 static GType _gst_validate_action_type = 0;
230
231 struct _GstValidateActionPrivate
232 {
233   GstStructure *main_structure;
234   GstValidateExecuteActionReturn state; /* Actually ActionState */
235   gboolean printed;
236   gboolean executing_last_subaction;
237   gboolean optional;
238
239   GstClockTime execution_time;
240   GstClockTime timeout;
241
242   GWeakRef scenario;
243 };
244
245 static JsonNode *
246 gst_validate_action_serialize (GstValidateAction * action)
247 {
248   JsonNode *node = json_node_alloc ();
249   JsonObject *jreport = json_object_new ();
250   gchar *action_args = gst_structure_to_string (action->structure);
251
252   json_object_set_string_member (jreport, "type", "action");
253   json_object_set_string_member (jreport, "action-type", action->type);
254   json_object_set_int_member (jreport, "playback-time",
255       (gint64) action->playback_time);
256   json_object_set_string_member (jreport, "args", action_args);
257   g_free (action_args);
258
259   node = json_node_init_object (node, jreport);
260   json_object_unref (jreport);
261
262   return node;
263 }
264
265 GType
266 gst_validate_action_get_type (void)
267 {
268   if (_gst_validate_action_type == 0) {
269     _gst_validate_action_type =
270         g_boxed_type_register_static (g_intern_static_string
271         ("GstValidateAction"), (GBoxedCopyFunc) gst_mini_object_ref,
272         (GBoxedFreeFunc) gst_mini_object_unref);
273
274     json_boxed_register_serialize_func (_gst_validate_action_type,
275         JSON_NODE_OBJECT,
276         (JsonBoxedSerializeFunc) gst_validate_action_serialize);
277   }
278
279   return _gst_validate_action_type;
280 }
281
282 static GstValidateAction *gst_validate_action_new (GstValidateScenario *
283     scenario, GstValidateActionType * type);
284 static gboolean execute_next_action (GstValidateScenario * scenario);
285 static gboolean
286 gst_validate_scenario_load (GstValidateScenario * scenario,
287     const gchar * scenario_name, const gchar * relative_scenario);
288
289 static GstValidateAction *
290 _action_copy (GstValidateAction * act)
291 {
292   GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
293   GstValidateAction *copy = gst_validate_action_new (scenario,
294       _find_action_type (act->type));
295
296   gst_object_unref (scenario);
297
298   if (act->structure) {
299     copy->structure = gst_structure_copy (act->structure);
300     copy->type = gst_structure_get_name (copy->structure);
301     if (!(act->name = gst_structure_get_string (copy->structure, "name")))
302       act->name = "";
303   }
304
305   if (act->priv->main_structure)
306     copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
307
308   copy->action_number = act->action_number;
309   copy->playback_time = act->playback_time;
310   copy->priv->timeout = act->priv->timeout;
311
312   return copy;
313 }
314
315 static void
316 _action_free (GstValidateAction * action)
317 {
318   if (action->structure)
319     gst_structure_free (action->structure);
320
321   if (action->priv->main_structure)
322     gst_structure_free (action->priv->main_structure);
323
324   g_weak_ref_clear (&action->priv->scenario);
325
326   g_slice_free (GstValidateActionPrivate, action->priv);
327   g_slice_free (GstValidateAction, action);
328 }
329
330 static void
331 gst_validate_action_init (GstValidateAction * action)
332 {
333   gst_mini_object_init (((GstMiniObject *) action), 0,
334       _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
335       (GstMiniObjectFreeFunction) _action_free);
336
337   action->priv = g_slice_new0 (GstValidateActionPrivate);
338
339   g_weak_ref_init (&action->priv->scenario, NULL);
340 }
341
342 static void
343 gst_validate_action_unref (GstValidateAction * action)
344 {
345   gst_mini_object_unref (GST_MINI_OBJECT (action));
346 }
347
348 static GstValidateAction *
349 gst_validate_action_new (GstValidateScenario * scenario,
350     GstValidateActionType * action_type)
351 {
352   GstValidateAction *action = g_slice_new0 (GstValidateAction);
353
354   gst_validate_action_init (action);
355   action->playback_time = GST_CLOCK_TIME_NONE;
356   action->priv->timeout = GST_CLOCK_TIME_NONE;
357   action->type = action_type->name;
358   action->repeat = -1;
359
360   g_weak_ref_set (&action->priv->scenario, scenario);
361
362   return action;
363 }
364
365 gboolean
366 _action_check_and_set_printed (GstValidateAction * action)
367 {
368   if (action->priv->printed == FALSE) {
369     gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
370             (action), action));
371
372     action->priv->printed = TRUE;
373
374     return FALSE;
375   }
376
377   return TRUE;
378 }
379
380 gboolean
381 gst_validate_action_is_subaction (GstValidateAction * action)
382 {
383   return !gst_structure_is_equal (action->structure,
384       action->priv->main_structure);
385 }
386
387 /* GstValidateActionType implementation */
388 GType _gst_validate_action_type_type;
389 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
390 static GstValidateActionType *gst_validate_action_type_new (void);
391
392 static void
393 _action_type_free (GstValidateActionType * type)
394 {
395   g_free (type->parameters);
396   g_free (type->description);
397   g_free (type->name);
398   g_free (type->implementer_namespace);
399
400   if (type->overriden_type)
401     gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
402
403   g_slice_free (GstValidateActionType, type);
404 }
405
406 static void
407 gst_validate_action_type_init (GstValidateActionType * type)
408 {
409   gst_mini_object_init ((GstMiniObject *) type, 0,
410       _gst_validate_action_type_type, NULL, NULL,
411       (GstMiniObjectFreeFunction) _action_type_free);
412 }
413
414 GstValidateActionType *
415 gst_validate_action_type_new (void)
416 {
417   GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
418
419   gst_validate_action_type_init (type);
420
421   /* action types are never freed */
422   GST_MINI_OBJECT_FLAG_SET (type, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
423   return type;
424 }
425
426 static GstValidateActionType *
427 _find_action_type (const gchar * type_name)
428 {
429   GList *tmp;
430
431   for (tmp = action_types; tmp; tmp = tmp->next) {
432     GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
433     if (g_strcmp0 (atype->name, type_name) == 0)
434       return atype;
435   }
436
437   return NULL;
438 }
439
440 static gboolean
441 _set_variable_func (const gchar * name, double *value, gpointer user_data)
442 {
443   gboolean res;
444   GstValidateScenario *scenario = GST_VALIDATE_SCENARIO (user_data);
445   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
446
447   if (!pipeline) {
448     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
449
450     return FALSE;
451   }
452
453   if (!g_strcmp0 (name, "duration")) {
454     gint64 duration;
455
456     if (!(res =
457             gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration))
458         || !GST_CLOCK_TIME_IS_VALID (duration)) {
459       GstValidateMonitor *monitor =
460           (GstValidateMonitor *) (g_object_get_data ((GObject *)
461               pipeline, "validate-monitor"));
462       GST_WARNING_OBJECT (scenario,
463           "Could not query duration. Trying to get duration from media-info");
464       if (monitor && monitor->media_descriptor)
465         duration =
466             gst_validate_media_descriptor_get_duration
467             (monitor->media_descriptor);
468       else {
469         GST_ERROR_OBJECT (scenario, "Media-info not set");
470         if (!res)
471           goto fail;
472       }
473     }
474
475     if (!GST_CLOCK_TIME_IS_VALID (duration))
476       *value = G_MAXDOUBLE;
477     else
478       *value = ((double) duration / GST_SECOND);
479
480     goto done;
481   } else if (!g_strcmp0 (name, "position")) {
482     gint64 position;
483
484     if (!gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
485       GST_WARNING_OBJECT (scenario, "Could not query position");
486       goto fail;
487     }
488
489     if (!GST_CLOCK_TIME_IS_VALID (position))
490       *value = G_MAXDOUBLE;
491     else
492       *value = ((double) position / GST_SECOND);
493
494     goto done;
495   }
496
497 fail:
498   gst_object_unref (pipeline);
499   return FALSE;
500
501 done:
502   gst_object_unref (pipeline);
503   return TRUE;
504 }
505
506 /* Check that @list doesn't contain any non-optional actions */
507 static gboolean
508 actions_list_is_done (GList * list)
509 {
510   GList *l;
511
512   for (l = list; l != NULL; l = g_list_next (l)) {
513     GstValidateAction *action = l->data;
514
515     if (!action->priv->optional)
516       return FALSE;
517   }
518
519   return TRUE;
520 }
521
522 static void
523 _check_scenario_is_done (GstValidateScenario * scenario)
524 {
525   SCENARIO_LOCK (scenario);
526   if (actions_list_is_done (scenario->priv->actions) &&
527       actions_list_is_done (scenario->priv->interlaced_actions) &&
528       actions_list_is_done (scenario->priv->on_addition_actions)) {
529     SCENARIO_UNLOCK (scenario);
530
531     g_signal_emit (scenario, scenario_signals[DONE], 0);
532   } else {
533     SCENARIO_UNLOCK (scenario);
534   }
535 }
536
537 /**
538  * gst_validate_action_get_clocktime:
539  * @scenario: The #GstValidateScenario from which to get a time
540  *            for a parameter of an action
541  * @action: The action from which to retrieve the time for @name
542  *          parameter.
543  * @name: The name of the parameter for which to retrieve a time
544  * @retval: (out): The return value for the wanted time
545  *
546  *
547  * Get a time value for the @name parameter of an action. This
548  * method should be called to retrieve and compute a timed value of a given
549  * action. It will first try to retrieve the value as a double,
550  * then get it as a string and execute any formula taking into account
551  * the 'position' and 'duration' variables. And it will always convert that
552  * value to a GstClockTime.
553  *
554  * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
555  */
556 gboolean
557 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
558     GstValidateAction * action, const gchar * name, GstClockTime * retval)
559 {
560   if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
561     gdouble val;
562     gchar *error = NULL;
563     const gchar *strval;
564
565     if (!(strval = gst_structure_get_string (action->structure, name))) {
566       GST_INFO_OBJECT (scenario, "Could not find %s", name);
567       return -1;
568     }
569
570     val = gst_validate_utils_parse_expression (strval, _set_variable_func,
571         scenario, &error);
572
573     if (error) {
574       GST_WARNING ("Error while parsing %s: %s", strval, error);
575       g_free (error);
576
577       return FALSE;
578     } else if (val == -1.0) {
579       *retval = GST_CLOCK_TIME_NONE;
580     } else {
581       *retval = val * GST_SECOND;
582       *retval = GST_ROUND_UP_4 (*retval);
583     }
584
585     return TRUE;
586   }
587
588   return TRUE;
589 }
590
591 /**
592  * gst_validate_scenario_execute_seek:
593  * @scenario: The #GstValidateScenario for which to execute a seek action
594  * @action: The seek action to execute
595  * @rate: The playback rate of the seek
596  * @format: The #GstFormat of the seek
597  * @flags: The #GstSeekFlags of the seek
598  * @start_type: The #GstSeekType of the start value of the seek
599  * @start: The start time of the seek
600  * @stop_type: The #GstSeekType of the stop value of the seek
601  * @stop: The stop time of the seek
602  *
603  * Executes a seek event on the scenario's pipeline. You should always use
604  * this method when you want to execute a seek inside a new action type
605  * so that the scenario state is updated taking into account that seek.
606  *
607  * For more information you should have a look at #gst_event_new_seek
608  *
609  * Returns: %TRUE if the seek could be executed, %FALSE otherwise
610  */
611 GstValidateExecuteActionReturn
612 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
613     GstValidateAction * action, gdouble rate, GstFormat format,
614     GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
615     GstSeekType stop_type, GstClockTime stop)
616 {
617   GstEvent *seek;
618
619   GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
620   GstValidateScenarioPrivate *priv = scenario->priv;
621   DECLARE_AND_GET_PIPELINE (scenario, action);
622
623   seek = gst_event_new_seek (rate, format, flags, start_type, start,
624       stop_type, stop);
625
626   gst_event_ref (seek);
627   if (gst_element_send_event (pipeline, seek)) {
628     gst_event_replace (&priv->last_seek, seek);
629     priv->seek_flags = flags;
630   } else {
631     GST_VALIDATE_REPORT (scenario, EVENT_SEEK_NOT_HANDLED,
632         "Could not execute seek: '(position %" GST_TIME_FORMAT
633         "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
634         " stop: %" GST_TIME_FORMAT " Rate %lf'",
635         GST_TIME_ARGS (action->playback_time), action->name,
636         action->action_number, action->repeat, GST_TIME_ARGS (start),
637         GST_TIME_ARGS (stop), rate);
638     ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
639   }
640   gst_event_unref (seek);
641   gst_object_unref (pipeline);
642
643   return ret;
644 }
645
646 static gint
647 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
648 {
649   const char *str_format, *str_flags, *str_start_type, *str_stop_type;
650
651   gdouble rate = 1.0;
652   guint format = GST_FORMAT_TIME;
653   GstSeekFlags flags = 0;
654   guint start_type = GST_SEEK_TYPE_SET;
655   GstClockTime start;
656   guint stop_type = GST_SEEK_TYPE_SET;
657   GstClockTime stop = GST_CLOCK_TIME_NONE;
658
659   if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
660     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
661
662   gst_structure_get_double (action->structure, "rate", &rate);
663   if ((str_format = gst_structure_get_string (action->structure, "format")))
664     gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
665
666   if ((str_start_type =
667           gst_structure_get_string (action->structure, "start_type")))
668     gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
669         &start_type);
670
671   if ((str_stop_type =
672           gst_structure_get_string (action->structure, "stop_type")))
673     gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
674         &stop_type);
675
676   if ((str_flags = gst_structure_get_string (action->structure, "flags")))
677     flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
678
679   gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
680
681   return gst_validate_scenario_execute_seek (scenario, action, rate, format,
682       flags, start_type, start, stop_type, stop);
683 }
684
685 static gboolean
686 _pause_action_restore_playing (GstValidateScenario * scenario)
687 {
688   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
689
690   if (!pipeline) {
691     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
692
693     return FALSE;
694   }
695
696   gst_validate_printf (scenario, "Back to playing\n");
697
698   if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
699       GST_STATE_CHANGE_FAILURE) {
700     GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
701         "Failed to set state to playing");
702     scenario->priv->target_state = GST_STATE_PLAYING;
703   }
704
705   gst_object_unref (pipeline);
706
707   return FALSE;
708 }
709
710 static gboolean
711 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
712 {
713   gst_structure_id_set_value (consts, field_id, value);
714
715   return TRUE;
716 }
717
718 static GstValidateExecuteActionReturn
719 _execute_define_vars (GstValidateScenario * scenario,
720     GstValidateAction * action)
721 {
722   gst_structure_foreach (action->structure,
723       (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
724
725   return GST_VALIDATE_EXECUTE_ACTION_OK;
726 }
727
728 static GstValidateExecuteActionReturn
729 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
730 {
731   guint state;
732   const gchar *str_state;
733   GstStateChangeReturn ret;
734   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
735
736   DECLARE_AND_GET_PIPELINE (scenario, action);
737
738   g_return_val_if_fail ((str_state =
739           gst_structure_get_string (action->structure, "state")), FALSE);
740
741   g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
742           str_state, &state), FALSE);
743
744
745   scenario->priv->target_state = state;
746   scenario->priv->changing_state = TRUE;
747   scenario->priv->seeked_in_pause = FALSE;
748
749   ret = gst_element_set_state (pipeline, state);
750   if (ret == GST_STATE_CHANGE_FAILURE) {
751     scenario->priv->changing_state = FALSE;
752     GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
753         "Failed to set state to %s", str_state);
754
755     /* Nothing async on failure, action will be removed automatically */
756     res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
757     goto done;
758   } else if (ret == GST_STATE_CHANGE_ASYNC) {
759
760     scenario->priv->needs_async_done = TRUE;
761     res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
762
763     goto done;
764   }
765
766   scenario->priv->changing_state = FALSE;
767
768 done:
769   gst_object_unref (pipeline);
770
771   return res;
772 }
773
774 static GstValidateExecuteActionReturn
775 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
776 {
777   GstClockTime duration = 0;
778   GstValidateExecuteActionReturn ret;
779
780   gst_structure_get (action->structure, "duration", G_TYPE_UINT64, &duration,
781       NULL);
782   gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
783
784   GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
785       GST_TIME_ARGS (duration));
786
787   ret = _execute_set_state (scenario, action);
788
789   if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
790     g_timeout_add (GST_TIME_AS_MSECONDS (duration),
791         (GSourceFunc) _pause_action_restore_playing, scenario);
792
793   return ret;
794 }
795
796 static GstValidateExecuteActionReturn
797 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
798 {
799   GST_DEBUG ("Playing back");
800
801   gst_structure_set (action->structure, "state", G_TYPE_STRING,
802       "playing", NULL);
803
804
805   return _execute_set_state (scenario, action);
806 }
807
808 static gboolean
809 _action_sets_state (GstValidateAction * action)
810 {
811   if (action == NULL)
812     return FALSE;
813
814   if (g_strcmp0 (action->type, "set-state") == 0)
815     return TRUE;
816
817   if (g_strcmp0 (action->type, "play") == 0)
818     return TRUE;
819
820   if (g_strcmp0 (action->type, "pause") == 0)
821     return TRUE;
822
823   return FALSE;
824
825 }
826
827 static void
828 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
829 {
830   GstValidateScenarioPrivate *priv = scenario->priv;
831   guint dropped;
832
833   dropped = g_atomic_int_get (&priv->dropped);
834
835   if (priv->max_dropped == -1 || dropped != -1)
836     return;
837
838   GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
839       dropped, priv->max_dropped);
840
841   if (dropped > priv->max_dropped) {
842     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_TOO_MANY_BUFFERS_DROPPED,
843         "Too many buffers have been dropped: %d (max allowed: %d)",
844         dropped, priv->max_dropped);
845   }
846 }
847
848 static GstValidateExecuteActionReturn
849 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
850 {
851   GstBus *bus;
852   GstValidateScenarioPrivate *priv = scenario->priv;
853
854   DECLARE_AND_GET_PIPELINE (scenario, action);
855
856   bus = gst_element_get_bus (pipeline);
857   SCENARIO_LOCK (scenario);
858   if (priv->execute_actions_source_id) {
859     g_source_remove (priv->execute_actions_source_id);
860     priv->execute_actions_source_id = 0;
861   }
862   SCENARIO_UNLOCK (scenario);
863
864   gst_validate_scenario_check_dropped (scenario);
865
866   gst_bus_post (bus,
867       gst_message_new_request_state (GST_OBJECT_CAST (scenario),
868           GST_STATE_NULL));
869   gst_object_unref (bus);
870   gst_object_unref (pipeline);
871
872   return TRUE;
873 }
874
875 static GstValidateExecuteActionReturn
876 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
877 {
878   gboolean ret;
879
880   DECLARE_AND_GET_PIPELINE (scenario, action);
881
882   GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
883       GST_TIME_ARGS (action->playback_time));
884
885   ret = gst_element_send_event (pipeline, gst_event_new_eos ());
886   gst_object_unref (pipeline);
887
888   return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
889       GST_VALIDATE_EXECUTE_ACTION_ERROR;
890 }
891
892 static int
893 find_input_selector (GValue * velement, const gchar * type)
894 {
895   GstElement *element = g_value_get_object (velement);
896   int result = !0;
897
898   if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
899     GstPad *srcpad = gst_element_get_static_pad (element, "src");
900
901     if (srcpad) {
902       GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
903
904       if (caps) {
905         const char *mime =
906             gst_structure_get_name (gst_caps_get_structure (caps, 0));
907         gboolean found = FALSE;
908
909         if (g_strcmp0 (type, "audio") == 0)
910           found = g_str_has_prefix (mime, "audio/");
911         else if (g_strcmp0 (type, "video") == 0)
912           found = g_str_has_prefix (mime, "video/")
913               && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
914         else if (g_strcmp0 (type, "text") == 0)
915           found = g_str_has_prefix (mime, "text/")
916               || g_str_has_prefix (mime, "subtitle/")
917               || g_str_has_prefix (mime, "video/x-dvd-subpicture");
918
919         if (found)
920           result = 0;
921       }
922
923       gst_caps_unref (caps);
924       gst_object_unref (srcpad);
925     }
926   }
927   return result;
928 }
929
930 static GstElement *
931 find_input_selector_with_type (GstBin * bin, const gchar * type)
932 {
933   GValue result = { 0, };
934   GstElement *input_selector = NULL;
935   GstIterator *iterator = gst_bin_iterate_recurse (bin);
936
937   if (gst_iterator_find_custom (iterator,
938           (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
939     input_selector = g_value_get_object (&result);
940   }
941   gst_iterator_free (iterator);
942
943   return input_selector;
944 }
945
946 static GstPad *
947 find_nth_sink_pad (GstElement * element, int index)
948 {
949   GstIterator *iterator;
950   gboolean done = FALSE;
951   GstPad *pad = NULL;
952   int dec_index = index;
953   GValue data = { 0, };
954
955   iterator = gst_element_iterate_sink_pads (element);
956   while (!done) {
957     switch (gst_iterator_next (iterator, &data)) {
958       case GST_ITERATOR_OK:
959         if (!dec_index--) {
960           done = TRUE;
961           pad = g_value_get_object (&data);
962           break;
963         }
964         g_value_reset (&data);
965         break;
966       case GST_ITERATOR_RESYNC:
967         gst_iterator_resync (iterator);
968         dec_index = index;
969         break;
970       case GST_ITERATOR_ERROR:
971         done = TRUE;
972         break;
973       case GST_ITERATOR_DONE:
974         done = TRUE;
975         break;
976     }
977   }
978   gst_iterator_free (iterator);
979   return pad;
980 }
981
982 static int
983 find_sink_pad_index (GstElement * element, GstPad * pad)
984 {
985   GstIterator *iterator;
986   gboolean done = FALSE;
987   int index = 0;
988   GValue data = { 0, };
989
990   iterator = gst_element_iterate_sink_pads (element);
991   while (!done) {
992     switch (gst_iterator_next (iterator, &data)) {
993       case GST_ITERATOR_OK:
994         if (pad == g_value_get_object (&data)) {
995           done = TRUE;
996         } else {
997           index++;
998         }
999         g_value_reset (&data);
1000         break;
1001       case GST_ITERATOR_RESYNC:
1002         gst_iterator_resync (iterator);
1003         index = 0;
1004         break;
1005       case GST_ITERATOR_ERROR:
1006         done = TRUE;
1007         break;
1008       case GST_ITERATOR_DONE:
1009         done = TRUE;
1010         break;
1011     }
1012   }
1013   gst_iterator_free (iterator);
1014   return index;
1015 }
1016
1017 static GstPadProbeReturn
1018 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1019     GstValidateAction * action)
1020 {
1021   if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1022     gst_validate_action_set_done (action);
1023
1024     return GST_PAD_PROBE_REMOVE;
1025   }
1026
1027   return GST_PAD_PROBE_OK;
1028 }
1029
1030 static GstValidateExecuteActionReturn
1031 execute_switch_track_default (GstValidateScenario * scenario,
1032     GstValidateAction * action)
1033 {
1034   guint index;
1035   gboolean relative = FALSE;
1036   const gchar *type, *str_index;
1037   GstElement *input_selector;
1038   GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1039
1040   DECLARE_AND_GET_PIPELINE (scenario, action);
1041
1042   if (!(type = gst_structure_get_string (action->structure, "type")))
1043     type = "audio";
1044
1045   /* First find an input selector that has the right type */
1046   input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1047   if (input_selector) {
1048     GstState state, next;
1049     GstPad *pad, *cpad, *srcpad;
1050
1051     ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1052     str_index = gst_structure_get_string (action->structure, "index");
1053
1054     if (str_index == NULL) {
1055       if (!gst_structure_get_uint (action->structure, "index", &index)) {
1056         GST_WARNING ("No index given, defaulting to +1");
1057         index = 1;
1058         relative = TRUE;
1059       }
1060     } else {
1061       relative = strchr ("+-", str_index[0]) != NULL;
1062       index = g_ascii_strtoll (str_index, NULL, 10);
1063     }
1064
1065     if (relative) {             /* We are changing track relatively to current track */
1066       int npads;
1067
1068       g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1069       if (pad) {
1070         int current_index = find_sink_pad_index (input_selector, pad);
1071
1072         index = (current_index + index) % npads;
1073         gst_object_unref (pad);
1074       }
1075     }
1076
1077     pad = find_nth_sink_pad (input_selector, index);
1078     g_object_get (input_selector, "active-pad", &cpad, NULL);
1079     if (gst_element_get_state (pipeline, &state, &next, 0) &&
1080         state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1081       srcpad = gst_element_get_static_pad (input_selector, "src");
1082
1083       gst_pad_add_probe (srcpad,
1084           GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1085           (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1086       ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1087       gst_object_unref (srcpad);
1088     }
1089
1090     g_object_set (input_selector, "active-pad", pad, NULL);
1091     gst_object_unref (pad);
1092     gst_object_unref (cpad);
1093     gst_object_unref (input_selector);
1094
1095     goto done;
1096   }
1097
1098   /* No selector found -> Failed */
1099 done:
1100   gst_object_unref (pipeline);
1101
1102   return ret;
1103 }
1104
1105 static GstPadProbeReturn
1106 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1107     GstValidateAction * action)
1108 {
1109   if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1110     gst_validate_action_set_done (action);
1111     return GST_PAD_PROBE_REMOVE;
1112   }
1113   return GST_PAD_PROBE_OK;
1114 }
1115
1116 static GstValidateExecuteActionReturn
1117 execute_switch_track_pb (GstValidateScenario * scenario,
1118     GstValidateAction * action)
1119 {
1120   gint index, n;
1121   const gchar *type, *str_index;
1122
1123   gint flags, current, tflag;
1124   gchar *tmp, *current_txt;
1125
1126   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1127   gboolean relative = FALSE, disabling = FALSE;
1128
1129   DECLARE_AND_GET_PIPELINE (scenario, action);
1130
1131   if (!(type = gst_structure_get_string (action->structure, "type")))
1132     type = "audio";
1133
1134   tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1135       type);
1136   current_txt = g_strdup_printf ("current-%s", type);
1137
1138   tmp = g_strdup_printf ("n-%s", type);
1139   g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, &current,
1140       NULL);
1141
1142   /* Don't try to use -1 */
1143   if (current == -1)
1144     current = 0;
1145
1146   g_free (tmp);
1147
1148   if (gst_structure_has_field (action->structure, "disable")) {
1149     disabling = TRUE;
1150     flags &= ~tflag;
1151     index = -1;
1152   } else if (!(str_index =
1153           gst_structure_get_string (action->structure, "index"))) {
1154     if (!gst_structure_get_int (action->structure, "index", &index)) {
1155       GST_WARNING ("No index given, defaulting to +1");
1156       index = 1;
1157       relative = TRUE;
1158     }
1159   } else {
1160     relative = strchr ("+-", str_index[0]) != NULL;
1161     index = g_ascii_strtoll (str_index, NULL, 10);
1162   }
1163
1164   if (relative) {               /* We are changing track relatively to current track */
1165     if (n == 0) {
1166       GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1167           "Trying to execute a relative %s for %s track when there"
1168           " is no track of this type available on current stream.",
1169           action->type, type);
1170
1171       res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1172       goto done;
1173     }
1174
1175     index = (current + index) % n;
1176   }
1177
1178   if (!disabling) {
1179     GstState state, next;
1180     GstPad *oldpad, *newpad;
1181     tmp = g_strdup_printf ("get-%s-pad", type);
1182     g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1183     g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1184
1185     gst_validate_printf (action, "Switching to track number: %i,"
1186         " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1187         GST_DEBUG_PAD_NAME (newpad));
1188     flags |= tflag;
1189     g_free (tmp);
1190
1191     if (gst_element_get_state (pipeline, &state, &next, 0) &&
1192         state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1193       GstPad *srcpad = NULL;
1194       GstElement *combiner = NULL;
1195       if (newpad == oldpad) {
1196         srcpad = gst_pad_get_peer (oldpad);
1197       } else if (newpad) {
1198         combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1199         if (combiner) {
1200           srcpad = gst_element_get_static_pad (combiner, "src");
1201           gst_object_unref (combiner);
1202         }
1203       }
1204
1205       if (srcpad) {
1206         gst_pad_add_probe (srcpad,
1207             GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1208             (GstPadProbeCallback) _check_pad_event_selection_done, action,
1209             NULL);
1210         gst_object_unref (srcpad);
1211
1212         res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1213       } else
1214         res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1215     }
1216
1217     if (oldpad)
1218       gst_object_unref (oldpad);
1219     gst_object_unref (newpad);
1220   } else {
1221     gst_validate_printf (action, "Disabling track type %s", type);
1222   }
1223
1224   g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1225   g_free (current_txt);
1226
1227 done:
1228   gst_object_unref (pipeline);
1229   return res;
1230 }
1231
1232 static GstStreamType
1233 stream_type_from_string (const gchar * type)
1234 {
1235   if (!g_strcmp0 (type, "video"))
1236     return GST_STREAM_TYPE_VIDEO;
1237   else if (!g_strcmp0 (type, "text"))
1238     return GST_STREAM_TYPE_TEXT;
1239
1240   /* default */
1241   return GST_STREAM_TYPE_AUDIO;
1242 }
1243
1244 /* Return a list of stream ID all the currently selected streams but the ones
1245  * of type @type */
1246 static GList *
1247 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1248 {
1249   GList *streams = NULL, *l;
1250
1251   for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1252     GstStream *s = l->data;
1253
1254     if (gst_stream_get_stream_type (s) != type) {
1255       streams = g_list_append (streams, (gpointer) s->stream_id);
1256     }
1257   }
1258
1259   return streams;
1260 }
1261
1262 static GList *
1263 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1264     GstStreamType type, gint index, gboolean relative)
1265 {
1266   guint nb_streams;
1267   guint i, n = 0, current = 0;
1268   GList *result = NULL, *l;
1269   GstStream *streams[256], *s, *current_stream = NULL;
1270
1271   /* Keep all streams which are not @type */
1272   for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1273     s = l->data;
1274
1275     if (gst_stream_get_stream_type (s) != type) {
1276       result = g_list_append (result, (gpointer) s->stream_id);
1277     } else if (!current_stream) {
1278       /* Assume the stream we want to switch from is the first one */
1279       current_stream = s;
1280     }
1281   }
1282
1283   /* Calculate the number of @type streams */
1284   nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1285   for (i = 0; i < nb_streams; i++) {
1286     s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1287
1288     if (gst_stream_get_stream_type (s) == type) {
1289       streams[n] = s;
1290
1291       if (current_stream
1292           && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1293         current = n;
1294
1295       n++;
1296     }
1297   }
1298
1299   if (G_UNLIKELY (n == 0)) {
1300     GST_ERROR ("No streams available of the required type");
1301     return result;
1302   }
1303
1304   if (relative) {               /* We are changing track relatively to current track */
1305     index = (current + index) % n;
1306   } else
1307     index %= n;
1308
1309   /* Add the new stream we want to switch to */
1310   s = streams[index];
1311
1312   gst_validate_printf (action, "Switching from stream %s to %s",
1313       current_stream ? current_stream->stream_id : "", s->stream_id);
1314
1315   return g_list_append (result, (gpointer) s->stream_id);
1316 }
1317
1318 static GstValidateExecuteActionReturn
1319 execute_switch_track_pb3 (GstValidateScenario * scenario,
1320     GstValidateAction * action)
1321 {
1322   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1323   GstValidateScenarioPrivate *priv = scenario->priv;
1324   gint index;
1325   GstStreamType stype;
1326   const gchar *type, *str_index;
1327   GList *new_streams = NULL;
1328   GstValidatePipelineMonitor *monitor;
1329   DECLARE_AND_GET_PIPELINE (scenario, action);
1330
1331   monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1332           pipeline, "validate-monitor"));
1333
1334   if (!monitor->stream_collection) {
1335     GST_VALIDATE_REPORT (scenario,
1336         SCENARIO_ACTION_EXECUTION_ERROR,
1337         "No stream collection message received on the bus, "
1338         "can not switch track.");
1339     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1340     goto done;
1341   }
1342
1343   if (!monitor->streams_selected) {
1344     GST_VALIDATE_REPORT (scenario,
1345         SCENARIO_ACTION_EXECUTION_ERROR,
1346         "No streams selected message received on the bus");
1347     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1348     goto done;
1349   }
1350
1351   type = gst_structure_get_string (action->structure, "type");
1352   stype = stream_type_from_string (type);
1353
1354   if (gst_structure_has_field (action->structure, "disable")) {
1355     gst_validate_printf (action, "Disabling track type %s", type);
1356     new_streams = disable_stream (monitor, stype);
1357   } else {
1358     gboolean relative = FALSE;
1359
1360     if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
1361       if (!gst_structure_get_int (action->structure, "index", &index)) {
1362         GST_WARNING ("No index given, defaulting to +1");
1363         index = 1;
1364         relative = TRUE;
1365       }
1366     } else {
1367       relative = strchr ("+-", str_index[0]) != NULL;
1368       index = g_ascii_strtoll (str_index, NULL, 10);
1369     }
1370
1371     new_streams = switch_stream (monitor, action, stype, index, relative);
1372   }
1373
1374   gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
1375       ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
1376       (GDestroyNotify) g_list_free);
1377
1378   if (!gst_element_send_event (pipeline,
1379           gst_event_new_select_streams (new_streams))) {
1380     GST_VALIDATE_REPORT (scenario,
1381         SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
1382     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1383     goto done;
1384   }
1385
1386   priv->pending_switch_track = action;
1387   if (scenario->priv->target_state > GST_STATE_PAUSED) {
1388     res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1389   } else {
1390     gst_mini_object_ref ((GstMiniObject *) action);
1391     res = GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
1392   }
1393
1394 done:
1395   gst_object_unref (pipeline);
1396
1397   return res;
1398 }
1399
1400 static GstValidateExecuteActionReturn
1401 _execute_switch_track (GstValidateScenario * scenario,
1402     GstValidateAction * action)
1403 {
1404   GstValidatePipelineMonitor *monitor;
1405
1406   DECLARE_AND_GET_PIPELINE (scenario, action);
1407
1408   monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1409           pipeline, "validate-monitor"));
1410   gst_object_unref (pipeline);
1411
1412   if (monitor->is_playbin)
1413     return execute_switch_track_pb (scenario, action);
1414   else if (monitor->is_playbin3)
1415     return execute_switch_track_pb3 (scenario, action);
1416
1417   return execute_switch_track_default (scenario, action);
1418 }
1419
1420 static GstValidateExecuteActionReturn
1421 _execute_set_rank (GstValidateScenario * scenario, GstValidateAction * action)
1422 {
1423   guint rank;
1424   GList *features, *origlist;
1425   GstPlugin *plugin;
1426   GstPluginFeature *feature;
1427   const gchar *name;
1428
1429   if (!(name = gst_structure_get_string (action->structure, "feature-name")) &&
1430       !(name = gst_structure_get_string (action->structure, "name"))) {
1431     GST_ERROR ("Could not find the name of the plugin feature(s) to tweak");
1432
1433     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1434   }
1435
1436   if (!(gst_structure_get_uint (action->structure, "rank", &rank) ||
1437           gst_structure_get_int (action->structure, "rank", (gint *) & rank))) {
1438     GST_ERROR ("Could not get rank to set on %s", name);
1439
1440     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1441   }
1442
1443   feature = gst_registry_lookup_feature (gst_registry_get (), name);
1444   if (feature) {
1445     gst_plugin_feature_set_rank (feature, rank);
1446     gst_object_unref (feature);
1447
1448     return GST_VALIDATE_EXECUTE_ACTION_OK;
1449   }
1450
1451   plugin = gst_registry_find_plugin (gst_registry_get (), name);
1452   if (!plugin) {
1453     GST_ERROR ("Could not find %s", name);
1454
1455     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1456   }
1457
1458   origlist = features =
1459       gst_registry_get_feature_list_by_plugin (gst_registry_get (),
1460       gst_plugin_get_name (plugin));
1461   for (; features; features = features->next)
1462     gst_plugin_feature_set_rank (features->data, rank);
1463   gst_plugin_feature_list_free (origlist);
1464
1465   return GST_VALIDATE_EXECUTE_ACTION_OK;
1466 }
1467
1468 static inline gboolean
1469 _add_execute_actions_gsource (GstValidateScenario * scenario)
1470 {
1471   GstValidateScenarioPrivate *priv = scenario->priv;
1472
1473   SCENARIO_LOCK (scenario);
1474   if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
1475       && priv->signal_handler_id == 0 && priv->message_type == NULL) {
1476     if (!scenario->priv->action_execution_interval)
1477       priv->execute_actions_source_id =
1478           g_idle_add ((GSourceFunc) execute_next_action, scenario);
1479     else
1480       priv->execute_actions_source_id =
1481           g_timeout_add (scenario->priv->action_execution_interval,
1482           (GSourceFunc) execute_next_action, scenario);
1483     SCENARIO_UNLOCK (scenario);
1484
1485     GST_DEBUG_OBJECT (scenario, "Start checking position again");
1486     return TRUE;
1487   }
1488   SCENARIO_UNLOCK (scenario);
1489
1490   GST_LOG_OBJECT (scenario, "No need to start a new gsource");
1491   return FALSE;
1492 }
1493
1494 static gboolean
1495 _get_position (GstValidateScenario * scenario,
1496     GstValidateAction * act, GstClockTime * position)
1497 {
1498   gboolean has_pos = FALSE, has_dur = FALSE;
1499   GstClockTime duration = -1;
1500
1501   GstValidateScenarioPrivate *priv = scenario->priv;
1502   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1503
1504   if (!pipeline) {
1505     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1506
1507     return FALSE;
1508   }
1509
1510   has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
1511       (gint64 *) position)
1512       && GST_CLOCK_TIME_IS_VALID (*position);
1513   has_dur =
1514       gst_element_query_duration (pipeline, GST_FORMAT_TIME,
1515       (gint64 *) & duration)
1516       && GST_CLOCK_TIME_IS_VALID (duration);
1517
1518   if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
1519       act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1520     GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
1521         GST_TIME_ARGS (*position));
1522
1523     goto fail;
1524   }
1525
1526   if (has_pos && has_dur && !priv->got_eos) {
1527     if (*position > duration) {
1528       _add_execute_actions_gsource (scenario);
1529
1530       GST_VALIDATE_REPORT (scenario,
1531           QUERY_POSITION_SUPERIOR_DURATION,
1532           "Reported position %" GST_TIME_FORMAT " > reported duration %"
1533           GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
1534
1535       goto done;
1536     }
1537   }
1538
1539 done:
1540   gst_object_unref (pipeline);
1541   return TRUE;
1542
1543 fail:
1544   gst_object_unref (pipeline);
1545   return FALSE;
1546 }
1547
1548 static gboolean
1549 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
1550     GstClockTime * position, gdouble * rate)
1551 {
1552   GstQuery *query;
1553
1554   GstClockTime start_with_tolerance, stop_with_tolerance;
1555   GstValidateScenarioPrivate *priv = scenario->priv;
1556   GstElement *pipeline;
1557
1558   if (!_get_position (scenario, act, position))
1559     return FALSE;
1560
1561   GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
1562       GST_TIME_ARGS (*position));
1563
1564   /* Check if playback is within seek segment */
1565   start_with_tolerance = (priv->segment_start <
1566       priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
1567   stop_with_tolerance =
1568       GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
1569       priv->seek_pos_tol : -1;
1570
1571   if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
1572           && *position > stop_with_tolerance)
1573       || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
1574           && *position < start_with_tolerance)) {
1575
1576     GST_VALIDATE_REPORT (scenario, QUERY_POSITION_OUT_OF_SEGMENT,
1577         "Current position %" GST_TIME_FORMAT " not in the expected range [%"
1578         GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
1579         GST_TIME_ARGS (start_with_tolerance),
1580         GST_TIME_ARGS (stop_with_tolerance));
1581   }
1582
1583   pipeline = gst_validate_scenario_get_pipeline (scenario);
1584   if (pipeline == NULL) {
1585     GST_INFO_OBJECT (scenario, "No pipeline set anymore");
1586
1587     return TRUE;
1588   }
1589
1590   query = gst_query_new_segment (GST_FORMAT_DEFAULT);
1591   if (gst_element_query (GST_ELEMENT (pipeline), query))
1592     gst_query_parse_segment (query, rate, NULL, NULL, NULL);
1593   gst_query_unref (query);
1594   gst_object_unref (pipeline);
1595
1596   if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE) {
1597     if ((*rate > 0 && (*position >= priv->segment_start + priv->seek_pos_tol ||
1598                 *position < ((priv->segment_start <
1599                         priv->seek_pos_tol) ? 0 : priv->segment_start -
1600                     priv->seek_pos_tol)))
1601         || (*rate < 0 && (*position > priv->segment_start + priv->seek_pos_tol
1602                 || *position < ((priv->segment_start <
1603                         priv->seek_pos_tol) ? 0 : priv->segment_start -
1604                     priv->seek_pos_tol)))) {
1605       priv->seeked_in_pause = FALSE;
1606       GST_VALIDATE_REPORT (scenario, EVENT_SEEK_RESULT_POSITION_WRONG,
1607           "Reported position after accurate seek in PAUSED state should be exactly"
1608           " what the user asked for. Position %" GST_TIME_FORMAT
1609           " is not not the expected one:  %" GST_TIME_FORMAT,
1610           GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
1611     }
1612   }
1613
1614   return TRUE;
1615 }
1616
1617 static gboolean
1618 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
1619     GstClockTime position, gdouble rate)
1620 {
1621   GstElement *pipeline;
1622
1623   if (!act) {
1624     GST_DEBUG_OBJECT (scenario, "No action to execute");
1625
1626     return FALSE;
1627   }
1628
1629   pipeline = gst_validate_scenario_get_pipeline (scenario);
1630   if (pipeline == NULL) {
1631
1632     if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
1633             GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
1634       GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1635           "Trying to execute an %s action after the pipeline has been destroyed"
1636           " but the type has not been marked as "
1637           "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
1638
1639       return FALSE;
1640     } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1641       GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1642           "Trying to execute action %s with playback time %" GST_TIME_FORMAT
1643           " after the pipeline has been destroyed. It is impossible"
1644           " to execute an action with a playback time specified"
1645           " after the pipeline has been destroyed",
1646           act->type, GST_TIME_ARGS (act->playback_time));
1647
1648       goto no;
1649     }
1650
1651     GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
1652
1653     goto yes;
1654   } else if (scenario->priv->got_eos) {
1655     GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
1656     scenario->priv->got_eos = FALSE;
1657   } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
1658     GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
1659         "just executing actions");
1660
1661     goto yes;
1662   } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
1663     GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
1664
1665     goto yes;
1666   } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
1667     GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
1668         " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1669         GST_TIME_ARGS (act->playback_time));
1670
1671     goto no;
1672   } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
1673     GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
1674         " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1675         GST_TIME_ARGS (act->playback_time));
1676
1677     goto no;
1678   }
1679
1680 yes:
1681   gst_object_unref (pipeline);
1682   return TRUE;
1683
1684 no:
1685   gst_object_unref (pipeline);
1686   return FALSE;
1687 }
1688
1689 GstValidateExecuteActionReturn
1690 gst_validate_execute_action (GstValidateActionType * action_type,
1691     GstValidateAction * action)
1692 {
1693   GstValidateExecuteActionReturn res;
1694   GstValidateScenario *scenario;
1695
1696   g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
1697       GST_VALIDATE_EXECUTE_ACTION_ERROR);
1698
1699   scenario = gst_validate_action_get_scenario (action);
1700
1701   if (action_type->prepare) {
1702     if (action_type->prepare (action) == FALSE) {
1703       GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
1704           " could not be prepared", action->structure);
1705
1706       gst_object_unref (scenario);
1707       return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1708     }
1709   }
1710
1711   gst_validate_print_action (action, NULL);
1712
1713   action->priv->execution_time = gst_util_get_timestamp ();
1714   action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
1715   res = action_type->execute (scenario, action);
1716   gst_object_unref (scenario);
1717
1718   if (!gst_structure_has_field (action->structure, "sub-action")) {
1719     gst_structure_free (action->structure);
1720
1721     action->priv->printed = FALSE;
1722     action->structure = gst_structure_copy (action->priv->main_structure);
1723
1724     if (!(action->name = gst_structure_get_string (action->structure, "name")))
1725       action->name = "";
1726
1727     if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC)
1728       action->priv->executing_last_subaction = TRUE;
1729   }
1730
1731   return res;
1732 }
1733
1734 static gboolean
1735 _set_action_playback_time (GstValidateScenario * scenario,
1736     GstValidateAction * action)
1737 {
1738   if (!gst_validate_action_get_clocktime (scenario, action,
1739           "playback-time", &action->playback_time)) {
1740     gchar *str = gst_structure_to_string (action->structure);
1741
1742     g_error ("Could not parse playback-time on structure: %s", str);
1743     g_free (str);
1744
1745     return FALSE;
1746   }
1747
1748   gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
1749       action->playback_time, NULL);
1750
1751   return TRUE;
1752 }
1753
1754 /* scenario can be NULL **only** if the action is a CONFIG action and
1755  * add_to_lists is FALSE */
1756 static GstValidateExecuteActionReturn
1757 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
1758     GstStructure * structure, gboolean add_to_lists)
1759 {
1760   gdouble playback_time;
1761   gboolean is_config = FALSE;
1762   GstValidateActionType *action_type;
1763   const gchar *str_playback_time = NULL;
1764   GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
1765   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1766   gboolean optional;
1767
1768   action->type = gst_structure_get_name (structure);
1769   action_type = _find_action_type (action->type);
1770
1771   if (!action_type) {
1772     GST_ERROR_OBJECT (scenario, "Action type %s no found",
1773         gst_structure_get_name (structure));
1774
1775     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1776   }
1777
1778   if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
1779       gst_structure_get_double (structure, "playback_time", &playback_time)) {
1780     action->playback_time = playback_time * GST_SECOND;
1781   } else if ((str_playback_time =
1782           gst_structure_get_string (structure, "playback-time")) ||
1783       (str_playback_time =
1784           gst_structure_get_string (structure, "playback_time"))) {
1785
1786     if (add_to_lists && priv)
1787       priv->needs_parsing = g_list_append (priv->needs_parsing, action);
1788     else if (!_set_action_playback_time (scenario, action))
1789       return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1790
1791   } else
1792     GST_INFO_OBJECT (scenario,
1793         "No playback time for action %" GST_PTR_FORMAT, structure);
1794
1795   if (!gst_validate_utils_get_clocktime (structure,
1796           "timeout", &action->priv->timeout)) {
1797     GST_INFO_OBJECT (scenario,
1798         "No timeout time for action %" GST_PTR_FORMAT, structure);
1799   }
1800
1801   action->structure = gst_structure_copy (structure);
1802
1803   if (!(action->name = gst_structure_get_string (action->structure, "name")))
1804     action->name = "";
1805
1806   if (!action->priv->main_structure)
1807     action->priv->main_structure = gst_structure_copy (structure);
1808
1809   if (gst_structure_get_boolean (structure, "optional", &optional)) {
1810     if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
1811       GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
1812           gst_structure_get_name (structure));
1813       return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1814     }
1815     action->priv->optional = optional;
1816   }
1817
1818   if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
1819       (gst_structure_get_boolean (action->structure, "as-config",
1820               &is_config) && is_config == TRUE)) {
1821
1822     res = action_type->execute (scenario, action);
1823     gst_validate_print_action (action, NULL);
1824
1825     return res;
1826   }
1827
1828   if (!add_to_lists)
1829     return res;
1830
1831   if (str_playback_time == NULL && priv != NULL) {
1832     GstValidateActionType *type = _find_action_type (action->type);
1833     gboolean can_execute_on_addition =
1834         type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
1835         && !GST_CLOCK_TIME_IS_VALID (action->playback_time);
1836
1837     if (priv->needs_parsing)
1838       can_execute_on_addition = FALSE;
1839
1840     if (can_execute_on_addition) {
1841       GList *tmp;
1842
1843       for (tmp = priv->actions; tmp; tmp = tmp->next) {
1844         GstValidateAction *act = (GstValidateAction *) tmp->data;
1845         if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1846           can_execute_on_addition = FALSE;
1847           break;
1848         }
1849       }
1850
1851     }
1852
1853     if (can_execute_on_addition) {
1854       SCENARIO_LOCK (scenario);
1855       priv->on_addition_actions = g_list_append (priv->on_addition_actions,
1856           action);
1857       SCENARIO_UNLOCK (scenario);
1858
1859     } else {
1860       priv->actions = g_list_append (priv->actions, action);
1861     }
1862   }
1863
1864   return res;
1865 }
1866
1867 static GstValidateExecuteActionReturn
1868 _execute_sub_action_action (GstValidateAction * action)
1869 {
1870   const gchar *subaction_str;
1871   GstStructure *subaction_struct = NULL;
1872   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1873   GstValidateScenario *scenario = NULL;
1874
1875   if (action->priv->executing_last_subaction) {
1876     action->priv->executing_last_subaction = FALSE;
1877
1878     goto done;
1879   }
1880
1881   scenario = gst_validate_action_get_scenario (action);
1882   g_assert (scenario);
1883   subaction_str = gst_structure_get_string (action->structure, "sub-action");
1884   if (subaction_str) {
1885     subaction_struct = gst_structure_from_string (subaction_str, NULL);
1886
1887     if (subaction_struct == NULL) {
1888       GST_VALIDATE_REPORT (scenario, SCENARIO_FILE_MALFORMED,
1889           "Sub action %s could not be parsed", subaction_str);
1890
1891       res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1892       goto done;
1893     }
1894
1895   } else {
1896     gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE,
1897         &subaction_struct, NULL);
1898   }
1899
1900   if (subaction_struct) {
1901     if (action->structure) {
1902       GST_INFO_OBJECT (scenario, "Clearing old action structure");
1903       gst_structure_free (action->structure);
1904     }
1905
1906     res = _fill_action (scenario, action, subaction_struct, FALSE);
1907     if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
1908       GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1909           "Sub action %" GST_PTR_FORMAT " could not be filled",
1910           subaction_struct);
1911
1912       goto done;
1913     }
1914
1915     if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) {
1916       GstValidateActionType *action_type = _find_action_type (action->type);
1917
1918       action->priv->printed = FALSE;
1919       res = gst_validate_execute_action (action_type, action);
1920
1921       goto done;
1922     }
1923
1924   }
1925
1926 done:
1927   if (scenario)
1928     gst_object_unref (scenario);
1929   if (subaction_struct)
1930     gst_structure_free (subaction_struct);
1931   return res;
1932 }
1933
1934
1935 /* This is the main action execution function
1936  * it checks whether it is time to run the next action
1937  * and if it is the case executes it.
1938  *
1939  * If the 'execute-on-idle' property is not TRUE,
1940  * the function will recurse while the actions are run
1941  * synchronously
1942  */
1943 static gboolean
1944 execute_next_action (GstValidateScenario * scenario)
1945 {
1946   GList *tmp;
1947   gdouble rate = 1.0;
1948   GstClockTime position = -1;
1949   GstValidateAction *act = NULL;
1950   GstValidateActionType *type;
1951
1952   GstValidateScenarioPrivate *priv = scenario->priv;
1953
1954   if (priv->buffering) {
1955     GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
1956
1957     return G_SOURCE_CONTINUE;
1958   }
1959
1960   if (priv->changing_state || priv->needs_async_done) {
1961     GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
1962     return G_SOURCE_CONTINUE;
1963   }
1964
1965   if (scenario->priv->actions)
1966     act = scenario->priv->actions->data;
1967
1968   if (act) {
1969
1970     if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS) {
1971       return G_SOURCE_CONTINUE;
1972     } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK
1973         && act->repeat <= 0) {
1974       tmp = priv->actions;
1975       priv->actions = g_list_remove_link (priv->actions, tmp);
1976
1977       GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
1978           " executing next", act->structure);
1979
1980       gst_validate_action_unref (act);
1981       g_list_free (tmp);
1982
1983       if (scenario->priv->actions) {
1984         act = scenario->priv->actions->data;
1985       } else {
1986         _check_scenario_is_done (scenario);
1987         act = NULL;
1988       }
1989     } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
1990       if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
1991         GstClockTime etime =
1992             gst_util_get_timestamp () - act->priv->execution_time;
1993
1994         if (etime > act->priv->timeout) {
1995           gchar *str = gst_structure_to_string (act->structure);
1996
1997           GST_VALIDATE_REPORT (scenario,
1998               SCENARIO_ACTION_EXECUTION_ERROR,
1999               "Action %s timed out after: %" GST_TIME_FORMAT, str,
2000               GST_TIME_ARGS (etime));
2001
2002           g_free (str);
2003         }
2004       }
2005       GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2006           act->structure);
2007
2008       return G_SOURCE_CONTINUE;
2009     }
2010   }
2011
2012   if (!_check_position (scenario, act, &position, &rate))
2013     return G_SOURCE_CONTINUE;
2014
2015   if (!_should_execute_action (scenario, act, position, rate)) {
2016     _add_execute_actions_gsource (scenario);
2017
2018     return G_SOURCE_CONTINUE;
2019   }
2020
2021   type = _find_action_type (act->type);
2022
2023   if (act->repeat == -1 &&
2024       !gst_structure_get_int (act->structure, "repeat", &act->repeat)) {
2025     gchar *error = NULL;
2026     const gchar *repeat_expr = gst_structure_get_string (act->structure,
2027         "repeat");
2028
2029     if (repeat_expr) {
2030       act->repeat =
2031           gst_validate_utils_parse_expression (repeat_expr,
2032           _set_variable_func, scenario, &error);
2033     }
2034   }
2035
2036   GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2037       " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2038   priv->seeked_in_pause = FALSE;
2039
2040   act->priv->state = gst_validate_execute_action (type, act);
2041   if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2042     gchar *str = gst_structure_to_string (act->structure);
2043
2044     GST_VALIDATE_REPORT (scenario,
2045         SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str);
2046
2047     g_free (str);
2048   }
2049
2050   if (act->repeat > 0 && !gst_validate_action_is_subaction (act)) {
2051     act->repeat--;
2052   }
2053
2054   if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
2055     act->priv->state = _execute_sub_action_action (act);
2056   }
2057
2058   if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
2059     tmp = priv->actions;
2060     priv->actions = g_list_remove_link (priv->actions, tmp);
2061
2062     if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED)
2063       gst_validate_action_unref (act);
2064     else {
2065       SCENARIO_LOCK (scenario);
2066       priv->interlaced_actions = g_list_append (priv->interlaced_actions, act);
2067       SCENARIO_UNLOCK (scenario);
2068     }
2069
2070     if (priv->actions == NULL)
2071       _check_scenario_is_done (scenario);
2072
2073     g_list_free (tmp);
2074
2075     /* Recurse to the next action if it is possible
2076      * to execute right away */
2077     if (!scenario->priv->execute_on_idle) {
2078       GST_DEBUG_OBJECT (scenario, "linking next action execution");
2079
2080       return execute_next_action (scenario);
2081     } else {
2082       _add_execute_actions_gsource (scenario);
2083       GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2084           " next dispatch");
2085
2086       return G_SOURCE_CONTINUE;
2087     }
2088   } else {
2089     GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2090         " to be done.");
2091
2092     SCENARIO_LOCK (scenario);
2093     priv->execute_actions_source_id = 0;
2094     SCENARIO_UNLOCK (scenario);
2095
2096     return G_SOURCE_CONTINUE;
2097   }
2098
2099   return G_SOURCE_CONTINUE;
2100 }
2101
2102 static gboolean
2103 stop_waiting (GstValidateAction * action)
2104 {
2105   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2106
2107   gst_validate_printf (scenario, "Stop waiting\n");
2108
2109   SCENARIO_LOCK (scenario);
2110   scenario->priv->wait_id = 0;
2111   SCENARIO_UNLOCK (scenario);
2112
2113   gst_validate_action_set_done (action);
2114   _add_execute_actions_gsource (scenario);
2115   gst_object_unref (scenario);
2116
2117
2118   return G_SOURCE_REMOVE;
2119 }
2120
2121 static GstElement *_get_target_element (GstValidateScenario * scenario,
2122     GstValidateAction * action);
2123
2124 static void
2125 stop_waiting_signal (GstBin * bin, GstElement * element,
2126     GstValidateAction * action)
2127 {
2128   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2129   GstValidateScenarioPrivate *priv = scenario->priv;
2130
2131   g_assert (scenario);
2132   gst_validate_printf (scenario, "Stop waiting for signal\n");
2133
2134   g_signal_handler_disconnect (bin, priv->signal_handler_id);
2135
2136   priv->signal_handler_id = 0;
2137   gst_validate_action_set_done (action);
2138   _add_execute_actions_gsource (scenario);
2139   gst_object_unref (scenario);
2140 }
2141
2142 static GstValidateExecuteActionReturn
2143 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2144 {
2145   GstValidateScenarioPrivate *priv = scenario->priv;
2146   GstClockTime duration;
2147
2148   gdouble wait_multiplier = 1;
2149   const gchar *str_wait_multiplier =
2150       g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2151
2152   if (str_wait_multiplier) {
2153     errno = 0;
2154     wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2155
2156     if (errno) {
2157       GST_ERROR ("Could not use the WAIT MULTIPLIER");
2158
2159       wait_multiplier = 1;
2160     }
2161
2162     if (wait_multiplier == 0) {
2163       GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2164       return GST_VALIDATE_EXECUTE_ACTION_OK;
2165     }
2166   }
2167
2168   if (!gst_validate_action_get_clocktime (scenario, action,
2169           "duration", &duration)) {
2170     GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2171
2172     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2173   }
2174
2175   duration *= wait_multiplier;
2176
2177   SCENARIO_LOCK (scenario);
2178   if (priv->execute_actions_source_id) {
2179     g_source_remove (priv->execute_actions_source_id);
2180     priv->execute_actions_source_id = 0;
2181   }
2182   SCENARIO_UNLOCK (scenario);
2183
2184   SCENARIO_LOCK (scenario);
2185   priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2186       (GSourceFunc) stop_waiting, action);
2187   SCENARIO_UNLOCK (scenario);
2188
2189   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2190 }
2191
2192 static GstValidateExecuteActionReturn
2193 _execute_wait_for_signal (GstValidateScenario * scenario,
2194     GstValidateAction * action)
2195 {
2196   GstValidateScenarioPrivate *priv = scenario->priv;
2197   const gchar *signal_name = gst_structure_get_string
2198       (action->structure, "signal-name");
2199   GstElement *target;
2200   DECLARE_AND_GET_PIPELINE (scenario, action);
2201
2202   if (signal_name == NULL) {
2203     GST_ERROR ("No signal-name given for wait action");
2204     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2205   }
2206
2207   target = _get_target_element (scenario, action);
2208   if (target == NULL) {
2209     gst_object_unref (pipeline);
2210
2211     return FALSE;
2212   }
2213
2214   gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2215
2216   if (priv->execute_actions_source_id) {
2217     g_source_remove (priv->execute_actions_source_id);
2218     priv->execute_actions_source_id = 0;
2219   }
2220
2221   priv->signal_handler_id =
2222       g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal,
2223       action);
2224
2225   gst_object_unref (target);
2226   gst_object_unref (pipeline);
2227
2228   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2229 }
2230
2231 static gboolean
2232 _execute_wait_for_message (GstValidateScenario * scenario,
2233     GstValidateAction * action)
2234 {
2235   GstValidateScenarioPrivate *priv = scenario->priv;
2236   const gchar *message_type = gst_structure_get_string
2237       (action->structure, "message-type");
2238   DECLARE_AND_GET_PIPELINE (scenario, action);
2239
2240   gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2241
2242   if (priv->execute_actions_source_id) {
2243     g_source_remove (priv->execute_actions_source_id);
2244     priv->execute_actions_source_id = 0;
2245   }
2246
2247   priv->message_type = g_strdup (message_type);
2248   gst_object_unref (pipeline);
2249
2250   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2251 }
2252
2253 static GstValidateExecuteActionReturn
2254 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2255 {
2256   if (gst_structure_has_field (action->structure, "signal-name")) {
2257     return _execute_wait_for_signal (scenario, action);
2258   } else if (gst_structure_has_field (action->structure, "message-type")) {
2259     return _execute_wait_for_message (scenario, action);
2260   } else {
2261     return _execute_timed_wait (scenario, action);
2262   }
2263
2264   return FALSE;
2265 }
2266
2267 static gboolean
2268 _execute_dot_pipeline (GstValidateScenario * scenario,
2269     GstValidateAction * action)
2270 {
2271   gchar *dotname;
2272   gint details = GST_DEBUG_GRAPH_SHOW_ALL;
2273   const gchar *name = gst_structure_get_string (action->structure, "name");
2274   DECLARE_AND_GET_PIPELINE (scenario, action);
2275
2276   gst_structure_get_int (action->structure, "details", &details);
2277   if (name)
2278     dotname = g_strdup_printf ("validate.action.%s", name);
2279   else
2280     dotname = g_strdup ("validate.action.unnamed");
2281
2282   GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
2283
2284   g_free (dotname);
2285   gst_object_unref (pipeline);
2286
2287   return TRUE;
2288 }
2289
2290 static GstElement *
2291 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
2292 {
2293   const gchar *name;
2294   GstElement *target;
2295   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2296
2297   if (!pipeline) {
2298     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2299
2300     return NULL;
2301   }
2302
2303   name = gst_structure_get_string (action->structure, "target-element-name");
2304   if (name == NULL) {
2305     gst_object_unref (pipeline);
2306
2307     return NULL;
2308   }
2309
2310   if (strcmp (GST_OBJECT_NAME (pipeline), name) == 0) {
2311     target = gst_object_ref (pipeline);
2312   } else {
2313     target = gst_bin_get_by_name (GST_BIN (pipeline), name);
2314   }
2315
2316   if (target == NULL)
2317     GST_ERROR ("Target element with given name (%s) not found", name);
2318   gst_object_unref (pipeline);
2319
2320   return target;
2321 }
2322
2323 /**
2324  * _get_target_elements_by_klass:
2325  * @scenario: a #GstValidateScenario
2326  * @action: a #GstValidateAction
2327  *
2328  * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
2329  * matches the 'target-element-klass' of @action and the factory name matches
2330  * the 'target-element-factory-name'.
2331  *
2332  * Returns: (transfer full) (element-type GstElement): a list of #GstElement
2333  */
2334 static GList *
2335 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
2336     GstValidateAction * action)
2337 {
2338   GList *result = NULL;
2339   GstIterator *it;
2340   const gchar *klass, *fname;
2341   GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
2342   gboolean done = FALSE;
2343   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2344
2345   if (!pipeline) {
2346     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2347
2348     return NULL;
2349   }
2350
2351   klass = gst_structure_get_string (action->structure, "target-element-klass");
2352   fname =
2353       gst_structure_get_string (action->structure,
2354       "target-element-factory-name");
2355   if (!klass && !fname) {
2356     gst_object_unref (pipeline);
2357
2358     return NULL;
2359   }
2360
2361   if (klass && gst_validate_element_has_klass (pipeline, klass))
2362     result = g_list_prepend (result, gst_object_ref (pipeline));
2363
2364   if (fname && gst_element_get_factory (pipeline)
2365       && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
2366           fname))
2367     result = g_list_prepend (result, gst_object_ref (pipeline));
2368
2369   it = gst_bin_iterate_recurse (GST_BIN (pipeline));
2370
2371   g_value_init (&param, G_TYPE_STRING);
2372   g_value_set_string (&param, klass);
2373
2374   while (!done) {
2375     switch (gst_iterator_next (it, &v)) {
2376       case GST_ITERATOR_OK:{
2377         GstElement *child = g_value_get_object (&v);
2378
2379         if (g_list_find (result, child))
2380           goto next;
2381
2382         if (klass && gst_validate_element_has_klass (child, klass)) {
2383           result = g_list_prepend (result, gst_object_ref (child));
2384           goto next;
2385         }
2386
2387         if (fname && gst_element_get_factory (child)
2388             && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
2389                 fname))
2390           result = g_list_prepend (result, gst_object_ref (child));
2391       next:
2392         g_value_reset (&v);
2393       }
2394         break;
2395       case GST_ITERATOR_RESYNC:
2396         gst_iterator_resync (it);
2397         break;
2398       case GST_ITERATOR_ERROR:
2399       case GST_ITERATOR_DONE:
2400         done = TRUE;
2401     }
2402   }
2403
2404   g_value_reset (&v);
2405   g_value_reset (&param);
2406   gst_iterator_free (it);
2407   gst_object_unref (pipeline);
2408
2409   return result;
2410 }
2411
2412 static gboolean
2413 _execute_set_property (GstValidateScenario * scenario,
2414     GstValidateAction * action)
2415 {
2416   GstElement *target;
2417   GList *targets = NULL, *l;
2418   const gchar *property;
2419   const GValue *property_value;
2420   gboolean ret = TRUE;
2421
2422   /* set-property can be applied on either:
2423    * - a single element having target-element-name as name
2424    * - all the elements having target-element-klass as klass
2425    */
2426   if (gst_structure_get_string (action->structure, "target-element-name")) {
2427     target = _get_target_element (scenario, action);
2428     if (target == NULL)
2429       return FALSE;
2430
2431     targets = g_list_append (targets, target);
2432   } else if (gst_structure_get_string (action->structure,
2433           "target-element-klass") ||
2434       gst_structure_get_string (action->structure,
2435           "target-element-factory-name")) {
2436     targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
2437   } else {
2438     g_assert_not_reached ();
2439   }
2440
2441   property = gst_structure_get_string (action->structure, "property-name");
2442   property_value = gst_structure_get_value (action->structure,
2443       "property-value");
2444
2445   for (l = targets; l != NULL; l = g_list_next (l)) {
2446     GstValidateActionReturn tmpres =
2447         gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
2448         G_OBJECT (l->data), property,
2449         property_value, action->priv->optional);
2450
2451     if (!tmpres)
2452       ret = tmpres;
2453   }
2454
2455   g_list_free_full (targets, gst_object_unref);
2456   return ret;
2457 }
2458
2459 static gboolean
2460 _execute_set_debug_threshold (GstValidateScenario * scenario,
2461     GstValidateAction * action)
2462 {
2463   gchar *str = NULL;
2464   gboolean reset = TRUE;
2465   const gchar *threshold_str;
2466
2467   threshold_str =
2468       gst_structure_get_string (action->structure, "debug-threshold");
2469   if (threshold_str == NULL) {
2470     gint threshold;
2471
2472     if (gst_structure_get_int (action->structure, "debug-threshold",
2473             &threshold))
2474       threshold_str = str = g_strdup_printf ("%i", threshold);
2475     else
2476       return FALSE;
2477   }
2478
2479   gst_structure_get_boolean (action->structure, "reset", &reset);
2480
2481   gst_debug_set_threshold_from_string (threshold_str, reset);
2482
2483   g_free (str);
2484
2485   return TRUE;
2486 }
2487
2488 static gboolean
2489 _execute_emit_signal (GstValidateScenario * scenario,
2490     GstValidateAction * action)
2491 {
2492   GstElement *target;
2493   const gchar *signal_name;
2494
2495   target = _get_target_element (scenario, action);
2496   if (target == NULL) {
2497     return FALSE;
2498   }
2499
2500   signal_name = gst_structure_get_string (action->structure, "signal-name");
2501
2502   /* Right now we don't support arguments to signals as there weren't any use
2503    * cases to cover yet but it should be possible to do so */
2504   g_signal_emit_by_name (target, signal_name, NULL);
2505
2506   gst_object_unref (target);
2507   return TRUE;
2508 }
2509
2510 static GstValidateExecuteActionReturn
2511 _execute_disable_plugin (GstValidateScenario * scenario,
2512     GstValidateAction * action)
2513 {
2514   GstPlugin *plugin;
2515   const gchar *plugin_name;
2516
2517   plugin_name = gst_structure_get_string (action->structure, "plugin-name");
2518
2519   plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
2520
2521   if (plugin == NULL) {
2522     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2523         "Could not find plugin to disable: %s", plugin_name);
2524
2525     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2526   }
2527
2528   gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
2529   gst_registry_remove_plugin (gst_registry_get (), plugin);
2530
2531   return GST_VALIDATE_EXECUTE_ACTION_OK;
2532 }
2533
2534 static void
2535 gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario,
2536     GstEvent * seek)
2537 {
2538   GstValidateScenarioPrivate *priv = scenario->priv;
2539   gint64 start, stop;
2540   GstSeekType start_type, stop_type;
2541
2542   gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start,
2543       &stop_type, &stop);
2544
2545   if (start_type == GST_SEEK_TYPE_SET) {
2546     priv->segment_start = start;
2547   } else if (start_type == GST_SEEK_TYPE_END) {
2548     /* TODO fill me */
2549   }
2550
2551   if (stop_type == GST_SEEK_TYPE_SET) {
2552     priv->segment_stop = stop;
2553   } else if (stop_type == GST_SEEK_TYPE_END) {
2554     /* TODO fill me */
2555   }
2556 }
2557
2558 static gint
2559 _compare_actions (GstValidateAction * a, GstValidateAction * b)
2560 {
2561   if (a->action_number < b->action_number)
2562     return -1;
2563   else if (a->action_number == b->action_number)
2564     return 0;
2565
2566   return 1;
2567 }
2568
2569 static gboolean
2570 _structure_set_variables (GQuark field_id, GValue * value,
2571     GstValidateAction * action)
2572 {
2573   GstValidateScenario *scenario;
2574   const gchar *var_value;
2575
2576   if (!G_VALUE_HOLDS_STRING (value))
2577     return TRUE;
2578
2579   scenario = gst_validate_action_get_scenario (action);
2580   if (!scenario)
2581     return TRUE;
2582
2583   var_value =
2584       gst_structure_get_string (scenario->priv->vars,
2585       g_value_get_string (value));
2586   if (var_value) {
2587     GST_INFO_OBJECT (action, "Setting variable %s to %s",
2588         g_value_get_string (value), var_value);
2589     g_value_set_string (value, var_value);
2590   }
2591
2592   g_clear_object (&scenario);
2593
2594   return TRUE;
2595 }
2596
2597 static gboolean
2598 gst_validate_action_default_prepare_func (GstValidateAction * action)
2599 {
2600   gulong i;
2601   GstClockTime time;
2602   const gchar *vars[] = { "duration", "start", "stop" };
2603   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2604
2605   for (i = 0; i < G_N_ELEMENTS (vars); i++) {
2606     gint res = gst_validate_action_get_clocktime (scenario, action, vars[i],
2607         &time);
2608     if (res == FALSE) {
2609       GST_ERROR_OBJECT (scenario, "Could not get clocktime for"
2610           " variable %s", vars[i]);
2611
2612       gst_object_unref (scenario);
2613       return FALSE;
2614     } else if (res == -1) {
2615       continue;
2616     }
2617
2618     gst_structure_set (action->structure, vars[i], GST_TYPE_CLOCK_TIME,
2619         time, NULL);
2620   }
2621   gst_object_unref (scenario);
2622   gst_structure_filter_and_map_in_place (action->structure,
2623       (GstStructureFilterMapFunc) _structure_set_variables, action);
2624
2625   return TRUE;
2626 }
2627
2628 static void
2629 _check_waiting_for_message (GstValidateScenario * scenario,
2630     GstMessage * message)
2631 {
2632   GstValidateScenarioPrivate *priv = scenario->priv;
2633
2634   if (!g_strcmp0 (priv->message_type,
2635           gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
2636     GstValidateAction *action = scenario->priv->actions->data;
2637
2638     g_free ((gpointer) priv->message_type);
2639     priv->message_type = NULL;
2640
2641     gst_validate_printf (scenario, "Stop waiting for message\n");
2642
2643     gst_validate_action_set_done (action);
2644     _add_execute_actions_gsource (scenario);
2645   }
2646 }
2647
2648 static gboolean
2649 streams_list_contain (GList * streams, const gchar * stream_id)
2650 {
2651   GList *l;
2652
2653   for (l = streams; l; l = g_list_next (l)) {
2654     GstStream *s = l->data;
2655
2656     if (!g_strcmp0 (s->stream_id, stream_id))
2657       return TRUE;
2658   }
2659
2660   return FALSE;
2661 }
2662
2663 static void
2664 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
2665     GstElement * pipeline)
2666 {
2667   GstValidateScenarioPrivate *priv = scenario->priv;
2668   GstQuery *query;
2669   GstClockTime min_latency;
2670
2671   query = gst_query_new_latency ();
2672   if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
2673     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2674         "Failed to perfom LATENCY query");
2675     gst_query_unref (query);
2676     return;
2677   }
2678
2679   gst_query_parse_latency (query, NULL, &min_latency, NULL);
2680   GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
2681       " max allowed: %" GST_TIME_FORMAT,
2682       GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
2683
2684   if (priv->max_latency != GST_CLOCK_TIME_NONE &&
2685       min_latency > priv->max_latency) {
2686     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_LATENCY_TOO_HIGH,
2687         "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
2688         GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
2689         GST_TIME_ARGS (priv->max_latency));
2690   }
2691 }
2692
2693 static gboolean
2694 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
2695 {
2696   gboolean is_error = FALSE;
2697   GstValidateScenarioPrivate *priv = scenario->priv;
2698   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2699
2700   if (!pipeline) {
2701     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2702
2703     return FALSE;
2704   }
2705
2706   switch (GST_MESSAGE_TYPE (message)) {
2707     case GST_MESSAGE_ASYNC_DONE:
2708       if (priv->last_seek) {
2709         gst_validate_scenario_update_segment_from_seek (scenario,
2710             priv->last_seek);
2711
2712         if (priv->target_state == GST_STATE_PAUSED)
2713           priv->seeked_in_pause = TRUE;
2714
2715         gst_event_replace (&priv->last_seek, NULL);
2716         gst_validate_action_set_done (priv->actions->data);
2717       } else if (scenario->priv->needs_async_done) {
2718         scenario->priv->needs_async_done = FALSE;
2719         if (priv->actions && _action_sets_state (priv->actions->data)
2720             && !priv->changing_state)
2721           gst_validate_action_set_done (priv->actions->data);
2722
2723       }
2724
2725       if (priv->needs_parsing) {
2726         GList *tmp;
2727
2728         for (tmp = priv->needs_parsing; tmp; tmp = tmp->next) {
2729           GstValidateAction *action = (GstValidateAction *) tmp->data;
2730
2731           if (!_set_action_playback_time (scenario, action))
2732             return FALSE;
2733
2734           priv->actions = g_list_insert_sorted (priv->actions, action,
2735               (GCompareFunc) _compare_actions);
2736         }
2737
2738         g_list_free (priv->needs_parsing);
2739         priv->needs_parsing = NULL;
2740       }
2741       _add_execute_actions_gsource (scenario);
2742       break;
2743     case GST_MESSAGE_STATE_CHANGED:
2744     {
2745       if (pipeline && GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
2746         GstState nstate, pstate;
2747
2748         gst_message_parse_state_changed (message, &pstate, &nstate, NULL);
2749
2750         if (scenario->priv->changing_state &&
2751             scenario->priv->target_state == nstate) {
2752           scenario->priv->changing_state = FALSE;
2753
2754           if (priv->actions && _action_sets_state (priv->actions->data) &&
2755               !priv->needs_async_done)
2756             gst_validate_action_set_done (priv->actions->data);
2757         }
2758
2759         if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED)
2760           _add_execute_actions_gsource (scenario);
2761
2762         /* GstBin only send a new latency message when reaching PLAYING if
2763          * async-handling=true so check the latency manually. */
2764         if (nstate == GST_STATE_PLAYING)
2765           gst_validate_scenario_check_latency (scenario, pipeline);
2766       }
2767       break;
2768     }
2769     case GST_MESSAGE_ERROR:
2770       is_error = TRUE;
2771
2772       /* Passtrough */
2773     case GST_MESSAGE_EOS:
2774     {
2775       GstValidateAction *stop_action;
2776       GstValidateActionType *stop_action_type;
2777       GstStructure *s;
2778
2779       if (!is_error) {
2780         priv->got_eos = TRUE;
2781         if (priv->message_type) {
2782
2783           if (priv->actions->next) {
2784             GST_DEBUG_OBJECT (scenario,
2785                 "Waiting for a message and got a next action"
2786                 " to execute, letting it a chance!");
2787             goto done;
2788           } else {
2789             /* Clear current message wait if waiting for EOS */
2790             _check_waiting_for_message (scenario, message);
2791           }
2792         }
2793       }
2794
2795       SCENARIO_LOCK (scenario);
2796       if (scenario->priv->actions || scenario->priv->interlaced_actions ||
2797           scenario->priv->on_addition_actions) {
2798         guint nb_actions = 0;
2799         gchar *actions = g_strdup (""), *tmpconcat;
2800         GList *tmp;
2801         GList *all_actions =
2802             g_list_concat (g_list_concat (scenario->priv->actions,
2803                 scenario->priv->interlaced_actions),
2804             scenario->priv->on_addition_actions);
2805
2806         for (tmp = all_actions; tmp; tmp = tmp->next) {
2807           gchar *action_string;
2808           GstValidateAction *action = (GstValidateAction *) tmp->data;
2809           GstValidateActionType *type = _find_action_type (action->type);
2810
2811           tmpconcat = actions;
2812
2813           if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
2814               action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
2815               action->priv->optional) {
2816             gst_validate_action_unref (action);
2817
2818             continue;
2819           }
2820
2821           nb_actions++;
2822
2823           action_string = gst_structure_to_string (action->structure);
2824           actions =
2825               g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string);
2826           gst_validate_action_unref (action);
2827           g_free (tmpconcat);
2828           g_free (action_string);
2829         }
2830         g_list_free (all_actions);
2831         scenario->priv->actions = NULL;
2832         scenario->priv->interlaced_actions = NULL;
2833         scenario->priv->on_addition_actions = NULL;
2834
2835
2836         if (nb_actions > 0) {
2837           GstClockTime position = GST_CLOCK_TIME_NONE;
2838
2839           _get_position (scenario, NULL, &position);
2840           GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
2841               "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
2842               ")", nb_actions, actions, GST_TIME_ARGS (position));
2843         }
2844         g_free (actions);
2845       }
2846       SCENARIO_UNLOCK (scenario);
2847
2848       GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
2849
2850       stop_action_type = _find_action_type ("stop");
2851       stop_action = gst_validate_action_new (scenario, stop_action_type);
2852       s = gst_structure_from_string ("stop, generated-after-eos=true;", NULL);
2853       _fill_action (scenario, stop_action, s, FALSE);
2854       gst_structure_free (s);
2855       gst_validate_execute_action (stop_action_type, stop_action);
2856       gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
2857
2858       break;
2859     }
2860     case GST_MESSAGE_BUFFERING:
2861     {
2862       gint percent;
2863
2864       gst_message_parse_buffering (message, &percent);
2865
2866       if (percent == 100)
2867         priv->buffering = FALSE;
2868       else
2869         priv->buffering = TRUE;
2870       break;
2871     }
2872     case GST_MESSAGE_STREAMS_SELECTED:
2873     {
2874       guint i;
2875       GList *streams_selected = NULL;
2876
2877       for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
2878         GstStream *stream =
2879             gst_message_streams_selected_get_stream (message, i);
2880
2881         streams_selected = g_list_append (streams_selected, stream);
2882       }
2883
2884       /* Is there a pending switch-track action waiting for the new streams to
2885        * be selected? */
2886       if (priv->pending_switch_track) {
2887         GList *expected, *l;
2888         GstValidateScenario *scenario =
2889             gst_validate_action_get_scenario (priv->pending_switch_track);
2890
2891         expected =
2892             gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
2893             (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
2894
2895         if (g_list_length (expected) != g_list_length (streams_selected)) {
2896           GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2897               "Was expecting %d selected streams but got %d",
2898               g_list_length (expected), g_list_length (streams_selected));
2899           goto action_done;
2900         }
2901
2902         for (l = expected; l; l = g_list_next (l)) {
2903           const gchar *stream_id = l->data;
2904
2905           if (!streams_list_contain (streams_selected, stream_id)) {
2906             GST_VALIDATE_REPORT (scenario,
2907                 SCENARIO_ACTION_EXECUTION_ERROR,
2908                 "Stream %s has not be activated", stream_id);
2909             goto action_done;
2910           }
2911         }
2912
2913       action_done:
2914         gst_object_unref (scenario);
2915         gst_validate_action_set_done (priv->pending_switch_track);
2916         priv->pending_switch_track = NULL;
2917       }
2918
2919       g_list_free_full (streams_selected, gst_object_unref);
2920       break;
2921     }
2922     case GST_MESSAGE_LATENCY:
2923       gst_validate_scenario_check_latency (scenario, pipeline);
2924       break;
2925
2926     case GST_MESSAGE_QOS:
2927     {
2928       guint64 dropped;
2929
2930       /* Check the maximum allowed when scenario is terminating so the report
2931        * will include the actual number of dropped buffers. */
2932       gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
2933       if (dropped != -1)
2934         g_atomic_int_set (&priv->dropped, dropped);
2935       break;
2936     }
2937
2938     default:
2939       break;
2940   }
2941
2942 done:
2943   gst_object_unref (pipeline);
2944   /* Check if we got the message expected by a wait action */
2945   if (priv->message_type)
2946     _check_waiting_for_message (scenario, message);
2947
2948   return TRUE;
2949 }
2950
2951 static gboolean
2952 _action_type_has_parameter (GstValidateActionType * atype,
2953     const gchar * paramname)
2954 {
2955   gint i;
2956
2957   if (!atype->parameters)
2958     return FALSE;
2959
2960   for (i = 0; atype->parameters[i].name; i++)
2961     if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
2962       return TRUE;
2963
2964   return FALSE;
2965 }
2966
2967 static gboolean
2968 _load_scenario_file (GstValidateScenario * scenario,
2969     const gchar * scenario_file, gboolean * is_config)
2970 {
2971   gboolean ret = TRUE;
2972   GList *structures, *tmp;
2973   GstValidateScenarioPrivate *priv = scenario->priv;
2974   GList *config;
2975
2976   *is_config = FALSE;
2977
2978   structures = gst_validate_utils_structs_parse_from_filename (scenario_file);
2979   if (structures == NULL)
2980     goto failed;
2981
2982   for (tmp = structures; tmp; tmp = tmp->next) {
2983     GstValidateAction *action;
2984     GstValidateActionType *action_type;
2985     const gchar *type;
2986     GstStructure *structure = (GstStructure *) tmp->data;
2987
2988     type = gst_structure_get_name (structure);
2989     if (!g_strcmp0 (type, "description")) {
2990       const gchar *pipeline_name;
2991
2992       gst_structure_get_boolean (structure, "is-config", is_config);
2993       gst_structure_get_boolean (structure, "handles-states",
2994           &priv->handles_state);
2995
2996       pipeline_name = gst_structure_get_string (structure, "pipeline-name");
2997       if (pipeline_name) {
2998         g_free (priv->pipeline_name);
2999         priv->pipeline_name = g_strdup (pipeline_name);
3000       }
3001
3002       gst_validate_utils_get_clocktime (structure, "max-latency",
3003           &priv->max_latency);
3004
3005       gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
3006
3007       continue;
3008     } else if (!g_strcmp0 (type, "include")) {
3009       const gchar *location = gst_structure_get_string (structure, "location");
3010
3011       if (!location) {
3012         GST_ERROR_OBJECT (scenario,
3013             "Mandatory field 'location' not present in structure: %"
3014             GST_PTR_FORMAT, structure);
3015         goto failed;
3016       }
3017
3018       if (!gst_validate_scenario_load (scenario, location, scenario_file)) {
3019         GST_ERROR ("Failed including scenario %s", location);
3020         goto failed;
3021       }
3022
3023       continue;
3024     } else if (!(action_type = _find_action_type (type))) {
3025       if (gst_structure_has_field (structure, "optional-action-type")) {
3026         GST_INFO_OBJECT (scenario,
3027             "Action type not found %s but marked as not mandatory", type);
3028         continue;
3029       }
3030
3031       GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type);
3032       goto failed;
3033     }
3034
3035     if (action_type->parameters) {
3036       guint i;
3037
3038       for (i = 0; action_type->parameters[i].name; i++) {
3039         if (action_type->parameters[i].mandatory &&
3040             gst_structure_has_field (structure,
3041                 action_type->parameters[i].name) == FALSE) {
3042           GST_ERROR_OBJECT (scenario,
3043               "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
3044               action_type->parameters[i].name, structure);
3045           goto failed;
3046         }
3047       }
3048     }
3049
3050     action = gst_validate_action_new (scenario, action_type);
3051     if (_fill_action (scenario, action,
3052             structure, TRUE) == GST_VALIDATE_EXECUTE_ACTION_ERROR)
3053       goto failed;
3054
3055     action->action_number = priv->num_actions++;
3056   }
3057
3058   /* max-latency and max-dropped can be overriden using config */
3059   for (config = gst_validate_plugin_get_config (NULL); config;
3060       config = g_list_next (config)) {
3061     gst_validate_utils_get_clocktime (config->data, "max-latency",
3062         &priv->max_latency);
3063
3064     gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
3065   }
3066
3067 done:
3068   g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3069
3070   return ret;
3071
3072 failed:
3073   ret = FALSE;
3074
3075   goto done;
3076 }
3077
3078 static gboolean
3079 gst_validate_scenario_load (GstValidateScenario * scenario,
3080     const gchar * scenario_name, const gchar * relative_scenario)
3081 {
3082   gchar **scenarios = NULL;
3083   guint i;
3084   gboolean found_actions = FALSE, is_config, ret = TRUE;
3085   gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
3086
3087   gchar **env_scenariodir;
3088
3089   if (relative_scenario) {
3090     gchar *relative_dir = g_path_get_dirname (relative_scenario);
3091     gchar *tmp_scenarios_path =
3092         g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
3093         relative_dir);
3094
3095     g_free (scenarios_path);
3096     scenarios_path = tmp_scenarios_path;
3097   }
3098
3099   env_scenariodir =
3100       scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
3101       0) : NULL;
3102   g_free (scenarios_path);
3103
3104   if (!scenario_name)
3105     goto invalid_name;
3106
3107   scenarios = g_strsplit (scenario_name, ":", -1);
3108
3109   for (i = 0; scenarios[i]; i++) {
3110     gchar *lfilename = NULL, *tldir = NULL;
3111
3112     /* First check if the scenario name is not a full path to the
3113      * actual scenario */
3114     if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
3115       GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
3116           "Trying to load it", scenarios[i]);
3117       if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config)))
3118         goto check_scenario;
3119     }
3120
3121     if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
3122       lfilename = g_strdup (scenarios[i]);
3123     else
3124       lfilename =
3125           g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
3126
3127     if (env_scenariodir) {
3128       guint i;
3129
3130       for (i = 0; env_scenariodir[i]; i++) {
3131         tldir = g_build_filename (env_scenariodir[i], lfilename, NULL);
3132         if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3133           goto check_scenario;
3134         g_free (tldir);
3135       }
3136     }
3137
3138     tldir = g_build_filename ("data", "scenarios", lfilename, NULL);
3139
3140     if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3141       goto check_scenario;
3142
3143     g_free (tldir);
3144
3145     /* Try from local profiles */
3146     tldir =
3147         g_build_filename (g_get_user_data_dir (),
3148         "gstreamer-" GST_API_VERSION, "validate",
3149         GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3150
3151     if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3152       g_free (tldir);
3153       /* Try from system-wide profiles */
3154       tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3155           "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3156
3157       if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3158         goto error;
3159       }
3160     }
3161     /* else check scenario */
3162   check_scenario:
3163     g_free (tldir);
3164     g_free (lfilename);
3165
3166     if (!is_config) {
3167       if (found_actions == TRUE)
3168         goto one_actions_scenario_max;
3169       else
3170         found_actions = TRUE;
3171     }
3172   }
3173
3174 done:
3175
3176   if (env_scenariodir)
3177     g_strfreev (env_scenariodir);
3178
3179   g_strfreev (scenarios);
3180
3181   if (ret == FALSE)
3182     g_error ("Could not set scenario %s => EXIT\n", scenario_name);
3183
3184   return ret;
3185
3186 invalid_name:
3187   {
3188     GST_ERROR ("Invalid name for scenario '%s'", scenario_name);
3189   error:
3190     ret = FALSE;
3191     goto done;
3192   }
3193 one_actions_scenario_max:
3194   {
3195     GST_ERROR ("You can set at most only one action scenario. "
3196         "You can have several config scenarios though (a config scenario's "
3197         "file must have is-config=true, and all its actions must be executable "
3198         "at parsing time).");
3199     ret = FALSE;
3200     goto done;
3201
3202   }
3203 }
3204
3205
3206 static void
3207 gst_validate_scenario_set_property (GObject * object, guint prop_id,
3208     const GValue * value, GParamSpec * pspec)
3209 {
3210   GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3211
3212   switch (prop_id) {
3213     case PROP_RUNNER:
3214       /* we assume the runner is valid as long as this scenario is,
3215        * no ref taken */
3216       gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
3217           g_value_get_object (value));
3218       break;
3219     case PROP_HANDLES_STATE:
3220       g_assert_not_reached ();
3221       break;
3222     case PROP_EXECUTE_ON_IDLE:
3223       self->priv->execute_on_idle = g_value_get_boolean (value);
3224       break;
3225     default:
3226       break;
3227   }
3228 }
3229
3230 static void
3231 gst_validate_scenario_get_property (GObject * object, guint prop_id,
3232     GValue * value, GParamSpec * pspec)
3233 {
3234   GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3235
3236   switch (prop_id) {
3237     case PROP_RUNNER:
3238       /* we assume the runner is valid as long as this scenario is,
3239        * no ref taken */
3240       g_value_take_object (value,
3241           gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
3242       break;
3243     case PROP_HANDLES_STATE:
3244       g_value_set_boolean (value, self->priv->handles_state);
3245       break;
3246     case PROP_EXECUTE_ON_IDLE:
3247       g_value_set_boolean (value, self->priv->execute_on_idle);
3248       break;
3249     default:
3250       break;
3251   }
3252 }
3253
3254 static void
3255 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
3256 {
3257   GObjectClass *object_class = G_OBJECT_CLASS (klass);
3258
3259   object_class->dispose = gst_validate_scenario_dispose;
3260   object_class->finalize = gst_validate_scenario_finalize;
3261
3262   object_class->get_property = gst_validate_scenario_get_property;
3263   object_class->set_property = gst_validate_scenario_set_property;
3264
3265   g_object_class_install_property (object_class, PROP_RUNNER,
3266       g_param_spec_object ("validate-runner", "VALIDATE Runner",
3267           "The Validate runner to report errors to",
3268           GST_TYPE_VALIDATE_RUNNER,
3269           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
3270
3271   g_object_class_install_property (object_class, PROP_HANDLES_STATE,
3272       g_param_spec_boolean ("handles-states", "Handles state",
3273           "True if the application should not handle the first state change. "
3274           "False if it is application responsibility",
3275           FALSE, G_PARAM_READABLE));
3276
3277   g_object_class_install_property (object_class,
3278       PROP_EXECUTE_ON_IDLE,
3279       g_param_spec_boolean ("execute-on-idle",
3280           "Force waiting between actions",
3281           "Always execute actions on idle and do not chain them to execute as"
3282           " fast as possible. Setting this property is useful if action"
3283           " execution can lead to the addition of new sources on the same main"
3284           " loop as it provides these new GSource a chance to be dispatched"
3285           " between actions", FALSE, G_PARAM_READWRITE));
3286
3287   /**
3288    * GstValidateScenario::done:
3289    * @scenario: The scenario runing
3290    *
3291    * Emitted once all actions have been executed
3292    */
3293   scenario_signals[DONE] =
3294       g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
3295       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
3296 }
3297
3298 static void
3299 gst_validate_scenario_init (GstValidateScenario * scenario)
3300 {
3301   GstValidateScenarioPrivate *priv = scenario->priv =
3302       gst_validate_scenario_get_instance_private (scenario);
3303
3304   priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
3305   priv->segment_start = 0;
3306   priv->segment_stop = GST_CLOCK_TIME_NONE;
3307   priv->action_execution_interval = 10;
3308   priv->vars = gst_structure_new_empty ("vars");
3309   g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
3310   priv->max_latency = GST_CLOCK_TIME_NONE;
3311   priv->max_dropped = -1;
3312
3313   g_mutex_init (&priv->lock);
3314 }
3315
3316 static void
3317 gst_validate_scenario_dispose (GObject * object)
3318 {
3319   GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3320
3321   if (priv->last_seek)
3322     gst_event_unref (priv->last_seek);
3323   g_weak_ref_clear (&priv->ref_pipeline);
3324
3325   if (priv->bus) {
3326     gst_bus_remove_signal_watch (priv->bus);
3327     gst_object_unref (priv->bus);
3328     priv->bus = NULL;
3329   }
3330
3331   G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
3332 }
3333
3334 static void
3335 gst_validate_scenario_finalize (GObject * object)
3336 {
3337   GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3338
3339   /* Because g_object_add_weak_pointer() is used, this MUST be on the
3340    * main thread. */
3341   g_assert (g_main_context_acquire (g_main_context_default ()));
3342   g_main_context_release (g_main_context_default ());
3343
3344   g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref);
3345   g_list_free_full (priv->interlaced_actions,
3346       (GDestroyNotify) gst_mini_object_unref);
3347   g_list_free_full (priv->on_addition_actions,
3348       (GDestroyNotify) gst_mini_object_unref);
3349   g_list_free_full (priv->needs_parsing,
3350       (GDestroyNotify) gst_mini_object_unref);
3351   g_free (priv->pipeline_name);
3352   gst_structure_free (priv->vars);
3353   g_mutex_clear (&priv->lock);
3354
3355   G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
3356 }
3357
3358 static void _element_added_cb (GstBin * bin, GstElement * element,
3359     GstValidateScenario * scenario);
3360
3361 static void
3362 iterate_children (GstValidateScenario * scenario, GstBin * bin)
3363 {
3364   GstIterator *it;
3365   GValue v = G_VALUE_INIT;
3366   gboolean done = FALSE;
3367   GHashTable *called;           /* set of GstElement on which we already called _element_added_cb() */
3368
3369   called = g_hash_table_new (NULL, NULL);
3370   it = gst_bin_iterate_elements (bin);
3371
3372   while (!done) {
3373     switch (gst_iterator_next (it, &v)) {
3374       case GST_ITERATOR_OK:{
3375         GstElement *child = g_value_get_object (&v);
3376
3377         if (g_hash_table_lookup (called, child) == NULL) {
3378           _element_added_cb (bin, child, scenario);
3379           g_hash_table_add (called, child);
3380         }
3381         g_value_reset (&v);
3382       }
3383         break;
3384       case GST_ITERATOR_RESYNC:
3385         gst_iterator_resync (it);
3386         break;
3387       case GST_ITERATOR_ERROR:
3388       case GST_ITERATOR_DONE:
3389         done = TRUE;
3390     }
3391   }
3392   g_value_reset (&v);
3393   gst_iterator_free (it);
3394   g_hash_table_unref (called);
3395 }
3396
3397 static gboolean
3398 should_execute_action (GstElement * element, GstValidateAction * action)
3399 {
3400   const gchar *tmp;
3401
3402   tmp = gst_structure_get_string (action->structure, "target-element-name");
3403   if (tmp != NULL && !strcmp (tmp, GST_ELEMENT_NAME (element)))
3404     return TRUE;
3405
3406   tmp = gst_structure_get_string (action->structure, "target-element-klass");
3407   if (tmp != NULL && gst_validate_element_has_klass (element, tmp))
3408     return TRUE;
3409
3410   tmp =
3411       gst_structure_get_string (action->structure,
3412       "target-element-factory-name");
3413   if (tmp != NULL && gst_element_get_factory (element)
3414       && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)), tmp))
3415     return TRUE;
3416
3417   return FALSE;
3418 }
3419
3420 static void
3421 _element_added_cb (GstBin * bin, GstElement * element,
3422     GstValidateScenario * scenario)
3423 {
3424   GList *tmp;
3425
3426   GstValidateScenarioPrivate *priv = scenario->priv;
3427
3428   /* Check if it's an element we track for a set-property action */
3429   SCENARIO_LOCK (scenario);
3430   tmp = priv->on_addition_actions;
3431   while (tmp) {
3432     GstValidateAction *action = (GstValidateAction *) tmp->data;
3433
3434     if (action->playback_time != GST_CLOCK_TIME_NONE)
3435       break;
3436     if (g_strcmp0 (action->type, "set-property"))
3437       break;
3438
3439     GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
3440         action, action->type);
3441     if (should_execute_action (element, action)) {
3442       GstValidateActionType *action_type;
3443       action_type = _find_action_type (action->type);
3444       GST_DEBUG_OBJECT (element, "Executing set-property action");
3445       if (gst_validate_execute_action (action_type, action)) {
3446         priv->on_addition_actions =
3447             g_list_remove_link (priv->on_addition_actions, tmp);
3448         gst_mini_object_unref (GST_MINI_OBJECT (action));
3449         g_list_free (tmp);
3450         tmp = priv->on_addition_actions;
3451       } else
3452         tmp = tmp->next;
3453     } else
3454       tmp = tmp->next;
3455   }
3456   SCENARIO_UNLOCK (scenario);
3457
3458   _check_scenario_is_done (scenario);
3459
3460   /* If it's a bin, listen to the child */
3461   if (GST_IS_BIN (element)) {
3462     g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
3463         scenario);
3464     iterate_children (scenario, GST_BIN (element));
3465   }
3466 }
3467
3468 /**
3469  * gst_validate_scenario_factory_create:
3470  * @runner: The #GstValidateRunner to use to report issues
3471  * @pipeline: The pipeline to run the scenario on
3472  * @scenario_name: The name (or path) of the scenario to run
3473  *
3474  * Returns: (transfer full): A #GstValidateScenario or NULL
3475  */
3476 GstValidateScenario *
3477 gst_validate_scenario_factory_create (GstValidateRunner *
3478     runner, GstElement * pipeline, const gchar * scenario_name)
3479 {
3480   GList *config;
3481   GstValidateScenario *scenario =
3482       g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
3483       runner, NULL);
3484
3485   GST_LOG ("Creating scenario %s", scenario_name);
3486   if (!gst_validate_scenario_load (scenario, scenario_name, NULL)) {
3487     g_object_unref (scenario);
3488
3489     return NULL;
3490   }
3491
3492   if (scenario->priv->pipeline_name &&
3493       !g_pattern_match_simple (scenario->priv->pipeline_name,
3494           GST_OBJECT_NAME (pipeline))) {
3495     GST_INFO ("Scenario %s only applies on pipeline %s not %s",
3496         scenario_name, scenario->priv->pipeline_name,
3497         GST_OBJECT_NAME (pipeline));
3498
3499     gst_object_unref (scenario);
3500
3501     return NULL;
3502   }
3503
3504   g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
3505   gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
3506       g_strdup (scenario_name));
3507
3508   g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
3509       scenario);
3510
3511   iterate_children (scenario, GST_BIN (pipeline));
3512
3513   scenario->priv->bus = gst_element_get_bus (pipeline);
3514   gst_bus_add_signal_watch (scenario->priv->bus);
3515   g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
3516       scenario);
3517
3518   for (config = gst_validate_plugin_get_config (NULL); config;
3519       config = config->next) {
3520     gint interval;
3521
3522     if (gst_structure_get_uint (config->data,
3523             "scenario-action-execution-interval",
3524             &scenario->priv->action_execution_interval)) {
3525       GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3526           scenario->priv->action_execution_interval);
3527       break;
3528     } else if (gst_structure_get_int (config->data,
3529             "scenario-action-execution-interval", &interval)) {
3530       if (interval > 0) {
3531         scenario->priv->action_execution_interval = (guint) interval;
3532         GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3533             scenario->priv->action_execution_interval);
3534
3535         break;
3536       } else {
3537         GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
3538       }
3539     }
3540   }
3541
3542   if (scenario->priv->handles_state) {
3543     GST_INFO_OBJECT (scenario, "Scenario handles state."
3544         " Starting the get position source");
3545     _add_execute_actions_gsource (scenario);
3546   }
3547
3548   gst_validate_printf (NULL,
3549       "\n=========================================\n"
3550       "Running scenario %s on pipeline %s"
3551       "\n=========================================\n", scenario_name,
3552       GST_OBJECT_NAME (pipeline));
3553
3554   scenario->priv->overrides =
3555       gst_validate_override_registry_get_override_for_names
3556       (gst_validate_override_registry_get (), "scenarios", NULL);
3557
3558   return scenario;
3559 }
3560
3561 static gboolean
3562 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
3563 {
3564   gchar *tmp = gst_value_serialize (value);
3565
3566   g_key_file_set_string (kfg->kf, kfg->group_name,
3567       g_quark_to_string (field_id), g_strcompress (tmp));
3568
3569   g_free (tmp);
3570
3571   return TRUE;
3572 }
3573
3574
3575 static gboolean
3576 _parse_scenario (GFile * f, GKeyFile * kf)
3577 {
3578   gboolean ret = FALSE;
3579   gchar *fname = g_file_get_basename (f);
3580
3581   if (g_str_has_suffix (fname, GST_VALIDATE_SCENARIO_SUFFIX)) {
3582     gboolean needs_clock_sync = FALSE;
3583     GstStructure *desc = NULL;
3584
3585     gchar **name = g_strsplit (fname, GST_VALIDATE_SCENARIO_SUFFIX, 0);
3586     GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f);
3587
3588     for (tmp = structures; tmp; tmp = tmp->next) {
3589       GstStructure *_struct = (GstStructure *) tmp->data;
3590       GstValidateActionType *type =
3591           _find_action_type (gst_structure_get_name (_struct));
3592
3593       if (!desc && gst_structure_has_name (_struct, "description"))
3594         desc = gst_structure_copy (_struct);
3595       else if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
3596         needs_clock_sync = TRUE;
3597     }
3598
3599     if (needs_clock_sync) {
3600       if (desc)
3601         gst_structure_set (desc, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
3602       else
3603         desc = gst_structure_from_string ("description, need-clock-sync=true;",
3604             NULL);
3605     }
3606
3607     if (desc) {
3608       KeyFileGroupName kfg;
3609
3610       kfg.group_name = name[0];
3611       kfg.kf = kf;
3612
3613       gst_structure_foreach (desc,
3614           (GstStructureForeachFunc) _add_description, &kfg);
3615       gst_structure_free (desc);
3616     } else {
3617       g_key_file_set_string (kf, name[0], "noinfo", "nothing");
3618     }
3619     g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3620     g_strfreev (name);
3621
3622     ret = TRUE;
3623   }
3624
3625   g_free (fname);
3626   return ret;
3627 }
3628
3629 static void
3630 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
3631 {
3632   GFileEnumerator *fenum;
3633   GFileInfo *info;
3634
3635   fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
3636       G_FILE_QUERY_INFO_NONE, NULL, NULL);
3637
3638   if (fenum == NULL)
3639     return;
3640
3641   for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
3642       info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
3643     GFile *f = g_file_enumerator_get_child (fenum, info);
3644
3645     _parse_scenario (f, kf);
3646     gst_object_unref (f);
3647   }
3648
3649   gst_object_unref (fenum);
3650 }
3651
3652 gboolean
3653 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
3654     gchar * output_file)
3655 {
3656   gchar *result;
3657   gsize datalength;
3658
3659   GError *err = NULL;
3660   GKeyFile *kf = NULL;
3661   gint res = 0;
3662   const gchar *envvar;
3663   gchar **env_scenariodir = NULL;
3664   gchar *tldir = g_build_filename (g_get_user_data_dir (),
3665       "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
3666       NULL);
3667   GFile *dir = g_file_new_for_path (tldir);
3668
3669   kf = g_key_file_new ();
3670   if (num_scenarios > 0) {
3671     gint i;
3672     GFile *file;
3673
3674     for (i = 0; i < num_scenarios; i++) {
3675       file = g_file_new_for_path (scenarios[i]);
3676       if (!_parse_scenario (file, kf)) {
3677         GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
3678
3679         gst_object_unref (file);
3680         res = 1;
3681       }
3682     }
3683
3684     goto done;
3685   }
3686
3687   envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
3688   if (envvar)
3689     env_scenariodir = g_strsplit (envvar, ":", 0);
3690
3691   _list_scenarios_in_dir (dir, kf);
3692   g_object_unref (dir);
3693   g_free (tldir);
3694
3695   tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3696       "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
3697   dir = g_file_new_for_path (tldir);
3698   _list_scenarios_in_dir (dir, kf);
3699   g_object_unref (dir);
3700   g_free (tldir);
3701
3702   if (env_scenariodir) {
3703     guint i;
3704
3705     for (i = 0; env_scenariodir[i]; i++) {
3706       dir = g_file_new_for_path (env_scenariodir[i]);
3707       _list_scenarios_in_dir (dir, kf);
3708       g_object_unref (dir);
3709     }
3710   }
3711
3712   /* Hack to make it work uninstalled */
3713   dir = g_file_new_for_path ("data/scenarios");
3714   _list_scenarios_in_dir (dir, kf);
3715   g_object_unref (dir);
3716
3717 done:
3718   result = g_key_file_to_data (kf, &datalength, &err);
3719   g_print ("All scenarios available:\n%s", result);
3720
3721   if (output_file && !err)
3722     if (!g_file_set_contents (output_file, result, datalength, &err)) {
3723       GST_WARNING ("Error writing to file '%s'", output_file);
3724     }
3725
3726   if (env_scenariodir)
3727     g_strfreev (env_scenariodir);
3728
3729   if (err) {
3730     GST_WARNING ("Got error '%s' listing scenarios", err->message);
3731     g_clear_error (&err);
3732
3733     res = FALSE;
3734   }
3735
3736   g_key_file_free (kf);
3737
3738   return res;
3739 }
3740
3741 static GstValidateExecuteActionReturn
3742 _check_last_sample_checksum (GstValidateScenario * scenario,
3743     GstValidateAction * action, GstElement * sink)
3744 {
3745   GstSample *sample;
3746   gchar *sum;
3747   GstMapInfo map;
3748   GstBuffer *buffer;
3749   const gchar *target_sum;
3750   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3751
3752   target_sum = gst_structure_get_string (action->structure, "checksum");
3753   g_object_get (sink, "last-sample", &sample, NULL);
3754   if (sample == NULL) {
3755     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3756         "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
3757         " 'last-sample' property is NULL"
3758         ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
3759         sink);
3760     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3761     goto done;
3762   }
3763
3764   buffer = gst_sample_get_buffer (sample);
3765   if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
3766     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3767         "Last sample buffer could not be mapped, action can't run.");
3768     goto done;
3769   }
3770
3771   sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
3772   gst_buffer_unmap (buffer, &map);
3773
3774   if (g_strcmp0 (sum, target_sum)) {
3775     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3776         "Last buffer checksum '%s' is different than the expected one: '%s'",
3777         sum, target_sum);
3778
3779     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3780   }
3781   g_free (sum);
3782
3783 done:
3784   return res;
3785 }
3786
3787 static gboolean
3788 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
3789     const gchar * fname, GstCaps * sinkpad_caps)
3790 {
3791   GstCaps *tmpcaps;
3792   GstPad *sinkpad;
3793   GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
3794   GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
3795
3796   if (!paramspec)
3797     return FALSE;
3798
3799   if (paramspec->value_type != GST_TYPE_SAMPLE)
3800     return FALSE;
3801
3802   if (!name && !fname && !sinkpad_caps)
3803     return TRUE;
3804
3805   if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
3806     return TRUE;
3807
3808   if (fname
3809       && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
3810     return TRUE;
3811
3812   if (!sinkpad_caps)
3813     return FALSE;
3814
3815   sinkpad = gst_element_get_static_pad (sink, "sink");
3816   if (!sinkpad)
3817     return FALSE;
3818
3819   tmpcaps = gst_pad_get_current_caps (sinkpad);
3820   if (tmpcaps) {
3821     gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
3822
3823     GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
3824     gst_caps_unref (tmpcaps);
3825
3826     return res;
3827   } else {
3828     GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
3829   }
3830
3831   return FALSE;
3832 }
3833
3834 static GstValidateExecuteActionReturn
3835 _execute_check_last_sample (GstValidateScenario * scenario,
3836     GstValidateAction * action)
3837 {
3838   GstIterator *it;
3839   GValue data = { 0, };
3840   gboolean done = FALSE;
3841   GstCaps *caps = NULL;
3842   GstElement *sink = NULL, *tmpelement;
3843   const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
3844       *factory_name =
3845       gst_structure_get_string (action->structure, "sink-factory-name"),
3846       *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
3847   DECLARE_AND_GET_PIPELINE (scenario, action);
3848
3849   if (caps_str) {
3850     caps = gst_caps_from_string (caps_str);
3851
3852     g_assert (caps);
3853   }
3854
3855   it = gst_bin_iterate_recurse (GST_BIN (pipeline));
3856   while (!done) {
3857     switch (gst_iterator_next (it, &data)) {
3858       case GST_ITERATOR_OK:
3859         tmpelement = g_value_get_object (&data);
3860         if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
3861                 caps)) {
3862           if (sink) {
3863
3864             GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3865                 "Could not \"check-last-sample\" as several elements were found "
3866                 "from describing string: '%" GST_PTR_FORMAT
3867                 "' (%s and %s match)", action->structure,
3868                 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
3869
3870             gst_object_unref (sink);
3871           }
3872
3873           sink = gst_object_ref (tmpelement);
3874         }
3875         g_value_reset (&data);
3876         break;
3877       case GST_ITERATOR_RESYNC:
3878         gst_iterator_resync (it);
3879         g_clear_object (&sink);
3880         break;
3881       case GST_ITERATOR_ERROR:
3882         /* Fallthrough */
3883       case GST_ITERATOR_DONE:
3884         done = TRUE;
3885         break;
3886     }
3887   }
3888   gst_iterator_free (it);
3889   if (caps)
3890     gst_caps_unref (caps);
3891
3892   if (!sink) {
3893     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3894         "Could not \"check-last-sample\" as no sink was found from description: '%"
3895         GST_PTR_FORMAT "'", action->structure);
3896
3897     goto error;
3898   }
3899
3900   return _check_last_sample_checksum (scenario, action, sink);
3901
3902 error:
3903   g_clear_object (&sink);
3904   return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3905 }
3906
3907 static gboolean
3908 _action_set_done (GstValidateAction * action)
3909 {
3910   JsonBuilder *jbuild;
3911   GstClockTime execution_duration;
3912   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3913
3914   if (scenario == NULL)
3915     return G_SOURCE_REMOVE;
3916
3917   execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
3918
3919   jbuild = json_builder_new ();
3920   json_builder_begin_object (jbuild);
3921   json_builder_set_member_name (jbuild, "type");
3922   json_builder_add_string_value (jbuild, "action-done");
3923   json_builder_set_member_name (jbuild, "action-type");
3924   json_builder_add_string_value (jbuild, action->type);
3925   json_builder_set_member_name (jbuild, "execution-duration");
3926   json_builder_add_double_value (jbuild,
3927       ((gdouble) execution_duration / GST_SECOND));
3928   json_builder_end_object (jbuild);
3929
3930   gst_validate_send (json_builder_get_root (jbuild));
3931   g_object_unref (jbuild);
3932
3933   gst_validate_printf (NULL, "  -> Action %s done (duration: %" GST_TIME_FORMAT
3934       ")\n", action->type, GST_TIME_ARGS (execution_duration));
3935   action->priv->execution_time = GST_CLOCK_TIME_NONE;
3936   action->priv->state = _execute_sub_action_action (action);
3937
3938   if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
3939     GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
3940
3941     execute_next_action (scenario);
3942   }
3943   gst_object_unref (scenario);
3944
3945   return G_SOURCE_REMOVE;
3946 }
3947
3948 /* gst_validate_action_set_done:
3949  * @action: The action that is done executing
3950  *
3951  * Sets @action as "done", meaning that the next action can
3952  * now be executed.
3953  */
3954 void
3955 gst_validate_action_set_done (GstValidateAction * action)
3956 {
3957
3958   if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) {
3959     GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3960     GList *item = NULL;
3961
3962     if (scenario) {
3963       SCENARIO_LOCK (scenario);
3964       item = g_list_find (scenario->priv->interlaced_actions, action);
3965       scenario->priv->interlaced_actions =
3966           g_list_delete_link (scenario->priv->interlaced_actions, item);
3967       SCENARIO_UNLOCK (scenario);
3968       g_object_unref (scenario);
3969     }
3970
3971     if (item)
3972       gst_validate_action_unref (action);
3973   }
3974
3975   g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
3976       (GSourceFunc) _action_set_done,
3977       gst_mini_object_ref (GST_MINI_OBJECT (action)),
3978       (GDestroyNotify) gst_validate_action_unref);
3979 }
3980
3981 /**
3982  * gst_validate_action_get_scenario:
3983  * @action: The action for which to retrieve the scenario
3984  *
3985  * Retrieve the scenario from which @action is executed.
3986  *
3987  * Returns: (transfer full): The scenario from which the action is being executed.
3988  */
3989 GstValidateScenario *
3990 gst_validate_action_get_scenario (GstValidateAction * action)
3991 {
3992   return g_weak_ref_get (&action->priv->scenario);
3993 }
3994
3995 /**
3996  * gst_validate_register_action_type:
3997  * @type_name: The name of the new action type to add
3998  * @implementer_namespace: The namespace of the implementer of the action type.
3999  *                         That should always be the name of the GstPlugin as
4000  *                         retrieved with #gst_plugin_get_name when the action type
4001  *                         is registered inside a plugin.
4002  * @function: (scope notified): The function to be called to execute the action
4003  * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4004  * @description: A description of the new type
4005  * @flags: The #GstValidateActionTypeFlags to set on the new action type
4006  *
4007  * Register a new action type to the action type system. If the action type already
4008  * exists, it will be overridden by the new definition
4009  *
4010  * Returns: (transfer none): The newly created action type or the already registered action type
4011  * if it had a higher rank
4012  */
4013 GstValidateActionType *
4014 gst_validate_register_action_type (const gchar * type_name,
4015     const gchar * implementer_namespace,
4016     GstValidateExecuteAction function,
4017     GstValidateActionParameter * parameters,
4018     const gchar * description, GstValidateActionTypeFlags flags)
4019 {
4020   GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
4021       type_name, GST_RANK_NONE, function, parameters, description,
4022       flags);
4023
4024   g_free (type->implementer_namespace);
4025   type->implementer_namespace = g_strdup (implementer_namespace);
4026
4027   return type;
4028 }
4029
4030 static void
4031 _free_action_types (GList * _action_types)
4032 {
4033   g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
4034 }
4035
4036 /**
4037  * gst_validate_register_action_type_dynamic:
4038  * @plugin: (allow-none): The #GstPlugin that register the action type,
4039  *                        or NULL for a static element.
4040  * @rank: The ranking of that implementation of the action type called
4041  *        @type_name. If an action type has been registered with the same
4042  *        name with a higher rank, the new implementation will not be used,
4043  *        and the already registered action type is returned.
4044  *        If the already registered implementation has a lower rank, the
4045  *        new implementation will be used and returned.
4046  * @type_name: The name of the new action type to add
4047  * @function: (scope notified): The function to be called to execute the action
4048  * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4049  * @description: A description of the new type
4050  * @flags: The #GstValidateActionTypeFlags to be set on the new action type
4051  *
4052  * Returns: (transfer none): The newly created action type or the already registered action type
4053  * if it had a higher rank
4054  */
4055 GstValidateActionType *
4056 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
4057     const gchar * type_name, GstRank rank,
4058     GstValidateExecuteAction function, GstValidateActionParameter * parameters,
4059     const gchar * description, GstValidateActionTypeFlags flags)
4060 {
4061   GstValidateActionType *tmptype;
4062   GstValidateActionType *type = gst_validate_action_type_new ();
4063   gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
4064   gint n_params = is_config ? 0 : 2;
4065
4066   if (parameters) {
4067     for (n_params = 0; parameters[n_params].name != NULL; n_params++);
4068     n_params += 1;
4069   }
4070
4071   if (n_params) {
4072     type->parameters = g_new0 (GstValidateActionParameter, n_params);
4073   }
4074
4075   if (parameters) {
4076     memcpy (type->parameters, parameters,
4077         sizeof (GstValidateActionParameter) * (n_params));
4078   }
4079
4080   type->prepare = gst_validate_action_default_prepare_func;
4081   type->execute = function;
4082   type->name = g_strdup (type_name);
4083   if (plugin)
4084     type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
4085   else
4086     type->implementer_namespace = g_strdup ("none");
4087
4088   type->description = g_strdup (description);
4089   type->flags = flags;
4090   type->rank = rank;
4091
4092   if ((tmptype = _find_action_type (type_name))) {
4093     if (tmptype->rank <= rank) {
4094       action_types = g_list_remove (action_types, tmptype);
4095       type->overriden_type = tmptype;
4096     } else {
4097       gst_mini_object_unref (GST_MINI_OBJECT (type));
4098
4099       type = tmptype;
4100     }
4101   }
4102
4103   if (type != tmptype)
4104     action_types = g_list_append (action_types, type);
4105
4106   if (plugin) {
4107     GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
4108         "GstValidatePluginActionTypes");
4109
4110     plugin_action_types = g_list_prepend (plugin_action_types,
4111         gst_mini_object_ref (GST_MINI_OBJECT (type)));
4112
4113     g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
4114         plugin_action_types, (GDestroyNotify) _free_action_types);
4115   }
4116
4117   return type;
4118 }
4119
4120 GstValidateActionType *
4121 gst_validate_get_action_type (const gchar * type_name)
4122 {
4123   GstValidateActionType *type = _find_action_type (type_name);
4124
4125   if (type)
4126     return
4127         GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
4128
4129   return NULL;
4130 }
4131
4132 static GList *
4133 gst_validate_list_action_types (void)
4134 {
4135   return action_types;
4136 }
4137
4138 /**
4139  * gst_validate_print_action_types:
4140  * @wanted_types: (array length=num_wanted_types): (optional):  List of types to be printed
4141  * @num_wanted_types: Length of @wanted_types
4142  *
4143  * Prints the action types details wanted in @wanted_types
4144  *
4145  * Returns: True if all types could be printed
4146  */
4147 gboolean
4148 gst_validate_print_action_types (const gchar ** wanted_types,
4149     gint num_wanted_types)
4150 {
4151   GList *tmp;
4152   gint nfound = 0;
4153
4154   for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
4155     GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
4156     gboolean print = FALSE;
4157
4158     if (num_wanted_types) {
4159       gint n;
4160
4161       for (n = 0; n < num_wanted_types; n++) {
4162         if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
4163             g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
4164           nfound++;
4165           print = TRUE;
4166
4167           break;
4168         }
4169       }
4170     } else {
4171       print = TRUE;
4172     }
4173
4174     if (print && num_wanted_types) {
4175       gst_validate_printf (atype, "\n");
4176     } else if (print) {
4177       gchar *desc =
4178           g_regex_replace (newline_regex, atype->description, -1, 0, "\n      ",
4179           0,
4180           NULL);
4181
4182       gst_validate_printf (NULL, "\n%s: %s:\n      %s\n",
4183           atype->implementer_namespace, atype->name, desc);
4184       g_free (desc);
4185     }
4186   }
4187
4188   if (num_wanted_types && num_wanted_types > nfound) {
4189     return FALSE;
4190   }
4191
4192   return TRUE;
4193 }
4194
4195 /**
4196  * gst_validate_scenario_get_actions:
4197  * @scenario: The scenario to retrieve remaining actions for
4198  *
4199  * Get remaining actions from @scenario.
4200  *
4201  * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
4202  */
4203 GList *
4204 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
4205 {
4206   GList *ret;
4207   gboolean main_context_acquired;
4208
4209   main_context_acquired = g_main_context_acquire (g_main_context_default ());
4210   g_return_val_if_fail (main_context_acquired, NULL);
4211
4212   ret = g_list_copy_deep (scenario->priv->actions,
4213       (GCopyFunc) gst_mini_object_ref, NULL);
4214
4215   g_main_context_release (g_main_context_default ());
4216
4217   return ret;
4218 }
4219
4220 /**
4221  * gst_validate_scenario_get_target_state:
4222  * @scenario: The scenario to retrieve the current target state for
4223  *
4224  * Get current target state from @scenario.
4225  *
4226  * Returns: Current target state.
4227  */
4228 GstState
4229 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
4230 {
4231   return scenario->priv->target_state;
4232 }
4233
4234 void
4235 init_scenarios (void)
4236 {
4237   GList *tmp;
4238   GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
4239       GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
4240
4241   _gst_validate_action_type = gst_validate_action_get_type ();
4242   _gst_validate_action_type_type = gst_validate_action_type_get_type ();
4243
4244   /*  *INDENT-OFF* */
4245   REGISTER_ACTION_TYPE ("description", NULL,
4246       ((GstValidateActionParameter [])  {
4247       {
4248         .name = "summary",
4249         .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
4250         .mandatory = FALSE,
4251         .types = "string",
4252         .possible_variables = NULL,
4253         .def = "'Nothing'"},
4254       {
4255         .name = "is-config",
4256         .description = "Whether the scenario is a config only scenario",
4257         .mandatory = FALSE,
4258         .types = "boolean",
4259         .possible_variables = NULL,
4260         .def = "false"
4261       },
4262       {
4263         .name = "handles-states",
4264         .description = "Whether the scenario handles pipeline state changes from the beginning\n"
4265         "in that case the application should not set the state of the pipeline to anything\n"
4266         "and the scenario action will be executed from the beginning",
4267         .mandatory = FALSE,
4268         .types = "boolean",
4269         .possible_variables = NULL,
4270         .def = "false"},
4271       {
4272         .name = "seek",
4273         .description = "Whether the scenario executes seek actions or not",
4274         .mandatory = FALSE,
4275         .types = "boolean",
4276         .possible_variables = NULL,
4277         .def = "false"
4278       },
4279       {
4280         .name = "reverse-playback",
4281         .description = "Whether the scenario plays the stream backward",
4282         .mandatory = FALSE,
4283         .types = "boolean",
4284         .possible_variables = NULL,
4285         .def = "false"
4286       },
4287       {
4288         .name = "need-clock-sync",
4289         .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
4290                        "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
4291         .mandatory = FALSE,
4292         .types = "boolean",
4293         .possible_variables = NULL,
4294         .def = "false"
4295       },
4296       {
4297         .name = "min-media-duration",
4298         .description = "Lets the user know the minimum duration of the stream for the scenario\n"
4299                        "to be usable",
4300         .mandatory = FALSE,
4301         .types = "double",
4302         .possible_variables = NULL,
4303         .def = "0.0"
4304       },
4305       {
4306         .name = "min-audio-track",
4307         .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
4308                        "for the scenario to be usable",
4309         .mandatory = FALSE,
4310         .types = "int",
4311         .possible_variables = NULL,
4312         .def = "0"
4313       },
4314       {
4315        .name = "min-video-track",
4316        .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
4317                       "for the scenario to be usable",
4318        .mandatory = FALSE,
4319        .types = "int",
4320        .possible_variables = NULL,
4321        .def = "0"
4322       },
4323       {
4324         .name = "duration",
4325         .description = "Lets the user know the time the scenario needs to be fully executed",
4326         .mandatory = FALSE,
4327         .types = "double, int",
4328         .possible_variables = NULL,
4329         .def = "infinite (GST_CLOCK_TIME_NONE)"
4330       },
4331       {
4332         .name = "pipeline-name",
4333         .description = "The name of the GstPipeline on which the scenario should be executed.\n"
4334           "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
4335         .mandatory = FALSE,
4336         .types = "string",
4337         .possible_variables = NULL,
4338         .def = "NULL"
4339       },
4340       {
4341         .name = "max-latency",
4342         .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
4343           "It can be overriden using core configuration, like for example by defining the "
4344           "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
4345         .mandatory = FALSE,
4346         .types = "double, int",
4347         .possible_variables = NULL,
4348         .def = "infinite (GST_CLOCK_TIME_NONE)"
4349       },
4350       {
4351         .name = "max-dropped",
4352         .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
4353           "It can be overriden using core configuration, like for example by defining the "
4354           "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
4355         .mandatory = FALSE,
4356         .types = "int",
4357         .possible_variables = NULL,
4358         .def = "infinite (-1)"
4359       },
4360       {NULL}
4361       }),
4362       "Allows to describe the scenario in various ways",
4363       GST_VALIDATE_ACTION_TYPE_CONFIG);
4364
4365   REGISTER_ACTION_TYPE ("seek", _execute_seek,
4366       ((GstValidateActionParameter [])  {
4367         {
4368           .name = "start",
4369           .description = "The starting value of the seek",
4370           .mandatory = TRUE,
4371           .types = "double or string",
4372           .possible_variables = "position: The current position in the stream\n"
4373             "duration: The duration of the stream",
4374            NULL
4375         },
4376         {
4377           .name = "flags",
4378           .description = "The GstSeekFlags to use",
4379           .mandatory = TRUE,
4380           .types = "string describing the GstSeekFlags to set",
4381           NULL,
4382         },
4383         {
4384           .name = "rate",
4385           .description = "The rate value of the seek",
4386           .mandatory = FALSE,
4387           .types = "double",
4388           .possible_variables = NULL,
4389           .def = "1.0"
4390         },
4391         {
4392           .name = "start_type",
4393           .description = "The GstSeekType to use for the start of the seek, in:\n"
4394           "  [none, set, end]",
4395           .mandatory = FALSE,
4396           .types = "string",
4397         .possible_variables = NULL,
4398           .def = "set"
4399         },
4400         {
4401           .name = "stop_type",
4402           .description = "The GstSeekType to use for the stop of the seek, in:\n"
4403                          "  [none, set, end]",
4404           .mandatory = FALSE,
4405           .types = "string",
4406           .possible_variables = NULL,
4407           .def = "set"
4408         },
4409         {"stop", "The stop value of the seek", FALSE, "double or ",
4410           "position: The current position in the stream\n"
4411             "duration: The duration of the stream"
4412             "GST_CLOCK_TIME_NONE",
4413         },
4414         {NULL}
4415       }),
4416       "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
4417       "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
4418       "  seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
4419       GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
4420   );
4421
4422   REGISTER_ACTION_TYPE ("pause", _execute_pause,
4423       ((GstValidateActionParameter []) {
4424         {
4425           .name = "duration",
4426           .description = "The duration during which the stream will be paused",
4427           .mandatory = FALSE,
4428           .types = "double",
4429           .possible_variables = NULL,
4430           .def = "0.0",
4431         },
4432         {NULL}
4433       }),
4434       "Sets pipeline to PAUSED. You can add a 'duration'\n"
4435       "parameter so the pipeline goes back to playing after that duration\n"
4436       "(in second)",
4437       GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
4438
4439   REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
4440       "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
4441
4442   REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
4443       "Stops the execution of the scenario. It will post a 'request-state'"
4444       " message on the bus with NULL as a requested state"
4445       " and the application is responsible for stopping itself."
4446       " If you override that action type, make sure to link up.",
4447       GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4448
4449   REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
4450       "Sends an EOS event to the pipeline",
4451       GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4452
4453   REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
4454       ((GstValidateActionParameter []) {
4455         {
4456           .name = "type",
4457           .description = "Selects which track type to change (can be 'audio', 'video',"
4458                           " or 'text').",
4459           .mandatory = FALSE,
4460           .types = "string",
4461           .possible_variables = NULL,
4462           .def = "audio",
4463         },
4464         {
4465           .name = "index",
4466           .description = "Selects which track of this type to use: it can be either a number,\n"
4467                          "which will be the Nth track of the given type, or a number with a '+' or\n"
4468                          "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
4469                          "'-1' means 'previous track')",
4470           .mandatory = FALSE,
4471           .types = "string: to switch track relatively\n"
4472                    "int: To use the actual index to use",
4473           .possible_variables = NULL,
4474           .def = "+1",
4475         },
4476         {NULL}
4477       }),
4478       "The 'switch-track' command can be used to switch tracks."
4479       , GST_VALIDATE_ACTION_TYPE_NONE);
4480
4481   REGISTER_ACTION_TYPE ("wait", _execute_wait,
4482       ((GstValidateActionParameter []) {
4483         {
4484           .name = "duration",
4485           .description = "the duration while no other action will be executed",
4486           .mandatory = FALSE,
4487           NULL},
4488         {
4489           .name = "target-element-name",
4490           .description = "The name of the GstElement to wait @signal-name on.",
4491           .mandatory = FALSE,
4492           .types = "string"
4493         },
4494         {
4495           .name = "signal-name",
4496           .description = "The name of the signal to wait for on @target-element-name",
4497           .mandatory = FALSE,
4498           .types = "string",
4499           NULL
4500         },
4501         {
4502           .name = "message-type",
4503           .description = "The name of the message type to wait for (on @target-element-name"
4504             " if specified)",
4505           .mandatory = FALSE,
4506           .types = "string",
4507           NULL
4508         },
4509         {NULL}
4510       }),
4511       "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
4512       GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
4513
4514   REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
4515       "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
4516       "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
4517       "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
4518       GST_VALIDATE_ACTION_TYPE_NONE);
4519
4520   REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank,
4521       ((GstValidateActionParameter []) {
4522         {
4523           .name = "name",
4524           .description = "The name of a GstFeature or GstPlugin",
4525           .mandatory = TRUE,
4526           .types = "string",
4527           NULL},
4528         {
4529           .name = "rank",
4530           .description = "The GstRank to set on @name",
4531           .mandatory = TRUE,
4532           .types = "string, int",
4533           NULL},
4534         {NULL}
4535       }),
4536       "Changes the ranking of a particular plugin feature(s)",
4537       GST_VALIDATE_ACTION_TYPE_CONFIG);
4538
4539   REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank,
4540       ((GstValidateActionParameter []) {
4541         {
4542           .name = "feature-name",
4543           .description = "The name of a GstFeature",
4544           .mandatory = TRUE,
4545           .types = "string",
4546           NULL},
4547         {
4548           .name = "rank",
4549           .description = "The GstRank to set on @feature-name",
4550           .mandatory = TRUE,
4551           .types = "string, int",
4552           NULL},
4553         {NULL}
4554       }),
4555       "Changes the ranking of a particular plugin feature",
4556       GST_VALIDATE_ACTION_TYPE_CONFIG);
4557
4558   REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
4559       ((GstValidateActionParameter []) {
4560         {
4561           .name = "state",
4562           .description = "A GstState as a string, should be in: \n"
4563                          "    * ['null', 'ready', 'paused', 'playing']",
4564           .mandatory = TRUE,
4565           .types = "string",
4566         },
4567         {NULL}
4568       }),
4569       "Changes the state of the pipeline to any GstState",
4570       GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
4571
4572   REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
4573       ((GstValidateActionParameter []) {
4574         {NULL}
4575       }),
4576       "Define vars to be used in other actions.\n"
4577       "For example you can define vars for buffer checksum"
4578       " to be used in the \"check-last-sample\" action type as follow:\n\n"
4579       "```\n"
4580       " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
4581       " check-last-sample, checksum=frame1\n"
4582       "```\n",
4583       GST_VALIDATE_ACTION_TYPE_NONE);
4584
4585   REGISTER_ACTION_TYPE ("set-property", _execute_set_property,
4586       ((GstValidateActionParameter []) {
4587         {
4588           .name = "target-element-name",
4589           .description = "The name of the GstElement to set a property on",
4590           .mandatory = FALSE,
4591           .types = "string",
4592           NULL
4593         },
4594         {
4595           .name = "target-element-factory-name",
4596           .description = "The name factory for which to set a property on built elements",
4597           .mandatory = FALSE,
4598           .types = "string",
4599           NULL
4600         },
4601         {
4602           .name = "target-element-klass",
4603           .description = "The klass of the GstElements to set a property on",
4604           .mandatory = FALSE,
4605           .types = "string",
4606           NULL
4607         },
4608         {
4609           .name = "property-name",
4610           .description = "The name of the property to set on @target-element-name",
4611           .mandatory = TRUE,
4612           .types = "string",
4613           NULL
4614         },
4615         {
4616           .name = "property-value",
4617           .description = "The value of @property-name to be set on the element",
4618           .mandatory = TRUE,
4619           .types = "The same type of @property-name",
4620           NULL
4621         },
4622         {NULL}
4623       }),
4624       "Sets a property of an element or klass of elements in the pipeline.\n"
4625       "Besides property-name and value, either 'target-element-name' or\n"
4626       "'target-element-klass' needs to be defined",
4627       GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
4628           GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
4629           GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
4630
4631   REGISTER_ACTION_TYPE ("set-debug-threshold",
4632       _execute_set_debug_threshold,
4633       ((GstValidateActionParameter [])
4634         {
4635           {
4636             .name = "debug-threshold",
4637             .description = "String defining debug threshold\n"
4638                            "See gst_debug_set_threshold_from_string",
4639             .mandatory = TRUE,
4640             .types = "string"},
4641           {NULL}
4642         }),
4643       "Sets the debug level to be used, same format as\n"
4644       "setting the GST_DEBUG env variable",
4645       GST_VALIDATE_ACTION_TYPE_NONE);
4646
4647   REGISTER_ACTION_TYPE ("include",
4648       NULL, /* This is handled directly when loading a scenario */
4649       ((GstValidateActionParameter [])
4650         {
4651           {
4652             .name = "location",
4653             .description = "The location of the sub scenario to include.",
4654             .mandatory = TRUE,
4655             .types = "string"},
4656           {NULL}
4657         }),
4658       "Include a sub scenario file.",
4659       GST_VALIDATE_ACTION_TYPE_NONE);
4660
4661   REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
4662       ((GstValidateActionParameter [])
4663       {
4664         {
4665           .name = "target-element-name",
4666           .description = "The name of the GstElement to emit a signal on",
4667           .mandatory = TRUE,
4668           .types = "string"
4669         },
4670         {
4671           .name = "signal-name",
4672           .description = "The name of the signal to emit on @target-element-name",
4673           .mandatory = TRUE,
4674           .types = "string",
4675           NULL
4676         },
4677         {NULL}
4678       }),
4679       "Emits a signal to an element in the pipeline",
4680       GST_VALIDATE_ACTION_TYPE_NONE);
4681
4682   REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
4683       ((GstValidateActionParameter [])
4684       {
4685         {
4686           .name = "plugin-name",
4687           .description = "The name of the GstPlugin to disable",
4688           .mandatory = TRUE,
4689           .types = "string"
4690         },
4691         {
4692           .name = "as-config",
4693           .description = "Execute action as a config action (meaning when loading the scenario)",
4694           .mandatory = FALSE,
4695           .types = "boolean",
4696           .def = "false"
4697         },
4698         {NULL}
4699       }),
4700       "Disables a GstPlugin",
4701       GST_VALIDATE_ACTION_TYPE_NONE);
4702
4703   REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
4704       ((GstValidateActionParameter []) {
4705         {
4706           .name = "sink-name",
4707           .description = "The name of the sink element to check sample on.",
4708           .mandatory = FALSE,
4709           .types = "string",
4710           NULL
4711         },
4712         {
4713           .name = "sink-factory-name",
4714           .description = "The name of the factory of the sink element to check sample on.",
4715           .mandatory = FALSE,
4716           .types = "string",
4717           NULL
4718         },
4719         {
4720           .name = "sinkpad-caps",
4721           .description = "The caps (as string) of the sink to check.",
4722           .mandatory = FALSE,
4723           .types = "string",
4724           NULL
4725         },
4726         {
4727           .name = "checksum",
4728           .description = "The reference checksum of the buffer.",
4729           .mandatory = TRUE,
4730           .types = "string",
4731           NULL
4732         },
4733         {NULL}
4734       }),
4735       "Checks the last-sample checksum on declared Sink element."
4736       " This allows checking the checksum of a buffer after a 'seek' or after a GESTimeline 'commit'"
4737       " for example",
4738       GST_VALIDATE_ACTION_TYPE_INTERLACED);
4739
4740   /*  *INDENT-ON* */
4741
4742   for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
4743     const gchar *action_typename;
4744     GstStructure *plug_conf = (GstStructure *) tmp->data;
4745
4746     if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
4747       GstValidateAction *action;
4748       GstValidateActionType *atype = _find_action_type (action_typename);
4749
4750       if (!atype) {
4751         g_error ("[CONFIG ERROR] Action type %s not found", action_typename);
4752
4753         continue;
4754       }
4755
4756
4757       if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
4758         GST_INFO ("Action type %s from configuration files"
4759             " is handled.", action_typename);
4760         continue;
4761       }
4762
4763       if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
4764           !(_action_type_has_parameter (atype, "as-config"))) {
4765         g_error ("[CONFIG ERROR] Action '%s' is not a config action",
4766             action_typename);
4767
4768         continue;
4769       }
4770
4771       gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
4772       gst_structure_set_name (plug_conf, action_typename);
4773
4774       action = gst_validate_action_new (NULL, atype);
4775       _fill_action (NULL, action, plug_conf, FALSE);
4776     }
4777   }
4778 }
4779
4780 void
4781 gst_validate_scenario_deinit (void)
4782 {
4783   _free_action_types (action_types);
4784   action_types = NULL;
4785 }