4 * Copyright (C) 2013 Collabora Ltd.
5 * Author: Thibault Saunier <thibault.saunier@collabora.com>
6 * Copyright (C) 2018-2022 Igalia S.L
7 * Author: Thibault Saunier <tsaunier@igalia.com>
10 * gst-validate-scenario.c - Validate Scenario class
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with this library; if not, write to the
24 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 * Boston, MA 02111-1307, USA.
28 * SECTION:gst-validate-scenario
29 * @title: GstValidateScenario
30 * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
32 * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
33 * It is basically an ordered list of #GstValidateAction that will be executed during the
34 * execution of the pipeline.
36 * Possible configurations (see [GST_VALIDATE_CONFIG](gst-validate-environment-variables.md)):
37 * * scenario-action-execution-interval: Sets the interval in
38 * milliseconds (1/1000ths of a second), between which actions
39 * will be executed, setting it to 0 means "execute in idle".
40 * The default value is 10ms.
53 #include <gst/check/gsttestclock.h>
54 #include "gst-validate-internal.h"
55 #include "gst-validate-scenario.h"
56 #include "gst-validate-reporter.h"
57 #include "gst-validate-report.h"
58 #include "gst-validate-utils.h"
59 #include "gst-validate-internal.h"
61 #include <gst/controller/controller.h>
62 #include <gst/validate/gst-validate-override.h>
63 #include <gst/validate/gst-validate-override-registry.h>
64 #include <gst/validate/gst-validate-pipeline-monitor.h>
66 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
68 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval
69 TODO make it overridable */
71 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
72 #undef GST_CAT_DEFAULT
73 #define GST_CAT_DEFAULT gst_validate_scenario_debug
75 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
76 type = gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
79 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
81 #define SCENARIO_LOCK(scenario) G_STMT_START { \
82 GST_LOG_OBJECT (scenario, "About to lock %p", &scenario->priv->lock); \
83 g_mutex_lock(&scenario->priv->lock); \
84 GST_LOG_OBJECT (scenario, "Acquired lock %p", &scenario->priv->lock); \
87 #define SCENARIO_UNLOCK(scenario) G_STMT_START { \
88 GST_LOG_OBJECT (scenario, "About to unlock %p", &scenario->priv->lock); \
89 g_mutex_unlock(&scenario->priv->lock); \
90 GST_LOG_OBJECT (scenario, "unlocked %p", &scenario->priv->lock); \
93 #define DECLARE_AND_GET_PIPELINE(s,a) \
94 GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
95 if (pipeline == NULL) { \
96 GST_VALIDATE_REPORT_ACTION (s, a, SCENARIO_ACTION_EXECUTION_ERROR, \
97 "Can't execute a '%s' action after the pipeline " \
98 "has been destroyed.", a->type); \
99 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
102 #ifdef G_HAVE_ISO_VARARGS
103 #define REPORT_UNLESS(condition, errpoint, ...) \
105 if (!(condition)) { \
106 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
107 gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \
108 SCENARIO_ACTION_EXECUTION_ERROR, \
114 #elif defined(G_HAVE_GNUC_VARARGS)
115 #define REPORT_UNLESS(condition, errpoint, args...) \
117 if (!(condition)) { \
118 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
119 gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \
120 SCENARIO_ACTION_EXECUTION_ERROR, ##args); \
132 PROP_EXECUTE_ON_IDLE,
143 static guint scenario_signals[LAST_SIGNAL] = { 0 };
145 static GList *action_types = NULL;
146 static void gst_validate_scenario_dispose (GObject * object);
147 static void gst_validate_scenario_finalize (GObject * object);
148 static GstValidateActionType *_find_action_type (const gchar * type_name);
149 static GstValidateExecuteActionReturn
150 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
151 GstStructure * structure, gboolean add_to_lists);
152 static gboolean _action_set_done (GstValidateAction * action);
153 static GList *_find_elements_defined_in_action (GstValidateScenario * scenario,
154 GstValidateAction * action);
155 static GstValidateAction *gst_validate_create_subaction (GstValidateScenario *
156 scenario, GstStructure * lvariables, GstValidateAction * action,
157 GstStructure * nstruct, gint it, gint max);
159 /* GstValidateSinkInformation tracks information for all sinks in the pipeline */
162 GstElement *sink; /* The sink element tracked */
163 guint32 segment_seqnum; /* The latest segment seqnum. GST_SEQNUM_INVALID if none */
164 GstSegment segment; /* The latest segment */
165 } GstValidateSinkInformation;
167 /* GstValidateSeekInformation tracks:
168 * * The values used in the seek
169 * * The seqnum used in the seek event
170 * * The validate action to which it relates
174 guint32 seqnum; /* seqnum of the seek event */
180 GstSeekType start_type, stop_type;
183 /* The action corresponding to this seek */
184 GstValidateAction *action;
185 } GstValidateSeekInformation;
187 /* GstValidateScenario is not really thread safe and
188 * everything should be done from the thread GstValidate
189 * was inited from, unless stated otherwise.
191 struct _GstValidateScenarioPrivate
194 GstValidateRunner *runner;
195 gboolean execute_on_idle;
200 GList *non_blocking_running_actions; /* MT safe. Protected with SCENARIO_LOCK */
201 GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */
203 gboolean needs_playback_parsing;
205 GList *sinks; /* List of GstValidateSinkInformation */
206 GList *seeks; /* List of GstValidateSeekInformation */
208 /* Seek currently applied (set when all sinks received segment with
209 * an identical seqnum and there is a matching pending seek).
210 * do not free, should always be present in the seek list above */
211 GstValidateSeekInformation *current_seek;
212 /* Current unified seqnum. Set when all sinks received segment with
213 * an identical seqnum, even if there wasn't a matching pending seek
215 guint32 current_seqnum;
217 /* List of action that need parsing when reaching ASYNC_DONE
218 * most probably to be able to query duration */
221 * * Only set for seek actions, and only if seek succeeded
222 * * Only Used in _check_position()
223 * FIXME : Just use the seek information */
224 GstSeekFlags seek_flags;
225 GstFormat seek_format;
227 /* segment_start/segment_stop :
228 * * Set : from seek values
229 * * Read : In _check_position()
230 * FIXME : Just use the current seek information */
231 GstClockTime segment_start;
232 GstClockTime segment_stop;
234 /* Always initialized to a default value
235 * FIXME : Is it still needed with the new seeking validation system ? */
236 GstClockTime seek_pos_tol;
238 /* If we seeked in paused the position should be exactly what
239 * the seek value was (if accurate) */
240 gboolean seeked_in_pause;
244 gboolean handles_state;
246 guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */
248 guint signal_handler_id; /* MT safe. Protect with SCENARIO_LOCK */
249 guint action_execution_interval;
251 /* Name of message the wait action is waiting for */
252 GstValidateAction *wait_message_action;
257 gboolean changing_state;
258 gboolean needs_async_done;
260 gboolean allow_errors;
261 GstState target_state;
265 gchar *pipeline_name;
266 GstClockTime max_latency;
270 /* 'switch-track action' currently waiting for
271 * GST_MESSAGE_STREAMS_SELECTED to be completed. */
272 GstValidateAction *pending_switch_track;
276 GWeakRef ref_pipeline;
279 guint segments_needed;
281 GMainContext *context;
284 typedef struct KeyFileGroupName
290 #define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1
293 gst_validate_sink_information_free (GstValidateSinkInformation * info)
295 gst_object_unref (info->sink);
300 gst_validate_seek_information_free (GstValidateSeekInformation * info)
302 gst_validate_action_unref (info->action);
306 static GstValidateInterceptionReturn
307 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
308 GstValidateReport * report)
312 for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
314 GstValidateOverride *override = (GstValidateOverride *) tmp->data;
316 gst_validate_override_get_severity (override,
317 gst_validate_issue_get_id (report->issue), report->level);
320 return GST_VALIDATE_REPORTER_REPORT;
324 * gst_validate_scenario_get_pipeline:
325 * @scenario: The scenario to retrieve a pipeline from
327 * Returns: (transfer full) (nullable): The #GstPipeline the scenario is running
331 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
333 return g_weak_ref_get (&scenario->priv->ref_pipeline);
337 _get_pipeline (GstValidateReporter * reporter)
340 GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
341 (GST_VALIDATE_SCENARIO (reporter)));
345 _reporter_iface_init (GstValidateReporterInterface * iface)
347 iface->intercept_report = gst_validate_scenario_intercept_report;
348 iface->get_pipeline = _get_pipeline;
351 static GQuark chain_qdata;
352 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
353 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
354 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init)
355 chain_qdata = g_quark_from_static_string ("__validate_scenario_chain_data")
358 /* GstValidateAction implementation */
359 static GType _gst_validate_action_type = 0;
361 struct _GstValidateActionPrivate
363 GstStructure *main_structure;
364 GstValidateExecuteActionReturn state; /* Actually ActionState */
366 gboolean executing_last_subaction;
367 gboolean subaction_level;
370 GstClockTime execution_time;
371 GstClockTime execution_duration;
372 GstClockTime timeout;
375 gboolean needs_playback_parsing;
376 gboolean pending_set_done;
378 GMainContext *context;
384 gst_validate_action_serialize (GstValidateAction * action)
386 JsonNode *node = json_node_alloc ();
387 JsonObject *jreport = json_object_new ();
388 gchar *action_args = gst_structure_to_string (action->structure);
390 json_object_set_string_member (jreport, "type", "action");
391 json_object_set_string_member (jreport, "action-type", action->type);
392 json_object_set_int_member (jreport, "playback-time",
393 (gint64) action->playback_time);
394 json_object_set_string_member (jreport, "args", action_args);
395 g_free (action_args);
397 node = json_node_init_object (node, jreport);
398 json_object_unref (jreport);
404 gst_validate_action_get_type (void)
406 if (_gst_validate_action_type == 0) {
407 _gst_validate_action_type =
408 g_boxed_type_register_static (g_intern_static_string
409 ("GstValidateAction"), (GBoxedCopyFunc) gst_validate_action_ref,
410 (GBoxedFreeFunc) gst_validate_action_unref);
412 json_boxed_register_serialize_func (_gst_validate_action_type,
414 (JsonBoxedSerializeFunc) gst_validate_action_serialize);
417 return _gst_validate_action_type;
420 static gboolean execute_next_action (GstValidateScenario * scenario);
422 gst_validate_scenario_load (GstValidateScenario * scenario,
423 const gchar * scenario_name);
425 static GstValidateAction *
426 _action_copy (GstValidateAction * act)
428 GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
429 GstValidateAction *copy = gst_validate_action_new (scenario,
430 _find_action_type (act->type), NULL, FALSE);
432 gst_object_unref (scenario);
434 if (act->structure) {
435 copy->structure = gst_structure_copy (act->structure);
436 copy->type = gst_structure_get_name (copy->structure);
437 if (!(act->name = gst_structure_get_string (copy->structure, "name")))
441 if (act->priv->main_structure)
442 copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
444 copy->action_number = act->action_number;
445 copy->playback_time = act->playback_time;
446 copy->priv->timeout = act->priv->timeout;
447 GST_VALIDATE_ACTION_LINENO (copy) = GST_VALIDATE_ACTION_LINENO (act);
448 GST_VALIDATE_ACTION_FILENAME (copy) =
449 g_strdup (GST_VALIDATE_ACTION_FILENAME (act));
450 GST_VALIDATE_ACTION_DEBUG (copy) = g_strdup (GST_VALIDATE_ACTION_DEBUG (act));
451 GST_VALIDATE_ACTION_N_REPEATS (copy) = GST_VALIDATE_ACTION_N_REPEATS (act);
452 GST_VALIDATE_ACTION_RANGE_NAME (copy) = GST_VALIDATE_ACTION_RANGE_NAME (act);
454 if (act->priv->it_value.g_type != 0) {
455 g_value_init (©->priv->it_value, G_VALUE_TYPE (&act->priv->it_value));
456 g_value_copy (&act->priv->it_value, ©->priv->it_value);
463 gst_validate_action_return_get_name (GstValidateActionReturn r)
466 case GST_VALIDATE_EXECUTE_ACTION_ERROR:
468 case GST_VALIDATE_EXECUTE_ACTION_OK:
470 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
472 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
473 return "NON-BLOCKING";
474 case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
475 return "ERROR(reported)";
476 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
477 return "IN_PROGRESS";
478 case GST_VALIDATE_EXECUTE_ACTION_NONE:
480 case GST_VALIDATE_EXECUTE_ACTION_DONE:
483 g_assert_not_reached ();
488 _action_free (GstValidateAction * action)
490 if (action->structure)
491 gst_structure_free (action->structure);
493 if (action->priv->main_structure)
494 gst_structure_free (action->priv->main_structure);
496 if (action->priv->it_value.g_type != 0)
497 g_value_reset (&action->priv->it_value);
498 g_weak_ref_clear (&action->priv->scenario);
499 g_free (GST_VALIDATE_ACTION_FILENAME (action));
500 g_free (GST_VALIDATE_ACTION_DEBUG (action));
502 g_slice_free (GstValidateActionPrivate, action->priv);
503 g_slice_free (GstValidateAction, action);
507 gst_validate_action_init (GstValidateAction * action)
509 gst_mini_object_init (((GstMiniObject *) action), 0,
510 _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
511 (GstMiniObjectFreeFunction) _action_free);
513 action->priv = g_slice_new0 (GstValidateActionPrivate);
515 g_weak_ref_init (&action->priv->scenario, NULL);
519 gst_validate_action_ref (GstValidateAction * action)
521 return (GstValidateAction *) gst_mini_object_ref (GST_MINI_OBJECT (action));
525 gst_validate_action_unref (GstValidateAction * action)
527 gst_mini_object_unref (GST_MINI_OBJECT (action));
531 * gst_validate_action_new:
532 * @scenario: (allow-none): The scenario executing the action
533 * @action_type: The action type
534 * @structure: The structure containing the action arguments
535 * @add_to_lists: Weather the action should be added to the scenario action list
537 * Returns: A newly created #GstValidateAction
540 gst_validate_action_new (GstValidateScenario * scenario,
541 GstValidateActionType * action_type, GstStructure * structure,
542 gboolean add_to_lists)
544 GstValidateAction *action = g_slice_new0 (GstValidateAction);
546 g_assert (action_type);
548 gst_validate_action_init (action);
549 action->playback_time = GST_CLOCK_TIME_NONE;
550 action->priv->timeout = GST_CLOCK_TIME_NONE;
551 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_NONE;
552 action->type = action_type->name;
555 g_weak_ref_set (&action->priv->scenario, scenario);
557 gchar *filename = NULL;
558 gst_structure_get (structure,
559 "__lineno__", G_TYPE_INT, &GST_VALIDATE_ACTION_LINENO (action),
560 "__filename__", G_TYPE_STRING, &filename,
561 "__debug__", G_TYPE_STRING, &GST_VALIDATE_ACTION_DEBUG (action), NULL);
563 GST_VALIDATE_ACTION_FILENAME (action) =
564 g_filename_display_basename (filename);
567 gst_structure_remove_fields (structure, "__lineno__", "__filename__",
569 action->priv->state =
570 _fill_action (scenario, action, structure, add_to_lists);
577 _action_check_and_set_printed (GstValidateAction * action)
579 if (action->priv->printed == FALSE) {
580 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
583 action->priv->printed = TRUE;
592 gst_validate_action_get_level (GstValidateAction * action)
594 return action->priv->subaction_level;
597 /* GstValidateActionType implementation */
598 GType _gst_validate_action_type_type;
599 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
600 static GstValidateActionType *gst_validate_action_type_new (void);
602 struct _GstValidateActionTypePrivate
608 _action_type_free (GstValidateActionType * type)
610 g_free (type->parameters);
611 g_free (type->description);
613 g_free (type->implementer_namespace);
616 if (type->overriden_type)
617 gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
619 g_slice_free (GstValidateActionType, type);
623 gst_validate_action_type_init (GstValidateActionType * type)
625 type->priv = g_new0 (GstValidateActionTypePrivate, 1);
627 gst_mini_object_init ((GstMiniObject *) type, 0,
628 _gst_validate_action_type_type, NULL, NULL,
629 (GstMiniObjectFreeFunction) _action_type_free);
632 GstValidateActionType *
633 gst_validate_action_type_new (void)
635 GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
637 gst_validate_action_type_init (type);
642 static GstValidateActionType *
643 _find_action_type (const gchar * type_name)
647 for (tmp = action_types; tmp; tmp = tmp->next) {
648 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
649 if (g_strcmp0 (atype->name, type_name) == 0)
657 _update_well_known_vars (GstValidateScenario * scenario)
659 gint64 duration, position;
660 gdouble dduration, dposition;
661 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
663 gst_structure_remove_fields (scenario->priv->vars, "position", "duration",
669 if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration) ||
670 !GST_CLOCK_TIME_IS_VALID (duration)) {
671 GstValidateMonitor *monitor =
672 (GstValidateMonitor *) (g_object_get_data ((GObject *)
673 pipeline, "validate-monitor"));
674 GST_INFO_OBJECT (scenario,
675 "Could not query duration. Trying to get duration from media-info");
676 if (monitor && monitor->media_descriptor)
678 gst_validate_media_descriptor_get_duration
679 (monitor->media_descriptor);
682 if (!GST_CLOCK_TIME_IS_VALID (duration))
683 dduration = G_MAXDOUBLE;
685 dduration = ((double) duration / GST_SECOND);
687 gst_structure_set (scenario->priv->vars, "duration", G_TYPE_DOUBLE, dduration,
689 if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
691 if (!GST_CLOCK_TIME_IS_VALID (position))
692 dposition = G_MAXDOUBLE;
694 dposition = ((double) position / GST_SECOND);
696 gst_structure_set (scenario->priv->vars, "position", G_TYPE_DOUBLE,
699 GST_INFO_OBJECT (scenario, "Could not query position");
703 static GstElement *_get_target_element (GstValidateScenario * scenario,
704 GstValidateAction * action);
707 _get_target_object_property (GstValidateScenario * scenario,
708 GstValidateAction * action, const gchar * property_path,
711 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
712 gchar **elem_pad_name = NULL;
713 gchar **object_prop_name = NULL;
714 const gchar *elemname;
715 const gchar *padname = NULL;
716 GstObject *target = NULL;
719 elem_pad_name = g_strsplit (property_path, ".", 2);
721 g_strsplit (elem_pad_name[1] ? elem_pad_name[1] : elem_pad_name[0], "::",
723 REPORT_UNLESS (object_prop_name[1], err,
724 "Property specification %s is missing a `::propename` part",
727 if (elem_pad_name[1]) {
728 elemname = elem_pad_name[0];
729 padname = object_prop_name[0];
731 elemname = object_prop_name[0];
734 gst_structure_set (action->structure, "target-element-name", G_TYPE_STRING,
737 target = (GstObject *) _get_target_element (scenario, action);
738 gst_structure_remove_field (action->structure, "target-element-name");
739 REPORT_UNLESS (target, err, "Target element with given name (%s) not found",
743 gboolean done = FALSE;
744 GstIterator *it = gst_element_iterate_pads (GST_ELEMENT (target));
745 GValue v = G_VALUE_INIT;
747 gst_clear_object (&target);
749 switch (gst_iterator_next (it, &v)) {
750 case GST_ITERATOR_OK:{
751 GstPad *pad = g_value_get_object (&v);
752 gchar *name = gst_object_get_name (GST_OBJECT (pad));
754 if (!g_strcmp0 (name, padname)) {
756 gst_clear_object (&target);
758 target = gst_object_ref (pad);
764 case GST_ITERATOR_RESYNC:
765 gst_iterator_resync (it);
767 case GST_ITERATOR_ERROR:
768 case GST_ITERATOR_DONE:
773 gst_iterator_free (it);
775 REPORT_UNLESS (target, err, "Could not find pad: %s::%s", elemname, padname);
778 const gchar *propname = object_prop_name[i];
781 g_object_class_find_property (G_OBJECT_GET_CLASS (target), propname);
783 REPORT_UNLESS (*pspec, err,
784 "Object %" GST_PTR_FORMAT " doesn't have a property call '%s'", target,
787 if (!object_prop_name[++i])
790 REPORT_UNLESS (g_type_is_a ((*pspec)->owner_type, G_TYPE_OBJECT), err,
791 "Property: %" GST_PTR_FORMAT "::%s not a GObject, can't use it.",
794 g_object_get (target, propname, &target, NULL);
795 REPORT_UNLESS (target, err,
796 "Property: %" GST_PTR_FORMAT "::%s is NULL can't get %s.",
797 target, propname, object_prop_name[i + 1]);
800 REPORT_UNLESS (res == GST_VALIDATE_EXECUTE_ACTION_OK, err, "Something fishy");
803 g_strfreev (elem_pad_name);
804 g_strfreev (object_prop_name);
808 gst_clear_object (&target);
813 _set_variable_func (const gchar * name, double *value, gpointer user_data)
815 GstValidateScenario *scenario = (GstValidateScenario *) user_data;
817 if (!gst_structure_get_double (scenario->priv->vars, name, value))
823 /* Check that @list doesn't contain any non-optional actions */
825 actions_list_is_done (GList * list)
829 for (l = list; l != NULL; l = g_list_next (l)) {
830 GstValidateAction *action = l->data;
832 if (!action->priv->optional)
840 _check_scenario_is_done (GstValidateScenario * scenario)
842 SCENARIO_LOCK (scenario);
843 if (actions_list_is_done (scenario->priv->actions) &&
844 actions_list_is_done (scenario->priv->non_blocking_running_actions) &&
845 actions_list_is_done (scenario->priv->on_addition_actions)) {
846 SCENARIO_UNLOCK (scenario);
848 g_signal_emit (scenario, scenario_signals[DONE], 0);
850 SCENARIO_UNLOCK (scenario);
855 _reset_sink_information (GstValidateSinkInformation * sinkinfo)
857 sinkinfo->segment_seqnum = GST_SEQNUM_INVALID;
858 gst_segment_init (&sinkinfo->segment, GST_FORMAT_UNDEFINED);
862 * gst_validate_action_get_clocktime:
863 * @scenario: The #GstValidateScenario from which to get a time
864 * for a parameter of an action
865 * @action: The action from which to retrieve the time for @name
867 * @name: The name of the parameter for which to retrieve a time
868 * @retval: (out): The return value for the wanted time
870 * Get a time value for the @name parameter of an action. This
871 * method should be called to retrieve and compute a timed value of a given
872 * action. It will first try to retrieve the value as a double,
873 * then get it as a string and execute any formula taking into account
874 * the 'position' and 'duration' variables. And it will always convert that
875 * value to a GstClockTime.
877 * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
880 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
881 GstValidateAction * action, const gchar * name, GstClockTime * retval)
884 if (!gst_structure_has_field (action->structure, name))
887 if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
889 gchar *error = NULL, *strval;
890 const gchar *tmpvalue = gst_structure_get_string (action->structure, name);
893 GST_INFO_OBJECT (scenario, "Could not find %s (%" GST_PTR_FORMAT ")",
894 name, action->structure);
898 _update_well_known_vars (scenario);
900 gst_validate_replace_variables_in_string (action, scenario->priv->vars,
901 tmpvalue, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
906 gst_validate_utils_parse_expression (strval, _set_variable_func,
909 GST_WARNING ("Error while parsing %s: %s (%" GST_PTR_FORMAT ")",
910 strval, error, scenario->priv->vars);
915 } else if (val == -1.0) {
916 *retval = GST_CLOCK_TIME_NONE;
920 gst_util_double_to_fraction (val, &n, &d);
921 *retval = gst_util_uint64_scale_int_round (n, GST_SECOND, d);
923 gst_structure_set (action->structure, name, G_TYPE_UINT64, *retval, NULL);
932 /* WITH SCENARIO LOCK TAKEN */
933 static GstValidateSinkInformation *
934 _find_sink_information (GstValidateScenario * scenario, GstElement * sink)
938 for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
939 GstValidateSinkInformation *sink_info =
940 (GstValidateSinkInformation *) tmp->data;
941 if (sink_info->sink == sink)
947 /* WITH SCENARIO LOCK TAKEN */
948 static GstValidateSeekInformation *
949 _find_seek_information (GstValidateScenario * scenario, guint32 seqnum)
953 for (tmp = scenario->priv->seeks; tmp; tmp = tmp->next) {
954 GstValidateSeekInformation *seek_info =
955 (GstValidateSeekInformation *) tmp->data;
956 if (seek_info->seqnum == seqnum)
963 /* WITH SCENARIO LOCK TAKEN */
965 _validate_sink_information (GstValidateScenario * scenario)
968 gboolean all_sinks_ready = TRUE;
969 gboolean identical_seqnum = TRUE;
970 gboolean transitioning = FALSE;
971 guint32 common_seqnum = GST_SEQNUM_INVALID;
972 guint32 next_seqnum = GST_SEQNUM_INVALID;
973 GstValidateSeekInformation *seek_info;
975 if (scenario->priv->seeks)
976 /* If we have a pending seek, get the expected seqnum to
977 * figure out whether we are transitioning to a seek */
979 ((GstValidateSeekInformation *) scenario->priv->seeks->data)->seqnum;
981 GST_LOG_OBJECT (scenario, "next_seqnum %" G_GUINT32_FORMAT, next_seqnum);
983 for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
984 GstValidateSinkInformation *sink_info =
985 (GstValidateSinkInformation *) tmp->data;
986 GST_DEBUG_OBJECT (sink_info->sink,
987 "seqnum:%" G_GUINT32_FORMAT " segment:%" GST_SEGMENT_FORMAT,
988 sink_info->segment_seqnum, &sink_info->segment);
989 if (sink_info->segment_seqnum == GST_SEQNUM_INVALID)
990 all_sinks_ready = FALSE;
991 else if (sink_info->segment.format == GST_FORMAT_TIME) {
992 /* Are we in the middle of switching segments (from the current
993 * one, or to the next week) ? */
994 if (sink_info->segment_seqnum == scenario->priv->current_seqnum ||
995 sink_info->segment_seqnum == next_seqnum)
996 transitioning = TRUE;
998 /* We are only interested in sinks that handle TIME segments */
999 if (common_seqnum == GST_SEQNUM_INVALID)
1000 common_seqnum = sink_info->segment_seqnum;
1001 else if (common_seqnum != sink_info->segment_seqnum) {
1002 identical_seqnum = FALSE;
1007 /* If not all sinks have received a segment, just return */
1008 if (!all_sinks_ready)
1011 GST_FIXME_OBJECT (scenario,
1012 "All sinks have valid segment. identical_seqnum:%d transitioning:%d seqnum:%"
1013 G_GUINT32_FORMAT " (current:%" G_GUINT32_FORMAT ") seeks:%p",
1014 identical_seqnum, transitioning, common_seqnum,
1015 scenario->priv->current_seqnum, scenario->priv->seeks);
1017 if (!identical_seqnum) {
1018 /* If all sinks received a segment *and* there is a pending seek *and* there
1019 * wasn't one previously, we definitely have a failure */
1020 if (!transitioning && scenario->priv->current_seek == NULL
1021 && scenario->priv->seeks) {
1022 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_INVALID_SEQNUM,
1023 "Not all segments from a given seek have the same seqnum");
1026 /* Otherwise we're either doing the initial preroll (without seek)
1027 * or we are in the middle of switching to another seek */
1031 /* Now check if we have seek data related to that seqnum */
1032 seek_info = _find_seek_information (scenario, common_seqnum);
1034 if (seek_info && seek_info != scenario->priv->current_seek) {
1035 GST_DEBUG_OBJECT (scenario, "Found a corresponding seek !");
1036 /* Updating values */
1037 /* FIXME : Check segment values if needed ! */
1038 /* FIXME : Non-flushing seek, validate here */
1039 if (seek_info->start_type == GST_SEEK_TYPE_SET)
1040 scenario->priv->segment_start = seek_info->start;
1041 if (seek_info->stop_type == GST_SEEK_TYPE_SET)
1042 scenario->priv->segment_stop = seek_info->stop;
1043 if (scenario->priv->target_state == GST_STATE_PAUSED)
1044 scenario->priv->seeked_in_pause = TRUE;
1045 SCENARIO_UNLOCK (scenario);
1046 /* If it's a non-flushing seek, validate it here
1047 * otherwise we will do it when the async_done is received */
1048 if (!(seek_info->flags & GST_SEEK_FLAG_FLUSH))
1049 gst_validate_action_set_done (seek_info->action);
1050 SCENARIO_LOCK (scenario);
1052 /* We always set the current_seek. Can be NULL if no matching */
1053 scenario->priv->current_seek = seek_info;
1054 scenario->priv->current_seqnum = common_seqnum;
1058 * gst_validate_scenario_execute_seek:
1059 * @scenario: The #GstValidateScenario for which to execute a seek action
1060 * @action: The seek action to execute
1061 * @rate: The playback rate of the seek
1062 * @format: The #GstFormat of the seek
1063 * @flags: The #GstSeekFlags of the seek
1064 * @start_type: The #GstSeekType of the start value of the seek
1065 * @start: The start time of the seek
1066 * @stop_type: The #GstSeekType of the stop value of the seek
1067 * @stop: The stop time of the seek
1069 * Executes a seek event on the scenario's pipeline. You should always use
1070 * this method when you want to execute a seek inside a new action type
1071 * so that the scenario state is updated taking into account that seek.
1073 * For more information you should have a look at #gst_event_new_seek
1075 * Returns: %TRUE if the seek could be executed, %FALSE otherwise
1077 GstValidateExecuteActionReturn
1078 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
1079 GstValidateAction * action, gdouble rate, GstFormat format,
1080 GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
1081 GstSeekType stop_type, GstClockTime stop)
1084 GstValidateSeekInformation *seek_info;
1086 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1087 GstValidateScenarioPrivate *priv = scenario->priv;
1088 DECLARE_AND_GET_PIPELINE (scenario, action);
1090 seek = gst_event_new_seek (rate, format, flags, start_type, start,
1093 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
1094 GST_VALIDATE_REPORT_ACTION (scenario, action,
1095 SCENARIO_ACTION_EXECUTION_ERROR,
1096 "Trying to seek in format %d, but not support yet!", format);
1099 seek_info = g_new0 (GstValidateSeekInformation, 1);
1100 seek_info->seqnum = GST_EVENT_SEQNUM (seek);
1101 seek_info->rate = rate;
1102 seek_info->format = format;
1103 seek_info->flags = flags;
1104 seek_info->start = start;
1105 seek_info->stop = stop;
1106 seek_info->start_type = start_type;
1107 seek_info->stop_type = stop_type;
1108 seek_info->action = gst_validate_action_ref (action);
1110 SCENARIO_LOCK (scenario);
1111 priv->seeks = g_list_append (priv->seeks, seek_info);
1112 SCENARIO_UNLOCK (scenario);
1114 gst_event_ref (seek);
1115 if (gst_element_send_event (pipeline, seek)) {
1116 priv->seek_flags = flags;
1117 priv->seek_format = format;
1120 case GST_FORMAT_TIME:
1121 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1122 "Could not execute seek: '(position %" GST_TIME_FORMAT
1123 "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
1124 " stop: %" GST_TIME_FORMAT " Rate %lf'",
1125 GST_TIME_ARGS (action->playback_time), action->name,
1126 action->action_number, action->repeat, GST_TIME_ARGS (start),
1127 GST_TIME_ARGS (stop), rate);
1131 gchar *format_str = g_enum_to_string (GST_TYPE_FORMAT, format);
1133 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1134 "Could not execute seek in format %s '(position %" GST_TIME_FORMAT
1135 "), %s (num %u, missing repeat: %i), seeking to: %" G_GINT64_FORMAT
1136 " stop: %" G_GINT64_FORMAT " Rate %lf'", format_str,
1137 GST_TIME_ARGS (action->playback_time), action->name,
1138 action->action_number, action->repeat, start, stop, rate);
1139 g_free (format_str);
1143 SCENARIO_LOCK (scenario);
1144 priv->seeks = g_list_remove (priv->seeks, seek_info);
1145 SCENARIO_UNLOCK (scenario);
1147 gst_validate_seek_information_free (seek_info);
1148 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1150 gst_event_unref (seek);
1151 gst_object_unref (pipeline);
1157 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
1159 const char *str_format, *str_flags, *str_start_type, *str_stop_type;
1162 guint format = GST_FORMAT_TIME;
1163 GstSeekFlags flags = 0;
1164 guint start_type = GST_SEEK_TYPE_SET;
1166 guint stop_type = GST_SEEK_TYPE_SET;
1167 GstClockTime stop = GST_CLOCK_TIME_NONE;
1169 if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
1170 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1172 gst_structure_get_double (action->structure, "rate", &rate);
1173 if ((str_format = gst_structure_get_string (action->structure, "format")))
1174 gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
1176 if ((str_start_type =
1177 gst_structure_get_string (action->structure, "start_type")))
1178 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
1181 if ((str_stop_type =
1182 gst_structure_get_string (action->structure, "stop_type")))
1183 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
1186 if ((str_flags = gst_structure_get_string (action->structure, "flags")))
1187 flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
1189 gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
1191 return gst_validate_scenario_execute_seek (scenario, action, rate, format,
1192 flags, start_type, start, stop_type, stop);
1196 _pause_action_restore_playing (GstValidateScenario * scenario)
1198 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1201 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1206 gst_validate_printf (scenario, "Back to playing\n");
1208 if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
1209 GST_STATE_CHANGE_FAILURE) {
1210 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
1211 "Failed to set state to playing");
1212 scenario->priv->target_state = GST_STATE_PLAYING;
1215 gst_object_unref (pipeline);
1221 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
1223 gst_structure_id_set_value (consts, field_id, value);
1228 static GstValidateExecuteActionReturn
1229 _execute_define_vars (GstValidateScenario * scenario,
1230 GstValidateAction * action)
1232 gst_structure_foreach (action->structure,
1233 (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
1235 return GST_VALIDATE_EXECUTE_ACTION_OK;
1238 static GstValidateExecuteActionReturn
1239 _set_timed_value (GQuark field_id, const GValue * gvalue,
1240 GstStructure * structure)
1242 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1244 GstClockTime timestamp;
1245 GstTimedValueControlSource *source = NULL;
1246 GstControlBinding *binding;
1247 GstValidateScenario *scenario;
1248 GstValidateAction *action;
1249 GstObject *obj = NULL;
1250 GParamSpec *paramspec = NULL;
1251 const gchar *field = g_quark_to_string (field_id);
1252 const gchar *unused_fields[] =
1253 { "binding-type", "source-type", "interpolation-mode",
1254 "timestamp", "__scenario__", "__action__", "__res__", "repeat",
1255 "playback-time", NULL
1258 if (g_strv_contains (unused_fields, field))
1261 gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1262 "__action__", G_TYPE_POINTER, &action, NULL);
1265 if (G_VALUE_HOLDS_DOUBLE (gvalue))
1266 value = g_value_get_double (gvalue);
1267 else if (G_VALUE_HOLDS_INT (gvalue))
1268 value = (gdouble) g_value_get_int (gvalue);
1270 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1271 "Invalid value type for property '%s': %s",
1272 field, G_VALUE_TYPE_NAME (gvalue));
1276 obj = _get_target_object_property (scenario, action, field, ¶mspec);
1277 if (!obj || !paramspec) {
1278 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1282 REPORT_UNLESS (gst_validate_action_get_clocktime (scenario, action,
1283 "timestamp", ×tamp), err,
1284 "Could get timestamp on %" GST_PTR_FORMAT, action->structure);
1286 binding = gst_object_get_control_binding (obj, paramspec->name);
1290 const gchar *interpolation_mode =
1291 gst_structure_get_string (action->structure, "interpolation-mode");
1292 const gchar *source_type_name =
1293 gst_structure_get_string (action->structure, "source-type");
1295 if (source_type_name) {
1296 source_type = g_type_from_name (source_type_name);
1298 REPORT_UNLESS (g_type_is_a (source_type,
1299 GST_TYPE_TIMED_VALUE_CONTROL_SOURCE), err,
1300 "Source type '%s' is not supported", source_type_name);
1302 source_type = GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
1305 source = g_object_new (source_type, NULL);
1306 gst_object_ref_sink (source);
1307 if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
1308 if (interpolation_mode)
1309 REPORT_UNLESS (gst_validate_utils_enum_from_str
1310 (GST_TYPE_INTERPOLATION_MODE, interpolation_mode, &mode), err,
1311 "Could not convert interpolation-mode '%s'", interpolation_mode);
1314 mode = GST_INTERPOLATION_MODE_LINEAR;
1316 g_object_set (source, "mode", mode, NULL);
1319 if (!g_strcmp0 (gst_structure_get_string (action->structure,
1320 "binding-type"), "direct-absolute")) {
1322 gst_direct_control_binding_new_absolute (obj, paramspec->name,
1323 GST_CONTROL_SOURCE (source));
1326 gst_direct_control_binding_new (obj, paramspec->name,
1327 GST_CONTROL_SOURCE (source));
1330 gst_object_add_control_binding (obj, binding);
1332 g_object_get (binding, "control-source", &source, NULL);
1335 REPORT_UNLESS (GST_IS_TIMED_VALUE_CONTROL_SOURCE (source), err,
1336 "Could not find timed value control source on %s", field);
1338 REPORT_UNLESS (gst_timed_value_control_source_set (source, timestamp, value),
1339 err, "Could not set %s=%f at %" GST_TIME_FORMAT, field, value,
1340 GST_TIME_ARGS (timestamp));
1342 gst_object_unref (obj);
1343 gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1348 gst_clear_object (&obj);
1349 gst_structure_set (structure, "__res__", G_TYPE_INT,
1350 GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED, NULL);
1355 static GstValidateExecuteActionReturn
1356 _set_timed_value_property (GstValidateScenario * scenario,
1357 GstValidateAction * action)
1359 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1361 gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1362 action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1364 gst_structure_foreach (action->structure,
1365 (GstStructureForeachFunc) _set_timed_value, action->structure);
1366 gst_structure_get_int (action->structure, "__res__", &res);
1367 gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1374 static GstValidateExecuteActionReturn
1375 _check_property (GstValidateScenario * scenario, GstValidateAction * action,
1376 gpointer object, const gchar * propname, const GValue * expected_value)
1378 GValue cvalue = G_VALUE_INIT;
1380 g_value_init (&cvalue, G_VALUE_TYPE (expected_value));
1381 g_object_get_property (object, propname, &cvalue);
1383 if (gst_value_compare (&cvalue, expected_value) != GST_VALUE_EQUAL) {
1384 gchar *expected = gst_value_serialize (expected_value), *observed =
1385 gst_value_serialize (&cvalue);
1387 GST_VALIDATE_REPORT_ACTION (scenario, action,
1388 SCENARIO_ACTION_EXECUTION_ERROR,
1390 "::%s expected value: '(%s)%s' different than observed: '(%s)%s'",
1391 object, propname, G_VALUE_TYPE_NAME (&cvalue), expected,
1392 G_VALUE_TYPE_NAME (expected_value), observed);
1397 g_value_reset (&cvalue);
1398 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1400 g_value_reset (&cvalue);
1402 return GST_VALIDATE_EXECUTE_ACTION_OK;
1406 static GstValidateExecuteActionReturn
1407 _set_or_check_properties (GQuark field_id, const GValue * value,
1408 GstStructure * structure)
1410 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1411 GstValidateScenario *scenario;
1412 GstValidateAction *action;
1413 GstObject *obj = NULL;
1414 GParamSpec *paramspec = NULL;
1415 const gchar *field = g_quark_to_string (field_id);
1416 const gchar *unused_fields[] = { "__scenario__", "__action__", "__res__",
1417 "playback-time", "repeat", NULL
1420 if (g_strv_contains (unused_fields, field))
1423 gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1424 "__action__", G_TYPE_POINTER, &action, NULL);
1426 obj = _get_target_object_property (scenario, action, field, ¶mspec);
1427 if (!obj || !paramspec) {
1428 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1431 if (gst_structure_has_name (action->structure, "set-properties"))
1432 res = gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
1433 G_OBJECT (obj), paramspec->name, value, action->priv->optional);
1435 res = _check_property (scenario, action, obj, paramspec->name, value);
1438 gst_clear_object (&obj);
1439 if (!gst_structure_has_field (structure, "__res__")
1440 || res != GST_VALIDATE_EXECUTE_ACTION_OK)
1441 gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1445 static GstValidateExecuteActionReturn
1446 _execute_set_or_check_properties (GstValidateScenario * scenario,
1447 GstValidateAction * action)
1449 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1451 gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1452 action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1454 gst_structure_foreach (action->structure,
1455 (GstStructureForeachFunc) _set_or_check_properties, action->structure);
1456 gst_structure_get_int (action->structure, "__res__", &res);
1457 gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1463 static GstValidateExecuteActionReturn
1464 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
1467 const gchar *str_state;
1468 GstStateChangeReturn ret;
1469 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1471 DECLARE_AND_GET_PIPELINE (scenario, action);
1473 g_return_val_if_fail ((str_state =
1474 gst_structure_get_string (action->structure, "state")), FALSE);
1476 g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
1477 str_state, &state), FALSE);
1480 scenario->priv->target_state = state;
1481 scenario->priv->changing_state = TRUE;
1482 scenario->priv->seeked_in_pause = FALSE;
1484 ret = gst_element_set_state (pipeline, state);
1485 if (ret == GST_STATE_CHANGE_FAILURE) {
1486 scenario->priv->changing_state = FALSE;
1487 GST_VALIDATE_REPORT_ACTION (scenario, action, STATE_CHANGE_FAILURE,
1488 "Failed to set state to %s", str_state);
1490 /* Nothing async on failure, action will be removed automatically */
1491 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1493 } else if (ret == GST_STATE_CHANGE_ASYNC) {
1495 scenario->priv->needs_async_done = TRUE;
1496 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1501 scenario->priv->changing_state = FALSE;
1504 gst_object_unref (pipeline);
1509 static GstValidateExecuteActionReturn
1510 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
1512 GstClockTime duration = 0;
1513 GstValidateExecuteActionReturn ret;
1515 gst_validate_action_get_clocktime (scenario, action, "duration", &duration);
1516 gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
1518 GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
1519 GST_TIME_ARGS (duration));
1521 ret = _execute_set_state (scenario, action);
1523 if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
1524 g_timeout_add (GST_TIME_AS_MSECONDS (duration),
1525 (GSourceFunc) _pause_action_restore_playing, scenario);
1530 static GstValidateExecuteActionReturn
1531 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
1533 GST_DEBUG ("Playing back");
1535 gst_structure_set (action->structure, "state", G_TYPE_STRING,
1539 return _execute_set_state (scenario, action);
1543 _action_sets_state (GstValidateAction * action)
1548 if (g_strcmp0 (action->type, "set-state") == 0)
1551 if (g_strcmp0 (action->type, "play") == 0)
1554 if (g_strcmp0 (action->type, "pause") == 0)
1562 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
1564 GstValidateScenarioPrivate *priv = scenario->priv;
1566 if (priv->max_dropped == -1 || priv->dropped == -1)
1569 GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
1570 priv->dropped, priv->max_dropped);
1572 if (priv->dropped > priv->max_dropped) {
1573 GST_VALIDATE_REPORT (scenario, CONFIG_TOO_MANY_BUFFERS_DROPPED,
1574 "Too many buffers have been dropped: %d (max allowed: %d)",
1575 priv->dropped, priv->max_dropped);
1579 static GstValidateExecuteActionReturn
1580 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
1584 DECLARE_AND_GET_PIPELINE (scenario, action);
1586 GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
1587 GST_TIME_ARGS (action->playback_time));
1589 ret = gst_element_send_event (pipeline, gst_event_new_eos ());
1590 gst_object_unref (pipeline);
1592 return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
1593 GST_VALIDATE_EXECUTE_ACTION_ERROR;
1597 find_input_selector (GValue * velement, const gchar * type)
1599 GstElement *element = g_value_get_object (velement);
1602 if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
1603 GstPad *srcpad = gst_element_get_static_pad (element, "src");
1606 GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
1610 gst_structure_get_name (gst_caps_get_structure (caps, 0));
1611 gboolean found = FALSE;
1613 if (g_strcmp0 (type, "audio") == 0)
1614 found = g_str_has_prefix (mime, "audio/");
1615 else if (g_strcmp0 (type, "video") == 0)
1616 found = g_str_has_prefix (mime, "video/")
1617 && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
1618 else if (g_strcmp0 (type, "text") == 0)
1619 found = g_str_has_prefix (mime, "text/")
1620 || g_str_has_prefix (mime, "subtitle/")
1621 || g_str_has_prefix (mime, "video/x-dvd-subpicture");
1627 gst_caps_unref (caps);
1628 gst_object_unref (srcpad);
1635 find_input_selector_with_type (GstBin * bin, const gchar * type)
1637 GValue result = { 0, };
1638 GstElement *input_selector = NULL;
1639 GstIterator *iterator = gst_bin_iterate_recurse (bin);
1641 if (gst_iterator_find_custom (iterator,
1642 (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
1643 input_selector = g_value_get_object (&result);
1645 gst_iterator_free (iterator);
1647 return input_selector;
1651 find_nth_sink_pad (GstElement * element, int index)
1653 GstIterator *iterator;
1654 gboolean done = FALSE;
1656 int dec_index = index;
1657 GValue data = { 0, };
1659 iterator = gst_element_iterate_sink_pads (element);
1661 switch (gst_iterator_next (iterator, &data)) {
1662 case GST_ITERATOR_OK:
1665 pad = g_value_get_object (&data);
1668 g_value_reset (&data);
1670 case GST_ITERATOR_RESYNC:
1671 gst_iterator_resync (iterator);
1674 case GST_ITERATOR_ERROR:
1677 case GST_ITERATOR_DONE:
1682 gst_iterator_free (iterator);
1687 find_sink_pad_index (GstElement * element, GstPad * pad)
1689 GstIterator *iterator;
1690 gboolean done = FALSE;
1692 GValue data = { 0, };
1694 iterator = gst_element_iterate_sink_pads (element);
1696 switch (gst_iterator_next (iterator, &data)) {
1697 case GST_ITERATOR_OK:
1698 if (pad == g_value_get_object (&data)) {
1703 g_value_reset (&data);
1705 case GST_ITERATOR_RESYNC:
1706 gst_iterator_resync (iterator);
1709 case GST_ITERATOR_ERROR:
1712 case GST_ITERATOR_DONE:
1717 gst_iterator_free (iterator);
1721 static GstPadProbeReturn
1722 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1723 GstValidateAction * action)
1725 if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1726 gst_validate_action_set_done (action);
1728 return GST_PAD_PROBE_REMOVE;
1731 return GST_PAD_PROBE_OK;
1734 static GstValidateExecuteActionReturn
1735 execute_switch_track_default (GstValidateScenario * scenario,
1736 GstValidateAction * action)
1739 gboolean relative = FALSE;
1740 const gchar *type, *str_index;
1741 GstElement *input_selector;
1742 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1744 DECLARE_AND_GET_PIPELINE (scenario, action);
1746 if (!(type = gst_structure_get_string (action->structure, "type")))
1749 /* First find an input selector that has the right type */
1750 input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1751 if (input_selector) {
1752 GstState state, next;
1753 GstPad *pad, *cpad, *srcpad;
1755 ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1756 str_index = gst_structure_get_string (action->structure, "index");
1758 if (str_index == NULL) {
1759 if (!gst_structure_get_uint (action->structure, "index", &index)) {
1760 GST_WARNING ("No index given, defaulting to +1");
1765 relative = strchr ("+-", str_index[0]) != NULL;
1766 index = g_ascii_strtoll (str_index, NULL, 10);
1769 if (relative) { /* We are changing track relatively to current track */
1772 g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1774 int current_index = find_sink_pad_index (input_selector, pad);
1776 index = (current_index + index) % npads;
1777 gst_object_unref (pad);
1781 pad = find_nth_sink_pad (input_selector, index);
1782 g_object_get (input_selector, "active-pad", &cpad, NULL);
1783 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1784 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1785 srcpad = gst_element_get_static_pad (input_selector, "src");
1787 gst_pad_add_probe (srcpad,
1788 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1789 (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1790 ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1791 gst_object_unref (srcpad);
1794 g_object_set (input_selector, "active-pad", pad, NULL);
1795 gst_object_unref (pad);
1796 gst_object_unref (cpad);
1797 gst_object_unref (input_selector);
1802 /* No selector found -> Failed */
1804 gst_object_unref (pipeline);
1809 static GstPadProbeReturn
1810 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1811 GstValidateAction * action)
1813 if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1814 gst_validate_action_set_done (action);
1815 return GST_PAD_PROBE_REMOVE;
1817 return GST_PAD_PROBE_OK;
1820 static GstValidateExecuteActionReturn
1821 execute_switch_track_pb (GstValidateScenario * scenario,
1822 GstValidateAction * action)
1825 const gchar *type, *str_index;
1827 gint flags, current, tflag;
1828 gchar *tmp, *current_txt;
1830 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1831 gboolean relative = FALSE, disabling = FALSE;
1833 DECLARE_AND_GET_PIPELINE (scenario, action);
1835 if (!(type = gst_structure_get_string (action->structure, "type")))
1838 tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1840 current_txt = g_strdup_printf ("current-%s", type);
1842 tmp = g_strdup_printf ("n-%s", type);
1843 g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, ¤t,
1846 /* Don't try to use -1 */
1852 if (gst_structure_has_field (action->structure, "disable")) {
1856 } else if (!(str_index =
1857 gst_structure_get_string (action->structure, "index"))) {
1858 if (!gst_structure_get_int (action->structure, "index", &index)) {
1859 GST_WARNING ("No index given, defaulting to +1");
1864 relative = strchr ("+-", str_index[0]) != NULL;
1865 index = g_ascii_strtoll (str_index, NULL, 10);
1868 if (relative) { /* We are changing track relatively to current track */
1870 GST_VALIDATE_REPORT_ACTION (scenario, action,
1871 SCENARIO_ACTION_EXECUTION_ERROR,
1872 "Trying to execute a relative %s for %s track when there"
1873 " is no track of this type available on current stream.",
1874 action->type, type);
1876 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1880 index = (current + index) % n;
1884 GstState state, next;
1885 GstPad *oldpad, *newpad;
1886 tmp = g_strdup_printf ("get-%s-pad", type);
1887 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1888 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1890 gst_validate_printf (action, "Switching to track number: %i,"
1891 " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1892 GST_DEBUG_PAD_NAME (newpad));
1896 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1897 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1898 GstPad *srcpad = NULL;
1899 GstElement *combiner = NULL;
1900 if (newpad == oldpad) {
1901 srcpad = gst_pad_get_peer (oldpad);
1902 } else if (newpad) {
1903 combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1905 srcpad = gst_element_get_static_pad (combiner, "src");
1906 gst_object_unref (combiner);
1911 gst_pad_add_probe (srcpad,
1912 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1913 (GstPadProbeCallback) _check_pad_event_selection_done, action,
1915 gst_object_unref (srcpad);
1917 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1919 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1923 gst_object_unref (oldpad);
1924 gst_object_unref (newpad);
1926 gst_validate_printf (action, "Disabling track type %s", type);
1929 g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1930 g_free (current_txt);
1933 gst_object_unref (pipeline);
1937 static GstStreamType
1938 stream_type_from_string (const gchar * type)
1940 if (!g_strcmp0 (type, "video"))
1941 return GST_STREAM_TYPE_VIDEO;
1942 else if (!g_strcmp0 (type, "text"))
1943 return GST_STREAM_TYPE_TEXT;
1946 return GST_STREAM_TYPE_AUDIO;
1949 /* Return a list of stream ID all the currently selected streams but the ones
1952 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1954 GList *streams = NULL, *l;
1956 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1957 GstStream *s = l->data;
1959 if (gst_stream_get_stream_type (s) != type) {
1960 streams = g_list_append (streams, (gpointer) s->stream_id);
1968 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1969 GstStreamType type, gint index, gboolean relative)
1972 guint i, n = 0, current = 0;
1973 GList *result = NULL, *l;
1974 GstStream *streams[256], *s, *current_stream = NULL;
1976 /* Keep all streams which are not @type */
1977 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1980 if (gst_stream_get_stream_type (s) != type) {
1981 result = g_list_append (result, (gpointer) s->stream_id);
1982 } else if (!current_stream) {
1983 /* Assume the stream we want to switch from is the first one */
1988 /* Calculate the number of @type streams */
1989 nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1990 for (i = 0; i < nb_streams; i++) {
1991 s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1993 if (gst_stream_get_stream_type (s) == type) {
1997 && !g_strcmp0 (s->stream_id, current_stream->stream_id))
2004 if (G_UNLIKELY (n == 0)) {
2005 GST_ERROR ("No streams available of the required type");
2009 if (relative) { /* We are changing track relatively to current track */
2010 index = (current + index) % n;
2014 /* Add the new stream we want to switch to */
2017 gst_validate_printf (action, "Switching from stream %s to %s",
2018 current_stream ? current_stream->stream_id : "", s->stream_id);
2020 return g_list_append (result, (gpointer) s->stream_id);
2023 static GstValidateExecuteActionReturn
2024 execute_switch_track_pb3 (GstValidateScenario * scenario,
2025 GstValidateAction * action)
2027 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
2028 GstValidateScenarioPrivate *priv = scenario->priv;
2030 GstStreamType stype;
2031 const gchar *type, *str_index;
2032 GList *new_streams = NULL;
2033 GstValidatePipelineMonitor *monitor;
2034 DECLARE_AND_GET_PIPELINE (scenario, action);
2036 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2037 pipeline, "validate-monitor"));
2039 if (!monitor->stream_collection) {
2040 GST_VALIDATE_REPORT_ACTION (scenario, action,
2041 SCENARIO_ACTION_EXECUTION_ERROR,
2042 "No stream collection message received on the bus, "
2043 "can not switch track.");
2044 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2048 if (!monitor->streams_selected) {
2049 GST_VALIDATE_REPORT_ACTION (scenario, action,
2050 SCENARIO_ACTION_EXECUTION_ERROR,
2051 "No streams selected message received on the bus");
2052 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2056 type = gst_structure_get_string (action->structure, "type");
2057 stype = stream_type_from_string (type);
2059 if (gst_structure_has_field (action->structure, "disable")) {
2060 gst_validate_printf (action, "Disabling track type %s", type);
2061 new_streams = disable_stream (monitor, stype);
2063 gboolean relative = FALSE;
2065 if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
2066 if (!gst_structure_get_int (action->structure, "index", &index)) {
2067 GST_WARNING ("No index given, defaulting to +1");
2072 relative = strchr ("+-", str_index[0]) != NULL;
2073 index = g_ascii_strtoll (str_index, NULL, 10);
2076 new_streams = switch_stream (monitor, action, stype, index, relative);
2079 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
2080 ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
2081 (GDestroyNotify) g_list_free);
2083 if (!gst_element_send_event (pipeline,
2084 gst_event_new_select_streams (new_streams))) {
2085 GST_VALIDATE_REPORT_ACTION (scenario, action,
2086 SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
2087 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2091 priv->pending_switch_track = action;
2092 if (scenario->priv->target_state > GST_STATE_PAUSED) {
2093 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2095 gst_validate_action_ref (action);
2096 res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
2100 gst_object_unref (pipeline);
2105 static GstValidateExecuteActionReturn
2106 _execute_switch_track (GstValidateScenario * scenario,
2107 GstValidateAction * action)
2109 GstValidatePipelineMonitor *monitor;
2111 DECLARE_AND_GET_PIPELINE (scenario, action);
2113 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2114 pipeline, "validate-monitor"));
2115 gst_object_unref (pipeline);
2117 if (monitor->is_playbin)
2118 return execute_switch_track_pb (scenario, action);
2119 else if (monitor->is_playbin3)
2120 return execute_switch_track_pb3 (scenario, action);
2122 return execute_switch_track_default (scenario, action);
2125 static GstValidateExecuteActionReturn
2126 _execute_set_rank_or_disable_feature (GstValidateScenario * scenario,
2127 GstValidateAction * action)
2129 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2131 GList *features, *origlist;
2133 GstPluginFeature *feature;
2135 gboolean removing_feature =
2136 gst_structure_has_name (action->structure, "remove-plugin-feature");
2137 GstRegistry *registry = gst_registry_get ();
2140 (name = gst_structure_get_string (action->structure, "feature-name")) ||
2141 (name = gst_structure_get_string (action->structure, "name")), done,
2142 "Could not find the name of the plugin/feature(s) to tweak");
2144 if (removing_feature)
2146 (gst_structure_get_uint (action->structure, "rank", &rank)) ||
2147 (gst_structure_get_int (action->structure, "rank", (gint *) & rank)),
2148 done, "Could not get rank to set on %s", name);
2150 feature = gst_registry_lookup_feature (registry, name);
2152 if (removing_feature)
2153 gst_plugin_feature_set_rank (feature, rank);
2155 gst_registry_remove_feature (registry, feature);
2156 gst_object_unref (feature);
2161 REPORT_UNLESS ((plugin = gst_registry_find_plugin (registry, name)),
2162 done, "Could not find %s", name);
2164 if (removing_feature) {
2165 gst_registry_remove_plugin (registry, plugin);
2169 origlist = features =
2170 gst_registry_get_feature_list_by_plugin (registry,
2171 gst_plugin_get_name (plugin));
2172 for (; features; features = features->next)
2173 gst_plugin_feature_set_rank (features->data, rank);
2174 gst_plugin_feature_list_free (origlist);
2180 static inline gboolean
2181 _add_execute_actions_gsource (GstValidateScenario * scenario)
2183 GstValidateScenarioPrivate *priv = scenario->priv;
2185 SCENARIO_LOCK (scenario);
2186 if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
2187 && priv->signal_handler_id == 0 && priv->wait_message_action == NULL) {
2188 if (!scenario->priv->action_execution_interval)
2189 priv->execute_actions_source_id =
2190 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
2191 (GSourceFunc) execute_next_action,
2192 gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2194 priv->execute_actions_source_id =
2195 g_timeout_add_full (G_PRIORITY_DEFAULT,
2196 scenario->priv->action_execution_interval,
2197 (GSourceFunc) execute_next_action,
2198 gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2199 SCENARIO_UNLOCK (scenario);
2201 GST_DEBUG_OBJECT (scenario, "Start checking position again");
2204 SCENARIO_UNLOCK (scenario);
2206 GST_LOG_OBJECT (scenario, "No need to start a new gsource");
2211 _get_position (GstValidateScenario * scenario,
2212 GstValidateAction * act, GstClockTime * position)
2214 gboolean has_pos = FALSE, has_dur = FALSE;
2215 GstClockTime duration = -1;
2217 GstValidateScenarioPrivate *priv = scenario->priv;
2218 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2221 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2226 has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
2227 (gint64 *) position)
2228 && GST_CLOCK_TIME_IS_VALID (*position);
2230 gst_element_query_duration (pipeline, GST_FORMAT_TIME,
2231 (gint64 *) & duration)
2232 && GST_CLOCK_TIME_IS_VALID (duration);
2234 if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
2235 act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2236 GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
2237 GST_TIME_ARGS (*position));
2242 if (has_pos && has_dur && !priv->got_eos) {
2243 if (*position > duration) {
2244 _add_execute_actions_gsource (scenario);
2245 GST_VALIDATE_REPORT (scenario,
2246 QUERY_POSITION_SUPERIOR_DURATION,
2247 "Reported position %" GST_TIME_FORMAT " > reported duration %"
2248 GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
2255 gst_object_unref (pipeline);
2259 gst_object_unref (pipeline);
2264 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
2265 GstClockTime * position, gdouble * rate)
2269 GstClockTime start_with_tolerance, stop_with_tolerance;
2270 GstValidateScenarioPrivate *priv = scenario->priv;
2271 GstElement *pipeline;
2273 if (!_get_position (scenario, act, position))
2276 GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
2277 GST_TIME_ARGS (*position));
2279 /* Check if playback is within seek segment */
2280 start_with_tolerance = (priv->segment_start <
2281 priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
2282 stop_with_tolerance =
2283 GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
2284 priv->seek_pos_tol : -1;
2286 if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
2287 && *position > stop_with_tolerance)
2288 || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
2289 && *position < start_with_tolerance
2290 && priv->seek_format == GST_FORMAT_TIME)) {
2292 GST_VALIDATE_REPORT_ACTION (scenario, act, QUERY_POSITION_OUT_OF_SEGMENT,
2293 "Current position %" GST_TIME_FORMAT " not in the expected range [%"
2294 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
2295 GST_TIME_ARGS (start_with_tolerance),
2296 GST_TIME_ARGS (stop_with_tolerance));
2299 pipeline = gst_validate_scenario_get_pipeline (scenario);
2300 if (pipeline == NULL) {
2301 GST_INFO_OBJECT (scenario, "No pipeline set anymore");
2306 query = gst_query_new_segment (GST_FORMAT_DEFAULT);
2307 if (gst_element_query (GST_ELEMENT (pipeline), query))
2308 gst_query_parse_segment (query, rate, NULL, NULL, NULL);
2309 gst_query_unref (query);
2310 gst_object_unref (pipeline);
2312 if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE &&
2313 priv->seek_format == GST_FORMAT_TIME) {
2315 && (GstClockTime) ABS (GST_CLOCK_DIFF (*position,
2316 priv->segment_start)) > priv->seek_pos_tol) {
2317 priv->seeked_in_pause = FALSE;
2318 GST_VALIDATE_REPORT_ACTION (scenario, act,
2319 EVENT_SEEK_RESULT_POSITION_WRONG,
2320 "Reported position after accurate seek in PAUSED state should be exactly"
2321 " what the user asked for. Position %" GST_TIME_FORMAT
2322 " is not not the expected one: %" GST_TIME_FORMAT,
2323 GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
2331 _check_message_type (GstValidateScenario * scenario, GstValidateAction * act,
2332 GstMessage * message)
2334 return act && message
2335 && !g_strcmp0 (gst_structure_get_string (act->structure, "on-message"),
2336 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2340 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
2341 GstClockTime position, gdouble rate)
2343 GstElement *pipeline = NULL;
2346 GST_DEBUG_OBJECT (scenario, "No action to execute");
2351 pipeline = gst_validate_scenario_get_pipeline (scenario);
2352 if (pipeline == NULL) {
2354 if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
2355 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
2356 GST_VALIDATE_REPORT_ACTION (scenario, act,
2357 SCENARIO_ACTION_EXECUTION_ERROR,
2358 "Trying to execute an %s action after the pipeline has been destroyed"
2359 " but the type has not been marked as "
2360 "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
2363 } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2364 GST_VALIDATE_REPORT_ACTION (scenario, act,
2365 SCENARIO_ACTION_EXECUTION_ERROR,
2366 "Trying to execute action %s with playback time %" GST_TIME_FORMAT
2367 " after the pipeline has been destroyed. It is impossible"
2368 " to execute an action with a playback time specified"
2369 " after the pipeline has been destroyed", act->type,
2370 GST_TIME_ARGS (act->playback_time));
2375 GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
2378 } else if (scenario->priv->got_eos) {
2379 GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
2380 scenario->priv->got_eos = FALSE;
2381 } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
2382 GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
2383 "just executing actions");
2386 } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
2387 GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
2390 } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
2391 GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
2392 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2393 GST_TIME_ARGS (act->playback_time));
2396 } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
2397 GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
2398 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2399 GST_TIME_ARGS (act->playback_time));
2405 gst_object_unref (pipeline);
2409 gst_clear_object (&pipeline);
2414 _set_action_playback_time (GstValidateScenario * scenario,
2415 GstValidateAction * action)
2417 if (!gst_validate_action_get_clocktime (scenario, action,
2418 "playback-time", &action->playback_time)) {
2419 gst_validate_error_structure (action,
2420 "Could not parse playback-time in %" GST_PTR_FORMAT, action->structure);
2425 gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
2426 action->playback_time, NULL);
2432 gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
2434 GstValidateAction *action;
2435 GstValidateScenarioPrivate *priv = self->priv;
2440 action = (GstValidateAction *) priv->actions->data;
2441 if (!action->priv->needs_playback_parsing)
2444 if (!_set_action_playback_time (self, action)) {
2445 GST_ERROR_OBJECT (self, "Could not set playback_time!");
2449 action->priv->needs_playback_parsing = FALSE;
2455 _foreach_find_iterator (GQuark field_id, GValue * value,
2456 GstValidateAction * action)
2458 const gchar *field = g_quark_to_string (field_id);
2460 if (!g_strcmp0 (field, "actions"))
2463 if (!GST_VALUE_HOLDS_INT_RANGE (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
2464 gst_validate_error_structure (action,
2465 "Unsupported iterator type `%s` for %s"
2466 ". Only ranges (`[(int)start, (int)stop, [(int)step]]`) and arrays "
2467 " (`<item1, item2>`) are supported", field, G_VALUE_TYPE_NAME (value));
2471 if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
2472 gst_validate_error_structure (action, "Wrong iterator syntax, "
2473 " only one iterator field is supported.");
2477 GST_VALIDATE_ACTION_RANGE_NAME (action) = field;
2483 * gst_validate_execute_action:
2484 * @action_type: The #GstValidateActionType to execute
2485 * @action: (transfer full): The #GstValidateAction to execute
2489 GstValidateExecuteActionReturn
2490 gst_validate_execute_action (GstValidateActionType * action_type,
2491 GstValidateAction * action)
2493 GstValidateExecuteActionReturn res;
2494 GstValidateScenario *scenario;
2496 g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
2497 GST_VALIDATE_EXECUTE_ACTION_ERROR);
2499 scenario = gst_validate_action_get_scenario (action);
2500 g_assert (scenario);
2502 action->priv->context = g_main_context_ref (scenario->priv->context);
2503 if (action_type->prepare) {
2504 res = action_type->prepare (action);
2505 if (res == GST_VALIDATE_EXECUTE_ACTION_DONE) {
2506 gst_validate_print_action (action, NULL);
2507 return GST_VALIDATE_EXECUTE_ACTION_OK;
2510 if (res != GST_VALIDATE_EXECUTE_ACTION_OK) {
2511 GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
2512 " could not be prepared", action->structure);
2514 gst_object_unref (scenario);
2519 gst_validate_print_action (action, NULL);
2521 action->priv->execution_time = gst_util_get_timestamp ();
2522 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
2523 action_type->priv->n_calls++;
2524 res = action_type->execute (scenario, action);
2525 gst_object_unref (scenario);
2530 /* scenario can be NULL **only** if the action is a CONFIG action and
2531 * add_to_lists is FALSE */
2532 static GstValidateExecuteActionReturn
2533 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
2534 GstStructure * structure, gboolean add_to_lists)
2536 gdouble playback_time;
2537 gboolean is_config = FALSE;
2538 GstValidateActionType *action_type;
2539 GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
2540 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_NONE;
2541 gboolean optional, needs_parsing = FALSE;
2543 action->type = gst_structure_get_name (structure);
2544 action_type = _find_action_type (action->type);
2547 GST_ERROR_OBJECT (scenario, "Action type %s no found",
2548 gst_structure_get_name (structure));
2550 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2553 if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
2554 gst_structure_get_double (structure, "playback_time", &playback_time)) {
2555 action->playback_time = playback_time * GST_SECOND;
2556 } else if (gst_structure_has_field_typed (structure, "playback-time",
2558 || gst_structure_has_field_typed (structure, "playback_time",
2561 if (add_to_lists && priv) {
2562 action->priv->needs_playback_parsing = TRUE;
2563 needs_parsing = TRUE;
2566 GST_INFO_OBJECT (scenario,
2567 "No playback time for action %" GST_PTR_FORMAT, structure);
2569 if (!gst_validate_utils_get_clocktime (structure,
2570 "timeout", &action->priv->timeout)) {
2571 GST_INFO_OBJECT (scenario,
2572 "No timeout time for action %" GST_PTR_FORMAT, structure);
2575 action->structure = gst_structure_copy (structure);
2577 if (!(action->name = gst_structure_get_string (action->structure, "name")))
2580 if (!action->priv->main_structure)
2581 action->priv->main_structure = gst_structure_copy (structure);
2583 if (gst_structure_get_boolean (structure, "optional", &optional)) {
2584 if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
2585 GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
2586 gst_structure_get_name (structure));
2587 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2589 action->priv->optional = optional;
2592 if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
2593 (gst_structure_get_boolean (action->structure, "as-config",
2594 &is_config) && is_config == TRUE)) {
2596 action_type->priv->n_calls++;
2597 res = action_type->execute (scenario, action);
2598 gst_validate_print_action (action, NULL);
2607 GstValidateActionType *type = _find_action_type (action->type);
2608 gboolean can_execute_on_addition =
2609 type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
2610 && !GST_CLOCK_TIME_IS_VALID (action->playback_time)
2611 && !gst_structure_has_field (action->structure, "on-message");
2614 can_execute_on_addition = FALSE;
2616 if (can_execute_on_addition) {
2619 for (tmp = priv->actions; tmp; tmp = tmp->next) {
2620 GstValidateAction *act = (GstValidateAction *) tmp->data;
2621 if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2622 can_execute_on_addition = FALSE;
2629 if (can_execute_on_addition) {
2630 SCENARIO_LOCK (scenario);
2631 priv->on_addition_actions = g_list_append (priv->on_addition_actions,
2633 SCENARIO_UNLOCK (scenario);
2636 priv->actions = g_list_append (priv->actions, action);
2644 gst_validate_scenario_execute_next_or_restart_looping (GstValidateScenario *
2647 /* Recurse to the next action if it is possible
2648 * to execute right away */
2649 if (!scenario->priv->execute_on_idle) {
2650 GST_DEBUG_OBJECT (scenario, "linking next action execution");
2652 return execute_next_action (scenario);
2654 _add_execute_actions_gsource (scenario);
2655 GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2658 return G_SOURCE_CONTINUE;
2661 /* This is the main action execution function
2662 * it checks whether it is time to run the next action
2663 * and if it is the case executes it.
2665 * If the 'execute-on-idle' property is not TRUE,
2666 * the function will recurse while the actions are run
2670 execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
2673 GstClockTime position = -1;
2674 GstValidateAction *act = NULL;
2675 GstValidateActionType *type;
2677 GstValidateScenarioPrivate *priv = scenario->priv;
2679 if (priv->buffering) {
2680 GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
2682 return G_SOURCE_CONTINUE;
2685 if (priv->changing_state || priv->needs_async_done) {
2686 GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
2687 return G_SOURCE_CONTINUE;
2690 if (scenario->priv->actions)
2691 act = scenario->priv->actions->data;
2694 _check_scenario_is_done (scenario);
2695 return G_SOURCE_CONTINUE;
2698 if (message && GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS
2699 && act->playback_time != GST_CLOCK_TIME_NONE) {
2700 GST_VALIDATE_REPORT_ACTION (scenario, act,
2701 SCENARIO_ACTION_ENDED_EARLY,
2702 "Got EOS before action playback time %" GST_TIME_FORMAT,
2703 GST_TIME_ARGS (act->playback_time));
2704 goto execute_action;
2707 switch (act->priv->state) {
2708 case GST_VALIDATE_EXECUTE_ACTION_NONE:
2709 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2711 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2712 return G_SOURCE_CONTINUE;
2713 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2714 if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
2715 GstClockTime etime =
2716 gst_util_get_timestamp () - act->priv->execution_time;
2718 if (etime > act->priv->timeout) {
2719 gchar *str = gst_structure_to_string (act->structure);
2721 GST_VALIDATE_REPORT_ACTION (scenario, act,
2722 SCENARIO_ACTION_EXECUTION_ERROR,
2723 "Action %s timed out after: %" GST_TIME_FORMAT, str,
2724 GST_TIME_ARGS (etime));
2729 GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2732 return G_SOURCE_CONTINUE;
2734 GST_ERROR ("State is %d", act->priv->state);
2735 g_assert_not_reached ();
2739 if (!_check_message_type (scenario, act, message))
2740 return G_SOURCE_CONTINUE;
2741 } else if ((act && gst_structure_get_string (act->structure, "on-message") &&
2742 !GST_CLOCK_TIME_IS_VALID (act->playback_time)) ||
2743 (!_check_position (scenario, act, &position, &rate))) {
2744 return G_SOURCE_CONTINUE;
2747 if (!_should_execute_action (scenario, act, position, rate)) {
2748 _add_execute_actions_gsource (scenario);
2750 return G_SOURCE_CONTINUE;
2754 type = _find_action_type (act->type);
2756 GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2757 " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2758 priv->seeked_in_pause = FALSE;
2761 gst_structure_remove_field (act->structure, "playback-time");
2763 gst_structure_remove_field (act->structure, "on-message");
2765 act->priv->state = gst_validate_execute_action (type, act);
2766 switch (act->priv->state) {
2767 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2768 GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2771 SCENARIO_LOCK (scenario);
2772 priv->execute_actions_source_id = 0;
2773 SCENARIO_UNLOCK (scenario);
2775 return G_SOURCE_CONTINUE;
2776 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2777 return G_SOURCE_CONTINUE;
2778 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2779 SCENARIO_LOCK (scenario);
2780 priv->non_blocking_running_actions =
2781 g_list_append (priv->non_blocking_running_actions, act);
2782 priv->actions = g_list_remove (priv->actions, act);
2783 SCENARIO_UNLOCK (scenario);
2784 return gst_validate_scenario_execute_next_or_restart_looping (scenario);
2786 gst_validate_action_set_done (act);
2787 return G_SOURCE_CONTINUE;
2792 execute_next_action (GstValidateScenario * scenario)
2794 return execute_next_action_full (scenario, NULL);
2798 stop_waiting (GstValidateAction * action)
2800 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2802 SCENARIO_LOCK (scenario);
2803 scenario->priv->wait_id = 0;
2804 SCENARIO_UNLOCK (scenario);
2806 gst_validate_action_set_done (action);
2807 _add_execute_actions_gsource (scenario);
2808 gst_object_unref (scenario);
2811 return G_SOURCE_REMOVE;
2815 stop_waiting_signal (GstStructure * data)
2819 GstStructure *check = NULL;
2820 GstValidateAction *action;
2821 GstValidateScenario *scenario;
2823 gst_structure_get (data, "target", G_TYPE_POINTER, &target,
2824 "action", GST_TYPE_VALIDATE_ACTION, &action, "sigid", G_TYPE_UINT, &sigid,
2826 gst_structure_free (data);
2828 scenario = gst_validate_action_get_scenario (action);
2830 g_assert (scenario);
2831 SCENARIO_LOCK (scenario);
2832 g_signal_handler_disconnect (target,
2833 sigid ? sigid : scenario->priv->signal_handler_id);
2835 scenario->priv->signal_handler_id = 0;
2836 SCENARIO_UNLOCK (scenario);
2838 if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE,
2840 GstValidateAction *subact =
2841 gst_validate_create_subaction (scenario, NULL, action,
2843 GstValidateActionType *subact_type = _find_action_type (subact->type);
2844 if (!(subact_type->flags & GST_VALIDATE_ACTION_TYPE_CHECK)) {
2845 gst_validate_error_structure (action,
2846 "`check` action %s is not marked as 'check'", subact->type);
2849 gst_validate_execute_action (subact_type, subact);
2850 gst_validate_action_unref (subact);
2853 gst_validate_action_set_done (action);
2854 gst_validate_action_unref (action);
2855 _add_execute_actions_gsource (scenario);
2856 gst_object_unref (scenario);
2857 gst_object_unref (target);
2860 static GstValidateExecuteActionReturn
2861 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2863 GstValidateScenarioPrivate *priv = scenario->priv;
2864 GstClockTime duration;
2866 gdouble wait_multiplier = 1;
2867 const gchar *str_wait_multiplier =
2868 g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2870 if (str_wait_multiplier) {
2872 wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2875 GST_ERROR ("Could not use the WAIT MULTIPLIER");
2877 wait_multiplier = 1;
2880 if (wait_multiplier == 0) {
2881 GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2882 return GST_VALIDATE_EXECUTE_ACTION_OK;
2886 if (!gst_validate_action_get_clocktime (scenario, action,
2887 "duration", &duration)) {
2888 GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2890 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2893 duration *= wait_multiplier;
2895 SCENARIO_LOCK (scenario);
2896 if (priv->execute_actions_source_id) {
2897 g_source_remove (priv->execute_actions_source_id);
2898 priv->execute_actions_source_id = 0;
2900 SCENARIO_UNLOCK (scenario);
2902 SCENARIO_LOCK (scenario);
2903 priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2904 (GSourceFunc) stop_waiting, action);
2905 SCENARIO_UNLOCK (scenario);
2907 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2910 static GstValidateExecuteActionReturn
2911 _execute_wait_for_signal (GstValidateScenario * scenario,
2912 GstValidateAction * action)
2914 gboolean non_blocking;
2915 GstValidateScenarioPrivate *priv = scenario->priv;
2916 const gchar *signal_name = gst_structure_get_string
2917 (action->structure, "signal-name");
2918 GList *targets = NULL;
2921 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2922 DECLARE_AND_GET_PIPELINE (scenario, action);
2924 REPORT_UNLESS (signal_name, err, "No signal-name given for wait action");
2925 targets = _find_elements_defined_in_action (scenario, action);
2926 REPORT_UNLESS ((g_list_length (targets) == 1), err,
2927 "Could not find target element.");
2929 gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2931 if (priv->execute_actions_source_id) {
2932 g_source_remove (priv->execute_actions_source_id);
2933 priv->execute_actions_source_id = 0;
2936 target = targets->data;
2938 gst_structure_new ("a", "action", GST_TYPE_VALIDATE_ACTION, action,
2939 "target", G_TYPE_POINTER, target, NULL);
2940 SCENARIO_LOCK (scenario);
2941 priv->signal_handler_id = g_signal_connect_swapped (target, signal_name,
2942 (GCallback) stop_waiting_signal, data);
2945 gst_structure_get_boolean (action->structure, "non-blocking",
2948 gst_structure_set (data, "sigid", G_TYPE_UINT, priv->signal_handler_id,
2950 priv->signal_handler_id = 0;
2952 SCENARIO_UNLOCK (scenario);
2954 gst_object_unref (pipeline);
2955 g_list_free (targets);
2958 return non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING :
2959 GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2962 g_list_free_full (targets, gst_object_unref);
2963 gst_object_unref (pipeline);
2968 _execute_wait_for_message (GstValidateScenario * scenario,
2969 GstValidateAction * action)
2971 GstValidateScenarioPrivate *priv = scenario->priv;
2972 const gchar *message_type = gst_structure_get_string
2973 (action->structure, "message-type");
2974 DECLARE_AND_GET_PIPELINE (scenario, action);
2976 gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2978 if (priv->execute_actions_source_id) {
2979 g_source_remove (priv->execute_actions_source_id);
2980 priv->execute_actions_source_id = 0;
2983 g_assert (!priv->wait_message_action);
2984 priv->wait_message_action = gst_validate_action_ref (action);
2985 gst_object_unref (pipeline);
2987 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2990 static GstValidateExecuteActionReturn
2991 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2993 gboolean on_clock = FALSE;
2995 gst_structure_get_boolean (action->structure, "on-clock", &on_clock);
2996 if (gst_structure_has_field (action->structure, "signal-name")) {
2997 return _execute_wait_for_signal (scenario, action);
2998 } else if (gst_structure_has_field (action->structure, "message-type")) {
2999 return _execute_wait_for_message (scenario, action);
3000 } else if (on_clock) {
3001 gst_test_clock_wait_for_next_pending_id (scenario->priv->clock, NULL);
3003 return GST_VALIDATE_EXECUTE_ACTION_OK;
3005 return _execute_timed_wait (scenario, action);
3012 _execute_dot_pipeline (GstValidateScenario * scenario,
3013 GstValidateAction * action)
3016 gint details = GST_DEBUG_GRAPH_SHOW_ALL;
3017 const gchar *name = gst_structure_get_string (action->structure, "name");
3018 DECLARE_AND_GET_PIPELINE (scenario, action);
3020 gst_structure_get_int (action->structure, "details", &details);
3022 dotname = g_strdup_printf ("validate.action.%s", name);
3024 dotname = g_strdup ("validate.action.unnamed");
3026 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
3029 gst_object_unref (pipeline);
3035 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
3039 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3042 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3047 name = gst_structure_get_string (action->structure, "target-element-name");
3049 gst_object_unref (pipeline);
3054 if (g_strcmp0 (GST_OBJECT_NAME (pipeline), name) == 0) {
3055 target = gst_object_ref (pipeline);
3057 target = gst_bin_get_by_name (GST_BIN (pipeline), name);
3061 GST_ERROR ("Target element with given name (%s) not found", name);
3062 gst_object_unref (pipeline);
3067 /* _get_target_elements_by_klass_or_factory_name:
3068 * @scenario: a #GstValidateScenario
3069 * @action: a #GstValidateAction
3071 * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
3072 * matches the 'target-element-klass' of @action and the factory name matches
3073 * the 'target-element-factory-name'.
3075 * Returns: (transfer full) (element-type GstElement): a list of #GstElement
3078 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
3079 GstValidateAction * action)
3081 GList *result = NULL;
3083 const gchar *klass, *fname;
3084 GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
3085 gboolean done = FALSE;
3086 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3089 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3094 klass = gst_structure_get_string (action->structure, "target-element-klass");
3096 gst_structure_get_string (action->structure,
3097 "target-element-factory-name");
3098 if (!klass && !fname) {
3099 gst_object_unref (pipeline);
3104 if (klass && gst_validate_element_has_klass (pipeline, klass))
3105 result = g_list_prepend (result, gst_object_ref (pipeline));
3107 if (fname && gst_element_get_factory (pipeline)
3108 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
3110 result = g_list_prepend (result, gst_object_ref (pipeline));
3112 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
3114 g_value_init (¶m, G_TYPE_STRING);
3115 g_value_set_string (¶m, klass);
3118 switch (gst_iterator_next (it, &v)) {
3119 case GST_ITERATOR_OK:{
3120 GstElement *child = g_value_get_object (&v);
3122 if (g_list_find (result, child))
3125 if (klass && gst_validate_element_has_klass (child, klass)) {
3126 result = g_list_prepend (result, gst_object_ref (child));
3130 if (fname && gst_element_get_factory (child)
3131 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
3133 result = g_list_prepend (result, gst_object_ref (child));
3138 case GST_ITERATOR_RESYNC:
3139 gst_iterator_resync (it);
3141 case GST_ITERATOR_ERROR:
3142 case GST_ITERATOR_DONE:
3148 g_value_reset (¶m);
3149 gst_iterator_free (it);
3150 gst_object_unref (pipeline);
3156 _find_elements_defined_in_action (GstValidateScenario * scenario,
3157 GstValidateAction * action)
3160 GList *targets = NULL;
3162 /* set-property can be applied on either:
3163 * - a single element having target-element-name as name
3164 * - all the elements having target-element-klass as klass
3166 if (gst_structure_get_string (action->structure, "target-element-name")) {
3167 target = _get_target_element (scenario, action);
3171 targets = g_list_append (targets, target);
3172 } else if (gst_structure_get_string (action->structure,
3173 "target-element-klass") ||
3174 gst_structure_get_string (action->structure,
3175 "target-element-factory-name")) {
3176 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
3182 static GstValidateExecuteActionReturn
3183 _execute_check_action_type_calls (GstValidateScenario * scenario,
3184 GstValidateAction * action)
3187 GstValidateActionType *t;
3188 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3191 REPORT_UNLESS (gst_structure_get_int (action->structure, "n", &n),
3193 REPORT_UNLESS ((type = gst_structure_get_string (action->structure, "type")),
3194 done, "No `type`!");
3196 _find_action_type (type)), done, "Can't find `%s`!", type);
3197 REPORT_UNLESS (t->priv->n_calls == n, done,
3198 "%s called %d times instead of expected %d", type, t->priv->n_calls, n);
3205 static GstValidateExecuteActionReturn
3206 _execute_check_subaction_level (GstValidateScenario * scenario,
3207 GstValidateAction * action)
3209 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3212 REPORT_UNLESS (gst_structure_get_int (action->structure, "level", &n),
3214 REPORT_UNLESS (gst_validate_action_get_level (action) == n, done,
3215 "Expected subaction level %d, got %d", n,
3216 gst_validate_action_get_level (action));
3224 set_env_var (GQuark field_id, GValue * value,
3225 GSubprocessLauncher * subproc_launcher)
3227 g_subprocess_launcher_setenv (subproc_launcher, g_quark_to_string (field_id),
3228 g_value_get_string (value), TRUE);
3233 static GstValidateExecuteActionReturn
3234 _run_command (GstValidateScenario * scenario, GstValidateAction * action)
3236 gchar **argv = NULL, *_stderr = NULL;
3237 GError *error = NULL;
3238 const GValue *env = NULL;
3239 GSubprocess *subproc = NULL;
3240 GSubprocessLauncher *subproc_launcher = NULL;
3241 GstValidateExecuteActionReturn res =
3242 GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3244 REPORT_UNLESS ((argv = gst_validate_utils_get_strv (action->structure,
3246 "Couldn't find `argv` as array of strings in %" GST_PTR_FORMAT,
3249 subproc_launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDERR_PIPE);
3250 g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_SCENARIO");
3251 g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_CONFIG");
3253 env = gst_structure_get_value (action->structure, "env");
3254 REPORT_UNLESS (!env || GST_VALUE_HOLDS_STRUCTURE (env), done,
3255 "The `env` parameter should be a GstStructure, got %s",
3256 G_VALUE_TYPE_NAME (env));
3258 gst_structure_foreach (gst_value_get_structure (env),
3259 (GstStructureForeachFunc) set_env_var, subproc_launcher);
3264 g_subprocess_launcher_spawnv (subproc_launcher,
3265 (const gchar * const *) argv, &error)), done,
3266 "Couldn't start subprocess: %s", error->message);
3268 REPORT_UNLESS (g_subprocess_communicate_utf8 (subproc, NULL, NULL, NULL,
3269 &_stderr, &error), done, "Failed to run check: %s", error->message);
3271 REPORT_UNLESS (g_subprocess_get_exit_status (subproc) == 0,
3272 done, "Sub command failed. Stderr: %s", _stderr);
3276 res = GST_VALIDATE_EXECUTE_ACTION_OK;
3281 g_clear_object (&subproc_launcher);
3282 g_clear_object (&subproc);
3287 static GstValidateExecuteActionReturn
3288 _execute_check_pad_caps (GstValidateScenario * scenario,
3289 GstValidateAction * action)
3291 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3292 GList *elements = NULL;
3294 GstStructure *expected_struct = NULL;
3295 GstCaps *expected = NULL, *current_caps = NULL;
3296 const gchar *pad_name, *comparison_type =
3297 gst_structure_get_string (action->structure, "comparision-mode");
3299 DECLARE_AND_GET_PIPELINE (scenario, action);
3301 REPORT_UNLESS (elements =
3302 _find_elements_defined_in_action (scenario, action), done,
3303 "Could not find any element from %" GST_PTR_FORMAT, action->structure);
3305 REPORT_UNLESS (g_list_length (elements) == 1, done,
3306 "More than one element found from %" GST_PTR_FORMAT, action->structure);
3308 pad_name = gst_structure_get_string (action->structure, "pad");
3309 REPORT_UNLESS (pad =
3310 gst_element_get_static_pad (elements->data, pad_name), done,
3311 "Could not find pad %s in %" GST_PTR_FORMAT, pad_name, elements->data);
3313 current_caps = gst_pad_get_current_caps (pad);
3314 if (gst_structure_get (action->structure, "expected-caps", GST_TYPE_STRUCTURE,
3315 &expected_struct, NULL))
3316 expected = gst_caps_new_full (gst_structure_copy (expected_struct), NULL);
3318 gst_structure_get (action->structure, "expected-caps", GST_TYPE_CAPS,
3321 if (!comparison_type || !g_strcmp0 (comparison_type, "intersect")) {
3322 REPORT_UNLESS (expected, done, "Can't intersect with NULL expected caps");
3323 REPORT_UNLESS (gst_caps_can_intersect (expected, current_caps), done,
3324 "Caps can't intesect. Expected: \n - %" GST_PTR_FORMAT "\nGot:\n - %"
3325 GST_PTR_FORMAT, expected, current_caps);
3326 } else if (!g_strcmp0 (comparison_type, "equal")) {
3327 REPORT_UNLESS ((expected == NULL && current_caps == NULL)
3328 || gst_caps_is_equal (expected, current_caps), done,
3329 "Caps do not match. Expected: %" GST_PTR_FORMAT " got %" GST_PTR_FORMAT,
3330 expected, current_caps);
3332 REPORT_UNLESS (FALSE, done, "Invalid caps `comparision-type`: '%s'",
3337 g_clear_object (&pipeline);
3338 g_clear_object (&pad);
3339 g_list_free_full (elements, gst_object_unref);
3340 gst_clear_structure (&expected_struct);
3341 gst_clear_caps (¤t_caps);
3342 gst_clear_caps (&expected);
3348 static GstValidateExecuteActionReturn
3349 _execute_check_position (GstValidateScenario * scenario,
3350 GstValidateAction * action)
3352 GstClockTime expected_pos, pos;
3354 if (!gst_validate_action_get_clocktime (scenario, action,
3355 "expected-position", &expected_pos)) {
3356 GST_VALIDATE_REPORT_ACTION (scenario, action,
3357 SCENARIO_ACTION_EXECUTION_ERROR,
3358 "Could not retrieve expected position in: %" GST_PTR_FORMAT,
3361 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3364 if (!_get_position (scenario, NULL, &pos)) {
3365 GST_VALIDATE_REPORT_ACTION (scenario, action,
3366 SCENARIO_ACTION_EXECUTION_ERROR, "Could not get pipeline position");
3368 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3371 if (pos != expected_pos) {
3372 GST_VALIDATE_REPORT_ACTION (scenario, action,
3373 SCENARIO_ACTION_EXECUTION_ERROR,
3374 "Pipeline position doesn't match expectations"
3375 " got %" GST_TIME_FORMAT " instead of %" GST_TIME_FORMAT,
3376 GST_TIME_ARGS (pos), GST_TIME_ARGS (expected_pos));
3378 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3381 return GST_VALIDATE_EXECUTE_ACTION_OK;
3385 static GstValidateExecuteActionReturn
3386 _execute_set_or_check_property (GstValidateScenario * scenario,
3387 GstValidateAction * action)
3390 const gchar *property;
3391 const GValue *property_value;
3392 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_OK;
3393 gboolean check = gst_structure_has_name (action->structure, "check-property");
3395 targets = _find_elements_defined_in_action (scenario, action);
3397 GST_VALIDATE_REPORT_ACTION (scenario, action,
3398 SCENARIO_ACTION_EXECUTION_ERROR,
3399 "No element found for action: %" GST_PTR_FORMAT, action->structure);
3401 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3404 property = gst_structure_get_string (action->structure, "property-name");
3405 property_value = gst_structure_get_value (action->structure,
3408 for (l = targets; l != NULL; l = g_list_next (l)) {
3410 GstValidateActionReturn tmpres;
3413 gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
3414 G_OBJECT (l->data), property, property_value, action->priv->optional);
3420 _check_property (scenario, action, l->data, property, property_value);
3424 g_list_free_full (targets, gst_object_unref);
3429 _execute_set_debug_threshold (GstValidateScenario * scenario,
3430 GstValidateAction * action)
3433 gboolean reset = TRUE;
3434 const gchar *threshold_str;
3437 gst_structure_get_string (action->structure, "debug-threshold");
3438 if (threshold_str == NULL) {
3441 if (gst_structure_get_int (action->structure, "debug-threshold",
3443 threshold_str = str = g_strdup_printf ("%i", threshold);
3448 gst_structure_get_boolean (action->structure, "reset", &reset);
3450 gst_debug_set_threshold_from_string (threshold_str, reset);
3457 static GstValidateExecuteActionReturn
3458 _execute_emit_signal (GstValidateScenario * scenario,
3459 GstValidateAction * action)
3461 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3464 GSignalQuery query = { 0, };
3465 GValue *values = NULL, lparams = { 0, };
3466 const GValue *params;
3468 REPORT_UNLESS ((target =
3469 _get_target_element (scenario, action)), out, "No element found");
3472 gst_structure_get_string (action->structure, "signal-name");
3473 query.signal_id = g_signal_lookup (query.signal_name, G_OBJECT_TYPE (target));
3474 REPORT_UNLESS (query.signal_id != 0, out, "Invalid signal `%s::%s`",
3475 G_OBJECT_TYPE_NAME (target), query.signal_name);
3477 g_signal_query (query.signal_id, &query);
3479 params = gst_structure_get_value (action->structure, "params");
3481 if (G_VALUE_HOLDS_STRING (params)) {
3482 g_value_init (&lparams, GST_TYPE_ARRAY);
3484 REPORT_UNLESS (gst_value_deserialize (&lparams,
3485 g_value_get_string (params)), out,
3486 "\"params\" argument should be a value array or a string deserializable"
3487 " as value array, got string %s", g_value_get_string (params)
3491 REPORT_UNLESS (GST_VALUE_HOLDS_ARRAY (params), out,
3492 "\"params\" argument should be a value array, got %s",
3493 G_VALUE_TYPE_NAME (params));
3495 n_params = gst_value_array_get_size (params);
3497 REPORT_UNLESS (query.n_params == (n_params), out,
3498 "Expected %d `params` got %d", query.n_params, n_params);
3499 values = g_malloc0 ((n_params + 2) * sizeof (GValue));
3500 g_value_init (&values[0], G_OBJECT_TYPE (target));
3501 g_value_take_object (&values[0], target);
3502 for (gint i = 1; i < n_params + 1; i++) {
3503 const GValue *param = gst_value_array_get_value (params, i - 1);
3504 g_value_init (&values[i], query.param_types[i - 1]);
3506 if (query.param_types[i - 1] == G_TYPE_BYTES
3507 && G_VALUE_TYPE (param) == G_TYPE_STRING) {
3508 const gchar *s = g_value_get_string (param);
3509 g_value_take_boxed (&values[i], g_bytes_new (s, strlen (s)));
3511 REPORT_UNLESS (g_value_transform (param, &values[i]), out,
3512 "Could not transform param %d from %s to %s", i - 1,
3513 G_VALUE_TYPE_NAME (param), G_VALUE_TYPE_NAME (&values[i]));
3517 g_signal_emitv (values, query.signal_id, 0, NULL);
3519 for (gint i = 0; i < n_params + 1; i++)
3520 g_value_reset (&values[i]);
3522 if (G_VALUE_TYPE (&lparams))
3523 g_value_reset (&lparams);
3530 typedef struct _ChainWrapperFunctionData ChainWrapperFunctionData;
3531 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
3532 GstBuffer * buffer, ChainWrapperFunctionData * data);
3534 struct _ChainWrapperFunctionData
3536 GstPadChainFunction wrapped_chain_func;
3537 gpointer wrapped_chain_data;
3538 GDestroyNotify wrapped_chain_notify;
3539 ChainWrapperFunction wrapper_function;
3540 gpointer wrapper_function_user_data;
3542 GMutex actions_lock;
3548 chain_wrapper_function_free (ChainWrapperFunctionData * data)
3550 g_list_free_full (data->actions, (GDestroyNotify) gst_validate_action_unref);
3551 g_mutex_clear (&data->actions_lock);
3554 static GstFlowReturn
3555 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
3557 ChainWrapperFunctionData *data =
3558 g_object_get_qdata (G_OBJECT (pad), chain_qdata);
3560 return data->wrapper_function (pad, parent, buffer,
3561 g_object_get_qdata (G_OBJECT (pad), chain_qdata));
3565 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
3566 GstValidateAction * action)
3568 ChainWrapperFunctionData *data =
3569 g_object_get_qdata (G_OBJECT (pad), chain_qdata);
3572 g_mutex_lock (&data->actions_lock);
3573 data->actions = g_list_append (data->actions, action);
3574 g_mutex_unlock (&data->actions_lock);
3579 data = g_new0 (ChainWrapperFunctionData, 1);
3580 data->actions = g_list_append (data->actions, action);
3582 g_object_set_qdata_full (G_OBJECT (pad), chain_qdata, data,
3583 (GDestroyNotify) chain_wrapper_function_free);
3585 data->wrapped_chain_func = pad->chainfunc;
3586 data->wrapper_function = new_function;
3587 pad->chainfunc = _pad_chain_wrapper;
3590 static GstFlowReturn
3591 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent,
3592 GstBuffer * buffer, ChainWrapperFunctionData * data)
3594 GstValidateAction *action;
3595 GstValidateScenario *scenario;
3598 g_mutex_lock (&data->actions_lock);
3599 if (data->actions) {
3600 action = data->actions->data;
3601 data->actions = g_list_remove (data->actions, action);
3602 g_mutex_unlock (&data->actions_lock);
3604 scenario = gst_validate_action_get_scenario (action);
3606 g_mutex_unlock (&data->actions_lock);
3608 return data->wrapped_chain_func (pad, parent, buffer);
3611 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
3612 ret = data->wrapped_chain_func (pad, parent, buffer);
3613 gst_validate_action_set_done (action);
3614 gst_validate_action_unref (action);
3615 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3616 g_object_unref (scenario);
3622 structure_get_uint64_permissive (const GstStructure * structure,
3623 const gchar * fieldname, guint64 * dest)
3625 const GValue *original;
3626 GValue transformed = G_VALUE_INIT;
3628 original = gst_structure_get_value (structure, fieldname);
3632 g_value_init (&transformed, G_TYPE_UINT64);
3633 if (!g_value_transform (original, &transformed))
3636 *dest = g_value_get_uint64 (&transformed);
3637 g_value_unset (&transformed);
3642 _execute_appsrc_push (GstValidateScenario * scenario,
3643 GstValidateAction * action)
3645 GstElement *target = NULL;
3646 gchar *file_name = NULL;
3647 gchar *file_contents = NULL;
3648 GError *error = NULL;
3651 guint64 size = 0, read;
3652 gint push_sample_ret;
3654 GFileInfo *finfo = NULL;
3656 GstPad *appsrc_pad = NULL;
3657 GstPad *peer_pad = NULL;
3658 GInputStream *stream = NULL;
3659 GstValidateExecuteActionReturn res;
3661 GstCaps *caps = NULL;
3664 /* We will only wait for the the buffer to be pushed if we are in a state
3665 * that allows flow of buffers (>=PAUSED). Otherwise the buffer will just
3667 wait = scenario->priv->target_state >= GST_STATE_PAUSED;
3669 target = _get_target_element (scenario, action);
3670 REPORT_UNLESS (target, err, "No element found.");
3672 g_strdup (gst_structure_get_string (action->structure, "file-name"));
3673 REPORT_UNLESS (file_name, err, "Missing file-name property.");
3675 structure_get_uint64_permissive (action->structure, "offset", &offset);
3676 structure_get_uint64_permissive (action->structure, "size", &size);
3678 f = g_file_new_for_path (file_name);
3679 stream = G_INPUT_STREAM (g_file_read (f, NULL, &error));
3680 REPORT_UNLESS (!error, err, "Could not open file for action. Error: %s",
3684 read = g_input_stream_skip (stream, offset, NULL, &error);
3685 REPORT_UNLESS (!error, err, "Could not skip to offset. Error: %s",
3687 REPORT_UNLESS (read == offset, err,
3688 "Could not skip to offset, only skipped: %" G_GUINT64_FORMAT, read);
3693 g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_SIZE,
3694 G_FILE_QUERY_INFO_NONE, NULL, &error);
3696 REPORT_UNLESS (!error, err, "Could not query file size. Error: %s",
3698 size = g_file_info_get_size (finfo);
3701 file_contents = g_malloc (size);
3702 read = g_input_stream_read (stream, file_contents, size, NULL, &error);
3703 REPORT_UNLESS (!error, err, "Could not read input file. Error: %s",
3705 REPORT_UNLESS (read == size, err,
3706 "Could read enough data, only read: %" G_GUINT64_FORMAT, read);
3708 buffer = gst_buffer_new_wrapped (file_contents, size);
3709 file_contents = NULL;
3710 gst_validate_action_get_clocktime (scenario,
3711 action, "pts", &GST_BUFFER_PTS (buffer)
3713 gst_validate_action_get_clocktime (scenario,
3714 action, "dts", &GST_BUFFER_DTS (buffer)
3716 gst_validate_action_get_clocktime (scenario,
3717 action, "duration", &GST_BUFFER_DURATION (buffer)
3721 const GValue *caps_value;
3722 caps_value = gst_structure_get_value (action->structure, "caps");
3724 if (G_VALUE_HOLDS_STRING (caps_value)) {
3725 caps = gst_caps_from_string (g_value_get_string (caps_value));
3726 REPORT_UNLESS (caps, err, "Invalid caps string: %s",
3727 g_value_get_string (caps_value));
3729 caps = gst_caps_copy (gst_value_get_caps (caps_value));
3732 REPORT_UNLESS (caps, err, "Could not get caps value");
3736 /* We temporarily override the peer pad chain function to finish the action
3737 * once the buffer chain actually ends. */
3738 appsrc_pad = gst_element_get_static_pad (target, "src");
3739 peer_pad = gst_pad_get_peer (appsrc_pad);
3740 REPORT_UNLESS (peer_pad, err, "Action failed, pad not linked");
3742 wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
3744 /* Keep the action alive until set done is called. */
3745 gst_validate_action_ref (action);
3747 sample = gst_sample_new (buffer, caps, NULL, NULL);
3748 gst_clear_caps (&caps);
3749 gst_buffer_unref (buffer);
3750 if (gst_structure_has_field (action->structure, "segment")) {
3752 GstStructure *segment_struct;
3755 REPORT_UNLESS (gst_structure_get (action->structure, "segment",
3756 GST_TYPE_STRUCTURE, &segment_struct, NULL), err,
3757 "Segment field not in right format (expected GstStructure).");
3759 if (!gst_structure_get (segment_struct, "format", GST_TYPE_FORMAT, &format,
3761 g_object_get (target, "format", &format, NULL);
3763 gst_segment_init (&segment, format);
3764 if (gst_validate_utils_get_clocktime (segment_struct, "base", &tmp))
3766 if (gst_validate_utils_get_clocktime (segment_struct, "offset", &tmp))
3767 segment.offset = tmp;
3768 if (gst_validate_utils_get_clocktime (segment_struct, "time", &tmp))
3770 if (gst_validate_utils_get_clocktime (segment_struct, "position", &tmp))
3771 segment.position = tmp;
3772 if (gst_validate_utils_get_clocktime (segment_struct, "duration", &tmp))
3773 segment.duration = tmp;
3774 if (gst_validate_utils_get_clocktime (segment_struct, "start", &tmp))
3775 segment.start = tmp;
3776 if (gst_validate_utils_get_clocktime (segment_struct, "stop", &tmp))
3778 gst_structure_get_double (segment_struct, "rate", &segment.rate);
3780 gst_structure_free (segment_struct);
3782 gst_sample_set_segment (sample, &segment);
3785 g_signal_emit_by_name (target, "push-sample", sample, &push_sample_ret);
3786 gst_sample_unref (sample);
3787 REPORT_UNLESS (push_sample_ret == GST_FLOW_OK, err,
3788 "push-buffer signal failed in action.");
3791 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
3793 gst_validate_printf (NULL,
3794 "Pipeline is not ready to push buffers, interlacing appsrc-push action...\n");
3795 res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
3798 gst_clear_object (&target);
3799 gst_clear_object (&appsrc_pad);
3800 gst_clear_object (&peer_pad);
3801 g_clear_pointer (&file_name, g_free);
3802 g_clear_pointer (&file_contents, g_free);
3803 g_clear_error (&error);
3804 g_clear_object (&f);
3805 g_clear_object (&finfo);
3806 g_clear_object (&stream);
3811 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3816 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
3821 target = _get_target_element (scenario, action);
3822 if (target == NULL) {
3823 gchar *structure_string = gst_structure_to_string (action->structure);
3824 GST_VALIDATE_REPORT_ACTION (scenario, action,
3825 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3827 g_free (structure_string);
3828 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3831 g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
3832 if (eos_ret != GST_FLOW_OK) {
3833 gchar *structure_string = gst_structure_to_string (action->structure);
3834 GST_VALIDATE_REPORT_ACTION (scenario, action,
3835 SCENARIO_ACTION_EXECUTION_ERROR,
3836 "Failed to emit end-of-stream signal for action: %s", structure_string);
3837 g_free (structure_string);
3838 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3841 gst_object_unref (target);
3842 return GST_VALIDATE_EXECUTE_ACTION_OK;
3846 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
3850 gboolean reset_time = TRUE;
3852 target = _get_target_element (scenario, action);
3853 if (target == NULL) {
3854 gchar *structure_string = gst_structure_to_string (action->structure);
3855 GST_VALIDATE_REPORT_ACTION (scenario, action,
3856 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3858 g_free (structure_string);
3859 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3862 gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
3864 event = gst_event_new_flush_start ();
3865 if (!gst_element_send_event (target, event)) {
3866 GST_VALIDATE_REPORT_ACTION (scenario, action,
3867 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_START event was not handled");
3868 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3871 event = gst_event_new_flush_stop (reset_time);
3872 if (!gst_element_send_event (target, event)) {
3873 GST_VALIDATE_REPORT_ACTION (scenario, action,
3874 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_STOP event was not handled");
3875 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3878 return GST_VALIDATE_EXECUTE_ACTION_OK;
3881 static GstValidateExecuteActionReturn
3882 _execute_disable_plugin (GstValidateScenario * scenario,
3883 GstValidateAction * action)
3886 const gchar *plugin_name;
3888 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
3890 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
3892 if (plugin == NULL) {
3893 GST_VALIDATE_REPORT_ACTION (scenario, action,
3894 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find plugin to disable: %s",
3897 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3900 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
3901 gst_registry_remove_plugin (gst_registry_get (), plugin);
3903 return GST_VALIDATE_EXECUTE_ACTION_OK;
3907 gst_validate_action_setup_repeat (GstValidateScenario * scenario,
3908 GstValidateAction * action)
3911 gchar *error = NULL;
3912 gint repeat, position, i;
3914 if (!gst_structure_has_field (action->structure, "repeat"))
3917 if (gst_structure_get_int (action->structure, "repeat", &repeat))
3920 if (gst_structure_get_double (action->structure, "repeat",
3921 (gdouble *) & repeat))
3924 repeat_expr = gst_validate_replace_variables_in_string (action,
3925 scenario->priv->vars, gst_structure_get_string (action->structure,
3926 "repeat"), GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3928 gst_validate_error_structure (action, "Invalid value for 'repeat'");
3932 repeat = gst_validate_utils_parse_expression (repeat_expr, _set_variable_func,
3935 gst_validate_error_structure (action, "Invalid value for 'repeat': %s",
3940 g_free (repeat_expr);
3943 gst_structure_remove_field (action->structure, "repeat");
3944 gst_structure_remove_field (action->priv->main_structure, "repeat");
3947 GST_VALIDATE_ACTION_N_REPEATS (action) = repeat;
3949 position = g_list_index (scenario->priv->actions, action);
3950 g_assert (position >= 0);
3951 for (i = 1; i < repeat; i++) {
3952 GstValidateAction *copy = _action_copy (action);
3955 scenario->priv->actions =
3956 g_list_insert (scenario->priv->actions, copy, position + i);
3962 static GstValidateExecuteActionReturn
3963 gst_validate_action_default_prepare_func (GstValidateAction * action)
3967 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3968 GstValidateActionType *type = gst_validate_get_action_type (action->type);
3969 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3971 _update_well_known_vars (scenario);
3972 if (!gst_validate_action_setup_repeat (scenario, action))
3975 if (GST_VALIDATE_ACTION_N_REPEATS (action)) {
3976 if (action->priv->it_value.g_type != 0) {
3977 gst_structure_set_value (scenario->priv->vars,
3978 GST_VALIDATE_ACTION_RANGE_NAME (action), &action->priv->it_value);
3980 gst_structure_set (scenario->priv->vars,
3981 GST_VALIDATE_ACTION_RANGE_NAME (action) ?
3982 GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", G_TYPE_INT,
3983 action->repeat, NULL);
3986 gst_validate_structure_resolve_variables (action, action->structure,
3987 scenario->priv->vars, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3988 for (i = 0; type->parameters[i].name; i++) {
3989 if (type->parameters[i].types
3990 && g_str_has_suffix (type->parameters[i].types, "(GstClockTime)"))
3991 gst_validate_action_get_clocktime (scenario, action,
3992 type->parameters[i].name, &tmp);
3997 gst_clear_mini_object ((GstMiniObject **) & type);
3999 gst_object_unref (scenario);
4003 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
4007 static GstValidateExecuteActionReturn
4008 gst_validate_set_property_prepare_func (GstValidateAction * action)
4010 action->priv->optional = gst_structure_has_field_typed (action->structure,
4011 "on-all-instances", G_TYPE_BOOLEAN);
4013 return gst_validate_action_default_prepare_func (action);
4017 add_gvalue_to_list_as_struct (gpointer source, GList * list, const GValue * v)
4019 if (G_VALUE_HOLDS_STRING (v)) {
4020 GstStructure *structure =
4021 gst_structure_new_from_string (g_value_get_string (v));
4024 gst_validate_error_structure (source, "Invalid structure: %s",
4025 g_value_get_string (v));
4027 return g_list_append (list, structure);
4030 if (GST_VALUE_HOLDS_STRUCTURE (v))
4031 return g_list_append (list,
4032 gst_structure_copy (gst_value_get_structure (v)));
4035 gst_validate_error_structure (source, "Expected a string or a structure,"
4036 " got %s instead", gst_value_serialize (v));
4041 gst_validate_utils_get_structures (gpointer source,
4042 GstStructure * str, const gchar * fieldname)
4046 const GValue *value = gst_structure_get_value (str, fieldname);
4051 if (G_VALUE_HOLDS_STRING (value) || GST_VALUE_HOLDS_STRUCTURE (value))
4052 return add_gvalue_to_list_as_struct (source, res, value);
4054 if (!GST_VALUE_HOLDS_LIST (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
4055 g_error ("%s must have type list of structure/string (or a string), "
4056 "e.g. %s={ [struct1, a=val1], [struct2, a=val2] }, got: \"%s\" in %s",
4057 fieldname, fieldname, gst_value_serialize (value),
4058 gst_structure_to_string (str));
4063 GST_VALUE_HOLDS_LIST (value) ? gst_value_list_get_size (value) :
4064 gst_value_array_get_size (value);
4065 for (i = 0; i < size; i++)
4067 add_gvalue_to_list_as_struct (source, res,
4068 GST_VALUE_HOLDS_LIST (value) ?
4069 gst_value_list_get_value (value, i) :
4070 gst_value_array_get_value (value, i));
4075 static GstValidateAction *
4076 gst_validate_create_subaction (GstValidateScenario * scenario,
4077 GstStructure * lvariables, GstValidateAction * action,
4078 GstStructure * nstruct, gint it, gint max)
4080 GstValidateAction *subaction;
4081 GstValidateActionType *action_type =
4082 _find_action_type (gst_structure_get_name (nstruct));
4085 gst_validate_error_structure (action,
4086 "Unknown action type: '%s'", gst_structure_get_name (nstruct));
4087 subaction = gst_validate_action_new (scenario, action_type, nstruct, FALSE);
4088 GST_VALIDATE_ACTION_RANGE_NAME (subaction) =
4089 GST_VALIDATE_ACTION_RANGE_NAME (action);
4090 GST_VALIDATE_ACTION_FILENAME (subaction) =
4091 g_strdup (GST_VALIDATE_ACTION_FILENAME (action));
4092 GST_VALIDATE_ACTION_DEBUG (subaction) =
4093 g_strdup (GST_VALIDATE_ACTION_DEBUG (action));
4094 GST_VALIDATE_ACTION_LINENO (subaction) = GST_VALIDATE_ACTION_LINENO (action);
4095 subaction->repeat = it;
4096 subaction->priv->subaction_level = action->priv->subaction_level + 1;
4097 GST_VALIDATE_ACTION_N_REPEATS (subaction) = max;
4098 gst_validate_structure_resolve_variables (subaction, subaction->structure,
4100 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_LOCAL_ONLY |
4101 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_FAILURE |
4102 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_EXPRESSION);
4103 gst_structure_free (nstruct);
4108 static GstValidateExecuteActionReturn
4109 gst_validate_foreach_prepare (GstValidateAction * action)
4112 gint min = 0, max = 1, step = 1;
4113 const GValue *it_array = NULL;
4114 GstValidateScenario *scenario;
4115 GList *actions, *tmp;
4117 scenario = gst_validate_action_get_scenario (action);
4118 g_assert (scenario);
4119 _update_well_known_vars (scenario);
4120 gst_validate_action_setup_repeat (scenario, action);
4122 GST_VALIDATE_ACTION_RANGE_NAME (action) = NULL;
4123 gst_structure_foreach (action->structure,
4124 (GstStructureForeachFunc) _foreach_find_iterator, action);
4126 /* Allow using the repeat field here too */
4127 if (!GST_VALIDATE_ACTION_RANGE_NAME (action)
4128 && !GST_VALIDATE_ACTION_N_REPEATS (action))
4129 gst_validate_error_structure (action, "Missing range specifier field.");
4131 if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
4132 const GValue *it_value = gst_structure_get_value (action->structure,
4133 GST_VALIDATE_ACTION_RANGE_NAME (action));
4135 if (GST_VALUE_HOLDS_INT_RANGE (it_value)) {
4136 min = gst_value_get_int_range_min (it_value);
4137 max = gst_value_get_int_range_max (it_value);
4138 step = gst_value_get_int_range_step (it_value);
4140 if (min % step != 0)
4141 gst_validate_error_structure (action,
4142 "Range min[%d] must be a multiple of step[%d].", min, step);
4144 if (max % step != 0)
4145 gst_validate_error_structure (action,
4146 "Range max[%d] must be a multiple of step[%d].", max, step);
4148 it_array = it_value;
4149 max = gst_value_array_get_size (it_array);
4152 min = action->repeat;
4153 max = action->repeat + 1;
4156 actions = gst_validate_utils_get_structures (action, action->structure,
4158 i = g_list_index (scenario->priv->actions, action);
4159 for (it = min; it < max; it = it + step) {
4160 GstStructure *lvariables = gst_structure_new_empty ("vars");
4161 const GValue *it_value = NULL;
4164 it_value = gst_value_array_get_value (it_array, it);
4166 gst_structure_set_value (lvariables,
4167 GST_VALIDATE_ACTION_RANGE_NAME (action), it_value);
4171 for (tmp = actions; tmp; tmp = tmp->next) {
4172 GstValidateAction *subact =
4173 gst_validate_create_subaction (scenario, lvariables, action,
4174 gst_structure_copy (tmp->data), it, max);
4175 scenario->priv->actions =
4176 g_list_insert (scenario->priv->actions, subact, i++);
4178 g_value_init (&subact->priv->it_value, G_VALUE_TYPE (it_value));
4179 g_value_copy (it_value, &subact->priv->it_value);
4183 gst_structure_free (lvariables);
4185 g_list_free_full (actions, (GDestroyNotify) gst_structure_free);
4187 scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
4188 gst_structure_remove_field (action->structure, "actions");
4190 gst_object_unref (scenario);
4191 return GST_VALIDATE_EXECUTE_ACTION_DONE;
4195 _check_structure_has_expected_value (GQuark field_id, const GValue * value,
4196 GstStructure * message_struct)
4198 const GValue *v = gst_structure_id_get_value (message_struct, field_id);
4201 gst_structure_set (message_struct, "__validate_has_expected_values",
4202 G_TYPE_BOOLEAN, FALSE, NULL);
4206 if (gst_value_compare (value, v) != GST_VALUE_EQUAL) {
4207 gst_structure_set (message_struct, "__validate_has_expected_values",
4208 G_TYPE_BOOLEAN, FALSE, NULL);
4212 gst_structure_set (message_struct, "__validate_has_expected_values",
4213 G_TYPE_BOOLEAN, TRUE, NULL);
4219 _check_waiting_for_message (GstValidateScenario * scenario,
4220 GstMessage * message)
4222 GstStructure *expected_values = NULL;
4223 GstValidateScenarioPrivate *priv = scenario->priv;
4224 const gchar *message_type;
4226 if (!priv->wait_message_action) {
4227 GST_LOG_OBJECT (scenario, "Not waiting for message");
4231 message_type = gst_structure_get_string (priv->wait_message_action->structure,
4234 if (g_strcmp0 (message_type, GST_MESSAGE_TYPE_NAME (message)))
4237 GST_LOG_OBJECT (scenario, " Waiting for %s and got %s", message_type,
4238 GST_MESSAGE_TYPE_NAME (message));
4240 gst_structure_get (priv->wait_message_action->structure, "expected-values",
4241 GST_TYPE_STRUCTURE, &expected_values, NULL);
4242 if (expected_values) {
4243 gboolean res = FALSE;
4244 GstStructure *message_struct =
4245 (GstStructure *) gst_message_get_structure (message);
4248 message_struct ? gst_structure_copy (message_struct) : NULL;
4249 if (!message_struct) {
4250 GST_DEBUG_OBJECT (scenario,
4251 "Waiting for %" GST_PTR_FORMAT " but message has no structure.",
4252 priv->wait_message_action->structure);
4256 gst_structure_set (message_struct, "__validate_has_expected_values",
4257 G_TYPE_BOOLEAN, FALSE, NULL);
4258 gst_structure_foreach (expected_values,
4259 (GstStructureForeachFunc) _check_structure_has_expected_value,
4262 if (!gst_structure_get_boolean (message_struct,
4263 "__validate_has_expected_values", &res) || !res) {
4268 gst_validate_action_set_done (priv->wait_message_action);
4269 _add_execute_actions_gsource (scenario);
4273 streams_list_contain (GList * streams, const gchar * stream_id)
4277 for (l = streams; l; l = g_list_next (l)) {
4278 GstStream *s = l->data;
4280 if (!g_strcmp0 (s->stream_id, stream_id))
4288 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
4289 GstElement * pipeline)
4291 GstValidateScenarioPrivate *priv = scenario->priv;
4293 GstClockTime min_latency;
4295 query = gst_query_new_latency ();
4296 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
4297 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4298 "Failed to perform LATENCY query");
4299 gst_query_unref (query);
4303 gst_query_parse_latency (query, NULL, &min_latency, NULL);
4304 gst_query_unref (query);
4305 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
4306 " max allowed: %" GST_TIME_FORMAT,
4307 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
4309 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
4310 min_latency > priv->max_latency) {
4311 GST_VALIDATE_REPORT (scenario, CONFIG_LATENCY_TOO_HIGH,
4312 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
4313 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
4314 GST_TIME_ARGS (priv->max_latency));
4319 gst_validate_scenario_is_flush_seeking (GstValidateScenario * scenario)
4321 GstValidateSeekInformation *seekinfo = scenario->priv->current_seek;
4326 if (!(seekinfo->flags & GST_SEEK_FLAG_FLUSH))
4329 return seekinfo->action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4333 gst_validate_scenario_reset (GstValidateScenario * scenario)
4335 /* Reset sink information */
4336 SCENARIO_LOCK (scenario);
4337 g_list_foreach (scenario->priv->sinks, (GFunc) _reset_sink_information, NULL);
4338 /* Reset current seek */
4339 scenario->priv->current_seek = NULL;
4340 scenario->priv->current_seqnum = GST_SEQNUM_INVALID;
4341 SCENARIO_UNLOCK (scenario);
4346 GstValidateScenario *scenario;
4347 GstMessage *message;
4351 message_data_free (MessageData * d)
4353 gst_message_unref (d->message);
4354 gst_object_unref (d->scenario);
4360 handle_bus_message (MessageData * d)
4362 gboolean is_error = FALSE;
4363 GstMessage *message = d->message;
4364 GstValidateScenario *scenario = d->scenario;
4365 GstValidateScenarioPrivate *priv = scenario->priv;
4366 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
4369 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
4370 return G_SOURCE_REMOVE;
4373 GST_DEBUG_OBJECT (scenario, "message %" GST_PTR_FORMAT, message);
4375 switch (GST_MESSAGE_TYPE (message)) {
4376 case GST_MESSAGE_ASYNC_DONE:
4377 if (!gst_validate_scenario_is_flush_seeking (scenario) &&
4378 priv->needs_async_done) {
4379 priv->needs_async_done = FALSE;
4380 if (priv->actions && _action_sets_state (priv->actions->data)
4381 && !priv->changing_state)
4382 gst_validate_action_set_done (priv->actions->data);
4385 if (priv->needs_playback_parsing) {
4386 priv->needs_playback_parsing = FALSE;
4387 if (!gst_validate_parse_next_action_playback_time (scenario))
4388 return G_SOURCE_REMOVE;
4390 _add_execute_actions_gsource (scenario);
4392 case GST_MESSAGE_STATE_CHANGED:
4394 GstState old_state, state, pending_state;
4395 gboolean reached_state;
4397 if (!pipeline || GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline))
4400 gst_message_parse_state_changed (message, &old_state, &state,
4403 reached_state = pending_state == GST_STATE_VOID_PENDING;
4405 if (old_state == GST_STATE_PAUSED && state == GST_STATE_READY)
4406 gst_validate_scenario_reset (scenario);
4408 if (reached_state && gst_validate_scenario_is_flush_seeking (scenario))
4409 gst_validate_action_set_done (priv->current_seek->action);
4411 if (priv->changing_state && priv->target_state == state) {
4412 priv->changing_state = FALSE;
4413 if (priv->actions && _action_sets_state (priv->actions->data)
4415 gst_validate_action_set_done (priv->actions->data);
4418 if (old_state == GST_STATE_READY && state == GST_STATE_PAUSED)
4419 _add_execute_actions_gsource (scenario);
4421 /* GstBin only send a new latency message when reaching PLAYING if
4422 * async-handling=true so check the latency manually. */
4423 if (state == GST_STATE_PLAYING)
4424 gst_validate_scenario_check_latency (scenario, pipeline);
4427 case GST_MESSAGE_ERROR:
4431 case GST_MESSAGE_EOS:
4433 GstValidateAction *stop_action;
4434 GstValidateActionType *stop_action_type;
4437 if (!is_error && priv->ignore_eos) {
4438 GST_INFO_OBJECT (scenario, "Got EOS but ignoring it!");
4442 if (is_error && priv->allow_errors) {
4443 GST_INFO_OBJECT (scenario, "Got error but ignoring it!");
4444 if (scenario->priv->needs_async_done || scenario->priv->changing_state) {
4446 if (scenario->priv->actions) {
4447 GstValidateAction *act =
4448 gst_validate_action_ref (scenario->priv->actions->data);
4450 GST_VALIDATE_REPORT_ACTION (scenario, act,
4451 SCENARIO_ACTION_EXECUTION_ERROR,
4452 "Error message happened while executing action");
4453 gst_validate_action_set_done (act);
4455 gst_validate_action_unref (act);
4458 scenario->priv->needs_async_done = scenario->priv->changing_state =
4464 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
4466 /* gst_validate_action_set_done() does not finish the action
4467 * immediately. Instead, it posts a task to the main thread to do most
4468 * of the work in _action_set_done().
4470 * While the EOS handling lock guarantees that if an action had to call
4471 * gst_validate_action_set_done() it has done so, it does not guarantee
4472 * that _action_set_done() has been called.
4474 * Is it possible that this handler is run before _action_set_done(), so
4475 * we check at this point for actions that have a pending_set_done and
4476 * call it before continuing. */
4477 GList *actions = g_list_copy (priv->actions);
4479 for (i = actions; i; i = i->next) {
4480 GstValidateAction *action = (GstValidateAction *) i->data;
4481 if (action->priv->pending_set_done)
4482 _action_set_done (action);
4484 g_list_free (actions);
4488 priv->got_eos = TRUE;
4489 if (priv->wait_message_action) {
4491 if (priv->actions && priv->actions->next) {
4492 GST_DEBUG_OBJECT (scenario,
4493 "Waiting for a message and got a next action"
4494 " to execute, letting it a chance!");
4495 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4498 /* Clear current message wait if waiting for EOS */
4499 _check_waiting_for_message (scenario, message);
4504 SCENARIO_LOCK (scenario);
4505 /* Make sure that if there is an ASYNC_DONE in the message queue, we do not
4506 take it into account */
4507 g_list_free_full (priv->seeks,
4508 (GDestroyNotify) gst_validate_seek_information_free);
4510 SCENARIO_UNLOCK (scenario);
4512 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
4514 stop_action_type = _find_action_type ("stop");
4515 s = gst_structure_new ("stop", "generated-after-eos", G_TYPE_BOOLEAN,
4516 !is_error, "generated-after-error", G_TYPE_BOOLEAN, is_error, NULL);
4517 stop_action = gst_validate_action_new (scenario, stop_action_type,
4519 gst_structure_free (s);
4520 gst_validate_execute_action (stop_action_type, stop_action);
4521 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
4523 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4526 case GST_MESSAGE_BUFFERING:
4530 gst_message_parse_buffering (message, &percent);
4533 priv->buffering = FALSE;
4535 priv->buffering = TRUE;
4538 case GST_MESSAGE_STREAMS_SELECTED:
4541 GList *streams_selected = NULL;
4543 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
4545 gst_message_streams_selected_get_stream (message, i);
4547 streams_selected = g_list_append (streams_selected, stream);
4550 /* Is there a pending switch-track action waiting for the new streams to
4552 if (priv->pending_switch_track) {
4553 GList *expected, *l;
4554 GstValidateScenario *scenario =
4555 gst_validate_action_get_scenario (priv->pending_switch_track);
4558 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
4559 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
4561 if (g_list_length (expected) != g_list_length (streams_selected)) {
4562 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4563 SCENARIO_ACTION_EXECUTION_ERROR,
4564 "Was expecting %d selected streams but got %d",
4565 g_list_length (expected), g_list_length (streams_selected));
4569 for (l = expected; l; l = g_list_next (l)) {
4570 const gchar *stream_id = l->data;
4572 if (!streams_list_contain (streams_selected, stream_id)) {
4573 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4574 SCENARIO_ACTION_EXECUTION_ERROR,
4575 "Stream %s has not be activated", stream_id);
4581 gst_object_unref (scenario);
4582 gst_validate_action_set_done (priv->pending_switch_track);
4583 priv->pending_switch_track = NULL;
4586 g_list_free_full (streams_selected, gst_object_unref);
4589 case GST_MESSAGE_LATENCY:
4590 gst_validate_scenario_check_latency (scenario, pipeline);
4593 case GST_MESSAGE_QOS:
4597 /* Check the maximum allowed when scenario is terminating so the report
4598 * will include the actual number of dropped buffers. */
4599 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
4601 priv->dropped = dropped;
4604 case GST_MESSAGE_APPLICATION:
4606 const GstStructure *s;
4607 s = gst_message_get_structure (message);
4608 if (gst_structure_has_name (s, "validate-segment")) {
4609 GstValidateSinkInformation *sink_info;
4611 SCENARIO_LOCK (scenario);
4613 _find_sink_information (scenario,
4614 (GstElement *) GST_MESSAGE_SRC (message));
4617 const GValue *segment_value;
4618 const GstSegment *segment;
4620 GST_DEBUG_OBJECT (scenario, "Got segment update for %s",
4621 GST_ELEMENT_NAME (sink_info->sink));
4622 sink_info->segment_seqnum = GST_MESSAGE_SEQNUM (message);
4623 segment_value = gst_structure_get_value (s, "segment");
4624 g_assert (segment_value != NULL);
4625 segment = (const GstSegment *) g_value_get_boxed (segment_value);
4626 gst_segment_copy_into (segment, &sink_info->segment);
4627 _validate_sink_information (scenario);
4629 SCENARIO_UNLOCK (scenario);
4637 gst_object_unref (pipeline);
4638 /* Check if we got the message expected by a wait action */
4639 _check_waiting_for_message (scenario, message);
4641 execute_next_action_full (scenario, message);
4643 return G_SOURCE_REMOVE;
4647 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
4649 MessageData *d = g_new0 (MessageData, 1);
4651 d->message = gst_message_ref (message);
4652 d->scenario = gst_object_ref (scenario);
4654 g_main_context_invoke_full (scenario->priv->context,
4655 G_PRIORITY_DEFAULT_IDLE,
4656 (GSourceFunc) handle_bus_message, d, (GDestroyNotify) message_data_free);
4660 _action_type_has_parameter (GstValidateActionType * atype,
4661 const gchar * paramname)
4665 if (!atype->parameters)
4668 for (i = 0; atype->parameters[i].name; i++)
4669 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
4676 gst_validate_scenario_load_structures (GstValidateScenario * scenario,
4677 GList * structures, gboolean * is_config, gchar * origin_file)
4679 gboolean ret = TRUE;
4681 GstValidateScenarioPrivate *priv = scenario->priv;
4687 GST_INFO_OBJECT (scenario, "No structures provided");
4691 for (tmp = structures; tmp; tmp = tmp->next) {
4692 GstValidateAction *action;
4693 GstValidateActionType *action_type;
4695 gboolean on_clock = FALSE;
4696 GstStructure *structure = (GstStructure *) tmp->data;
4698 type = gst_structure_get_name (structure);
4699 if (!g_strcmp0 (type, "description") || !g_strcmp0 (type, "meta")) {
4700 const gchar *pipeline_name;
4702 gst_structure_get_boolean (structure, "is-config", is_config);
4703 gst_structure_get_boolean (structure, "handles-states",
4704 &priv->handles_state);
4705 gst_structure_get_boolean (structure, "ignore-eos", &priv->ignore_eos);
4706 gst_structure_get_boolean (structure, "allow-errors",
4707 &priv->allow_errors);
4708 gst_structure_get_boolean (structure, "actions-on-idle",
4709 &priv->execute_on_idle);
4711 if (!priv->handles_state)
4712 priv->target_state = GST_STATE_PLAYING;
4714 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
4715 if (pipeline_name) {
4716 g_free (priv->pipeline_name);
4717 priv->pipeline_name = g_strdup (pipeline_name);
4720 gst_validate_utils_get_clocktime (structure, "max-latency",
4721 &priv->max_latency);
4723 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
4724 scenario->description = gst_structure_copy (structure);
4727 } else if (!(action_type = _find_action_type (type))) {
4728 if (gst_structure_has_field (structure, "optional-action-type")) {
4729 GST_INFO_OBJECT (scenario,
4730 "Action type not found %s but marked as not mandatory", type);
4734 gst_validate_error_structure (structure,
4735 "Unknown action type: '%s'", type);
4739 gst_structure_get_boolean (structure, "on-clock", &on_clock);
4740 if ((!g_strcmp0 (type, "crank-clock") || on_clock) && !priv->clock)
4741 priv->clock = GST_TEST_CLOCK (gst_test_clock_new ());
4743 if (action_type->parameters) {
4746 for (i = 0; action_type->parameters[i].name; i++) {
4747 if (action_type->parameters[i].mandatory &&
4748 gst_structure_has_field (structure,
4749 action_type->parameters[i].name) == FALSE) {
4750 gst_validate_error_structure (structure,
4751 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
4752 action_type->parameters[i].name, structure);
4758 action = gst_validate_action_new (scenario, action_type, structure, TRUE);
4759 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
4760 GST_ERROR_OBJECT (scenario, "Newly created action: %" GST_PTR_FORMAT
4761 " was in error state", structure);
4766 action->action_number = priv->num_actions++;
4768 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
4769 GST_DEBUG_OBJECT (scenario,
4770 "Unrefing action that has already been executed");
4771 gst_validate_action_unref (action);
4776 /* max-latency and max-dropped can be overridden using config */
4777 for (config = gst_validate_plugin_get_config (NULL); config;
4778 config = g_list_next (config)) {
4779 GstClockTime max_latency;
4781 gst_validate_utils_get_clocktime (config->data, "max-latency",
4783 if (GST_CLOCK_TIME_IS_VALID (max_latency))
4784 priv->max_latency = max_latency;
4786 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
4790 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
4801 gst_validate_scenario_get_include_paths (const gchar * relative_scenario)
4804 gchar **env_scenariodir;
4805 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
4807 if (relative_scenario) {
4808 gchar *relative_dir = g_path_get_dirname (relative_scenario);
4809 gchar *tmp_scenarios_path =
4810 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
4812 g_free (relative_dir);
4814 g_free (scenarios_path);
4815 scenarios_path = tmp_scenarios_path;
4819 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
4821 g_free (scenarios_path);
4823 n = env_scenariodir ? g_strv_length (env_scenariodir) : 0;
4824 env_scenariodir = g_realloc_n (env_scenariodir, n + 3, sizeof (gchar *));
4825 env_scenariodir[n] = g_build_filename (g_get_user_data_dir (),
4826 "gstreamer-" GST_API_VERSION, "validate",
4827 GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4828 env_scenariodir[n + 1] =
4829 g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION, "validate",
4830 GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4831 env_scenariodir[n + 2] = NULL;
4833 return env_scenariodir;
4837 _load_scenario_file (GstValidateScenario * scenario,
4838 gchar * scenario_file, gboolean * is_config)
4840 return gst_validate_scenario_load_structures (scenario,
4841 gst_validate_utils_structs_parse_from_filename (scenario_file,
4842 (GstValidateGetIncludePathsFunc)
4843 gst_validate_scenario_get_include_paths, NULL), is_config,
4848 gst_validate_scenario_load (GstValidateScenario * scenario,
4849 const gchar * scenario_name)
4851 gchar **scenarios = NULL;
4853 gboolean found_actions = FALSE, is_config, ret = FALSE;
4854 gchar **include_paths = gst_validate_scenario_get_include_paths (NULL);
4859 scenarios = g_strsplit (scenario_name, ":", -1);
4861 for (i = 0; scenarios[i]; i++) {
4863 gchar *lfilename = NULL, *tldir = NULL, *scenario_file = NULL;
4867 /* First check if the scenario name is not a full path to the
4868 * actual scenario */
4869 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
4870 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
4871 "Trying to load it", scenarios[i]);
4872 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config))) {
4873 scenario_file = scenarios[i];
4874 goto check_scenario;
4878 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
4879 lfilename = g_strdup (scenarios[i]);
4882 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
4884 for (include_i = 0; include_paths[include_i]; include_i++) {
4885 tldir = g_build_filename (include_paths[include_i], lfilename, NULL);
4886 if ((ret = _load_scenario_file (scenario, tldir, &is_config))) {
4887 scenario_file = tldir;
4896 /* else check scenario */
4899 gchar *scenario_dir = g_path_get_dirname (scenario_file);
4900 gchar *scenario_fname = g_path_get_basename (scenario_file);
4901 gchar **scenario_name =
4902 g_regex_split_simple ("\\.scenario", scenario_fname, 0, 0);
4904 gst_structure_set (scenario->priv->vars,
4905 "SCENARIO_DIR", G_TYPE_STRING, scenario_dir,
4906 "SCENARIO_NAME", G_TYPE_STRING, scenario_name[0],
4907 "SCENARIO_PATH", G_TYPE_STRING, scenario_file, NULL);
4909 g_free (scenario_dir);
4910 g_free (scenario_fname);
4911 g_strfreev (scenario_name);
4918 if (found_actions == TRUE)
4919 goto one_actions_scenario_max;
4921 found_actions = TRUE;
4928 g_strfreev (include_paths);
4930 g_strfreev (scenarios);
4933 gst_validate_abort ("Could not set scenario %s => EXIT\n", scenario_name);
4939 GST_ERROR ("Invalid name for scenario '%s'", GST_STR_NULL (scenario_name));
4944 one_actions_scenario_max:
4946 GST_ERROR ("You can set at most only one action scenario. "
4947 "You can have several config scenarios though (a config scenario's "
4948 "file must have is-config=true, and all its actions must be executable "
4949 "at parsing time).");
4958 gst_validate_scenario_set_property (GObject * object, guint prop_id,
4959 const GValue * value, GParamSpec * pspec)
4961 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4965 /* we assume the runner is valid as long as this scenario is,
4967 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
4968 g_value_get_object (value));
4970 case PROP_HANDLES_STATE:
4971 g_assert_not_reached ();
4973 case PROP_EXECUTE_ON_IDLE:
4974 self->priv->execute_on_idle = g_value_get_boolean (value);
4982 gst_validate_scenario_get_property (GObject * object, guint prop_id,
4983 GValue * value, GParamSpec * pspec)
4985 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4989 /* we assume the runner is valid as long as this scenario is,
4991 g_value_take_object (value,
4992 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
4994 case PROP_HANDLES_STATE:
4995 g_value_set_boolean (value, self->priv->handles_state);
4997 case PROP_EXECUTE_ON_IDLE:
4998 g_value_set_boolean (value, self->priv->execute_on_idle);
5006 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
5008 GObjectClass *object_class = G_OBJECT_CLASS (klass);
5010 object_class->dispose = gst_validate_scenario_dispose;
5011 object_class->finalize = gst_validate_scenario_finalize;
5013 object_class->get_property = gst_validate_scenario_get_property;
5014 object_class->set_property = gst_validate_scenario_set_property;
5016 g_object_class_install_property (object_class, PROP_RUNNER,
5017 g_param_spec_object ("validate-runner", "VALIDATE Runner",
5018 "The Validate runner to report errors to",
5019 GST_TYPE_VALIDATE_RUNNER,
5020 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
5022 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
5023 g_param_spec_boolean ("handles-states", "Handles state",
5024 "True if the application should not handle the first state change. "
5025 "False if it is application responsibility",
5026 FALSE, G_PARAM_READABLE));
5028 g_object_class_install_property (object_class,
5029 PROP_EXECUTE_ON_IDLE,
5030 g_param_spec_boolean ("execute-on-idle",
5031 "Force waiting between actions",
5032 "Always execute actions on idle and do not chain them to execute as"
5033 " fast as possible. Setting this property is useful if action"
5034 " execution can lead to the addition of new sources on the same main"
5035 " loop as it provides these new GSource a chance to be dispatched"
5036 " between actions", FALSE, G_PARAM_READWRITE));
5039 * GstValidateScenario::done:
5040 * @scenario: The scenario running
5042 * Emitted once all actions have been executed
5044 scenario_signals[DONE] =
5045 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
5046 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
5049 * GstValidateScenario::action-done:
5050 * @scenario: The scenario running
5051 * @action: The #GstValidateAction that is done running
5053 * Emitted when an action is done.
5057 scenario_signals[ACTION_DONE] =
5058 g_signal_new ("action-done", G_TYPE_FROM_CLASS (klass),
5059 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
5060 GST_TYPE_VALIDATE_ACTION);
5064 gst_validate_scenario_init (GstValidateScenario * scenario)
5066 GstValidateScenarioPrivate *priv = scenario->priv =
5067 gst_validate_scenario_get_instance_private (scenario);
5069 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
5070 priv->segment_start = 0;
5071 priv->segment_stop = GST_CLOCK_TIME_NONE;
5072 priv->current_seek = NULL;
5073 priv->current_seqnum = GST_SEQNUM_INVALID;
5074 priv->action_execution_interval = 10;
5075 priv->vars = gst_structure_new_empty ("vars");
5076 priv->needs_playback_parsing = TRUE;
5077 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
5078 priv->max_latency = GST_CLOCK_TIME_NONE;
5079 priv->max_dropped = -1;
5082 g_mutex_init (&priv->lock);
5084 scenario->priv->context = g_main_context_get_thread_default ();
5085 if (!scenario->priv->context)
5086 scenario->priv->context = g_main_context_default ();
5087 g_main_context_ref (scenario->priv->context);
5091 gst_validate_scenario_dispose (GObject * object)
5093 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
5095 g_weak_ref_clear (&priv->ref_pipeline);
5098 gst_bus_remove_signal_watch (priv->bus);
5099 gst_object_unref (priv->bus);
5103 gst_object_replace ((GstObject **) & priv->clock, NULL);
5105 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
5109 gst_validate_scenario_finalize (GObject * object)
5111 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
5112 GstValidateScenarioPrivate *priv = self->priv;
5114 /* Because g_object_add_weak_pointer() is used, this MUST be on the
5116 g_assert (g_main_context_acquire (priv->context));
5117 g_main_context_release (priv->context);
5119 g_main_context_unref (priv->context);
5120 priv->context = NULL;
5122 g_list_free_full (priv->seeks,
5123 (GDestroyNotify) gst_validate_seek_information_free);
5124 g_list_free_full (priv->sinks,
5125 (GDestroyNotify) gst_validate_sink_information_free);
5126 g_list_free_full (priv->actions, (GDestroyNotify) gst_validate_action_unref);
5127 g_list_free_full (priv->non_blocking_running_actions,
5128 (GDestroyNotify) gst_validate_action_unref);
5129 g_list_free_full (priv->on_addition_actions,
5130 (GDestroyNotify) gst_validate_action_unref);
5131 g_free (priv->pipeline_name);
5132 gst_structure_free (priv->vars);
5133 if (self->description)
5134 gst_structure_free (self->description);
5135 g_mutex_clear (&priv->lock);
5137 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
5140 static void _element_added_cb (GstBin * bin, GstElement * element,
5141 GstValidateScenario * scenario);
5142 static void _element_removed_cb (GstBin * bin, GstElement * element,
5143 GstValidateScenario * scenario);
5146 iterate_children (GstValidateScenario * scenario, GstBin * bin)
5149 GValue v = G_VALUE_INIT;
5150 gboolean done = FALSE;
5151 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
5153 called = g_hash_table_new (NULL, NULL);
5154 it = gst_bin_iterate_elements (bin);
5157 switch (gst_iterator_next (it, &v)) {
5158 case GST_ITERATOR_OK:{
5159 GstElement *child = g_value_get_object (&v);
5161 if (g_hash_table_lookup (called, child) == NULL) {
5162 _element_added_cb (bin, child, scenario);
5163 g_hash_table_add (called, child);
5168 case GST_ITERATOR_RESYNC:
5169 gst_iterator_resync (it);
5171 case GST_ITERATOR_ERROR:
5172 case GST_ITERATOR_DONE:
5177 gst_iterator_free (it);
5178 g_hash_table_unref (called);
5182 should_execute_action (GstElement * element, GstValidateAction * action)
5184 return gst_validate_element_matches_target (element, action->structure);
5188 * * The element has no parent (pipeline)
5189 * * Or it's a sink*/
5191 _all_parents_are_sink (GstElement * element)
5193 if (GST_OBJECT_PARENT (element) == NULL)
5196 if (!GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK))
5199 return _all_parents_are_sink ((GstElement *) GST_OBJECT_PARENT (element));
5203 _element_removed_cb (GstBin * bin, GstElement * element,
5204 GstValidateScenario * scenario)
5206 GstValidateScenarioPrivate *priv = scenario->priv;
5208 if (GST_IS_BASE_SINK (element)) {
5209 GstValidateSinkInformation *sink_info;
5210 SCENARIO_LOCK (scenario);
5211 sink_info = _find_sink_information (scenario, element);
5213 GST_DEBUG_OBJECT (scenario, "Removing sink information for %s",
5214 GST_ELEMENT_NAME (element));
5215 priv->sinks = g_list_remove (priv->sinks, sink_info);
5216 gst_validate_sink_information_free (sink_info);
5218 SCENARIO_UNLOCK (scenario);
5223 _element_added_cb (GstBin * bin, GstElement * element,
5224 GstValidateScenario * scenario)
5228 GstValidateScenarioPrivate *priv = scenario->priv;
5230 /* Check if it's an element we track for a set-property action */
5231 SCENARIO_LOCK (scenario);
5232 tmp = priv->on_addition_actions;
5234 GstValidateAction *action = (GstValidateAction *) tmp->data;
5236 if (action->playback_time != GST_CLOCK_TIME_NONE)
5238 if (g_strcmp0 (action->type, "set-property"))
5241 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
5242 action, action->type);
5243 if (should_execute_action (element, action)) {
5244 GstValidateActionType *action_type;
5245 action_type = _find_action_type (action->type);
5246 GST_DEBUG_OBJECT (element, "Executing set-property action");
5247 if (gst_validate_execute_action (action_type, action)) {
5248 if (!gst_structure_has_field_typed (action->structure,
5249 "on-all-instances", G_TYPE_BOOLEAN)) {
5250 priv->on_addition_actions =
5251 g_list_remove_link (priv->on_addition_actions, tmp);
5252 gst_mini_object_unref (GST_MINI_OBJECT (action));
5254 tmp = priv->on_addition_actions;
5264 /* If it's a new GstBaseSink, add to list of sink information */
5265 if (GST_IS_BASE_SINK (element) && _all_parents_are_sink (element)) {
5266 GstValidateSinkInformation *sink_info =
5267 g_new0 (GstValidateSinkInformation, 1);
5268 GST_DEBUG_OBJECT (scenario, "Adding %s to list of tracked sinks",
5269 GST_ELEMENT_NAME (element));
5270 sink_info->sink = gst_object_ref (element);
5271 priv->sinks = g_list_append (priv->sinks, sink_info);
5274 SCENARIO_UNLOCK (scenario);
5276 /* If it's a bin, listen to the child */
5277 if (GST_IS_BIN (element)) {
5278 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
5280 g_signal_connect (element, "element-removed",
5281 (GCallback) _element_removed_cb, scenario);
5282 iterate_children (scenario, GST_BIN (element));
5286 static GstValidateScenario *
5287 gst_validate_scenario_new (GstValidateRunner *
5288 runner, GstElement * pipeline, gchar * scenario_name, GList * structures)
5291 GstValidateScenario *scenario =
5292 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
5297 gst_validate_scenario_load_structures (scenario, structures, &is_config,
5301 GST_LOG ("Creating scenario %s", scenario_name);
5302 if (!gst_validate_scenario_load (scenario, scenario_name)) {
5303 g_object_unref (scenario);
5309 if (scenario->priv->pipeline_name &&
5310 !g_pattern_match_simple (scenario->priv->pipeline_name,
5311 GST_OBJECT_NAME (pipeline))) {
5312 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
5313 scenario_name, scenario->priv->pipeline_name,
5314 GST_OBJECT_NAME (pipeline));
5316 gst_object_unref (scenario);
5321 gst_validate_printf (NULL,
5322 "**-> Running scenario %s on pipeline %s**\n", scenario_name,
5323 GST_OBJECT_NAME (pipeline));
5325 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
5326 if (scenario->priv->clock) {
5327 gst_element_set_clock (pipeline, GST_CLOCK_CAST (scenario->priv->clock));
5328 gst_pipeline_use_clock (GST_PIPELINE (pipeline),
5329 GST_CLOCK_CAST (scenario->priv->clock));
5331 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
5332 g_filename_display_basename (scenario_name));
5334 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
5336 g_signal_connect (pipeline, "element-removed",
5337 (GCallback) _element_removed_cb, scenario);
5339 iterate_children (scenario, GST_BIN (pipeline));
5341 scenario->priv->bus = gst_element_get_bus (pipeline);
5342 gst_bus_add_signal_watch (scenario->priv->bus);
5343 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
5346 for (config = gst_validate_plugin_get_config (NULL); config;
5347 config = config->next) {
5350 if (gst_structure_get_uint (config->data,
5351 "scenario-action-execution-interval",
5352 &scenario->priv->action_execution_interval)) {
5353 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5354 scenario->priv->action_execution_interval);
5355 if (scenario->priv->action_execution_interval > 0)
5356 scenario->priv->execute_on_idle = TRUE;
5358 } else if (gst_structure_get_int (config->data,
5359 "scenario-action-execution-interval", &interval)) {
5361 scenario->priv->action_execution_interval = (guint) interval;
5362 scenario->priv->execute_on_idle = TRUE;
5363 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5364 scenario->priv->action_execution_interval);
5367 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
5372 if (scenario->priv->handles_state) {
5373 GST_INFO_OBJECT (scenario, "Scenario handles state."
5374 " Starting the get position source");
5375 _add_execute_actions_gsource (scenario);
5378 scenario->priv->overrides =
5379 gst_validate_override_registry_get_override_for_names
5380 (gst_validate_override_registry_get (), "scenarios", NULL);
5385 GstValidateScenario *
5386 gst_validate_scenario_from_structs (GstValidateRunner * runner,
5387 GstElement * pipeline, GList * structures, gchar * origin_file)
5389 g_return_val_if_fail (structures, NULL);
5391 return gst_validate_scenario_new (runner, pipeline, origin_file, structures);
5395 * gst_validate_scenario_factory_create:
5396 * @runner: The #GstValidateRunner to use to report issues
5397 * @pipeline: The pipeline to run the scenario on
5398 * @scenario_name: The name (or path) of the scenario to run
5400 * Returns: (transfer full) (nullable): A #GstValidateScenario or NULL
5402 GstValidateScenario *
5403 gst_validate_scenario_factory_create (GstValidateRunner *
5404 runner, GstElement * pipeline, const gchar * scenario_name)
5406 return gst_validate_scenario_new (runner, pipeline, (gchar *) scenario_name,
5411 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
5413 gchar *tmp = gst_value_serialize (value);
5414 gchar *tmpcompress = g_strcompress (tmp);
5416 g_key_file_set_string (kfg->kf, kfg->group_name,
5417 g_quark_to_string (field_id), tmpcompress);
5419 g_free (tmpcompress);
5426 gst_validate_scenario_check_and_set_needs_clock_sync (GList * structures,
5427 GstStructure ** meta)
5429 gboolean needs_clock_sync = FALSE;
5432 for (tmp = structures; tmp; tmp = tmp->next) {
5433 GstStructure *_struct = (GstStructure *) tmp->data;
5434 gboolean is_meta = gst_structure_has_name (_struct, "description")
5435 || gst_structure_has_name (_struct, "meta");
5438 GstValidateActionType *type =
5439 _find_action_type (gst_structure_get_name (_struct));
5441 if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
5442 needs_clock_sync = TRUE;
5447 *meta = gst_structure_copy (_struct);
5450 if (needs_clock_sync) {
5452 gst_structure_set (*meta, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
5454 *meta = gst_structure_from_string ("description, need-clock-sync=true;",
5458 return needs_clock_sync;
5462 _parse_scenario (GFile * f, GKeyFile * kf)
5464 gboolean ret = FALSE;
5465 gchar *path = g_file_get_path (f);
5467 if (g_str_has_suffix (path, GST_VALIDATE_SCENARIO_SUFFIX)
5468 || g_str_has_suffix (path, GST_VALIDATE_VALIDATE_TEST_SUFFIX)) {
5469 GstStructure *meta = NULL;
5470 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f,
5471 (GstValidateGetIncludePathsFunc)
5472 gst_validate_scenario_get_include_paths);
5474 gst_validate_scenario_check_and_set_needs_clock_sync (structures, &meta);
5475 for (tmp = structures; tmp; tmp = tmp->next)
5476 gst_structure_remove_fields (tmp->data, "__lineno__", "__filename__",
5480 KeyFileGroupName kfg;
5482 kfg.group_name = g_file_get_path (f);
5485 gst_structure_remove_fields (meta, "__lineno__", "__filename__",
5487 gst_structure_foreach (meta,
5488 (GstStructureForeachFunc) _add_description, &kfg);
5489 gst_structure_free (meta);
5490 g_free (kfg.group_name);
5492 g_key_file_set_string (kf, path, "noinfo", "nothing");
5494 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
5504 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
5506 GFileEnumerator *fenum;
5509 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
5510 G_FILE_QUERY_INFO_NONE, NULL, NULL);
5515 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
5516 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
5517 GFile *f = g_file_enumerator_get_child (fenum, info);
5519 _parse_scenario (f, kf);
5520 gst_object_unref (f);
5523 gst_object_unref (fenum);
5527 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
5528 gchar * output_file)
5534 GKeyFile *kf = NULL;
5536 const gchar *envvar;
5537 gchar **env_scenariodir = NULL;
5538 gchar *tldir = g_build_filename (g_get_user_data_dir (),
5539 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
5541 GFile *dir = g_file_new_for_path (tldir);
5544 kf = g_key_file_new ();
5545 if (num_scenarios > 0) {
5549 for (i = 0; i < num_scenarios; i++) {
5550 file = g_file_new_for_path (scenarios[i]);
5551 if (!_parse_scenario (file, kf)) {
5552 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
5556 g_clear_object (&file);
5562 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
5564 env_scenariodir = g_strsplit (envvar, ":", 0);
5566 _list_scenarios_in_dir (dir, kf);
5567 g_clear_object (&dir);
5569 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
5570 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
5571 dir = g_file_new_for_path (tldir);
5572 _list_scenarios_in_dir (dir, kf);
5573 g_clear_object (&dir);
5576 if (env_scenariodir) {
5580 for (i = 0; env_scenariodir[i]; i++) {
5581 subfile = g_file_new_for_path (env_scenariodir[i]);
5582 _list_scenarios_in_dir (subfile, kf);
5583 g_clear_object (&subfile);
5587 /* Hack to make it work within the development environment */
5588 dir = g_file_new_for_path ("data/scenarios");
5589 _list_scenarios_in_dir (dir, kf);
5590 g_clear_object (&dir);
5593 result = g_key_file_to_data (kf, &datalength, &err);
5594 gst_validate_printf (NULL, "All scenarios available:\n%s", result);
5596 if (output_file && !err) {
5597 if (!g_file_set_contents (output_file, result, datalength, &err)) {
5598 GST_WARNING ("Error writing to file '%s'", output_file);
5604 if (env_scenariodir)
5605 g_strfreev (env_scenariodir);
5608 GST_WARNING ("Got error '%s' listing scenarios", err->message);
5609 g_clear_error (&err);
5613 g_clear_object (&dir);
5615 g_key_file_free (kf);
5620 static GstValidateActionReturn
5621 check_last_sample_internal (GstValidateScenario * scenario,
5622 GstValidateAction * action, GstElement * sink)
5627 const gchar *target_sum;
5628 guint64 frame_number;
5629 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
5630 GstVideoTimeCodeMeta *tc_meta;
5632 g_object_get (sink, "last-sample", &sample, NULL);
5633 if (sample == NULL) {
5634 GST_VALIDATE_REPORT_ACTION (scenario, action,
5635 SCENARIO_ACTION_EXECUTION_ERROR,
5636 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
5637 " 'last-sample' property is NULL"
5638 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
5641 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5644 buffer = gst_sample_get_buffer (sample);
5645 target_sum = gst_structure_get_string (action->structure, "checksum");
5649 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
5650 GST_VALIDATE_REPORT_ACTION (scenario, action,
5651 SCENARIO_ACTION_EXECUTION_ERROR,
5652 "Last sample buffer could not be mapped, action can't run.");
5653 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5656 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
5657 gst_buffer_unmap (buffer, &map);
5659 if (g_strcmp0 (sum, target_sum)) {
5660 GST_VALIDATE_REPORT_ACTION (scenario, action,
5661 SCENARIO_ACTION_EXECUTION_ERROR,
5662 "Last buffer checksum '%s' is different than the expected one: '%s'",
5665 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5672 if (!gst_structure_get_uint64 (action->structure, "timecode-frame-number",
5676 if (!gst_structure_get_int (action->structure, "timecode-frame-number",
5678 GST_VALIDATE_REPORT_ACTION (scenario, action,
5679 SCENARIO_ACTION_EXECUTION_ERROR,
5680 "The 'checksum' or 'time-code-frame-number' parameters of the "
5681 "`check-last-sample` action type needs to be specified, none found");
5683 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5687 frame_number = (guint64) iframe_number;
5690 tc_meta = gst_buffer_get_video_time_code_meta (buffer);
5692 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5693 "Could not \"check-last-sample\" as the buffer doesn't contain a TimeCode"
5695 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5699 if (gst_video_time_code_frames_since_daily_jam (&tc_meta->tc) != frame_number) {
5700 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5701 "Last buffer frame number '%" G_GINT64_FORMAT
5702 "' is different than the expected one: '%" G_GINT64_FORMAT "'",
5703 gst_video_time_code_frames_since_daily_jam (&tc_meta->tc),
5705 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5709 gst_sample_unref (sample);
5714 sink_last_sample_notify_cb (GstElement * sink, GParamSpec * arg G_GNUC_UNUSED,
5715 GstValidateAction * action)
5717 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5720 GST_VALIDATE_REPORT_ACTION (scenario, action,
5721 SCENARIO_ACTION_EXECUTION_ERROR,
5722 "No pipeline anymore, can't check last sample");
5726 check_last_sample_internal (scenario, action, sink);
5727 gst_object_unref (scenario);
5730 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5732 gst_validate_action_set_done (action);
5733 gst_validate_action_unref (action);
5736 static GstValidateExecuteActionReturn
5737 _check_last_sample_value (GstValidateScenario * scenario,
5738 GstValidateAction * action, GstElement * sink)
5742 /* Connect before checking last sample to avoid a race where
5743 * the sample is set between the time we connect and the time
5744 * the time we get it */
5745 g_signal_connect (sink, "notify::last-sample",
5746 G_CALLBACK (sink_last_sample_notify_cb),
5747 gst_validate_action_ref (action));
5749 g_object_get (sink, "last-sample", &sample, NULL);
5751 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
5752 gst_sample_unref (sample);
5753 gst_validate_action_unref (action);
5755 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5758 return check_last_sample_internal (scenario, action, sink);
5762 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
5763 const gchar * fname, GstCaps * sinkpad_caps)
5767 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
5768 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
5773 if (paramspec->value_type != GST_TYPE_SAMPLE)
5776 if (!name && !fname && !sinkpad_caps)
5779 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
5783 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
5789 sinkpad = gst_element_get_static_pad (sink, "sink");
5793 tmpcaps = gst_pad_get_current_caps (sinkpad);
5795 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
5797 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
5798 gst_caps_unref (tmpcaps);
5802 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
5808 static GstValidateExecuteActionReturn
5809 _execute_check_last_sample (GstValidateScenario * scenario,
5810 GstValidateAction * action)
5813 GValue data = { 0, };
5814 gboolean done = FALSE;
5815 GstCaps *caps = NULL;
5816 GstElement *sink = NULL, *tmpelement;
5817 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
5819 gst_structure_get_string (action->structure, "sink-factory-name"),
5820 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
5821 DECLARE_AND_GET_PIPELINE (scenario, action);
5824 caps = gst_caps_from_string (caps_str);
5829 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
5831 switch (gst_iterator_next (it, &data)) {
5832 case GST_ITERATOR_OK:
5833 tmpelement = g_value_get_object (&data);
5834 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
5837 if (!gst_object_has_as_ancestor (GST_OBJECT (tmpelement),
5838 GST_OBJECT (sink))) {
5839 gchar *tmp = gst_structure_to_string (action->structure);
5841 GST_VALIDATE_REPORT_ACTION (scenario, action,
5842 SCENARIO_ACTION_EXECUTION_ERROR,
5843 "Could not \"check-last-sample\" as several elements were found "
5844 "from describing string: '%s' (%s and %s match)", tmp,
5845 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
5850 gst_object_unref (sink);
5853 sink = gst_object_ref (tmpelement);
5855 g_value_reset (&data);
5857 case GST_ITERATOR_RESYNC:
5858 gst_iterator_resync (it);
5859 g_clear_object (&sink);
5861 case GST_ITERATOR_ERROR:
5863 case GST_ITERATOR_DONE:
5868 gst_iterator_free (it);
5870 gst_caps_unref (caps);
5873 GST_VALIDATE_REPORT_ACTION (scenario, action,
5874 SCENARIO_ACTION_EXECUTION_ERROR,
5875 "Could not \"check-last-sample\" as no sink was found from description: '%"
5876 GST_PTR_FORMAT "'", action->structure);
5881 g_clear_object (&pipeline);
5882 return _check_last_sample_value (scenario, action, sink);
5885 g_clear_object (&sink);
5886 g_clear_object (&pipeline);
5887 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5890 static GstPadProbeReturn
5891 _check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info,
5892 GstValidateAction * action)
5894 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5895 GstClockTime target_running_time = GST_CLOCK_TIME_NONE;
5896 gint count_bufs = 0;
5898 gst_validate_action_get_clocktime (scenario, action,
5899 "running-time", &target_running_time);
5900 if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
5901 if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info)))
5902 gst_structure_set (action->structure, "__priv_seen_event", G_TYPE_BOOLEAN,
5904 else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT
5905 && GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
5906 const GstSegment *segment = NULL;
5908 gst_event_parse_segment (info->data, &segment);
5909 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5912 } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))
5913 && gst_structure_has_field_typed (action->structure, "__priv_seen_event",
5915 GstSegment *segment = NULL;
5917 if (GST_CLOCK_TIME_IS_VALID (target_running_time)) {
5918 GstClockTime running_time;
5920 gst_structure_get (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5923 gst_segment_to_running_time (segment, GST_FORMAT_TIME,
5924 GST_BUFFER_TIMESTAMP (info->data));
5926 if (running_time < target_running_time)
5930 gst_structure_get_int (action->structure, "__priv_count_bufs", &count_bufs);
5931 if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info),
5932 GST_BUFFER_FLAG_DELTA_UNIT)) {
5933 if (count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) {
5934 GST_VALIDATE_REPORT_ACTION (scenario, action,
5935 SCENARIO_ACTION_EXECUTION_ERROR,
5936 "Did not receive a key frame after requested one, "
5937 "at running_time %" GST_TIME_FORMAT " (with a %i "
5938 "frame tolerance)", GST_TIME_ARGS (target_running_time),
5939 NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE);
5941 gst_validate_action_set_done (action);
5942 gst_object_unref (scenario);
5943 return GST_PAD_PROBE_REMOVE;
5946 gst_structure_set (action->structure, "__priv_count_bufs", G_TYPE_INT,
5947 count_bufs++, NULL);
5949 GST_INFO_OBJECT (pad,
5950 "Properly got keyframe after \"force-keyframe\" event "
5951 "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))",
5952 GST_TIME_ARGS (target_running_time), count_bufs);
5954 gst_structure_remove_fields (action->structure, "__priv_count_bufs",
5955 "__priv_segment", "__priv_seen_event", NULL);
5956 gst_validate_action_set_done (action);
5957 gst_object_unref (scenario);
5958 return GST_PAD_PROBE_REMOVE;
5962 gst_object_unref (scenario);
5964 return GST_PAD_PROBE_OK;
5967 static GstValidateExecuteActionReturn
5968 _execute_crank_clock (GstValidateScenario * scenario,
5969 GstValidateAction * action)
5971 GstClockTime expected_diff, expected_time;
5972 GstClockTime prev_time =
5973 gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
5975 if (!gst_test_clock_crank (scenario->priv->clock)) {
5976 GST_VALIDATE_REPORT_ACTION (scenario, action,
5977 SCENARIO_ACTION_EXECUTION_ERROR, "Cranking clock failed");
5979 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5982 if (gst_validate_action_get_clocktime (scenario, action,
5983 "expected-elapsed-time", &expected_diff)) {
5984 GstClockTime elapsed =
5985 gst_clock_get_time (GST_CLOCK (scenario->priv->clock)) - prev_time;
5987 if (expected_diff != elapsed) {
5988 GST_VALIDATE_REPORT_ACTION (scenario, action,
5989 SCENARIO_ACTION_EXECUTION_ERROR,
5990 "Elapsed time during test clock cranking different than expected,"
5991 " waited for %" GST_TIME_FORMAT " instead of the expected %"
5992 GST_TIME_FORMAT, GST_TIME_ARGS (elapsed),
5993 GST_TIME_ARGS (expected_diff));
5995 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5999 if (gst_validate_action_get_clocktime (scenario, action, "expected-time",
6001 GstClockTime time = gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
6003 if (expected_time != time) {
6004 GST_VALIDATE_REPORT_ACTION (scenario, action,
6005 SCENARIO_ACTION_EXECUTION_ERROR,
6006 "Clock time after cranking different than expected,"
6007 " got %" GST_TIME_FORMAT " instead of the expected %" GST_TIME_FORMAT,
6008 GST_TIME_ARGS (time), GST_TIME_ARGS (expected_time));
6010 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
6014 return GST_VALIDATE_EXECUTE_ACTION_OK;
6018 _execute_request_key_unit (GstValidateScenario * scenario,
6019 GstValidateAction * action)
6022 gboolean all_headers = FALSE;
6023 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
6024 GstEvent *event = NULL;
6025 GstQuery *segment_query;
6026 GList *targets = NULL, *tmp;
6027 GstElement *video_encoder = NULL;
6028 GstPad *pad = NULL, *encoder_srcpad = NULL;
6029 GstClockTime running_time = GST_CLOCK_TIME_NONE;
6030 GstSegment segment = { 0, };
6031 const gchar *direction = gst_structure_get_string (action->structure,
6032 "direction"), *pad_name, *srcpad_name;
6034 DECLARE_AND_GET_PIPELINE (scenario, action);
6036 if (gst_structure_get_string (action->structure, "target-element-name")) {
6037 GstElement *target = _get_target_element (scenario, action);
6041 targets = g_list_append (targets, target);
6043 if (!gst_structure_get_string (action->structure,
6044 "target-element-klass") &&
6045 !gst_structure_get_string (action->structure,
6046 "target-element-factory-name")) {
6047 gst_structure_set (action->structure, "target-element-klass",
6048 G_TYPE_STRING, "Video/Encoder", NULL);
6051 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
6055 GST_VALIDATE_REPORT_ACTION (scenario, action,
6056 SCENARIO_ACTION_EXECUTION_ERROR,
6057 "Could not find any element from action: %" GST_PTR_FORMAT,
6062 gst_validate_action_get_clocktime (scenario, action,
6063 "running-time", &running_time);
6064 gst_structure_get_boolean (action->structure, "all-headers", &all_headers);
6065 if (!gst_structure_get_uint (action->structure, "count", &count)) {
6066 gst_structure_get_int (action->structure, "count", (gint *) & count);
6068 pad_name = gst_structure_get_string (action->structure, "pad");
6069 srcpad_name = gst_structure_get_string (action->structure, "srcpad");
6071 srcpad_name = "src";
6073 for (tmp = targets; tmp; tmp = tmp->next) {
6074 video_encoder = tmp->data;
6075 encoder_srcpad = gst_element_get_static_pad (video_encoder, srcpad_name);
6076 if (!encoder_srcpad) {
6077 GST_VALIDATE_REPORT_ACTION (scenario, action,
6078 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
6083 if (g_strcmp0 (direction, "upstream") == 0) {
6084 event = gst_video_event_new_upstream_force_key_unit (running_time,
6085 all_headers, count);
6087 pad = gst_element_get_static_pad (video_encoder, srcpad_name);
6089 GST_VALIDATE_REPORT_ACTION (scenario, action,
6090 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
6095 GST_ERROR_OBJECT (encoder_srcpad, "Sending RequestKeyUnit event");
6096 gst_pad_add_probe (encoder_srcpad,
6097 GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
6098 (GstPadProbeCallback) _check_is_key_unit_cb,
6099 gst_validate_action_ref (action),
6100 (GDestroyNotify) gst_validate_action_unref);
6101 } else if (g_strcmp0 (direction, "downstream") == 0) {
6102 GstClockTime timestamp = GST_CLOCK_TIME_NONE,
6103 stream_time = GST_CLOCK_TIME_NONE;
6108 pad = gst_element_get_static_pad (video_encoder, pad_name);
6110 GST_VALIDATE_REPORT_ACTION (scenario, action,
6111 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s", pad_name);
6116 gst_validate_action_get_clocktime (scenario, action,
6117 "timestamp", ×tamp);
6119 gst_validate_action_get_clocktime (scenario, action,
6120 "stream-time", &stream_time);
6123 gst_video_event_new_downstream_force_key_unit (timestamp, stream_time,
6124 running_time, all_headers, count);
6126 gst_pad_add_probe (pad,
6127 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6128 (GstPadProbeCallback) _check_is_key_unit_cb,
6129 gst_validate_action_ref (action),
6130 (GDestroyNotify) gst_validate_action_unref);
6132 GST_VALIDATE_REPORT_ACTION (scenario, action,
6133 SCENARIO_ACTION_EXECUTION_ERROR,
6134 "request keyunit direction %s invalid (should be in"
6135 " [downstrean, upstream]", direction);
6140 gst_validate_printf (action, "Sending a \"force key unit\" event %s\n",
6143 segment_query = gst_query_new_segment (GST_FORMAT_TIME);
6144 gst_pad_query (encoder_srcpad, segment_query);
6146 gst_query_parse_segment (segment_query, &(segment.rate),
6147 &(segment.format), (gint64 *) & (segment.start),
6148 (gint64 *) & (segment.stop));
6149 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
6152 gst_pad_add_probe (encoder_srcpad,
6153 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6154 (GstPadProbeCallback) _check_is_key_unit_cb,
6155 gst_validate_action_ref (action),
6156 (GDestroyNotify) gst_validate_action_unref);
6159 if (!gst_pad_send_event (pad, event)) {
6160 GST_VALIDATE_REPORT_ACTION (scenario, action,
6161 SCENARIO_ACTION_EXECUTION_ERROR,
6162 "Could not send \"force key unit\" event %s", direction);
6166 gst_clear_object (&pad);
6167 gst_clear_object (&encoder_srcpad);
6171 g_list_free_full (targets, gst_object_unref);
6172 gst_clear_object (&pad);
6173 gst_clear_object (&encoder_srcpad);
6178 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
6182 static GstValidateExecuteActionReturn
6183 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
6186 GstValidateScenarioPrivate *priv = scenario->priv;
6188 DECLARE_AND_GET_PIPELINE (scenario, action);
6190 bus = gst_element_get_bus (pipeline);
6191 SCENARIO_LOCK (scenario);
6192 if (priv->execute_actions_source_id) {
6193 g_source_remove (priv->execute_actions_source_id);
6194 priv->execute_actions_source_id = 0;
6196 if (scenario->priv->actions || scenario->priv->non_blocking_running_actions ||
6197 scenario->priv->on_addition_actions) {
6198 guint nb_actions = 0;
6199 gchar *actions = g_strdup (""), *tmpconcat;
6201 GList *all_actions = g_list_concat (g_list_concat (scenario->priv->actions,
6202 scenario->priv->non_blocking_running_actions),
6203 scenario->priv->on_addition_actions);
6205 for (tmp = all_actions; tmp; tmp = tmp->next) {
6206 GstValidateAction *remaining_action = (GstValidateAction *) tmp->data;
6207 GstValidateActionType *type;
6209 if (remaining_action == action)
6212 type = _find_action_type (remaining_action->type);
6214 tmpconcat = actions;
6216 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
6217 remaining_action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
6218 remaining_action->priv->optional) {
6219 gst_validate_action_unref (remaining_action);
6226 actions = g_strdup_printf ("%s\n%*s- `%s` at %s:%d", actions, 20, "",
6227 remaining_action->type,
6228 GST_VALIDATE_ACTION_FILENAME (remaining_action),
6229 GST_VALIDATE_ACTION_LINENO (remaining_action));
6230 gst_validate_action_unref (remaining_action);
6233 g_list_free (all_actions);
6234 scenario->priv->actions = NULL;
6235 scenario->priv->non_blocking_running_actions = NULL;
6236 scenario->priv->on_addition_actions = NULL;
6239 if (nb_actions > 0) {
6240 GstClockTime position = GST_CLOCK_TIME_NONE;
6242 _get_position (scenario, NULL, &position);
6243 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
6244 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
6245 ")", nb_actions, actions, GST_TIME_ARGS (position));
6249 SCENARIO_UNLOCK (scenario);
6251 gst_validate_scenario_check_dropped (scenario);
6254 gst_message_new_request_state (GST_OBJECT_CAST (scenario),
6256 gst_object_unref (bus);
6257 gst_object_unref (pipeline);
6263 _action_set_done (GstValidateAction * action)
6265 gchar *repeat_message = NULL;
6266 JsonBuilder *jbuild;
6267 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6269 if (scenario == NULL || !action->priv->pending_set_done)
6270 return G_SOURCE_REMOVE;
6272 action->priv->execution_duration =
6273 gst_util_get_timestamp () - action->priv->execution_time;
6275 jbuild = json_builder_new ();
6276 json_builder_begin_object (jbuild);
6277 json_builder_set_member_name (jbuild, "type");
6278 json_builder_add_string_value (jbuild, "action-done");
6279 json_builder_set_member_name (jbuild, "action-type");
6280 json_builder_add_string_value (jbuild, action->type);
6281 json_builder_set_member_name (jbuild, "execution-duration");
6282 json_builder_add_double_value (jbuild,
6283 ((gdouble) action->priv->execution_duration / GST_SECOND));
6284 json_builder_end_object (jbuild);
6286 gst_validate_send (json_builder_get_root (jbuild));
6287 g_object_unref (jbuild);
6289 action->priv->pending_set_done = FALSE;
6290 switch (action->priv->state) {
6291 case GST_VALIDATE_EXECUTE_ACTION_ERROR:
6292 GST_VALIDATE_REPORT_ACTION (scenario, action,
6293 SCENARIO_ACTION_EXECUTION_ERROR, "Action %s failed", action->type);
6294 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
6295 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
6296 case GST_VALIDATE_EXECUTE_ACTION_NONE:
6297 case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
6298 case GST_VALIDATE_EXECUTE_ACTION_OK:
6300 scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
6302 _check_scenario_is_done (scenario);
6304 if (!gst_validate_parse_next_action_playback_time (scenario)) {
6305 gst_validate_error_structure (scenario->priv->actions ? scenario->
6306 priv->actions->data : NULL,
6307 "Could not determine next action playback time!");
6310 GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
6311 " executing next", action->structure);
6315 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
6319 if (GST_VALIDATE_ACTION_N_REPEATS (action))
6321 g_strdup_printf ("[%d/%d]", action->repeat,
6322 GST_VALIDATE_ACTION_N_REPEATS (action));
6324 gst_validate_printf (NULL,
6325 "%*c⇨ Action `%s` at %s:%d done '%s' %s (duration: %" GST_TIME_FORMAT
6326 ")\n\n", (action->priv->subaction_level * 2) - 1, ' ',
6327 gst_structure_get_name (action->priv->main_structure),
6328 GST_VALIDATE_ACTION_FILENAME (action),
6329 GST_VALIDATE_ACTION_LINENO (action),
6330 gst_validate_action_return_get_name (action->priv->state),
6331 repeat_message ? repeat_message : "",
6332 GST_TIME_ARGS (action->priv->execution_duration));
6333 g_free (repeat_message);
6335 g_signal_emit (scenario, scenario_signals[ACTION_DONE], 0, action);
6336 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING)
6337 /* We took the 'scenario' reference... unreffing it now */
6338 gst_validate_action_unref (action);
6340 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_DONE;
6341 gst_validate_scenario_execute_next_or_restart_looping (scenario);
6342 gst_object_unref (scenario);
6343 return G_SOURCE_REMOVE;
6346 /* gst_validate_action_set_done:
6347 * @action: The action that is done executing
6349 * Sets @action as "done", meaning that the next action can
6353 gst_validate_action_set_done (GstValidateAction * action)
6355 GMainContext *context = action->priv->context;
6356 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6358 action->priv->context = NULL;
6359 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING) {
6363 SCENARIO_LOCK (scenario);
6364 item = g_list_find (scenario->priv->non_blocking_running_actions, action);
6365 scenario->priv->non_blocking_running_actions =
6366 g_list_delete_link (scenario->priv->non_blocking_running_actions,
6368 SCENARIO_UNLOCK (scenario);
6372 gst_validate_action_unref (action);
6375 g_assert (!action->priv->pending_set_done);
6376 action->priv->pending_set_done = TRUE;
6378 if (scenario && scenario->priv->wait_message_action == action) {
6379 gst_validate_action_unref (scenario->priv->wait_message_action);
6380 scenario->priv->wait_message_action = NULL;
6382 gst_clear_object (&scenario);
6384 g_main_context_invoke_full (action->priv->context,
6385 G_PRIORITY_DEFAULT_IDLE,
6386 (GSourceFunc) _action_set_done,
6387 gst_validate_action_ref (action),
6388 (GDestroyNotify) gst_validate_action_unref);
6391 g_main_context_unref (context);
6395 * gst_validate_action_get_scenario:
6396 * @action: The action for which to retrieve the scenario
6398 * Retrieve the scenario from which @action is executed.
6400 * Returns: (transfer full) (nullable): The scenario from which the action is being executed.
6402 GstValidateScenario *
6403 gst_validate_action_get_scenario (GstValidateAction * action)
6405 return g_weak_ref_get (&action->priv->scenario);
6409 * gst_validate_register_action_type:
6410 * @type_name: The name of the new action type to add
6411 * @implementer_namespace: The namespace of the implementer of the action type.
6412 * That should always be the name of the GstPlugin as
6413 * retrieved with #gst_plugin_get_name when the action type
6414 * is registered inside a plugin.
6415 * @function: (scope notified): The function to be called to execute the action
6416 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6417 * @description: A description of the new type
6418 * @flags: The #GstValidateActionTypeFlags to set on the new action type
6420 * Register a new action type to the action type system. If the action type already
6421 * exists, it will be overridden by the new definition
6423 * Returns: (transfer none): The newly created action type or the already registered action type
6424 * if it had a higher rank
6426 GstValidateActionType *
6427 gst_validate_register_action_type (const gchar * type_name,
6428 const gchar * implementer_namespace,
6429 GstValidateExecuteAction function,
6430 GstValidateActionParameter * parameters,
6431 const gchar * description, GstValidateActionTypeFlags flags)
6433 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
6434 type_name, GST_RANK_NONE, function, parameters, description,
6437 g_free (type->implementer_namespace);
6438 type->implementer_namespace = g_strdup (implementer_namespace);
6444 _free_action_types (GList * _action_types)
6446 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
6450 * gst_validate_register_action_type_dynamic:
6451 * @plugin: (allow-none): The #GstPlugin that register the action type,
6452 * or NULL for a static element.
6453 * @rank: The ranking of that implementation of the action type called
6454 * @type_name. If an action type has been registered with the same
6455 * name with a higher rank, the new implementation will not be used,
6456 * and the already registered action type is returned.
6457 * If the already registered implementation has a lower rank, the
6458 * new implementation will be used and returned.
6459 * @type_name: The name of the new action type to add
6460 * @function: (scope notified): The function to be called to execute the action
6461 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6462 * @description: A description of the new type
6463 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
6465 * Returns: (transfer none): The newly created action type or the already registered action type
6466 * if it had a higher rank
6468 GstValidateActionType *
6469 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
6470 const gchar * type_name, GstRank rank,
6471 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
6472 const gchar * description, GstValidateActionTypeFlags flags)
6474 GstValidateActionType *tmptype;
6475 GstValidateActionType *type = gst_validate_action_type_new ();
6476 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
6477 gint n_params = is_config ? 0 : 2;
6480 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
6485 type->parameters = g_new0 (GstValidateActionParameter, n_params);
6489 memcpy (type->parameters, parameters,
6490 sizeof (GstValidateActionParameter) * (n_params));
6493 type->prepare = gst_validate_action_default_prepare_func;
6494 type->execute = function;
6495 type->name = g_strdup (type_name);
6497 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
6499 type->implementer_namespace = g_strdup ("none");
6501 type->description = g_strdup (description);
6502 type->flags = flags;
6505 if ((tmptype = _find_action_type (type_name))) {
6506 if (tmptype->rank <= rank) {
6507 action_types = g_list_remove (action_types, tmptype);
6508 type->overriden_type = tmptype;
6510 gst_mini_object_unref (GST_MINI_OBJECT (type));
6516 if (type != tmptype)
6517 action_types = g_list_append (action_types, type);
6520 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
6521 "GstValidatePluginActionTypes");
6523 plugin_action_types = g_list_prepend (plugin_action_types,
6524 gst_mini_object_ref (GST_MINI_OBJECT (type)));
6526 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
6527 plugin_action_types, (GDestroyNotify) _free_action_types);
6533 GstValidateActionType *
6534 gst_validate_get_action_type (const gchar * type_name)
6536 GstValidateActionType *type = _find_action_type (type_name);
6540 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
6546 gst_validate_list_action_types (void)
6548 return action_types;
6552 * gst_validate_print_action_types:
6553 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
6554 * @num_wanted_types: Length of @wanted_types
6556 * Prints the action types details wanted in @wanted_types
6558 * Returns: True if all types could be printed
6561 gst_validate_print_action_types (const gchar ** wanted_types,
6562 gint num_wanted_types)
6566 gboolean print_all = (num_wanted_types == 1
6567 && !g_strcmp0 (wanted_types[0], "all"));
6570 gst_validate_printf (NULL, "# GstValidate action types");
6572 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
6573 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
6574 gboolean print = print_all;
6576 if (num_wanted_types) {
6579 for (n = 0; n < num_wanted_types; n++) {
6580 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
6581 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
6592 if (print && num_wanted_types) {
6593 gst_validate_printf (atype, "\n");
6596 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
6600 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
6601 atype->implementer_namespace, atype->name, desc);
6606 if (!print_all && num_wanted_types && num_wanted_types > nfound) {
6614 * gst_validate_scenario_get_actions:
6615 * @scenario: The scenario to retrieve remaining actions for
6617 * Get remaining actions from @scenario.
6619 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
6622 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
6625 gboolean main_context_acquired;
6627 main_context_acquired = g_main_context_acquire (g_main_context_default ());
6628 g_return_val_if_fail (main_context_acquired, NULL);
6630 ret = g_list_copy_deep (scenario->priv->actions,
6631 (GCopyFunc) gst_validate_action_ref, NULL);
6633 g_main_context_release (g_main_context_default ());
6639 * gst_validate_scenario_get_target_state:
6640 * @scenario: The scenario to retrieve the current target state for
6642 * Get current target state from @scenario.
6644 * Returns: Current target state.
6647 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
6649 return scenario->priv->target_state;
6653 init_scenarios (void)
6657 register_action_types ();
6659 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
6660 const gchar *action_typename;
6661 GstStructure *plug_conf = (GstStructure *) tmp->data;
6663 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
6664 GstValidateAction *action;
6665 GstValidateActionType *atype = _find_action_type (action_typename);
6668 gst_validate_error_structure (plug_conf,
6669 "[CONFIG ERROR] Action type %s not found", action_typename);
6675 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
6676 GST_INFO ("Action type %s from configuration files"
6677 " is handled.", action_typename);
6681 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
6682 !(_action_type_has_parameter (atype, "as-config"))) {
6683 gst_validate_error_structure (plug_conf,
6684 "[CONFIG ERROR] Action '%s' is not a config action",
6690 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
6691 gst_structure_set_name (plug_conf, action_typename);
6693 action = gst_validate_action_new (NULL, atype, plug_conf, FALSE);
6694 gst_validate_action_unref (action);
6700 register_action_types (void)
6702 GstValidateActionType *type;
6703 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
6704 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
6706 _gst_validate_action_type = gst_validate_action_get_type ();
6707 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
6710 REGISTER_ACTION_TYPE ("meta", NULL,
6711 ((GstValidateActionParameter []) {
6714 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
6717 .possible_variables = NULL,
6718 .def = "'Nothing'"},
6720 .name = "is-config",
6721 .description = "Whether the scenario is a config only scenario",
6724 .possible_variables = NULL,
6728 .name = "handles-states",
6729 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
6730 "in that case the application should not set the state of the pipeline to anything\n"
6731 "and the scenario action will be executed from the beginning",
6734 .possible_variables = NULL,
6738 .description = "Whether the scenario executes seek actions or not",
6741 .possible_variables = NULL,
6745 .name = "reverse-playback",
6746 .description = "Whether the scenario plays the stream backward",
6749 .possible_variables = NULL,
6753 .name = "need-clock-sync",
6754 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
6755 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
6758 .possible_variables = NULL,
6759 .def = "true if some action requires a playback-time false otherwise"
6762 .name = "min-media-duration",
6763 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
6767 .possible_variables = NULL,
6771 .name = "min-audio-track",
6772 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
6773 "for the scenario to be usable",
6776 .possible_variables = NULL,
6780 .name = "min-video-track",
6781 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
6782 "for the scenario to be usable",
6785 .possible_variables = NULL,
6790 .description = "Lets the user know the time the scenario needs to be fully executed",
6792 .types = "double, int",
6793 .possible_variables = NULL,
6794 .def = "infinite (GST_CLOCK_TIME_NONE)"
6797 .name = "pipeline-name",
6798 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
6799 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
6802 .possible_variables = NULL,
6806 .name = "max-latency",
6807 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
6808 "It can be overridden using core configuration, like for example by defining the "
6809 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
6811 .types = "double, int",
6812 .possible_variables = NULL,
6813 .def = "infinite (GST_CLOCK_TIME_NONE)"
6816 .name = "max-dropped",
6817 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
6818 "It can be overridden using core configuration, like for example by defining the "
6819 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
6822 .possible_variables = NULL,
6823 .def = "infinite (-1)"
6826 .name = "ignore-eos",
6827 .description = "Ignore EOS and keep executing the scenario when it happens.\n By default "
6828 "a 'stop' action is generated one EOS",
6831 .possible_variables = NULL,
6835 .name = "allow-errors",
6836 .description = "Ignore error messages and keep executing the\n"
6837 "scenario when it happens. By default a 'stop' action is generated on ERROR messages",
6840 .possible_variables = NULL,
6845 "Scenario metadata.\nNOTE: it used to be called \"description\"",
6846 GST_VALIDATE_ACTION_TYPE_CONFIG);
6848 REGISTER_ACTION_TYPE ("seek", _execute_seek,
6849 ((GstValidateActionParameter []) {
6852 .description = "The starting value of the seek",
6854 .types = "double or string (GstClockTime)",
6855 .possible_variables =
6856 "`position`: The current position in the stream\n"
6857 "`duration`: The duration of the stream",
6862 .description = "The GstSeekFlags to use",
6864 .types = "string describing the GstSeekFlags to set",
6869 .description = "The rate value of the seek",
6872 .possible_variables = NULL,
6876 .name = "start_type",
6877 .description = "The GstSeekType to use for the start of the seek, in:\n"
6878 " [none, set, end]",
6881 .possible_variables = NULL,
6885 .name = "stop_type",
6886 .description = "The GstSeekType to use for the stop of the seek, in:\n"
6887 " [none, set, end]",
6890 .possible_variables = NULL,
6895 .description = "The stop value of the seek",
6897 .types = "double or string (GstClockTime)",
6898 .possible_variables =
6899 "`position`: The current position in the stream\n"
6900 "`duration`: The duration of the stream",
6901 .def ="GST_CLOCK_TIME_NONE",
6905 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
6906 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
6907 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
6908 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
6911 REGISTER_ACTION_TYPE ("pause", _execute_pause,
6912 ((GstValidateActionParameter []) {
6915 .description = "The duration during which the stream will be paused",
6917 .types = "double or string (GstClockTime)",
6918 .possible_variables = NULL,
6923 "Sets pipeline to PAUSED. You can add a 'duration'\n"
6924 "parameter so the pipeline goes back to playing after that duration\n"
6926 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK | GST_VALIDATE_ACTION_TYPE_ASYNC);
6928 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
6929 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
6931 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
6932 "Stops the execution of the scenario. It will post a 'request-state'"
6933 " message on the bus with NULL as a requested state"
6934 " and the application is responsible for stopping itself."
6935 " If you override that action type, make sure to link up.",
6936 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6938 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
6939 "Sends an EOS event to the pipeline",
6940 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6942 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
6943 ((GstValidateActionParameter []) {
6946 .description = "Selects which track type to change (can be 'audio', 'video',"
6950 .possible_variables = NULL,
6955 .description = "Selects which track of this type to use: it can be either a number,\n"
6956 "which will be the Nth track of the given type, or a number with a '+' or\n"
6957 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
6958 "'-1' means 'previous track')",
6960 .types = "string: to switch track relatively\n"
6961 "int: To use the actual index to use",
6962 .possible_variables = NULL,
6967 "The 'switch-track' command can be used to switch tracks."
6968 , GST_VALIDATE_ACTION_TYPE_NONE);
6970 REGISTER_ACTION_TYPE ("wait", _execute_wait,
6971 ((GstValidateActionParameter []) {
6974 .description = "the duration while no other action will be executed",
6976 .types = "double or string (GstClockTime)",
6979 .name = "target-element-name",
6980 .description = "The name of the GstElement to wait @signal-name on.",
6985 .name = "target-element-factory-name",
6986 .description = "The name factory for which to wait @signal-name on",
6992 .name = "signal-name",
6993 .description = "The name of the signal to wait for on @target-element-name."
6994 " To ensure that the signal is executed without blocking while waiting for it"
6995 " you can set the field 'non-blocking=true'.",
7001 .name = "non-blocking",
7002 .description = "**Only for signals**."
7003 "Make the action non blocking meaning that next actions will be\n"
7004 "executed without waiting for the signal to be emitted.",
7010 .name = "message-type",
7011 .description = "The name of the message type to wait for (on @target-element-name"
7018 .name = "expected-values",
7019 .description = "Expected values in the message structure (valid only when "
7020 "`message-type`). Example: "
7021 "wait, on-client=true, message-type=buffering, expected-values=[values, buffer-percent=100]",
7023 .types = "structure",
7028 .description = "Wait until the test clock gets a new pending entry.\n"
7029 "See #gst_test_clock_wait_for_next_pending_id.",
7036 .description = "The check action to execute when non blocking signal is received",
7038 .types = "structure",
7043 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
7044 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
7046 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
7047 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
7048 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
7049 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
7050 GST_VALIDATE_ACTION_TYPE_NONE);
7052 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank_or_disable_feature,
7053 ((GstValidateActionParameter []) {
7056 .description = "The name of a GstFeature or GstPlugin",
7062 .description = "The GstRank to set on @name",
7064 .types = "string, int",
7068 "Changes the ranking of a particular plugin feature(s)",
7069 GST_VALIDATE_ACTION_TYPE_CONFIG);
7071 REGISTER_ACTION_TYPE ("remove-feature", _execute_set_rank_or_disable_feature,
7072 ((GstValidateActionParameter []) {
7075 .description = "The name of a GstFeature or GstPlugin to remove",
7082 "Remove a plugin feature(s) or a plugin from the registry",
7083 GST_VALIDATE_ACTION_TYPE_CONFIG);
7085 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank_or_disable_feature,
7086 ((GstValidateActionParameter []) {
7088 .name = "feature-name",
7089 .description = "The name of a GstFeature",
7095 .description = "The GstRank to set on @feature-name",
7097 .types = "string, int",
7101 "Changes the ranking of a particular plugin feature",
7102 GST_VALIDATE_ACTION_TYPE_CONFIG);
7104 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
7105 ((GstValidateActionParameter []) {
7108 .description = "A GstState as a string, should be in: \n"
7109 " * ['null', 'ready', 'paused', 'playing']",
7115 "Changes the state of the pipeline to any GstState",
7116 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
7118 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
7119 ((GstValidateActionParameter []) {
7122 "Define vars to be used in other actions.\n"
7123 "For example you can define vars for buffer checksum"
7124 " to be used in the \"check-last-sample\" action type as follow:\n\n"
7126 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
7127 " check-last-sample, checksum=frame1\n"
7129 GST_VALIDATE_ACTION_TYPE_NONE);
7131 GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
7132 GST_TYPE_TRIGGER_CONTROL_SOURCE;
7133 REGISTER_ACTION_TYPE ("set-timed-value-properties", _set_timed_value_property,
7134 ((GstValidateActionParameter []) {
7136 .name = "binding-type",
7137 .description = "The name of the type of binding to use",
7143 .name = "source-type",
7144 .description = "The name of the type of ControlSource to use",
7147 .def = "GstInterpolationControlSource",
7150 .name = "interpolation-mode",
7151 .description = "The name of the GstInterpolationMode to set on the source",
7157 .name = "timestamp",
7158 .description = "The timestamp of the keyframe",
7159 .types = "string or float (GstClockTime)",
7164 "Sets GstTimedValue on pads on elements properties using GstControlBindings\n"
7165 "and GstControlSource as defined in the parameters.\n"
7166 "The properties values to set will be defined as:\n\n"
7168 "element-name.padname::property-name=new-value\n"
7170 "> NOTE: `.padname` is not needed if setting a property on an element\n\n"
7171 "This action also adds necessary control source/control bindings.\n",
7172 GST_VALIDATE_ACTION_TYPE_NONE);
7174 REGISTER_ACTION_TYPE ("check-properties", _execute_set_or_check_properties,
7175 ((GstValidateActionParameter []) {
7178 "Check elements and pads properties values.\n"
7179 "The properties values to check will be defined as:\n\n"
7181 "element-name.padname::property-name\n"
7183 "> NOTE: `.padname` is not needed if checking an element property\n\n",
7184 GST_VALIDATE_ACTION_TYPE_CHECK);
7186 REGISTER_ACTION_TYPE ("set-properties", _execute_set_or_check_properties,
7187 ((GstValidateActionParameter []) {
7190 "Set elements and pads properties values.\n"
7191 "The properties values to set will be defined as:\n\n"
7193 " element-name.padname::property-name\n"
7195 "> NOTE: `.padname` is not needed if set an element property\n\n",
7196 GST_VALIDATE_ACTION_TYPE_NONE);
7198 REGISTER_ACTION_TYPE ("set-property", _execute_set_or_check_property,
7199 ((GstValidateActionParameter []) {
7201 .name = "target-element-name",
7202 .description = "The name of the GstElement to set a property on",
7208 .name = "target-element-factory-name",
7209 .description = "The name factory for which to set a property on built elements",
7215 .name = "target-element-klass",
7216 .description = "The klass of the GstElements to set a property on",
7222 .name = "property-name",
7223 .description = "The name of the property to set on @target-element-name",
7229 .name = "property-value",
7230 .description = "The value of @property-name to be set on the element",
7232 .types = "The same type of @property-name",
7236 .name = "on-all-instances",
7237 .description = "Whether to set property on all instances matching "
7245 "Sets a property of an element or klass of elements in the pipeline.\n"
7246 "Besides property-name and value, either 'target-element-name' or\n"
7247 "'target-element-klass' needs to be defined",
7248 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
7249 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
7250 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
7251 type->prepare = gst_validate_set_property_prepare_func;
7253 REGISTER_ACTION_TYPE("check-property", _execute_set_or_check_property,
7254 ((GstValidateActionParameter[]) {
7255 { .name = "target-element-name",
7256 .description = "The name of the GstElement to check a property value",
7260 { .name = "target-element-factory-name",
7261 .description = "The name factory for which to check a property value on built elements",
7265 { .name = "target-element-klass",
7266 .description = "The klass of the GstElements to check a property on",
7270 { .name = "property-name",
7271 .description = "The name of the property to set on @target-element-name",
7275 { .name = "property-value",
7276 .description = "The expected value of @property-name",
7278 .types = "The same type of @property-name",
7281 "Check the value of property of an element or klass of elements in the pipeline.\n"
7282 "Besides property-name and value, either 'target-element-name' or\n"
7283 "'target-element-klass' needs to be defined",
7284 GST_VALIDATE_ACTION_TYPE_CHECK);
7286 REGISTER_ACTION_TYPE ("set-debug-threshold",
7287 _execute_set_debug_threshold,
7288 ((GstValidateActionParameter [])
7291 .name = "debug-threshold",
7292 .description = "String defining debug threshold\n"
7293 "See gst_debug_set_threshold_from_string",
7298 "Sets the debug level to be used, same format as\n"
7299 "setting the GST_DEBUG env variable",
7300 GST_VALIDATE_ACTION_TYPE_NONE);
7302 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
7303 ((GstValidateActionParameter [])
7306 .name = "target-element-name",
7307 .description = "The name of the GstElement to emit a signal on",
7312 .name = "signal-name",
7313 .description = "The name of the signal to emit on @target-element-name",
7320 .description = "The signal parameters",
7322 .types = "ValueArray",
7327 "Emits a signal to an element in the pipeline",
7328 GST_VALIDATE_ACTION_TYPE_NONE);
7330 REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
7331 ((GstValidateActionParameter [])
7334 .name = "target-element-name",
7335 .description = "The name of the appsrc to push data on",
7340 .name = "file-name",
7341 .description = "Relative path to a file whose contents will be pushed as a buffer",
7347 .description = "Offset within the file where the buffer will start",
7353 .description = "Number of bytes from the file that will be pushed as a buffer",
7359 .description = "Caps for the buffer to be pushed",
7365 .description = "Buffer PTS",
7367 .types = "GstClockTime"
7371 .description = "Buffer DTS",
7373 .types = "GstClockTime"
7377 .description = "Buffer duration",
7379 .types = "GstClockTime"
7383 .description = "The GstSegment to configure as part of the sample",
7385 .types = "(GstStructure)segment,"
7386 "[start=(GstClockTime)]"
7387 "[stop=(GstClockTime)]"
7388 "[base=(GstClockTime)]"
7389 "[offset=(GstClockTime)]"
7390 "[time=(GstClockTime)]"
7391 "[postion=(GstClockTime)]"
7392 "[duration=(GstClockTime)]"
7396 "Queues a sample in an appsrc. If the pipeline state allows flow of buffers, "
7397 " the next action is not run until the buffer has been pushed.",
7398 GST_VALIDATE_ACTION_TYPE_NONE);
7400 REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
7401 ((GstValidateActionParameter [])
7404 .name = "target-element-name",
7405 .description = "The name of the appsrc to emit EOS on",
7411 "Queues a EOS event in an appsrc.",
7412 GST_VALIDATE_ACTION_TYPE_NONE);
7414 REGISTER_ACTION_TYPE ("flush", _execute_flush,
7415 ((GstValidateActionParameter [])
7418 .name = "target-element-name",
7419 .description = "The name of the appsrc to flush on",
7424 .name = "reset-time",
7425 .description = "Whether the flush should reset running time",
7432 "Sends FLUSH_START and FLUSH_STOP events.",
7433 GST_VALIDATE_ACTION_TYPE_NONE);
7435 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
7436 ((GstValidateActionParameter [])
7439 .name = "plugin-name",
7440 .description = "The name of the GstPlugin to disable",
7445 .name = "as-config",
7446 .description = "Execute action as a config action (meaning when loading the scenario)",
7453 "Disables a GstPlugin",
7454 GST_VALIDATE_ACTION_TYPE_NONE);
7456 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
7457 ((GstValidateActionParameter []) {
7459 .name = "sink-name",
7460 .description = "The name of the sink element to check sample on.",
7466 .name = "sink-factory-name",
7467 .description = "The name of the factory of the sink element to check sample on.",
7473 .name = "sinkpad-caps",
7474 .description = "The caps (as string) of the sink to check.",
7481 .description = "The reference checksum of the buffer.",
7487 .name = "timecode-frame-number",
7488 .description = "The frame number of the buffer as specified on its"
7489 " GstVideoTimeCodeMeta",
7496 "Checks the last-sample checksum or frame number (set on its "
7497 " GstVideoTimeCodeMeta) on declared Sink element."
7498 " This allows checking the checksum of a buffer after a 'seek' or after a"
7499 " GESTimeline 'commit'"
7501 GST_VALIDATE_ACTION_TYPE_NON_BLOCKING | GST_VALIDATE_ACTION_TYPE_CHECK);
7503 REGISTER_ACTION_TYPE ("crank-clock", _execute_crank_clock,
7504 ((GstValidateActionParameter []) {
7506 .name = "expected-time",
7507 .description = "Expected clock time after cranking",
7509 .types = "GstClockTime",
7513 .name = "expected-elapsed-time",
7514 .description = "Check time elapsed during the clock cranking",
7516 .types = "GstClockTime",
7520 }), "Crank the clock, possibly checking how much time was supposed to be waited on the clock"
7521 " and/or the clock running time after the crank."
7522 " Using one `crank-clock` action in a scenario implies that the scenario is driving the "
7523 " clock and a #GstTestClock will be used. The user will need to crank it the number of "
7524 " time required (using the `repeat` parameter comes handy here).",
7525 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
7527 REGISTER_ACTION_TYPE ("video-request-key-unit", _execute_request_key_unit,
7528 ((GstValidateActionParameter []) {
7530 .name = "direction",
7531 .description = "The direction for the event to travel, should be in\n"
7532 " * [upstream, downstream]",
7538 .name = "running-time",
7539 .description = "The running_time can be set to request a new key unit at a specific running_time.\n"
7540 "If not set, GST_CLOCK_TIME_NONE will be used so upstream elements will produce a new key unit "
7541 "as soon as possible.",
7543 .types = "double or string",
7544 .possible_variables = "position: The current position in the stream\n"
7545 "duration: The duration of the stream",
7549 .name = "all-headers",
7550 .description = "TRUE to produce headers when starting a new key unit",
7558 .description = "integer that can be used to number key units",
7565 .name = "target-element-name",
7566 .description = "The name of the GstElement to send a send force-key-unit to",
7572 .name = "target-element-factory-name",
7573 .description = "The factory name of the GstElements to send a send force-key-unit to",
7579 .name = "target-element-klass",
7580 .description = "The klass of the GstElements to send a send force-key-unit to",
7582 .def = "Video/Encoder",
7588 .description = "The name of the GstPad to send a send force-key-unit to",
7596 .description = "The name of the GstPad to send a send force-key-unit to",
7604 "Request a video key unit", FALSE);
7606 REGISTER_ACTION_TYPE("check-position", _execute_check_position,
7607 ((GstValidateActionParameter[]) {
7608 { .name = "expected-position",
7609 .description = "The expected pipeline position",
7611 .types = "GstClockTime",
7615 "Check current pipeline position.\n",
7616 /* FIXME: Make MT safe so it can be marked as GST_VALIDATE_ACTION_TYPE_CHECK */
7617 GST_VALIDATE_ACTION_TYPE_NONE);
7619 REGISTER_ACTION_TYPE("check-current-pad-caps", _execute_check_pad_caps,
7620 ((GstValidateActionParameter[]) {
7622 .name = "expected-caps",
7623 .description = "The expected caps. If not present, expected no caps to be set",
7625 .types = "caps,structure",
7629 .name = "target-element-name",
7630 .description = "The name of the GstElement to send a send force-key-unit to",
7636 .name = "target-element-factory-name",
7637 .description = "The factory name of the GstElements to get pad from",
7643 .name = "target-element-klass",
7644 .description = "The klass of the GstElements to get pad from",
7651 .description = "The name of the GstPad to get pad from",
7657 .name = "comparison-type",
7660 .types = "string in [intersect, equal]",
7665 "Check currently set caps on a particular pad.\n",
7666 GST_VALIDATE_ACTION_TYPE_NONE | GST_VALIDATE_ACTION_TYPE_CHECK );
7668 REGISTER_ACTION_TYPE("run-command", _run_command,
7669 ((GstValidateActionParameter[]) {
7672 .description = "The subprocess arguments, include the program name itself",
7674 .types = "(string){array,}",
7679 .description = "Extra environment variables to set",
7681 .types = "structure",
7686 "Run an external command.\n",
7687 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL);
7689 REGISTER_ACTION_TYPE("foreach", NULL,
7690 ((GstValidateActionParameter[]) {
7691 { .name = "actions",
7692 .description = "The array of actions to repeat",
7694 .types = "{array of [structures]}",
7697 "Run actions defined in the `actions` array the number of times specified\n"
7698 "with an iterator parameter passed in. The iterator can be\n"
7699 "a range like: `i=[start, end, step]` or array of values\n"
7700 "such as: `values=<value1, value2>`.\n"
7701 "One and only one iterator field is supported as parameter.",
7702 GST_VALIDATE_ACTION_TYPE_NONE);
7703 type->prepare = gst_validate_foreach_prepare;
7705 /* Internal actions types to test the validate scenario implementation */
7706 REGISTER_ACTION_TYPE("priv_check-action-type-calls",
7707 _execute_check_action_type_calls, NULL, NULL, 0);
7708 REGISTER_ACTION_TYPE("priv_check-subaction-level",
7709 _execute_check_subaction_level, NULL, NULL, 0);
7714 gst_validate_scenario_deinit (void)
7716 _free_action_types (action_types);
7717 action_types = NULL;