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