validate-scenario: fix g-i warning in annotation
[platform/upstream/gstreamer.git] / subprojects / gst-devtools / validate / gst / validate / gst-validate-scenario.c
1 /* GStreamer
2
3  *
4  * Copyright (C) 2013 Collabora Ltd.
5  *  Author: Thibault Saunier <thibault.saunier@collabora.com>
6  * Copyright (C) 2018-2022 Igalia S.L
7  *  Author: Thibault Saunier <tsaunier@igalia.com>
8
9  *
10  * gst-validate-scenario.c - Validate Scenario class
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27 /**
28  * SECTION:gst-validate-scenario
29  * @title: GstValidateScenario
30  * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
31  *
32  * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
33  * It is basically an ordered list of #GstValidateAction that will be executed during the
34  * execution of the pipeline.
35  *
36  * Possible configurations (see [GST_VALIDATE_CONFIG](gst-validate-environment-variables.md)):
37  *  * scenario-action-execution-interval: Sets the interval in
38  *    milliseconds (1/1000ths of a second), between which actions
39  *    will be executed, setting it to 0 means "execute in idle".
40  *    The default value is 10ms.
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <gst/gst.h>
48 #include <gio/gio.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <math.h>
52
53 #include <gst/check/gsttestclock.h>
54 #include "gst-validate-internal.h"
55 #include "gst-validate-scenario.h"
56 #include "gst-validate-reporter.h"
57 #include "gst-validate-report.h"
58 #include "gst-validate-utils.h"
59 #include "gst-validate-internal.h"
60 #include "validate.h"
61 #include <gst/controller/controller.h>
62 #include <gst/validate/gst-validate-override.h>
63 #include <gst/validate/gst-validate-override-registry.h>
64 #include <gst/validate/gst-validate-pipeline-monitor.h>
65
66 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
67
68 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND)        /* tolerance seek interval
69                                                            TODO make it overridable  */
70
71 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
72 #undef GST_CAT_DEFAULT
73 #define GST_CAT_DEFAULT gst_validate_scenario_debug
74
75 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
76   type = gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
77 } G_STMT_END
78
79 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
80
81 #define SCENARIO_LOCK(scenario) G_STMT_START {                          \
82     GST_LOG_OBJECT (scenario, "About to lock %p", &scenario->priv->lock); \
83     g_mutex_lock(&scenario->priv->lock);                                \
84     GST_LOG_OBJECT (scenario, "Acquired lock %p", &scenario->priv->lock); \
85   } G_STMT_END
86
87 #define SCENARIO_UNLOCK(scenario) G_STMT_START {                        \
88     GST_LOG_OBJECT (scenario, "About to unlock %p", &scenario->priv->lock); \
89     g_mutex_unlock(&scenario->priv->lock);                              \
90     GST_LOG_OBJECT (scenario, "unlocked %p", &scenario->priv->lock);    \
91   } G_STMT_END
92
93 #define DECLARE_AND_GET_PIPELINE(s,a) \
94   GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
95   if (pipeline == NULL) { \
96     GST_VALIDATE_REPORT_ACTION (s, a, SCENARIO_ACTION_EXECUTION_ERROR, \
97             "Can't execute a '%s' action after the pipeline " \
98             "has been destroyed.", a->type); \
99     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
100   }
101
102 #ifdef G_HAVE_ISO_VARARGS
103 #define REPORT_UNLESS(condition, errpoint, ...)                                \
104   G_STMT_START {                                                               \
105     if (!(condition)) {                                                        \
106       res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;                        \
107       gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action,      \
108                                  SCENARIO_ACTION_EXECUTION_ERROR,              \
109                                  __VA_ARGS__);                                 \
110       goto errpoint;                                                           \
111     }                                                                          \
112   }                                                                            \
113   G_STMT_END
114 #elif defined(G_HAVE_GNUC_VARARGS)
115 #define REPORT_UNLESS(condition, errpoint, args...)                            \
116   G_STMT_START {                                                               \
117     if (!(condition)) {                                                        \
118       res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;                        \
119       gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action,      \
120                                  SCENARIO_ACTION_EXECUTION_ERROR, ##args);     \
121       goto errpoint;                                                           \
122     }                                                                          \
123   }                                                                            \
124   G_STMT_END
125 #endif
126
127 enum
128 {
129   PROP_0,
130   PROP_RUNNER,
131   PROP_HANDLES_STATE,
132   PROP_EXECUTE_ON_IDLE,
133   PROP_LAST
134 };
135
136 enum
137 {
138   DONE,
139   ACTION_DONE,
140   LAST_SIGNAL
141 };
142
143 static guint scenario_signals[LAST_SIGNAL] = { 0 };
144
145 static GList *action_types = NULL;
146 static void gst_validate_scenario_dispose (GObject * object);
147 static void gst_validate_scenario_finalize (GObject * object);
148 static GstValidateActionType *_find_action_type (const gchar * type_name);
149 static GstValidateExecuteActionReturn
150 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
151     GstStructure * structure, gboolean add_to_lists);
152 static gboolean _action_set_done (GstValidateAction * action);
153 static GList *_find_elements_defined_in_action (GstValidateScenario * scenario,
154     GstValidateAction * action);
155 static GstValidateAction *gst_validate_create_subaction (GstValidateScenario *
156     scenario, GstStructure * lvariables, GstValidateAction * action,
157     GstStructure * nstruct, gint it, gint max);
158
159 /* GstValidateSinkInformation tracks information for all sinks in the pipeline */
160 typedef struct
161 {
162   GstElement *sink;             /* The sink element tracked */
163   guint32 segment_seqnum;       /* The latest segment seqnum. GST_SEQNUM_INVALID if none */
164   GstSegment segment;           /* The latest segment */
165 } GstValidateSinkInformation;
166
167 /* GstValidateSeekInformation tracks:
168  * * The values used in the seek
169  * * The seqnum used in the seek event
170  * * The validate action to which it relates
171  */
172 typedef struct
173 {
174   guint32 seqnum;               /* seqnum of the seek event */
175
176   /* Seek values */
177   gdouble rate;
178   GstFormat format;
179   GstSeekFlags flags;
180   GstSeekType start_type, stop_type;
181   gint64 start, stop;
182
183   /* The action corresponding to this seek */
184   GstValidateAction *action;
185 } GstValidateSeekInformation;
186
187 /* GstValidateScenario is not really thread safe and
188  * everything should be done from the thread GstValidate
189  * was inited from, unless stated otherwise.
190  */
191 struct _GstValidateScenarioPrivate
192 {
193   GstBus *bus;
194   GstValidateRunner *runner;
195   gboolean execute_on_idle;
196
197   GMutex lock;
198
199   GList *actions;
200   GList *non_blocking_running_actions;  /* MT safe. Protected with SCENARIO_LOCK */
201   GList *on_addition_actions;   /* MT safe. Protected with SCENARIO_LOCK */
202
203   gboolean needs_playback_parsing;
204
205   GList *sinks;                 /* List of GstValidateSinkInformation */
206   GList *seeks;                 /* List of GstValidateSeekInformation */
207
208   /* Seek currently applied (set when all sinks received segment with
209    * an identical seqnum and there is a matching pending seek).
210    * do not free, should always be present in the seek list above */
211   GstValidateSeekInformation *current_seek;
212   /* Current unified seqnum. Set when all sinks received segment with
213    * an identical seqnum, even if there wasn't a matching pending seek
214    */
215   guint32 current_seqnum;
216
217   /*  List of action that need parsing when reaching ASYNC_DONE
218    *  most probably to be able to query duration */
219
220   /* seek_flags :
221    *  * Only set for seek actions, and only if seek succeeded
222    *  * Only Used in _check_position()
223    * FIXME : Just use the seek information */
224   GstSeekFlags seek_flags;
225   GstFormat seek_format;
226
227   /* segment_start/segment_stop :
228    *  * Set : from seek values
229    *  * Read : In _check_position()
230    * FIXME : Just use the current seek information */
231   GstClockTime segment_start;
232   GstClockTime segment_stop;
233
234   /* Always initialized to a default value
235    * FIXME : Is it still needed with the new seeking validation system ? */
236   GstClockTime seek_pos_tol;
237
238   /* If we seeked in paused the position should be exactly what
239    * the seek value was (if accurate) */
240   gboolean seeked_in_pause;
241
242   guint num_actions;
243
244   gboolean handles_state;
245
246   guint execute_actions_source_id;      /* MT safe. Protect with SCENARIO_LOCK */
247   guint wait_id;
248   guint signal_handler_id;      /* MT safe. Protect with SCENARIO_LOCK */
249   guint action_execution_interval;
250
251   /* Name of message the wait action is waiting for */
252   GstValidateAction *wait_message_action;
253
254   gboolean buffering;
255
256   gboolean got_eos;
257   gboolean changing_state;
258   gboolean needs_async_done;
259   gboolean ignore_eos;
260   gboolean allow_errors;
261   GstState target_state;
262
263   GList *overrides;
264
265   gchar *pipeline_name;
266   GstClockTime max_latency;
267   gint dropped;
268   gint max_dropped;
269
270   /* 'switch-track action' currently waiting for
271    * GST_MESSAGE_STREAMS_SELECTED to be completed. */
272   GstValidateAction *pending_switch_track;
273
274   GstStructure *vars;
275
276   GWeakRef ref_pipeline;
277
278   GstTestClock *clock;
279   guint segments_needed;
280
281   GMainContext *context;
282 };
283
284 typedef struct KeyFileGroupName
285 {
286   GKeyFile *kf;
287   gchar *group_name;
288 } KeyFileGroupName;
289
290 #define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1
291
292 static void
293 gst_validate_sink_information_free (GstValidateSinkInformation * info)
294 {
295   gst_object_unref (info->sink);
296   g_free (info);
297 }
298
299 static void
300 gst_validate_seek_information_free (GstValidateSeekInformation * info)
301 {
302   gst_validate_action_unref (info->action);
303   g_free (info);
304 }
305
306 static GstValidateInterceptionReturn
307 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
308     GstValidateReport * report)
309 {
310   GList *tmp;
311
312   for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
313       tmp = tmp->next) {
314     GstValidateOverride *override = (GstValidateOverride *) tmp->data;
315     report->level =
316         gst_validate_override_get_severity (override,
317         gst_validate_issue_get_id (report->issue), report->level);
318   }
319
320   return GST_VALIDATE_REPORTER_REPORT;
321 }
322
323 /**
324  * gst_validate_scenario_get_pipeline:
325  * @scenario: The scenario to retrieve a pipeline from
326  *
327  * Returns: (transfer full) (nullable): The #GstPipeline the scenario is running
328  * against
329  */
330 GstElement *
331 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
332 {
333   return g_weak_ref_get (&scenario->priv->ref_pipeline);
334 }
335
336 static GstPipeline *
337 _get_pipeline (GstValidateReporter * reporter)
338 {
339   return
340       GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
341       (GST_VALIDATE_SCENARIO (reporter)));
342 }
343
344 static void
345 _reporter_iface_init (GstValidateReporterInterface * iface)
346 {
347   iface->intercept_report = gst_validate_scenario_intercept_report;
348   iface->get_pipeline = _get_pipeline;
349 }
350
351 static GQuark chain_qdata;
352 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
353     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
354     G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init)
355     chain_qdata = g_quark_from_static_string ("__validate_scenario_chain_data")
356     );
357
358 /* GstValidateAction implementation */
359 static GType _gst_validate_action_type = 0;
360
361 struct _GstValidateActionPrivate
362 {
363   GstStructure *main_structure;
364   GstValidateExecuteActionReturn state; /* Actually ActionState */
365   gboolean printed;
366   gboolean executing_last_subaction;
367   gboolean subaction_level;
368   gboolean optional;
369
370   GstClockTime execution_time;
371   GstClockTime execution_duration;
372   GstClockTime timeout;
373
374   GWeakRef scenario;
375   gboolean needs_playback_parsing;
376   gboolean pending_set_done;
377
378   GMainContext *context;
379
380   GValue it_value;
381 };
382
383 static JsonNode *
384 gst_validate_action_serialize (GstValidateAction * action)
385 {
386   JsonNode *node = json_node_alloc ();
387   JsonObject *jreport = json_object_new ();
388   gchar *action_args = gst_structure_to_string (action->structure);
389
390   json_object_set_string_member (jreport, "type", "action");
391   json_object_set_string_member (jreport, "action-type", action->type);
392   json_object_set_int_member (jreport, "playback-time",
393       (gint64) action->playback_time);
394   json_object_set_string_member (jreport, "args", action_args);
395   g_free (action_args);
396
397   node = json_node_init_object (node, jreport);
398   json_object_unref (jreport);
399
400   return node;
401 }
402
403 GType
404 gst_validate_action_get_type (void)
405 {
406   if (_gst_validate_action_type == 0) {
407     _gst_validate_action_type =
408         g_boxed_type_register_static (g_intern_static_string
409         ("GstValidateAction"), (GBoxedCopyFunc) gst_validate_action_ref,
410         (GBoxedFreeFunc) gst_validate_action_unref);
411
412     json_boxed_register_serialize_func (_gst_validate_action_type,
413         JSON_NODE_OBJECT,
414         (JsonBoxedSerializeFunc) gst_validate_action_serialize);
415   }
416
417   return _gst_validate_action_type;
418 }
419
420 static gboolean execute_next_action (GstValidateScenario * scenario);
421 static gboolean
422 gst_validate_scenario_load (GstValidateScenario * scenario,
423     const gchar * scenario_name);
424
425 static GstValidateAction *
426 _action_copy (GstValidateAction * act)
427 {
428   GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
429   GstValidateAction *copy = gst_validate_action_new (scenario,
430       _find_action_type (act->type), NULL, FALSE);
431
432   gst_object_unref (scenario);
433
434   if (act->structure) {
435     copy->structure = gst_structure_copy (act->structure);
436     copy->type = gst_structure_get_name (copy->structure);
437     if (!(act->name = gst_structure_get_string (copy->structure, "name")))
438       act->name = "";
439   }
440
441   if (act->priv->main_structure)
442     copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
443
444   copy->action_number = act->action_number;
445   copy->playback_time = act->playback_time;
446   copy->priv->timeout = act->priv->timeout;
447   GST_VALIDATE_ACTION_LINENO (copy) = GST_VALIDATE_ACTION_LINENO (act);
448   GST_VALIDATE_ACTION_FILENAME (copy) =
449       g_strdup (GST_VALIDATE_ACTION_FILENAME (act));
450   GST_VALIDATE_ACTION_DEBUG (copy) = g_strdup (GST_VALIDATE_ACTION_DEBUG (act));
451   GST_VALIDATE_ACTION_N_REPEATS (copy) = GST_VALIDATE_ACTION_N_REPEATS (act);
452   GST_VALIDATE_ACTION_RANGE_NAME (copy) = GST_VALIDATE_ACTION_RANGE_NAME (act);
453
454   if (act->priv->it_value.g_type != 0) {
455     g_value_init (&copy->priv->it_value, G_VALUE_TYPE (&act->priv->it_value));
456     g_value_copy (&act->priv->it_value, &copy->priv->it_value);
457   }
458
459   return copy;
460 }
461
462 const gchar *
463 gst_validate_action_return_get_name (GstValidateActionReturn r)
464 {
465   switch (r) {
466     case GST_VALIDATE_EXECUTE_ACTION_ERROR:
467       return "ERROR";
468     case GST_VALIDATE_EXECUTE_ACTION_OK:
469       return "OK";
470     case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
471       return "ASYNC";
472     case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
473       return "NON-BLOCKING";
474     case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
475       return "ERROR(reported)";
476     case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
477       return "IN_PROGRESS";
478     case GST_VALIDATE_EXECUTE_ACTION_NONE:
479       return "NONE";
480     case GST_VALIDATE_EXECUTE_ACTION_DONE:
481       return "DONE";
482   }
483   g_assert_not_reached ();
484   return "???";
485 }
486
487 static void
488 _action_free (GstValidateAction * action)
489 {
490   if (action->structure)
491     gst_structure_free (action->structure);
492
493   if (action->priv->main_structure)
494     gst_structure_free (action->priv->main_structure);
495
496   if (action->priv->it_value.g_type != 0)
497     g_value_reset (&action->priv->it_value);
498   g_weak_ref_clear (&action->priv->scenario);
499   g_free (GST_VALIDATE_ACTION_FILENAME (action));
500   g_free (GST_VALIDATE_ACTION_DEBUG (action));
501
502   g_slice_free (GstValidateActionPrivate, action->priv);
503   g_slice_free (GstValidateAction, action);
504 }
505
506 static void
507 gst_validate_action_init (GstValidateAction * action)
508 {
509   gst_mini_object_init (((GstMiniObject *) action), 0,
510       _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
511       (GstMiniObjectFreeFunction) _action_free);
512
513   action->priv = g_slice_new0 (GstValidateActionPrivate);
514
515   g_weak_ref_init (&action->priv->scenario, NULL);
516 }
517
518 GstValidateAction *
519 gst_validate_action_ref (GstValidateAction * action)
520 {
521   return (GstValidateAction *) gst_mini_object_ref (GST_MINI_OBJECT (action));
522 }
523
524 void
525 gst_validate_action_unref (GstValidateAction * action)
526 {
527   gst_mini_object_unref (GST_MINI_OBJECT (action));
528 }
529
530 /**
531  * gst_validate_action_new:
532  * @scenario: (allow-none): The scenario executing the action
533  * @action_type: The action type
534  * @structure: The structure containing the action arguments
535  * @add_to_lists: Weather the action should be added to the scenario action list
536  *
537  * Returns: A newly created #GstValidateAction
538  */
539 GstValidateAction *
540 gst_validate_action_new (GstValidateScenario * scenario,
541     GstValidateActionType * action_type, GstStructure * structure,
542     gboolean add_to_lists)
543 {
544   GstValidateAction *action = g_slice_new0 (GstValidateAction);
545
546   g_assert (action_type);
547
548   gst_validate_action_init (action);
549   action->playback_time = GST_CLOCK_TIME_NONE;
550   action->priv->timeout = GST_CLOCK_TIME_NONE;
551   action->priv->state = GST_VALIDATE_EXECUTE_ACTION_NONE;
552   action->type = action_type->name;
553   action->repeat = -1;
554
555   g_weak_ref_set (&action->priv->scenario, scenario);
556   if (structure) {
557     gchar *filename = NULL;
558     gst_structure_get (structure,
559         "__lineno__", G_TYPE_INT, &GST_VALIDATE_ACTION_LINENO (action),
560         "__filename__", G_TYPE_STRING, &filename,
561         "__debug__", G_TYPE_STRING, &GST_VALIDATE_ACTION_DEBUG (action), NULL);
562     if (filename) {
563       GST_VALIDATE_ACTION_FILENAME (action) =
564           g_filename_display_basename (filename);
565       g_free (filename);
566     }
567     gst_structure_remove_fields (structure, "__lineno__", "__filename__",
568         "__debug__", NULL);
569     action->priv->state =
570         _fill_action (scenario, action, structure, add_to_lists);
571   }
572
573   return action;
574 }
575
576 gboolean
577 _action_check_and_set_printed (GstValidateAction * action)
578 {
579   if (action->priv->printed == FALSE) {
580     gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
581             (action), action));
582
583     action->priv->printed = TRUE;
584
585     return FALSE;
586   }
587
588   return TRUE;
589 }
590
591 gint
592 gst_validate_action_get_level (GstValidateAction * action)
593 {
594   return action->priv->subaction_level;
595 }
596
597 /* GstValidateActionType implementation */
598 GType _gst_validate_action_type_type;
599 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
600 static GstValidateActionType *gst_validate_action_type_new (void);
601
602 struct _GstValidateActionTypePrivate
603 {
604   gint n_calls;
605 };
606
607 static void
608 _action_type_free (GstValidateActionType * type)
609 {
610   g_free (type->parameters);
611   g_free (type->description);
612   g_free (type->name);
613   g_free (type->implementer_namespace);
614   g_free (type->priv);
615
616   if (type->overriden_type)
617     gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
618
619   g_slice_free (GstValidateActionType, type);
620 }
621
622 static void
623 gst_validate_action_type_init (GstValidateActionType * type)
624 {
625   type->priv = g_new0 (GstValidateActionTypePrivate, 1);
626
627   gst_mini_object_init ((GstMiniObject *) type, 0,
628       _gst_validate_action_type_type, NULL, NULL,
629       (GstMiniObjectFreeFunction) _action_type_free);
630 }
631
632 GstValidateActionType *
633 gst_validate_action_type_new (void)
634 {
635   GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
636
637   gst_validate_action_type_init (type);
638
639   return type;
640 }
641
642 static GstValidateActionType *
643 _find_action_type (const gchar * type_name)
644 {
645   GList *tmp;
646
647   for (tmp = action_types; tmp; tmp = tmp->next) {
648     GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
649     if (g_strcmp0 (atype->name, type_name) == 0)
650       return atype;
651   }
652
653   return NULL;
654 }
655
656 static void
657 _update_well_known_vars (GstValidateScenario * scenario)
658 {
659   gint64 duration, position;
660   gdouble dduration, dposition;
661   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
662
663   gst_structure_remove_fields (scenario->priv->vars, "position", "duration",
664       NULL);
665
666   if (!pipeline)
667     return;
668
669   if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration) ||
670       !GST_CLOCK_TIME_IS_VALID (duration)) {
671     GstValidateMonitor *monitor =
672         (GstValidateMonitor *) (g_object_get_data ((GObject *)
673             pipeline, "validate-monitor"));
674     GST_INFO_OBJECT (scenario,
675         "Could not query duration. Trying to get duration from media-info");
676     if (monitor && monitor->media_descriptor)
677       duration =
678           gst_validate_media_descriptor_get_duration
679           (monitor->media_descriptor);
680   }
681
682   if (!GST_CLOCK_TIME_IS_VALID (duration))
683     dduration = G_MAXDOUBLE;
684   else
685     dduration = ((double) duration / GST_SECOND);
686
687   gst_structure_set (scenario->priv->vars, "duration", G_TYPE_DOUBLE, dduration,
688       NULL);
689   if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
690
691     if (!GST_CLOCK_TIME_IS_VALID (position))
692       dposition = G_MAXDOUBLE;
693     else
694       dposition = ((double) position / GST_SECOND);
695
696     gst_structure_set (scenario->priv->vars, "position", G_TYPE_DOUBLE,
697         dposition, NULL);
698   } else {
699     GST_INFO_OBJECT (scenario, "Could not query position");
700   }
701 }
702
703 static GstElement *_get_target_element (GstValidateScenario * scenario,
704     GstValidateAction * action);
705
706 static GstObject *
707 _get_target_object_property (GstValidateScenario * scenario,
708     GstValidateAction * action, const gchar * property_path,
709     GParamSpec ** pspec)
710 {
711   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
712   gchar **elem_pad_name = NULL;
713   gchar **object_prop_name = NULL;
714   const gchar *elemname;
715   const gchar *padname = NULL;
716   GstObject *target = NULL;
717   gint i;
718
719   elem_pad_name = g_strsplit (property_path, ".", 2);
720   object_prop_name =
721       g_strsplit (elem_pad_name[1] ? elem_pad_name[1] : elem_pad_name[0], "::",
722       -1);
723   REPORT_UNLESS (object_prop_name[1], err,
724       "Property specification %s is missing a `::propename` part",
725       property_path);
726
727   if (elem_pad_name[1]) {
728     elemname = elem_pad_name[0];
729     padname = object_prop_name[0];
730   } else {
731     elemname = object_prop_name[0];
732   }
733
734   gst_structure_set (action->structure, "target-element-name", G_TYPE_STRING,
735       elemname, NULL);
736
737   target = (GstObject *) _get_target_element (scenario, action);
738   gst_structure_remove_field (action->structure, "target-element-name");
739   REPORT_UNLESS (target, err, "Target element with given name (%s) not found",
740       elemname);
741
742   if (padname) {
743     gboolean done = FALSE;
744     GstIterator *it = gst_element_iterate_pads (GST_ELEMENT (target));
745     GValue v = G_VALUE_INIT;
746
747     gst_clear_object (&target);
748     while (!done) {
749       switch (gst_iterator_next (it, &v)) {
750         case GST_ITERATOR_OK:{
751           GstPad *pad = g_value_get_object (&v);
752           gchar *name = gst_object_get_name (GST_OBJECT (pad));
753
754           if (!g_strcmp0 (name, padname)) {
755             done = TRUE;
756             gst_clear_object (&target);
757
758             target = gst_object_ref (pad);
759           }
760           g_free (name);
761           g_value_reset (&v);
762           break;
763         }
764         case GST_ITERATOR_RESYNC:
765           gst_iterator_resync (it);
766           break;
767         case GST_ITERATOR_ERROR:
768         case GST_ITERATOR_DONE:
769           done = TRUE;
770       }
771     }
772
773     gst_iterator_free (it);
774   }
775   REPORT_UNLESS (target, err, "Could not find pad: %s::%s", elemname, padname);
776
777   for (i = 1;;) {
778     const gchar *propname = object_prop_name[i];
779
780     *pspec =
781         g_object_class_find_property (G_OBJECT_GET_CLASS (target), propname);
782
783     REPORT_UNLESS (*pspec, err,
784         "Object %" GST_PTR_FORMAT " doesn't have a property call '%s'", target,
785         propname);
786
787     if (!object_prop_name[++i])
788       break;
789
790     REPORT_UNLESS (g_type_is_a ((*pspec)->owner_type, G_TYPE_OBJECT), err,
791         "Property: %" GST_PTR_FORMAT "::%s not a GObject, can't use it.",
792         target, propname);
793
794     g_object_get (target, propname, &target, NULL);
795     REPORT_UNLESS (target, err,
796         "Property: %" GST_PTR_FORMAT "::%s is NULL can't get %s.",
797         target, propname, object_prop_name[i + 1]);
798   }
799
800   REPORT_UNLESS (res == GST_VALIDATE_EXECUTE_ACTION_OK, err, "Something fishy");
801
802 done:
803   g_strfreev (elem_pad_name);
804   g_strfreev (object_prop_name);
805   return target;
806
807 err:
808   gst_clear_object (&target);
809   goto done;
810 }
811
812 static gboolean
813 _set_variable_func (const gchar * name, double *value, gpointer user_data)
814 {
815   GstValidateScenario *scenario = (GstValidateScenario *) user_data;
816
817   if (!gst_structure_get_double (scenario->priv->vars, name, value))
818     return FALSE;
819
820   return TRUE;
821 }
822
823 /* Check that @list doesn't contain any non-optional actions */
824 static gboolean
825 actions_list_is_done (GList * list)
826 {
827   GList *l;
828
829   for (l = list; l != NULL; l = g_list_next (l)) {
830     GstValidateAction *action = l->data;
831
832     if (!action->priv->optional)
833       return FALSE;
834   }
835
836   return TRUE;
837 }
838
839 static void
840 _check_scenario_is_done (GstValidateScenario * scenario)
841 {
842   SCENARIO_LOCK (scenario);
843   if (actions_list_is_done (scenario->priv->actions) &&
844       actions_list_is_done (scenario->priv->non_blocking_running_actions) &&
845       actions_list_is_done (scenario->priv->on_addition_actions)) {
846     SCENARIO_UNLOCK (scenario);
847
848     g_signal_emit (scenario, scenario_signals[DONE], 0);
849   } else {
850     SCENARIO_UNLOCK (scenario);
851   }
852 }
853
854 static void
855 _reset_sink_information (GstValidateSinkInformation * sinkinfo)
856 {
857   sinkinfo->segment_seqnum = GST_SEQNUM_INVALID;
858   gst_segment_init (&sinkinfo->segment, GST_FORMAT_UNDEFINED);
859 }
860
861 /**
862  * gst_validate_action_get_clocktime:
863  * @scenario: The #GstValidateScenario from which to get a time
864  *            for a parameter of an action
865  * @action: The action from which to retrieve the time for @name
866  *          parameter.
867  * @name: The name of the parameter for which to retrieve a time
868  * @retval: (out): The return value for the wanted time
869  *
870  * Get a time value for the @name parameter of an action. This
871  * method should be called to retrieve and compute a timed value of a given
872  * action. It will first try to retrieve the value as a double,
873  * then get it as a string and execute any formula taking into account
874  * the 'position' and 'duration' variables. And it will always convert that
875  * value to a GstClockTime.
876  *
877  * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
878  */
879 gboolean
880 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
881     GstValidateAction * action, const gchar * name, GstClockTime * retval)
882 {
883
884   if (!gst_structure_has_field (action->structure, name))
885     return FALSE;
886
887   if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
888     gdouble val;
889     gchar *error = NULL, *strval;
890     const gchar *tmpvalue = gst_structure_get_string (action->structure, name);
891
892     if (!tmpvalue) {
893       GST_INFO_OBJECT (scenario, "Could not find %s (%" GST_PTR_FORMAT ")",
894           name, action->structure);
895       return -1;
896     }
897
898     _update_well_known_vars (scenario);
899     strval =
900         gst_validate_replace_variables_in_string (action, scenario->priv->vars,
901         tmpvalue, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
902     if (!strval)
903       return FALSE;
904
905     val =
906         gst_validate_utils_parse_expression (strval, _set_variable_func,
907         scenario, &error);
908     if (error) {
909       GST_WARNING ("Error while parsing %s: %s (%" GST_PTR_FORMAT ")",
910           strval, error, scenario->priv->vars);
911       g_free (error);
912       g_free (strval);
913
914       return FALSE;
915     } else if (val == -1.0) {
916       *retval = GST_CLOCK_TIME_NONE;
917     } else {
918       gint n, d;
919
920       gst_util_double_to_fraction (val, &n, &d);
921       *retval = gst_util_uint64_scale_int_round (n, GST_SECOND, d);
922     }
923     gst_structure_set (action->structure, name, G_TYPE_UINT64, *retval, NULL);
924     g_free (strval);
925
926     return TRUE;
927   }
928
929   return TRUE;
930 }
931
932 /* WITH SCENARIO LOCK TAKEN */
933 static GstValidateSinkInformation *
934 _find_sink_information (GstValidateScenario * scenario, GstElement * sink)
935 {
936   GList *tmp;
937
938   for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
939     GstValidateSinkInformation *sink_info =
940         (GstValidateSinkInformation *) tmp->data;
941     if (sink_info->sink == sink)
942       return sink_info;
943   }
944   return NULL;
945 }
946
947 /* WITH SCENARIO LOCK TAKEN */
948 static GstValidateSeekInformation *
949 _find_seek_information (GstValidateScenario * scenario, guint32 seqnum)
950 {
951   GList *tmp;
952
953   for (tmp = scenario->priv->seeks; tmp; tmp = tmp->next) {
954     GstValidateSeekInformation *seek_info =
955         (GstValidateSeekInformation *) tmp->data;
956     if (seek_info->seqnum == seqnum)
957       return seek_info;
958   }
959
960   return NULL;
961 }
962
963 /* WITH SCENARIO LOCK TAKEN */
964 static void
965 _validate_sink_information (GstValidateScenario * scenario)
966 {
967   GList *tmp;
968   gboolean all_sinks_ready = TRUE;
969   gboolean identical_seqnum = TRUE;
970   gboolean transitioning = FALSE;
971   guint32 common_seqnum = GST_SEQNUM_INVALID;
972   guint32 next_seqnum = GST_SEQNUM_INVALID;
973   GstValidateSeekInformation *seek_info;
974
975   if (scenario->priv->seeks)
976     /* If we have a pending seek, get the expected seqnum to
977      * figure out whether we are transitioning to a seek */
978     next_seqnum =
979         ((GstValidateSeekInformation *) scenario->priv->seeks->data)->seqnum;
980
981   GST_LOG_OBJECT (scenario, "next_seqnum %" G_GUINT32_FORMAT, next_seqnum);
982
983   for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
984     GstValidateSinkInformation *sink_info =
985         (GstValidateSinkInformation *) tmp->data;
986     GST_DEBUG_OBJECT (sink_info->sink,
987         "seqnum:%" G_GUINT32_FORMAT " segment:%" GST_SEGMENT_FORMAT,
988         sink_info->segment_seqnum, &sink_info->segment);
989     if (sink_info->segment_seqnum == GST_SEQNUM_INVALID)
990       all_sinks_ready = FALSE;
991     else if (sink_info->segment.format == GST_FORMAT_TIME) {
992       /* Are we in the middle of switching segments (from the current
993        * one, or to the next week) ? */
994       if (sink_info->segment_seqnum == scenario->priv->current_seqnum ||
995           sink_info->segment_seqnum == next_seqnum)
996         transitioning = TRUE;
997
998       /* We are only interested in sinks that handle TIME segments */
999       if (common_seqnum == GST_SEQNUM_INVALID)
1000         common_seqnum = sink_info->segment_seqnum;
1001       else if (common_seqnum != sink_info->segment_seqnum) {
1002         identical_seqnum = FALSE;
1003       }
1004     }
1005   }
1006
1007   /* If not all sinks have received a segment, just return */
1008   if (!all_sinks_ready)
1009     return;
1010
1011   GST_FIXME_OBJECT (scenario,
1012       "All sinks have valid segment. identical_seqnum:%d transitioning:%d seqnum:%"
1013       G_GUINT32_FORMAT " (current:%" G_GUINT32_FORMAT ") seeks:%p",
1014       identical_seqnum, transitioning, common_seqnum,
1015       scenario->priv->current_seqnum, scenario->priv->seeks);
1016
1017   if (!identical_seqnum) {
1018     /* If all sinks received a segment *and* there is a pending seek *and* there
1019      * wasn't one previously, we definitely have a failure */
1020     if (!transitioning && scenario->priv->current_seek == NULL
1021         && scenario->priv->seeks) {
1022       GST_VALIDATE_REPORT (scenario, EVENT_SEEK_INVALID_SEQNUM,
1023           "Not all segments from a given seek have the same seqnum");
1024       return;
1025     }
1026     /* Otherwise we're either doing the initial preroll (without seek)
1027      * or we are in the middle of switching to another seek */
1028     return;
1029   }
1030
1031   /* Now check if we have seek data related to that seqnum */
1032   seek_info = _find_seek_information (scenario, common_seqnum);
1033
1034   if (seek_info && seek_info != scenario->priv->current_seek) {
1035     GST_DEBUG_OBJECT (scenario, "Found a corresponding seek !");
1036     /* Updating values */
1037     /* FIXME : Check segment values if needed ! */
1038     /* FIXME : Non-flushing seek, validate here */
1039     if (seek_info->start_type == GST_SEEK_TYPE_SET)
1040       scenario->priv->segment_start = seek_info->start;
1041     if (seek_info->stop_type == GST_SEEK_TYPE_SET)
1042       scenario->priv->segment_stop = seek_info->stop;
1043     if (scenario->priv->target_state == GST_STATE_PAUSED)
1044       scenario->priv->seeked_in_pause = TRUE;
1045     SCENARIO_UNLOCK (scenario);
1046     /* If it's a non-flushing seek, validate it here
1047      * otherwise we will do it when the async_done is received */
1048     if (!(seek_info->flags & GST_SEEK_FLAG_FLUSH))
1049       gst_validate_action_set_done (seek_info->action);
1050     SCENARIO_LOCK (scenario);
1051   }
1052   /* We always set the current_seek. Can be NULL if no matching  */
1053   scenario->priv->current_seek = seek_info;
1054   scenario->priv->current_seqnum = common_seqnum;
1055 }
1056
1057 /**
1058  * gst_validate_scenario_execute_seek:
1059  * @scenario: The #GstValidateScenario for which to execute a seek action
1060  * @action: The seek action to execute
1061  * @rate: The playback rate of the seek
1062  * @format: The #GstFormat of the seek
1063  * @flags: The #GstSeekFlags of the seek
1064  * @start_type: The #GstSeekType of the start value of the seek
1065  * @start: The start time of the seek
1066  * @stop_type: The #GstSeekType of the stop value of the seek
1067  * @stop: The stop time of the seek
1068  *
1069  * Executes a seek event on the scenario's pipeline. You should always use
1070  * this method when you want to execute a seek inside a new action type
1071  * so that the scenario state is updated taking into account that seek.
1072  *
1073  * For more information you should have a look at #gst_event_new_seek
1074  *
1075  * Returns: %TRUE if the seek could be executed, %FALSE otherwise
1076  */
1077 GstValidateExecuteActionReturn
1078 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
1079     GstValidateAction * action, gdouble rate, GstFormat format,
1080     GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
1081     GstSeekType stop_type, GstClockTime stop)
1082 {
1083   GstEvent *seek;
1084   GstValidateSeekInformation *seek_info;
1085
1086   GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1087   GstValidateScenarioPrivate *priv = scenario->priv;
1088   DECLARE_AND_GET_PIPELINE (scenario, action);
1089
1090   seek = gst_event_new_seek (rate, format, flags, start_type, start,
1091       stop_type, stop);
1092
1093   if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
1094     GST_VALIDATE_REPORT_ACTION (scenario, action,
1095         SCENARIO_ACTION_EXECUTION_ERROR,
1096         "Trying to seek in format %d, but not support yet!", format);
1097   }
1098
1099   seek_info = g_new0 (GstValidateSeekInformation, 1);
1100   seek_info->seqnum = GST_EVENT_SEQNUM (seek);
1101   seek_info->rate = rate;
1102   seek_info->format = format;
1103   seek_info->flags = flags;
1104   seek_info->start = start;
1105   seek_info->stop = stop;
1106   seek_info->start_type = start_type;
1107   seek_info->stop_type = stop_type;
1108   seek_info->action = gst_validate_action_ref (action);
1109
1110   SCENARIO_LOCK (scenario);
1111   priv->seeks = g_list_append (priv->seeks, seek_info);
1112   SCENARIO_UNLOCK (scenario);
1113
1114   gst_event_ref (seek);
1115   if (gst_element_send_event (pipeline, seek)) {
1116     priv->seek_flags = flags;
1117     priv->seek_format = format;
1118   } else {
1119     switch (format) {
1120       case GST_FORMAT_TIME:
1121         GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1122             "Could not execute seek: '(position %" GST_TIME_FORMAT
1123             "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
1124             " stop: %" GST_TIME_FORMAT " Rate %lf'",
1125             GST_TIME_ARGS (action->playback_time), action->name,
1126             action->action_number, action->repeat, GST_TIME_ARGS (start),
1127             GST_TIME_ARGS (stop), rate);
1128         break;
1129       default:
1130       {
1131         gchar *format_str = g_enum_to_string (GST_TYPE_FORMAT, format);
1132
1133         GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1134             "Could not execute seek in format %s '(position %" GST_TIME_FORMAT
1135             "), %s (num %u, missing repeat: %i), seeking to: %" G_GINT64_FORMAT
1136             " stop: %" G_GINT64_FORMAT " Rate %lf'", format_str,
1137             GST_TIME_ARGS (action->playback_time), action->name,
1138             action->action_number, action->repeat, start, stop, rate);
1139         g_free (format_str);
1140         break;
1141       }
1142     }
1143     SCENARIO_LOCK (scenario);
1144     priv->seeks = g_list_remove (priv->seeks, seek_info);
1145     SCENARIO_UNLOCK (scenario);
1146
1147     gst_validate_seek_information_free (seek_info);
1148     ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1149   }
1150   gst_event_unref (seek);
1151   gst_object_unref (pipeline);
1152
1153   return ret;
1154 }
1155
1156 static gint
1157 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
1158 {
1159   const char *str_format, *str_flags, *str_start_type, *str_stop_type;
1160
1161   gdouble rate = 1.0;
1162   guint format = GST_FORMAT_TIME;
1163   GstSeekFlags flags = 0;
1164   guint start_type = GST_SEEK_TYPE_SET;
1165   GstClockTime start;
1166   guint stop_type = GST_SEEK_TYPE_SET;
1167   GstClockTime stop = GST_CLOCK_TIME_NONE;
1168
1169   if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
1170     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1171
1172   gst_structure_get_double (action->structure, "rate", &rate);
1173   if ((str_format = gst_structure_get_string (action->structure, "format")))
1174     gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
1175
1176   if ((str_start_type =
1177           gst_structure_get_string (action->structure, "start_type")))
1178     gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
1179         &start_type);
1180
1181   if ((str_stop_type =
1182           gst_structure_get_string (action->structure, "stop_type")))
1183     gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
1184         &stop_type);
1185
1186   if ((str_flags = gst_structure_get_string (action->structure, "flags")))
1187     flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
1188
1189   gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
1190
1191   return gst_validate_scenario_execute_seek (scenario, action, rate, format,
1192       flags, start_type, start, stop_type, stop);
1193 }
1194
1195 static gboolean
1196 _pause_action_restore_playing (GstValidateScenario * scenario)
1197 {
1198   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1199
1200   if (!pipeline) {
1201     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1202
1203     return FALSE;
1204   }
1205
1206   gst_validate_printf (scenario, "Back to playing\n");
1207
1208   if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
1209       GST_STATE_CHANGE_FAILURE) {
1210     GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
1211         "Failed to set state to playing");
1212     scenario->priv->target_state = GST_STATE_PLAYING;
1213   }
1214
1215   gst_object_unref (pipeline);
1216
1217   return FALSE;
1218 }
1219
1220 static gboolean
1221 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
1222 {
1223   gst_structure_id_set_value (consts, field_id, value);
1224
1225   return TRUE;
1226 }
1227
1228 static GstValidateExecuteActionReturn
1229 _execute_define_vars (GstValidateScenario * scenario,
1230     GstValidateAction * action)
1231 {
1232   gst_structure_foreach (action->structure,
1233       (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
1234
1235   return GST_VALIDATE_EXECUTE_ACTION_OK;
1236 }
1237
1238 static GstValidateExecuteActionReturn
1239 _set_timed_value (GQuark field_id, const GValue * gvalue,
1240     GstStructure * structure)
1241 {
1242   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1243   gdouble value;
1244   GstClockTime timestamp;
1245   GstTimedValueControlSource *source = NULL;
1246   GstControlBinding *binding;
1247   GstValidateScenario *scenario;
1248   GstValidateAction *action;
1249   GstObject *obj = NULL;
1250   GParamSpec *paramspec = NULL;
1251   const gchar *field = g_quark_to_string (field_id);
1252   const gchar *unused_fields[] =
1253       { "binding-type", "source-type", "interpolation-mode",
1254     "timestamp", "__scenario__", "__action__", "__res__", "repeat",
1255     "playback-time", NULL
1256   };
1257
1258   if (g_strv_contains (unused_fields, field))
1259     return TRUE;
1260
1261   gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1262       "__action__", G_TYPE_POINTER, &action, NULL);
1263
1264
1265   if (G_VALUE_HOLDS_DOUBLE (gvalue))
1266     value = g_value_get_double (gvalue);
1267   else if (G_VALUE_HOLDS_INT (gvalue))
1268     value = (gdouble) g_value_get_int (gvalue);
1269   else {
1270     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1271         "Invalid value type for property '%s': %s",
1272         field, G_VALUE_TYPE_NAME (gvalue));
1273     goto err;
1274   }
1275
1276   obj = _get_target_object_property (scenario, action, field, &paramspec);
1277   if (!obj || !paramspec) {
1278     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1279     goto err;
1280   }
1281
1282   REPORT_UNLESS (gst_validate_action_get_clocktime (scenario, action,
1283           "timestamp", &timestamp), err,
1284       "Could get timestamp on %" GST_PTR_FORMAT, action->structure);
1285
1286   binding = gst_object_get_control_binding (obj, paramspec->name);
1287   if (!binding) {
1288     guint mode;
1289     GType source_type;
1290     const gchar *interpolation_mode =
1291         gst_structure_get_string (action->structure, "interpolation-mode");
1292     const gchar *source_type_name =
1293         gst_structure_get_string (action->structure, "source-type");
1294
1295     if (source_type_name) {
1296       source_type = g_type_from_name (source_type_name);
1297
1298       REPORT_UNLESS (g_type_is_a (source_type,
1299               GST_TYPE_TIMED_VALUE_CONTROL_SOURCE), err,
1300           "Source type '%s' is not supported", source_type_name);
1301     } else {
1302       source_type = GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
1303     }
1304
1305     source = g_object_new (source_type, NULL);
1306     gst_object_ref_sink (source);
1307     if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
1308       if (interpolation_mode)
1309         REPORT_UNLESS (gst_validate_utils_enum_from_str
1310             (GST_TYPE_INTERPOLATION_MODE, interpolation_mode, &mode), err,
1311             "Could not convert interpolation-mode '%s'", interpolation_mode);
1312
1313       else
1314         mode = GST_INTERPOLATION_MODE_LINEAR;
1315
1316       g_object_set (source, "mode", mode, NULL);
1317     }
1318
1319     if (!g_strcmp0 (gst_structure_get_string (action->structure,
1320                 "binding-type"), "direct-absolute")) {
1321       binding =
1322           gst_direct_control_binding_new_absolute (obj, paramspec->name,
1323           GST_CONTROL_SOURCE (source));
1324     } else {
1325       binding =
1326           gst_direct_control_binding_new (obj, paramspec->name,
1327           GST_CONTROL_SOURCE (source));
1328     }
1329
1330     gst_object_add_control_binding (obj, binding);
1331   } else {
1332     g_object_get (binding, "control-source", &source, NULL);
1333   }
1334
1335   REPORT_UNLESS (GST_IS_TIMED_VALUE_CONTROL_SOURCE (source), err,
1336       "Could not find timed value control source on %s", field);
1337
1338   REPORT_UNLESS (gst_timed_value_control_source_set (source, timestamp, value),
1339       err, "Could not set %s=%f at %" GST_TIME_FORMAT, field, value,
1340       GST_TIME_ARGS (timestamp));
1341
1342   gst_object_unref (obj);
1343   gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1344
1345   return TRUE;
1346
1347 err:
1348   gst_clear_object (&obj);
1349   gst_structure_set (structure, "__res__", G_TYPE_INT,
1350       GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED, NULL);
1351
1352   return FALSE;
1353 }
1354
1355 static GstValidateExecuteActionReturn
1356 _set_timed_value_property (GstValidateScenario * scenario,
1357     GstValidateAction * action)
1358 {
1359   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1360
1361   gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1362       action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1363
1364   gst_structure_foreach (action->structure,
1365       (GstStructureForeachFunc) _set_timed_value, action->structure);
1366   gst_structure_get_int (action->structure, "__res__", &res);
1367   gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1368       "__res__", NULL);
1369
1370   return res;
1371 }
1372
1373
1374 static GstValidateExecuteActionReturn
1375 _check_property (GstValidateScenario * scenario, GstValidateAction * action,
1376     gpointer object, const gchar * propname, const GValue * expected_value)
1377 {
1378   GValue cvalue = G_VALUE_INIT;
1379
1380   g_value_init (&cvalue, G_VALUE_TYPE (expected_value));
1381   g_object_get_property (object, propname, &cvalue);
1382
1383   if (gst_value_compare (&cvalue, expected_value) != GST_VALUE_EQUAL) {
1384     gchar *expected = gst_value_serialize (expected_value), *observed =
1385         gst_value_serialize (&cvalue);
1386
1387     GST_VALIDATE_REPORT_ACTION (scenario, action,
1388         SCENARIO_ACTION_EXECUTION_ERROR,
1389         "%" GST_PTR_FORMAT
1390         "::%s expected value: '(%s)%s' different than observed: '(%s)%s'",
1391         object, propname, G_VALUE_TYPE_NAME (&cvalue), expected,
1392         G_VALUE_TYPE_NAME (expected_value), observed);
1393
1394     g_free (expected);
1395     g_free (observed);
1396
1397     g_value_reset (&cvalue);
1398     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1399   }
1400   g_value_reset (&cvalue);
1401
1402   return GST_VALIDATE_EXECUTE_ACTION_OK;
1403
1404 }
1405
1406 static GstValidateExecuteActionReturn
1407 _set_or_check_properties (GQuark field_id, const GValue * value,
1408     GstStructure * structure)
1409 {
1410   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1411   GstValidateScenario *scenario;
1412   GstValidateAction *action;
1413   GstObject *obj = NULL;
1414   GParamSpec *paramspec = NULL;
1415   const gchar *field = g_quark_to_string (field_id);
1416   const gchar *unused_fields[] = { "__scenario__", "__action__", "__res__",
1417     "playback-time", "repeat", NULL
1418   };
1419
1420   if (g_strv_contains (unused_fields, field))
1421     return TRUE;
1422
1423   gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1424       "__action__", G_TYPE_POINTER, &action, NULL);
1425
1426   obj = _get_target_object_property (scenario, action, field, &paramspec);
1427   if (!obj || !paramspec) {
1428     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1429     goto done;
1430   }
1431   if (gst_structure_has_name (action->structure, "set-properties"))
1432     res = gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
1433         G_OBJECT (obj), paramspec->name, value, action->priv->optional);
1434   else
1435     res = _check_property (scenario, action, obj, paramspec->name, value);
1436
1437 done:
1438   gst_clear_object (&obj);
1439   if (!gst_structure_has_field (structure, "__res__")
1440       || res != GST_VALIDATE_EXECUTE_ACTION_OK)
1441     gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1442   return TRUE;
1443 }
1444
1445 static GstValidateExecuteActionReturn
1446 _execute_set_or_check_properties (GstValidateScenario * scenario,
1447     GstValidateAction * action)
1448 {
1449   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1450
1451   gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1452       action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1453
1454   gst_structure_foreach (action->structure,
1455       (GstStructureForeachFunc) _set_or_check_properties, action->structure);
1456   gst_structure_get_int (action->structure, "__res__", &res);
1457   gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1458       "__res__", NULL);
1459
1460   return res;
1461 }
1462
1463 static GstValidateExecuteActionReturn
1464 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
1465 {
1466   guint state;
1467   const gchar *str_state;
1468   GstStateChangeReturn ret;
1469   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1470
1471   DECLARE_AND_GET_PIPELINE (scenario, action);
1472
1473   g_return_val_if_fail ((str_state =
1474           gst_structure_get_string (action->structure, "state")), FALSE);
1475
1476   g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
1477           str_state, &state), FALSE);
1478
1479
1480   scenario->priv->target_state = state;
1481   scenario->priv->changing_state = TRUE;
1482   scenario->priv->seeked_in_pause = FALSE;
1483
1484   ret = gst_element_set_state (pipeline, state);
1485   if (ret == GST_STATE_CHANGE_FAILURE) {
1486     scenario->priv->changing_state = FALSE;
1487     GST_VALIDATE_REPORT_ACTION (scenario, action, STATE_CHANGE_FAILURE,
1488         "Failed to set state to %s", str_state);
1489
1490     /* Nothing async on failure, action will be removed automatically */
1491     res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1492     goto done;
1493   } else if (ret == GST_STATE_CHANGE_ASYNC) {
1494
1495     scenario->priv->needs_async_done = TRUE;
1496     res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1497
1498     goto done;
1499   }
1500
1501   scenario->priv->changing_state = FALSE;
1502
1503 done:
1504   gst_object_unref (pipeline);
1505
1506   return res;
1507 }
1508
1509 static GstValidateExecuteActionReturn
1510 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
1511 {
1512   GstClockTime duration = 0;
1513   GstValidateExecuteActionReturn ret;
1514
1515   gst_validate_action_get_clocktime (scenario, action, "duration", &duration);
1516   gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
1517
1518   GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
1519       GST_TIME_ARGS (duration));
1520
1521   ret = _execute_set_state (scenario, action);
1522
1523   if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
1524     g_timeout_add (GST_TIME_AS_MSECONDS (duration),
1525         (GSourceFunc) _pause_action_restore_playing, scenario);
1526
1527   return ret;
1528 }
1529
1530 static GstValidateExecuteActionReturn
1531 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
1532 {
1533   GST_DEBUG ("Playing back");
1534
1535   gst_structure_set (action->structure, "state", G_TYPE_STRING,
1536       "playing", NULL);
1537
1538
1539   return _execute_set_state (scenario, action);
1540 }
1541
1542 static gboolean
1543 _action_sets_state (GstValidateAction * action)
1544 {
1545   if (action == NULL)
1546     return FALSE;
1547
1548   if (g_strcmp0 (action->type, "set-state") == 0)
1549     return TRUE;
1550
1551   if (g_strcmp0 (action->type, "play") == 0)
1552     return TRUE;
1553
1554   if (g_strcmp0 (action->type, "pause") == 0)
1555     return TRUE;
1556
1557   return FALSE;
1558
1559 }
1560
1561 static void
1562 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
1563 {
1564   GstValidateScenarioPrivate *priv = scenario->priv;
1565
1566   if (priv->max_dropped == -1 || priv->dropped == -1)
1567     return;
1568
1569   GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
1570       priv->dropped, priv->max_dropped);
1571
1572   if (priv->dropped > priv->max_dropped) {
1573     GST_VALIDATE_REPORT (scenario, CONFIG_TOO_MANY_BUFFERS_DROPPED,
1574         "Too many buffers have been dropped: %d (max allowed: %d)",
1575         priv->dropped, priv->max_dropped);
1576   }
1577 }
1578
1579 static GstValidateExecuteActionReturn
1580 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
1581 {
1582   gboolean ret;
1583
1584   DECLARE_AND_GET_PIPELINE (scenario, action);
1585
1586   GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
1587       GST_TIME_ARGS (action->playback_time));
1588
1589   ret = gst_element_send_event (pipeline, gst_event_new_eos ());
1590   gst_object_unref (pipeline);
1591
1592   return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
1593       GST_VALIDATE_EXECUTE_ACTION_ERROR;
1594 }
1595
1596 static int
1597 find_input_selector (GValue * velement, const gchar * type)
1598 {
1599   GstElement *element = g_value_get_object (velement);
1600   int result = !0;
1601
1602   if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
1603     GstPad *srcpad = gst_element_get_static_pad (element, "src");
1604
1605     if (srcpad) {
1606       GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
1607
1608       if (caps) {
1609         const char *mime =
1610             gst_structure_get_name (gst_caps_get_structure (caps, 0));
1611         gboolean found = FALSE;
1612
1613         if (g_strcmp0 (type, "audio") == 0)
1614           found = g_str_has_prefix (mime, "audio/");
1615         else if (g_strcmp0 (type, "video") == 0)
1616           found = g_str_has_prefix (mime, "video/")
1617               && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
1618         else if (g_strcmp0 (type, "text") == 0)
1619           found = g_str_has_prefix (mime, "text/")
1620               || g_str_has_prefix (mime, "subtitle/")
1621               || g_str_has_prefix (mime, "video/x-dvd-subpicture");
1622
1623         if (found)
1624           result = 0;
1625       }
1626
1627       gst_caps_unref (caps);
1628       gst_object_unref (srcpad);
1629     }
1630   }
1631   return result;
1632 }
1633
1634 static GstElement *
1635 find_input_selector_with_type (GstBin * bin, const gchar * type)
1636 {
1637   GValue result = { 0, };
1638   GstElement *input_selector = NULL;
1639   GstIterator *iterator = gst_bin_iterate_recurse (bin);
1640
1641   if (gst_iterator_find_custom (iterator,
1642           (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
1643     input_selector = g_value_get_object (&result);
1644   }
1645   gst_iterator_free (iterator);
1646
1647   return input_selector;
1648 }
1649
1650 static GstPad *
1651 find_nth_sink_pad (GstElement * element, int index)
1652 {
1653   GstIterator *iterator;
1654   gboolean done = FALSE;
1655   GstPad *pad = NULL;
1656   int dec_index = index;
1657   GValue data = { 0, };
1658
1659   iterator = gst_element_iterate_sink_pads (element);
1660   while (!done) {
1661     switch (gst_iterator_next (iterator, &data)) {
1662       case GST_ITERATOR_OK:
1663         if (!dec_index--) {
1664           done = TRUE;
1665           pad = g_value_get_object (&data);
1666           break;
1667         }
1668         g_value_reset (&data);
1669         break;
1670       case GST_ITERATOR_RESYNC:
1671         gst_iterator_resync (iterator);
1672         dec_index = index;
1673         break;
1674       case GST_ITERATOR_ERROR:
1675         done = TRUE;
1676         break;
1677       case GST_ITERATOR_DONE:
1678         done = TRUE;
1679         break;
1680     }
1681   }
1682   gst_iterator_free (iterator);
1683   return pad;
1684 }
1685
1686 static int
1687 find_sink_pad_index (GstElement * element, GstPad * pad)
1688 {
1689   GstIterator *iterator;
1690   gboolean done = FALSE;
1691   int index = 0;
1692   GValue data = { 0, };
1693
1694   iterator = gst_element_iterate_sink_pads (element);
1695   while (!done) {
1696     switch (gst_iterator_next (iterator, &data)) {
1697       case GST_ITERATOR_OK:
1698         if (pad == g_value_get_object (&data)) {
1699           done = TRUE;
1700         } else {
1701           index++;
1702         }
1703         g_value_reset (&data);
1704         break;
1705       case GST_ITERATOR_RESYNC:
1706         gst_iterator_resync (iterator);
1707         index = 0;
1708         break;
1709       case GST_ITERATOR_ERROR:
1710         done = TRUE;
1711         break;
1712       case GST_ITERATOR_DONE:
1713         done = TRUE;
1714         break;
1715     }
1716   }
1717   gst_iterator_free (iterator);
1718   return index;
1719 }
1720
1721 static GstPadProbeReturn
1722 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1723     GstValidateAction * action)
1724 {
1725   if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1726     gst_validate_action_set_done (action);
1727
1728     return GST_PAD_PROBE_REMOVE;
1729   }
1730
1731   return GST_PAD_PROBE_OK;
1732 }
1733
1734 static GstValidateExecuteActionReturn
1735 execute_switch_track_default (GstValidateScenario * scenario,
1736     GstValidateAction * action)
1737 {
1738   guint index;
1739   gboolean relative = FALSE;
1740   const gchar *type, *str_index;
1741   GstElement *input_selector;
1742   GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1743
1744   DECLARE_AND_GET_PIPELINE (scenario, action);
1745
1746   if (!(type = gst_structure_get_string (action->structure, "type")))
1747     type = "audio";
1748
1749   /* First find an input selector that has the right type */
1750   input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1751   if (input_selector) {
1752     GstState state, next;
1753     GstPad *pad, *cpad, *srcpad;
1754
1755     ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1756     str_index = gst_structure_get_string (action->structure, "index");
1757
1758     if (str_index == NULL) {
1759       if (!gst_structure_get_uint (action->structure, "index", &index)) {
1760         GST_WARNING ("No index given, defaulting to +1");
1761         index = 1;
1762         relative = TRUE;
1763       }
1764     } else {
1765       relative = strchr ("+-", str_index[0]) != NULL;
1766       index = g_ascii_strtoll (str_index, NULL, 10);
1767     }
1768
1769     if (relative) {             /* We are changing track relatively to current track */
1770       int npads;
1771
1772       g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1773       if (pad) {
1774         int current_index = find_sink_pad_index (input_selector, pad);
1775
1776         index = (current_index + index) % npads;
1777         gst_object_unref (pad);
1778       }
1779     }
1780
1781     pad = find_nth_sink_pad (input_selector, index);
1782     g_object_get (input_selector, "active-pad", &cpad, NULL);
1783     if (gst_element_get_state (pipeline, &state, &next, 0) &&
1784         state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1785       srcpad = gst_element_get_static_pad (input_selector, "src");
1786
1787       gst_pad_add_probe (srcpad,
1788           GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1789           (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1790       ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1791       gst_object_unref (srcpad);
1792     }
1793
1794     g_object_set (input_selector, "active-pad", pad, NULL);
1795     gst_object_unref (pad);
1796     gst_object_unref (cpad);
1797     gst_object_unref (input_selector);
1798
1799     goto done;
1800   }
1801
1802   /* No selector found -> Failed */
1803 done:
1804   gst_object_unref (pipeline);
1805
1806   return ret;
1807 }
1808
1809 static GstPadProbeReturn
1810 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1811     GstValidateAction * action)
1812 {
1813   if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1814     gst_validate_action_set_done (action);
1815     return GST_PAD_PROBE_REMOVE;
1816   }
1817   return GST_PAD_PROBE_OK;
1818 }
1819
1820 static GstValidateExecuteActionReturn
1821 execute_switch_track_pb (GstValidateScenario * scenario,
1822     GstValidateAction * action)
1823 {
1824   gint index, n;
1825   const gchar *type, *str_index;
1826
1827   gint flags, current, tflag;
1828   gchar *tmp, *current_txt;
1829
1830   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1831   gboolean relative = FALSE, disabling = FALSE;
1832
1833   DECLARE_AND_GET_PIPELINE (scenario, action);
1834
1835   if (!(type = gst_structure_get_string (action->structure, "type")))
1836     type = "audio";
1837
1838   tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1839       type);
1840   current_txt = g_strdup_printf ("current-%s", type);
1841
1842   tmp = g_strdup_printf ("n-%s", type);
1843   g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, &current,
1844       NULL);
1845
1846   /* Don't try to use -1 */
1847   if (current == -1)
1848     current = 0;
1849
1850   g_free (tmp);
1851
1852   if (gst_structure_has_field (action->structure, "disable")) {
1853     disabling = TRUE;
1854     flags &= ~tflag;
1855     index = -1;
1856   } else if (!(str_index =
1857           gst_structure_get_string (action->structure, "index"))) {
1858     if (!gst_structure_get_int (action->structure, "index", &index)) {
1859       GST_WARNING ("No index given, defaulting to +1");
1860       index = 1;
1861       relative = TRUE;
1862     }
1863   } else {
1864     relative = strchr ("+-", str_index[0]) != NULL;
1865     index = g_ascii_strtoll (str_index, NULL, 10);
1866   }
1867
1868   if (relative) {               /* We are changing track relatively to current track */
1869     if (n == 0) {
1870       GST_VALIDATE_REPORT_ACTION (scenario, action,
1871           SCENARIO_ACTION_EXECUTION_ERROR,
1872           "Trying to execute a relative %s for %s track when there"
1873           " is no track of this type available on current stream.",
1874           action->type, type);
1875
1876       res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1877       goto done;
1878     }
1879
1880     index = (current + index) % n;
1881   }
1882
1883   if (!disabling) {
1884     GstState state, next;
1885     GstPad *oldpad, *newpad;
1886     tmp = g_strdup_printf ("get-%s-pad", type);
1887     g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1888     g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1889
1890     gst_validate_printf (action, "Switching to track number: %i,"
1891         " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1892         GST_DEBUG_PAD_NAME (newpad));
1893     flags |= tflag;
1894     g_free (tmp);
1895
1896     if (gst_element_get_state (pipeline, &state, &next, 0) &&
1897         state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1898       GstPad *srcpad = NULL;
1899       GstElement *combiner = NULL;
1900       if (newpad == oldpad) {
1901         srcpad = gst_pad_get_peer (oldpad);
1902       } else if (newpad) {
1903         combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1904         if (combiner) {
1905           srcpad = gst_element_get_static_pad (combiner, "src");
1906           gst_object_unref (combiner);
1907         }
1908       }
1909
1910       if (srcpad) {
1911         gst_pad_add_probe (srcpad,
1912             GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1913             (GstPadProbeCallback) _check_pad_event_selection_done, action,
1914             NULL);
1915         gst_object_unref (srcpad);
1916
1917         res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1918       } else
1919         res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1920     }
1921
1922     if (oldpad)
1923       gst_object_unref (oldpad);
1924     gst_object_unref (newpad);
1925   } else {
1926     gst_validate_printf (action, "Disabling track type %s", type);
1927   }
1928
1929   g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1930   g_free (current_txt);
1931
1932 done:
1933   gst_object_unref (pipeline);
1934   return res;
1935 }
1936
1937 static GstStreamType
1938 stream_type_from_string (const gchar * type)
1939 {
1940   if (!g_strcmp0 (type, "video"))
1941     return GST_STREAM_TYPE_VIDEO;
1942   else if (!g_strcmp0 (type, "text"))
1943     return GST_STREAM_TYPE_TEXT;
1944
1945   /* default */
1946   return GST_STREAM_TYPE_AUDIO;
1947 }
1948
1949 /* Return a list of stream ID all the currently selected streams but the ones
1950  * of type @type */
1951 static GList *
1952 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1953 {
1954   GList *streams = NULL, *l;
1955
1956   for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1957     GstStream *s = l->data;
1958
1959     if (gst_stream_get_stream_type (s) != type) {
1960       streams = g_list_append (streams, (gpointer) s->stream_id);
1961     }
1962   }
1963
1964   return streams;
1965 }
1966
1967 static GList *
1968 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1969     GstStreamType type, gint index, gboolean relative)
1970 {
1971   guint nb_streams;
1972   guint i, n = 0, current = 0;
1973   GList *result = NULL, *l;
1974   GstStream *streams[256], *s, *current_stream = NULL;
1975
1976   /* Keep all streams which are not @type */
1977   for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1978     s = l->data;
1979
1980     if (gst_stream_get_stream_type (s) != type) {
1981       result = g_list_append (result, (gpointer) s->stream_id);
1982     } else if (!current_stream) {
1983       /* Assume the stream we want to switch from is the first one */
1984       current_stream = s;
1985     }
1986   }
1987
1988   /* Calculate the number of @type streams */
1989   nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1990   for (i = 0; i < nb_streams; i++) {
1991     s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1992
1993     if (gst_stream_get_stream_type (s) == type) {
1994       streams[n] = s;
1995
1996       if (current_stream
1997           && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1998         current = n;
1999
2000       n++;
2001     }
2002   }
2003
2004   if (G_UNLIKELY (n == 0)) {
2005     GST_ERROR ("No streams available of the required type");
2006     return result;
2007   }
2008
2009   if (relative) {               /* We are changing track relatively to current track */
2010     index = (current + index) % n;
2011   } else
2012     index %= n;
2013
2014   /* Add the new stream we want to switch to */
2015   s = streams[index];
2016
2017   gst_validate_printf (action, "Switching from stream %s to %s",
2018       current_stream ? current_stream->stream_id : "", s->stream_id);
2019
2020   return g_list_append (result, (gpointer) s->stream_id);
2021 }
2022
2023 static GstValidateExecuteActionReturn
2024 execute_switch_track_pb3 (GstValidateScenario * scenario,
2025     GstValidateAction * action)
2026 {
2027   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
2028   GstValidateScenarioPrivate *priv = scenario->priv;
2029   gint index;
2030   GstStreamType stype;
2031   const gchar *type, *str_index;
2032   GList *new_streams = NULL;
2033   GstValidatePipelineMonitor *monitor;
2034   DECLARE_AND_GET_PIPELINE (scenario, action);
2035
2036   monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2037           pipeline, "validate-monitor"));
2038
2039   if (!monitor->stream_collection) {
2040     GST_VALIDATE_REPORT_ACTION (scenario, action,
2041         SCENARIO_ACTION_EXECUTION_ERROR,
2042         "No stream collection message received on the bus, "
2043         "can not switch track.");
2044     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2045     goto done;
2046   }
2047
2048   if (!monitor->streams_selected) {
2049     GST_VALIDATE_REPORT_ACTION (scenario, action,
2050         SCENARIO_ACTION_EXECUTION_ERROR,
2051         "No streams selected message received on the bus");
2052     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2053     goto done;
2054   }
2055
2056   type = gst_structure_get_string (action->structure, "type");
2057   stype = stream_type_from_string (type);
2058
2059   if (gst_structure_has_field (action->structure, "disable")) {
2060     gst_validate_printf (action, "Disabling track type %s", type);
2061     new_streams = disable_stream (monitor, stype);
2062   } else {
2063     gboolean relative = FALSE;
2064
2065     if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
2066       if (!gst_structure_get_int (action->structure, "index", &index)) {
2067         GST_WARNING ("No index given, defaulting to +1");
2068         index = 1;
2069         relative = TRUE;
2070       }
2071     } else {
2072       relative = strchr ("+-", str_index[0]) != NULL;
2073       index = g_ascii_strtoll (str_index, NULL, 10);
2074     }
2075
2076     new_streams = switch_stream (monitor, action, stype, index, relative);
2077   }
2078
2079   gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
2080       ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
2081       (GDestroyNotify) g_list_free);
2082
2083   if (!gst_element_send_event (pipeline,
2084           gst_event_new_select_streams (new_streams))) {
2085     GST_VALIDATE_REPORT_ACTION (scenario, action,
2086         SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
2087     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2088     goto done;
2089   }
2090
2091   priv->pending_switch_track = action;
2092   if (scenario->priv->target_state > GST_STATE_PAUSED) {
2093     res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2094   } else {
2095     gst_validate_action_ref (action);
2096     res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
2097   }
2098
2099 done:
2100   gst_object_unref (pipeline);
2101
2102   return res;
2103 }
2104
2105 static GstValidateExecuteActionReturn
2106 _execute_switch_track (GstValidateScenario * scenario,
2107     GstValidateAction * action)
2108 {
2109   GstValidatePipelineMonitor *monitor;
2110
2111   DECLARE_AND_GET_PIPELINE (scenario, action);
2112
2113   monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2114           pipeline, "validate-monitor"));
2115   gst_object_unref (pipeline);
2116
2117   if (monitor->is_playbin)
2118     return execute_switch_track_pb (scenario, action);
2119   else if (monitor->is_playbin3)
2120     return execute_switch_track_pb3 (scenario, action);
2121
2122   return execute_switch_track_default (scenario, action);
2123 }
2124
2125 static GstValidateExecuteActionReturn
2126 _execute_set_rank_or_disable_feature (GstValidateScenario * scenario,
2127     GstValidateAction * action)
2128 {
2129   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2130   guint rank;
2131   GList *features, *origlist;
2132   GstPlugin *plugin;
2133   GstPluginFeature *feature;
2134   const gchar *name;
2135   gboolean removing_feature =
2136       gst_structure_has_name (action->structure, "remove-plugin-feature");
2137   GstRegistry *registry = gst_registry_get ();
2138
2139   REPORT_UNLESS (
2140       (name = gst_structure_get_string (action->structure, "feature-name")) ||
2141       (name = gst_structure_get_string (action->structure, "name")), done,
2142       "Could not find the name of the plugin/feature(s) to tweak");
2143
2144   if (removing_feature)
2145     REPORT_UNLESS (
2146         (gst_structure_get_uint (action->structure, "rank", &rank)) ||
2147         (gst_structure_get_int (action->structure, "rank", (gint *) & rank)),
2148         done, "Could not get rank to set on %s", name);
2149
2150   feature = gst_registry_lookup_feature (registry, name);
2151   if (feature) {
2152     if (removing_feature)
2153       gst_plugin_feature_set_rank (feature, rank);
2154     else
2155       gst_registry_remove_feature (registry, feature);
2156     gst_object_unref (feature);
2157
2158     goto done;
2159   }
2160
2161   REPORT_UNLESS ((plugin = gst_registry_find_plugin (registry, name)),
2162       done, "Could not find %s", name);
2163
2164   if (removing_feature) {
2165     gst_registry_remove_plugin (registry, plugin);
2166     goto done;
2167   }
2168
2169   origlist = features =
2170       gst_registry_get_feature_list_by_plugin (registry,
2171       gst_plugin_get_name (plugin));
2172   for (; features; features = features->next)
2173     gst_plugin_feature_set_rank (features->data, rank);
2174   gst_plugin_feature_list_free (origlist);
2175
2176 done:
2177   return res;
2178 }
2179
2180 static inline gboolean
2181 _add_execute_actions_gsource (GstValidateScenario * scenario)
2182 {
2183   GstValidateScenarioPrivate *priv = scenario->priv;
2184
2185   SCENARIO_LOCK (scenario);
2186   if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
2187       && priv->signal_handler_id == 0 && priv->wait_message_action == NULL) {
2188     if (!scenario->priv->action_execution_interval)
2189       priv->execute_actions_source_id =
2190           g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
2191           (GSourceFunc) execute_next_action,
2192           gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2193     else
2194       priv->execute_actions_source_id =
2195           g_timeout_add_full (G_PRIORITY_DEFAULT,
2196           scenario->priv->action_execution_interval,
2197           (GSourceFunc) execute_next_action,
2198           gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2199     SCENARIO_UNLOCK (scenario);
2200
2201     GST_DEBUG_OBJECT (scenario, "Start checking position again");
2202     return TRUE;
2203   }
2204   SCENARIO_UNLOCK (scenario);
2205
2206   GST_LOG_OBJECT (scenario, "No need to start a new gsource");
2207   return FALSE;
2208 }
2209
2210 static gboolean
2211 _get_position (GstValidateScenario * scenario,
2212     GstValidateAction * act, GstClockTime * position)
2213 {
2214   gboolean has_pos = FALSE, has_dur = FALSE;
2215   GstClockTime duration = -1;
2216
2217   GstValidateScenarioPrivate *priv = scenario->priv;
2218   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2219
2220   if (!pipeline) {
2221     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2222
2223     return FALSE;
2224   }
2225
2226   has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
2227       (gint64 *) position)
2228       && GST_CLOCK_TIME_IS_VALID (*position);
2229   has_dur =
2230       gst_element_query_duration (pipeline, GST_FORMAT_TIME,
2231       (gint64 *) & duration)
2232       && GST_CLOCK_TIME_IS_VALID (duration);
2233
2234   if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
2235       act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2236     GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
2237         GST_TIME_ARGS (*position));
2238
2239     goto fail;
2240   }
2241
2242   if (has_pos && has_dur && !priv->got_eos) {
2243     if (*position > duration) {
2244       _add_execute_actions_gsource (scenario);
2245       GST_VALIDATE_REPORT (scenario,
2246           QUERY_POSITION_SUPERIOR_DURATION,
2247           "Reported position %" GST_TIME_FORMAT " > reported duration %"
2248           GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
2249
2250       goto done;
2251     }
2252   }
2253
2254 done:
2255   gst_object_unref (pipeline);
2256   return TRUE;
2257
2258 fail:
2259   gst_object_unref (pipeline);
2260   return FALSE;
2261 }
2262
2263 static gboolean
2264 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
2265     GstClockTime * position, gdouble * rate)
2266 {
2267   GstQuery *query;
2268
2269   GstClockTime start_with_tolerance, stop_with_tolerance;
2270   GstValidateScenarioPrivate *priv = scenario->priv;
2271   GstElement *pipeline;
2272
2273   if (!_get_position (scenario, act, position))
2274     return FALSE;
2275
2276   GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
2277       GST_TIME_ARGS (*position));
2278
2279   /* Check if playback is within seek segment */
2280   start_with_tolerance = (priv->segment_start <
2281       priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
2282   stop_with_tolerance =
2283       GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
2284       priv->seek_pos_tol : -1;
2285
2286   if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
2287           && *position > stop_with_tolerance)
2288       || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
2289           && *position < start_with_tolerance
2290           && priv->seek_format == GST_FORMAT_TIME)) {
2291
2292     GST_VALIDATE_REPORT_ACTION (scenario, act, QUERY_POSITION_OUT_OF_SEGMENT,
2293         "Current position %" GST_TIME_FORMAT " not in the expected range [%"
2294         GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
2295         GST_TIME_ARGS (start_with_tolerance),
2296         GST_TIME_ARGS (stop_with_tolerance));
2297   }
2298
2299   pipeline = gst_validate_scenario_get_pipeline (scenario);
2300   if (pipeline == NULL) {
2301     GST_INFO_OBJECT (scenario, "No pipeline set anymore");
2302
2303     return TRUE;
2304   }
2305
2306   query = gst_query_new_segment (GST_FORMAT_DEFAULT);
2307   if (gst_element_query (GST_ELEMENT (pipeline), query))
2308     gst_query_parse_segment (query, rate, NULL, NULL, NULL);
2309   gst_query_unref (query);
2310   gst_object_unref (pipeline);
2311
2312   if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE &&
2313       priv->seek_format == GST_FORMAT_TIME) {
2314     if (*rate > 0
2315         && (GstClockTime) ABS (GST_CLOCK_DIFF (*position,
2316                 priv->segment_start)) > priv->seek_pos_tol) {
2317       priv->seeked_in_pause = FALSE;
2318       GST_VALIDATE_REPORT_ACTION (scenario, act,
2319           EVENT_SEEK_RESULT_POSITION_WRONG,
2320           "Reported position after accurate seek in PAUSED state should be exactly"
2321           " what the user asked for. Position %" GST_TIME_FORMAT
2322           " is not not the expected one:  %" GST_TIME_FORMAT,
2323           GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
2324     }
2325   }
2326
2327   return TRUE;
2328 }
2329
2330 static gboolean
2331 _check_message_type (GstValidateScenario * scenario, GstValidateAction * act,
2332     GstMessage * message)
2333 {
2334   return act && message
2335       && !g_strcmp0 (gst_structure_get_string (act->structure, "on-message"),
2336       gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2337 }
2338
2339 static gboolean
2340 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
2341     GstClockTime position, gdouble rate)
2342 {
2343   GstElement *pipeline = NULL;
2344
2345   if (!act) {
2346     GST_DEBUG_OBJECT (scenario, "No action to execute");
2347
2348     goto no;
2349   }
2350
2351   pipeline = gst_validate_scenario_get_pipeline (scenario);
2352   if (pipeline == NULL) {
2353
2354     if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
2355             GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
2356       GST_VALIDATE_REPORT_ACTION (scenario, act,
2357           SCENARIO_ACTION_EXECUTION_ERROR,
2358           "Trying to execute an %s action after the pipeline has been destroyed"
2359           " but the type has not been marked as "
2360           "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
2361
2362       return FALSE;
2363     } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2364       GST_VALIDATE_REPORT_ACTION (scenario, act,
2365           SCENARIO_ACTION_EXECUTION_ERROR,
2366           "Trying to execute action %s with playback time %" GST_TIME_FORMAT
2367           " after the pipeline has been destroyed. It is impossible"
2368           " to execute an action with a playback time specified"
2369           " after the pipeline has been destroyed", act->type,
2370           GST_TIME_ARGS (act->playback_time));
2371
2372       goto no;
2373     }
2374
2375     GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
2376
2377     goto yes;
2378   } else if (scenario->priv->got_eos) {
2379     GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
2380     scenario->priv->got_eos = FALSE;
2381   } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
2382     GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
2383         "just executing actions");
2384
2385     goto yes;
2386   } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
2387     GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
2388
2389     goto yes;
2390   } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
2391     GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
2392         " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2393         GST_TIME_ARGS (act->playback_time));
2394
2395     goto no;
2396   } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
2397     GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
2398         " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2399         GST_TIME_ARGS (act->playback_time));
2400
2401     goto no;
2402   }
2403
2404 yes:
2405   gst_object_unref (pipeline);
2406   return TRUE;
2407
2408 no:
2409   gst_clear_object (&pipeline);
2410   return FALSE;
2411 }
2412
2413 static gboolean
2414 _set_action_playback_time (GstValidateScenario * scenario,
2415     GstValidateAction * action)
2416 {
2417   if (!gst_validate_action_get_clocktime (scenario, action,
2418           "playback-time", &action->playback_time)) {
2419     gst_validate_error_structure (action,
2420         "Could not parse playback-time in %" GST_PTR_FORMAT, action->structure);
2421
2422     return FALSE;
2423   }
2424
2425   gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
2426       action->playback_time, NULL);
2427
2428   return TRUE;
2429 }
2430
2431 static gboolean
2432 gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
2433 {
2434   GstValidateAction *action;
2435   GstValidateScenarioPrivate *priv = self->priv;
2436
2437   if (!priv->actions)
2438     return TRUE;
2439
2440   action = (GstValidateAction *) priv->actions->data;
2441   if (!action->priv->needs_playback_parsing)
2442     return TRUE;
2443
2444   if (!_set_action_playback_time (self, action)) {
2445     GST_ERROR_OBJECT (self, "Could not set playback_time!");
2446
2447     return FALSE;
2448   }
2449   action->priv->needs_playback_parsing = FALSE;
2450
2451   return TRUE;
2452 }
2453
2454 static gboolean
2455 _foreach_find_iterator (GQuark field_id, GValue * value,
2456     GstValidateAction * action)
2457 {
2458   const gchar *field = g_quark_to_string (field_id);
2459
2460   if (!g_strcmp0 (field, "actions"))
2461     return TRUE;
2462
2463   if (!GST_VALUE_HOLDS_INT_RANGE (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
2464     gst_validate_error_structure (action,
2465         "Unsupported iterator type `%s` for %s"
2466         ". Only ranges (`[(int)start, (int)stop, [(int)step]]`) and arrays "
2467         " (`<item1, item2>`) are supported", field, G_VALUE_TYPE_NAME (value));
2468     return TRUE;
2469   }
2470
2471   if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
2472     gst_validate_error_structure (action, "Wrong iterator syntax, "
2473         " only one iterator field is supported.");
2474     return FALSE;
2475   }
2476
2477   GST_VALIDATE_ACTION_RANGE_NAME (action) = field;
2478   return TRUE;
2479 }
2480
2481
2482 /**
2483  * gst_validate_execute_action:
2484  * @action_type: The #GstValidateActionType to execute
2485  * @action: (transfer full): The #GstValidateAction to execute
2486  *
2487  * Executes @action
2488  */
2489 GstValidateExecuteActionReturn
2490 gst_validate_execute_action (GstValidateActionType * action_type,
2491     GstValidateAction * action)
2492 {
2493   GstValidateExecuteActionReturn res;
2494   GstValidateScenario *scenario;
2495
2496   g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
2497       GST_VALIDATE_EXECUTE_ACTION_ERROR);
2498
2499   scenario = gst_validate_action_get_scenario (action);
2500   g_assert (scenario);
2501
2502   action->priv->context = g_main_context_ref (scenario->priv->context);
2503   if (action_type->prepare) {
2504     res = action_type->prepare (action);
2505     if (res == GST_VALIDATE_EXECUTE_ACTION_DONE) {
2506       gst_validate_print_action (action, NULL);
2507       return GST_VALIDATE_EXECUTE_ACTION_OK;
2508     }
2509
2510     if (res != GST_VALIDATE_EXECUTE_ACTION_OK) {
2511       GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
2512           " could not be prepared", action->structure);
2513
2514       gst_object_unref (scenario);
2515       return res;
2516     }
2517   }
2518
2519   gst_validate_print_action (action, NULL);
2520
2521   action->priv->execution_time = gst_util_get_timestamp ();
2522   action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
2523   action_type->priv->n_calls++;
2524   res = action_type->execute (scenario, action);
2525   gst_object_unref (scenario);
2526
2527   return res;
2528 }
2529
2530 /* scenario can be NULL **only** if the action is a CONFIG action and
2531  * add_to_lists is FALSE */
2532 static GstValidateExecuteActionReturn
2533 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
2534     GstStructure * structure, gboolean add_to_lists)
2535 {
2536   gdouble playback_time;
2537   gboolean is_config = FALSE;
2538   GstValidateActionType *action_type;
2539   GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
2540   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_NONE;
2541   gboolean optional, needs_parsing = FALSE;
2542
2543   action->type = gst_structure_get_name (structure);
2544   action_type = _find_action_type (action->type);
2545
2546   if (!action_type) {
2547     GST_ERROR_OBJECT (scenario, "Action type %s no found",
2548         gst_structure_get_name (structure));
2549
2550     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2551   }
2552
2553   if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
2554       gst_structure_get_double (structure, "playback_time", &playback_time)) {
2555     action->playback_time = playback_time * GST_SECOND;
2556   } else if (gst_structure_has_field_typed (structure, "playback-time",
2557           G_TYPE_STRING)
2558       || gst_structure_has_field_typed (structure, "playback_time",
2559           G_TYPE_STRING)) {
2560
2561     if (add_to_lists && priv) {
2562       action->priv->needs_playback_parsing = TRUE;
2563       needs_parsing = TRUE;
2564     }
2565   } else
2566     GST_INFO_OBJECT (scenario,
2567         "No playback time for action %" GST_PTR_FORMAT, structure);
2568
2569   if (!gst_validate_utils_get_clocktime (structure,
2570           "timeout", &action->priv->timeout)) {
2571     GST_INFO_OBJECT (scenario,
2572         "No timeout time for action %" GST_PTR_FORMAT, structure);
2573   }
2574
2575   action->structure = gst_structure_copy (structure);
2576
2577   if (!(action->name = gst_structure_get_string (action->structure, "name")))
2578     action->name = "";
2579
2580   if (!action->priv->main_structure)
2581     action->priv->main_structure = gst_structure_copy (structure);
2582
2583   if (gst_structure_get_boolean (structure, "optional", &optional)) {
2584     if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
2585       GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
2586           gst_structure_get_name (structure));
2587       return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2588     }
2589     action->priv->optional = optional;
2590   }
2591
2592   if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
2593       (gst_structure_get_boolean (action->structure, "as-config",
2594               &is_config) && is_config == TRUE)) {
2595
2596     action_type->priv->n_calls++;
2597     res = action_type->execute (scenario, action);
2598     gst_validate_print_action (action, NULL);
2599
2600     return res;
2601   }
2602
2603   if (!add_to_lists)
2604     return res;
2605
2606   if (priv != NULL) {
2607     GstValidateActionType *type = _find_action_type (action->type);
2608     gboolean can_execute_on_addition =
2609         type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
2610         && !GST_CLOCK_TIME_IS_VALID (action->playback_time)
2611         && !gst_structure_has_field (action->structure, "on-message");
2612
2613     if (needs_parsing)
2614       can_execute_on_addition = FALSE;
2615
2616     if (can_execute_on_addition) {
2617       GList *tmp;
2618
2619       for (tmp = priv->actions; tmp; tmp = tmp->next) {
2620         GstValidateAction *act = (GstValidateAction *) tmp->data;
2621         if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2622           can_execute_on_addition = FALSE;
2623           break;
2624         }
2625       }
2626
2627     }
2628
2629     if (can_execute_on_addition) {
2630       SCENARIO_LOCK (scenario);
2631       priv->on_addition_actions = g_list_append (priv->on_addition_actions,
2632           action);
2633       SCENARIO_UNLOCK (scenario);
2634
2635     } else {
2636       priv->actions = g_list_append (priv->actions, action);
2637     }
2638   }
2639
2640   return res;
2641 }
2642
2643 static gboolean
2644 gst_validate_scenario_execute_next_or_restart_looping (GstValidateScenario *
2645     scenario)
2646 {
2647   /* Recurse to the next action if it is possible
2648    * to execute right away */
2649   if (!scenario->priv->execute_on_idle) {
2650     GST_DEBUG_OBJECT (scenario, "linking next action execution");
2651
2652     return execute_next_action (scenario);
2653   } else {
2654     _add_execute_actions_gsource (scenario);
2655     GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2656         " next dispatch");
2657   }
2658   return G_SOURCE_CONTINUE;
2659 }
2660
2661 /* This is the main action execution function
2662  * it checks whether it is time to run the next action
2663  * and if it is the case executes it.
2664  *
2665  * If the 'execute-on-idle' property is not TRUE,
2666  * the function will recurse while the actions are run
2667  * synchronously
2668  */
2669 static gboolean
2670 execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
2671 {
2672   gdouble rate = 1.0;
2673   GstClockTime position = -1;
2674   GstValidateAction *act = NULL;
2675   GstValidateActionType *type;
2676
2677   GstValidateScenarioPrivate *priv = scenario->priv;
2678
2679   if (priv->buffering) {
2680     GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
2681
2682     return G_SOURCE_CONTINUE;
2683   }
2684
2685   if (priv->changing_state || priv->needs_async_done) {
2686     GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
2687     return G_SOURCE_CONTINUE;
2688   }
2689
2690   if (scenario->priv->actions)
2691     act = scenario->priv->actions->data;
2692
2693   if (!act) {
2694     _check_scenario_is_done (scenario);
2695     return G_SOURCE_CONTINUE;
2696   }
2697
2698   if (message && GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS
2699       && act->playback_time != GST_CLOCK_TIME_NONE) {
2700     GST_VALIDATE_REPORT_ACTION (scenario, act,
2701         SCENARIO_ACTION_ENDED_EARLY,
2702         "Got EOS before action playback time %" GST_TIME_FORMAT,
2703         GST_TIME_ARGS (act->playback_time));
2704     goto execute_action;
2705   }
2706
2707   switch (act->priv->state) {
2708     case GST_VALIDATE_EXECUTE_ACTION_NONE:
2709     case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2710       break;
2711     case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2712       return G_SOURCE_CONTINUE;
2713     case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2714       if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
2715         GstClockTime etime =
2716             gst_util_get_timestamp () - act->priv->execution_time;
2717
2718         if (etime > act->priv->timeout) {
2719           gchar *str = gst_structure_to_string (act->structure);
2720
2721           GST_VALIDATE_REPORT_ACTION (scenario, act,
2722               SCENARIO_ACTION_EXECUTION_ERROR,
2723               "Action %s timed out after: %" GST_TIME_FORMAT, str,
2724               GST_TIME_ARGS (etime));
2725
2726           g_free (str);
2727         }
2728       }
2729       GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2730           act->structure);
2731
2732       return G_SOURCE_CONTINUE;
2733     default:
2734       GST_ERROR ("State is %d", act->priv->state);
2735       g_assert_not_reached ();
2736   }
2737
2738   if (message) {
2739     if (!_check_message_type (scenario, act, message))
2740       return G_SOURCE_CONTINUE;
2741   } else if ((act && gst_structure_get_string (act->structure, "on-message") &&
2742           !GST_CLOCK_TIME_IS_VALID (act->playback_time)) ||
2743       (!_check_position (scenario, act, &position, &rate))) {
2744     return G_SOURCE_CONTINUE;
2745   }
2746
2747   if (!_should_execute_action (scenario, act, position, rate)) {
2748     _add_execute_actions_gsource (scenario);
2749
2750     return G_SOURCE_CONTINUE;
2751   }
2752
2753 execute_action:
2754   type = _find_action_type (act->type);
2755
2756   GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2757       " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2758   priv->seeked_in_pause = FALSE;
2759
2760   if (message)
2761     gst_structure_remove_field (act->structure, "playback-time");
2762   else
2763     gst_structure_remove_field (act->structure, "on-message");
2764
2765   act->priv->state = gst_validate_execute_action (type, act);
2766   switch (act->priv->state) {
2767     case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2768       GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2769           " to be done.");
2770
2771       SCENARIO_LOCK (scenario);
2772       priv->execute_actions_source_id = 0;
2773       SCENARIO_UNLOCK (scenario);
2774
2775       return G_SOURCE_CONTINUE;
2776     case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2777       return G_SOURCE_CONTINUE;
2778     case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2779       SCENARIO_LOCK (scenario);
2780       priv->non_blocking_running_actions =
2781           g_list_append (priv->non_blocking_running_actions, act);
2782       priv->actions = g_list_remove (priv->actions, act);
2783       SCENARIO_UNLOCK (scenario);
2784       return gst_validate_scenario_execute_next_or_restart_looping (scenario);
2785     default:
2786       gst_validate_action_set_done (act);
2787       return G_SOURCE_CONTINUE;
2788   }
2789 }
2790
2791 static gboolean
2792 execute_next_action (GstValidateScenario * scenario)
2793 {
2794   return execute_next_action_full (scenario, NULL);
2795 }
2796
2797 static gboolean
2798 stop_waiting (GstValidateAction * action)
2799 {
2800   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2801
2802   SCENARIO_LOCK (scenario);
2803   scenario->priv->wait_id = 0;
2804   SCENARIO_UNLOCK (scenario);
2805
2806   gst_validate_action_set_done (action);
2807   _add_execute_actions_gsource (scenario);
2808   gst_object_unref (scenario);
2809
2810
2811   return G_SOURCE_REMOVE;
2812 }
2813
2814 static void
2815 stop_waiting_signal (GstStructure * data)
2816 {
2817   guint sigid = 0;
2818   GstElement *target;
2819   GstStructure *check = NULL;
2820   GstValidateAction *action;
2821   GstValidateScenario *scenario;
2822
2823   gst_structure_get (data, "target", G_TYPE_POINTER, &target,
2824       "action", GST_TYPE_VALIDATE_ACTION, &action, "sigid", G_TYPE_UINT, &sigid,
2825       NULL);
2826   gst_structure_free (data);
2827
2828   scenario = gst_validate_action_get_scenario (action);
2829
2830   g_assert (scenario);
2831   SCENARIO_LOCK (scenario);
2832   g_signal_handler_disconnect (target,
2833       sigid ? sigid : scenario->priv->signal_handler_id);
2834   if (!sigid)
2835     scenario->priv->signal_handler_id = 0;
2836   SCENARIO_UNLOCK (scenario);
2837
2838   if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE,
2839           &check, NULL)) {
2840     GstValidateAction *subact =
2841         gst_validate_create_subaction (scenario, NULL, action,
2842         check, 0, 0);
2843     GstValidateActionType *subact_type = _find_action_type (subact->type);
2844     if (!(subact_type->flags & GST_VALIDATE_ACTION_TYPE_CHECK)) {
2845       gst_validate_error_structure (action,
2846           "`check` action %s is not marked as 'check'", subact->type);
2847     }
2848
2849     gst_validate_execute_action (subact_type, subact);
2850     gst_validate_action_unref (subact);
2851   }
2852
2853   gst_validate_action_set_done (action);
2854   gst_validate_action_unref (action);
2855   _add_execute_actions_gsource (scenario);
2856   gst_object_unref (scenario);
2857   gst_object_unref (target);
2858 }
2859
2860 static GstValidateExecuteActionReturn
2861 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2862 {
2863   GstValidateScenarioPrivate *priv = scenario->priv;
2864   GstClockTime duration;
2865
2866   gdouble wait_multiplier = 1;
2867   const gchar *str_wait_multiplier =
2868       g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2869
2870   if (str_wait_multiplier) {
2871     errno = 0;
2872     wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2873
2874     if (errno) {
2875       GST_ERROR ("Could not use the WAIT MULTIPLIER");
2876
2877       wait_multiplier = 1;
2878     }
2879
2880     if (wait_multiplier == 0) {
2881       GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2882       return GST_VALIDATE_EXECUTE_ACTION_OK;
2883     }
2884   }
2885
2886   if (!gst_validate_action_get_clocktime (scenario, action,
2887           "duration", &duration)) {
2888     GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2889
2890     return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2891   }
2892
2893   duration *= wait_multiplier;
2894
2895   SCENARIO_LOCK (scenario);
2896   if (priv->execute_actions_source_id) {
2897     g_source_remove (priv->execute_actions_source_id);
2898     priv->execute_actions_source_id = 0;
2899   }
2900   SCENARIO_UNLOCK (scenario);
2901
2902   SCENARIO_LOCK (scenario);
2903   priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2904       (GSourceFunc) stop_waiting, action);
2905   SCENARIO_UNLOCK (scenario);
2906
2907   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2908 }
2909
2910 static GstValidateExecuteActionReturn
2911 _execute_wait_for_signal (GstValidateScenario * scenario,
2912     GstValidateAction * action)
2913 {
2914   gboolean non_blocking;
2915   GstValidateScenarioPrivate *priv = scenario->priv;
2916   const gchar *signal_name = gst_structure_get_string
2917       (action->structure, "signal-name");
2918   GList *targets = NULL;
2919   GstElement *target;
2920   GstStructure *data;
2921   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2922   DECLARE_AND_GET_PIPELINE (scenario, action);
2923
2924   REPORT_UNLESS (signal_name, err, "No signal-name given for wait action");
2925   targets = _find_elements_defined_in_action (scenario, action);
2926   REPORT_UNLESS ((g_list_length (targets) == 1), err,
2927       "Could not find target element.");
2928
2929   gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2930
2931   if (priv->execute_actions_source_id) {
2932     g_source_remove (priv->execute_actions_source_id);
2933     priv->execute_actions_source_id = 0;
2934   }
2935
2936   target = targets->data;
2937   data =
2938       gst_structure_new ("a", "action", GST_TYPE_VALIDATE_ACTION, action,
2939       "target", G_TYPE_POINTER, target, NULL);
2940   SCENARIO_LOCK (scenario);
2941   priv->signal_handler_id = g_signal_connect_swapped (target, signal_name,
2942       (GCallback) stop_waiting_signal, data);
2943
2944   non_blocking =
2945       gst_structure_get_boolean (action->structure, "non-blocking",
2946       &non_blocking);
2947   if (non_blocking) {
2948     gst_structure_set (data, "sigid", G_TYPE_UINT, priv->signal_handler_id,
2949         NULL);
2950     priv->signal_handler_id = 0;
2951   }
2952   SCENARIO_UNLOCK (scenario);
2953
2954   gst_object_unref (pipeline);
2955   g_list_free (targets);
2956
2957
2958   return non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING :
2959       GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2960
2961 err:
2962   g_list_free_full (targets, gst_object_unref);
2963   gst_object_unref (pipeline);
2964   return res;
2965 }
2966
2967 static gboolean
2968 _execute_wait_for_message (GstValidateScenario * scenario,
2969     GstValidateAction * action)
2970 {
2971   GstValidateScenarioPrivate *priv = scenario->priv;
2972   const gchar *message_type = gst_structure_get_string
2973       (action->structure, "message-type");
2974   DECLARE_AND_GET_PIPELINE (scenario, action);
2975
2976   gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2977
2978   if (priv->execute_actions_source_id) {
2979     g_source_remove (priv->execute_actions_source_id);
2980     priv->execute_actions_source_id = 0;
2981   }
2982
2983   g_assert (!priv->wait_message_action);
2984   priv->wait_message_action = gst_validate_action_ref (action);
2985   gst_object_unref (pipeline);
2986
2987   return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2988 }
2989
2990 static GstValidateExecuteActionReturn
2991 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2992 {
2993   gboolean on_clock = FALSE;
2994
2995   gst_structure_get_boolean (action->structure, "on-clock", &on_clock);
2996   if (gst_structure_has_field (action->structure, "signal-name")) {
2997     return _execute_wait_for_signal (scenario, action);
2998   } else if (gst_structure_has_field (action->structure, "message-type")) {
2999     return _execute_wait_for_message (scenario, action);
3000   } else if (on_clock) {
3001     gst_test_clock_wait_for_next_pending_id (scenario->priv->clock, NULL);
3002
3003     return GST_VALIDATE_EXECUTE_ACTION_OK;
3004   } else {
3005     return _execute_timed_wait (scenario, action);
3006   }
3007
3008   return FALSE;
3009 }
3010
3011 static gboolean
3012 _execute_dot_pipeline (GstValidateScenario * scenario,
3013     GstValidateAction * action)
3014 {
3015   gchar *dotname;
3016   gint details = GST_DEBUG_GRAPH_SHOW_ALL;
3017   const gchar *name = gst_structure_get_string (action->structure, "name");
3018   DECLARE_AND_GET_PIPELINE (scenario, action);
3019
3020   gst_structure_get_int (action->structure, "details", &details);
3021   if (name)
3022     dotname = g_strdup_printf ("validate.action.%s", name);
3023   else
3024     dotname = g_strdup ("validate.action.unnamed");
3025
3026   GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
3027
3028   g_free (dotname);
3029   gst_object_unref (pipeline);
3030
3031   return TRUE;
3032 }
3033
3034 static GstElement *
3035 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
3036 {
3037   const gchar *name;
3038   GstElement *target;
3039   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3040
3041   if (!pipeline) {
3042     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3043
3044     return NULL;
3045   }
3046
3047   name = gst_structure_get_string (action->structure, "target-element-name");
3048   if (name == NULL) {
3049     gst_object_unref (pipeline);
3050
3051     return NULL;
3052   }
3053
3054   if (g_strcmp0 (GST_OBJECT_NAME (pipeline), name) == 0) {
3055     target = gst_object_ref (pipeline);
3056   } else {
3057     target = gst_bin_get_by_name (GST_BIN (pipeline), name);
3058   }
3059
3060   if (target == NULL)
3061     GST_ERROR ("Target element with given name (%s) not found", name);
3062   gst_object_unref (pipeline);
3063
3064   return target;
3065 }
3066
3067 /* _get_target_elements_by_klass_or_factory_name:
3068  * @scenario: a #GstValidateScenario
3069  * @action: a #GstValidateAction
3070  *
3071  * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
3072  * matches the 'target-element-klass' of @action and the factory name matches
3073  * the 'target-element-factory-name'.
3074  *
3075  * Returns: (transfer full) (element-type GstElement): a list of #GstElement
3076  */
3077 static GList *
3078 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
3079     GstValidateAction * action)
3080 {
3081   GList *result = NULL;
3082   GstIterator *it;
3083   const gchar *klass, *fname;
3084   GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
3085   gboolean done = FALSE;
3086   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3087
3088   if (!pipeline) {
3089     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3090
3091     return NULL;
3092   }
3093
3094   klass = gst_structure_get_string (action->structure, "target-element-klass");
3095   fname =
3096       gst_structure_get_string (action->structure,
3097       "target-element-factory-name");
3098   if (!klass && !fname) {
3099     gst_object_unref (pipeline);
3100
3101     return NULL;
3102   }
3103
3104   if (klass && gst_validate_element_has_klass (pipeline, klass))
3105     result = g_list_prepend (result, gst_object_ref (pipeline));
3106
3107   if (fname && gst_element_get_factory (pipeline)
3108       && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
3109           fname))
3110     result = g_list_prepend (result, gst_object_ref (pipeline));
3111
3112   it = gst_bin_iterate_recurse (GST_BIN (pipeline));
3113
3114   g_value_init (&param, G_TYPE_STRING);
3115   g_value_set_string (&param, klass);
3116
3117   while (!done) {
3118     switch (gst_iterator_next (it, &v)) {
3119       case GST_ITERATOR_OK:{
3120         GstElement *child = g_value_get_object (&v);
3121
3122         if (g_list_find (result, child))
3123           goto next;
3124
3125         if (klass && gst_validate_element_has_klass (child, klass)) {
3126           result = g_list_prepend (result, gst_object_ref (child));
3127           goto next;
3128         }
3129
3130         if (fname && gst_element_get_factory (child)
3131             && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
3132                 fname))
3133           result = g_list_prepend (result, gst_object_ref (child));
3134       next:
3135         g_value_reset (&v);
3136       }
3137         break;
3138       case GST_ITERATOR_RESYNC:
3139         gst_iterator_resync (it);
3140         break;
3141       case GST_ITERATOR_ERROR:
3142       case GST_ITERATOR_DONE:
3143         done = TRUE;
3144     }
3145   }
3146
3147   g_value_reset (&v);
3148   g_value_reset (&param);
3149   gst_iterator_free (it);
3150   gst_object_unref (pipeline);
3151
3152   return result;
3153 }
3154
3155 static GList *
3156 _find_elements_defined_in_action (GstValidateScenario * scenario,
3157     GstValidateAction * action)
3158 {
3159   GstElement *target;
3160   GList *targets = NULL;
3161
3162   /* set-property can be applied on either:
3163    * - a single element having target-element-name as name
3164    * - all the elements having target-element-klass as klass
3165    */
3166   if (gst_structure_get_string (action->structure, "target-element-name")) {
3167     target = _get_target_element (scenario, action);
3168     if (target == NULL)
3169       return FALSE;
3170
3171     targets = g_list_append (targets, target);
3172   } else if (gst_structure_get_string (action->structure,
3173           "target-element-klass") ||
3174       gst_structure_get_string (action->structure,
3175           "target-element-factory-name")) {
3176     targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
3177   }
3178
3179   return targets;
3180 }
3181
3182 static GstValidateExecuteActionReturn
3183 _execute_check_action_type_calls (GstValidateScenario * scenario,
3184     GstValidateAction * action)
3185 {
3186   const gchar *type;
3187   GstValidateActionType *t;
3188   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3189   gint n;
3190
3191   REPORT_UNLESS (gst_structure_get_int (action->structure, "n", &n),
3192       done, "No `n`!");
3193   REPORT_UNLESS ((type = gst_structure_get_string (action->structure, "type")),
3194       done, "No `type`!");
3195   REPORT_UNLESS ((t =
3196           _find_action_type (type)), done, "Can't find `%s`!", type);
3197   REPORT_UNLESS (t->priv->n_calls == n, done,
3198       "%s called %d times instead of expected %d", type, t->priv->n_calls, n);
3199
3200
3201 done:
3202   return res;
3203 }
3204
3205 static GstValidateExecuteActionReturn
3206 _execute_check_subaction_level (GstValidateScenario * scenario,
3207     GstValidateAction * action)
3208 {
3209   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3210   gint n;
3211
3212   REPORT_UNLESS (gst_structure_get_int (action->structure, "level", &n),
3213       done, "No `n`!");
3214   REPORT_UNLESS (gst_validate_action_get_level (action) == n, done,
3215       "Expected subaction level %d, got %d", n,
3216       gst_validate_action_get_level (action));
3217
3218
3219 done:
3220   return res;
3221 }
3222
3223 static gboolean
3224 set_env_var (GQuark field_id, GValue * value,
3225     GSubprocessLauncher * subproc_launcher)
3226 {
3227   g_subprocess_launcher_setenv (subproc_launcher, g_quark_to_string (field_id),
3228       g_value_get_string (value), TRUE);
3229
3230   return TRUE;
3231 }
3232
3233 static GstValidateExecuteActionReturn
3234 _run_command (GstValidateScenario * scenario, GstValidateAction * action)
3235 {
3236   gchar **argv = NULL, *_stderr = NULL;
3237   GError *error = NULL;
3238   const GValue *env = NULL;
3239   GSubprocess *subproc = NULL;
3240   GSubprocessLauncher *subproc_launcher = NULL;
3241   GstValidateExecuteActionReturn res =
3242       GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3243
3244   REPORT_UNLESS ((argv = gst_validate_utils_get_strv (action->structure,
3245               "argv")), done,
3246       "Couldn't find `argv` as array of strings in %" GST_PTR_FORMAT,
3247       action->structure);
3248
3249   subproc_launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDERR_PIPE);
3250   g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_SCENARIO");
3251   g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_CONFIG");
3252
3253   env = gst_structure_get_value (action->structure, "env");
3254   REPORT_UNLESS (!env || GST_VALUE_HOLDS_STRUCTURE (env), done,
3255       "The `env` parameter should be a GstStructure, got %s",
3256       G_VALUE_TYPE_NAME (env));
3257   if (env) {
3258     gst_structure_foreach (gst_value_get_structure (env),
3259         (GstStructureForeachFunc) set_env_var, subproc_launcher);
3260   }
3261
3262   REPORT_UNLESS (
3263       (subproc =
3264           g_subprocess_launcher_spawnv (subproc_launcher,
3265               (const gchar * const *) argv, &error)), done,
3266       "Couldn't start subprocess: %s", error->message);
3267
3268   REPORT_UNLESS (g_subprocess_communicate_utf8 (subproc, NULL, NULL, NULL,
3269           &_stderr, &error), done, "Failed to run check: %s", error->message);
3270
3271   REPORT_UNLESS (g_subprocess_get_exit_status (subproc) == 0,
3272       done, "Sub command failed. Stderr: %s", _stderr);
3273
3274   g_free (_stderr);
3275
3276   res = GST_VALIDATE_EXECUTE_ACTION_OK;
3277
3278 done:
3279   if (argv)
3280     g_strfreev (argv);
3281   g_clear_object (&subproc_launcher);
3282   g_clear_object (&subproc);
3283
3284   return res;
3285 }
3286
3287 static GstValidateExecuteActionReturn
3288 _execute_check_pad_caps (GstValidateScenario * scenario,
3289     GstValidateAction * action)
3290 {
3291   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3292   GList *elements = NULL;
3293   GstPad *pad = NULL;
3294   GstStructure *expected_struct = NULL;
3295   GstCaps *expected = NULL, *current_caps = NULL;
3296   const gchar *pad_name, *comparison_type =
3297       gst_structure_get_string (action->structure, "comparision-mode");
3298
3299   DECLARE_AND_GET_PIPELINE (scenario, action);
3300
3301   REPORT_UNLESS (elements =
3302       _find_elements_defined_in_action (scenario, action), done,
3303       "Could not find any element from %" GST_PTR_FORMAT, action->structure);
3304
3305   REPORT_UNLESS (g_list_length (elements) == 1, done,
3306       "More than one element found from %" GST_PTR_FORMAT, action->structure);
3307
3308   pad_name = gst_structure_get_string (action->structure, "pad");
3309   REPORT_UNLESS (pad =
3310       gst_element_get_static_pad (elements->data, pad_name), done,
3311       "Could not find pad %s in %" GST_PTR_FORMAT, pad_name, elements->data);
3312
3313   current_caps = gst_pad_get_current_caps (pad);
3314   if (gst_structure_get (action->structure, "expected-caps", GST_TYPE_STRUCTURE,
3315           &expected_struct, NULL))
3316     expected = gst_caps_new_full (gst_structure_copy (expected_struct), NULL);
3317   else
3318     gst_structure_get (action->structure, "expected-caps", GST_TYPE_CAPS,
3319         &expected, NULL);
3320
3321   if (!comparison_type || !g_strcmp0 (comparison_type, "intersect")) {
3322     REPORT_UNLESS (expected, done, "Can't intersect with NULL expected caps");
3323     REPORT_UNLESS (gst_caps_can_intersect (expected, current_caps), done,
3324         "Caps can't intesect. Expected: \n - %" GST_PTR_FORMAT "\nGot:\n - %"
3325         GST_PTR_FORMAT, expected, current_caps);
3326   } else if (!g_strcmp0 (comparison_type, "equal")) {
3327     REPORT_UNLESS ((expected == NULL && current_caps == NULL)
3328         || gst_caps_is_equal (expected, current_caps), done,
3329         "Caps do not match. Expected: %" GST_PTR_FORMAT " got %" GST_PTR_FORMAT,
3330         expected, current_caps);
3331   } else {
3332     REPORT_UNLESS (FALSE, done, "Invalid caps `comparision-type`: '%s'",
3333         comparison_type);
3334   }
3335
3336 done:
3337   g_clear_object (&pipeline);
3338   g_clear_object (&pad);
3339   g_list_free_full (elements, gst_object_unref);
3340   gst_clear_structure (&expected_struct);
3341   gst_clear_caps (&current_caps);
3342   gst_clear_caps (&expected);
3343
3344   return res;
3345
3346 }
3347
3348 static GstValidateExecuteActionReturn
3349 _execute_check_position (GstValidateScenario * scenario,
3350     GstValidateAction * action)
3351 {
3352   GstClockTime expected_pos, pos;
3353
3354   if (!gst_validate_action_get_clocktime (scenario, action,
3355           "expected-position", &expected_pos)) {
3356     GST_VALIDATE_REPORT_ACTION (scenario, action,
3357         SCENARIO_ACTION_EXECUTION_ERROR,
3358         "Could not retrieve expected position in: %" GST_PTR_FORMAT,
3359         action->structure);
3360
3361     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3362   }
3363
3364   if (!_get_position (scenario, NULL, &pos)) {
3365     GST_VALIDATE_REPORT_ACTION (scenario, action,
3366         SCENARIO_ACTION_EXECUTION_ERROR, "Could not get pipeline position");
3367
3368     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3369   }
3370
3371   if (pos != expected_pos) {
3372     GST_VALIDATE_REPORT_ACTION (scenario, action,
3373         SCENARIO_ACTION_EXECUTION_ERROR,
3374         "Pipeline position doesn't match expectations"
3375         " got %" GST_TIME_FORMAT " instead of %" GST_TIME_FORMAT,
3376         GST_TIME_ARGS (pos), GST_TIME_ARGS (expected_pos));
3377
3378     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3379   }
3380
3381   return GST_VALIDATE_EXECUTE_ACTION_OK;
3382
3383 }
3384
3385 static GstValidateExecuteActionReturn
3386 _execute_set_or_check_property (GstValidateScenario * scenario,
3387     GstValidateAction * action)
3388 {
3389   GList *targets, *l;
3390   const gchar *property;
3391   const GValue *property_value;
3392   gboolean ret = GST_VALIDATE_EXECUTE_ACTION_OK;
3393   gboolean check = gst_structure_has_name (action->structure, "check-property");
3394
3395   targets = _find_elements_defined_in_action (scenario, action);
3396   if (!targets) {
3397     GST_VALIDATE_REPORT_ACTION (scenario, action,
3398         SCENARIO_ACTION_EXECUTION_ERROR,
3399         "No element found for action: %" GST_PTR_FORMAT, action->structure);
3400
3401     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3402   }
3403
3404   property = gst_structure_get_string (action->structure, "property-name");
3405   property_value = gst_structure_get_value (action->structure,
3406       "property-value");
3407
3408   for (l = targets; l != NULL; l = g_list_next (l)) {
3409     if (!check) {
3410       GstValidateActionReturn tmpres;
3411
3412       tmpres =
3413           gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
3414           G_OBJECT (l->data), property, property_value, action->priv->optional);
3415
3416       if (!tmpres)
3417         ret = tmpres;
3418     } else {
3419       ret =
3420           _check_property (scenario, action, l->data, property, property_value);
3421     }
3422   }
3423
3424   g_list_free_full (targets, gst_object_unref);
3425   return ret;
3426 }
3427
3428 static gboolean
3429 _execute_set_debug_threshold (GstValidateScenario * scenario,
3430     GstValidateAction * action)
3431 {
3432   gchar *str = NULL;
3433   gboolean reset = TRUE;
3434   const gchar *threshold_str;
3435
3436   threshold_str =
3437       gst_structure_get_string (action->structure, "debug-threshold");
3438   if (threshold_str == NULL) {
3439     gint threshold;
3440
3441     if (gst_structure_get_int (action->structure, "debug-threshold",
3442             &threshold))
3443       threshold_str = str = g_strdup_printf ("%i", threshold);
3444     else
3445       return FALSE;
3446   }
3447
3448   gst_structure_get_boolean (action->structure, "reset", &reset);
3449
3450   gst_debug_set_threshold_from_string (threshold_str, reset);
3451
3452   g_free (str);
3453
3454   return TRUE;
3455 }
3456
3457 static GstValidateExecuteActionReturn
3458 _execute_emit_signal (GstValidateScenario * scenario,
3459     GstValidateAction * action)
3460 {
3461   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3462   GstElement *target;
3463   guint n_params = 0;
3464   GSignalQuery query = { 0, };
3465   GValue *values = NULL, lparams = { 0, };
3466   const GValue *params;
3467
3468   REPORT_UNLESS ((target =
3469           _get_target_element (scenario, action)), out, "No element found");
3470
3471   query.signal_name =
3472       gst_structure_get_string (action->structure, "signal-name");
3473   query.signal_id = g_signal_lookup (query.signal_name, G_OBJECT_TYPE (target));
3474   REPORT_UNLESS (query.signal_id != 0, out, "Invalid signal `%s::%s`",
3475       G_OBJECT_TYPE_NAME (target), query.signal_name);
3476
3477   g_signal_query (query.signal_id, &query);
3478
3479   params = gst_structure_get_value (action->structure, "params");
3480   if (params) {
3481     if (G_VALUE_HOLDS_STRING (params)) {
3482       g_value_init (&lparams, GST_TYPE_ARRAY);
3483
3484       REPORT_UNLESS (gst_value_deserialize (&lparams,
3485               g_value_get_string (params)), out,
3486           "\"params\" argument should be a value array or a string deserializable"
3487           " as value array, got string %s", g_value_get_string (params)
3488           );
3489       params = &lparams;
3490     } else {
3491       REPORT_UNLESS (GST_VALUE_HOLDS_ARRAY (params), out,
3492           "\"params\" argument should be a value array, got %s",
3493           G_VALUE_TYPE_NAME (params));
3494     }
3495     n_params = gst_value_array_get_size (params);
3496   }
3497   REPORT_UNLESS (query.n_params == (n_params), out,
3498       "Expected %d `params` got %d", query.n_params, n_params);
3499   values = g_malloc0 ((n_params + 2) * sizeof (GValue));
3500   g_value_init (&values[0], G_OBJECT_TYPE (target));
3501   g_value_take_object (&values[0], target);
3502   for (gint i = 1; i < n_params + 1; i++) {
3503     const GValue *param = gst_value_array_get_value (params, i - 1);
3504     g_value_init (&values[i], query.param_types[i - 1]);
3505
3506     if (query.param_types[i - 1] == G_TYPE_BYTES
3507         && G_VALUE_TYPE (param) == G_TYPE_STRING) {
3508       const gchar *s = g_value_get_string (param);
3509       g_value_take_boxed (&values[i], g_bytes_new (s, strlen (s)));
3510     } else {
3511       REPORT_UNLESS (g_value_transform (param, &values[i]), out,
3512           "Could not transform param %d from %s to %s", i - 1,
3513           G_VALUE_TYPE_NAME (param), G_VALUE_TYPE_NAME (&values[i]));
3514     }
3515   }
3516
3517   g_signal_emitv (values, query.signal_id, 0, NULL);
3518
3519   for (gint i = 0; i < n_params + 1; i++)
3520     g_value_reset (&values[i]);
3521
3522   if (G_VALUE_TYPE (&lparams))
3523     g_value_reset (&lparams);
3524
3525 out:
3526   return res;
3527
3528 }
3529
3530 typedef struct _ChainWrapperFunctionData ChainWrapperFunctionData;
3531 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
3532     GstBuffer * buffer, ChainWrapperFunctionData * data);
3533
3534 struct _ChainWrapperFunctionData
3535 {
3536   GstPadChainFunction wrapped_chain_func;
3537   gpointer wrapped_chain_data;
3538   GDestroyNotify wrapped_chain_notify;
3539   ChainWrapperFunction wrapper_function;
3540   gpointer wrapper_function_user_data;
3541
3542   GMutex actions_lock;
3543   GList *actions;
3544
3545 };
3546
3547 static void
3548 chain_wrapper_function_free (ChainWrapperFunctionData * data)
3549 {
3550   g_list_free_full (data->actions, (GDestroyNotify) gst_validate_action_unref);
3551   g_mutex_clear (&data->actions_lock);
3552 }
3553
3554 static GstFlowReturn
3555 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
3556 {
3557   ChainWrapperFunctionData *data =
3558       g_object_get_qdata (G_OBJECT (pad), chain_qdata);
3559
3560   return data->wrapper_function (pad, parent, buffer,
3561       g_object_get_qdata (G_OBJECT (pad), chain_qdata));
3562 }
3563
3564 static void
3565 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
3566     GstValidateAction * action)
3567 {
3568   ChainWrapperFunctionData *data =
3569       g_object_get_qdata (G_OBJECT (pad), chain_qdata);
3570
3571   if (data) {
3572     g_mutex_lock (&data->actions_lock);
3573     data->actions = g_list_append (data->actions, action);
3574     g_mutex_unlock (&data->actions_lock);
3575
3576     return;
3577   }
3578
3579   data = g_new0 (ChainWrapperFunctionData, 1);
3580   data->actions = g_list_append (data->actions, action);
3581
3582   g_object_set_qdata_full (G_OBJECT (pad), chain_qdata, data,
3583       (GDestroyNotify) chain_wrapper_function_free);
3584
3585   data->wrapped_chain_func = pad->chainfunc;
3586   data->wrapper_function = new_function;
3587   pad->chainfunc = _pad_chain_wrapper;
3588 }
3589
3590 static GstFlowReturn
3591 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent,
3592     GstBuffer * buffer, ChainWrapperFunctionData * data)
3593 {
3594   GstValidateAction *action;
3595   GstValidateScenario *scenario;
3596   GstFlowReturn ret;
3597
3598   g_mutex_lock (&data->actions_lock);
3599   if (data->actions) {
3600     action = data->actions->data;
3601     data->actions = g_list_remove (data->actions, action);
3602     g_mutex_unlock (&data->actions_lock);
3603
3604     scenario = gst_validate_action_get_scenario (action);
3605   } else {
3606     g_mutex_unlock (&data->actions_lock);
3607
3608     return data->wrapped_chain_func (pad, parent, buffer);
3609   }
3610
3611   GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
3612   ret = data->wrapped_chain_func (pad, parent, buffer);
3613   gst_validate_action_set_done (action);
3614   gst_validate_action_unref (action);
3615   GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3616   g_object_unref (scenario);
3617
3618   return ret;
3619 }
3620
3621 static gboolean
3622 structure_get_uint64_permissive (const GstStructure * structure,
3623     const gchar * fieldname, guint64 * dest)
3624 {
3625   const GValue *original;
3626   GValue transformed = G_VALUE_INIT;
3627
3628   original = gst_structure_get_value (structure, fieldname);
3629   if (!original)
3630     return FALSE;
3631
3632   g_value_init (&transformed, G_TYPE_UINT64);
3633   if (!g_value_transform (original, &transformed))
3634     return FALSE;
3635
3636   *dest = g_value_get_uint64 (&transformed);
3637   g_value_unset (&transformed);
3638   return TRUE;
3639 }
3640
3641 static gint
3642 _execute_appsrc_push (GstValidateScenario * scenario,
3643     GstValidateAction * action)
3644 {
3645   GstElement *target = NULL;
3646   gchar *file_name = NULL;
3647   gchar *file_contents = NULL;
3648   GError *error = NULL;
3649   GstBuffer *buffer;
3650   guint64 offset = 0;
3651   guint64 size = 0, read;
3652   gint push_sample_ret;
3653   gboolean wait;
3654   GFileInfo *finfo = NULL;
3655   GFile *f = NULL;
3656   GstPad *appsrc_pad = NULL;
3657   GstPad *peer_pad = NULL;
3658   GInputStream *stream = NULL;
3659   GstValidateExecuteActionReturn res;
3660   GstSegment segment;
3661   GstCaps *caps = NULL;
3662   GstSample *sample;
3663
3664   /* We will only wait for the the buffer to be pushed if we are in a state
3665    * that allows flow of buffers (>=PAUSED). Otherwise the buffer will just
3666    * be enqueued. */
3667   wait = scenario->priv->target_state >= GST_STATE_PAUSED;
3668
3669   target = _get_target_element (scenario, action);
3670   REPORT_UNLESS (target, err, "No element found.");
3671   file_name =
3672       g_strdup (gst_structure_get_string (action->structure, "file-name"));
3673   REPORT_UNLESS (file_name, err, "Missing file-name property.");
3674
3675   structure_get_uint64_permissive (action->structure, "offset", &offset);
3676   structure_get_uint64_permissive (action->structure, "size", &size);
3677
3678   f = g_file_new_for_path (file_name);
3679   stream = G_INPUT_STREAM (g_file_read (f, NULL, &error));
3680   REPORT_UNLESS (!error, err, "Could not open file for action. Error: %s",
3681       error->message);
3682
3683   if (offset > 0) {
3684     read = g_input_stream_skip (stream, offset, NULL, &error);
3685     REPORT_UNLESS (!error, err, "Could not skip to offset. Error: %s",
3686         error->message);
3687     REPORT_UNLESS (read == offset, err,
3688         "Could not skip to offset, only skipped: %" G_GUINT64_FORMAT, read);
3689   }
3690
3691   if (size <= 0) {
3692     finfo =
3693         g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_SIZE,
3694         G_FILE_QUERY_INFO_NONE, NULL, &error);
3695
3696     REPORT_UNLESS (!error, err, "Could not query file size. Error: %s",
3697         error->message);
3698     size = g_file_info_get_size (finfo);
3699   }
3700
3701   file_contents = g_malloc (size);
3702   read = g_input_stream_read (stream, file_contents, size, NULL, &error);
3703   REPORT_UNLESS (!error, err, "Could not read input file. Error: %s",
3704       error->message);
3705   REPORT_UNLESS (read == size, err,
3706       "Could read enough data, only read: %" G_GUINT64_FORMAT, read);
3707
3708   buffer = gst_buffer_new_wrapped (file_contents, size);
3709   file_contents = NULL;
3710   gst_validate_action_get_clocktime (scenario,
3711       action, "pts", &GST_BUFFER_PTS (buffer)
3712       );
3713   gst_validate_action_get_clocktime (scenario,
3714       action, "dts", &GST_BUFFER_DTS (buffer)
3715       );
3716   gst_validate_action_get_clocktime (scenario,
3717       action, "duration", &GST_BUFFER_DURATION (buffer)
3718       );
3719
3720   {
3721     const GValue *caps_value;
3722     caps_value = gst_structure_get_value (action->structure, "caps");
3723     if (caps_value) {
3724       if (G_VALUE_HOLDS_STRING (caps_value)) {
3725         caps = gst_caps_from_string (g_value_get_string (caps_value));
3726         REPORT_UNLESS (caps, err, "Invalid caps string: %s",
3727             g_value_get_string (caps_value));
3728       } else {
3729         caps = gst_caps_copy (gst_value_get_caps (caps_value));
3730       }
3731
3732       REPORT_UNLESS (caps, err, "Could not get caps value");
3733     }
3734   }
3735
3736   /* We temporarily override the peer pad chain function to finish the action
3737    * once the buffer chain actually ends. */
3738   appsrc_pad = gst_element_get_static_pad (target, "src");
3739   peer_pad = gst_pad_get_peer (appsrc_pad);
3740   REPORT_UNLESS (peer_pad, err, "Action failed, pad not linked");
3741
3742   wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
3743
3744   /* Keep the action alive until set done is called. */
3745   gst_validate_action_ref (action);
3746
3747   sample = gst_sample_new (buffer, caps, NULL, NULL);
3748   gst_clear_caps (&caps);
3749   gst_buffer_unref (buffer);
3750   if (gst_structure_has_field (action->structure, "segment")) {
3751     GstFormat format;
3752     GstStructure *segment_struct;
3753     GstClockTime tmp;
3754
3755     REPORT_UNLESS (gst_structure_get (action->structure, "segment",
3756             GST_TYPE_STRUCTURE, &segment_struct, NULL), err,
3757         "Segment field not in right format (expected GstStructure).");
3758
3759     if (!gst_structure_get (segment_struct, "format", GST_TYPE_FORMAT, &format,
3760             NULL))
3761       g_object_get (target, "format", &format, NULL);
3762
3763     gst_segment_init (&segment, format);
3764     if (gst_validate_utils_get_clocktime (segment_struct, "base", &tmp))
3765       segment.base = tmp;
3766     if (gst_validate_utils_get_clocktime (segment_struct, "offset", &tmp))
3767       segment.offset = tmp;
3768     if (gst_validate_utils_get_clocktime (segment_struct, "time", &tmp))
3769       segment.time = tmp;
3770     if (gst_validate_utils_get_clocktime (segment_struct, "position", &tmp))
3771       segment.position = tmp;
3772     if (gst_validate_utils_get_clocktime (segment_struct, "duration", &tmp))
3773       segment.duration = tmp;
3774     if (gst_validate_utils_get_clocktime (segment_struct, "start", &tmp))
3775       segment.start = tmp;
3776     if (gst_validate_utils_get_clocktime (segment_struct, "stop", &tmp))
3777       segment.stop = tmp;
3778     gst_structure_get_double (segment_struct, "rate", &segment.rate);
3779
3780     gst_structure_free (segment_struct);
3781
3782     gst_sample_set_segment (sample, &segment);
3783   }
3784
3785   g_signal_emit_by_name (target, "push-sample", sample, &push_sample_ret);
3786   gst_sample_unref (sample);
3787   REPORT_UNLESS (push_sample_ret == GST_FLOW_OK, err,
3788       "push-buffer signal failed in action.");
3789
3790   if (wait) {
3791     res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
3792   } else {
3793     gst_validate_printf (NULL,
3794         "Pipeline is not ready to push buffers, interlacing appsrc-push action...\n");
3795     res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
3796   }
3797 done:
3798   gst_clear_object (&target);
3799   gst_clear_object (&appsrc_pad);
3800   gst_clear_object (&peer_pad);
3801   g_clear_pointer (&file_name, g_free);
3802   g_clear_pointer (&file_contents, g_free);
3803   g_clear_error (&error);
3804   g_clear_object (&f);
3805   g_clear_object (&finfo);
3806   g_clear_object (&stream);
3807
3808   return res;
3809
3810 err:
3811   res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3812   goto done;
3813 }
3814
3815 static gint
3816 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
3817 {
3818   GstElement *target;
3819   gint eos_ret;
3820
3821   target = _get_target_element (scenario, action);
3822   if (target == NULL) {
3823     gchar *structure_string = gst_structure_to_string (action->structure);
3824     GST_VALIDATE_REPORT_ACTION (scenario, action,
3825         SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3826         structure_string);
3827     g_free (structure_string);
3828     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3829   }
3830
3831   g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
3832   if (eos_ret != GST_FLOW_OK) {
3833     gchar *structure_string = gst_structure_to_string (action->structure);
3834     GST_VALIDATE_REPORT_ACTION (scenario, action,
3835         SCENARIO_ACTION_EXECUTION_ERROR,
3836         "Failed to emit end-of-stream signal for action: %s", structure_string);
3837     g_free (structure_string);
3838     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3839   }
3840
3841   gst_object_unref (target);
3842   return GST_VALIDATE_EXECUTE_ACTION_OK;
3843 }
3844
3845 static gint
3846 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
3847 {
3848   GstElement *target;
3849   GstEvent *event;
3850   gboolean reset_time = TRUE;
3851
3852   target = _get_target_element (scenario, action);
3853   if (target == NULL) {
3854     gchar *structure_string = gst_structure_to_string (action->structure);
3855     GST_VALIDATE_REPORT_ACTION (scenario, action,
3856         SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3857         structure_string);
3858     g_free (structure_string);
3859     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3860   }
3861
3862   gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
3863
3864   event = gst_event_new_flush_start ();
3865   if (!gst_element_send_event (target, event)) {
3866     GST_VALIDATE_REPORT_ACTION (scenario, action,
3867         SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_START event was not handled");
3868     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3869   }
3870
3871   event = gst_event_new_flush_stop (reset_time);
3872   if (!gst_element_send_event (target, event)) {
3873     GST_VALIDATE_REPORT_ACTION (scenario, action,
3874         SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_STOP event was not handled");
3875     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3876   }
3877
3878   return GST_VALIDATE_EXECUTE_ACTION_OK;
3879 }
3880
3881 static GstValidateExecuteActionReturn
3882 _execute_disable_plugin (GstValidateScenario * scenario,
3883     GstValidateAction * action)
3884 {
3885   GstPlugin *plugin;
3886   const gchar *plugin_name;
3887
3888   plugin_name = gst_structure_get_string (action->structure, "plugin-name");
3889
3890   plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
3891
3892   if (plugin == NULL) {
3893     GST_VALIDATE_REPORT_ACTION (scenario, action,
3894         SCENARIO_ACTION_EXECUTION_ERROR, "Could not find plugin to disable: %s",
3895         plugin_name);
3896
3897     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3898   }
3899
3900   gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
3901   gst_registry_remove_plugin (gst_registry_get (), plugin);
3902
3903   return GST_VALIDATE_EXECUTE_ACTION_OK;
3904 }
3905
3906 static gboolean
3907 gst_validate_action_setup_repeat (GstValidateScenario * scenario,
3908     GstValidateAction * action)
3909 {
3910   gchar *repeat_expr;
3911   gchar *error = NULL;
3912   gint repeat, position, i;
3913
3914   if (!gst_structure_has_field (action->structure, "repeat"))
3915     return TRUE;
3916
3917   if (gst_structure_get_int (action->structure, "repeat", &repeat))
3918     goto done;
3919
3920   if (gst_structure_get_double (action->structure, "repeat",
3921           (gdouble *) & repeat))
3922     goto done;
3923
3924   repeat_expr = gst_validate_replace_variables_in_string (action,
3925       scenario->priv->vars, gst_structure_get_string (action->structure,
3926           "repeat"), GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3927   if (!repeat_expr) {
3928     gst_validate_error_structure (action, "Invalid value for 'repeat'");
3929     return FALSE;
3930   }
3931
3932   repeat = gst_validate_utils_parse_expression (repeat_expr, _set_variable_func,
3933       scenario, &error);
3934   if (error) {
3935     gst_validate_error_structure (action, "Invalid value for 'repeat': %s",
3936         error);
3937     g_free (error);
3938     return FALSE;
3939   }
3940   g_free (repeat_expr);
3941
3942 done:
3943   gst_structure_remove_field (action->structure, "repeat");
3944   gst_structure_remove_field (action->priv->main_structure, "repeat");
3945
3946   action->repeat = 0;
3947   GST_VALIDATE_ACTION_N_REPEATS (action) = repeat;
3948
3949   position = g_list_index (scenario->priv->actions, action);
3950   g_assert (position >= 0);
3951   for (i = 1; i < repeat; i++) {
3952     GstValidateAction *copy = _action_copy (action);
3953
3954     copy->repeat = i;
3955     scenario->priv->actions =
3956         g_list_insert (scenario->priv->actions, copy, position + i);
3957   }
3958
3959   return TRUE;
3960 }
3961
3962 static GstValidateExecuteActionReturn
3963 gst_validate_action_default_prepare_func (GstValidateAction * action)
3964 {
3965   gint i;
3966   GstClockTime tmp;
3967   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3968   GstValidateActionType *type = gst_validate_get_action_type (action->type);
3969   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3970
3971   _update_well_known_vars (scenario);
3972   if (!gst_validate_action_setup_repeat (scenario, action))
3973     goto err;
3974
3975   if (GST_VALIDATE_ACTION_N_REPEATS (action)) {
3976     if (action->priv->it_value.g_type != 0) {
3977       gst_structure_set_value (scenario->priv->vars,
3978           GST_VALIDATE_ACTION_RANGE_NAME (action), &action->priv->it_value);
3979     } else {
3980       gst_structure_set (scenario->priv->vars,
3981           GST_VALIDATE_ACTION_RANGE_NAME (action) ?
3982           GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", G_TYPE_INT,
3983           action->repeat, NULL);
3984     }
3985   }
3986   gst_validate_structure_resolve_variables (action, action->structure,
3987       scenario->priv->vars, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3988   for (i = 0; type->parameters[i].name; i++) {
3989     if (type->parameters[i].types
3990         && g_str_has_suffix (type->parameters[i].types, "(GstClockTime)"))
3991       gst_validate_action_get_clocktime (scenario, action,
3992           type->parameters[i].name, &tmp);
3993   }
3994
3995
3996 done:
3997   gst_clear_mini_object ((GstMiniObject **) & type);
3998   if (scenario)
3999     gst_object_unref (scenario);
4000
4001   return res;
4002 err:
4003   res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
4004   goto done;
4005 }
4006
4007 static GstValidateExecuteActionReturn
4008 gst_validate_set_property_prepare_func (GstValidateAction * action)
4009 {
4010   action->priv->optional = gst_structure_has_field_typed (action->structure,
4011       "on-all-instances", G_TYPE_BOOLEAN);
4012
4013   return gst_validate_action_default_prepare_func (action);
4014 }
4015
4016 static GList *
4017 add_gvalue_to_list_as_struct (gpointer source, GList * list, const GValue * v)
4018 {
4019   if (G_VALUE_HOLDS_STRING (v)) {
4020     GstStructure *structure =
4021         gst_structure_new_from_string (g_value_get_string (v));
4022
4023     if (!structure)
4024       gst_validate_error_structure (source, "Invalid structure: %s",
4025           g_value_get_string (v));
4026
4027     return g_list_append (list, structure);
4028   }
4029
4030   if (GST_VALUE_HOLDS_STRUCTURE (v))
4031     return g_list_append (list,
4032         gst_structure_copy (gst_value_get_structure (v)));
4033
4034
4035   gst_validate_error_structure (source, "Expected a string or a structure,"
4036       " got %s instead", gst_value_serialize (v));
4037   return NULL;
4038 }
4039
4040 static GList *
4041 gst_validate_utils_get_structures (gpointer source,
4042     GstStructure * str, const gchar * fieldname)
4043 {
4044   guint i, size;
4045   GList *res = NULL;
4046   const GValue *value = gst_structure_get_value (str, fieldname);
4047
4048   if (!value)
4049     return NULL;
4050
4051   if (G_VALUE_HOLDS_STRING (value) || GST_VALUE_HOLDS_STRUCTURE (value))
4052     return add_gvalue_to_list_as_struct (source, res, value);
4053
4054   if (!GST_VALUE_HOLDS_LIST (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
4055     g_error ("%s must have type list of structure/string (or a string), "
4056         "e.g. %s={ [struct1, a=val1], [struct2, a=val2] }, got: \"%s\" in %s",
4057         fieldname, fieldname, gst_value_serialize (value),
4058         gst_structure_to_string (str));
4059     return NULL;
4060   }
4061
4062   size =
4063       GST_VALUE_HOLDS_LIST (value) ? gst_value_list_get_size (value) :
4064       gst_value_array_get_size (value);
4065   for (i = 0; i < size; i++)
4066     res =
4067         add_gvalue_to_list_as_struct (source, res,
4068         GST_VALUE_HOLDS_LIST (value) ?
4069         gst_value_list_get_value (value, i) :
4070         gst_value_array_get_value (value, i));
4071
4072   return res;
4073 }
4074
4075 static GstValidateAction *
4076 gst_validate_create_subaction (GstValidateScenario * scenario,
4077     GstStructure * lvariables, GstValidateAction * action,
4078     GstStructure * nstruct, gint it, gint max)
4079 {
4080   GstValidateAction *subaction;
4081   GstValidateActionType *action_type =
4082       _find_action_type (gst_structure_get_name (nstruct));
4083
4084   if (!action_type)
4085     gst_validate_error_structure (action,
4086         "Unknown action type: '%s'", gst_structure_get_name (nstruct));
4087   subaction = gst_validate_action_new (scenario, action_type, nstruct, FALSE);
4088   GST_VALIDATE_ACTION_RANGE_NAME (subaction) =
4089       GST_VALIDATE_ACTION_RANGE_NAME (action);
4090   GST_VALIDATE_ACTION_FILENAME (subaction) =
4091       g_strdup (GST_VALIDATE_ACTION_FILENAME (action));
4092   GST_VALIDATE_ACTION_DEBUG (subaction) =
4093       g_strdup (GST_VALIDATE_ACTION_DEBUG (action));
4094   GST_VALIDATE_ACTION_LINENO (subaction) = GST_VALIDATE_ACTION_LINENO (action);
4095   subaction->repeat = it;
4096   subaction->priv->subaction_level = action->priv->subaction_level + 1;
4097   GST_VALIDATE_ACTION_N_REPEATS (subaction) = max;
4098   gst_validate_structure_resolve_variables (subaction, subaction->structure,
4099       lvariables,
4100       GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_LOCAL_ONLY |
4101       GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_FAILURE |
4102       GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_EXPRESSION);
4103   gst_structure_free (nstruct);
4104
4105   return subaction;
4106 }
4107
4108 static GstValidateExecuteActionReturn
4109 gst_validate_foreach_prepare (GstValidateAction * action)
4110 {
4111   gint it, i;
4112   gint min = 0, max = 1, step = 1;
4113   const GValue *it_array = NULL;
4114   GstValidateScenario *scenario;
4115   GList *actions, *tmp;
4116
4117   scenario = gst_validate_action_get_scenario (action);
4118   g_assert (scenario);
4119   _update_well_known_vars (scenario);
4120   gst_validate_action_setup_repeat (scenario, action);
4121
4122   GST_VALIDATE_ACTION_RANGE_NAME (action) = NULL;
4123   gst_structure_foreach (action->structure,
4124       (GstStructureForeachFunc) _foreach_find_iterator, action);
4125
4126   /* Allow using the repeat field here too */
4127   if (!GST_VALIDATE_ACTION_RANGE_NAME (action)
4128       && !GST_VALIDATE_ACTION_N_REPEATS (action))
4129     gst_validate_error_structure (action, "Missing range specifier field.");
4130
4131   if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
4132     const GValue *it_value = gst_structure_get_value (action->structure,
4133         GST_VALIDATE_ACTION_RANGE_NAME (action));
4134
4135     if (GST_VALUE_HOLDS_INT_RANGE (it_value)) {
4136       min = gst_value_get_int_range_min (it_value);
4137       max = gst_value_get_int_range_max (it_value);
4138       step = gst_value_get_int_range_step (it_value);
4139
4140       if (min % step != 0)
4141         gst_validate_error_structure (action,
4142             "Range min[%d] must be a multiple of step[%d].", min, step);
4143
4144       if (max % step != 0)
4145         gst_validate_error_structure (action,
4146             "Range max[%d] must be a multiple of step[%d].", max, step);
4147     } else {
4148       it_array = it_value;
4149       max = gst_value_array_get_size (it_array);
4150     }
4151   } else {
4152     min = action->repeat;
4153     max = action->repeat + 1;
4154   }
4155
4156   actions = gst_validate_utils_get_structures (action, action->structure,
4157       "actions");
4158   i = g_list_index (scenario->priv->actions, action);
4159   for (it = min; it < max; it = it + step) {
4160     GstStructure *lvariables = gst_structure_new_empty ("vars");
4161     const GValue *it_value = NULL;
4162
4163     if (it_array) {
4164       it_value = gst_value_array_get_value (it_array, it);
4165
4166       gst_structure_set_value (lvariables,
4167           GST_VALIDATE_ACTION_RANGE_NAME (action), it_value);
4168
4169     }
4170
4171     for (tmp = actions; tmp; tmp = tmp->next) {
4172       GstValidateAction *subact =
4173           gst_validate_create_subaction (scenario, lvariables, action,
4174           gst_structure_copy (tmp->data), it, max);
4175       scenario->priv->actions =
4176           g_list_insert (scenario->priv->actions, subact, i++);
4177       if (it_value) {
4178         g_value_init (&subact->priv->it_value, G_VALUE_TYPE (it_value));
4179         g_value_copy (it_value, &subact->priv->it_value);
4180       }
4181     }
4182
4183     gst_structure_free (lvariables);
4184   }
4185   g_list_free_full (actions, (GDestroyNotify) gst_structure_free);
4186
4187   scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
4188   gst_structure_remove_field (action->structure, "actions");
4189
4190   gst_object_unref (scenario);
4191   return GST_VALIDATE_EXECUTE_ACTION_DONE;
4192 }
4193
4194 static gboolean
4195 _check_structure_has_expected_value (GQuark field_id, const GValue * value,
4196     GstStructure * message_struct)
4197 {
4198   const GValue *v = gst_structure_id_get_value (message_struct, field_id);
4199
4200   if (!v) {
4201     gst_structure_set (message_struct, "__validate_has_expected_values",
4202         G_TYPE_BOOLEAN, FALSE, NULL);
4203     return FALSE;
4204   }
4205
4206   if (gst_value_compare (value, v) != GST_VALUE_EQUAL) {
4207     gst_structure_set (message_struct, "__validate_has_expected_values",
4208         G_TYPE_BOOLEAN, FALSE, NULL);
4209     return FALSE;
4210   }
4211
4212   gst_structure_set (message_struct, "__validate_has_expected_values",
4213       G_TYPE_BOOLEAN, TRUE, NULL);
4214
4215   return TRUE;
4216 }
4217
4218 static void
4219 _check_waiting_for_message (GstValidateScenario * scenario,
4220     GstMessage * message)
4221 {
4222   GstStructure *expected_values = NULL;
4223   GstValidateScenarioPrivate *priv = scenario->priv;
4224   const gchar *message_type;
4225
4226   if (!priv->wait_message_action) {
4227     GST_LOG_OBJECT (scenario, "Not waiting for message");
4228     return;
4229   }
4230
4231   message_type = gst_structure_get_string (priv->wait_message_action->structure,
4232       "message-type");
4233
4234   if (g_strcmp0 (message_type, GST_MESSAGE_TYPE_NAME (message)))
4235     return;
4236
4237   GST_LOG_OBJECT (scenario, " Waiting for %s and got %s", message_type,
4238       GST_MESSAGE_TYPE_NAME (message));
4239
4240   gst_structure_get (priv->wait_message_action->structure, "expected-values",
4241       GST_TYPE_STRUCTURE, &expected_values, NULL);
4242   if (expected_values) {
4243     gboolean res = FALSE;
4244     GstStructure *message_struct =
4245         (GstStructure *) gst_message_get_structure (message);
4246
4247     message_struct =
4248         message_struct ? gst_structure_copy (message_struct) : NULL;
4249     if (!message_struct) {
4250       GST_DEBUG_OBJECT (scenario,
4251           "Waiting for %" GST_PTR_FORMAT " but message has no structure.",
4252           priv->wait_message_action->structure);
4253       return;
4254     }
4255
4256     gst_structure_set (message_struct, "__validate_has_expected_values",
4257         G_TYPE_BOOLEAN, FALSE, NULL);
4258     gst_structure_foreach (expected_values,
4259         (GstStructureForeachFunc) _check_structure_has_expected_value,
4260         message_struct);
4261
4262     if (!gst_structure_get_boolean (message_struct,
4263             "__validate_has_expected_values", &res) || !res) {
4264       return;
4265     }
4266   }
4267
4268   gst_validate_action_set_done (priv->wait_message_action);
4269   _add_execute_actions_gsource (scenario);
4270 }
4271
4272 static gboolean
4273 streams_list_contain (GList * streams, const gchar * stream_id)
4274 {
4275   GList *l;
4276
4277   for (l = streams; l; l = g_list_next (l)) {
4278     GstStream *s = l->data;
4279
4280     if (!g_strcmp0 (s->stream_id, stream_id))
4281       return TRUE;
4282   }
4283
4284   return FALSE;
4285 }
4286
4287 static void
4288 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
4289     GstElement * pipeline)
4290 {
4291   GstValidateScenarioPrivate *priv = scenario->priv;
4292   GstQuery *query;
4293   GstClockTime min_latency;
4294
4295   query = gst_query_new_latency ();
4296   if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
4297     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4298         "Failed to perform LATENCY query");
4299     gst_query_unref (query);
4300     return;
4301   }
4302
4303   gst_query_parse_latency (query, NULL, &min_latency, NULL);
4304   gst_query_unref (query);
4305   GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
4306       " max allowed: %" GST_TIME_FORMAT,
4307       GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
4308
4309   if (priv->max_latency != GST_CLOCK_TIME_NONE &&
4310       min_latency > priv->max_latency) {
4311     GST_VALIDATE_REPORT (scenario, CONFIG_LATENCY_TOO_HIGH,
4312         "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
4313         GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
4314         GST_TIME_ARGS (priv->max_latency));
4315   }
4316 }
4317
4318 static gboolean
4319 gst_validate_scenario_is_flush_seeking (GstValidateScenario * scenario)
4320 {
4321   GstValidateSeekInformation *seekinfo = scenario->priv->current_seek;
4322
4323   if (!seekinfo)
4324     return FALSE;
4325
4326   if (!(seekinfo->flags & GST_SEEK_FLAG_FLUSH))
4327     return FALSE;
4328
4329   return seekinfo->action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4330 }
4331
4332 static void
4333 gst_validate_scenario_reset (GstValidateScenario * scenario)
4334 {
4335   /* Reset sink information */
4336   SCENARIO_LOCK (scenario);
4337   g_list_foreach (scenario->priv->sinks, (GFunc) _reset_sink_information, NULL);
4338   /* Reset current seek */
4339   scenario->priv->current_seek = NULL;
4340   scenario->priv->current_seqnum = GST_SEQNUM_INVALID;
4341   SCENARIO_UNLOCK (scenario);
4342 }
4343
4344 typedef struct
4345 {
4346   GstValidateScenario *scenario;
4347   GstMessage *message;
4348 } MessageData;
4349
4350 static void
4351 message_data_free (MessageData * d)
4352 {
4353   gst_message_unref (d->message);
4354   gst_object_unref (d->scenario);
4355
4356   g_free (d);
4357 }
4358
4359 static gboolean
4360 handle_bus_message (MessageData * d)
4361 {
4362   gboolean is_error = FALSE;
4363   GstMessage *message = d->message;
4364   GstValidateScenario *scenario = d->scenario;
4365   GstValidateScenarioPrivate *priv = scenario->priv;
4366   GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
4367
4368   if (!pipeline) {
4369     GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
4370     return G_SOURCE_REMOVE;
4371   }
4372
4373   GST_DEBUG_OBJECT (scenario, "message %" GST_PTR_FORMAT, message);
4374
4375   switch (GST_MESSAGE_TYPE (message)) {
4376     case GST_MESSAGE_ASYNC_DONE:
4377       if (!gst_validate_scenario_is_flush_seeking (scenario) &&
4378           priv->needs_async_done) {
4379         priv->needs_async_done = FALSE;
4380         if (priv->actions && _action_sets_state (priv->actions->data)
4381             && !priv->changing_state)
4382           gst_validate_action_set_done (priv->actions->data);
4383       }
4384
4385       if (priv->needs_playback_parsing) {
4386         priv->needs_playback_parsing = FALSE;
4387         if (!gst_validate_parse_next_action_playback_time (scenario))
4388           return G_SOURCE_REMOVE;
4389       }
4390       _add_execute_actions_gsource (scenario);
4391       break;
4392     case GST_MESSAGE_STATE_CHANGED:
4393     {
4394       GstState old_state, state, pending_state;
4395       gboolean reached_state;
4396
4397       if (!pipeline || GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline))
4398         break;
4399
4400       gst_message_parse_state_changed (message, &old_state, &state,
4401           &pending_state);
4402
4403       reached_state = pending_state == GST_STATE_VOID_PENDING;
4404
4405       if (old_state == GST_STATE_PAUSED && state == GST_STATE_READY)
4406         gst_validate_scenario_reset (scenario);
4407
4408       if (reached_state && gst_validate_scenario_is_flush_seeking (scenario))
4409         gst_validate_action_set_done (priv->current_seek->action);
4410
4411       if (priv->changing_state && priv->target_state == state) {
4412         priv->changing_state = FALSE;
4413         if (priv->actions && _action_sets_state (priv->actions->data)
4414             && reached_state)
4415           gst_validate_action_set_done (priv->actions->data);
4416       }
4417
4418       if (old_state == GST_STATE_READY && state == GST_STATE_PAUSED)
4419         _add_execute_actions_gsource (scenario);
4420
4421       /* GstBin only send a new latency message when reaching PLAYING if
4422        * async-handling=true so check the latency manually. */
4423       if (state == GST_STATE_PLAYING)
4424         gst_validate_scenario_check_latency (scenario, pipeline);
4425       break;
4426     }
4427     case GST_MESSAGE_ERROR:
4428       is_error = TRUE;
4429
4430       /* Passthrough */
4431     case GST_MESSAGE_EOS:
4432     {
4433       GstValidateAction *stop_action;
4434       GstValidateActionType *stop_action_type;
4435       GstStructure *s;
4436
4437       if (!is_error && priv->ignore_eos) {
4438         GST_INFO_OBJECT (scenario, "Got EOS but ignoring it!");
4439         goto done;
4440       }
4441
4442       if (is_error && priv->allow_errors) {
4443         GST_INFO_OBJECT (scenario, "Got error but ignoring it!");
4444         if (scenario->priv->needs_async_done || scenario->priv->changing_state) {
4445
4446           if (scenario->priv->actions) {
4447             GstValidateAction *act =
4448                 gst_validate_action_ref (scenario->priv->actions->data);
4449
4450             GST_VALIDATE_REPORT_ACTION (scenario, act,
4451                 SCENARIO_ACTION_EXECUTION_ERROR,
4452                 "Error message happened while executing action");
4453             gst_validate_action_set_done (act);
4454
4455             gst_validate_action_unref (act);
4456           }
4457
4458           scenario->priv->needs_async_done = scenario->priv->changing_state =
4459               FALSE;
4460         }
4461         goto done;
4462       }
4463
4464       GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
4465       {
4466         /* gst_validate_action_set_done() does not finish the action
4467          * immediately. Instead, it posts a task to the main thread to do most
4468          * of the work in _action_set_done().
4469          *
4470          * While the EOS handling lock guarantees that if an action had to call
4471          * gst_validate_action_set_done() it has done so, it does not guarantee
4472          * that _action_set_done() has been called.
4473          *
4474          * Is it possible that this handler is run before _action_set_done(), so
4475          * we check at this point for actions that have a pending_set_done and
4476          * call it before continuing. */
4477         GList *actions = g_list_copy (priv->actions);
4478         GList *i;
4479         for (i = actions; i; i = i->next) {
4480           GstValidateAction *action = (GstValidateAction *) i->data;
4481           if (action->priv->pending_set_done)
4482             _action_set_done (action);
4483         }
4484         g_list_free (actions);
4485       }
4486
4487       if (!is_error) {
4488         priv->got_eos = TRUE;
4489         if (priv->wait_message_action) {
4490
4491           if (priv->actions && priv->actions->next) {
4492             GST_DEBUG_OBJECT (scenario,
4493                 "Waiting for a message and got a next action"
4494                 " to execute, letting it a chance!");
4495             GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4496             goto done;
4497           } else {
4498             /* Clear current message wait if waiting for EOS */
4499             _check_waiting_for_message (scenario, message);
4500           }
4501         }
4502       }
4503
4504       SCENARIO_LOCK (scenario);
4505       /* Make sure that if there is an ASYNC_DONE in the message queue, we do not
4506          take it into account */
4507       g_list_free_full (priv->seeks,
4508           (GDestroyNotify) gst_validate_seek_information_free);
4509       priv->seeks = NULL;
4510       SCENARIO_UNLOCK (scenario);
4511
4512       GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
4513
4514       stop_action_type = _find_action_type ("stop");
4515       s = gst_structure_new ("stop", "generated-after-eos", G_TYPE_BOOLEAN,
4516           !is_error, "generated-after-error", G_TYPE_BOOLEAN, is_error, NULL);
4517       stop_action = gst_validate_action_new (scenario, stop_action_type,
4518           s, FALSE);
4519       gst_structure_free (s);
4520       gst_validate_execute_action (stop_action_type, stop_action);
4521       gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
4522
4523       GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4524       break;
4525     }
4526     case GST_MESSAGE_BUFFERING:
4527     {
4528       gint percent;
4529
4530       gst_message_parse_buffering (message, &percent);
4531
4532       if (percent == 100)
4533         priv->buffering = FALSE;
4534       else
4535         priv->buffering = TRUE;
4536       break;
4537     }
4538     case GST_MESSAGE_STREAMS_SELECTED:
4539     {
4540       guint i;
4541       GList *streams_selected = NULL;
4542
4543       for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
4544         GstStream *stream =
4545             gst_message_streams_selected_get_stream (message, i);
4546
4547         streams_selected = g_list_append (streams_selected, stream);
4548       }
4549
4550       /* Is there a pending switch-track action waiting for the new streams to
4551        * be selected? */
4552       if (priv->pending_switch_track) {
4553         GList *expected, *l;
4554         GstValidateScenario *scenario =
4555             gst_validate_action_get_scenario (priv->pending_switch_track);
4556
4557         expected =
4558             gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
4559             (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
4560
4561         if (g_list_length (expected) != g_list_length (streams_selected)) {
4562           GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4563               SCENARIO_ACTION_EXECUTION_ERROR,
4564               "Was expecting %d selected streams but got %d",
4565               g_list_length (expected), g_list_length (streams_selected));
4566           goto action_done;
4567         }
4568
4569         for (l = expected; l; l = g_list_next (l)) {
4570           const gchar *stream_id = l->data;
4571
4572           if (!streams_list_contain (streams_selected, stream_id)) {
4573             GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4574                 SCENARIO_ACTION_EXECUTION_ERROR,
4575                 "Stream %s has not be activated", stream_id);
4576             goto action_done;
4577           }
4578         }
4579
4580       action_done:
4581         gst_object_unref (scenario);
4582         gst_validate_action_set_done (priv->pending_switch_track);
4583         priv->pending_switch_track = NULL;
4584       }
4585
4586       g_list_free_full (streams_selected, gst_object_unref);
4587       break;
4588     }
4589     case GST_MESSAGE_LATENCY:
4590       gst_validate_scenario_check_latency (scenario, pipeline);
4591       break;
4592
4593     case GST_MESSAGE_QOS:
4594     {
4595       guint64 dropped;
4596
4597       /* Check the maximum allowed when scenario is terminating so the report
4598        * will include the actual number of dropped buffers. */
4599       gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
4600       if (dropped != -1)
4601         priv->dropped = dropped;
4602       break;
4603     }
4604     case GST_MESSAGE_APPLICATION:
4605     {
4606       const GstStructure *s;
4607       s = gst_message_get_structure (message);
4608       if (gst_structure_has_name (s, "validate-segment")) {
4609         GstValidateSinkInformation *sink_info;
4610
4611         SCENARIO_LOCK (scenario);
4612         sink_info =
4613             _find_sink_information (scenario,
4614             (GstElement *) GST_MESSAGE_SRC (message));
4615
4616         if (sink_info) {
4617           const GValue *segment_value;
4618           const GstSegment *segment;
4619
4620           GST_DEBUG_OBJECT (scenario, "Got segment update for %s",
4621               GST_ELEMENT_NAME (sink_info->sink));
4622           sink_info->segment_seqnum = GST_MESSAGE_SEQNUM (message);
4623           segment_value = gst_structure_get_value (s, "segment");
4624           g_assert (segment_value != NULL);
4625           segment = (const GstSegment *) g_value_get_boxed (segment_value);
4626           gst_segment_copy_into (segment, &sink_info->segment);
4627           _validate_sink_information (scenario);
4628         }
4629         SCENARIO_UNLOCK (scenario);
4630       }
4631     }
4632     default:
4633       break;
4634   }
4635
4636 done:
4637   gst_object_unref (pipeline);
4638   /* Check if we got the message expected by a wait action */
4639   _check_waiting_for_message (scenario, message);
4640
4641   execute_next_action_full (scenario, message);
4642
4643   return G_SOURCE_REMOVE;
4644 }
4645
4646 static void
4647 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
4648 {
4649   MessageData *d = g_new0 (MessageData, 1);
4650
4651   d->message = gst_message_ref (message);
4652   d->scenario = gst_object_ref (scenario);
4653
4654   g_main_context_invoke_full (scenario->priv->context,
4655       G_PRIORITY_DEFAULT_IDLE,
4656       (GSourceFunc) handle_bus_message, d, (GDestroyNotify) message_data_free);
4657 }
4658
4659 static gboolean
4660 _action_type_has_parameter (GstValidateActionType * atype,
4661     const gchar * paramname)
4662 {
4663   gint i;
4664
4665   if (!atype->parameters)
4666     return FALSE;
4667
4668   for (i = 0; atype->parameters[i].name; i++)
4669     if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
4670       return TRUE;
4671
4672   return FALSE;
4673 }
4674
4675 static gboolean
4676 gst_validate_scenario_load_structures (GstValidateScenario * scenario,
4677     GList * structures, gboolean * is_config, gchar * origin_file)
4678 {
4679   gboolean ret = TRUE;
4680   GList *tmp;
4681   GstValidateScenarioPrivate *priv = scenario->priv;
4682   GList *config;
4683
4684   *is_config = FALSE;
4685
4686   if (!structures) {
4687     GST_INFO_OBJECT (scenario, "No structures provided");
4688     return FALSE;
4689   }
4690
4691   for (tmp = structures; tmp; tmp = tmp->next) {
4692     GstValidateAction *action;
4693     GstValidateActionType *action_type;
4694     const gchar *type;
4695     gboolean on_clock = FALSE;
4696     GstStructure *structure = (GstStructure *) tmp->data;
4697
4698     type = gst_structure_get_name (structure);
4699     if (!g_strcmp0 (type, "description") || !g_strcmp0 (type, "meta")) {
4700       const gchar *pipeline_name;
4701
4702       gst_structure_get_boolean (structure, "is-config", is_config);
4703       gst_structure_get_boolean (structure, "handles-states",
4704           &priv->handles_state);
4705       gst_structure_get_boolean (structure, "ignore-eos", &priv->ignore_eos);
4706       gst_structure_get_boolean (structure, "allow-errors",
4707           &priv->allow_errors);
4708       gst_structure_get_boolean (structure, "actions-on-idle",
4709           &priv->execute_on_idle);
4710
4711       if (!priv->handles_state)
4712         priv->target_state = GST_STATE_PLAYING;
4713
4714       pipeline_name = gst_structure_get_string (structure, "pipeline-name");
4715       if (pipeline_name) {
4716         g_free (priv->pipeline_name);
4717         priv->pipeline_name = g_strdup (pipeline_name);
4718       }
4719
4720       gst_validate_utils_get_clocktime (structure, "max-latency",
4721           &priv->max_latency);
4722
4723       gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
4724       scenario->description = gst_structure_copy (structure);
4725
4726       continue;
4727     } else if (!(action_type = _find_action_type (type))) {
4728       if (gst_structure_has_field (structure, "optional-action-type")) {
4729         GST_INFO_OBJECT (scenario,
4730             "Action type not found %s but marked as not mandatory", type);
4731         continue;
4732       }
4733
4734       gst_validate_error_structure (structure,
4735           "Unknown action type: '%s'", type);
4736       goto failed;
4737     }
4738
4739     gst_structure_get_boolean (structure, "on-clock", &on_clock);
4740     if ((!g_strcmp0 (type, "crank-clock") || on_clock) && !priv->clock)
4741       priv->clock = GST_TEST_CLOCK (gst_test_clock_new ());
4742
4743     if (action_type->parameters) {
4744       guint i;
4745
4746       for (i = 0; action_type->parameters[i].name; i++) {
4747         if (action_type->parameters[i].mandatory &&
4748             gst_structure_has_field (structure,
4749                 action_type->parameters[i].name) == FALSE) {
4750           gst_validate_error_structure (structure,
4751               "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
4752               action_type->parameters[i].name, structure);
4753           goto failed;
4754         }
4755       }
4756     }
4757
4758     action = gst_validate_action_new (scenario, action_type, structure, TRUE);
4759     if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
4760       GST_ERROR_OBJECT (scenario, "Newly created action: %" GST_PTR_FORMAT
4761           " was in error state", structure);
4762
4763       goto failed;
4764     }
4765
4766     action->action_number = priv->num_actions++;
4767
4768     if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
4769       GST_DEBUG_OBJECT (scenario,
4770           "Unrefing action that has already been executed");
4771       gst_validate_action_unref (action);
4772       action = NULL;
4773     }
4774   }
4775
4776   /* max-latency and max-dropped can be overridden using config */
4777   for (config = gst_validate_plugin_get_config (NULL); config;
4778       config = g_list_next (config)) {
4779     GstClockTime max_latency;
4780
4781     gst_validate_utils_get_clocktime (config->data, "max-latency",
4782         &max_latency);
4783     if (GST_CLOCK_TIME_IS_VALID (max_latency))
4784       priv->max_latency = max_latency;
4785
4786     gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
4787   }
4788
4789 done:
4790   g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
4791
4792   return ret;
4793
4794 failed:
4795   ret = FALSE;
4796
4797   goto done;
4798 }
4799
4800 gchar **
4801 gst_validate_scenario_get_include_paths (const gchar * relative_scenario)
4802 {
4803   gint n;
4804   gchar **env_scenariodir;
4805   gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
4806
4807   if (relative_scenario) {
4808     gchar *relative_dir = g_path_get_dirname (relative_scenario);
4809     gchar *tmp_scenarios_path =
4810         g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
4811         relative_dir);
4812     g_free (relative_dir);
4813
4814     g_free (scenarios_path);
4815     scenarios_path = tmp_scenarios_path;
4816   }
4817
4818   env_scenariodir =
4819       scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
4820       0) : NULL;
4821   g_free (scenarios_path);
4822
4823   n = env_scenariodir ? g_strv_length (env_scenariodir) : 0;
4824   env_scenariodir = g_realloc_n (env_scenariodir, n + 3, sizeof (gchar *));
4825   env_scenariodir[n] = g_build_filename (g_get_user_data_dir (),
4826       "gstreamer-" GST_API_VERSION, "validate",
4827       GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4828   env_scenariodir[n + 1] =
4829       g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION, "validate",
4830       GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4831   env_scenariodir[n + 2] = NULL;
4832
4833   return env_scenariodir;
4834 }
4835
4836 static gboolean
4837 _load_scenario_file (GstValidateScenario * scenario,
4838     gchar * scenario_file, gboolean * is_config)
4839 {
4840   return gst_validate_scenario_load_structures (scenario,
4841       gst_validate_utils_structs_parse_from_filename (scenario_file,
4842           (GstValidateGetIncludePathsFunc)
4843           gst_validate_scenario_get_include_paths, NULL), is_config,
4844       scenario_file);
4845 }
4846
4847 static gboolean
4848 gst_validate_scenario_load (GstValidateScenario * scenario,
4849     const gchar * scenario_name)
4850 {
4851   gchar **scenarios = NULL;
4852   guint i;
4853   gboolean found_actions = FALSE, is_config, ret = FALSE;
4854   gchar **include_paths = gst_validate_scenario_get_include_paths (NULL);
4855
4856   if (!scenario_name)
4857     goto invalid_name;
4858
4859   scenarios = g_strsplit (scenario_name, ":", -1);
4860
4861   for (i = 0; scenarios[i]; i++) {
4862     guint include_i;
4863     gchar *lfilename = NULL, *tldir = NULL, *scenario_file = NULL;
4864
4865     ret = FALSE;
4866
4867     /* First check if the scenario name is not a full path to the
4868      * actual scenario */
4869     if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
4870       GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
4871           "Trying to load it", scenarios[i]);
4872       if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config))) {
4873         scenario_file = scenarios[i];
4874         goto check_scenario;
4875       }
4876     }
4877
4878     if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
4879       lfilename = g_strdup (scenarios[i]);
4880     else
4881       lfilename =
4882           g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
4883
4884     for (include_i = 0; include_paths[include_i]; include_i++) {
4885       tldir = g_build_filename (include_paths[include_i], lfilename, NULL);
4886       if ((ret = _load_scenario_file (scenario, tldir, &is_config))) {
4887         scenario_file = tldir;
4888         break;
4889       }
4890       g_free (tldir);
4891     }
4892
4893     if (!ret)
4894       goto error;
4895
4896     /* else check scenario */
4897   check_scenario:
4898     if (!is_config) {
4899       gchar *scenario_dir = g_path_get_dirname (scenario_file);
4900       gchar *scenario_fname = g_path_get_basename (scenario_file);
4901       gchar **scenario_name =
4902           g_regex_split_simple ("\\.scenario", scenario_fname, 0, 0);
4903
4904       gst_structure_set (scenario->priv->vars,
4905           "SCENARIO_DIR", G_TYPE_STRING, scenario_dir,
4906           "SCENARIO_NAME", G_TYPE_STRING, scenario_name[0],
4907           "SCENARIO_PATH", G_TYPE_STRING, scenario_file, NULL);
4908
4909       g_free (scenario_dir);
4910       g_free (scenario_fname);
4911       g_strfreev (scenario_name);
4912     }
4913
4914     g_free (tldir);
4915     g_free (lfilename);
4916
4917     if (!is_config) {
4918       if (found_actions == TRUE)
4919         goto one_actions_scenario_max;
4920       else
4921         found_actions = TRUE;
4922     }
4923   }
4924
4925 done:
4926
4927   if (include_paths)
4928     g_strfreev (include_paths);
4929
4930   g_strfreev (scenarios);
4931
4932   if (ret == FALSE)
4933     gst_validate_abort ("Could not set scenario %s => EXIT\n", scenario_name);
4934
4935   return ret;
4936
4937 invalid_name:
4938   {
4939     GST_ERROR ("Invalid name for scenario '%s'", GST_STR_NULL (scenario_name));
4940   error:
4941     ret = FALSE;
4942     goto done;
4943   }
4944 one_actions_scenario_max:
4945   {
4946     GST_ERROR ("You can set at most only one action scenario. "
4947         "You can have several config scenarios though (a config scenario's "
4948         "file must have is-config=true, and all its actions must be executable "
4949         "at parsing time).");
4950     ret = FALSE;
4951     goto done;
4952
4953   }
4954 }
4955
4956
4957 static void
4958 gst_validate_scenario_set_property (GObject * object, guint prop_id,
4959     const GValue * value, GParamSpec * pspec)
4960 {
4961   GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4962
4963   switch (prop_id) {
4964     case PROP_RUNNER:
4965       /* we assume the runner is valid as long as this scenario is,
4966        * no ref taken */
4967       gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
4968           g_value_get_object (value));
4969       break;
4970     case PROP_HANDLES_STATE:
4971       g_assert_not_reached ();
4972       break;
4973     case PROP_EXECUTE_ON_IDLE:
4974       self->priv->execute_on_idle = g_value_get_boolean (value);
4975       break;
4976     default:
4977       break;
4978   }
4979 }
4980
4981 static void
4982 gst_validate_scenario_get_property (GObject * object, guint prop_id,
4983     GValue * value, GParamSpec * pspec)
4984 {
4985   GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4986
4987   switch (prop_id) {
4988     case PROP_RUNNER:
4989       /* we assume the runner is valid as long as this scenario is,
4990        * no ref taken */
4991       g_value_take_object (value,
4992           gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
4993       break;
4994     case PROP_HANDLES_STATE:
4995       g_value_set_boolean (value, self->priv->handles_state);
4996       break;
4997     case PROP_EXECUTE_ON_IDLE:
4998       g_value_set_boolean (value, self->priv->execute_on_idle);
4999       break;
5000     default:
5001       break;
5002   }
5003 }
5004
5005 static void
5006 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
5007 {
5008   GObjectClass *object_class = G_OBJECT_CLASS (klass);
5009
5010   object_class->dispose = gst_validate_scenario_dispose;
5011   object_class->finalize = gst_validate_scenario_finalize;
5012
5013   object_class->get_property = gst_validate_scenario_get_property;
5014   object_class->set_property = gst_validate_scenario_set_property;
5015
5016   g_object_class_install_property (object_class, PROP_RUNNER,
5017       g_param_spec_object ("validate-runner", "VALIDATE Runner",
5018           "The Validate runner to report errors to",
5019           GST_TYPE_VALIDATE_RUNNER,
5020           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
5021
5022   g_object_class_install_property (object_class, PROP_HANDLES_STATE,
5023       g_param_spec_boolean ("handles-states", "Handles state",
5024           "True if the application should not handle the first state change. "
5025           "False if it is application responsibility",
5026           FALSE, G_PARAM_READABLE));
5027
5028   g_object_class_install_property (object_class,
5029       PROP_EXECUTE_ON_IDLE,
5030       g_param_spec_boolean ("execute-on-idle",
5031           "Force waiting between actions",
5032           "Always execute actions on idle and do not chain them to execute as"
5033           " fast as possible. Setting this property is useful if action"
5034           " execution can lead to the addition of new sources on the same main"
5035           " loop as it provides these new GSource a chance to be dispatched"
5036           " between actions", FALSE, G_PARAM_READWRITE));
5037
5038   /**
5039    * GstValidateScenario::done:
5040    * @scenario: The scenario running
5041    *
5042    * Emitted once all actions have been executed
5043    */
5044   scenario_signals[DONE] =
5045       g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
5046       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
5047
5048   /**
5049    * GstValidateScenario::action-done:
5050    * @scenario: The scenario running
5051    * @action: The #GstValidateAction that is done running
5052    *
5053    * Emitted when an action is done.
5054    *
5055    * Since: 1.20
5056    */
5057   scenario_signals[ACTION_DONE] =
5058       g_signal_new ("action-done", G_TYPE_FROM_CLASS (klass),
5059       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
5060       GST_TYPE_VALIDATE_ACTION);
5061 }
5062
5063 static void
5064 gst_validate_scenario_init (GstValidateScenario * scenario)
5065 {
5066   GstValidateScenarioPrivate *priv = scenario->priv =
5067       gst_validate_scenario_get_instance_private (scenario);
5068
5069   priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
5070   priv->segment_start = 0;
5071   priv->segment_stop = GST_CLOCK_TIME_NONE;
5072   priv->current_seek = NULL;
5073   priv->current_seqnum = GST_SEQNUM_INVALID;
5074   priv->action_execution_interval = 10;
5075   priv->vars = gst_structure_new_empty ("vars");
5076   priv->needs_playback_parsing = TRUE;
5077   g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
5078   priv->max_latency = GST_CLOCK_TIME_NONE;
5079   priv->max_dropped = -1;
5080   priv->clock = NULL;
5081
5082   g_mutex_init (&priv->lock);
5083
5084   scenario->priv->context = g_main_context_get_thread_default ();
5085   if (!scenario->priv->context)
5086     scenario->priv->context = g_main_context_default ();
5087   g_main_context_ref (scenario->priv->context);
5088 }
5089
5090 static void
5091 gst_validate_scenario_dispose (GObject * object)
5092 {
5093   GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
5094
5095   g_weak_ref_clear (&priv->ref_pipeline);
5096
5097   if (priv->bus) {
5098     gst_bus_remove_signal_watch (priv->bus);
5099     gst_object_unref (priv->bus);
5100     priv->bus = NULL;
5101   }
5102
5103   gst_object_replace ((GstObject **) & priv->clock, NULL);
5104
5105   G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
5106 }
5107
5108 static void
5109 gst_validate_scenario_finalize (GObject * object)
5110 {
5111   GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
5112   GstValidateScenarioPrivate *priv = self->priv;
5113
5114   /* Because g_object_add_weak_pointer() is used, this MUST be on the
5115    * main thread. */
5116   g_assert (g_main_context_acquire (priv->context));
5117   g_main_context_release (priv->context);
5118
5119   g_main_context_unref (priv->context);
5120   priv->context = NULL;
5121
5122   g_list_free_full (priv->seeks,
5123       (GDestroyNotify) gst_validate_seek_information_free);
5124   g_list_free_full (priv->sinks,
5125       (GDestroyNotify) gst_validate_sink_information_free);
5126   g_list_free_full (priv->actions, (GDestroyNotify) gst_validate_action_unref);
5127   g_list_free_full (priv->non_blocking_running_actions,
5128       (GDestroyNotify) gst_validate_action_unref);
5129   g_list_free_full (priv->on_addition_actions,
5130       (GDestroyNotify) gst_validate_action_unref);
5131   g_free (priv->pipeline_name);
5132   gst_structure_free (priv->vars);
5133   if (self->description)
5134     gst_structure_free (self->description);
5135   g_mutex_clear (&priv->lock);
5136
5137   G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
5138 }
5139
5140 static void _element_added_cb (GstBin * bin, GstElement * element,
5141     GstValidateScenario * scenario);
5142 static void _element_removed_cb (GstBin * bin, GstElement * element,
5143     GstValidateScenario * scenario);
5144
5145 static void
5146 iterate_children (GstValidateScenario * scenario, GstBin * bin)
5147 {
5148   GstIterator *it;
5149   GValue v = G_VALUE_INIT;
5150   gboolean done = FALSE;
5151   GHashTable *called;           /* set of GstElement on which we already called _element_added_cb() */
5152
5153   called = g_hash_table_new (NULL, NULL);
5154   it = gst_bin_iterate_elements (bin);
5155
5156   while (!done) {
5157     switch (gst_iterator_next (it, &v)) {
5158       case GST_ITERATOR_OK:{
5159         GstElement *child = g_value_get_object (&v);
5160
5161         if (g_hash_table_lookup (called, child) == NULL) {
5162           _element_added_cb (bin, child, scenario);
5163           g_hash_table_add (called, child);
5164         }
5165         g_value_reset (&v);
5166       }
5167         break;
5168       case GST_ITERATOR_RESYNC:
5169         gst_iterator_resync (it);
5170         break;
5171       case GST_ITERATOR_ERROR:
5172       case GST_ITERATOR_DONE:
5173         done = TRUE;
5174     }
5175   }
5176   g_value_reset (&v);
5177   gst_iterator_free (it);
5178   g_hash_table_unref (called);
5179 }
5180
5181 static gboolean
5182 should_execute_action (GstElement * element, GstValidateAction * action)
5183 {
5184   return gst_validate_element_matches_target (element, action->structure);
5185 }
5186
5187 /* Returns TRUE if:
5188  * * The element has no parent (pipeline)
5189  * * Or it's a sink*/
5190 static gboolean
5191 _all_parents_are_sink (GstElement * element)
5192 {
5193   if (GST_OBJECT_PARENT (element) == NULL)
5194     return TRUE;
5195
5196   if (!GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK))
5197     return FALSE;
5198
5199   return _all_parents_are_sink ((GstElement *) GST_OBJECT_PARENT (element));
5200 }
5201
5202 static void
5203 _element_removed_cb (GstBin * bin, GstElement * element,
5204     GstValidateScenario * scenario)
5205 {
5206   GstValidateScenarioPrivate *priv = scenario->priv;
5207
5208   if (GST_IS_BASE_SINK (element)) {
5209     GstValidateSinkInformation *sink_info;
5210     SCENARIO_LOCK (scenario);
5211     sink_info = _find_sink_information (scenario, element);
5212     if (sink_info) {
5213       GST_DEBUG_OBJECT (scenario, "Removing sink information for %s",
5214           GST_ELEMENT_NAME (element));
5215       priv->sinks = g_list_remove (priv->sinks, sink_info);
5216       gst_validate_sink_information_free (sink_info);
5217     }
5218     SCENARIO_UNLOCK (scenario);
5219   }
5220 }
5221
5222 static void
5223 _element_added_cb (GstBin * bin, GstElement * element,
5224     GstValidateScenario * scenario)
5225 {
5226   GList *tmp;
5227
5228   GstValidateScenarioPrivate *priv = scenario->priv;
5229
5230   /* Check if it's an element we track for a set-property action */
5231   SCENARIO_LOCK (scenario);
5232   tmp = priv->on_addition_actions;
5233   while (tmp) {
5234     GstValidateAction *action = (GstValidateAction *) tmp->data;
5235
5236     if (action->playback_time != GST_CLOCK_TIME_NONE)
5237       break;
5238     if (g_strcmp0 (action->type, "set-property"))
5239       break;
5240
5241     GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
5242         action, action->type);
5243     if (should_execute_action (element, action)) {
5244       GstValidateActionType *action_type;
5245       action_type = _find_action_type (action->type);
5246       GST_DEBUG_OBJECT (element, "Executing set-property action");
5247       if (gst_validate_execute_action (action_type, action)) {
5248         if (!gst_structure_has_field_typed (action->structure,
5249                 "on-all-instances", G_TYPE_BOOLEAN)) {
5250           priv->on_addition_actions =
5251               g_list_remove_link (priv->on_addition_actions, tmp);
5252           gst_mini_object_unref (GST_MINI_OBJECT (action));
5253           g_list_free (tmp);
5254           tmp = priv->on_addition_actions;
5255         } else {
5256           tmp = tmp->next;
5257         }
5258       } else
5259         tmp = tmp->next;
5260     } else
5261       tmp = tmp->next;
5262   }
5263
5264   /* If it's a new GstBaseSink, add to list of sink information */
5265   if (GST_IS_BASE_SINK (element) && _all_parents_are_sink (element)) {
5266     GstValidateSinkInformation *sink_info =
5267         g_new0 (GstValidateSinkInformation, 1);
5268     GST_DEBUG_OBJECT (scenario, "Adding %s to list of tracked sinks",
5269         GST_ELEMENT_NAME (element));
5270     sink_info->sink = gst_object_ref (element);
5271     priv->sinks = g_list_append (priv->sinks, sink_info);
5272   }
5273
5274   SCENARIO_UNLOCK (scenario);
5275
5276   /* If it's a bin, listen to the child */
5277   if (GST_IS_BIN (element)) {
5278     g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
5279         scenario);
5280     g_signal_connect (element, "element-removed",
5281         (GCallback) _element_removed_cb, scenario);
5282     iterate_children (scenario, GST_BIN (element));
5283   }
5284 }
5285
5286 static GstValidateScenario *
5287 gst_validate_scenario_new (GstValidateRunner *
5288     runner, GstElement * pipeline, gchar * scenario_name, GList * structures)
5289 {
5290   GList *config;
5291   GstValidateScenario *scenario =
5292       g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
5293       runner, NULL);
5294
5295   if (structures) {
5296     gboolean is_config;
5297     gst_validate_scenario_load_structures (scenario, structures, &is_config,
5298         scenario_name);
5299   } else {
5300
5301     GST_LOG ("Creating scenario %s", scenario_name);
5302     if (!gst_validate_scenario_load (scenario, scenario_name)) {
5303       g_object_unref (scenario);
5304
5305       return NULL;
5306     }
5307   }
5308
5309   if (scenario->priv->pipeline_name &&
5310       !g_pattern_match_simple (scenario->priv->pipeline_name,
5311           GST_OBJECT_NAME (pipeline))) {
5312     GST_INFO ("Scenario %s only applies on pipeline %s not %s",
5313         scenario_name, scenario->priv->pipeline_name,
5314         GST_OBJECT_NAME (pipeline));
5315
5316     gst_object_unref (scenario);
5317
5318     return NULL;
5319   }
5320
5321   gst_validate_printf (NULL,
5322       "**-> Running scenario %s on pipeline %s**\n", scenario_name,
5323       GST_OBJECT_NAME (pipeline));
5324
5325   g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
5326   if (scenario->priv->clock) {
5327     gst_element_set_clock (pipeline, GST_CLOCK_CAST (scenario->priv->clock));
5328     gst_pipeline_use_clock (GST_PIPELINE (pipeline),
5329         GST_CLOCK_CAST (scenario->priv->clock));
5330   }
5331   gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
5332       g_filename_display_basename (scenario_name));
5333
5334   g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
5335       scenario);
5336   g_signal_connect (pipeline, "element-removed",
5337       (GCallback) _element_removed_cb, scenario);
5338
5339   iterate_children (scenario, GST_BIN (pipeline));
5340
5341   scenario->priv->bus = gst_element_get_bus (pipeline);
5342   gst_bus_add_signal_watch (scenario->priv->bus);
5343   g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
5344       scenario);
5345
5346   for (config = gst_validate_plugin_get_config (NULL); config;
5347       config = config->next) {
5348     gint interval;
5349
5350     if (gst_structure_get_uint (config->data,
5351             "scenario-action-execution-interval",
5352             &scenario->priv->action_execution_interval)) {
5353       GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5354           scenario->priv->action_execution_interval);
5355       if (scenario->priv->action_execution_interval > 0)
5356         scenario->priv->execute_on_idle = TRUE;
5357       break;
5358     } else if (gst_structure_get_int (config->data,
5359             "scenario-action-execution-interval", &interval)) {
5360       if (interval > 0) {
5361         scenario->priv->action_execution_interval = (guint) interval;
5362         scenario->priv->execute_on_idle = TRUE;
5363         GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5364             scenario->priv->action_execution_interval);
5365         break;
5366       } else {
5367         GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
5368       }
5369     }
5370   }
5371
5372   if (scenario->priv->handles_state) {
5373     GST_INFO_OBJECT (scenario, "Scenario handles state."
5374         " Starting the get position source");
5375     _add_execute_actions_gsource (scenario);
5376   }
5377
5378   scenario->priv->overrides =
5379       gst_validate_override_registry_get_override_for_names
5380       (gst_validate_override_registry_get (), "scenarios", NULL);
5381
5382   return scenario;
5383 }
5384
5385 GstValidateScenario *
5386 gst_validate_scenario_from_structs (GstValidateRunner * runner,
5387     GstElement * pipeline, GList * structures, gchar * origin_file)
5388 {
5389   g_return_val_if_fail (structures, NULL);
5390
5391   return gst_validate_scenario_new (runner, pipeline, origin_file, structures);
5392 }
5393
5394 /**
5395  * gst_validate_scenario_factory_create:
5396  * @runner: The #GstValidateRunner to use to report issues
5397  * @pipeline: The pipeline to run the scenario on
5398  * @scenario_name: The name (or path) of the scenario to run
5399  *
5400  * Returns: (transfer full) (nullable): A #GstValidateScenario or NULL
5401  */
5402 GstValidateScenario *
5403 gst_validate_scenario_factory_create (GstValidateRunner *
5404     runner, GstElement * pipeline, const gchar * scenario_name)
5405 {
5406   return gst_validate_scenario_new (runner, pipeline, (gchar *) scenario_name,
5407       NULL);
5408 }
5409
5410 static gboolean
5411 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
5412 {
5413   gchar *tmp = gst_value_serialize (value);
5414   gchar *tmpcompress = g_strcompress (tmp);
5415
5416   g_key_file_set_string (kfg->kf, kfg->group_name,
5417       g_quark_to_string (field_id), tmpcompress);
5418
5419   g_free (tmpcompress);
5420   g_free (tmp);
5421
5422   return TRUE;
5423 }
5424
5425 gboolean
5426 gst_validate_scenario_check_and_set_needs_clock_sync (GList * structures,
5427     GstStructure ** meta)
5428 {
5429   gboolean needs_clock_sync = FALSE;
5430   GList *tmp;
5431
5432   for (tmp = structures; tmp; tmp = tmp->next) {
5433     GstStructure *_struct = (GstStructure *) tmp->data;
5434     gboolean is_meta = gst_structure_has_name (_struct, "description")
5435         || gst_structure_has_name (_struct, "meta");
5436
5437     if (!is_meta) {
5438       GstValidateActionType *type =
5439           _find_action_type (gst_structure_get_name (_struct));
5440
5441       if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
5442         needs_clock_sync = TRUE;
5443       continue;
5444     }
5445
5446     if (!*meta)
5447       *meta = gst_structure_copy (_struct);
5448   }
5449
5450   if (needs_clock_sync) {
5451     if (*meta)
5452       gst_structure_set (*meta, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
5453     else
5454       *meta = gst_structure_from_string ("description, need-clock-sync=true;",
5455           NULL);
5456   }
5457
5458   return needs_clock_sync;
5459 }
5460
5461 static gboolean
5462 _parse_scenario (GFile * f, GKeyFile * kf)
5463 {
5464   gboolean ret = FALSE;
5465   gchar *path = g_file_get_path (f);
5466
5467   if (g_str_has_suffix (path, GST_VALIDATE_SCENARIO_SUFFIX)
5468       || g_str_has_suffix (path, GST_VALIDATE_VALIDATE_TEST_SUFFIX)) {
5469     GstStructure *meta = NULL;
5470     GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f,
5471         (GstValidateGetIncludePathsFunc)
5472         gst_validate_scenario_get_include_paths);
5473
5474     gst_validate_scenario_check_and_set_needs_clock_sync (structures, &meta);
5475     for (tmp = structures; tmp; tmp = tmp->next)
5476       gst_structure_remove_fields (tmp->data, "__lineno__", "__filename__",
5477           "__debug__", NULL);
5478
5479     if (meta) {
5480       KeyFileGroupName kfg;
5481
5482       kfg.group_name = g_file_get_path (f);
5483       kfg.kf = kf;
5484
5485       gst_structure_remove_fields (meta, "__lineno__", "__filename__",
5486           "__debug__", NULL);
5487       gst_structure_foreach (meta,
5488           (GstStructureForeachFunc) _add_description, &kfg);
5489       gst_structure_free (meta);
5490       g_free (kfg.group_name);
5491     } else {
5492       g_key_file_set_string (kf, path, "noinfo", "nothing");
5493     }
5494     g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
5495
5496     ret = TRUE;
5497   }
5498
5499   g_free (path);
5500   return ret;
5501 }
5502
5503 static void
5504 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
5505 {
5506   GFileEnumerator *fenum;
5507   GFileInfo *info;
5508
5509   fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
5510       G_FILE_QUERY_INFO_NONE, NULL, NULL);
5511
5512   if (fenum == NULL)
5513     return;
5514
5515   for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
5516       info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
5517     GFile *f = g_file_enumerator_get_child (fenum, info);
5518
5519     _parse_scenario (f, kf);
5520     gst_object_unref (f);
5521   }
5522
5523   gst_object_unref (fenum);
5524 }
5525
5526 gboolean
5527 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
5528     gchar * output_file)
5529 {
5530   gchar *result;
5531   gsize datalength;
5532
5533   GError *err = NULL;
5534   GKeyFile *kf = NULL;
5535   gint res = 0;
5536   const gchar *envvar;
5537   gchar **env_scenariodir = NULL;
5538   gchar *tldir = g_build_filename (g_get_user_data_dir (),
5539       "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
5540       NULL);
5541   GFile *dir = g_file_new_for_path (tldir);
5542   g_free (tldir);
5543
5544   kf = g_key_file_new ();
5545   if (num_scenarios > 0) {
5546     gint i;
5547     GFile *file;
5548
5549     for (i = 0; i < num_scenarios; i++) {
5550       file = g_file_new_for_path (scenarios[i]);
5551       if (!_parse_scenario (file, kf)) {
5552         GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
5553
5554         res = 1;
5555       }
5556       g_clear_object (&file);
5557     }
5558
5559     goto done;
5560   }
5561
5562   envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
5563   if (envvar)
5564     env_scenariodir = g_strsplit (envvar, ":", 0);
5565
5566   _list_scenarios_in_dir (dir, kf);
5567   g_clear_object (&dir);
5568
5569   tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
5570       "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
5571   dir = g_file_new_for_path (tldir);
5572   _list_scenarios_in_dir (dir, kf);
5573   g_clear_object (&dir);
5574   g_free (tldir);
5575
5576   if (env_scenariodir) {
5577     guint i;
5578     GFile *subfile;
5579
5580     for (i = 0; env_scenariodir[i]; i++) {
5581       subfile = g_file_new_for_path (env_scenariodir[i]);
5582       _list_scenarios_in_dir (subfile, kf);
5583       g_clear_object (&subfile);
5584     }
5585   }
5586
5587   /* Hack to make it work within the development environment */
5588   dir = g_file_new_for_path ("data/scenarios");
5589   _list_scenarios_in_dir (dir, kf);
5590   g_clear_object (&dir);
5591
5592 done:
5593   result = g_key_file_to_data (kf, &datalength, &err);
5594   gst_validate_printf (NULL, "All scenarios available:\n%s", result);
5595
5596   if (output_file && !err) {
5597     if (!g_file_set_contents (output_file, result, datalength, &err)) {
5598       GST_WARNING ("Error writing to file '%s'", output_file);
5599     }
5600   }
5601
5602   g_free (result);
5603
5604   if (env_scenariodir)
5605     g_strfreev (env_scenariodir);
5606
5607   if (err) {
5608     GST_WARNING ("Got error '%s' listing scenarios", err->message);
5609     g_clear_error (&err);
5610
5611     res = FALSE;
5612   }
5613   g_clear_object (&dir);
5614
5615   g_key_file_free (kf);
5616
5617   return res;
5618 }
5619
5620 static GstValidateActionReturn
5621 check_last_sample_internal (GstValidateScenario * scenario,
5622     GstValidateAction * action, GstElement * sink)
5623 {
5624   GstSample *sample;
5625   gchar *sum;
5626   GstBuffer *buffer;
5627   const gchar *target_sum;
5628   guint64 frame_number;
5629   GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
5630   GstVideoTimeCodeMeta *tc_meta;
5631
5632   g_object_get (sink, "last-sample", &sample, NULL);
5633   if (sample == NULL) {
5634     GST_VALIDATE_REPORT_ACTION (scenario, action,
5635         SCENARIO_ACTION_EXECUTION_ERROR,
5636         "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
5637         " 'last-sample' property is NULL"
5638         ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
5639         sink);
5640
5641     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5642   }
5643
5644   buffer = gst_sample_get_buffer (sample);
5645   target_sum = gst_structure_get_string (action->structure, "checksum");
5646   if (target_sum) {
5647     GstMapInfo map;
5648
5649     if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
5650       GST_VALIDATE_REPORT_ACTION (scenario, action,
5651           SCENARIO_ACTION_EXECUTION_ERROR,
5652           "Last sample buffer could not be mapped, action can't run.");
5653       res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5654       goto done;
5655     }
5656     sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
5657     gst_buffer_unmap (buffer, &map);
5658
5659     if (g_strcmp0 (sum, target_sum)) {
5660       GST_VALIDATE_REPORT_ACTION (scenario, action,
5661           SCENARIO_ACTION_EXECUTION_ERROR,
5662           "Last buffer checksum '%s' is different than the expected one: '%s'",
5663           sum, target_sum);
5664
5665       res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5666     }
5667     g_free (sum);
5668
5669     goto done;
5670   }
5671
5672   if (!gst_structure_get_uint64 (action->structure, "timecode-frame-number",
5673           &frame_number)) {
5674     gint iframe_number;
5675
5676     if (!gst_structure_get_int (action->structure, "timecode-frame-number",
5677             &iframe_number)) {
5678       GST_VALIDATE_REPORT_ACTION (scenario, action,
5679           SCENARIO_ACTION_EXECUTION_ERROR,
5680           "The 'checksum' or 'time-code-frame-number' parameters of the "
5681           "`check-last-sample` action type needs to be specified, none found");
5682
5683       res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5684       goto done;
5685     }
5686
5687     frame_number = (guint64) iframe_number;
5688   }
5689
5690   tc_meta = gst_buffer_get_video_time_code_meta (buffer);
5691   if (!tc_meta) {
5692     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5693         "Could not \"check-last-sample\" as the buffer doesn't contain a TimeCode"
5694         " meta");
5695     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5696     goto done;
5697   }
5698
5699   if (gst_video_time_code_frames_since_daily_jam (&tc_meta->tc) != frame_number) {
5700     GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5701         "Last buffer frame number '%" G_GINT64_FORMAT
5702         "' is different than the expected one: '%" G_GINT64_FORMAT "'",
5703         gst_video_time_code_frames_since_daily_jam (&tc_meta->tc),
5704         frame_number);
5705     res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5706   }
5707
5708 done:
5709   gst_sample_unref (sample);
5710   return res;
5711 }
5712
5713 static void
5714 sink_last_sample_notify_cb (GstElement * sink, GParamSpec * arg G_GNUC_UNUSED,
5715     GstValidateAction * action)
5716 {
5717   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5718
5719   if (!scenario) {
5720     GST_VALIDATE_REPORT_ACTION (scenario, action,
5721         SCENARIO_ACTION_EXECUTION_ERROR,
5722         "No pipeline anymore, can't check last sample");
5723     goto done;
5724   }
5725
5726   check_last_sample_internal (scenario, action, sink);
5727   gst_object_unref (scenario);
5728
5729 done:
5730   g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5731       action);
5732   gst_validate_action_set_done (action);
5733   gst_validate_action_unref (action);
5734 }
5735
5736 static GstValidateExecuteActionReturn
5737 _check_last_sample_value (GstValidateScenario * scenario,
5738     GstValidateAction * action, GstElement * sink)
5739 {
5740   GstSample *sample;
5741
5742   /* Connect before checking last sample to avoid a race where
5743    * the sample is set between the time we connect and the time
5744    * the time we get it */
5745   g_signal_connect (sink, "notify::last-sample",
5746       G_CALLBACK (sink_last_sample_notify_cb),
5747       gst_validate_action_ref (action));
5748
5749   g_object_get (sink, "last-sample", &sample, NULL);
5750   if (sample == NULL)
5751     return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
5752   gst_sample_unref (sample);
5753   gst_validate_action_unref (action);
5754
5755   g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5756       action);
5757
5758   return check_last_sample_internal (scenario, action, sink);
5759 }
5760
5761 static gboolean
5762 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
5763     const gchar * fname, GstCaps * sinkpad_caps)
5764 {
5765   GstCaps *tmpcaps;
5766   GstPad *sinkpad;
5767   GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
5768   GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
5769
5770   if (!paramspec)
5771     return FALSE;
5772
5773   if (paramspec->value_type != GST_TYPE_SAMPLE)
5774     return FALSE;
5775
5776   if (!name && !fname && !sinkpad_caps)
5777     return TRUE;
5778
5779   if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
5780     return TRUE;
5781
5782   if (fname
5783       && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
5784     return TRUE;
5785
5786   if (!sinkpad_caps)
5787     return FALSE;
5788
5789   sinkpad = gst_element_get_static_pad (sink, "sink");
5790   if (!sinkpad)
5791     return FALSE;
5792
5793   tmpcaps = gst_pad_get_current_caps (sinkpad);
5794   if (tmpcaps) {
5795     gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
5796
5797     GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
5798     gst_caps_unref (tmpcaps);
5799
5800     return res;
5801   } else {
5802     GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
5803   }
5804
5805   return FALSE;
5806 }
5807
5808 static GstValidateExecuteActionReturn
5809 _execute_check_last_sample (GstValidateScenario * scenario,
5810     GstValidateAction * action)
5811 {
5812   GstIterator *it;
5813   GValue data = { 0, };
5814   gboolean done = FALSE;
5815   GstCaps *caps = NULL;
5816   GstElement *sink = NULL, *tmpelement;
5817   const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
5818       *factory_name =
5819       gst_structure_get_string (action->structure, "sink-factory-name"),
5820       *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
5821   DECLARE_AND_GET_PIPELINE (scenario, action);
5822
5823   if (caps_str) {
5824     caps = gst_caps_from_string (caps_str);
5825
5826     g_assert (caps);
5827   }
5828
5829   it = gst_bin_iterate_recurse (GST_BIN (pipeline));
5830   while (!done) {
5831     switch (gst_iterator_next (it, &data)) {
5832       case GST_ITERATOR_OK:
5833         tmpelement = g_value_get_object (&data);
5834         if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
5835                 caps)) {
5836           if (sink) {
5837             if (!gst_object_has_as_ancestor (GST_OBJECT (tmpelement),
5838                     GST_OBJECT (sink))) {
5839               gchar *tmp = gst_structure_to_string (action->structure);
5840
5841               GST_VALIDATE_REPORT_ACTION (scenario, action,
5842                   SCENARIO_ACTION_EXECUTION_ERROR,
5843                   "Could not \"check-last-sample\" as several elements were found "
5844                   "from describing string: '%s' (%s and %s match)", tmp,
5845                   GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
5846
5847               g_free (tmp);
5848             }
5849
5850             gst_object_unref (sink);
5851           }
5852
5853           sink = gst_object_ref (tmpelement);
5854         }
5855         g_value_reset (&data);
5856         break;
5857       case GST_ITERATOR_RESYNC:
5858         gst_iterator_resync (it);
5859         g_clear_object (&sink);
5860         break;
5861       case GST_ITERATOR_ERROR:
5862         /* Fallthrough */
5863       case GST_ITERATOR_DONE:
5864         done = TRUE;
5865         break;
5866     }
5867   }
5868   gst_iterator_free (it);
5869   if (caps)
5870     gst_caps_unref (caps);
5871
5872   if (!sink) {
5873     GST_VALIDATE_REPORT_ACTION (scenario, action,
5874         SCENARIO_ACTION_EXECUTION_ERROR,
5875         "Could not \"check-last-sample\" as no sink was found from description: '%"
5876         GST_PTR_FORMAT "'", action->structure);
5877
5878     goto error;
5879   }
5880
5881   g_clear_object (&pipeline);
5882   return _check_last_sample_value (scenario, action, sink);
5883
5884 error:
5885   g_clear_object (&sink);
5886   g_clear_object (&pipeline);
5887   return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5888 }
5889
5890 static GstPadProbeReturn
5891 _check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info,
5892     GstValidateAction * action)
5893 {
5894   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5895   GstClockTime target_running_time = GST_CLOCK_TIME_NONE;
5896   gint count_bufs = 0;
5897
5898   gst_validate_action_get_clocktime (scenario, action,
5899       "running-time", &target_running_time);
5900   if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
5901     if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info)))
5902       gst_structure_set (action->structure, "__priv_seen_event", G_TYPE_BOOLEAN,
5903           TRUE, NULL);
5904     else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT
5905         && GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
5906       const GstSegment *segment = NULL;
5907
5908       gst_event_parse_segment (info->data, &segment);
5909       gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5910           segment, NULL);
5911     }
5912   } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))
5913       && gst_structure_has_field_typed (action->structure, "__priv_seen_event",
5914           G_TYPE_BOOLEAN)) {
5915     GstSegment *segment = NULL;
5916
5917     if (GST_CLOCK_TIME_IS_VALID (target_running_time)) {
5918       GstClockTime running_time;
5919
5920       gst_structure_get (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5921           &segment, NULL);
5922       running_time =
5923           gst_segment_to_running_time (segment, GST_FORMAT_TIME,
5924           GST_BUFFER_TIMESTAMP (info->data));
5925
5926       if (running_time < target_running_time)
5927         goto done;
5928     }
5929
5930     gst_structure_get_int (action->structure, "__priv_count_bufs", &count_bufs);
5931     if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info),
5932             GST_BUFFER_FLAG_DELTA_UNIT)) {
5933       if (count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) {
5934         GST_VALIDATE_REPORT_ACTION (scenario, action,
5935             SCENARIO_ACTION_EXECUTION_ERROR,
5936             "Did not receive a key frame after requested one, "
5937             "at running_time %" GST_TIME_FORMAT " (with a %i "
5938             "frame tolerance)", GST_TIME_ARGS (target_running_time),
5939             NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE);
5940
5941         gst_validate_action_set_done (action);
5942         gst_object_unref (scenario);
5943         return GST_PAD_PROBE_REMOVE;
5944       }
5945
5946       gst_structure_set (action->structure, "__priv_count_bufs", G_TYPE_INT,
5947           count_bufs++, NULL);
5948     } else {
5949       GST_INFO_OBJECT (pad,
5950           "Properly got keyframe after \"force-keyframe\" event "
5951           "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))",
5952           GST_TIME_ARGS (target_running_time), count_bufs);
5953
5954       gst_structure_remove_fields (action->structure, "__priv_count_bufs",
5955           "__priv_segment", "__priv_seen_event", NULL);
5956       gst_validate_action_set_done (action);
5957       gst_object_unref (scenario);
5958       return GST_PAD_PROBE_REMOVE;
5959     }
5960   }
5961 done:
5962   gst_object_unref (scenario);
5963
5964   return GST_PAD_PROBE_OK;
5965 }
5966
5967 static GstValidateExecuteActionReturn
5968 _execute_crank_clock (GstValidateScenario * scenario,
5969     GstValidateAction * action)
5970 {
5971   GstClockTime expected_diff, expected_time;
5972   GstClockTime prev_time =
5973       gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
5974
5975   if (!gst_test_clock_crank (scenario->priv->clock)) {
5976     GST_VALIDATE_REPORT_ACTION (scenario, action,
5977         SCENARIO_ACTION_EXECUTION_ERROR, "Cranking clock failed");
5978
5979     return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5980   }
5981
5982   if (gst_validate_action_get_clocktime (scenario, action,
5983           "expected-elapsed-time", &expected_diff)) {
5984     GstClockTime elapsed =
5985         gst_clock_get_time (GST_CLOCK (scenario->priv->clock)) - prev_time;
5986
5987     if (expected_diff != elapsed) {
5988       GST_VALIDATE_REPORT_ACTION (scenario, action,
5989           SCENARIO_ACTION_EXECUTION_ERROR,
5990           "Elapsed time during test clock cranking different than expected,"
5991           " waited for %" GST_TIME_FORMAT " instead of the expected %"
5992           GST_TIME_FORMAT, GST_TIME_ARGS (elapsed),
5993           GST_TIME_ARGS (expected_diff));
5994
5995       return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5996     }
5997   }
5998
5999   if (gst_validate_action_get_clocktime (scenario, action, "expected-time",
6000           &expected_time)) {
6001     GstClockTime time = gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
6002
6003     if (expected_time != time) {
6004       GST_VALIDATE_REPORT_ACTION (scenario, action,
6005           SCENARIO_ACTION_EXECUTION_ERROR,
6006           "Clock time after cranking different than expected,"
6007           " got %" GST_TIME_FORMAT " instead of the expected %" GST_TIME_FORMAT,
6008           GST_TIME_ARGS (time), GST_TIME_ARGS (expected_time));
6009
6010       return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
6011     }
6012   }
6013
6014   return GST_VALIDATE_EXECUTE_ACTION_OK;
6015 }
6016
6017 static gboolean
6018 _execute_request_key_unit (GstValidateScenario * scenario,
6019     GstValidateAction * action)
6020 {
6021   guint count = 0;
6022   gboolean all_headers = FALSE;
6023   gboolean ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
6024   GstEvent *event = NULL;
6025   GstQuery *segment_query;
6026   GList *targets = NULL, *tmp;
6027   GstElement *video_encoder = NULL;
6028   GstPad *pad = NULL, *encoder_srcpad = NULL;
6029   GstClockTime running_time = GST_CLOCK_TIME_NONE;
6030   GstSegment segment = { 0, };
6031   const gchar *direction = gst_structure_get_string (action->structure,
6032       "direction"), *pad_name, *srcpad_name;
6033
6034   DECLARE_AND_GET_PIPELINE (scenario, action);
6035
6036   if (gst_structure_get_string (action->structure, "target-element-name")) {
6037     GstElement *target = _get_target_element (scenario, action);
6038     if (target == NULL)
6039       return FALSE;
6040
6041     targets = g_list_append (targets, target);
6042   } else {
6043     if (!gst_structure_get_string (action->structure,
6044             "target-element-klass") &&
6045         !gst_structure_get_string (action->structure,
6046             "target-element-factory-name")) {
6047       gst_structure_set (action->structure, "target-element-klass",
6048           G_TYPE_STRING, "Video/Encoder", NULL);
6049     }
6050
6051     targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
6052   }
6053
6054   if (!targets) {
6055     GST_VALIDATE_REPORT_ACTION (scenario, action,
6056         SCENARIO_ACTION_EXECUTION_ERROR,
6057         "Could not find any element from action: %" GST_PTR_FORMAT,
6058         action->structure);
6059     goto fail;
6060   }
6061
6062   gst_validate_action_get_clocktime (scenario, action,
6063       "running-time", &running_time);
6064   gst_structure_get_boolean (action->structure, "all-headers", &all_headers);
6065   if (!gst_structure_get_uint (action->structure, "count", &count)) {
6066     gst_structure_get_int (action->structure, "count", (gint *) & count);
6067   }
6068   pad_name = gst_structure_get_string (action->structure, "pad");
6069   srcpad_name = gst_structure_get_string (action->structure, "srcpad");
6070   if (!srcpad_name)
6071     srcpad_name = "src";
6072
6073   for (tmp = targets; tmp; tmp = tmp->next) {
6074     video_encoder = tmp->data;
6075     encoder_srcpad = gst_element_get_static_pad (video_encoder, srcpad_name);
6076     if (!encoder_srcpad) {
6077       GST_VALIDATE_REPORT_ACTION (scenario, action,
6078           SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
6079           srcpad_name);
6080
6081       goto fail;
6082     }
6083     if (g_strcmp0 (direction, "upstream") == 0) {
6084       event = gst_video_event_new_upstream_force_key_unit (running_time,
6085           all_headers, count);
6086
6087       pad = gst_element_get_static_pad (video_encoder, srcpad_name);
6088       if (!pad) {
6089         GST_VALIDATE_REPORT_ACTION (scenario, action,
6090             SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
6091             srcpad_name);
6092
6093         goto fail;
6094       }
6095       GST_ERROR_OBJECT (encoder_srcpad, "Sending RequestKeyUnit event");
6096       gst_pad_add_probe (encoder_srcpad,
6097           GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
6098           (GstPadProbeCallback) _check_is_key_unit_cb,
6099           gst_validate_action_ref (action),
6100           (GDestroyNotify) gst_validate_action_unref);
6101     } else if (g_strcmp0 (direction, "downstream") == 0) {
6102       GstClockTime timestamp = GST_CLOCK_TIME_NONE,
6103           stream_time = GST_CLOCK_TIME_NONE;
6104
6105       if (!pad_name)
6106         pad_name = "sink";
6107
6108       pad = gst_element_get_static_pad (video_encoder, pad_name);
6109       if (!pad) {
6110         GST_VALIDATE_REPORT_ACTION (scenario, action,
6111             SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s", pad_name);
6112
6113         goto fail;
6114       }
6115
6116       gst_validate_action_get_clocktime (scenario, action,
6117           "timestamp", &timestamp);
6118
6119       gst_validate_action_get_clocktime (scenario, action,
6120           "stream-time", &stream_time);
6121
6122       event =
6123           gst_video_event_new_downstream_force_key_unit (timestamp, stream_time,
6124           running_time, all_headers, count);
6125
6126       gst_pad_add_probe (pad,
6127           GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6128           (GstPadProbeCallback) _check_is_key_unit_cb,
6129           gst_validate_action_ref (action),
6130           (GDestroyNotify) gst_validate_action_unref);
6131     } else {
6132       GST_VALIDATE_REPORT_ACTION (scenario, action,
6133           SCENARIO_ACTION_EXECUTION_ERROR,
6134           "request keyunit direction %s invalid (should be in"
6135           " [downstrean, upstream]", direction);
6136
6137       goto fail;
6138     }
6139
6140     gst_validate_printf (action, "Sending a \"force key unit\" event %s\n",
6141         direction);
6142
6143     segment_query = gst_query_new_segment (GST_FORMAT_TIME);
6144     gst_pad_query (encoder_srcpad, segment_query);
6145
6146     gst_query_parse_segment (segment_query, &(segment.rate),
6147         &(segment.format), (gint64 *) & (segment.start),
6148         (gint64 *) & (segment.stop));
6149     gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
6150         &segment, NULL);
6151
6152     gst_pad_add_probe (encoder_srcpad,
6153         GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6154         (GstPadProbeCallback) _check_is_key_unit_cb,
6155         gst_validate_action_ref (action),
6156         (GDestroyNotify) gst_validate_action_unref);
6157
6158
6159     if (!gst_pad_send_event (pad, event)) {
6160       GST_VALIDATE_REPORT_ACTION (scenario, action,
6161           SCENARIO_ACTION_EXECUTION_ERROR,
6162           "Could not send \"force key unit\" event %s", direction);
6163       goto fail;
6164     }
6165
6166     gst_clear_object (&pad);
6167     gst_clear_object (&encoder_srcpad);
6168   }
6169
6170 done:
6171   g_list_free_full (targets, gst_object_unref);
6172   gst_clear_object (&pad);
6173   gst_clear_object (&encoder_srcpad);
6174
6175   return ret;
6176
6177 fail:
6178   ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
6179   goto done;
6180 }
6181
6182 static GstValidateExecuteActionReturn
6183 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
6184 {
6185   GstBus *bus;
6186   GstValidateScenarioPrivate *priv = scenario->priv;
6187
6188   DECLARE_AND_GET_PIPELINE (scenario, action);
6189
6190   bus = gst_element_get_bus (pipeline);
6191   SCENARIO_LOCK (scenario);
6192   if (priv->execute_actions_source_id) {
6193     g_source_remove (priv->execute_actions_source_id);
6194     priv->execute_actions_source_id = 0;
6195   }
6196   if (scenario->priv->actions || scenario->priv->non_blocking_running_actions ||
6197       scenario->priv->on_addition_actions) {
6198     guint nb_actions = 0;
6199     gchar *actions = g_strdup (""), *tmpconcat;
6200     GList *tmp;
6201     GList *all_actions = g_list_concat (g_list_concat (scenario->priv->actions,
6202             scenario->priv->non_blocking_running_actions),
6203         scenario->priv->on_addition_actions);
6204
6205     for (tmp = all_actions; tmp; tmp = tmp->next) {
6206       GstValidateAction *remaining_action = (GstValidateAction *) tmp->data;
6207       GstValidateActionType *type;
6208
6209       if (remaining_action == action)
6210         continue;
6211
6212       type = _find_action_type (remaining_action->type);
6213
6214       tmpconcat = actions;
6215
6216       if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
6217           remaining_action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
6218           remaining_action->priv->optional) {
6219         gst_validate_action_unref (remaining_action);
6220
6221         continue;
6222       }
6223
6224       nb_actions++;
6225
6226       actions = g_strdup_printf ("%s\n%*s- `%s` at %s:%d", actions, 20, "",
6227           remaining_action->type,
6228           GST_VALIDATE_ACTION_FILENAME (remaining_action),
6229           GST_VALIDATE_ACTION_LINENO (remaining_action));
6230       gst_validate_action_unref (remaining_action);
6231       g_free (tmpconcat);
6232     }
6233     g_list_free (all_actions);
6234     scenario->priv->actions = NULL;
6235     scenario->priv->non_blocking_running_actions = NULL;
6236     scenario->priv->on_addition_actions = NULL;
6237
6238
6239     if (nb_actions > 0) {
6240       GstClockTime position = GST_CLOCK_TIME_NONE;
6241
6242       _get_position (scenario, NULL, &position);
6243       GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
6244           "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
6245           ")", nb_actions, actions, GST_TIME_ARGS (position));
6246     }
6247     g_free (actions);
6248   }
6249   SCENARIO_UNLOCK (scenario);
6250
6251   gst_validate_scenario_check_dropped (scenario);
6252
6253   gst_bus_post (bus,
6254       gst_message_new_request_state (GST_OBJECT_CAST (scenario),
6255           GST_STATE_NULL));
6256   gst_object_unref (bus);
6257   gst_object_unref (pipeline);
6258
6259   return TRUE;
6260 }
6261
6262 static gboolean
6263 _action_set_done (GstValidateAction * action)
6264 {
6265   gchar *repeat_message = NULL;
6266   JsonBuilder *jbuild;
6267   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6268
6269   if (scenario == NULL || !action->priv->pending_set_done)
6270     return G_SOURCE_REMOVE;
6271
6272   action->priv->execution_duration =
6273       gst_util_get_timestamp () - action->priv->execution_time;
6274
6275   jbuild = json_builder_new ();
6276   json_builder_begin_object (jbuild);
6277   json_builder_set_member_name (jbuild, "type");
6278   json_builder_add_string_value (jbuild, "action-done");
6279   json_builder_set_member_name (jbuild, "action-type");
6280   json_builder_add_string_value (jbuild, action->type);
6281   json_builder_set_member_name (jbuild, "execution-duration");
6282   json_builder_add_double_value (jbuild,
6283       ((gdouble) action->priv->execution_duration / GST_SECOND));
6284   json_builder_end_object (jbuild);
6285
6286   gst_validate_send (json_builder_get_root (jbuild));
6287   g_object_unref (jbuild);
6288
6289   action->priv->pending_set_done = FALSE;
6290   switch (action->priv->state) {
6291     case GST_VALIDATE_EXECUTE_ACTION_ERROR:
6292       GST_VALIDATE_REPORT_ACTION (scenario, action,
6293           SCENARIO_ACTION_EXECUTION_ERROR, "Action %s failed", action->type);
6294     case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
6295     case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
6296     case GST_VALIDATE_EXECUTE_ACTION_NONE:
6297     case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
6298     case GST_VALIDATE_EXECUTE_ACTION_OK:
6299     {
6300       scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
6301
6302       _check_scenario_is_done (scenario);
6303
6304       if (!gst_validate_parse_next_action_playback_time (scenario)) {
6305         gst_validate_error_structure (scenario->priv->actions ? scenario->
6306             priv->actions->data : NULL,
6307             "Could not determine next action playback time!");
6308       }
6309
6310       GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
6311           " executing next", action->structure);
6312
6313       break;
6314     }
6315     case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
6316       break;
6317   }
6318
6319   if (GST_VALIDATE_ACTION_N_REPEATS (action))
6320     repeat_message =
6321         g_strdup_printf ("[%d/%d]", action->repeat,
6322         GST_VALIDATE_ACTION_N_REPEATS (action));
6323
6324   gst_validate_printf (NULL,
6325       "%*c⇨ Action `%s` at %s:%d done '%s' %s (duration: %" GST_TIME_FORMAT
6326       ")\n\n", (action->priv->subaction_level * 2) - 1, ' ',
6327       gst_structure_get_name (action->priv->main_structure),
6328       GST_VALIDATE_ACTION_FILENAME (action),
6329       GST_VALIDATE_ACTION_LINENO (action),
6330       gst_validate_action_return_get_name (action->priv->state),
6331       repeat_message ? repeat_message : "",
6332       GST_TIME_ARGS (action->priv->execution_duration));
6333   g_free (repeat_message);
6334
6335   g_signal_emit (scenario, scenario_signals[ACTION_DONE], 0, action);
6336   if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING)
6337     /* We took the 'scenario' reference... unreffing it now */
6338     gst_validate_action_unref (action);
6339
6340   action->priv->state = GST_VALIDATE_EXECUTE_ACTION_DONE;
6341   gst_validate_scenario_execute_next_or_restart_looping (scenario);
6342   gst_object_unref (scenario);
6343   return G_SOURCE_REMOVE;
6344 }
6345
6346 /* gst_validate_action_set_done:
6347  * @action: The action that is done executing
6348  *
6349  * Sets @action as "done", meaning that the next action can
6350  * now be executed.
6351  */
6352 void
6353 gst_validate_action_set_done (GstValidateAction * action)
6354 {
6355   GMainContext *context = action->priv->context;
6356   GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6357
6358   action->priv->context = NULL;
6359   if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING) {
6360     GList *item = NULL;
6361
6362     if (scenario) {
6363       SCENARIO_LOCK (scenario);
6364       item = g_list_find (scenario->priv->non_blocking_running_actions, action);
6365       scenario->priv->non_blocking_running_actions =
6366           g_list_delete_link (scenario->priv->non_blocking_running_actions,
6367           item);
6368       SCENARIO_UNLOCK (scenario);
6369     }
6370
6371     if (item)
6372       gst_validate_action_unref (action);
6373   }
6374
6375   g_assert (!action->priv->pending_set_done);
6376   action->priv->pending_set_done = TRUE;
6377
6378   if (scenario && scenario->priv->wait_message_action == action) {
6379     gst_validate_action_unref (scenario->priv->wait_message_action);
6380     scenario->priv->wait_message_action = NULL;
6381   }
6382   gst_clear_object (&scenario);
6383
6384   g_main_context_invoke_full (action->priv->context,
6385       G_PRIORITY_DEFAULT_IDLE,
6386       (GSourceFunc) _action_set_done,
6387       gst_validate_action_ref (action),
6388       (GDestroyNotify) gst_validate_action_unref);
6389
6390   if (context)
6391     g_main_context_unref (context);
6392 }
6393
6394 /**
6395  * gst_validate_action_get_scenario:
6396  * @action: The action for which to retrieve the scenario
6397  *
6398  * Retrieve the scenario from which @action is executed.
6399  *
6400  * Returns: (transfer full) (nullable): The scenario from which the action is being executed.
6401  */
6402 GstValidateScenario *
6403 gst_validate_action_get_scenario (GstValidateAction * action)
6404 {
6405   return g_weak_ref_get (&action->priv->scenario);
6406 }
6407
6408 /**
6409  * gst_validate_register_action_type:
6410  * @type_name: The name of the new action type to add
6411  * @implementer_namespace: The namespace of the implementer of the action type.
6412  *                         That should always be the name of the GstPlugin as
6413  *                         retrieved with #gst_plugin_get_name when the action type
6414  *                         is registered inside a plugin.
6415  * @function: (scope notified): The function to be called to execute the action
6416  * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6417  * @description: A description of the new type
6418  * @flags: The #GstValidateActionTypeFlags to set on the new action type
6419  *
6420  * Register a new action type to the action type system. If the action type already
6421  * exists, it will be overridden by the new definition
6422  *
6423  * Returns: (transfer none): The newly created action type or the already registered action type
6424  * if it had a higher rank
6425  */
6426 GstValidateActionType *
6427 gst_validate_register_action_type (const gchar * type_name,
6428     const gchar * implementer_namespace,
6429     GstValidateExecuteAction function,
6430     GstValidateActionParameter * parameters,
6431     const gchar * description, GstValidateActionTypeFlags flags)
6432 {
6433   GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
6434       type_name, GST_RANK_NONE, function, parameters, description,
6435       flags);
6436
6437   g_free (type->implementer_namespace);
6438   type->implementer_namespace = g_strdup (implementer_namespace);
6439
6440   return type;
6441 }
6442
6443 static void
6444 _free_action_types (GList * _action_types)
6445 {
6446   g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
6447 }
6448
6449 /**
6450  * gst_validate_register_action_type_dynamic:
6451  * @plugin: (allow-none): The #GstPlugin that register the action type,
6452  *                        or NULL for a static element.
6453  * @rank: The ranking of that implementation of the action type called
6454  *        @type_name. If an action type has been registered with the same
6455  *        name with a higher rank, the new implementation will not be used,
6456  *        and the already registered action type is returned.
6457  *        If the already registered implementation has a lower rank, the
6458  *        new implementation will be used and returned.
6459  * @type_name: The name of the new action type to add
6460  * @function: (scope notified): The function to be called to execute the action
6461  * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6462  * @description: A description of the new type
6463  * @flags: The #GstValidateActionTypeFlags to be set on the new action type
6464  *
6465  * Returns: (transfer none): The newly created action type or the already registered action type
6466  * if it had a higher rank
6467  */
6468 GstValidateActionType *
6469 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
6470     const gchar * type_name, GstRank rank,
6471     GstValidateExecuteAction function, GstValidateActionParameter * parameters,
6472     const gchar * description, GstValidateActionTypeFlags flags)
6473 {
6474   GstValidateActionType *tmptype;
6475   GstValidateActionType *type = gst_validate_action_type_new ();
6476   gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
6477   gint n_params = is_config ? 0 : 2;
6478
6479   if (parameters) {
6480     for (n_params = 0; parameters[n_params].name != NULL; n_params++);
6481     n_params += 1;
6482   }
6483
6484   if (n_params) {
6485     type->parameters = g_new0 (GstValidateActionParameter, n_params);
6486   }
6487
6488   if (parameters) {
6489     memcpy (type->parameters, parameters,
6490         sizeof (GstValidateActionParameter) * (n_params));
6491   }
6492
6493   type->prepare = gst_validate_action_default_prepare_func;
6494   type->execute = function;
6495   type->name = g_strdup (type_name);
6496   if (plugin)
6497     type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
6498   else
6499     type->implementer_namespace = g_strdup ("none");
6500
6501   type->description = g_strdup (description);
6502   type->flags = flags;
6503   type->rank = rank;
6504
6505   if ((tmptype = _find_action_type (type_name))) {
6506     if (tmptype->rank <= rank) {
6507       action_types = g_list_remove (action_types, tmptype);
6508       type->overriden_type = tmptype;
6509     } else {
6510       gst_mini_object_unref (GST_MINI_OBJECT (type));
6511
6512       type = tmptype;
6513     }
6514   }
6515
6516   if (type != tmptype)
6517     action_types = g_list_append (action_types, type);
6518
6519   if (plugin) {
6520     GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
6521         "GstValidatePluginActionTypes");
6522
6523     plugin_action_types = g_list_prepend (plugin_action_types,
6524         gst_mini_object_ref (GST_MINI_OBJECT (type)));
6525
6526     g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
6527         plugin_action_types, (GDestroyNotify) _free_action_types);
6528   }
6529
6530   return type;
6531 }
6532
6533 GstValidateActionType *
6534 gst_validate_get_action_type (const gchar * type_name)
6535 {
6536   GstValidateActionType *type = _find_action_type (type_name);
6537
6538   if (type)
6539     return
6540         GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
6541
6542   return NULL;
6543 }
6544
6545 static GList *
6546 gst_validate_list_action_types (void)
6547 {
6548   return action_types;
6549 }
6550
6551 /**
6552  * gst_validate_print_action_types:
6553  * @wanted_types: (array length=num_wanted_types): (optional):  List of types to be printed
6554  * @num_wanted_types: Length of @wanted_types
6555  *
6556  * Prints the action types details wanted in @wanted_types
6557  *
6558  * Returns: True if all types could be printed
6559  */
6560 gboolean
6561 gst_validate_print_action_types (const gchar ** wanted_types,
6562     gint num_wanted_types)
6563 {
6564   GList *tmp;
6565   gint nfound = 0;
6566   gboolean print_all = (num_wanted_types == 1
6567       && !g_strcmp0 (wanted_types[0], "all"));
6568
6569   if (print_all)
6570     gst_validate_printf (NULL, "# GstValidate action types");
6571
6572   for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
6573     GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
6574     gboolean print = print_all;
6575
6576     if (num_wanted_types) {
6577       gint n;
6578
6579       for (n = 0; n < num_wanted_types; n++) {
6580         if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
6581             g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
6582           nfound++;
6583           print = TRUE;
6584
6585           break;
6586         }
6587       }
6588     } else {
6589       print = TRUE;
6590     }
6591
6592     if (print && num_wanted_types) {
6593       gst_validate_printf (atype, "\n");
6594     } else if (print) {
6595       gchar *desc =
6596           g_regex_replace (newline_regex, atype->description, -1, 0, "\n      ",
6597           0,
6598           NULL);
6599
6600       gst_validate_printf (NULL, "\n%s: %s:\n      %s\n",
6601           atype->implementer_namespace, atype->name, desc);
6602       g_free (desc);
6603     }
6604   }
6605
6606   if (!print_all && num_wanted_types && num_wanted_types > nfound) {
6607     return FALSE;
6608   }
6609
6610   return TRUE;
6611 }
6612
6613 /**
6614  * gst_validate_scenario_get_actions:
6615  * @scenario: The scenario to retrieve remaining actions for
6616  *
6617  * Get remaining actions from @scenario.
6618  *
6619  * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
6620  */
6621 GList *
6622 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
6623 {
6624   GList *ret;
6625   gboolean main_context_acquired;
6626
6627   main_context_acquired = g_main_context_acquire (g_main_context_default ());
6628   g_return_val_if_fail (main_context_acquired, NULL);
6629
6630   ret = g_list_copy_deep (scenario->priv->actions,
6631       (GCopyFunc) gst_validate_action_ref, NULL);
6632
6633   g_main_context_release (g_main_context_default ());
6634
6635   return ret;
6636 }
6637
6638 /**
6639  * gst_validate_scenario_get_target_state:
6640  * @scenario: The scenario to retrieve the current target state for
6641  *
6642  * Get current target state from @scenario.
6643  *
6644  * Returns: Current target state.
6645  */
6646 GstState
6647 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
6648 {
6649   return scenario->priv->target_state;
6650 }
6651
6652 void
6653 init_scenarios (void)
6654 {
6655   GList *tmp;
6656
6657   register_action_types ();
6658
6659   for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
6660     const gchar *action_typename;
6661     GstStructure *plug_conf = (GstStructure *) tmp->data;
6662
6663     if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
6664       GstValidateAction *action;
6665       GstValidateActionType *atype = _find_action_type (action_typename);
6666
6667       if (!atype) {
6668         gst_validate_error_structure (plug_conf,
6669             "[CONFIG ERROR] Action type %s not found", action_typename);
6670
6671         continue;
6672       }
6673
6674
6675       if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
6676         GST_INFO ("Action type %s from configuration files"
6677             " is handled.", action_typename);
6678         continue;
6679       }
6680
6681       if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
6682           !(_action_type_has_parameter (atype, "as-config"))) {
6683         gst_validate_error_structure (plug_conf,
6684             "[CONFIG ERROR] Action '%s' is not a config action",
6685             action_typename);
6686
6687         continue;
6688       }
6689
6690       gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
6691       gst_structure_set_name (plug_conf, action_typename);
6692
6693       action = gst_validate_action_new (NULL, atype, plug_conf, FALSE);
6694       gst_validate_action_unref (action);
6695     }
6696   }
6697 }
6698
6699 void
6700 register_action_types (void)
6701 {
6702   GstValidateActionType *type;
6703   GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
6704       GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
6705
6706   _gst_validate_action_type = gst_validate_action_get_type ();
6707   _gst_validate_action_type_type = gst_validate_action_type_get_type ();
6708
6709   /*  *INDENT-OFF* */
6710   REGISTER_ACTION_TYPE ("meta", NULL,
6711       ((GstValidateActionParameter [])  {
6712       {
6713         .name = "summary",
6714         .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
6715         .mandatory = FALSE,
6716         .types = "string",
6717         .possible_variables = NULL,
6718         .def = "'Nothing'"},
6719       {
6720         .name = "is-config",
6721         .description = "Whether the scenario is a config only scenario",
6722         .mandatory = FALSE,
6723         .types = "boolean",
6724         .possible_variables = NULL,
6725         .def = "false"
6726       },
6727       {
6728         .name = "handles-states",
6729         .description = "Whether the scenario handles pipeline state changes from the beginning\n"
6730         "in that case the application should not set the state of the pipeline to anything\n"
6731         "and the scenario action will be executed from the beginning",
6732         .mandatory = FALSE,
6733         .types = "boolean",
6734         .possible_variables = NULL,
6735         .def = "false"},
6736       {
6737         .name = "seek",
6738         .description = "Whether the scenario executes seek actions or not",
6739         .mandatory = FALSE,
6740         .types = "boolean",
6741         .possible_variables = NULL,
6742         .def = "false"
6743       },
6744       {
6745         .name = "reverse-playback",
6746         .description = "Whether the scenario plays the stream backward",
6747         .mandatory = FALSE,
6748         .types = "boolean",
6749         .possible_variables = NULL,
6750         .def = "false"
6751       },
6752       {
6753         .name = "need-clock-sync",
6754         .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
6755                        "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
6756         .mandatory = FALSE,
6757         .types = "boolean",
6758         .possible_variables = NULL,
6759         .def = "true if some action requires a playback-time false otherwise"
6760       },
6761       {
6762         .name = "min-media-duration",
6763         .description = "Lets the user know the minimum duration of the stream for the scenario\n"
6764                        "to be usable",
6765         .mandatory = FALSE,
6766         .types = "double",
6767         .possible_variables = NULL,
6768         .def = "0.0"
6769       },
6770       {
6771         .name = "min-audio-track",
6772         .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
6773                        "for the scenario to be usable",
6774         .mandatory = FALSE,
6775         .types = "int",
6776         .possible_variables = NULL,
6777         .def = "0"
6778       },
6779       {
6780        .name = "min-video-track",
6781        .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
6782                       "for the scenario to be usable",
6783        .mandatory = FALSE,
6784        .types = "int",
6785        .possible_variables = NULL,
6786        .def = "0"
6787       },
6788       {
6789         .name = "duration",
6790         .description = "Lets the user know the time the scenario needs to be fully executed",
6791         .mandatory = FALSE,
6792         .types = "double, int",
6793         .possible_variables = NULL,
6794         .def = "infinite (GST_CLOCK_TIME_NONE)"
6795       },
6796       {
6797         .name = "pipeline-name",
6798         .description = "The name of the GstPipeline on which the scenario should be executed.\n"
6799           "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
6800         .mandatory = FALSE,
6801         .types = "string",
6802         .possible_variables = NULL,
6803         .def = "NULL"
6804       },
6805       {
6806         .name = "max-latency",
6807         .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
6808           "It can be overridden using core configuration, like for example by defining the "
6809           "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
6810         .mandatory = FALSE,
6811         .types = "double, int",
6812         .possible_variables = NULL,
6813         .def = "infinite (GST_CLOCK_TIME_NONE)"
6814       },
6815       {
6816         .name = "max-dropped",
6817         .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
6818           "It can be overridden using core configuration, like for example by defining the "
6819           "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
6820         .mandatory = FALSE,
6821         .types = "int",
6822         .possible_variables = NULL,
6823         .def = "infinite (-1)"
6824       },
6825       {
6826         .name = "ignore-eos",
6827         .description = "Ignore EOS and keep executing the scenario when it happens.\n By default "
6828           "a 'stop' action is generated one EOS",
6829         .mandatory = FALSE,
6830         .types = "boolean",
6831         .possible_variables = NULL,
6832         .def = "false"
6833       },
6834       {
6835         .name = "allow-errors",
6836         .description = "Ignore error messages and keep executing the\n"
6837           "scenario when it happens. By default a 'stop' action is generated on ERROR messages",
6838         .mandatory = FALSE,
6839         .types = "boolean",
6840         .possible_variables = NULL,
6841         .def = "false"
6842       },
6843       {NULL}
6844       }),
6845       "Scenario metadata.\nNOTE: it used to be called \"description\"",
6846       GST_VALIDATE_ACTION_TYPE_CONFIG);
6847
6848   REGISTER_ACTION_TYPE ("seek", _execute_seek,
6849       ((GstValidateActionParameter [])  {
6850         {
6851           .name = "start",
6852           .description = "The starting value of the seek",
6853           .mandatory = TRUE,
6854           .types = "double or string (GstClockTime)",
6855           .possible_variables =
6856             "`position`: The current position in the stream\n"
6857             "`duration`: The duration of the stream",
6858            NULL
6859         },
6860         {
6861           .name = "flags",
6862           .description = "The GstSeekFlags to use",
6863           .mandatory = TRUE,
6864           .types = "string describing the GstSeekFlags to set",
6865           NULL,
6866         },
6867         {
6868           .name = "rate",
6869           .description = "The rate value of the seek",
6870           .mandatory = FALSE,
6871           .types = "double",
6872           .possible_variables = NULL,
6873           .def = "1.0"
6874         },
6875         {
6876           .name = "start_type",
6877           .description = "The GstSeekType to use for the start of the seek, in:\n"
6878           "  [none, set, end]",
6879           .mandatory = FALSE,
6880           .types = "string",
6881           .possible_variables = NULL,
6882           .def = "set"
6883         },
6884         {
6885           .name = "stop_type",
6886           .description = "The GstSeekType to use for the stop of the seek, in:\n"
6887                          "  [none, set, end]",
6888           .mandatory = FALSE,
6889           .types = "string",
6890           .possible_variables = NULL,
6891           .def = "set"
6892         },
6893         {
6894           .name = "stop",
6895           .description = "The stop value of the seek",
6896           .mandatory = FALSE,
6897           .types = "double or string (GstClockTime)",
6898           .possible_variables =
6899             "`position`: The current position in the stream\n"
6900             "`duration`: The duration of the stream",
6901           .def ="GST_CLOCK_TIME_NONE",
6902         },
6903         {NULL}
6904       }),
6905       "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
6906       "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
6907       "  seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
6908       GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
6909   );
6910
6911   REGISTER_ACTION_TYPE ("pause", _execute_pause,
6912       ((GstValidateActionParameter []) {
6913         {
6914           .name = "duration",
6915           .description = "The duration during which the stream will be paused",
6916           .mandatory = FALSE,
6917           .types = "double or string (GstClockTime)",
6918           .possible_variables = NULL,
6919           .def = "0.0",
6920         },
6921         {NULL}
6922       }),
6923       "Sets pipeline to PAUSED. You can add a 'duration'\n"
6924       "parameter so the pipeline goes back to playing after that duration\n"
6925       "(in second)",
6926       GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK | GST_VALIDATE_ACTION_TYPE_ASYNC);
6927
6928   REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
6929       "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
6930
6931   REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
6932       "Stops the execution of the scenario. It will post a 'request-state'"
6933       " message on the bus with NULL as a requested state"
6934       " and the application is responsible for stopping itself."
6935       " If you override that action type, make sure to link up.",
6936       GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6937
6938   REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
6939       "Sends an EOS event to the pipeline",
6940       GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6941
6942   REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
6943       ((GstValidateActionParameter []) {
6944         {
6945           .name = "type",
6946           .description = "Selects which track type to change (can be 'audio', 'video',"
6947                           " or 'text').",
6948           .mandatory = FALSE,
6949           .types = "string",
6950           .possible_variables = NULL,
6951           .def = "audio",
6952         },
6953         {
6954           .name = "index",
6955           .description = "Selects which track of this type to use: it can be either a number,\n"
6956                          "which will be the Nth track of the given type, or a number with a '+' or\n"
6957                          "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
6958                          "'-1' means 'previous track')",
6959           .mandatory = FALSE,
6960           .types = "string: to switch track relatively\n"
6961                    "int: To use the actual index to use",
6962           .possible_variables = NULL,
6963           .def = "+1",
6964         },
6965         {NULL}
6966       }),
6967       "The 'switch-track' command can be used to switch tracks."
6968       , GST_VALIDATE_ACTION_TYPE_NONE);
6969
6970   REGISTER_ACTION_TYPE ("wait", _execute_wait,
6971       ((GstValidateActionParameter []) {
6972         {
6973           .name = "duration",
6974           .description = "the duration while no other action will be executed",
6975           .mandatory = FALSE,
6976           .types = "double or string (GstClockTime)",
6977           NULL},
6978         {
6979           .name = "target-element-name",
6980           .description = "The name of the GstElement to wait @signal-name on.",
6981           .mandatory = FALSE,
6982           .types = "string"
6983         },
6984         {
6985           .name = "target-element-factory-name",
6986           .description = "The name factory for which to wait @signal-name on",
6987           .mandatory = FALSE,
6988           .types = "string",
6989           NULL
6990         },
6991         {
6992           .name = "signal-name",
6993           .description = "The name of the signal to wait for on @target-element-name."
6994               " To ensure that the signal is executed without blocking while waiting for it"
6995               " you can set the field 'non-blocking=true'.",
6996           .mandatory = FALSE,
6997           .types = "string",
6998           NULL
6999         },
7000         {
7001           .name = "non-blocking",
7002           .description = "**Only for signals**."
7003             "Make the action non blocking meaning that next actions will be\n"
7004             "executed without waiting for the signal to be emitted.",
7005           .mandatory = FALSE,
7006           .types = "string",
7007           NULL
7008         },
7009         {
7010           .name = "message-type",
7011           .description = "The name of the message type to wait for (on @target-element-name"
7012             " if specified)",
7013           .mandatory = FALSE,
7014           .types = "string",
7015           NULL
7016         },
7017         {
7018           .name = "expected-values",
7019           .description = "Expected values in the message structure (valid only when "
7020             "`message-type`). Example: "
7021             "wait, on-client=true, message-type=buffering, expected-values=[values, buffer-percent=100]",
7022           .mandatory = FALSE,
7023           .types = "structure",
7024           NULL
7025         },
7026         {
7027           .name = "on-clock",
7028           .description = "Wait until the test clock gets a new pending entry.\n"
7029             "See #gst_test_clock_wait_for_next_pending_id.",
7030           .mandatory = FALSE,
7031           .types = "boolean",
7032           NULL
7033         },
7034         {
7035           .name = "check",
7036           .description = "The check action to execute when non blocking signal is received",
7037           .mandatory = FALSE,
7038           .types = "structure",
7039           NULL
7040         },
7041         {NULL}
7042       }),
7043       "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
7044       GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
7045
7046   REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
7047       "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
7048       "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
7049       "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
7050       GST_VALIDATE_ACTION_TYPE_NONE);
7051
7052   REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank_or_disable_feature,
7053       ((GstValidateActionParameter []) {
7054         {
7055           .name = "name",
7056           .description = "The name of a GstFeature or GstPlugin",
7057           .mandatory = TRUE,
7058           .types = "string",
7059           NULL},
7060         {
7061           .name = "rank",
7062           .description = "The GstRank to set on @name",
7063           .mandatory = TRUE,
7064           .types = "string, int",
7065           NULL},
7066         {NULL}
7067       }),
7068       "Changes the ranking of a particular plugin feature(s)",
7069       GST_VALIDATE_ACTION_TYPE_CONFIG);
7070
7071   REGISTER_ACTION_TYPE ("remove-feature", _execute_set_rank_or_disable_feature,
7072       ((GstValidateActionParameter []) {
7073         {
7074           .name = "name",
7075           .description = "The name of a GstFeature or GstPlugin to remove",
7076           .mandatory = TRUE,
7077           .types = "string",
7078           NULL
7079         },
7080         {NULL}
7081       }),
7082       "Remove a plugin feature(s) or a plugin from the registry",
7083       GST_VALIDATE_ACTION_TYPE_CONFIG);
7084
7085   REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank_or_disable_feature,
7086       ((GstValidateActionParameter []) {
7087         {
7088           .name = "feature-name",
7089           .description = "The name of a GstFeature",
7090           .mandatory = TRUE,
7091           .types = "string",
7092           NULL},
7093         {
7094           .name = "rank",
7095           .description = "The GstRank to set on @feature-name",
7096           .mandatory = TRUE,
7097           .types = "string, int",
7098           NULL},
7099         {NULL}
7100       }),
7101       "Changes the ranking of a particular plugin feature",
7102       GST_VALIDATE_ACTION_TYPE_CONFIG);
7103
7104   REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
7105       ((GstValidateActionParameter []) {
7106         {
7107           .name = "state",
7108           .description = "A GstState as a string, should be in: \n"
7109                          "    * ['null', 'ready', 'paused', 'playing']",
7110           .mandatory = TRUE,
7111           .types = "string",
7112         },
7113         {NULL}
7114       }),
7115       "Changes the state of the pipeline to any GstState",
7116       GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
7117
7118   REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
7119       ((GstValidateActionParameter []) {
7120         {NULL}
7121       }),
7122       "Define vars to be used in other actions.\n"
7123       "For example you can define vars for buffer checksum"
7124       " to be used in the \"check-last-sample\" action type as follow:\n\n"
7125       "```\n"
7126       " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
7127       " check-last-sample, checksum=frame1\n"
7128       "```\n",
7129       GST_VALIDATE_ACTION_TYPE_NONE);
7130
7131   GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
7132   GST_TYPE_TRIGGER_CONTROL_SOURCE;
7133   REGISTER_ACTION_TYPE ("set-timed-value-properties", _set_timed_value_property,
7134       ((GstValidateActionParameter []) {
7135         {
7136           .name = "binding-type",
7137           .description = "The name of the type of binding to use",
7138           .types = "string",
7139           .mandatory = FALSE,
7140           .def = "direct",
7141         },
7142         {
7143           .name = "source-type",
7144           .description = "The name of the type of ControlSource to use",
7145           .types = "string",
7146           .mandatory = FALSE,
7147           .def = "GstInterpolationControlSource",
7148         },
7149         {
7150           .name = "interpolation-mode",
7151           .description = "The name of the GstInterpolationMode to set on the source",
7152           .types = "string",
7153           .mandatory = FALSE,
7154           .def = "linear",
7155         },
7156         {
7157           .name = "timestamp",
7158           .description = "The timestamp of the keyframe",
7159           .types = "string or float (GstClockTime)",
7160           .mandatory = TRUE,
7161         },
7162         {NULL}
7163       }),
7164         "Sets GstTimedValue on pads on elements properties using GstControlBindings\n"
7165         "and GstControlSource as defined in the parameters.\n"
7166         "The properties values to set will be defined as:\n\n"
7167         "```\n"
7168         "element-name.padname::property-name=new-value\n"
7169         "```\n\n"
7170         "> NOTE: `.padname` is not needed if setting a property on an element\n\n"
7171         "This action also adds necessary control source/control bindings.\n",
7172       GST_VALIDATE_ACTION_TYPE_NONE);
7173
7174   REGISTER_ACTION_TYPE ("check-properties", _execute_set_or_check_properties,
7175       ((GstValidateActionParameter []) {
7176         {NULL}
7177       }),
7178         "Check elements and pads properties values.\n"
7179         "The properties values to check will be defined as:\n\n"
7180         "```\n"
7181         "element-name.padname::property-name\n"
7182         "```\n\n"
7183         "> NOTE: `.padname` is not needed if checking an element property\n\n",
7184       GST_VALIDATE_ACTION_TYPE_CHECK);
7185
7186   REGISTER_ACTION_TYPE ("set-properties", _execute_set_or_check_properties,
7187       ((GstValidateActionParameter []) {
7188         {NULL}
7189       }),
7190         "Set elements and pads properties values.\n"
7191         "The properties values to set will be defined as:\n\n"
7192         "```\n"
7193         "    element-name.padname::property-name\n"
7194         "```\n\n"
7195         "> NOTE: `.padname` is not needed if set an element property\n\n",
7196       GST_VALIDATE_ACTION_TYPE_NONE);
7197
7198   REGISTER_ACTION_TYPE ("set-property", _execute_set_or_check_property,
7199       ((GstValidateActionParameter []) {
7200         {
7201           .name = "target-element-name",
7202           .description = "The name of the GstElement to set a property on",
7203           .mandatory = FALSE,
7204           .types = "string",
7205           NULL
7206         },
7207         {
7208           .name = "target-element-factory-name",
7209           .description = "The name factory for which to set a property on built elements",
7210           .mandatory = FALSE,
7211           .types = "string",
7212           NULL
7213         },
7214         {
7215           .name = "target-element-klass",
7216           .description = "The klass of the GstElements to set a property on",
7217           .mandatory = FALSE,
7218           .types = "string",
7219           NULL
7220         },
7221         {
7222           .name = "property-name",
7223           .description = "The name of the property to set on @target-element-name",
7224           .mandatory = TRUE,
7225           .types = "string",
7226           NULL
7227         },
7228         {
7229           .name = "property-value",
7230           .description = "The value of @property-name to be set on the element",
7231           .mandatory = TRUE,
7232           .types = "The same type of @property-name",
7233           NULL
7234         },
7235         {
7236           .name = "on-all-instances",
7237           .description = "Whether to set property on all instances matching "
7238                          "the requirements",
7239           .mandatory = FALSE,
7240           .types = "boolean",
7241           NULL
7242         },
7243         {NULL}
7244       }),
7245       "Sets a property of an element or klass of elements in the pipeline.\n"
7246       "Besides property-name and value, either 'target-element-name' or\n"
7247       "'target-element-klass' needs to be defined",
7248       GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
7249           GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
7250           GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
7251   type->prepare = gst_validate_set_property_prepare_func;
7252
7253   REGISTER_ACTION_TYPE("check-property", _execute_set_or_check_property,
7254       ((GstValidateActionParameter[]) {
7255           { .name = "target-element-name",
7256               .description = "The name of the GstElement to check a property value",
7257               .mandatory = FALSE,
7258               .types = "string",
7259               NULL },
7260           { .name = "target-element-factory-name",
7261               .description = "The name factory for which to check a property value on built elements",
7262               .mandatory = FALSE,
7263               .types = "string",
7264               NULL },
7265           { .name = "target-element-klass",
7266               .description = "The klass of the GstElements to check a property on",
7267               .mandatory = FALSE,
7268               .types = "string",
7269               NULL },
7270           { .name = "property-name",
7271               .description = "The name of the property to set on @target-element-name",
7272               .mandatory = TRUE,
7273               .types = "string",
7274               NULL },
7275           { .name = "property-value",
7276               .description = "The expected value of @property-name",
7277               .mandatory = TRUE,
7278               .types = "The same type of @property-name",
7279               NULL },
7280           { NULL } }),
7281       "Check the value of property of an element or klass of elements in the pipeline.\n"
7282       "Besides property-name and value, either 'target-element-name' or\n"
7283       "'target-element-klass' needs to be defined",
7284       GST_VALIDATE_ACTION_TYPE_CHECK);
7285
7286   REGISTER_ACTION_TYPE ("set-debug-threshold",
7287       _execute_set_debug_threshold,
7288       ((GstValidateActionParameter [])
7289         {
7290           {
7291             .name = "debug-threshold",
7292             .description = "String defining debug threshold\n"
7293                            "See gst_debug_set_threshold_from_string",
7294             .mandatory = TRUE,
7295             .types = "string"},
7296           {NULL}
7297         }),
7298       "Sets the debug level to be used, same format as\n"
7299       "setting the GST_DEBUG env variable",
7300       GST_VALIDATE_ACTION_TYPE_NONE);
7301
7302   REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
7303       ((GstValidateActionParameter [])
7304       {
7305         {
7306           .name = "target-element-name",
7307           .description = "The name of the GstElement to emit a signal on",
7308           .mandatory = TRUE,
7309           .types = "string"
7310         },
7311         {
7312           .name = "signal-name",
7313           .description = "The name of the signal to emit on @target-element-name",
7314           .mandatory = TRUE,
7315           .types = "string",
7316           NULL
7317         },
7318         {
7319           .name = "params",
7320           .description = "The signal parameters",
7321           .mandatory = FALSE,
7322           .types = "ValueArray",
7323           NULL
7324         },
7325         {NULL}
7326       }),
7327       "Emits a signal to an element in the pipeline",
7328       GST_VALIDATE_ACTION_TYPE_NONE);
7329
7330   REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
7331       ((GstValidateActionParameter [])
7332       {
7333         {
7334           .name = "target-element-name",
7335           .description = "The name of the appsrc to push data on",
7336           .mandatory = TRUE,
7337           .types = "string"
7338         },
7339         {
7340           .name = "file-name",
7341           .description = "Relative path to a file whose contents will be pushed as a buffer",
7342           .mandatory = TRUE,
7343           .types = "string"
7344         },
7345         {
7346           .name = "offset",
7347           .description = "Offset within the file where the buffer will start",
7348           .mandatory = FALSE,
7349           .types = "uint64"
7350         },
7351         {
7352           .name = "size",
7353           .description = "Number of bytes from the file that will be pushed as a buffer",
7354           .mandatory = FALSE,
7355           .types = "uint64"
7356         },
7357         {
7358           .name = "caps",
7359           .description = "Caps for the buffer to be pushed",
7360           .mandatory = FALSE,
7361           .types = "caps"
7362         },
7363         {
7364           .name = "pts",
7365           .description = "Buffer PTS",
7366           .mandatory = FALSE,
7367           .types = "GstClockTime"
7368         },
7369         {
7370           .name = "dts",
7371           .description = "Buffer DTS",
7372           .mandatory = FALSE,
7373           .types = "GstClockTime"
7374         },
7375         {
7376           .name = "duration",
7377           .description = "Buffer duration",
7378           .mandatory = FALSE,
7379           .types = "GstClockTime"
7380         },
7381         {
7382           .name = "segment",
7383           .description = "The GstSegment to configure as part of the sample",
7384           .mandatory = FALSE,
7385           .types = "(GstStructure)segment,"
7386                       "[start=(GstClockTime)]"
7387                       "[stop=(GstClockTime)]"
7388                       "[base=(GstClockTime)]"
7389                       "[offset=(GstClockTime)]"
7390                       "[time=(GstClockTime)]"
7391                       "[postion=(GstClockTime)]"
7392                       "[duration=(GstClockTime)]"
7393         },
7394         {NULL}
7395       }),
7396       "Queues a sample in an appsrc. If the pipeline state allows flow of buffers, "
7397       " the next action is not run until the buffer has been pushed.",
7398       GST_VALIDATE_ACTION_TYPE_NONE);
7399
7400   REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
7401       ((GstValidateActionParameter [])
7402       {
7403         {
7404           .name = "target-element-name",
7405           .description = "The name of the appsrc to emit EOS on",
7406           .mandatory = TRUE,
7407           .types = "string"
7408         },
7409         {NULL}
7410       }),
7411       "Queues a EOS event in an appsrc.",
7412       GST_VALIDATE_ACTION_TYPE_NONE);
7413
7414   REGISTER_ACTION_TYPE ("flush", _execute_flush,
7415       ((GstValidateActionParameter [])
7416       {
7417         {
7418           .name = "target-element-name",
7419           .description = "The name of the appsrc to flush on",
7420           .mandatory = TRUE,
7421           .types = "string"
7422         },
7423         {
7424           .name = "reset-time",
7425           .description = "Whether the flush should reset running time",
7426           .mandatory = FALSE,
7427           .types = "boolean",
7428           .def = "TRUE"
7429         },
7430         {NULL}
7431       }),
7432       "Sends FLUSH_START and FLUSH_STOP events.",
7433       GST_VALIDATE_ACTION_TYPE_NONE);
7434
7435   REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
7436       ((GstValidateActionParameter [])
7437       {
7438         {
7439           .name = "plugin-name",
7440           .description = "The name of the GstPlugin to disable",
7441           .mandatory = TRUE,
7442           .types = "string"
7443         },
7444         {
7445           .name = "as-config",
7446           .description = "Execute action as a config action (meaning when loading the scenario)",
7447           .mandatory = FALSE,
7448           .types = "boolean",
7449           .def = "false"
7450         },
7451         {NULL}
7452       }),
7453       "Disables a GstPlugin",
7454       GST_VALIDATE_ACTION_TYPE_NONE);
7455
7456   REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
7457       ((GstValidateActionParameter []) {
7458         {
7459           .name = "sink-name",
7460           .description = "The name of the sink element to check sample on.",
7461           .mandatory = FALSE,
7462           .types = "string",
7463           NULL
7464         },
7465         {
7466           .name = "sink-factory-name",
7467           .description = "The name of the factory of the sink element to check sample on.",
7468           .mandatory = FALSE,
7469           .types = "string",
7470           NULL
7471         },
7472         {
7473           .name = "sinkpad-caps",
7474           .description = "The caps (as string) of the sink to check.",
7475           .mandatory = FALSE,
7476           .types = "string",
7477           NULL
7478         },
7479         {
7480           .name = "checksum",
7481           .description = "The reference checksum of the buffer.",
7482           .mandatory = FALSE,
7483           .types = "string",
7484           NULL
7485         },
7486         {
7487           .name = "timecode-frame-number",
7488           .description = "The frame number of the buffer as specified on its"
7489                          " GstVideoTimeCodeMeta",
7490           .mandatory = FALSE,
7491           .types = "string",
7492           NULL
7493         },
7494         {NULL}
7495       }),
7496       "Checks the last-sample checksum or frame number (set on its "
7497       " GstVideoTimeCodeMeta) on declared Sink element."
7498       " This allows checking the checksum of a buffer after a 'seek' or after a"
7499       " GESTimeline 'commit'"
7500       " for example",
7501       GST_VALIDATE_ACTION_TYPE_NON_BLOCKING | GST_VALIDATE_ACTION_TYPE_CHECK);
7502
7503     REGISTER_ACTION_TYPE ("crank-clock", _execute_crank_clock,
7504       ((GstValidateActionParameter []) {
7505         {
7506           .name = "expected-time",
7507           .description = "Expected clock time after cranking",
7508           .mandatory = FALSE,
7509           .types = "GstClockTime",
7510           NULL
7511         },
7512         {
7513           .name = "expected-elapsed-time",
7514           .description = "Check time elapsed during the clock cranking",
7515           .mandatory = FALSE,
7516           .types = "GstClockTime",
7517           NULL
7518         },
7519         {NULL}
7520       }), "Crank the clock, possibly checking how much time was supposed to be waited on the clock"
7521           " and/or the clock running time after the crank."
7522           " Using one `crank-clock` action in a scenario implies that the scenario is driving the "
7523           " clock and a #GstTestClock will be used. The user will need to crank it the number of "
7524           " time required (using the `repeat` parameter comes handy here).",
7525         GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
7526
7527     REGISTER_ACTION_TYPE ("video-request-key-unit", _execute_request_key_unit,
7528       ((GstValidateActionParameter []) {
7529         {
7530           .name = "direction",
7531           .description = "The direction for the event to travel, should be in\n"
7532                           "  * [upstream, downstream]",
7533           .mandatory = TRUE,
7534           .types = "string",
7535           NULL
7536         },
7537         {
7538           .name = "running-time",
7539           .description = "The running_time can be set to request a new key unit at a specific running_time.\n"
7540                           "If not set, GST_CLOCK_TIME_NONE will be used so upstream elements will produce a new key unit "
7541                           "as soon as possible.",
7542           .mandatory = FALSE,
7543           .types = "double or string",
7544           .possible_variables = "position: The current position in the stream\n"
7545             "duration: The duration of the stream",
7546           NULL
7547         },
7548         {
7549           .name = "all-headers",
7550           .description = "TRUE to produce headers when starting a new key unit",
7551           .mandatory = FALSE,
7552           .def = "FALSE",
7553           .types = "boolean",
7554           NULL
7555         },
7556         {
7557           .name = "count",
7558           .description = "integer that can be used to number key units",
7559           .mandatory = FALSE,
7560           .def = "0",
7561           .types = "int",
7562           NULL
7563         },
7564         {
7565           .name = "target-element-name",
7566           .description = "The name of the GstElement to send a send force-key-unit to",
7567           .mandatory = FALSE,
7568           .types = "string",
7569           NULL
7570         },
7571         {
7572           .name = "target-element-factory-name",
7573           .description = "The factory name of the GstElements to send a send force-key-unit to",
7574           .mandatory = FALSE,
7575           .types = "string",
7576           NULL
7577         },
7578         {
7579           .name = "target-element-klass",
7580           .description = "The klass of the GstElements to send a send force-key-unit to",
7581           .mandatory = FALSE,
7582           .def = "Video/Encoder",
7583           .types = "string",
7584           NULL
7585         },
7586         {
7587           .name = "pad",
7588           .description = "The name of the GstPad to send a send force-key-unit to",
7589           .mandatory = FALSE,
7590           .def = "sink",
7591           .types = "string",
7592           NULL
7593         },
7594         {
7595           .name = "srcpad",
7596           .description = "The name of the GstPad to send a send force-key-unit to",
7597           .mandatory = FALSE,
7598           .def = "src",
7599           .types = "string",
7600           NULL
7601         },
7602         {NULL}
7603       }),
7604       "Request a video key unit", FALSE);
7605
7606   REGISTER_ACTION_TYPE("check-position", _execute_check_position,
7607       ((GstValidateActionParameter[]) {
7608           { .name = "expected-position",
7609               .description = "The expected pipeline position",
7610               .mandatory = TRUE,
7611               .types = "GstClockTime",
7612               NULL },
7613         {NULL}
7614       }),
7615       "Check current pipeline position.\n",
7616       /* FIXME: Make MT safe so it can be marked as GST_VALIDATE_ACTION_TYPE_CHECK */
7617       GST_VALIDATE_ACTION_TYPE_NONE);
7618
7619   REGISTER_ACTION_TYPE("check-current-pad-caps", _execute_check_pad_caps,
7620       ((GstValidateActionParameter[]) {
7621         {
7622            .name = "expected-caps",
7623            .description = "The expected caps. If not present, expected no caps to be set",
7624            .mandatory = FALSE,
7625            .types = "caps,structure",
7626            NULL
7627         },
7628         {
7629           .name = "target-element-name",
7630           .description = "The name of the GstElement to send a send force-key-unit to",
7631           .mandatory = FALSE,
7632           .types = "string",
7633           NULL
7634         },
7635         {
7636           .name = "target-element-factory-name",
7637           .description = "The factory name of the GstElements to get pad from",
7638           .mandatory = FALSE,
7639           .types = "string",
7640           NULL
7641         },
7642         {
7643           .name = "target-element-klass",
7644           .description = "The klass of the GstElements to get pad from",
7645           .mandatory = FALSE,
7646           .types = "string",
7647           NULL
7648         },
7649         {
7650           .name = "pad",
7651           .description = "The name of the GstPad to get pad from",
7652           .mandatory = FALSE,
7653           .types = "string",
7654           NULL
7655         },
7656         {
7657           .name = "comparison-type",
7658           .description = "",
7659           .mandatory = FALSE,
7660           .types = "string in [intersect, equal]",
7661           NULL
7662         },
7663         {NULL}
7664       }),
7665       "Check currently set caps on a particular pad.\n",
7666       GST_VALIDATE_ACTION_TYPE_NONE | GST_VALIDATE_ACTION_TYPE_CHECK );
7667
7668   REGISTER_ACTION_TYPE("run-command", _run_command,
7669       ((GstValidateActionParameter[]) {
7670           {
7671             .name = "argv",
7672             .description = "The subprocess arguments, include the program name itself",
7673             .mandatory = TRUE,
7674             .types = "(string){array,}",
7675             NULL
7676           },
7677           {
7678             .name = "env",
7679             .description = "Extra environment variables to set",
7680             .mandatory = FALSE,
7681             .types = "structure",
7682             NULL
7683           },
7684         {NULL}
7685       }),
7686       "Run an external command.\n",
7687       GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL);
7688
7689     REGISTER_ACTION_TYPE("foreach", NULL,
7690         ((GstValidateActionParameter[]) {
7691             { .name = "actions",
7692                 .description = "The array of actions to repeat",
7693                 .mandatory = TRUE,
7694                 .types = "{array of [structures]}",
7695                 NULL },
7696             { NULL } }),
7697         "Run actions defined in the `actions` array the number of times specified\n"
7698         "with an iterator parameter passed in. The iterator can be\n"
7699         "a range like: `i=[start, end, step]` or array of values\n"
7700         "such as: `values=<value1, value2>`.\n"
7701         "One and only one iterator field is supported as parameter.",
7702         GST_VALIDATE_ACTION_TYPE_NONE);
7703     type->prepare = gst_validate_foreach_prepare;
7704
7705     /* Internal actions types to test the validate scenario implementation */
7706     REGISTER_ACTION_TYPE("priv_check-action-type-calls",
7707       _execute_check_action_type_calls, NULL, NULL, 0);
7708     REGISTER_ACTION_TYPE("priv_check-subaction-level",
7709       _execute_check_subaction_level, NULL, NULL, 0);
7710     /*  *INDENT-ON* */
7711 }
7712
7713 void
7714 gst_validate_scenario_deinit (void)
7715 {
7716   _free_action_types (action_types);
7717   action_types = NULL;
7718 }