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