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