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