3 * Copyright (C) 2013 Collabora Ltd.
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
5 * Copyright (C) 2018-2020 Igalia S.L
8 * gst-validate-scenario.c - Validate Scenario class
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
26 * SECTION:gst-validate-scenario
27 * @title: GstValidateScenario
28 * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
30 * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
31 * It is basically an ordered list of #GstValidateAction that will be executed during the
32 * execution of the pipeline.
34 * Possible configurations (see [GST_VALIDATE_CONFIG](gst-validate-environment-variables.md)):
35 * * scenario-action-execution-interval: Sets the interval in
36 * milliseconds (1/1000ths of a second), between which actions
37 * will be executed, setting it to 0 means "execute in idle".
38 * The default value is 10ms.
51 #include <gst/check/gsttestclock.h>
52 #include "gst-validate-internal.h"
53 #include "gst-validate-scenario.h"
54 #include "gst-validate-reporter.h"
55 #include "gst-validate-report.h"
56 #include "gst-validate-utils.h"
57 #include "gst-validate-internal.h"
59 #include <gst/controller/controller.h>
60 #include <gst/validate/gst-validate-override.h>
61 #include <gst/validate/gst-validate-override-registry.h>
62 #include <gst/validate/gst-validate-pipeline-monitor.h>
64 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
66 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval
67 TODO make it overridable */
69 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
70 #undef GST_CAT_DEFAULT
71 #define GST_CAT_DEFAULT gst_validate_scenario_debug
73 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
74 type = gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
77 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
79 #define SCENARIO_LOCK(scenario) G_STMT_START { \
80 GST_LOG_OBJECT (scenario, "About to lock %p", &scenario->priv->lock); \
81 g_mutex_lock(&scenario->priv->lock); \
82 GST_LOG_OBJECT (scenario, "Acquired lock %p", &scenario->priv->lock); \
85 #define SCENARIO_UNLOCK(scenario) G_STMT_START { \
86 GST_LOG_OBJECT (scenario, "About to unlock %p", &scenario->priv->lock); \
87 g_mutex_unlock(&scenario->priv->lock); \
88 GST_LOG_OBJECT (scenario, "unlocked %p", &scenario->priv->lock); \
91 #define DECLARE_AND_GET_PIPELINE(s,a) \
92 GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
93 if (pipeline == NULL) { \
94 GST_VALIDATE_REPORT_ACTION (s, a, SCENARIO_ACTION_EXECUTION_ERROR, \
95 "Can't execute a '%s' action after the pipeline " \
96 "has been destroyed.", a->type); \
97 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
100 #ifdef G_HAVE_ISO_VARARGS
101 #define REPORT_UNLESS(condition, errpoint, ...) \
103 if (!(condition)) { \
104 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
105 gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \
106 SCENARIO_ACTION_EXECUTION_ERROR, \
112 #elif defined(G_HAVE_GNUC_VARARGS)
113 #define REPORT_UNLESS(condition, errpoint, args...) \
115 if (!(condition)) { \
116 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
117 gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \
118 SCENARIO_ACTION_EXECUTION_ERROR, ##args); \
130 PROP_EXECUTE_ON_IDLE,
141 static guint scenario_signals[LAST_SIGNAL] = { 0 };
143 static GList *action_types = NULL;
144 static void gst_validate_scenario_dispose (GObject * object);
145 static void gst_validate_scenario_finalize (GObject * object);
146 static GstValidateActionType *_find_action_type (const gchar * type_name);
147 static GstValidateExecuteActionReturn
148 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
149 GstStructure * structure, gboolean add_to_lists);
150 static gboolean _action_set_done (GstValidateAction * action);
151 static GList *_find_elements_defined_in_action (GstValidateScenario * scenario,
152 GstValidateAction * action);
153 static GstValidateAction *gst_validate_create_subaction (GstValidateScenario *
154 scenario, GstStructure * lvariables, GstValidateAction * action,
155 GstStructure * nstruct, gint it, gint max);
157 /* GstValidateSinkInformation tracks information for all sinks in the pipeline */
160 GstElement *sink; /* The sink element tracked */
161 guint32 segment_seqnum; /* The latest segment seqnum. GST_SEQNUM_INVALID if none */
162 GstSegment segment; /* The latest segment */
163 } GstValidateSinkInformation;
165 /* GstValidateSeekInformation tracks:
166 * * The values used in the seek
167 * * The seqnum used in the seek event
168 * * The validate action to which it relates
172 guint32 seqnum; /* seqnum of the seek event */
178 GstSeekType start_type, stop_type;
181 /* The action corresponding to this seek */
182 GstValidateAction *action;
183 } GstValidateSeekInformation;
185 /* GstValidateScenario is not really thread safe and
186 * everything should be done from the thread GstValidate
187 * was inited from, unless stated otherwise.
189 struct _GstValidateScenarioPrivate
192 GstValidateRunner *runner;
193 gboolean execute_on_idle;
198 GList *non_blocking_running_actions; /* MT safe. Protected with SCENARIO_LOCK */
199 GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */
201 gboolean needs_playback_parsing;
203 GList *sinks; /* List of GstValidateSinkInformation */
204 GList *seeks; /* List of GstValidateSeekInformation */
206 /* Seek currently applied (set when all sinks received segment with
207 * an identical seqnum and there is a matching pending seek).
208 * do not free, should always be present in the seek list above */
209 GstValidateSeekInformation *current_seek;
210 /* Current unified seqnum. Set when all sinks received segment with
211 * an identical seqnum, even if there wasn't a matching pending seek
213 guint32 current_seqnum;
215 /* List of action that need parsing when reaching ASYNC_DONE
216 * most probably to be able to query duration */
219 * * Only set for seek actions, and only if seek succeeded
220 * * Only Used in _check_position()
221 * FIXME : Just use the seek information */
222 GstSeekFlags seek_flags;
223 GstFormat seek_format;
225 /* segment_start/segment_stop :
226 * * Set : from seek values
227 * * Read : In _check_position()
228 * FIXME : Just use the current seek information */
229 GstClockTime segment_start;
230 GstClockTime segment_stop;
232 /* Always initialized to a default value
233 * FIXME : Is it still needed with the new seeking validation system ? */
234 GstClockTime seek_pos_tol;
236 /* If we seeked in paused the position should be exactly what
237 * the seek value was (if accurate) */
238 gboolean seeked_in_pause;
242 gboolean handles_state;
244 guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */
246 guint signal_handler_id; /* MT safe. Protect with SCENARIO_LOCK */
247 guint action_execution_interval;
249 /* Name of message the wait action is waiting for */
250 GstValidateAction *wait_message_action;
255 gboolean changing_state;
256 gboolean needs_async_done;
258 gboolean allow_errors;
259 GstState target_state;
263 gchar *pipeline_name;
264 GstClockTime max_latency;
268 /* 'switch-track action' currently waiting for
269 * GST_MESSAGE_STREAMS_SELECTED to be completed. */
270 GstValidateAction *pending_switch_track;
274 GWeakRef ref_pipeline;
277 guint segments_needed;
279 GMainContext *context;
282 typedef struct KeyFileGroupName
288 #define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1
291 gst_validate_sink_information_free (GstValidateSinkInformation * info)
293 gst_object_unref (info->sink);
298 gst_validate_seek_information_free (GstValidateSeekInformation * info)
300 gst_validate_action_unref (info->action);
304 static GstValidateInterceptionReturn
305 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
306 GstValidateReport * report)
310 for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
312 GstValidateOverride *override = (GstValidateOverride *) tmp->data;
314 gst_validate_override_get_severity (override,
315 gst_validate_issue_get_id (report->issue), report->level);
318 return GST_VALIDATE_REPORTER_REPORT;
322 * gst_validate_scenario_get_pipeline:
323 * @scenario: The scenario to retrieve a pipeline from
325 * Returns: (transfer full): The #GstPipeline the scenario is running
329 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
331 return g_weak_ref_get (&scenario->priv->ref_pipeline);
335 _get_pipeline (GstValidateReporter * reporter)
338 GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
339 (GST_VALIDATE_SCENARIO (reporter)));
343 _reporter_iface_init (GstValidateReporterInterface * iface)
345 iface->intercept_report = gst_validate_scenario_intercept_report;
346 iface->get_pipeline = _get_pipeline;
349 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
350 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
351 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init));
353 /* GstValidateAction implementation */
354 static GType _gst_validate_action_type = 0;
356 struct _GstValidateActionPrivate
358 GstStructure *main_structure;
359 GstValidateExecuteActionReturn state; /* Actually ActionState */
361 gboolean executing_last_subaction;
362 gboolean subaction_level;
365 GstClockTime execution_time;
366 GstClockTime execution_duration;
367 GstClockTime timeout;
370 gboolean needs_playback_parsing;
371 gboolean pending_set_done;
373 GMainContext *context;
377 gst_validate_action_serialize (GstValidateAction * action)
379 JsonNode *node = json_node_alloc ();
380 JsonObject *jreport = json_object_new ();
381 gchar *action_args = gst_structure_to_string (action->structure);
383 json_object_set_string_member (jreport, "type", "action");
384 json_object_set_string_member (jreport, "action-type", action->type);
385 json_object_set_int_member (jreport, "playback-time",
386 (gint64) action->playback_time);
387 json_object_set_string_member (jreport, "args", action_args);
388 g_free (action_args);
390 node = json_node_init_object (node, jreport);
391 json_object_unref (jreport);
397 gst_validate_action_get_type (void)
399 if (_gst_validate_action_type == 0) {
400 _gst_validate_action_type =
401 g_boxed_type_register_static (g_intern_static_string
402 ("GstValidateAction"), (GBoxedCopyFunc) gst_validate_action_ref,
403 (GBoxedFreeFunc) gst_validate_action_unref);
405 json_boxed_register_serialize_func (_gst_validate_action_type,
407 (JsonBoxedSerializeFunc) gst_validate_action_serialize);
410 return _gst_validate_action_type;
413 static gboolean execute_next_action (GstValidateScenario * scenario);
415 gst_validate_scenario_load (GstValidateScenario * scenario,
416 const gchar * scenario_name);
418 static GstValidateAction *
419 _action_copy (GstValidateAction * act)
421 GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
422 GstValidateAction *copy = gst_validate_action_new (scenario,
423 _find_action_type (act->type), NULL, FALSE);
425 gst_object_unref (scenario);
427 if (act->structure) {
428 copy->structure = gst_structure_copy (act->structure);
429 copy->type = gst_structure_get_name (copy->structure);
430 if (!(act->name = gst_structure_get_string (copy->structure, "name")))
434 if (act->priv->main_structure)
435 copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
437 copy->action_number = act->action_number;
438 copy->playback_time = act->playback_time;
439 copy->priv->timeout = act->priv->timeout;
440 GST_VALIDATE_ACTION_LINENO (copy) = GST_VALIDATE_ACTION_LINENO (act);
441 GST_VALIDATE_ACTION_FILENAME (copy) =
442 g_strdup (GST_VALIDATE_ACTION_FILENAME (act));
443 GST_VALIDATE_ACTION_DEBUG (copy) = g_strdup (GST_VALIDATE_ACTION_DEBUG (act));
444 GST_VALIDATE_ACTION_N_REPEATS (copy) = GST_VALIDATE_ACTION_N_REPEATS (act);
445 GST_VALIDATE_ACTION_RANGE_NAME (copy) = GST_VALIDATE_ACTION_RANGE_NAME (act);
451 gst_validate_action_return_get_name (GstValidateActionReturn r)
454 case GST_VALIDATE_EXECUTE_ACTION_ERROR:
456 case GST_VALIDATE_EXECUTE_ACTION_OK:
458 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
460 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
461 return "NON-BLOCKING";
462 case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
463 return "ERROR(reported)";
464 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
465 return "IN_PROGRESS";
466 case GST_VALIDATE_EXECUTE_ACTION_NONE:
468 case GST_VALIDATE_EXECUTE_ACTION_DONE:
471 g_assert_not_reached ();
476 _action_free (GstValidateAction * action)
478 if (action->structure)
479 gst_structure_free (action->structure);
481 if (action->priv->main_structure)
482 gst_structure_free (action->priv->main_structure);
484 g_weak_ref_clear (&action->priv->scenario);
485 g_free (GST_VALIDATE_ACTION_FILENAME (action));
486 g_free (GST_VALIDATE_ACTION_DEBUG (action));
488 g_slice_free (GstValidateActionPrivate, action->priv);
489 g_slice_free (GstValidateAction, action);
493 gst_validate_action_init (GstValidateAction * action)
495 gst_mini_object_init (((GstMiniObject *) action), 0,
496 _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
497 (GstMiniObjectFreeFunction) _action_free);
499 action->priv = g_slice_new0 (GstValidateActionPrivate);
501 g_weak_ref_init (&action->priv->scenario, NULL);
505 gst_validate_action_ref (GstValidateAction * action)
507 return (GstValidateAction *) gst_mini_object_ref (GST_MINI_OBJECT (action));
511 gst_validate_action_unref (GstValidateAction * action)
513 gst_mini_object_unref (GST_MINI_OBJECT (action));
517 * gst_validate_action_new:
518 * @scenario: (allow-none): The scenario executing the action
519 * @action_type: The action type
520 * @structure: The structure containing the action arguments
521 * @add_to_lists: Weather the action should be added to the scenario action list
523 * Returns: A newly created #GstValidateAction
526 gst_validate_action_new (GstValidateScenario * scenario,
527 GstValidateActionType * action_type, GstStructure * structure,
528 gboolean add_to_lists)
530 GstValidateAction *action = g_slice_new0 (GstValidateAction);
532 g_assert (action_type);
534 gst_validate_action_init (action);
535 action->playback_time = GST_CLOCK_TIME_NONE;
536 action->priv->timeout = GST_CLOCK_TIME_NONE;
537 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_NONE;
538 action->type = action_type->name;
541 g_weak_ref_set (&action->priv->scenario, scenario);
543 gchar *filename = NULL;
544 gst_structure_get (structure,
545 "__lineno__", G_TYPE_INT, &GST_VALIDATE_ACTION_LINENO (action),
546 "__filename__", G_TYPE_STRING, &filename,
547 "__debug__", G_TYPE_STRING, &GST_VALIDATE_ACTION_DEBUG (action), NULL);
549 GST_VALIDATE_ACTION_FILENAME (action) =
550 g_filename_display_basename (filename);
553 gst_structure_remove_fields (structure, "__lineno__", "__filename__",
555 action->priv->state =
556 _fill_action (scenario, action, structure, add_to_lists);
563 _action_check_and_set_printed (GstValidateAction * action)
565 if (action->priv->printed == FALSE) {
566 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
569 action->priv->printed = TRUE;
578 gst_validate_action_get_level (GstValidateAction * action)
580 return action->priv->subaction_level;
583 /* GstValidateActionType implementation */
584 GType _gst_validate_action_type_type;
585 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
586 static GstValidateActionType *gst_validate_action_type_new (void);
588 struct _GstValidateActionTypePrivate
594 _action_type_free (GstValidateActionType * type)
596 g_free (type->parameters);
597 g_free (type->description);
599 g_free (type->implementer_namespace);
602 if (type->overriden_type)
603 gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
605 g_slice_free (GstValidateActionType, type);
609 gst_validate_action_type_init (GstValidateActionType * type)
611 type->priv = g_new0 (GstValidateActionTypePrivate, 1);
613 gst_mini_object_init ((GstMiniObject *) type, 0,
614 _gst_validate_action_type_type, NULL, NULL,
615 (GstMiniObjectFreeFunction) _action_type_free);
618 GstValidateActionType *
619 gst_validate_action_type_new (void)
621 GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
623 gst_validate_action_type_init (type);
628 static GstValidateActionType *
629 _find_action_type (const gchar * type_name)
633 for (tmp = action_types; tmp; tmp = tmp->next) {
634 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
635 if (g_strcmp0 (atype->name, type_name) == 0)
643 _update_well_known_vars (GstValidateScenario * scenario)
645 gint64 duration, position;
646 gdouble dduration, dposition;
647 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
649 gst_structure_remove_fields (scenario->priv->vars, "position", "duration",
655 if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration) ||
656 !GST_CLOCK_TIME_IS_VALID (duration)) {
657 GstValidateMonitor *monitor =
658 (GstValidateMonitor *) (g_object_get_data ((GObject *)
659 pipeline, "validate-monitor"));
660 GST_INFO_OBJECT (scenario,
661 "Could not query duration. Trying to get duration from media-info");
662 if (monitor && monitor->media_descriptor)
664 gst_validate_media_descriptor_get_duration
665 (monitor->media_descriptor);
668 if (!GST_CLOCK_TIME_IS_VALID (duration))
669 dduration = G_MAXDOUBLE;
671 dduration = ((double) duration / GST_SECOND);
673 gst_structure_set (scenario->priv->vars, "duration", G_TYPE_DOUBLE, dduration,
675 if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
677 if (!GST_CLOCK_TIME_IS_VALID (position))
678 dposition = G_MAXDOUBLE;
680 dposition = ((double) position / GST_SECOND);
682 gst_structure_set (scenario->priv->vars, "position", G_TYPE_DOUBLE,
685 GST_INFO_OBJECT (scenario, "Could not query position");
689 static GstElement *_get_target_element (GstValidateScenario * scenario,
690 GstValidateAction * action);
693 _get_target_object_property (GstValidateScenario * scenario,
694 GstValidateAction * action, const gchar * property_path,
697 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
698 gchar **elem_pad_name = NULL;
699 gchar **object_prop_name = NULL;
700 const gchar *elemname;
701 const gchar *padname = NULL;
702 GstObject *target = NULL;
705 elem_pad_name = g_strsplit (property_path, ".", 2);
707 g_strsplit (elem_pad_name[1] ? elem_pad_name[1] : elem_pad_name[0], "::",
709 REPORT_UNLESS (object_prop_name[1], err,
710 "Property specification %s is missing a `::propename` part",
713 if (elem_pad_name[1]) {
714 elemname = elem_pad_name[0];
715 padname = object_prop_name[0];
717 elemname = object_prop_name[0];
720 gst_structure_set (action->structure, "target-element-name", G_TYPE_STRING,
723 target = (GstObject *) _get_target_element (scenario, action);
724 gst_structure_remove_field (action->structure, "target-element-name");
725 REPORT_UNLESS (target, err, "Target element with given name (%s) not found",
729 gboolean done = FALSE;
730 GstIterator *it = gst_element_iterate_pads (GST_ELEMENT (target));
731 GValue v = G_VALUE_INIT;
733 gst_clear_object (&target);
735 switch (gst_iterator_next (it, &v)) {
736 case GST_ITERATOR_OK:{
737 GstPad *pad = g_value_get_object (&v);
738 gchar *name = gst_object_get_name (GST_OBJECT (pad));
740 if (!g_strcmp0 (name, padname)) {
742 gst_clear_object (&target);
744 target = gst_object_ref (pad);
750 case GST_ITERATOR_RESYNC:
751 gst_iterator_resync (it);
753 case GST_ITERATOR_ERROR:
754 case GST_ITERATOR_DONE:
759 gst_iterator_free (it);
761 REPORT_UNLESS (target, err, "Could not find pad: %s::%s", elemname, padname);
764 const gchar *propname = object_prop_name[i];
767 g_object_class_find_property (G_OBJECT_GET_CLASS (target), propname);
769 REPORT_UNLESS (*pspec, err,
770 "Object %" GST_PTR_FORMAT " doesn't have a property call '%s'", target,
773 if (!object_prop_name[++i])
776 REPORT_UNLESS (g_type_is_a ((*pspec)->owner_type, G_TYPE_OBJECT), err,
777 "Property: %" GST_PTR_FORMAT "::%s not a GObject, can't use it.",
780 g_object_get (target, propname, &target, NULL);
781 REPORT_UNLESS (target, err,
782 "Property: %" GST_PTR_FORMAT "::%s is NULL can't get %s.",
783 target, propname, object_prop_name[i + 1]);
786 REPORT_UNLESS (res == GST_VALIDATE_EXECUTE_ACTION_OK, err, "Something fishy");
789 g_strfreev (elem_pad_name);
790 g_strfreev (object_prop_name);
794 gst_clear_object (&target);
799 _set_variable_func (const gchar * name, double *value, gpointer user_data)
801 GstValidateScenario *scenario = (GstValidateScenario *) user_data;
803 if (!gst_structure_get_double (scenario->priv->vars, name, value))
809 /* Check that @list doesn't contain any non-optional actions */
811 actions_list_is_done (GList * list)
815 for (l = list; l != NULL; l = g_list_next (l)) {
816 GstValidateAction *action = l->data;
818 if (!action->priv->optional)
826 _check_scenario_is_done (GstValidateScenario * scenario)
828 SCENARIO_LOCK (scenario);
829 if (actions_list_is_done (scenario->priv->actions) &&
830 actions_list_is_done (scenario->priv->non_blocking_running_actions) &&
831 actions_list_is_done (scenario->priv->on_addition_actions)) {
832 SCENARIO_UNLOCK (scenario);
834 g_signal_emit (scenario, scenario_signals[DONE], 0);
836 SCENARIO_UNLOCK (scenario);
841 _reset_sink_information (GstValidateSinkInformation * sinkinfo)
843 sinkinfo->segment_seqnum = GST_SEQNUM_INVALID;
844 gst_segment_init (&sinkinfo->segment, GST_FORMAT_UNDEFINED);
848 * gst_validate_action_get_clocktime:
849 * @scenario: The #GstValidateScenario from which to get a time
850 * for a parameter of an action
851 * @action: The action from which to retrieve the time for @name
853 * @name: The name of the parameter for which to retrieve a time
854 * @retval: (out): The return value for the wanted time
856 * Get a time value for the @name parameter of an action. This
857 * method should be called to retrieve and compute a timed value of a given
858 * action. It will first try to retrieve the value as a double,
859 * then get it as a string and execute any formula taking into account
860 * the 'position' and 'duration' variables. And it will always convert that
861 * value to a GstClockTime.
863 * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
866 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
867 GstValidateAction * action, const gchar * name, GstClockTime * retval)
870 if (!gst_structure_has_field (action->structure, name))
873 if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
875 gchar *error = NULL, *strval;
876 const gchar *tmpvalue = gst_structure_get_string (action->structure, name);
879 GST_INFO_OBJECT (scenario, "Could not find %s (%" GST_PTR_FORMAT ")",
880 name, action->structure);
884 _update_well_known_vars (scenario);
886 gst_validate_replace_variables_in_string (action, scenario->priv->vars,
887 tmpvalue, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
892 gst_validate_utils_parse_expression (strval, _set_variable_func,
895 GST_WARNING ("Error while parsing %s: %s (%" GST_PTR_FORMAT ")",
896 strval, error, scenario->priv->vars);
901 } else if (val == -1.0) {
902 *retval = GST_CLOCK_TIME_NONE;
906 gst_util_double_to_fraction (val, &n, &d);
907 *retval = gst_util_uint64_scale_int_round (n, GST_SECOND, d);
909 gst_structure_set (action->structure, name, G_TYPE_UINT64, *retval, NULL);
918 /* WITH SCENARIO LOCK TAKEN */
919 static GstValidateSinkInformation *
920 _find_sink_information (GstValidateScenario * scenario, GstElement * sink)
924 for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
925 GstValidateSinkInformation *sink_info =
926 (GstValidateSinkInformation *) tmp->data;
927 if (sink_info->sink == sink)
933 /* WITH SCENARIO LOCK TAKEN */
934 static GstValidateSeekInformation *
935 _find_seek_information (GstValidateScenario * scenario, guint32 seqnum)
939 for (tmp = scenario->priv->seeks; tmp; tmp = tmp->next) {
940 GstValidateSeekInformation *seek_info =
941 (GstValidateSeekInformation *) tmp->data;
942 if (seek_info->seqnum == seqnum)
949 /* WITH SCENARIO LOCK TAKEN */
951 _validate_sink_information (GstValidateScenario * scenario)
954 gboolean all_sinks_ready = TRUE;
955 gboolean identical_seqnum = TRUE;
956 gboolean transitioning = FALSE;
957 guint32 common_seqnum = GST_SEQNUM_INVALID;
958 guint32 next_seqnum = GST_SEQNUM_INVALID;
959 GstValidateSeekInformation *seek_info;
961 if (scenario->priv->seeks)
962 /* If we have a pending seek, get the expected seqnum to
963 * figure out whether we are transitioning to a seek */
965 ((GstValidateSeekInformation *) scenario->priv->seeks->data)->seqnum;
967 GST_LOG_OBJECT (scenario, "next_seqnum %" G_GUINT32_FORMAT, next_seqnum);
969 for (tmp = scenario->priv->sinks; tmp; tmp = tmp->next) {
970 GstValidateSinkInformation *sink_info =
971 (GstValidateSinkInformation *) tmp->data;
972 GST_DEBUG_OBJECT (sink_info->sink,
973 "seqnum:%" G_GUINT32_FORMAT " segment:%" GST_SEGMENT_FORMAT,
974 sink_info->segment_seqnum, &sink_info->segment);
975 if (sink_info->segment_seqnum == GST_SEQNUM_INVALID)
976 all_sinks_ready = FALSE;
977 else if (sink_info->segment.format == GST_FORMAT_TIME) {
978 /* Are we in the middle of switching segments (from the current
979 * one, or to the next week) ? */
980 if (sink_info->segment_seqnum == scenario->priv->current_seqnum ||
981 sink_info->segment_seqnum == next_seqnum)
982 transitioning = TRUE;
984 /* We are only interested in sinks that handle TIME segments */
985 if (common_seqnum == GST_SEQNUM_INVALID)
986 common_seqnum = sink_info->segment_seqnum;
987 else if (common_seqnum != sink_info->segment_seqnum) {
988 identical_seqnum = FALSE;
993 /* If not all sinks have received a segment, just return */
994 if (!all_sinks_ready)
997 GST_FIXME_OBJECT (scenario,
998 "All sinks have valid segment. identical_seqnum:%d transitioning:%d seqnum:%"
999 G_GUINT32_FORMAT " (current:%" G_GUINT32_FORMAT ") seeks:%p",
1000 identical_seqnum, transitioning, common_seqnum,
1001 scenario->priv->current_seqnum, scenario->priv->seeks);
1003 if (!identical_seqnum) {
1004 /* If all sinks received a segment *and* there is a pending seek *and* there
1005 * wasn't one previously, we definitely have a failure */
1006 if (!transitioning && scenario->priv->current_seek == NULL
1007 && scenario->priv->seeks) {
1008 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_INVALID_SEQNUM,
1009 "Not all segments from a given seek have the same seqnum");
1012 /* Otherwise we're either doing the initial preroll (without seek)
1013 * or we are in the middle of switching to another seek */
1017 /* Now check if we have seek data related to that seqnum */
1018 seek_info = _find_seek_information (scenario, common_seqnum);
1020 if (seek_info && seek_info != scenario->priv->current_seek) {
1021 GST_DEBUG_OBJECT (scenario, "Found a corresponding seek !");
1022 /* Updating values */
1023 /* FIXME : Check segment values if needed ! */
1024 /* FIXME : Non-flushing seek, validate here */
1025 if (seek_info->start_type == GST_SEEK_TYPE_SET)
1026 scenario->priv->segment_start = seek_info->start;
1027 if (seek_info->stop_type == GST_SEEK_TYPE_SET)
1028 scenario->priv->segment_stop = seek_info->stop;
1029 if (scenario->priv->target_state == GST_STATE_PAUSED)
1030 scenario->priv->seeked_in_pause = TRUE;
1031 SCENARIO_UNLOCK (scenario);
1032 /* If it's a non-flushing seek, validate it here
1033 * otherwise we will do it when the async_done is received */
1034 if (!(seek_info->flags & GST_SEEK_FLAG_FLUSH))
1035 gst_validate_action_set_done (seek_info->action);
1036 SCENARIO_LOCK (scenario);
1038 /* We always set the current_seek. Can be NULL if no matching */
1039 scenario->priv->current_seek = seek_info;
1040 scenario->priv->current_seqnum = common_seqnum;
1044 * gst_validate_scenario_execute_seek:
1045 * @scenario: The #GstValidateScenario for which to execute a seek action
1046 * @action: The seek action to execute
1047 * @rate: The playback rate of the seek
1048 * @format: The #GstFormat of the seek
1049 * @flags: The #GstSeekFlags of the seek
1050 * @start_type: The #GstSeekType of the start value of the seek
1051 * @start: The start time of the seek
1052 * @stop_type: The #GstSeekType of the stop value of the seek
1053 * @stop: The stop time of the seek
1055 * Executes a seek event on the scenario's pipeline. You should always use
1056 * this method when you want to execute a seek inside a new action type
1057 * so that the scenario state is updated taking into account that seek.
1059 * For more information you should have a look at #gst_event_new_seek
1061 * Returns: %TRUE if the seek could be executed, %FALSE otherwise
1063 GstValidateExecuteActionReturn
1064 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
1065 GstValidateAction * action, gdouble rate, GstFormat format,
1066 GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
1067 GstSeekType stop_type, GstClockTime stop)
1070 GstValidateSeekInformation *seek_info;
1072 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1073 GstValidateScenarioPrivate *priv = scenario->priv;
1074 DECLARE_AND_GET_PIPELINE (scenario, action);
1076 seek = gst_event_new_seek (rate, format, flags, start_type, start,
1079 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
1080 GST_VALIDATE_REPORT_ACTION (scenario, action,
1081 SCENARIO_ACTION_EXECUTION_ERROR,
1082 "Trying to seek in format %d, but not support yet!", format);
1085 seek_info = g_new0 (GstValidateSeekInformation, 1);
1086 seek_info->seqnum = GST_EVENT_SEQNUM (seek);
1087 seek_info->rate = rate;
1088 seek_info->format = format;
1089 seek_info->flags = flags;
1090 seek_info->start = start;
1091 seek_info->stop = stop;
1092 seek_info->start_type = start_type;
1093 seek_info->stop_type = stop_type;
1094 seek_info->action = gst_validate_action_ref (action);
1096 SCENARIO_LOCK (scenario);
1097 priv->seeks = g_list_append (priv->seeks, seek_info);
1098 SCENARIO_UNLOCK (scenario);
1100 gst_event_ref (seek);
1101 if (gst_element_send_event (pipeline, seek)) {
1102 priv->seek_flags = flags;
1103 priv->seek_format = format;
1106 case GST_FORMAT_TIME:
1107 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1108 "Could not execute seek: '(position %" GST_TIME_FORMAT
1109 "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
1110 " stop: %" GST_TIME_FORMAT " Rate %lf'",
1111 GST_TIME_ARGS (action->playback_time), action->name,
1112 action->action_number, action->repeat, GST_TIME_ARGS (start),
1113 GST_TIME_ARGS (stop), rate);
1117 gchar *format_str = g_enum_to_string (GST_TYPE_FORMAT, format);
1119 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
1120 "Could not execute seek in format %s '(position %" GST_TIME_FORMAT
1121 "), %s (num %u, missing repeat: %i), seeking to: %" G_GINT64_FORMAT
1122 " stop: %" G_GINT64_FORMAT " Rate %lf'", format_str,
1123 GST_TIME_ARGS (action->playback_time), action->name,
1124 action->action_number, action->repeat, start, stop, rate);
1125 g_free (format_str);
1129 SCENARIO_LOCK (scenario);
1130 priv->seeks = g_list_remove (priv->seeks, seek_info);
1131 SCENARIO_UNLOCK (scenario);
1133 gst_validate_seek_information_free (seek_info);
1134 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1136 gst_event_unref (seek);
1137 gst_object_unref (pipeline);
1143 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
1145 const char *str_format, *str_flags, *str_start_type, *str_stop_type;
1148 guint format = GST_FORMAT_TIME;
1149 GstSeekFlags flags = 0;
1150 guint start_type = GST_SEEK_TYPE_SET;
1152 guint stop_type = GST_SEEK_TYPE_SET;
1153 GstClockTime stop = GST_CLOCK_TIME_NONE;
1155 if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
1156 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1158 gst_structure_get_double (action->structure, "rate", &rate);
1159 if ((str_format = gst_structure_get_string (action->structure, "format")))
1160 gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
1162 if ((str_start_type =
1163 gst_structure_get_string (action->structure, "start_type")))
1164 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
1167 if ((str_stop_type =
1168 gst_structure_get_string (action->structure, "stop_type")))
1169 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
1172 if ((str_flags = gst_structure_get_string (action->structure, "flags")))
1173 flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
1175 gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
1177 return gst_validate_scenario_execute_seek (scenario, action, rate, format,
1178 flags, start_type, start, stop_type, stop);
1182 _pause_action_restore_playing (GstValidateScenario * scenario)
1184 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1187 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1192 gst_validate_printf (scenario, "Back to playing\n");
1194 if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
1195 GST_STATE_CHANGE_FAILURE) {
1196 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
1197 "Failed to set state to playing");
1198 scenario->priv->target_state = GST_STATE_PLAYING;
1201 gst_object_unref (pipeline);
1207 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
1209 gst_structure_id_set_value (consts, field_id, value);
1214 static GstValidateExecuteActionReturn
1215 _execute_define_vars (GstValidateScenario * scenario,
1216 GstValidateAction * action)
1218 gst_structure_foreach (action->structure,
1219 (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
1221 return GST_VALIDATE_EXECUTE_ACTION_OK;
1224 static GstValidateExecuteActionReturn
1225 _set_timed_value (GQuark field_id, const GValue * gvalue,
1226 GstStructure * structure)
1228 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1230 GstClockTime timestamp;
1231 GstTimedValueControlSource *source = NULL;
1232 GstControlBinding *binding;
1233 GstValidateScenario *scenario;
1234 GstValidateAction *action;
1235 GstObject *obj = NULL;
1236 GParamSpec *paramspec = NULL;
1237 const gchar *field = g_quark_to_string (field_id);
1238 const gchar *unused_fields[] =
1239 { "binding-type", "source-type", "interpolation-mode",
1240 "timestamp", "__scenario__", "__action__", "__res__", "repeat",
1241 "playback-time", NULL
1244 if (g_strv_contains (unused_fields, field))
1247 gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1248 "__action__", G_TYPE_POINTER, &action, NULL);
1251 if (G_VALUE_HOLDS_DOUBLE (gvalue))
1252 value = g_value_get_double (gvalue);
1253 else if (G_VALUE_HOLDS_INT (gvalue))
1254 value = (gdouble) g_value_get_int (gvalue);
1256 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1257 "Invalid value type for property '%s': %s",
1258 field, G_VALUE_TYPE_NAME (gvalue));
1262 obj = _get_target_object_property (scenario, action, field, ¶mspec);
1263 if (!obj || !paramspec) {
1264 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1268 REPORT_UNLESS (gst_validate_action_get_clocktime (scenario, action,
1269 "timestamp", ×tamp), err,
1270 "Could get timestamp on %" GST_PTR_FORMAT, action->structure);
1272 binding = gst_object_get_control_binding (obj, paramspec->name);
1276 const gchar *interpolation_mode =
1277 gst_structure_get_string (action->structure, "interpolation-mode");
1278 const gchar *source_type_name =
1279 gst_structure_get_string (action->structure, "source-type");
1281 if (source_type_name) {
1282 source_type = g_type_from_name (source_type_name);
1284 REPORT_UNLESS (g_type_is_a (source_type,
1285 GST_TYPE_TIMED_VALUE_CONTROL_SOURCE), err,
1286 "Source type '%s' is not supported", source_type_name);
1288 source_type = GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
1291 source = g_object_new (source_type, NULL);
1292 gst_object_ref_sink (source);
1293 if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
1294 if (interpolation_mode)
1295 REPORT_UNLESS (gst_validate_utils_enum_from_str
1296 (GST_TYPE_INTERPOLATION_MODE, interpolation_mode, &mode), err,
1297 "Could not convert interpolation-mode '%s'", interpolation_mode);
1300 mode = GST_INTERPOLATION_MODE_LINEAR;
1302 g_object_set (source, "mode", mode, NULL);
1305 if (!g_strcmp0 (gst_structure_get_string (action->structure,
1306 "binding-type"), "direct-absolute")) {
1308 gst_direct_control_binding_new_absolute (obj, paramspec->name,
1309 GST_CONTROL_SOURCE (source));
1312 gst_direct_control_binding_new (obj, paramspec->name,
1313 GST_CONTROL_SOURCE (source));
1316 gst_object_add_control_binding (obj, binding);
1318 g_object_get (binding, "control-source", &source, NULL);
1321 REPORT_UNLESS (GST_IS_TIMED_VALUE_CONTROL_SOURCE (source), err,
1322 "Could not find timed value control source on %s", field);
1324 REPORT_UNLESS (gst_timed_value_control_source_set (source, timestamp, value),
1325 err, "Could not set %s=%f at %" GST_TIME_FORMAT, field, value,
1326 GST_TIME_ARGS (timestamp));
1328 gst_object_unref (obj);
1329 gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1334 gst_clear_object (&obj);
1335 gst_structure_set (structure, "__res__", G_TYPE_INT,
1336 GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED, NULL);
1341 static GstValidateExecuteActionReturn
1342 _set_timed_value_property (GstValidateScenario * scenario,
1343 GstValidateAction * action)
1345 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1347 gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1348 action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1350 gst_structure_foreach (action->structure,
1351 (GstStructureForeachFunc) _set_timed_value, action->structure);
1352 gst_structure_get_int (action->structure, "__res__", &res);
1353 gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1360 static GstValidateExecuteActionReturn
1361 _check_property (GstValidateScenario * scenario, GstValidateAction * action,
1362 gpointer object, const gchar * propname, const GValue * expected_value)
1364 GValue cvalue = G_VALUE_INIT;
1366 g_value_init (&cvalue, G_VALUE_TYPE (expected_value));
1367 g_object_get_property (object, propname, &cvalue);
1369 if (gst_value_compare (&cvalue, expected_value) != GST_VALUE_EQUAL) {
1370 gchar *expected = gst_value_serialize (expected_value), *observed =
1371 gst_value_serialize (&cvalue);
1373 GST_VALIDATE_REPORT_ACTION (scenario, action,
1374 SCENARIO_ACTION_EXECUTION_ERROR,
1376 "::%s expected value: '(%s)%s' different than observed: '(%s)%s'",
1377 object, propname, G_VALUE_TYPE_NAME (&cvalue), expected,
1378 G_VALUE_TYPE_NAME (expected_value), observed);
1383 g_value_reset (&cvalue);
1384 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1386 g_value_reset (&cvalue);
1388 return GST_VALIDATE_EXECUTE_ACTION_OK;
1392 static GstValidateExecuteActionReturn
1393 _set_or_check_properties (GQuark field_id, const GValue * value,
1394 GstStructure * structure)
1396 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1397 GstValidateScenario *scenario;
1398 GstValidateAction *action;
1399 GstObject *obj = NULL;
1400 GParamSpec *paramspec = NULL;
1401 const gchar *field = g_quark_to_string (field_id);
1402 const gchar *unused_fields[] = { "__scenario__", "__action__", "__res__",
1403 "playback-time", "repeat", NULL
1406 if (g_strv_contains (unused_fields, field))
1409 gst_structure_get (structure, "__scenario__", G_TYPE_POINTER, &scenario,
1410 "__action__", G_TYPE_POINTER, &action, NULL);
1412 obj = _get_target_object_property (scenario, action, field, ¶mspec);
1413 if (!obj || !paramspec) {
1414 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1417 if (gst_structure_has_name (action->structure, "set-properties"))
1418 res = gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
1419 G_OBJECT (obj), paramspec->name, value, action->priv->optional);
1421 res = _check_property (scenario, action, obj, paramspec->name, value);
1424 gst_clear_object (&obj);
1425 if (!gst_structure_has_field (structure, "__res__")
1426 || res != GST_VALIDATE_EXECUTE_ACTION_OK)
1427 gst_structure_set (structure, "__res__", G_TYPE_INT, res, NULL);
1431 static GstValidateExecuteActionReturn
1432 _execute_set_or_check_properties (GstValidateScenario * scenario,
1433 GstValidateAction * action)
1435 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1437 gst_structure_set (action->structure, "__action__", G_TYPE_POINTER,
1438 action, "__scenario__", G_TYPE_POINTER, scenario, NULL);
1440 gst_structure_foreach (action->structure,
1441 (GstStructureForeachFunc) _set_or_check_properties, action->structure);
1442 gst_structure_get_int (action->structure, "__res__", &res);
1443 gst_structure_remove_fields (action->structure, "__action__", "__scenario__",
1449 static GstValidateExecuteActionReturn
1450 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
1453 const gchar *str_state;
1454 GstStateChangeReturn ret;
1455 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1457 DECLARE_AND_GET_PIPELINE (scenario, action);
1459 g_return_val_if_fail ((str_state =
1460 gst_structure_get_string (action->structure, "state")), FALSE);
1462 g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
1463 str_state, &state), FALSE);
1466 scenario->priv->target_state = state;
1467 scenario->priv->changing_state = TRUE;
1468 scenario->priv->seeked_in_pause = FALSE;
1470 ret = gst_element_set_state (pipeline, state);
1471 if (ret == GST_STATE_CHANGE_FAILURE) {
1472 scenario->priv->changing_state = FALSE;
1473 GST_VALIDATE_REPORT_ACTION (scenario, action, STATE_CHANGE_FAILURE,
1474 "Failed to set state to %s", str_state);
1476 /* Nothing async on failure, action will be removed automatically */
1477 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1479 } else if (ret == GST_STATE_CHANGE_ASYNC) {
1481 scenario->priv->needs_async_done = TRUE;
1482 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1487 scenario->priv->changing_state = FALSE;
1490 gst_object_unref (pipeline);
1495 static GstValidateExecuteActionReturn
1496 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
1498 GstClockTime duration = 0;
1499 GstValidateExecuteActionReturn ret;
1501 gst_validate_action_get_clocktime (scenario, action, "duration", &duration);
1502 gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
1504 GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
1505 GST_TIME_ARGS (duration));
1507 ret = _execute_set_state (scenario, action);
1509 if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
1510 g_timeout_add (GST_TIME_AS_MSECONDS (duration),
1511 (GSourceFunc) _pause_action_restore_playing, scenario);
1516 static GstValidateExecuteActionReturn
1517 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
1519 GST_DEBUG ("Playing back");
1521 gst_structure_set (action->structure, "state", G_TYPE_STRING,
1525 return _execute_set_state (scenario, action);
1529 _action_sets_state (GstValidateAction * action)
1534 if (g_strcmp0 (action->type, "set-state") == 0)
1537 if (g_strcmp0 (action->type, "play") == 0)
1540 if (g_strcmp0 (action->type, "pause") == 0)
1548 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
1550 GstValidateScenarioPrivate *priv = scenario->priv;
1552 if (priv->max_dropped == -1 || priv->dropped == -1)
1555 GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
1556 priv->dropped, priv->max_dropped);
1558 if (priv->dropped > priv->max_dropped) {
1559 GST_VALIDATE_REPORT (scenario, CONFIG_TOO_MANY_BUFFERS_DROPPED,
1560 "Too many buffers have been dropped: %d (max allowed: %d)",
1561 priv->dropped, priv->max_dropped);
1565 static GstValidateExecuteActionReturn
1566 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
1570 DECLARE_AND_GET_PIPELINE (scenario, action);
1572 GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
1573 GST_TIME_ARGS (action->playback_time));
1575 ret = gst_element_send_event (pipeline, gst_event_new_eos ());
1576 gst_object_unref (pipeline);
1578 return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
1579 GST_VALIDATE_EXECUTE_ACTION_ERROR;
1583 find_input_selector (GValue * velement, const gchar * type)
1585 GstElement *element = g_value_get_object (velement);
1588 if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
1589 GstPad *srcpad = gst_element_get_static_pad (element, "src");
1592 GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
1596 gst_structure_get_name (gst_caps_get_structure (caps, 0));
1597 gboolean found = FALSE;
1599 if (g_strcmp0 (type, "audio") == 0)
1600 found = g_str_has_prefix (mime, "audio/");
1601 else if (g_strcmp0 (type, "video") == 0)
1602 found = g_str_has_prefix (mime, "video/")
1603 && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
1604 else if (g_strcmp0 (type, "text") == 0)
1605 found = g_str_has_prefix (mime, "text/")
1606 || g_str_has_prefix (mime, "subtitle/")
1607 || g_str_has_prefix (mime, "video/x-dvd-subpicture");
1613 gst_caps_unref (caps);
1614 gst_object_unref (srcpad);
1621 find_input_selector_with_type (GstBin * bin, const gchar * type)
1623 GValue result = { 0, };
1624 GstElement *input_selector = NULL;
1625 GstIterator *iterator = gst_bin_iterate_recurse (bin);
1627 if (gst_iterator_find_custom (iterator,
1628 (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
1629 input_selector = g_value_get_object (&result);
1631 gst_iterator_free (iterator);
1633 return input_selector;
1637 find_nth_sink_pad (GstElement * element, int index)
1639 GstIterator *iterator;
1640 gboolean done = FALSE;
1642 int dec_index = index;
1643 GValue data = { 0, };
1645 iterator = gst_element_iterate_sink_pads (element);
1647 switch (gst_iterator_next (iterator, &data)) {
1648 case GST_ITERATOR_OK:
1651 pad = g_value_get_object (&data);
1654 g_value_reset (&data);
1656 case GST_ITERATOR_RESYNC:
1657 gst_iterator_resync (iterator);
1660 case GST_ITERATOR_ERROR:
1663 case GST_ITERATOR_DONE:
1668 gst_iterator_free (iterator);
1673 find_sink_pad_index (GstElement * element, GstPad * pad)
1675 GstIterator *iterator;
1676 gboolean done = FALSE;
1678 GValue data = { 0, };
1680 iterator = gst_element_iterate_sink_pads (element);
1682 switch (gst_iterator_next (iterator, &data)) {
1683 case GST_ITERATOR_OK:
1684 if (pad == g_value_get_object (&data)) {
1689 g_value_reset (&data);
1691 case GST_ITERATOR_RESYNC:
1692 gst_iterator_resync (iterator);
1695 case GST_ITERATOR_ERROR:
1698 case GST_ITERATOR_DONE:
1703 gst_iterator_free (iterator);
1707 static GstPadProbeReturn
1708 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1709 GstValidateAction * action)
1711 if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1712 gst_validate_action_set_done (action);
1714 return GST_PAD_PROBE_REMOVE;
1717 return GST_PAD_PROBE_OK;
1720 static GstValidateExecuteActionReturn
1721 execute_switch_track_default (GstValidateScenario * scenario,
1722 GstValidateAction * action)
1725 gboolean relative = FALSE;
1726 const gchar *type, *str_index;
1727 GstElement *input_selector;
1728 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1730 DECLARE_AND_GET_PIPELINE (scenario, action);
1732 if (!(type = gst_structure_get_string (action->structure, "type")))
1735 /* First find an input selector that has the right type */
1736 input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1737 if (input_selector) {
1738 GstState state, next;
1739 GstPad *pad, *cpad, *srcpad;
1741 ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1742 str_index = gst_structure_get_string (action->structure, "index");
1744 if (str_index == NULL) {
1745 if (!gst_structure_get_uint (action->structure, "index", &index)) {
1746 GST_WARNING ("No index given, defaulting to +1");
1751 relative = strchr ("+-", str_index[0]) != NULL;
1752 index = g_ascii_strtoll (str_index, NULL, 10);
1755 if (relative) { /* We are changing track relatively to current track */
1758 g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1760 int current_index = find_sink_pad_index (input_selector, pad);
1762 index = (current_index + index) % npads;
1763 gst_object_unref (pad);
1767 pad = find_nth_sink_pad (input_selector, index);
1768 g_object_get (input_selector, "active-pad", &cpad, NULL);
1769 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1770 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1771 srcpad = gst_element_get_static_pad (input_selector, "src");
1773 gst_pad_add_probe (srcpad,
1774 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1775 (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1776 ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1777 gst_object_unref (srcpad);
1780 g_object_set (input_selector, "active-pad", pad, NULL);
1781 gst_object_unref (pad);
1782 gst_object_unref (cpad);
1783 gst_object_unref (input_selector);
1788 /* No selector found -> Failed */
1790 gst_object_unref (pipeline);
1795 static GstPadProbeReturn
1796 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1797 GstValidateAction * action)
1799 if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1800 gst_validate_action_set_done (action);
1801 return GST_PAD_PROBE_REMOVE;
1803 return GST_PAD_PROBE_OK;
1806 static GstValidateExecuteActionReturn
1807 execute_switch_track_pb (GstValidateScenario * scenario,
1808 GstValidateAction * action)
1811 const gchar *type, *str_index;
1813 gint flags, current, tflag;
1814 gchar *tmp, *current_txt;
1816 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1817 gboolean relative = FALSE, disabling = FALSE;
1819 DECLARE_AND_GET_PIPELINE (scenario, action);
1821 if (!(type = gst_structure_get_string (action->structure, "type")))
1824 tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1826 current_txt = g_strdup_printf ("current-%s", type);
1828 tmp = g_strdup_printf ("n-%s", type);
1829 g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, ¤t,
1832 /* Don't try to use -1 */
1838 if (gst_structure_has_field (action->structure, "disable")) {
1842 } else if (!(str_index =
1843 gst_structure_get_string (action->structure, "index"))) {
1844 if (!gst_structure_get_int (action->structure, "index", &index)) {
1845 GST_WARNING ("No index given, defaulting to +1");
1850 relative = strchr ("+-", str_index[0]) != NULL;
1851 index = g_ascii_strtoll (str_index, NULL, 10);
1854 if (relative) { /* We are changing track relatively to current track */
1856 GST_VALIDATE_REPORT_ACTION (scenario, action,
1857 SCENARIO_ACTION_EXECUTION_ERROR,
1858 "Trying to execute a relative %s for %s track when there"
1859 " is no track of this type available on current stream.",
1860 action->type, type);
1862 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1866 index = (current + index) % n;
1870 GstState state, next;
1871 GstPad *oldpad, *newpad;
1872 tmp = g_strdup_printf ("get-%s-pad", type);
1873 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1874 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1876 gst_validate_printf (action, "Switching to track number: %i,"
1877 " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1878 GST_DEBUG_PAD_NAME (newpad));
1882 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1883 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1884 GstPad *srcpad = NULL;
1885 GstElement *combiner = NULL;
1886 if (newpad == oldpad) {
1887 srcpad = gst_pad_get_peer (oldpad);
1888 } else if (newpad) {
1889 combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1891 srcpad = gst_element_get_static_pad (combiner, "src");
1892 gst_object_unref (combiner);
1897 gst_pad_add_probe (srcpad,
1898 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1899 (GstPadProbeCallback) _check_pad_event_selection_done, action,
1901 gst_object_unref (srcpad);
1903 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1905 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1909 gst_object_unref (oldpad);
1910 gst_object_unref (newpad);
1912 gst_validate_printf (action, "Disabling track type %s", type);
1915 g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1916 g_free (current_txt);
1919 gst_object_unref (pipeline);
1923 static GstStreamType
1924 stream_type_from_string (const gchar * type)
1926 if (!g_strcmp0 (type, "video"))
1927 return GST_STREAM_TYPE_VIDEO;
1928 else if (!g_strcmp0 (type, "text"))
1929 return GST_STREAM_TYPE_TEXT;
1932 return GST_STREAM_TYPE_AUDIO;
1935 /* Return a list of stream ID all the currently selected streams but the ones
1938 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1940 GList *streams = NULL, *l;
1942 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1943 GstStream *s = l->data;
1945 if (gst_stream_get_stream_type (s) != type) {
1946 streams = g_list_append (streams, (gpointer) s->stream_id);
1954 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1955 GstStreamType type, gint index, gboolean relative)
1958 guint i, n = 0, current = 0;
1959 GList *result = NULL, *l;
1960 GstStream *streams[256], *s, *current_stream = NULL;
1962 /* Keep all streams which are not @type */
1963 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1966 if (gst_stream_get_stream_type (s) != type) {
1967 result = g_list_append (result, (gpointer) s->stream_id);
1968 } else if (!current_stream) {
1969 /* Assume the stream we want to switch from is the first one */
1974 /* Calculate the number of @type streams */
1975 nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1976 for (i = 0; i < nb_streams; i++) {
1977 s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1979 if (gst_stream_get_stream_type (s) == type) {
1983 && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1990 if (G_UNLIKELY (n == 0)) {
1991 GST_ERROR ("No streams available of the required type");
1995 if (relative) { /* We are changing track relatively to current track */
1996 index = (current + index) % n;
2000 /* Add the new stream we want to switch to */
2003 gst_validate_printf (action, "Switching from stream %s to %s",
2004 current_stream ? current_stream->stream_id : "", s->stream_id);
2006 return g_list_append (result, (gpointer) s->stream_id);
2009 static GstValidateExecuteActionReturn
2010 execute_switch_track_pb3 (GstValidateScenario * scenario,
2011 GstValidateAction * action)
2013 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
2014 GstValidateScenarioPrivate *priv = scenario->priv;
2016 GstStreamType stype;
2017 const gchar *type, *str_index;
2018 GList *new_streams = NULL;
2019 GstValidatePipelineMonitor *monitor;
2020 DECLARE_AND_GET_PIPELINE (scenario, action);
2022 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2023 pipeline, "validate-monitor"));
2025 if (!monitor->stream_collection) {
2026 GST_VALIDATE_REPORT_ACTION (scenario, action,
2027 SCENARIO_ACTION_EXECUTION_ERROR,
2028 "No stream collection message received on the bus, "
2029 "can not switch track.");
2030 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2034 if (!monitor->streams_selected) {
2035 GST_VALIDATE_REPORT_ACTION (scenario, action,
2036 SCENARIO_ACTION_EXECUTION_ERROR,
2037 "No streams selected message received on the bus");
2038 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2042 type = gst_structure_get_string (action->structure, "type");
2043 stype = stream_type_from_string (type);
2045 if (gst_structure_has_field (action->structure, "disable")) {
2046 gst_validate_printf (action, "Disabling track type %s", type);
2047 new_streams = disable_stream (monitor, stype);
2049 gboolean relative = FALSE;
2051 if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
2052 if (!gst_structure_get_int (action->structure, "index", &index)) {
2053 GST_WARNING ("No index given, defaulting to +1");
2058 relative = strchr ("+-", str_index[0]) != NULL;
2059 index = g_ascii_strtoll (str_index, NULL, 10);
2062 new_streams = switch_stream (monitor, action, stype, index, relative);
2065 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
2066 ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
2067 (GDestroyNotify) g_list_free);
2069 if (!gst_element_send_event (pipeline,
2070 gst_event_new_select_streams (new_streams))) {
2071 GST_VALIDATE_REPORT_ACTION (scenario, action,
2072 SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
2073 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2077 priv->pending_switch_track = action;
2078 if (scenario->priv->target_state > GST_STATE_PAUSED) {
2079 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2081 gst_validate_action_ref (action);
2082 res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
2086 gst_object_unref (pipeline);
2091 static GstValidateExecuteActionReturn
2092 _execute_switch_track (GstValidateScenario * scenario,
2093 GstValidateAction * action)
2095 GstValidatePipelineMonitor *monitor;
2097 DECLARE_AND_GET_PIPELINE (scenario, action);
2099 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
2100 pipeline, "validate-monitor"));
2101 gst_object_unref (pipeline);
2103 if (monitor->is_playbin)
2104 return execute_switch_track_pb (scenario, action);
2105 else if (monitor->is_playbin3)
2106 return execute_switch_track_pb3 (scenario, action);
2108 return execute_switch_track_default (scenario, action);
2111 static GstValidateExecuteActionReturn
2112 _execute_set_rank_or_disable_feature (GstValidateScenario * scenario,
2113 GstValidateAction * action)
2115 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2117 GList *features, *origlist;
2119 GstPluginFeature *feature;
2121 gboolean removing_feature =
2122 gst_structure_has_name (action->structure, "remove-plugin-feature");
2123 GstRegistry *registry = gst_registry_get ();
2126 (name = gst_structure_get_string (action->structure, "feature-name")) ||
2127 (name = gst_structure_get_string (action->structure, "name")), done,
2128 "Could not find the name of the plugin/feature(s) to tweak");
2130 if (removing_feature)
2132 (gst_structure_get_uint (action->structure, "rank", &rank)) ||
2133 (gst_structure_get_int (action->structure, "rank", (gint *) & rank)),
2134 done, "Could not get rank to set on %s", name);
2136 feature = gst_registry_lookup_feature (registry, name);
2138 if (removing_feature)
2139 gst_plugin_feature_set_rank (feature, rank);
2141 gst_registry_remove_feature (registry, feature);
2142 gst_object_unref (feature);
2147 REPORT_UNLESS ((plugin = gst_registry_find_plugin (registry, name)),
2148 done, "Could not find %s", name);
2150 if (removing_feature) {
2151 gst_registry_remove_plugin (registry, plugin);
2155 origlist = features =
2156 gst_registry_get_feature_list_by_plugin (registry,
2157 gst_plugin_get_name (plugin));
2158 for (; features; features = features->next)
2159 gst_plugin_feature_set_rank (features->data, rank);
2160 gst_plugin_feature_list_free (origlist);
2166 static inline gboolean
2167 _add_execute_actions_gsource (GstValidateScenario * scenario)
2169 GstValidateScenarioPrivate *priv = scenario->priv;
2171 SCENARIO_LOCK (scenario);
2172 if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
2173 && priv->signal_handler_id == 0 && priv->wait_message_action == NULL) {
2174 if (!scenario->priv->action_execution_interval)
2175 priv->execute_actions_source_id =
2176 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
2177 (GSourceFunc) execute_next_action,
2178 gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2180 priv->execute_actions_source_id =
2181 g_timeout_add_full (G_PRIORITY_DEFAULT,
2182 scenario->priv->action_execution_interval,
2183 (GSourceFunc) execute_next_action,
2184 gst_object_ref (GST_OBJECT_CAST (scenario)), gst_object_unref);
2185 SCENARIO_UNLOCK (scenario);
2187 GST_DEBUG_OBJECT (scenario, "Start checking position again");
2190 SCENARIO_UNLOCK (scenario);
2192 GST_LOG_OBJECT (scenario, "No need to start a new gsource");
2197 _get_position (GstValidateScenario * scenario,
2198 GstValidateAction * act, GstClockTime * position)
2200 gboolean has_pos = FALSE, has_dur = FALSE;
2201 GstClockTime duration = -1;
2203 GstValidateScenarioPrivate *priv = scenario->priv;
2204 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2207 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2212 has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
2213 (gint64 *) position)
2214 && GST_CLOCK_TIME_IS_VALID (*position);
2216 gst_element_query_duration (pipeline, GST_FORMAT_TIME,
2217 (gint64 *) & duration)
2218 && GST_CLOCK_TIME_IS_VALID (duration);
2220 if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
2221 act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2222 GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
2223 GST_TIME_ARGS (*position));
2228 if (has_pos && has_dur && !priv->got_eos) {
2229 if (*position > duration) {
2230 _add_execute_actions_gsource (scenario);
2231 GST_VALIDATE_REPORT (scenario,
2232 QUERY_POSITION_SUPERIOR_DURATION,
2233 "Reported position %" GST_TIME_FORMAT " > reported duration %"
2234 GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
2241 gst_object_unref (pipeline);
2245 gst_object_unref (pipeline);
2250 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
2251 GstClockTime * position, gdouble * rate)
2255 GstClockTime start_with_tolerance, stop_with_tolerance;
2256 GstValidateScenarioPrivate *priv = scenario->priv;
2257 GstElement *pipeline;
2259 if (!_get_position (scenario, act, position))
2262 GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
2263 GST_TIME_ARGS (*position));
2265 /* Check if playback is within seek segment */
2266 start_with_tolerance = (priv->segment_start <
2267 priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
2268 stop_with_tolerance =
2269 GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
2270 priv->seek_pos_tol : -1;
2272 if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
2273 && *position > stop_with_tolerance)
2274 || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
2275 && *position < start_with_tolerance
2276 && priv->seek_format == GST_FORMAT_TIME)) {
2278 GST_VALIDATE_REPORT_ACTION (scenario, act, QUERY_POSITION_OUT_OF_SEGMENT,
2279 "Current position %" GST_TIME_FORMAT " not in the expected range [%"
2280 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
2281 GST_TIME_ARGS (start_with_tolerance),
2282 GST_TIME_ARGS (stop_with_tolerance));
2285 pipeline = gst_validate_scenario_get_pipeline (scenario);
2286 if (pipeline == NULL) {
2287 GST_INFO_OBJECT (scenario, "No pipeline set anymore");
2292 query = gst_query_new_segment (GST_FORMAT_DEFAULT);
2293 if (gst_element_query (GST_ELEMENT (pipeline), query))
2294 gst_query_parse_segment (query, rate, NULL, NULL, NULL);
2295 gst_query_unref (query);
2296 gst_object_unref (pipeline);
2298 if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE &&
2299 priv->seek_format == GST_FORMAT_TIME) {
2301 && (GstClockTime) ABS (GST_CLOCK_DIFF (*position,
2302 priv->segment_start)) > priv->seek_pos_tol) {
2303 priv->seeked_in_pause = FALSE;
2304 GST_VALIDATE_REPORT_ACTION (scenario, act,
2305 EVENT_SEEK_RESULT_POSITION_WRONG,
2306 "Reported position after accurate seek in PAUSED state should be exactly"
2307 " what the user asked for. Position %" GST_TIME_FORMAT
2308 " is not not the expected one: %" GST_TIME_FORMAT,
2309 GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
2317 _check_message_type (GstValidateScenario * scenario, GstValidateAction * act,
2318 GstMessage * message)
2320 return act && message
2321 && !g_strcmp0 (gst_structure_get_string (act->structure, "on-message"),
2322 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
2326 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
2327 GstClockTime position, gdouble rate)
2329 GstElement *pipeline = NULL;
2332 GST_DEBUG_OBJECT (scenario, "No action to execute");
2337 pipeline = gst_validate_scenario_get_pipeline (scenario);
2338 if (pipeline == NULL) {
2340 if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
2341 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
2342 GST_VALIDATE_REPORT_ACTION (scenario, act,
2343 SCENARIO_ACTION_EXECUTION_ERROR,
2344 "Trying to execute an %s action after the pipeline has been destroyed"
2345 " but the type has not been marked as "
2346 "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
2349 } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2350 GST_VALIDATE_REPORT_ACTION (scenario, act,
2351 SCENARIO_ACTION_EXECUTION_ERROR,
2352 "Trying to execute action %s with playback time %" GST_TIME_FORMAT
2353 " after the pipeline has been destroyed. It is impossible"
2354 " to execute an action with a playback time specified"
2355 " after the pipeline has been destroyed", act->type,
2356 GST_TIME_ARGS (act->playback_time));
2361 GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
2364 } else if (scenario->priv->got_eos) {
2365 GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
2366 scenario->priv->got_eos = FALSE;
2367 } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
2368 GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
2369 "just executing actions");
2372 } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
2373 GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
2376 } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
2377 GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
2378 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2379 GST_TIME_ARGS (act->playback_time));
2382 } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
2383 GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
2384 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
2385 GST_TIME_ARGS (act->playback_time));
2391 gst_object_unref (pipeline);
2395 gst_clear_object (&pipeline);
2400 _set_action_playback_time (GstValidateScenario * scenario,
2401 GstValidateAction * action)
2403 if (!gst_validate_action_get_clocktime (scenario, action,
2404 "playback-time", &action->playback_time)) {
2405 gst_validate_error_structure (action,
2406 "Could not parse playback-time in %" GST_PTR_FORMAT, action->structure);
2411 gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
2412 action->playback_time, NULL);
2418 gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
2420 GstValidateAction *action;
2421 GstValidateScenarioPrivate *priv = self->priv;
2426 action = (GstValidateAction *) priv->actions->data;
2427 if (!action->priv->needs_playback_parsing)
2430 if (!_set_action_playback_time (self, action)) {
2431 GST_ERROR_OBJECT (self, "Could not set playback_time!");
2435 action->priv->needs_playback_parsing = FALSE;
2441 _foreach_find_iterator (GQuark field_id, GValue * value,
2442 GstValidateAction * action)
2444 const gchar *field = g_quark_to_string (field_id);
2446 if (!g_strcmp0 (field, "actions"))
2449 if (!GST_VALUE_HOLDS_INT_RANGE (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
2450 gst_validate_error_structure (action,
2451 "Unsupported iterator type `%s` for %s"
2452 ". Only ranges (`[(int)start, (int)stop, [(int)step]]`) and arrays "
2453 " (`<item1, item2>`) are supported", field, G_VALUE_TYPE_NAME (value));
2457 if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
2458 gst_validate_error_structure (action, "Wrong iterator syntax, "
2459 " only one iterator field is supported.");
2463 GST_VALIDATE_ACTION_RANGE_NAME (action) = field;
2468 GstValidateExecuteActionReturn
2469 gst_validate_execute_action (GstValidateActionType * action_type,
2470 GstValidateAction * action)
2472 GstValidateExecuteActionReturn res;
2473 GstValidateScenario *scenario;
2475 g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
2476 GST_VALIDATE_EXECUTE_ACTION_ERROR);
2478 scenario = gst_validate_action_get_scenario (action);
2479 g_assert (scenario);
2481 action->priv->context = g_main_context_ref (scenario->priv->context);
2482 if (action_type->prepare) {
2483 res = action_type->prepare (action);
2484 if (res == GST_VALIDATE_EXECUTE_ACTION_DONE) {
2485 gst_validate_print_action (action, NULL);
2486 return GST_VALIDATE_EXECUTE_ACTION_OK;
2489 if (res != GST_VALIDATE_EXECUTE_ACTION_OK) {
2490 GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
2491 " could not be prepared", action->structure);
2493 gst_object_unref (scenario);
2498 gst_validate_print_action (action, NULL);
2500 action->priv->execution_time = gst_util_get_timestamp ();
2501 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
2502 action_type->priv->n_calls++;
2503 res = action_type->execute (scenario, action);
2504 gst_object_unref (scenario);
2509 /* scenario can be NULL **only** if the action is a CONFIG action and
2510 * add_to_lists is FALSE */
2511 static GstValidateExecuteActionReturn
2512 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
2513 GstStructure * structure, gboolean add_to_lists)
2515 gdouble playback_time;
2516 gboolean is_config = FALSE;
2517 GstValidateActionType *action_type;
2518 GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
2519 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_NONE;
2520 gboolean optional, needs_parsing = FALSE;
2522 action->type = gst_structure_get_name (structure);
2523 action_type = _find_action_type (action->type);
2526 GST_ERROR_OBJECT (scenario, "Action type %s no found",
2527 gst_structure_get_name (structure));
2529 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2532 if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
2533 gst_structure_get_double (structure, "playback_time", &playback_time)) {
2534 action->playback_time = playback_time * GST_SECOND;
2535 } else if (gst_structure_has_field_typed (structure, "playback-time",
2537 || gst_structure_has_field_typed (structure, "playback_time",
2540 if (add_to_lists && priv) {
2541 action->priv->needs_playback_parsing = TRUE;
2542 needs_parsing = TRUE;
2545 GST_INFO_OBJECT (scenario,
2546 "No playback time for action %" GST_PTR_FORMAT, structure);
2548 if (!gst_validate_utils_get_clocktime (structure,
2549 "timeout", &action->priv->timeout)) {
2550 GST_INFO_OBJECT (scenario,
2551 "No timeout time for action %" GST_PTR_FORMAT, structure);
2554 action->structure = gst_structure_copy (structure);
2556 if (!(action->name = gst_structure_get_string (action->structure, "name")))
2559 if (!action->priv->main_structure)
2560 action->priv->main_structure = gst_structure_copy (structure);
2562 if (gst_structure_get_boolean (structure, "optional", &optional)) {
2563 if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
2564 GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
2565 gst_structure_get_name (structure));
2566 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2568 action->priv->optional = optional;
2571 if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
2572 (gst_structure_get_boolean (action->structure, "as-config",
2573 &is_config) && is_config == TRUE)) {
2575 action_type->priv->n_calls++;
2576 res = action_type->execute (scenario, action);
2577 gst_validate_print_action (action, NULL);
2586 GstValidateActionType *type = _find_action_type (action->type);
2587 gboolean can_execute_on_addition =
2588 type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
2589 && !GST_CLOCK_TIME_IS_VALID (action->playback_time)
2590 && !gst_structure_has_field (action->structure, "on-message");
2593 can_execute_on_addition = FALSE;
2595 if (can_execute_on_addition) {
2598 for (tmp = priv->actions; tmp; tmp = tmp->next) {
2599 GstValidateAction *act = (GstValidateAction *) tmp->data;
2600 if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
2601 can_execute_on_addition = FALSE;
2608 if (can_execute_on_addition) {
2609 SCENARIO_LOCK (scenario);
2610 priv->on_addition_actions = g_list_append (priv->on_addition_actions,
2612 SCENARIO_UNLOCK (scenario);
2615 priv->actions = g_list_append (priv->actions, action);
2623 gst_validate_scenario_execute_next_or_restart_looping (GstValidateScenario *
2626 /* Recurse to the next action if it is possible
2627 * to execute right away */
2628 if (!scenario->priv->execute_on_idle) {
2629 GST_DEBUG_OBJECT (scenario, "linking next action execution");
2631 return execute_next_action (scenario);
2633 _add_execute_actions_gsource (scenario);
2634 GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2637 return G_SOURCE_CONTINUE;
2640 /* This is the main action execution function
2641 * it checks whether it is time to run the next action
2642 * and if it is the case executes it.
2644 * If the 'execute-on-idle' property is not TRUE,
2645 * the function will recurse while the actions are run
2649 execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
2652 GstClockTime position = -1;
2653 GstValidateAction *act = NULL;
2654 GstValidateActionType *type;
2656 GstValidateScenarioPrivate *priv = scenario->priv;
2658 if (priv->buffering) {
2659 GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
2661 return G_SOURCE_CONTINUE;
2664 if (priv->changing_state || priv->needs_async_done) {
2665 GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
2666 return G_SOURCE_CONTINUE;
2669 if (scenario->priv->actions)
2670 act = scenario->priv->actions->data;
2673 _check_scenario_is_done (scenario);
2674 return G_SOURCE_CONTINUE;
2677 switch (act->priv->state) {
2678 case GST_VALIDATE_EXECUTE_ACTION_NONE:
2679 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2681 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2682 return G_SOURCE_CONTINUE;
2683 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2684 if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
2685 GstClockTime etime =
2686 gst_util_get_timestamp () - act->priv->execution_time;
2688 if (etime > act->priv->timeout) {
2689 gchar *str = gst_structure_to_string (act->structure);
2691 GST_VALIDATE_REPORT_ACTION (scenario, act,
2692 SCENARIO_ACTION_EXECUTION_ERROR,
2693 "Action %s timed out after: %" GST_TIME_FORMAT, str,
2694 GST_TIME_ARGS (etime));
2699 GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2702 return G_SOURCE_CONTINUE;
2704 GST_ERROR ("State is %d", act->priv->state);
2705 g_assert_not_reached ();
2709 if (!_check_message_type (scenario, act, message))
2710 return G_SOURCE_CONTINUE;
2711 } else if ((act && gst_structure_get_string (act->structure, "on-message") &&
2712 !GST_CLOCK_TIME_IS_VALID (act->playback_time)) ||
2713 (!_check_position (scenario, act, &position, &rate))) {
2714 return G_SOURCE_CONTINUE;
2717 if (!_should_execute_action (scenario, act, position, rate)) {
2718 _add_execute_actions_gsource (scenario);
2720 return G_SOURCE_CONTINUE;
2723 type = _find_action_type (act->type);
2725 GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2726 " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2727 priv->seeked_in_pause = FALSE;
2730 gst_structure_remove_field (act->structure, "playback-time");
2732 gst_structure_remove_field (act->structure, "on-message");
2734 act->priv->state = gst_validate_execute_action (type, act);
2735 switch (act->priv->state) {
2736 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
2737 GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2740 SCENARIO_LOCK (scenario);
2741 priv->execute_actions_source_id = 0;
2742 SCENARIO_UNLOCK (scenario);
2744 return G_SOURCE_CONTINUE;
2745 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
2746 return G_SOURCE_CONTINUE;
2747 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
2748 SCENARIO_LOCK (scenario);
2749 priv->non_blocking_running_actions =
2750 g_list_append (priv->non_blocking_running_actions, act);
2751 priv->actions = g_list_remove (priv->actions, act);
2752 SCENARIO_UNLOCK (scenario);
2753 return gst_validate_scenario_execute_next_or_restart_looping (scenario);
2755 gst_validate_action_set_done (act);
2756 return G_SOURCE_CONTINUE;
2761 execute_next_action (GstValidateScenario * scenario)
2763 return execute_next_action_full (scenario, NULL);
2767 stop_waiting (GstValidateAction * action)
2769 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2771 SCENARIO_LOCK (scenario);
2772 scenario->priv->wait_id = 0;
2773 SCENARIO_UNLOCK (scenario);
2775 gst_validate_action_set_done (action);
2776 _add_execute_actions_gsource (scenario);
2777 gst_object_unref (scenario);
2780 return G_SOURCE_REMOVE;
2784 stop_waiting_signal (GstStructure * data)
2788 GstStructure *check = NULL;
2789 GstValidateAction *action;
2790 GstValidateScenario *scenario;
2792 gst_structure_get (data, "target", G_TYPE_POINTER, &target,
2793 "action", GST_TYPE_VALIDATE_ACTION, &action, "sigid", G_TYPE_UINT, &sigid,
2795 gst_structure_free (data);
2797 scenario = gst_validate_action_get_scenario (action);
2799 g_assert (scenario);
2800 SCENARIO_LOCK (scenario);
2801 g_signal_handler_disconnect (target,
2802 sigid ? sigid : scenario->priv->signal_handler_id);
2804 scenario->priv->signal_handler_id = 0;
2805 SCENARIO_UNLOCK (scenario);
2807 if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE,
2809 GstValidateAction *subact =
2810 gst_validate_create_subaction (scenario, NULL, action,
2812 GstValidateActionType *subact_type = _find_action_type (subact->type);
2813 if (!(subact_type->flags & GST_VALIDATE_ACTION_TYPE_CHECK)) {
2814 gst_validate_error_structure (action,
2815 "`check` action %s is not marked as 'check'", subact->type);
2818 gst_validate_execute_action (subact_type, subact);
2819 gst_validate_action_unref (subact);
2822 gst_validate_action_set_done (action);
2823 gst_validate_action_unref (action);
2824 _add_execute_actions_gsource (scenario);
2825 gst_object_unref (scenario);
2826 gst_object_unref (target);
2829 static GstValidateExecuteActionReturn
2830 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2832 GstValidateScenarioPrivate *priv = scenario->priv;
2833 GstClockTime duration;
2835 gdouble wait_multiplier = 1;
2836 const gchar *str_wait_multiplier =
2837 g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2839 if (str_wait_multiplier) {
2841 wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2844 GST_ERROR ("Could not use the WAIT MULTIPLIER");
2846 wait_multiplier = 1;
2849 if (wait_multiplier == 0) {
2850 GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2851 return GST_VALIDATE_EXECUTE_ACTION_OK;
2855 if (!gst_validate_action_get_clocktime (scenario, action,
2856 "duration", &duration)) {
2857 GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2859 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2862 duration *= wait_multiplier;
2864 SCENARIO_LOCK (scenario);
2865 if (priv->execute_actions_source_id) {
2866 g_source_remove (priv->execute_actions_source_id);
2867 priv->execute_actions_source_id = 0;
2869 SCENARIO_UNLOCK (scenario);
2871 SCENARIO_LOCK (scenario);
2872 priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2873 (GSourceFunc) stop_waiting, action);
2874 SCENARIO_UNLOCK (scenario);
2876 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2879 static GstValidateExecuteActionReturn
2880 _execute_wait_for_signal (GstValidateScenario * scenario,
2881 GstValidateAction * action)
2883 gboolean non_blocking;
2884 GstValidateScenarioPrivate *priv = scenario->priv;
2885 const gchar *signal_name = gst_structure_get_string
2886 (action->structure, "signal-name");
2887 GList *targets = NULL;
2890 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
2891 DECLARE_AND_GET_PIPELINE (scenario, action);
2893 REPORT_UNLESS (signal_name, err, "No signal-name given for wait action");
2894 targets = _find_elements_defined_in_action (scenario, action);
2895 REPORT_UNLESS ((g_list_length (targets) == 1), err,
2896 "Could not find target element.");
2898 gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2900 if (priv->execute_actions_source_id) {
2901 g_source_remove (priv->execute_actions_source_id);
2902 priv->execute_actions_source_id = 0;
2905 target = targets->data;
2907 gst_structure_new ("a", "action", GST_TYPE_VALIDATE_ACTION, action,
2908 "target", G_TYPE_POINTER, target, NULL);
2909 SCENARIO_LOCK (scenario);
2910 priv->signal_handler_id = g_signal_connect_swapped (target, signal_name,
2911 (GCallback) stop_waiting_signal, data);
2914 gst_structure_get_boolean (action->structure, "non-blocking",
2917 gst_structure_set (data, "sigid", G_TYPE_UINT, priv->signal_handler_id,
2919 priv->signal_handler_id = 0;
2921 SCENARIO_UNLOCK (scenario);
2923 gst_object_unref (pipeline);
2924 g_list_free (targets);
2927 return non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING :
2928 GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2931 g_list_free_full (targets, gst_object_unref);
2932 gst_object_unref (pipeline);
2937 _execute_wait_for_message (GstValidateScenario * scenario,
2938 GstValidateAction * action)
2940 GstValidateScenarioPrivate *priv = scenario->priv;
2941 const gchar *message_type = gst_structure_get_string
2942 (action->structure, "message-type");
2943 DECLARE_AND_GET_PIPELINE (scenario, action);
2945 gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2947 if (priv->execute_actions_source_id) {
2948 g_source_remove (priv->execute_actions_source_id);
2949 priv->execute_actions_source_id = 0;
2952 g_assert (!priv->wait_message_action);
2953 priv->wait_message_action = gst_validate_action_ref (action);
2954 gst_object_unref (pipeline);
2956 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2959 static GstValidateExecuteActionReturn
2960 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2962 gboolean on_clock = FALSE;
2964 gst_structure_get_boolean (action->structure, "on-clock", &on_clock);
2965 if (gst_structure_has_field (action->structure, "signal-name")) {
2966 return _execute_wait_for_signal (scenario, action);
2967 } else if (gst_structure_has_field (action->structure, "message-type")) {
2968 return _execute_wait_for_message (scenario, action);
2969 } else if (on_clock) {
2970 gst_test_clock_wait_for_next_pending_id (scenario->priv->clock, NULL);
2972 return GST_VALIDATE_EXECUTE_ACTION_OK;
2974 return _execute_timed_wait (scenario, action);
2981 _execute_dot_pipeline (GstValidateScenario * scenario,
2982 GstValidateAction * action)
2985 gint details = GST_DEBUG_GRAPH_SHOW_ALL;
2986 const gchar *name = gst_structure_get_string (action->structure, "name");
2987 DECLARE_AND_GET_PIPELINE (scenario, action);
2989 gst_structure_get_int (action->structure, "details", &details);
2991 dotname = g_strdup_printf ("validate.action.%s", name);
2993 dotname = g_strdup ("validate.action.unnamed");
2995 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
2998 gst_object_unref (pipeline);
3004 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
3008 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3011 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3016 name = gst_structure_get_string (action->structure, "target-element-name");
3018 gst_object_unref (pipeline);
3023 if (g_strcmp0 (GST_OBJECT_NAME (pipeline), name) == 0) {
3024 target = gst_object_ref (pipeline);
3026 target = gst_bin_get_by_name (GST_BIN (pipeline), name);
3030 GST_ERROR ("Target element with given name (%s) not found", name);
3031 gst_object_unref (pipeline);
3036 /* _get_target_elements_by_klass_or_factory_name:
3037 * @scenario: a #GstValidateScenario
3038 * @action: a #GstValidateAction
3040 * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
3041 * matches the 'target-element-klass' of @action and the factory name matches
3042 * the 'target-element-factory-name'.
3044 * Returns: (transfer full) (element-type GstElement): a list of #GstElement
3047 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
3048 GstValidateAction * action)
3050 GList *result = NULL;
3052 const gchar *klass, *fname;
3053 GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
3054 gboolean done = FALSE;
3055 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3058 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3063 klass = gst_structure_get_string (action->structure, "target-element-klass");
3065 gst_structure_get_string (action->structure,
3066 "target-element-factory-name");
3067 if (!klass && !fname) {
3068 gst_object_unref (pipeline);
3073 if (klass && gst_validate_element_has_klass (pipeline, klass))
3074 result = g_list_prepend (result, gst_object_ref (pipeline));
3076 if (fname && gst_element_get_factory (pipeline)
3077 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
3079 result = g_list_prepend (result, gst_object_ref (pipeline));
3081 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
3083 g_value_init (¶m, G_TYPE_STRING);
3084 g_value_set_string (¶m, klass);
3087 switch (gst_iterator_next (it, &v)) {
3088 case GST_ITERATOR_OK:{
3089 GstElement *child = g_value_get_object (&v);
3091 if (g_list_find (result, child))
3094 if (klass && gst_validate_element_has_klass (child, klass)) {
3095 result = g_list_prepend (result, gst_object_ref (child));
3099 if (fname && gst_element_get_factory (child)
3100 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
3102 result = g_list_prepend (result, gst_object_ref (child));
3107 case GST_ITERATOR_RESYNC:
3108 gst_iterator_resync (it);
3110 case GST_ITERATOR_ERROR:
3111 case GST_ITERATOR_DONE:
3117 g_value_reset (¶m);
3118 gst_iterator_free (it);
3119 gst_object_unref (pipeline);
3125 _find_elements_defined_in_action (GstValidateScenario * scenario,
3126 GstValidateAction * action)
3129 GList *targets = NULL;
3131 /* set-property can be applied on either:
3132 * - a single element having target-element-name as name
3133 * - all the elements having target-element-klass as klass
3135 if (gst_structure_get_string (action->structure, "target-element-name")) {
3136 target = _get_target_element (scenario, action);
3140 targets = g_list_append (targets, target);
3141 } else if (gst_structure_get_string (action->structure,
3142 "target-element-klass") ||
3143 gst_structure_get_string (action->structure,
3144 "target-element-factory-name")) {
3145 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
3151 static GstValidateExecuteActionReturn
3152 _execute_check_action_type_calls (GstValidateScenario * scenario,
3153 GstValidateAction * action)
3156 GstValidateActionType *t;
3157 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3160 REPORT_UNLESS (gst_structure_get_int (action->structure, "n", &n),
3162 REPORT_UNLESS ((type = gst_structure_get_string (action->structure, "type")),
3163 done, "No `type`!");
3165 _find_action_type (type)), done, "Can't find `%s`!", type);
3166 REPORT_UNLESS (t->priv->n_calls == n, done,
3167 "%s called %d times instead of expected %d", type, t->priv->n_calls, n);
3174 static GstValidateExecuteActionReturn
3175 _execute_check_subaction_level (GstValidateScenario * scenario,
3176 GstValidateAction * action)
3178 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3181 REPORT_UNLESS (gst_structure_get_int (action->structure, "level", &n),
3183 REPORT_UNLESS (gst_validate_action_get_level (action) == n, done,
3184 "Expected subaction level %d, got %d", n,
3185 gst_validate_action_get_level (action));
3193 set_env_var (GQuark field_id, GValue * value,
3194 GSubprocessLauncher * subproc_launcher)
3196 g_subprocess_launcher_setenv (subproc_launcher, g_quark_to_string (field_id),
3197 g_value_get_string (value), TRUE);
3202 static GstValidateExecuteActionReturn
3203 _run_command (GstValidateScenario * scenario, GstValidateAction * action)
3205 gchar **argv = NULL, *_stderr;
3206 GError *error = NULL;
3207 const GValue *env = NULL;
3208 GSubprocess *subproc = NULL;
3209 GSubprocessLauncher *subproc_launcher = NULL;
3210 GstValidateExecuteActionReturn res =
3211 GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3213 REPORT_UNLESS ((argv = gst_validate_utils_get_strv (action->structure,
3215 "Couldn't find `argv` as array of strings in %" GST_PTR_FORMAT,
3218 subproc_launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDERR_PIPE);
3219 g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_SCENARIO");
3220 g_subprocess_launcher_unsetenv (subproc_launcher, "GST_VALIDATE_CONFIG");
3222 env = gst_structure_get_value (action->structure, "env");
3223 REPORT_UNLESS (!env || GST_VALUE_HOLDS_STRUCTURE (env), done,
3224 "The `env` parameter should be a GstStructure, got %s",
3225 G_VALUE_TYPE_NAME (env));
3227 gst_structure_foreach (gst_value_get_structure (env),
3228 (GstStructureForeachFunc) set_env_var, subproc_launcher);
3233 g_subprocess_launcher_spawnv (subproc_launcher,
3234 (const gchar * const *) argv, &error)), done,
3235 "Couldn't start subprocess: %s", error->message);
3237 REPORT_UNLESS (g_subprocess_communicate_utf8 (subproc, NULL, NULL, NULL,
3238 &_stderr, &error), done, "Failed to run check: %s", error->message);
3240 REPORT_UNLESS (g_subprocess_get_exit_status (subproc) == 0,
3241 done, "Sub command failed. Stderr: %s", _stderr);
3243 res = GST_VALIDATE_EXECUTE_ACTION_OK;
3248 g_clear_object (&subproc_launcher);
3249 g_clear_object (&subproc);
3254 static GstValidateExecuteActionReturn
3255 _execute_check_pad_caps (GstValidateScenario * scenario,
3256 GstValidateAction * action)
3258 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3259 GList *elements = NULL;
3261 GstStructure *expected_struct = NULL;
3262 GstCaps *expected = NULL, *current_caps = NULL;
3263 const gchar *pad_name, *comparison_type =
3264 gst_structure_get_string (action->structure, "comparision-mode");
3266 DECLARE_AND_GET_PIPELINE (scenario, action);
3268 REPORT_UNLESS (elements =
3269 _find_elements_defined_in_action (scenario, action), done,
3270 "Could not find any element from %" GST_PTR_FORMAT, action->structure);
3272 REPORT_UNLESS (g_list_length (elements) == 1, done,
3273 "More than one element found from %" GST_PTR_FORMAT, action->structure);
3275 pad_name = gst_structure_get_string (action->structure, "pad");
3276 REPORT_UNLESS (pad =
3277 gst_element_get_static_pad (elements->data, pad_name), done,
3278 "Could not find pad %s in %" GST_PTR_FORMAT, pad_name, elements->data);
3280 current_caps = gst_pad_get_current_caps (pad);
3281 if (gst_structure_get (action->structure, "expected-caps", GST_TYPE_STRUCTURE,
3282 &expected_struct, NULL))
3283 expected = gst_caps_new_full (gst_structure_copy (expected_struct), NULL);
3285 gst_structure_get (action->structure, "expected-caps", GST_TYPE_CAPS,
3288 if (!comparison_type || !g_strcmp0 (comparison_type, "intersect")) {
3289 REPORT_UNLESS (expected, done, "Can't intersect with NULL expected caps");
3290 REPORT_UNLESS (gst_caps_can_intersect (expected, current_caps), done,
3291 "Caps can't intesect. Expected: \n - %" GST_PTR_FORMAT "\nGot:\n - %"
3292 GST_PTR_FORMAT, expected, current_caps);
3293 } else if (!g_strcmp0 (comparison_type, "equal")) {
3294 REPORT_UNLESS ((expected == NULL && current_caps == NULL)
3295 || gst_caps_is_equal (expected, current_caps), done,
3296 "Caps do not match. Expected: %" GST_PTR_FORMAT " got %" GST_PTR_FORMAT,
3297 expected, current_caps);
3299 REPORT_UNLESS (FALSE, done, "Invalid caps `comparision-type`: '%s'",
3304 g_clear_object (&pipeline);
3305 g_clear_object (&pad);
3306 g_list_free_full (elements, gst_object_unref);
3307 gst_clear_structure (&expected_struct);
3308 gst_clear_caps (¤t_caps);
3309 gst_clear_caps (&expected);
3315 static GstValidateExecuteActionReturn
3316 _execute_check_position (GstValidateScenario * scenario,
3317 GstValidateAction * action)
3319 GstClockTime expected_pos, pos;
3321 if (!gst_validate_action_get_clocktime (scenario, action,
3322 "expected-position", &expected_pos)) {
3323 GST_VALIDATE_REPORT_ACTION (scenario, action,
3324 SCENARIO_ACTION_EXECUTION_ERROR,
3325 "Could not retrieve expected position in: %" GST_PTR_FORMAT,
3328 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3331 if (!_get_position (scenario, NULL, &pos)) {
3332 GST_VALIDATE_REPORT_ACTION (scenario, action,
3333 SCENARIO_ACTION_EXECUTION_ERROR, "Could not get pipeline position");
3335 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3338 if (pos != expected_pos) {
3339 GST_VALIDATE_REPORT_ACTION (scenario, action,
3340 SCENARIO_ACTION_EXECUTION_ERROR,
3341 "Pipeline position doesn't match expectations"
3342 " got %" GST_TIME_FORMAT " instead of %" GST_TIME_FORMAT,
3343 GST_TIME_ARGS (pos), GST_TIME_ARGS (expected_pos));
3345 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3348 return GST_VALIDATE_EXECUTE_ACTION_OK;
3352 static GstValidateExecuteActionReturn
3353 _execute_set_or_check_property (GstValidateScenario * scenario,
3354 GstValidateAction * action)
3357 const gchar *property;
3358 const GValue *property_value;
3359 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_OK;
3360 gboolean check = gst_structure_has_name (action->structure, "check-property");
3362 targets = _find_elements_defined_in_action (scenario, action);
3364 GST_VALIDATE_REPORT_ACTION (scenario, action,
3365 SCENARIO_ACTION_EXECUTION_ERROR,
3366 "No element found for action: %" GST_PTR_FORMAT, action->structure);
3368 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3371 property = gst_structure_get_string (action->structure, "property-name");
3372 property_value = gst_structure_get_value (action->structure,
3375 for (l = targets; l != NULL; l = g_list_next (l)) {
3377 GstValidateActionReturn tmpres;
3380 gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
3381 G_OBJECT (l->data), property, property_value, action->priv->optional);
3387 _check_property (scenario, action, l->data, property, property_value);
3391 g_list_free_full (targets, gst_object_unref);
3396 _execute_set_debug_threshold (GstValidateScenario * scenario,
3397 GstValidateAction * action)
3400 gboolean reset = TRUE;
3401 const gchar *threshold_str;
3404 gst_structure_get_string (action->structure, "debug-threshold");
3405 if (threshold_str == NULL) {
3408 if (gst_structure_get_int (action->structure, "debug-threshold",
3410 threshold_str = str = g_strdup_printf ("%i", threshold);
3415 gst_structure_get_boolean (action->structure, "reset", &reset);
3417 gst_debug_set_threshold_from_string (threshold_str, reset);
3424 static GstValidateExecuteActionReturn
3425 _execute_emit_signal (GstValidateScenario * scenario,
3426 GstValidateAction * action)
3428 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3431 GSignalQuery query = { 0, };
3432 GValue *values = NULL, lparams = { 0, };
3433 const GValue *params;
3435 REPORT_UNLESS ((target =
3436 _get_target_element (scenario, action)), out, "No element found");
3439 gst_structure_get_string (action->structure, "signal-name");
3440 query.signal_id = g_signal_lookup (query.signal_name, G_OBJECT_TYPE (target));
3441 REPORT_UNLESS (query.signal_id != 0, out, "Invalid signal `%s::%s`",
3442 G_OBJECT_TYPE_NAME (target), query.signal_name);
3444 g_signal_query (query.signal_id, &query);
3446 params = gst_structure_get_value (action->structure, "params");
3448 if (G_VALUE_HOLDS_STRING (params)) {
3449 g_value_init (&lparams, GST_TYPE_ARRAY);
3451 REPORT_UNLESS (gst_value_deserialize (&lparams,
3452 g_value_get_string (params)), out,
3453 "\"params\" argument should be a value array or a string deserializable"
3454 " as value array, got string %s", g_value_get_string (params)
3458 REPORT_UNLESS (GST_VALUE_HOLDS_ARRAY (params), out,
3459 "\"params\" argument should be a value array, got %s",
3460 G_VALUE_TYPE_NAME (params));
3462 n_params = gst_value_array_get_size (params);
3464 REPORT_UNLESS (query.n_params == (n_params), out,
3465 "Expected %d `params` got %d", query.n_params, n_params);
3466 values = g_malloc0 ((n_params + 2) * sizeof (GValue));
3467 g_value_init (&values[0], G_OBJECT_TYPE (target));
3468 g_value_take_object (&values[0], target);
3469 for (gint i = 1; i < n_params + 1; i++) {
3470 const GValue *param = gst_value_array_get_value (params, i - 1);
3471 g_value_init (&values[i], query.param_types[i - 1]);
3473 if (query.param_types[i - 1] == G_TYPE_BYTES
3474 && G_VALUE_TYPE (param) == G_TYPE_STRING) {
3475 const gchar *s = g_value_get_string (param);
3476 g_value_take_boxed (&values[i], g_bytes_new (s, strlen (s)));
3478 REPORT_UNLESS (g_value_transform (param, &values[i]), out,
3479 "Could not transform param %d from %s to %s", i - 1,
3480 G_VALUE_TYPE_NAME (param), G_VALUE_TYPE_NAME (&values[i]));
3484 g_signal_emitv (values, query.signal_id, 0, NULL);
3486 for (gint i = 0; i < n_params + 1; i++)
3487 g_value_reset (&values[i]);
3489 if (G_VALUE_TYPE (&lparams))
3490 g_value_reset (&lparams);
3497 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
3498 GstBuffer * buffer, gpointer * user_data, gboolean * remove_wrapper);
3500 typedef struct _ChainWrapperFunctionData
3502 GstPadChainFunction wrapped_chain_func;
3503 gpointer wrapped_chain_data;
3504 GDestroyNotify wrapped_chain_notify;
3505 ChainWrapperFunction wrapper_function;
3506 gpointer wrapper_function_user_data;
3507 } ChainWrapperFunctionData;
3509 static GstFlowReturn
3510 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
3512 ChainWrapperFunctionData *data = pad->chaindata;
3514 gboolean remove_wrapper = FALSE;
3516 pad->chainfunc = data->wrapped_chain_func;
3517 pad->chaindata = data->wrapped_chain_data;
3518 pad->chainnotify = data->wrapped_chain_notify;
3520 ret = data->wrapper_function (pad, parent, buffer,
3521 data->wrapper_function_user_data, &remove_wrapper);
3523 if (!remove_wrapper) {
3524 /* The chain function may have changed during the calling (e.g. if it was
3525 * a nested wrapper that decided to remove itself) so we need to update the
3526 * wrapped function just in case. */
3527 data->wrapped_chain_func = pad->chainfunc;
3528 data->wrapped_chain_data = pad->chaindata;
3529 data->wrapped_chain_notify = pad->chainnotify;
3531 /* Restore the wrapper as chain function */
3532 pad->chainfunc = _pad_chain_wrapper;
3533 pad->chaindata = data;
3534 pad->chainnotify = g_free;
3542 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
3545 ChainWrapperFunctionData *data = g_new (ChainWrapperFunctionData, 1);
3546 data->wrapped_chain_func = pad->chainfunc;
3547 data->wrapped_chain_data = pad->chaindata;
3548 data->wrapped_chain_notify = pad->chainnotify;
3549 data->wrapper_function = new_function;
3550 data->wrapper_function_user_data = user_data;
3552 pad->chainfunc = _pad_chain_wrapper;
3553 pad->chaindata = data;
3554 pad->chainnotify = g_free;
3557 static GstFlowReturn
3558 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer,
3559 gpointer * user_data, gboolean * remove_wrapper)
3561 GstValidateAction *action = (GstValidateAction *) user_data;
3562 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3564 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
3565 ret = pad->chainfunc (pad, parent, buffer);
3566 gst_validate_action_set_done (action);
3567 gst_validate_action_unref (action);
3568 *remove_wrapper = TRUE;
3569 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3570 g_object_unref (scenario);
3575 structure_get_uint64_permissive (const GstStructure * structure,
3576 const gchar * fieldname, guint64 * dest)
3578 const GValue *original;
3579 GValue transformed = G_VALUE_INIT;
3581 original = gst_structure_get_value (structure, fieldname);
3585 g_value_init (&transformed, G_TYPE_UINT64);
3586 if (!g_value_transform (original, &transformed))
3589 *dest = g_value_get_uint64 (&transformed);
3590 g_value_unset (&transformed);
3595 _execute_appsrc_push (GstValidateScenario * scenario,
3596 GstValidateAction * action)
3598 GstElement *target = NULL;
3599 gchar *file_name = NULL;
3600 gchar *file_contents = NULL;
3601 GError *error = NULL;
3604 guint64 size = 0, read;
3605 gint push_buffer_ret;
3607 GFileInfo *finfo = NULL;
3609 GstPad *appsrc_pad = NULL;
3610 GstPad *peer_pad = NULL;
3611 GInputStream *stream = NULL;
3612 GstValidateExecuteActionReturn res;
3614 /* We will only wait for the the buffer to be pushed if we are in a state
3615 * that allows flow of buffers (>=PAUSED). Otherwise the buffer will just
3617 wait = scenario->priv->target_state >= GST_STATE_PAUSED;
3619 target = _get_target_element (scenario, action);
3620 REPORT_UNLESS (target, err, "No element found.");
3622 g_strdup (gst_structure_get_string (action->structure, "file-name"));
3623 REPORT_UNLESS (file_name, err, "Missing file-name property.");
3625 structure_get_uint64_permissive (action->structure, "offset", &offset);
3626 structure_get_uint64_permissive (action->structure, "size", &size);
3628 f = g_file_new_for_path (file_name);
3629 stream = G_INPUT_STREAM (g_file_read (f, NULL, &error));
3630 REPORT_UNLESS (!error, err, "Could not open file for action. Error: %s",
3634 read = g_input_stream_skip (stream, offset, NULL, &error);
3635 REPORT_UNLESS (!error, err, "Could not skip to offset. Error: %s",
3637 REPORT_UNLESS (read == offset, err,
3638 "Could not skip to offset, only skipped: %" G_GUINT64_FORMAT, read);
3643 g_file_query_info (f, G_FILE_ATTRIBUTE_STANDARD_SIZE,
3644 G_FILE_QUERY_INFO_NONE, NULL, &error);
3646 REPORT_UNLESS (!error, err, "Could not query file size. Error: %s",
3648 size = g_file_info_get_size (finfo);
3651 file_contents = g_malloc (size);
3652 read = g_input_stream_read (stream, file_contents, size, NULL, &error);
3653 REPORT_UNLESS (!error, err, "Could not read input file. Error: %s",
3655 REPORT_UNLESS (read == size, err,
3656 "Could read enough data, only read: %" G_GUINT64_FORMAT, read);
3658 buffer = gst_buffer_new_wrapped (file_contents, size);
3659 file_contents = NULL;
3662 const GValue *caps_value;
3663 GstCaps *caps = NULL;
3664 caps_value = gst_structure_get_value (action->structure, "caps");
3666 if (G_VALUE_HOLDS_STRING (caps_value)) {
3667 caps = gst_caps_from_string (g_value_get_string (caps_value));
3668 REPORT_UNLESS (caps, err, "Invalid caps string: %s",
3669 g_value_get_string (caps_value));
3671 caps = gst_caps_copy (gst_value_get_caps (caps_value));
3674 REPORT_UNLESS (caps, err, "Could not get caps value");
3675 g_object_set (target, "caps", caps, NULL);
3676 gst_caps_unref (caps);
3680 /* We temporarily override the peer pad chain function to finish the action
3681 * once the buffer chain actually ends. */
3682 appsrc_pad = gst_element_get_static_pad (target, "src");
3683 peer_pad = gst_pad_get_peer (appsrc_pad);
3684 REPORT_UNLESS (peer_pad, err, "Action failed, pad not linked");
3686 wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
3688 /* Keep the action alive until set done is called. */
3689 gst_validate_action_ref (action);
3691 g_signal_emit_by_name (target, "push-buffer", buffer, &push_buffer_ret);
3692 gst_buffer_unref (buffer);
3693 REPORT_UNLESS (push_buffer_ret == GST_FLOW_OK, err,
3694 "push-buffer signal failed in action.");
3697 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
3699 gst_validate_printf (NULL,
3700 "Pipeline is not ready to push buffers, interlacing appsrc-push action...\n");
3701 res = GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING;
3704 gst_clear_object (&target);
3705 gst_clear_object (&appsrc_pad);
3706 gst_clear_object (&peer_pad);
3707 g_clear_pointer (&file_name, g_free);
3708 g_clear_pointer (&file_contents, g_free);
3709 g_clear_error (&error);
3710 g_clear_object (&f);
3711 g_clear_object (&finfo);
3712 g_clear_object (&stream);
3717 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3722 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
3727 target = _get_target_element (scenario, action);
3728 if (target == NULL) {
3729 gchar *structure_string = gst_structure_to_string (action->structure);
3730 GST_VALIDATE_REPORT_ACTION (scenario, action,
3731 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3733 g_free (structure_string);
3734 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3737 g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
3738 if (eos_ret != GST_FLOW_OK) {
3739 gchar *structure_string = gst_structure_to_string (action->structure);
3740 GST_VALIDATE_REPORT_ACTION (scenario, action,
3741 SCENARIO_ACTION_EXECUTION_ERROR,
3742 "Failed to emit end-of-stream signal for action: %s", structure_string);
3743 g_free (structure_string);
3744 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3747 gst_object_unref (target);
3748 return GST_VALIDATE_EXECUTE_ACTION_OK;
3752 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
3756 gboolean reset_time = TRUE;
3758 target = _get_target_element (scenario, action);
3759 if (target == NULL) {
3760 gchar *structure_string = gst_structure_to_string (action->structure);
3761 GST_VALIDATE_REPORT_ACTION (scenario, action,
3762 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
3764 g_free (structure_string);
3765 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3768 gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
3770 event = gst_event_new_flush_start ();
3771 if (!gst_element_send_event (target, event)) {
3772 GST_VALIDATE_REPORT_ACTION (scenario, action,
3773 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_START event was not handled");
3774 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3777 event = gst_event_new_flush_stop (reset_time);
3778 if (!gst_element_send_event (target, event)) {
3779 GST_VALIDATE_REPORT_ACTION (scenario, action,
3780 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_STOP event was not handled");
3781 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3784 return GST_VALIDATE_EXECUTE_ACTION_OK;
3787 static GstValidateExecuteActionReturn
3788 _execute_disable_plugin (GstValidateScenario * scenario,
3789 GstValidateAction * action)
3792 const gchar *plugin_name;
3794 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
3796 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
3798 if (plugin == NULL) {
3799 GST_VALIDATE_REPORT_ACTION (scenario, action,
3800 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find plugin to disable: %s",
3803 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3806 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
3807 gst_registry_remove_plugin (gst_registry_get (), plugin);
3809 return GST_VALIDATE_EXECUTE_ACTION_OK;
3813 gst_validate_action_setup_repeat (GstValidateScenario * scenario,
3814 GstValidateAction * action)
3817 gchar *error = NULL;
3818 gint repeat, position, i;
3820 if (!gst_structure_has_field (action->structure, "repeat"))
3823 if (gst_structure_get_int (action->structure, "repeat", &repeat))
3826 if (gst_structure_get_double (action->structure, "repeat",
3827 (gdouble *) & repeat))
3830 repeat_expr = gst_validate_replace_variables_in_string (action,
3831 scenario->priv->vars, gst_structure_get_string (action->structure,
3832 "repeat"), GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3834 gst_validate_error_structure (action, "Invalid value for 'repeat'");
3838 repeat = gst_validate_utils_parse_expression (repeat_expr, _set_variable_func,
3841 gst_validate_error_structure (action, "Invalid value for 'repeat': %s",
3846 g_free (repeat_expr);
3849 gst_structure_remove_field (action->structure, "repeat");
3850 gst_structure_remove_field (action->priv->main_structure, "repeat");
3853 GST_VALIDATE_ACTION_N_REPEATS (action) = repeat;
3855 position = g_list_index (scenario->priv->actions, action);
3856 g_assert (position >= 0);
3857 for (i = 1; i < repeat; i++) {
3858 GstValidateAction *copy = _action_copy (action);
3861 scenario->priv->actions =
3862 g_list_insert (scenario->priv->actions, copy, position + i);
3868 static GstValidateExecuteActionReturn
3869 gst_validate_action_default_prepare_func (GstValidateAction * action)
3873 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3874 GstValidateActionType *type = gst_validate_get_action_type (action->type);
3875 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3877 _update_well_known_vars (scenario);
3878 if (!gst_validate_action_setup_repeat (scenario, action))
3881 if (GST_VALIDATE_ACTION_N_REPEATS (action))
3882 gst_structure_set (scenario->priv->vars,
3883 GST_VALIDATE_ACTION_RANGE_NAME (action) ?
3884 GST_VALIDATE_ACTION_RANGE_NAME (action) : "repeat", G_TYPE_INT,
3885 action->repeat, NULL);
3886 gst_validate_structure_resolve_variables (action, action->structure,
3887 scenario->priv->vars, GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_ALL);
3888 for (i = 0; type->parameters[i].name; i++) {
3889 if (type->parameters[i].types
3890 && g_str_has_suffix (type->parameters[i].types, "(GstClockTime)"))
3891 gst_validate_action_get_clocktime (scenario, action,
3892 type->parameters[i].name, &tmp);
3897 gst_clear_mini_object ((GstMiniObject **) & type);
3899 gst_object_unref (scenario);
3903 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
3907 static GstValidateExecuteActionReturn
3908 gst_validate_set_property_prepare_func (GstValidateAction * action)
3910 action->priv->optional = gst_structure_has_field_typed (action->structure,
3911 "on-all-instances", G_TYPE_BOOLEAN);
3913 return gst_validate_action_default_prepare_func (action);
3917 add_gvalue_to_list_as_struct (gpointer source, GList * list, const GValue * v)
3919 if (G_VALUE_HOLDS_STRING (v)) {
3920 GstStructure *structure =
3921 gst_structure_new_from_string (g_value_get_string (v));
3924 gst_validate_error_structure (source, "Invalid structure: %s",
3925 g_value_get_string (v));
3927 return g_list_append (list, structure);
3930 if (GST_VALUE_HOLDS_STRUCTURE (v))
3931 return g_list_append (list,
3932 gst_structure_copy (gst_value_get_structure (v)));
3935 gst_validate_error_structure (source, "Expected a string or a structure,"
3936 " got %s instead", gst_value_serialize (v));
3941 gst_validate_utils_get_structures (gpointer source,
3942 GstStructure * str, const gchar * fieldname)
3946 const GValue *value = gst_structure_get_value (str, fieldname);
3951 if (G_VALUE_HOLDS_STRING (value) || GST_VALUE_HOLDS_STRUCTURE (value))
3952 return add_gvalue_to_list_as_struct (source, res, value);
3954 if (!GST_VALUE_HOLDS_LIST (value) && !GST_VALUE_HOLDS_ARRAY (value)) {
3955 g_error ("%s must have type list of structure/string (or a string), "
3956 "e.g. %s={ [struct1, a=val1], [struct2, a=val2] }, got: \"%s\" in %s",
3957 fieldname, fieldname, gst_value_serialize (value),
3958 gst_structure_to_string (str));
3963 GST_VALUE_HOLDS_LIST (value) ? gst_value_list_get_size (value) :
3964 gst_value_array_get_size (value);
3965 for (i = 0; i < size; i++)
3967 add_gvalue_to_list_as_struct (source, res,
3968 GST_VALUE_HOLDS_LIST (value) ?
3969 gst_value_list_get_value (value, i) :
3970 gst_value_array_get_value (value, i));
3975 static GstValidateAction *
3976 gst_validate_create_subaction (GstValidateScenario * scenario,
3977 GstStructure * lvariables, GstValidateAction * action,
3978 GstStructure * nstruct, gint it, gint max)
3980 GstValidateAction *subaction;
3981 GstValidateActionType *action_type =
3982 _find_action_type (gst_structure_get_name (nstruct));
3985 gst_validate_error_structure (action,
3986 "Unknown action type: '%s'", gst_structure_get_name (nstruct));
3987 subaction = gst_validate_action_new (scenario, action_type, nstruct, FALSE);
3988 GST_VALIDATE_ACTION_RANGE_NAME (subaction) =
3989 GST_VALIDATE_ACTION_RANGE_NAME (action);
3990 GST_VALIDATE_ACTION_FILENAME (subaction) =
3991 g_strdup (GST_VALIDATE_ACTION_FILENAME (action));
3992 GST_VALIDATE_ACTION_DEBUG (subaction) =
3993 g_strdup (GST_VALIDATE_ACTION_DEBUG (action));
3994 GST_VALIDATE_ACTION_LINENO (subaction) = GST_VALIDATE_ACTION_LINENO (action);
3995 subaction->repeat = it;
3996 subaction->priv->subaction_level = action->priv->subaction_level + 1;
3997 GST_VALIDATE_ACTION_N_REPEATS (subaction) = max;
3998 gst_validate_structure_resolve_variables (subaction, subaction->structure,
4000 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_LOCAL_ONLY |
4001 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_FAILURE |
4002 GST_VALIDATE_STRUCTURE_RESOLVE_VARIABLES_NO_EXPRESSION);
4003 gst_structure_free (nstruct);
4008 static GstValidateExecuteActionReturn
4009 gst_validate_foreach_prepare (GstValidateAction * action)
4012 gint min = 0, max = 1, step = 1;
4013 const GValue *it_array = NULL;
4014 GstValidateScenario *scenario;
4015 GList *actions, *tmp;
4017 scenario = gst_validate_action_get_scenario (action);
4018 g_assert (scenario);
4019 _update_well_known_vars (scenario);
4020 gst_validate_action_setup_repeat (scenario, action);
4022 GST_VALIDATE_ACTION_RANGE_NAME (action) = NULL;
4023 gst_structure_foreach (action->structure,
4024 (GstStructureForeachFunc) _foreach_find_iterator, action);
4026 /* Allow using the repeat field here too */
4027 if (!GST_VALIDATE_ACTION_RANGE_NAME (action)
4028 && !GST_VALIDATE_ACTION_N_REPEATS (action))
4029 gst_validate_error_structure (action, "Missing range specifier field.");
4031 if (GST_VALIDATE_ACTION_RANGE_NAME (action)) {
4032 const GValue *it_value = gst_structure_get_value (action->structure,
4033 GST_VALIDATE_ACTION_RANGE_NAME (action));
4035 if (GST_VALUE_HOLDS_INT_RANGE (it_value)) {
4036 min = gst_value_get_int_range_min (it_value);
4037 max = gst_value_get_int_range_max (it_value);
4038 step = gst_value_get_int_range_step (it_value);
4040 if (min % step != 0)
4041 gst_validate_error_structure (action,
4042 "Range min[%d] must be a multiple of step[%d].", min, step);
4044 if (max % step != 0)
4045 gst_validate_error_structure (action,
4046 "Range max[%d] must be a multiple of step[%d].", max, step);
4048 it_array = it_value;
4049 max = gst_value_array_get_size (it_array);
4052 min = action->repeat;
4053 max = action->repeat + 1;
4056 actions = gst_validate_utils_get_structures (action, action->structure,
4058 i = g_list_index (scenario->priv->actions, action);
4059 for (it = min; it < max; it = it + step) {
4060 GstStructure *lvariables = gst_structure_new_empty ("vars");
4063 gst_structure_set_value (lvariables,
4064 GST_VALIDATE_ACTION_RANGE_NAME (action),
4065 gst_value_array_get_value (it_array, it));
4067 for (tmp = actions; tmp; tmp = tmp->next) {
4068 scenario->priv->actions = g_list_insert (scenario->priv->actions,
4069 gst_validate_create_subaction (scenario, lvariables, action,
4070 gst_structure_copy (tmp->data), it, max), i++);
4073 g_list_free_full (actions, (GDestroyNotify) gst_structure_free);
4075 scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
4076 gst_structure_remove_field (action->structure, "actions");
4078 gst_object_unref (scenario);
4079 return GST_VALIDATE_EXECUTE_ACTION_DONE;
4083 _check_structure_has_expected_value (GQuark field_id, const GValue * value,
4084 GstStructure * message_struct)
4086 const GValue *v = gst_structure_id_get_value (message_struct, field_id);
4089 gst_structure_set (message_struct, "__validate_has_expected_values",
4090 G_TYPE_BOOLEAN, FALSE, NULL);
4094 if (gst_value_compare (value, v) != GST_VALUE_EQUAL) {
4095 gst_structure_set (message_struct, "__validate_has_expected_values",
4096 G_TYPE_BOOLEAN, FALSE, NULL);
4100 gst_structure_set (message_struct, "__validate_has_expected_values",
4101 G_TYPE_BOOLEAN, TRUE, NULL);
4107 _check_waiting_for_message (GstValidateScenario * scenario,
4108 GstMessage * message)
4110 GstStructure *expected_values = NULL;
4111 GstValidateScenarioPrivate *priv = scenario->priv;
4112 const gchar *message_type;
4114 if (!priv->wait_message_action) {
4115 GST_LOG_OBJECT (scenario, "Not waiting for message");
4119 message_type = gst_structure_get_string (priv->wait_message_action->structure,
4122 if (g_strcmp0 (message_type, GST_MESSAGE_TYPE_NAME (message)))
4125 GST_LOG_OBJECT (scenario, " Waiting for %s and got %s", message_type,
4126 GST_MESSAGE_TYPE_NAME (message));
4128 gst_structure_get (priv->wait_message_action->structure, "expected-values",
4129 GST_TYPE_STRUCTURE, &expected_values, NULL);
4130 if (expected_values) {
4131 gboolean res = FALSE;
4132 GstStructure *message_struct =
4133 (GstStructure *) gst_message_get_structure (message);
4136 message_struct ? gst_structure_copy (message_struct) : NULL;
4137 if (!message_struct) {
4138 GST_DEBUG_OBJECT (scenario,
4139 "Waiting for %" GST_PTR_FORMAT " but message has no structure.",
4140 priv->wait_message_action->structure);
4144 gst_structure_set (message_struct, "__validate_has_expected_values",
4145 G_TYPE_BOOLEAN, FALSE, NULL);
4146 gst_structure_foreach (expected_values,
4147 (GstStructureForeachFunc) _check_structure_has_expected_value,
4150 if (!gst_structure_get_boolean (message_struct,
4151 "__validate_has_expected_values", &res) || !res) {
4156 gst_validate_action_set_done (priv->wait_message_action);
4157 _add_execute_actions_gsource (scenario);
4161 streams_list_contain (GList * streams, const gchar * stream_id)
4165 for (l = streams; l; l = g_list_next (l)) {
4166 GstStream *s = l->data;
4168 if (!g_strcmp0 (s->stream_id, stream_id))
4176 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
4177 GstElement * pipeline)
4179 GstValidateScenarioPrivate *priv = scenario->priv;
4181 GstClockTime min_latency;
4183 query = gst_query_new_latency ();
4184 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
4185 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4186 "Failed to perform LATENCY query");
4187 gst_query_unref (query);
4191 gst_query_parse_latency (query, NULL, &min_latency, NULL);
4192 gst_query_unref (query);
4193 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
4194 " max allowed: %" GST_TIME_FORMAT,
4195 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
4197 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
4198 min_latency > priv->max_latency) {
4199 GST_VALIDATE_REPORT (scenario, CONFIG_LATENCY_TOO_HIGH,
4200 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
4201 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
4202 GST_TIME_ARGS (priv->max_latency));
4207 gst_validate_scenario_is_flush_seeking (GstValidateScenario * scenario)
4209 GstValidateSeekInformation *seekinfo = scenario->priv->current_seek;
4214 if (!(seekinfo->flags & GST_SEEK_FLAG_FLUSH))
4217 return seekinfo->action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4221 gst_validate_scenario_reset (GstValidateScenario * scenario)
4223 /* Reset sink information */
4224 SCENARIO_LOCK (scenario);
4225 g_list_foreach (scenario->priv->sinks, (GFunc) _reset_sink_information, NULL);
4226 /* Reset current seek */
4227 scenario->priv->current_seek = NULL;
4228 scenario->priv->current_seqnum = GST_SEQNUM_INVALID;
4229 SCENARIO_UNLOCK (scenario);
4234 GstValidateScenario *scenario;
4235 GstMessage *message;
4239 message_data_free (MessageData * d)
4241 gst_message_unref (d->message);
4242 gst_object_unref (d->scenario);
4248 handle_bus_message (MessageData * d)
4250 gboolean is_error = FALSE;
4251 GstMessage *message = d->message;
4252 GstValidateScenario *scenario = d->scenario;
4253 GstValidateScenarioPrivate *priv = scenario->priv;
4254 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
4257 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
4258 return G_SOURCE_REMOVE;
4261 GST_DEBUG_OBJECT (scenario, "message %" GST_PTR_FORMAT, message);
4263 switch (GST_MESSAGE_TYPE (message)) {
4264 case GST_MESSAGE_ASYNC_DONE:
4265 if (!gst_validate_scenario_is_flush_seeking (scenario) &&
4266 priv->needs_async_done) {
4267 priv->needs_async_done = FALSE;
4268 if (priv->actions && _action_sets_state (priv->actions->data)
4269 && !priv->changing_state)
4270 gst_validate_action_set_done (priv->actions->data);
4273 if (priv->needs_playback_parsing) {
4274 priv->needs_playback_parsing = FALSE;
4275 if (!gst_validate_parse_next_action_playback_time (scenario))
4276 return G_SOURCE_REMOVE;
4278 _add_execute_actions_gsource (scenario);
4280 case GST_MESSAGE_STATE_CHANGED:
4282 GstState old_state, state, pending_state;
4283 gboolean reached_state;
4285 if (!pipeline || GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline))
4288 gst_message_parse_state_changed (message, &old_state, &state,
4291 reached_state = pending_state == GST_STATE_VOID_PENDING;
4293 if (old_state == GST_STATE_PAUSED && state == GST_STATE_READY)
4294 gst_validate_scenario_reset (scenario);
4296 if (reached_state && gst_validate_scenario_is_flush_seeking (scenario))
4297 gst_validate_action_set_done (priv->current_seek->action);
4299 if (priv->changing_state && priv->target_state == state) {
4300 priv->changing_state = FALSE;
4301 if (priv->actions && _action_sets_state (priv->actions->data)
4303 gst_validate_action_set_done (priv->actions->data);
4306 if (old_state == GST_STATE_READY && state == GST_STATE_PAUSED)
4307 _add_execute_actions_gsource (scenario);
4309 /* GstBin only send a new latency message when reaching PLAYING if
4310 * async-handling=true so check the latency manually. */
4311 if (state == GST_STATE_PLAYING)
4312 gst_validate_scenario_check_latency (scenario, pipeline);
4315 case GST_MESSAGE_ERROR:
4319 case GST_MESSAGE_EOS:
4321 GstValidateAction *stop_action;
4322 GstValidateActionType *stop_action_type;
4325 if (!is_error && priv->ignore_eos) {
4326 GST_INFO_OBJECT (scenario, "Got EOS but ignoring it!");
4330 if (is_error && priv->allow_errors) {
4331 GST_INFO_OBJECT (scenario, "Got error but ignoring it!");
4332 if (scenario->priv->needs_async_done || scenario->priv->changing_state) {
4334 if (scenario->priv->actions) {
4335 GstValidateAction *act =
4336 gst_validate_action_ref (scenario->priv->actions->data);
4338 GST_VALIDATE_REPORT_ACTION (scenario, act,
4339 SCENARIO_ACTION_EXECUTION_ERROR,
4340 "Error message happened while executing action");
4341 gst_validate_action_set_done (act);
4343 gst_validate_action_unref (act);
4346 scenario->priv->needs_async_done = scenario->priv->changing_state =
4352 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
4354 /* gst_validate_action_set_done() does not finish the action
4355 * immediately. Instead, it posts a task to the main thread to do most
4356 * of the work in _action_set_done().
4358 * While the EOS handling lock guarantees that if an action had to call
4359 * gst_validate_action_set_done() it has done so, it does not guarantee
4360 * that _action_set_done() has been called.
4362 * Is it possible that this handler is run before _action_set_done(), so
4363 * we check at this point for actions that have a pending_set_done and
4364 * call it before continuing. */
4365 GList *actions = g_list_copy (priv->actions);
4367 for (i = actions; i; i = i->next) {
4368 GstValidateAction *action = (GstValidateAction *) i->data;
4369 if (action->priv->pending_set_done)
4370 _action_set_done (action);
4372 g_list_free (actions);
4376 priv->got_eos = TRUE;
4377 if (priv->wait_message_action) {
4379 if (priv->actions && priv->actions->next) {
4380 GST_DEBUG_OBJECT (scenario,
4381 "Waiting for a message and got a next action"
4382 " to execute, letting it a chance!");
4383 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4386 /* Clear current message wait if waiting for EOS */
4387 _check_waiting_for_message (scenario, message);
4392 SCENARIO_LOCK (scenario);
4393 /* Make sure that if there is an ASYNC_DONE in the message queue, we do not
4394 take it into account */
4395 g_list_free_full (priv->seeks,
4396 (GDestroyNotify) gst_validate_seek_information_free);
4398 SCENARIO_UNLOCK (scenario);
4400 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
4402 stop_action_type = _find_action_type ("stop");
4403 s = gst_structure_new ("stop", "generated-after-eos", G_TYPE_BOOLEAN,
4404 !is_error, "generated-after-error", G_TYPE_BOOLEAN, is_error, NULL);
4405 stop_action = gst_validate_action_new (scenario, stop_action_type,
4407 gst_structure_free (s);
4408 gst_validate_execute_action (stop_action_type, stop_action);
4409 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
4411 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
4414 case GST_MESSAGE_BUFFERING:
4418 gst_message_parse_buffering (message, &percent);
4421 priv->buffering = FALSE;
4423 priv->buffering = TRUE;
4426 case GST_MESSAGE_STREAMS_SELECTED:
4429 GList *streams_selected = NULL;
4431 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
4433 gst_message_streams_selected_get_stream (message, i);
4435 streams_selected = g_list_append (streams_selected, stream);
4438 /* Is there a pending switch-track action waiting for the new streams to
4440 if (priv->pending_switch_track) {
4441 GList *expected, *l;
4442 GstValidateScenario *scenario =
4443 gst_validate_action_get_scenario (priv->pending_switch_track);
4446 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
4447 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
4449 if (g_list_length (expected) != g_list_length (streams_selected)) {
4450 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4451 SCENARIO_ACTION_EXECUTION_ERROR,
4452 "Was expecting %d selected streams but got %d",
4453 g_list_length (expected), g_list_length (streams_selected));
4457 for (l = expected; l; l = g_list_next (l)) {
4458 const gchar *stream_id = l->data;
4460 if (!streams_list_contain (streams_selected, stream_id)) {
4461 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
4462 SCENARIO_ACTION_EXECUTION_ERROR,
4463 "Stream %s has not be activated", stream_id);
4469 gst_object_unref (scenario);
4470 gst_validate_action_set_done (priv->pending_switch_track);
4471 priv->pending_switch_track = NULL;
4474 g_list_free_full (streams_selected, gst_object_unref);
4477 case GST_MESSAGE_LATENCY:
4478 gst_validate_scenario_check_latency (scenario, pipeline);
4481 case GST_MESSAGE_QOS:
4485 /* Check the maximum allowed when scenario is terminating so the report
4486 * will include the actual number of dropped buffers. */
4487 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
4489 priv->dropped = dropped;
4492 case GST_MESSAGE_APPLICATION:
4494 const GstStructure *s;
4495 s = gst_message_get_structure (message);
4496 if (gst_structure_has_name (s, "validate-segment")) {
4497 GstValidateSinkInformation *sink_info;
4499 SCENARIO_LOCK (scenario);
4501 _find_sink_information (scenario,
4502 (GstElement *) GST_MESSAGE_SRC (message));
4505 const GValue *segment_value;
4506 const GstSegment *segment;
4508 GST_DEBUG_OBJECT (scenario, "Got segment update for %s",
4509 GST_ELEMENT_NAME (sink_info->sink));
4510 sink_info->segment_seqnum = GST_MESSAGE_SEQNUM (message);
4511 segment_value = gst_structure_get_value (s, "segment");
4512 g_assert (segment_value != NULL);
4513 segment = (const GstSegment *) g_value_get_boxed (segment_value);
4514 gst_segment_copy_into (segment, &sink_info->segment);
4515 _validate_sink_information (scenario);
4517 SCENARIO_UNLOCK (scenario);
4525 gst_object_unref (pipeline);
4526 /* Check if we got the message expected by a wait action */
4527 _check_waiting_for_message (scenario, message);
4529 execute_next_action_full (scenario, message);
4531 return G_SOURCE_REMOVE;
4535 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
4537 MessageData *d = g_new0 (MessageData, 1);
4539 d->message = gst_message_ref (message);
4540 d->scenario = gst_object_ref (scenario);
4542 g_main_context_invoke_full (scenario->priv->context,
4543 G_PRIORITY_DEFAULT_IDLE,
4544 (GSourceFunc) handle_bus_message, d, (GDestroyNotify) message_data_free);
4548 _action_type_has_parameter (GstValidateActionType * atype,
4549 const gchar * paramname)
4553 if (!atype->parameters)
4556 for (i = 0; atype->parameters[i].name; i++)
4557 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
4564 gst_validate_scenario_load_structures (GstValidateScenario * scenario,
4565 GList * structures, gboolean * is_config, gchar * origin_file)
4567 gboolean ret = TRUE;
4569 GstValidateScenarioPrivate *priv = scenario->priv;
4575 GST_INFO_OBJECT (scenario, "No structures provided");
4579 for (tmp = structures; tmp; tmp = tmp->next) {
4580 GstValidateAction *action;
4581 GstValidateActionType *action_type;
4583 gboolean on_clock = FALSE;
4584 GstStructure *structure = (GstStructure *) tmp->data;
4586 type = gst_structure_get_name (structure);
4587 if (!g_strcmp0 (type, "description") || !g_strcmp0 (type, "meta")) {
4588 const gchar *pipeline_name;
4590 gst_structure_get_boolean (structure, "is-config", is_config);
4591 gst_structure_get_boolean (structure, "handles-states",
4592 &priv->handles_state);
4593 gst_structure_get_boolean (structure, "ignore-eos", &priv->ignore_eos);
4594 gst_structure_get_boolean (structure, "allow-errors",
4595 &priv->allow_errors);
4596 gst_structure_get_boolean (structure, "actions-on-idle",
4597 &priv->execute_on_idle);
4599 if (!priv->handles_state)
4600 priv->target_state = GST_STATE_PLAYING;
4602 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
4603 if (pipeline_name) {
4604 g_free (priv->pipeline_name);
4605 priv->pipeline_name = g_strdup (pipeline_name);
4608 gst_validate_utils_get_clocktime (structure, "max-latency",
4609 &priv->max_latency);
4611 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
4612 scenario->description = gst_structure_copy (structure);
4615 } else if (!(action_type = _find_action_type (type))) {
4616 if (gst_structure_has_field (structure, "optional-action-type")) {
4617 GST_INFO_OBJECT (scenario,
4618 "Action type not found %s but marked as not mandatory", type);
4622 gst_validate_error_structure (structure,
4623 "Unknown action type: '%s'", type);
4627 gst_structure_get_boolean (structure, "on-clock", &on_clock);
4628 if ((!g_strcmp0 (type, "crank-clock") || on_clock) && !priv->clock)
4629 priv->clock = GST_TEST_CLOCK (gst_test_clock_new ());
4631 if (action_type->parameters) {
4634 for (i = 0; action_type->parameters[i].name; i++) {
4635 if (action_type->parameters[i].mandatory &&
4636 gst_structure_has_field (structure,
4637 action_type->parameters[i].name) == FALSE) {
4638 gst_validate_error_structure (structure,
4639 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
4640 action_type->parameters[i].name, structure);
4646 action = gst_validate_action_new (scenario, action_type, structure, TRUE);
4647 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
4648 GST_ERROR_OBJECT (scenario, "Newly created action: %" GST_PTR_FORMAT
4649 " was in error state", structure);
4654 action->action_number = priv->num_actions++;
4656 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
4657 GST_DEBUG_OBJECT (scenario,
4658 "Unrefing action that has already been executed");
4659 gst_validate_action_unref (action);
4664 /* max-latency and max-dropped can be overridden using config */
4665 for (config = gst_validate_plugin_get_config (NULL); config;
4666 config = g_list_next (config)) {
4667 GstClockTime max_latency;
4669 gst_validate_utils_get_clocktime (config->data, "max-latency",
4671 if (GST_CLOCK_TIME_IS_VALID (max_latency))
4672 priv->max_latency = max_latency;
4674 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
4678 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
4689 gst_validate_scenario_get_include_paths (const gchar * relative_scenario)
4692 gchar **env_scenariodir;
4693 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
4695 if (relative_scenario) {
4696 gchar *relative_dir = g_path_get_dirname (relative_scenario);
4697 gchar *tmp_scenarios_path =
4698 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
4700 g_free (relative_dir);
4702 g_free (scenarios_path);
4703 scenarios_path = tmp_scenarios_path;
4707 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
4709 g_free (scenarios_path);
4711 n = env_scenariodir ? g_strv_length (env_scenariodir) : 0;
4712 env_scenariodir = g_realloc_n (env_scenariodir, n + 3, sizeof (gchar *));
4713 env_scenariodir[n] = g_build_filename (g_get_user_data_dir (),
4714 "gstreamer-" GST_API_VERSION, "validate",
4715 GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4716 env_scenariodir[n + 1] =
4717 g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION, "validate",
4718 GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4719 env_scenariodir[n + 2] = NULL;
4721 return env_scenariodir;
4725 _load_scenario_file (GstValidateScenario * scenario,
4726 gchar * scenario_file, gboolean * is_config)
4728 return gst_validate_scenario_load_structures (scenario,
4729 gst_validate_utils_structs_parse_from_filename (scenario_file,
4730 (GstValidateGetIncludePathsFunc)
4731 gst_validate_scenario_get_include_paths, NULL), is_config,
4736 gst_validate_scenario_load (GstValidateScenario * scenario,
4737 const gchar * scenario_name)
4739 gchar **scenarios = NULL;
4741 gboolean found_actions = FALSE, is_config, ret = FALSE;
4742 gchar **include_paths = gst_validate_scenario_get_include_paths (NULL);
4747 scenarios = g_strsplit (scenario_name, ":", -1);
4749 for (i = 0; scenarios[i]; i++) {
4751 gchar *lfilename = NULL, *tldir = NULL, *scenario_file = NULL;
4755 /* First check if the scenario name is not a full path to the
4756 * actual scenario */
4757 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
4758 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
4759 "Trying to load it", scenarios[i]);
4760 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config))) {
4761 scenario_file = scenarios[i];
4762 goto check_scenario;
4766 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
4767 lfilename = g_strdup (scenarios[i]);
4770 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
4772 for (include_i = 0; include_paths[include_i]; include_i++) {
4773 tldir = g_build_filename (include_paths[include_i], lfilename, NULL);
4774 if ((ret = _load_scenario_file (scenario, tldir, &is_config))) {
4775 scenario_file = tldir;
4784 /* else check scenario */
4787 gchar *scenario_dir = g_path_get_dirname (scenario_file);
4788 gchar *scenario_fname = g_path_get_basename (scenario_file);
4789 gchar **scenario_name =
4790 g_regex_split_simple ("\\.scenario", scenario_fname, 0, 0);
4792 gst_structure_set (scenario->priv->vars,
4793 "SCENARIO_DIR", G_TYPE_STRING, scenario_dir,
4794 "SCENARIO_NAME", G_TYPE_STRING, scenario_name[0],
4795 "SCENARIO_PATH", G_TYPE_STRING, scenario_file, NULL);
4797 g_free (scenario_dir);
4798 g_free (scenario_fname);
4799 g_strfreev (scenario_name);
4806 if (found_actions == TRUE)
4807 goto one_actions_scenario_max;
4809 found_actions = TRUE;
4816 g_strfreev (include_paths);
4818 g_strfreev (scenarios);
4821 gst_validate_abort ("Could not set scenario %s => EXIT\n", scenario_name);
4827 GST_ERROR ("Invalid name for scenario '%s'", GST_STR_NULL (scenario_name));
4832 one_actions_scenario_max:
4834 GST_ERROR ("You can set at most only one action scenario. "
4835 "You can have several config scenarios though (a config scenario's "
4836 "file must have is-config=true, and all its actions must be executable "
4837 "at parsing time).");
4846 gst_validate_scenario_set_property (GObject * object, guint prop_id,
4847 const GValue * value, GParamSpec * pspec)
4849 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4853 /* we assume the runner is valid as long as this scenario is,
4855 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
4856 g_value_get_object (value));
4858 case PROP_HANDLES_STATE:
4859 g_assert_not_reached ();
4861 case PROP_EXECUTE_ON_IDLE:
4862 self->priv->execute_on_idle = g_value_get_boolean (value);
4870 gst_validate_scenario_get_property (GObject * object, guint prop_id,
4871 GValue * value, GParamSpec * pspec)
4873 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
4877 /* we assume the runner is valid as long as this scenario is,
4879 g_value_take_object (value,
4880 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
4882 case PROP_HANDLES_STATE:
4883 g_value_set_boolean (value, self->priv->handles_state);
4885 case PROP_EXECUTE_ON_IDLE:
4886 g_value_set_boolean (value, self->priv->execute_on_idle);
4894 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
4896 GObjectClass *object_class = G_OBJECT_CLASS (klass);
4898 object_class->dispose = gst_validate_scenario_dispose;
4899 object_class->finalize = gst_validate_scenario_finalize;
4901 object_class->get_property = gst_validate_scenario_get_property;
4902 object_class->set_property = gst_validate_scenario_set_property;
4904 g_object_class_install_property (object_class, PROP_RUNNER,
4905 g_param_spec_object ("validate-runner", "VALIDATE Runner",
4906 "The Validate runner to report errors to",
4907 GST_TYPE_VALIDATE_RUNNER,
4908 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
4910 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
4911 g_param_spec_boolean ("handles-states", "Handles state",
4912 "True if the application should not handle the first state change. "
4913 "False if it is application responsibility",
4914 FALSE, G_PARAM_READABLE));
4916 g_object_class_install_property (object_class,
4917 PROP_EXECUTE_ON_IDLE,
4918 g_param_spec_boolean ("execute-on-idle",
4919 "Force waiting between actions",
4920 "Always execute actions on idle and do not chain them to execute as"
4921 " fast as possible. Setting this property is useful if action"
4922 " execution can lead to the addition of new sources on the same main"
4923 " loop as it provides these new GSource a chance to be dispatched"
4924 " between actions", FALSE, G_PARAM_READWRITE));
4927 * GstValidateScenario::done:
4928 * @scenario: The scenario running
4930 * Emitted once all actions have been executed
4932 scenario_signals[DONE] =
4933 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
4934 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
4937 * GstValidateScenario::action-done:
4938 * @scenario: The scenario running
4939 * @action: The #GstValidateAction that is done running
4941 * Emitted when an action is done.
4945 scenario_signals[ACTION_DONE] =
4946 g_signal_new ("action-done", G_TYPE_FROM_CLASS (klass),
4947 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
4948 GST_TYPE_VALIDATE_ACTION);
4952 gst_validate_scenario_init (GstValidateScenario * scenario)
4954 GstValidateScenarioPrivate *priv = scenario->priv =
4955 gst_validate_scenario_get_instance_private (scenario);
4957 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
4958 priv->segment_start = 0;
4959 priv->segment_stop = GST_CLOCK_TIME_NONE;
4960 priv->current_seek = NULL;
4961 priv->current_seqnum = GST_SEQNUM_INVALID;
4962 priv->action_execution_interval = 10;
4963 priv->vars = gst_structure_new_empty ("vars");
4964 priv->needs_playback_parsing = TRUE;
4965 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
4966 priv->max_latency = GST_CLOCK_TIME_NONE;
4967 priv->max_dropped = -1;
4970 g_mutex_init (&priv->lock);
4972 scenario->priv->context = g_main_context_get_thread_default ();
4973 if (!scenario->priv->context)
4974 scenario->priv->context = g_main_context_default ();
4975 g_main_context_ref (scenario->priv->context);
4979 gst_validate_scenario_dispose (GObject * object)
4981 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
4983 g_weak_ref_clear (&priv->ref_pipeline);
4986 gst_bus_remove_signal_watch (priv->bus);
4987 gst_object_unref (priv->bus);
4991 gst_object_replace ((GstObject **) & priv->clock, NULL);
4993 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
4997 gst_validate_scenario_finalize (GObject * object)
4999 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
5000 GstValidateScenarioPrivate *priv = self->priv;
5002 /* Because g_object_add_weak_pointer() is used, this MUST be on the
5004 g_assert (g_main_context_acquire (priv->context));
5005 g_main_context_release (priv->context);
5007 g_main_context_unref (priv->context);
5008 priv->context = NULL;
5010 g_list_free_full (priv->seeks,
5011 (GDestroyNotify) gst_validate_seek_information_free);
5012 g_list_free_full (priv->sinks,
5013 (GDestroyNotify) gst_validate_sink_information_free);
5014 g_list_free_full (priv->actions, (GDestroyNotify) gst_validate_action_unref);
5015 g_list_free_full (priv->non_blocking_running_actions,
5016 (GDestroyNotify) gst_validate_action_unref);
5017 g_list_free_full (priv->on_addition_actions,
5018 (GDestroyNotify) gst_validate_action_unref);
5019 g_free (priv->pipeline_name);
5020 gst_structure_free (priv->vars);
5021 if (self->description)
5022 gst_structure_free (self->description);
5023 g_mutex_clear (&priv->lock);
5025 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
5028 static void _element_added_cb (GstBin * bin, GstElement * element,
5029 GstValidateScenario * scenario);
5030 static void _element_removed_cb (GstBin * bin, GstElement * element,
5031 GstValidateScenario * scenario);
5034 iterate_children (GstValidateScenario * scenario, GstBin * bin)
5037 GValue v = G_VALUE_INIT;
5038 gboolean done = FALSE;
5039 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
5041 called = g_hash_table_new (NULL, NULL);
5042 it = gst_bin_iterate_elements (bin);
5045 switch (gst_iterator_next (it, &v)) {
5046 case GST_ITERATOR_OK:{
5047 GstElement *child = g_value_get_object (&v);
5049 if (g_hash_table_lookup (called, child) == NULL) {
5050 _element_added_cb (bin, child, scenario);
5051 g_hash_table_add (called, child);
5056 case GST_ITERATOR_RESYNC:
5057 gst_iterator_resync (it);
5059 case GST_ITERATOR_ERROR:
5060 case GST_ITERATOR_DONE:
5065 gst_iterator_free (it);
5066 g_hash_table_unref (called);
5070 should_execute_action (GstElement * element, GstValidateAction * action)
5072 return gst_validate_element_matches_target (element, action->structure);
5076 * * The element has no parent (pipeline)
5077 * * Or it's a sink*/
5079 _all_parents_are_sink (GstElement * element)
5081 if (GST_OBJECT_PARENT (element) == NULL)
5084 if (!GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK))
5087 return _all_parents_are_sink ((GstElement *) GST_OBJECT_PARENT (element));
5091 _element_removed_cb (GstBin * bin, GstElement * element,
5092 GstValidateScenario * scenario)
5094 GstValidateScenarioPrivate *priv = scenario->priv;
5096 if (GST_IS_BASE_SINK (element)) {
5097 GstValidateSinkInformation *sink_info;
5098 SCENARIO_LOCK (scenario);
5099 sink_info = _find_sink_information (scenario, element);
5101 GST_DEBUG_OBJECT (scenario, "Removing sink information for %s",
5102 GST_ELEMENT_NAME (element));
5103 priv->sinks = g_list_remove (priv->sinks, sink_info);
5104 gst_validate_sink_information_free (sink_info);
5106 SCENARIO_UNLOCK (scenario);
5111 _element_added_cb (GstBin * bin, GstElement * element,
5112 GstValidateScenario * scenario)
5116 GstValidateScenarioPrivate *priv = scenario->priv;
5118 /* Check if it's an element we track for a set-property action */
5119 SCENARIO_LOCK (scenario);
5120 tmp = priv->on_addition_actions;
5122 GstValidateAction *action = (GstValidateAction *) tmp->data;
5124 if (action->playback_time != GST_CLOCK_TIME_NONE)
5126 if (g_strcmp0 (action->type, "set-property"))
5129 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
5130 action, action->type);
5131 if (should_execute_action (element, action)) {
5132 GstValidateActionType *action_type;
5133 action_type = _find_action_type (action->type);
5134 GST_DEBUG_OBJECT (element, "Executing set-property action");
5135 if (gst_validate_execute_action (action_type, action)) {
5136 if (!gst_structure_has_field_typed (action->structure,
5137 "on-all-instances", G_TYPE_BOOLEAN)) {
5138 priv->on_addition_actions =
5139 g_list_remove_link (priv->on_addition_actions, tmp);
5140 gst_mini_object_unref (GST_MINI_OBJECT (action));
5142 tmp = priv->on_addition_actions;
5152 /* If it's a new GstBaseSink, add to list of sink information */
5153 if (GST_IS_BASE_SINK (element) && _all_parents_are_sink (element)) {
5154 GstValidateSinkInformation *sink_info =
5155 g_new0 (GstValidateSinkInformation, 1);
5156 GST_DEBUG_OBJECT (scenario, "Adding %s to list of tracked sinks",
5157 GST_ELEMENT_NAME (element));
5158 sink_info->sink = gst_object_ref (element);
5159 priv->sinks = g_list_append (priv->sinks, sink_info);
5162 SCENARIO_UNLOCK (scenario);
5164 /* If it's a bin, listen to the child */
5165 if (GST_IS_BIN (element)) {
5166 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
5168 g_signal_connect (element, "element-removed",
5169 (GCallback) _element_removed_cb, scenario);
5170 iterate_children (scenario, GST_BIN (element));
5174 static GstValidateScenario *
5175 gst_validate_scenario_new (GstValidateRunner *
5176 runner, GstElement * pipeline, gchar * scenario_name, GList * structures)
5179 GstValidateScenario *scenario =
5180 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
5185 gst_validate_scenario_load_structures (scenario, structures, &is_config,
5189 GST_LOG ("Creating scenario %s", scenario_name);
5190 if (!gst_validate_scenario_load (scenario, scenario_name)) {
5191 g_object_unref (scenario);
5197 if (scenario->priv->pipeline_name &&
5198 !g_pattern_match_simple (scenario->priv->pipeline_name,
5199 GST_OBJECT_NAME (pipeline))) {
5200 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
5201 scenario_name, scenario->priv->pipeline_name,
5202 GST_OBJECT_NAME (pipeline));
5204 gst_object_unref (scenario);
5209 gst_validate_printf (NULL,
5210 "**-> Running scenario %s on pipeline %s**\n", scenario_name,
5211 GST_OBJECT_NAME (pipeline));
5213 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
5214 if (scenario->priv->clock) {
5215 gst_element_set_clock (pipeline, GST_CLOCK_CAST (scenario->priv->clock));
5216 gst_pipeline_use_clock (GST_PIPELINE (pipeline),
5217 GST_CLOCK_CAST (scenario->priv->clock));
5219 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
5220 g_filename_display_basename (scenario_name));
5222 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
5224 g_signal_connect (pipeline, "element-removed",
5225 (GCallback) _element_removed_cb, scenario);
5227 iterate_children (scenario, GST_BIN (pipeline));
5229 scenario->priv->bus = gst_element_get_bus (pipeline);
5230 gst_bus_add_signal_watch (scenario->priv->bus);
5231 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
5234 for (config = gst_validate_plugin_get_config (NULL); config;
5235 config = config->next) {
5238 if (gst_structure_get_uint (config->data,
5239 "scenario-action-execution-interval",
5240 &scenario->priv->action_execution_interval)) {
5241 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5242 scenario->priv->action_execution_interval);
5243 if (scenario->priv->action_execution_interval > 0)
5244 scenario->priv->execute_on_idle = TRUE;
5246 } else if (gst_structure_get_int (config->data,
5247 "scenario-action-execution-interval", &interval)) {
5249 scenario->priv->action_execution_interval = (guint) interval;
5250 scenario->priv->execute_on_idle = TRUE;
5251 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
5252 scenario->priv->action_execution_interval);
5255 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
5260 if (scenario->priv->handles_state) {
5261 GST_INFO_OBJECT (scenario, "Scenario handles state."
5262 " Starting the get position source");
5263 _add_execute_actions_gsource (scenario);
5266 scenario->priv->overrides =
5267 gst_validate_override_registry_get_override_for_names
5268 (gst_validate_override_registry_get (), "scenarios", NULL);
5273 GstValidateScenario *
5274 gst_validate_scenario_from_structs (GstValidateRunner * runner,
5275 GstElement * pipeline, GList * structures, gchar * origin_file)
5277 g_return_val_if_fail (structures, NULL);
5279 return gst_validate_scenario_new (runner, pipeline, origin_file, structures);
5283 * gst_validate_scenario_factory_create:
5284 * @runner: The #GstValidateRunner to use to report issues
5285 * @pipeline: The pipeline to run the scenario on
5286 * @scenario_name: The name (or path) of the scenario to run
5288 * Returns: (transfer full): A #GstValidateScenario or NULL
5290 GstValidateScenario *
5291 gst_validate_scenario_factory_create (GstValidateRunner *
5292 runner, GstElement * pipeline, const gchar * scenario_name)
5294 return gst_validate_scenario_new (runner, pipeline, (gchar *) scenario_name,
5299 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
5301 gchar *tmp = gst_value_serialize (value);
5302 gchar *tmpcompress = g_strcompress (tmp);
5304 g_key_file_set_string (kfg->kf, kfg->group_name,
5305 g_quark_to_string (field_id), tmpcompress);
5307 g_free (tmpcompress);
5314 gst_validate_scenario_check_and_set_needs_clock_sync (GList * structures,
5315 GstStructure ** meta)
5317 gboolean needs_clock_sync = FALSE;
5320 for (tmp = structures; tmp; tmp = tmp->next) {
5321 GstStructure *_struct = (GstStructure *) tmp->data;
5322 gboolean is_meta = gst_structure_has_name (_struct, "description")
5323 || gst_structure_has_name (_struct, "meta");
5326 GstValidateActionType *type =
5327 _find_action_type (gst_structure_get_name (_struct));
5329 if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
5330 needs_clock_sync = TRUE;
5335 *meta = gst_structure_copy (_struct);
5338 if (needs_clock_sync) {
5340 gst_structure_set (*meta, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
5342 *meta = gst_structure_from_string ("description, need-clock-sync=true;",
5346 return needs_clock_sync;
5350 _parse_scenario (GFile * f, GKeyFile * kf)
5352 gboolean ret = FALSE;
5353 gchar *path = g_file_get_path (f);
5355 if (g_str_has_suffix (path, GST_VALIDATE_SCENARIO_SUFFIX)) {
5356 GstStructure *meta = NULL;
5357 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f,
5358 (GstValidateGetIncludePathsFunc)
5359 gst_validate_scenario_get_include_paths);
5361 gst_validate_scenario_check_and_set_needs_clock_sync (structures, &meta);
5362 for (tmp = structures; tmp; tmp = tmp->next)
5363 gst_structure_remove_fields (tmp->data, "__lineno__", "__filename__",
5367 KeyFileGroupName kfg;
5369 kfg.group_name = g_file_get_path (f);
5372 gst_structure_remove_fields (meta, "__lineno__", "__filename__",
5374 gst_structure_foreach (meta,
5375 (GstStructureForeachFunc) _add_description, &kfg);
5376 gst_structure_free (meta);
5378 g_key_file_set_string (kf, path, "noinfo", "nothing");
5380 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
5390 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
5392 GFileEnumerator *fenum;
5395 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
5396 G_FILE_QUERY_INFO_NONE, NULL, NULL);
5401 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
5402 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
5403 GFile *f = g_file_enumerator_get_child (fenum, info);
5405 _parse_scenario (f, kf);
5406 gst_object_unref (f);
5409 gst_object_unref (fenum);
5413 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
5414 gchar * output_file)
5420 GKeyFile *kf = NULL;
5422 const gchar *envvar;
5423 gchar **env_scenariodir = NULL;
5424 gchar *tldir = g_build_filename (g_get_user_data_dir (),
5425 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
5427 GFile *dir = g_file_new_for_path (tldir);
5430 kf = g_key_file_new ();
5431 if (num_scenarios > 0) {
5435 for (i = 0; i < num_scenarios; i++) {
5436 file = g_file_new_for_path (scenarios[i]);
5437 if (!_parse_scenario (file, kf)) {
5438 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
5440 gst_object_unref (file);
5448 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
5450 env_scenariodir = g_strsplit (envvar, ":", 0);
5452 _list_scenarios_in_dir (dir, kf);
5453 g_object_unref (dir);
5455 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
5456 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
5457 dir = g_file_new_for_path (tldir);
5458 _list_scenarios_in_dir (dir, kf);
5459 g_object_unref (dir);
5462 if (env_scenariodir) {
5465 for (i = 0; env_scenariodir[i]; i++) {
5466 dir = g_file_new_for_path (env_scenariodir[i]);
5467 _list_scenarios_in_dir (dir, kf);
5468 g_object_unref (dir);
5472 /* Hack to make it work within the development environment */
5473 dir = g_file_new_for_path ("data/scenarios");
5474 _list_scenarios_in_dir (dir, kf);
5475 g_object_unref (dir);
5478 result = g_key_file_to_data (kf, &datalength, &err);
5479 gst_validate_printf (NULL, "All scenarios available:\n%s", result);
5481 if (output_file && !err) {
5482 if (!g_file_set_contents (output_file, result, datalength, &err)) {
5483 GST_WARNING ("Error writing to file '%s'", output_file);
5489 if (env_scenariodir)
5490 g_strfreev (env_scenariodir);
5493 GST_WARNING ("Got error '%s' listing scenarios", err->message);
5494 g_clear_error (&err);
5499 g_key_file_free (kf);
5504 static GstValidateActionReturn
5505 check_last_sample_internal (GstValidateScenario * scenario,
5506 GstValidateAction * action, GstElement * sink)
5511 const gchar *target_sum;
5512 guint64 frame_number;
5513 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
5514 GstVideoTimeCodeMeta *tc_meta;
5516 g_object_get (sink, "last-sample", &sample, NULL);
5517 if (sample == NULL) {
5518 GST_VALIDATE_REPORT_ACTION (scenario, action,
5519 SCENARIO_ACTION_EXECUTION_ERROR,
5520 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
5521 " 'last-sample' property is NULL"
5522 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
5525 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5528 buffer = gst_sample_get_buffer (sample);
5529 target_sum = gst_structure_get_string (action->structure, "checksum");
5533 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
5534 GST_VALIDATE_REPORT_ACTION (scenario, action,
5535 SCENARIO_ACTION_EXECUTION_ERROR,
5536 "Last sample buffer could not be mapped, action can't run.");
5537 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5540 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
5541 gst_buffer_unmap (buffer, &map);
5543 if (g_strcmp0 (sum, target_sum)) {
5544 GST_VALIDATE_REPORT_ACTION (scenario, action,
5545 SCENARIO_ACTION_EXECUTION_ERROR,
5546 "Last buffer checksum '%s' is different than the expected one: '%s'",
5549 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5556 if (!gst_structure_get_uint64 (action->structure, "timecode-frame-number",
5560 if (!gst_structure_get_int (action->structure, "timecode-frame-number",
5562 GST_VALIDATE_REPORT_ACTION (scenario, action,
5563 SCENARIO_ACTION_EXECUTION_ERROR,
5564 "The 'checksum' or 'time-code-frame-number' parameters of the "
5565 "`check-last-sample` action type needs to be specified, none found");
5567 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5571 frame_number = (guint64) iframe_number;
5574 tc_meta = gst_buffer_get_video_time_code_meta (buffer);
5576 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5577 "Could not \"check-last-sample\" as the buffer doesn't contain a TimeCode"
5579 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5583 if (gst_video_time_code_frames_since_daily_jam (&tc_meta->tc) != frame_number) {
5584 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
5585 "Last buffer frame number '%" G_GINT64_FORMAT
5586 "' is different than the expected one: '%" G_GINT64_FORMAT "'",
5587 gst_video_time_code_frames_since_daily_jam (&tc_meta->tc),
5589 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5593 gst_sample_unref (sample);
5598 sink_last_sample_notify_cb (GstElement * sink, GParamSpec * arg G_GNUC_UNUSED,
5599 GstValidateAction * action)
5601 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5604 GST_VALIDATE_REPORT_ACTION (scenario, action,
5605 SCENARIO_ACTION_EXECUTION_ERROR,
5606 "No pipeline anymore, can't check last sample");
5610 check_last_sample_internal (scenario, action, sink);
5611 gst_object_unref (scenario);
5614 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5616 gst_validate_action_set_done (action);
5617 gst_validate_action_unref (action);
5620 static GstValidateExecuteActionReturn
5621 _check_last_sample_value (GstValidateScenario * scenario,
5622 GstValidateAction * action, GstElement * sink)
5626 /* Connect before checking last sample to avoid a race where
5627 * the sample is set between the time we connect and the time
5628 * the time we get it */
5629 g_signal_connect (sink, "notify::last-sample",
5630 G_CALLBACK (sink_last_sample_notify_cb),
5631 gst_validate_action_ref (action));
5633 g_object_get (sink, "last-sample", &sample, NULL);
5635 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
5636 gst_sample_unref (sample);
5637 gst_validate_action_unref (action);
5639 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
5642 return check_last_sample_internal (scenario, action, sink);
5646 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
5647 const gchar * fname, GstCaps * sinkpad_caps)
5651 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
5652 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
5657 if (paramspec->value_type != GST_TYPE_SAMPLE)
5660 if (!name && !fname && !sinkpad_caps)
5663 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
5667 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
5673 sinkpad = gst_element_get_static_pad (sink, "sink");
5677 tmpcaps = gst_pad_get_current_caps (sinkpad);
5679 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
5681 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
5682 gst_caps_unref (tmpcaps);
5686 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
5692 static GstValidateExecuteActionReturn
5693 _execute_check_last_sample (GstValidateScenario * scenario,
5694 GstValidateAction * action)
5697 GValue data = { 0, };
5698 gboolean done = FALSE;
5699 GstCaps *caps = NULL;
5700 GstElement *sink = NULL, *tmpelement;
5701 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
5703 gst_structure_get_string (action->structure, "sink-factory-name"),
5704 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
5705 DECLARE_AND_GET_PIPELINE (scenario, action);
5708 caps = gst_caps_from_string (caps_str);
5713 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
5715 switch (gst_iterator_next (it, &data)) {
5716 case GST_ITERATOR_OK:
5717 tmpelement = g_value_get_object (&data);
5718 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
5721 if (!gst_object_has_as_ancestor (GST_OBJECT (tmpelement),
5722 GST_OBJECT (sink))) {
5723 gchar *tmp = gst_structure_to_string (action->structure);
5725 GST_VALIDATE_REPORT_ACTION (scenario, action,
5726 SCENARIO_ACTION_EXECUTION_ERROR,
5727 "Could not \"check-last-sample\" as several elements were found "
5728 "from describing string: '%s' (%s and %s match)", tmp,
5729 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
5734 gst_object_unref (sink);
5737 sink = gst_object_ref (tmpelement);
5739 g_value_reset (&data);
5741 case GST_ITERATOR_RESYNC:
5742 gst_iterator_resync (it);
5743 g_clear_object (&sink);
5745 case GST_ITERATOR_ERROR:
5747 case GST_ITERATOR_DONE:
5752 gst_iterator_free (it);
5754 gst_caps_unref (caps);
5757 GST_VALIDATE_REPORT_ACTION (scenario, action,
5758 SCENARIO_ACTION_EXECUTION_ERROR,
5759 "Could not \"check-last-sample\" as no sink was found from description: '%"
5760 GST_PTR_FORMAT "'", action->structure);
5765 g_clear_object (&pipeline);
5766 return _check_last_sample_value (scenario, action, sink);
5769 g_clear_object (&sink);
5770 g_clear_object (&pipeline);
5771 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5774 static GstPadProbeReturn
5775 _check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info,
5776 GstValidateAction * action)
5778 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
5779 GstClockTime target_running_time = GST_CLOCK_TIME_NONE;
5780 gint count_bufs = 0;
5782 gst_validate_action_get_clocktime (scenario, action,
5783 "running-time", &target_running_time);
5784 if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
5785 if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info)))
5786 gst_structure_set (action->structure, "__priv_seen_event", G_TYPE_BOOLEAN,
5788 else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT
5789 && GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
5790 const GstSegment *segment = NULL;
5792 gst_event_parse_segment (info->data, &segment);
5793 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5796 } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))
5797 && gst_structure_has_field_typed (action->structure, "__priv_seen_event",
5799 GstSegment *segment = NULL;
5801 if (GST_CLOCK_TIME_IS_VALID (target_running_time)) {
5802 GstClockTime running_time;
5804 gst_structure_get (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
5807 gst_segment_to_running_time (segment, GST_FORMAT_TIME,
5808 GST_BUFFER_TIMESTAMP (info->data));
5810 if (running_time < target_running_time)
5814 gst_structure_get_int (action->structure, "__priv_count_bufs", &count_bufs);
5815 if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info),
5816 GST_BUFFER_FLAG_DELTA_UNIT)) {
5817 if (count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) {
5818 GST_VALIDATE_REPORT_ACTION (scenario, action,
5819 SCENARIO_ACTION_EXECUTION_ERROR,
5820 "Did not receive a key frame after requested one, "
5821 "at running_time %" GST_TIME_FORMAT " (with a %i "
5822 "frame tolerance)", GST_TIME_ARGS (target_running_time),
5823 NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE);
5825 gst_validate_action_set_done (action);
5826 gst_object_unref (scenario);
5827 return GST_PAD_PROBE_REMOVE;
5830 gst_structure_set (action->structure, "__priv_count_bufs", G_TYPE_INT,
5831 count_bufs++, NULL);
5833 GST_INFO_OBJECT (pad,
5834 "Properly got keyframe after \"force-keyframe\" event "
5835 "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))",
5836 GST_TIME_ARGS (target_running_time), count_bufs);
5838 gst_structure_remove_fields (action->structure, "__priv_count_bufs",
5839 "__priv_segment", "__priv_seen_event", NULL);
5840 gst_validate_action_set_done (action);
5841 gst_object_unref (scenario);
5842 return GST_PAD_PROBE_REMOVE;
5846 gst_object_unref (scenario);
5848 return GST_PAD_PROBE_OK;
5851 static GstValidateExecuteActionReturn
5852 _execute_crank_clock (GstValidateScenario * scenario,
5853 GstValidateAction * action)
5855 GstClockTime expected_diff, expected_time;
5856 GstClockTime prev_time =
5857 gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
5859 if (!gst_test_clock_crank (scenario->priv->clock)) {
5860 GST_VALIDATE_REPORT_ACTION (scenario, action,
5861 SCENARIO_ACTION_EXECUTION_ERROR, "Cranking clock failed");
5863 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5866 if (gst_validate_action_get_clocktime (scenario, action,
5867 "expected-elapsed-time", &expected_diff)) {
5868 GstClockTime elapsed =
5869 gst_clock_get_time (GST_CLOCK (scenario->priv->clock)) - prev_time;
5871 if (expected_diff != elapsed) {
5872 GST_VALIDATE_REPORT_ACTION (scenario, action,
5873 SCENARIO_ACTION_EXECUTION_ERROR,
5874 "Elapsed time during test clock cranking different than expected,"
5875 " waited for %" GST_TIME_FORMAT " instead of the expected %"
5876 GST_TIME_FORMAT, GST_TIME_ARGS (elapsed),
5877 GST_TIME_ARGS (expected_diff));
5879 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5883 if (gst_validate_action_get_clocktime (scenario, action, "expected-time",
5885 GstClockTime time = gst_clock_get_time (GST_CLOCK (scenario->priv->clock));
5887 if (expected_time != time) {
5888 GST_VALIDATE_REPORT_ACTION (scenario, action,
5889 SCENARIO_ACTION_EXECUTION_ERROR,
5890 "Clock time after cranking different than expected,"
5891 " got %" GST_TIME_FORMAT " instead of the expected %" GST_TIME_FORMAT,
5892 GST_TIME_ARGS (time), GST_TIME_ARGS (expected_time));
5894 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
5898 return GST_VALIDATE_EXECUTE_ACTION_OK;
5902 _execute_request_key_unit (GstValidateScenario * scenario,
5903 GstValidateAction * action)
5906 gboolean all_headers = FALSE;
5907 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
5908 GstEvent *event = NULL;
5909 GstQuery *segment_query;
5910 GList *targets = NULL, *tmp;
5911 GstElement *video_encoder = NULL;
5912 GstPad *pad = NULL, *encoder_srcpad = NULL;
5913 GstClockTime running_time = GST_CLOCK_TIME_NONE;
5914 GstSegment segment = { 0, };
5915 const gchar *direction = gst_structure_get_string (action->structure,
5916 "direction"), *pad_name, *srcpad_name;
5918 DECLARE_AND_GET_PIPELINE (scenario, action);
5920 if (gst_structure_get_string (action->structure, "target-element-name")) {
5921 GstElement *target = _get_target_element (scenario, action);
5925 targets = g_list_append (targets, target);
5927 if (!gst_structure_get_string (action->structure,
5928 "target-element-klass") &&
5929 !gst_structure_get_string (action->structure,
5930 "target-element-factory-name")) {
5931 gst_structure_set (action->structure, "target-element-klass",
5932 G_TYPE_STRING, "Video/Encoder", NULL);
5935 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
5939 GST_VALIDATE_REPORT_ACTION (scenario, action,
5940 SCENARIO_ACTION_EXECUTION_ERROR,
5941 "Could not find any element from action: %" GST_PTR_FORMAT,
5946 gst_validate_action_get_clocktime (scenario, action,
5947 "running-time", &running_time);
5948 gst_structure_get_boolean (action->structure, "all-headers", &all_headers);
5949 if (!gst_structure_get_uint (action->structure, "count", &count)) {
5950 gst_structure_get_int (action->structure, "count", (gint *) & count);
5952 pad_name = gst_structure_get_string (action->structure, "pad");
5953 srcpad_name = gst_structure_get_string (action->structure, "srcpad");
5955 srcpad_name = "src";
5957 for (tmp = targets; tmp; tmp = tmp->next) {
5958 video_encoder = tmp->data;
5959 encoder_srcpad = gst_element_get_static_pad (video_encoder, srcpad_name);
5960 if (!encoder_srcpad) {
5961 GST_VALIDATE_REPORT_ACTION (scenario, action,
5962 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
5967 if (g_strcmp0 (direction, "upstream") == 0) {
5968 event = gst_video_event_new_upstream_force_key_unit (running_time,
5969 all_headers, count);
5971 pad = gst_element_get_static_pad (video_encoder, srcpad_name);
5973 GST_VALIDATE_REPORT_ACTION (scenario, action,
5974 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
5979 GST_ERROR_OBJECT (encoder_srcpad, "Sending RequestKeyUnit event");
5980 gst_pad_add_probe (encoder_srcpad,
5981 GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
5982 (GstPadProbeCallback) _check_is_key_unit_cb,
5983 gst_validate_action_ref (action),
5984 (GDestroyNotify) gst_validate_action_unref);
5985 } else if (g_strcmp0 (direction, "downstream") == 0) {
5986 GstClockTime timestamp = GST_CLOCK_TIME_NONE,
5987 stream_time = GST_CLOCK_TIME_NONE;
5992 pad = gst_element_get_static_pad (video_encoder, pad_name);
5994 GST_VALIDATE_REPORT_ACTION (scenario, action,
5995 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s", pad_name);
6000 gst_validate_action_get_clocktime (scenario, action,
6001 "timestamp", ×tamp);
6003 gst_validate_action_get_clocktime (scenario, action,
6004 "stream-time", &stream_time);
6007 gst_video_event_new_downstream_force_key_unit (timestamp, stream_time,
6008 running_time, all_headers, count);
6010 gst_pad_add_probe (pad,
6011 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6012 (GstPadProbeCallback) _check_is_key_unit_cb,
6013 gst_validate_action_ref (action),
6014 (GDestroyNotify) gst_validate_action_unref);
6016 GST_VALIDATE_REPORT_ACTION (scenario, action,
6017 SCENARIO_ACTION_EXECUTION_ERROR,
6018 "request keyunit direction %s invalid (should be in"
6019 " [downstrean, upstream]", direction);
6024 gst_validate_printf (action, "Sending a \"force key unit\" event %s\n",
6027 segment_query = gst_query_new_segment (GST_FORMAT_TIME);
6028 gst_pad_query (encoder_srcpad, segment_query);
6030 gst_query_parse_segment (segment_query, &(segment.rate),
6031 &(segment.format), (gint64 *) & (segment.start),
6032 (gint64 *) & (segment.stop));
6033 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
6036 gst_pad_add_probe (encoder_srcpad,
6037 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
6038 (GstPadProbeCallback) _check_is_key_unit_cb,
6039 gst_validate_action_ref (action),
6040 (GDestroyNotify) gst_validate_action_unref);
6043 if (!gst_pad_send_event (pad, event)) {
6044 GST_VALIDATE_REPORT_ACTION (scenario, action,
6045 SCENARIO_ACTION_EXECUTION_ERROR,
6046 "Could not send \"force key unit\" event %s", direction);
6050 gst_clear_object (&pad);
6051 gst_clear_object (&encoder_srcpad);
6055 g_list_free_full (targets, gst_object_unref);
6056 gst_clear_object (&pad);
6057 gst_clear_object (&encoder_srcpad);
6062 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
6066 static GstValidateExecuteActionReturn
6067 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
6070 GstValidateScenarioPrivate *priv = scenario->priv;
6072 DECLARE_AND_GET_PIPELINE (scenario, action);
6074 bus = gst_element_get_bus (pipeline);
6075 SCENARIO_LOCK (scenario);
6076 if (priv->execute_actions_source_id) {
6077 g_source_remove (priv->execute_actions_source_id);
6078 priv->execute_actions_source_id = 0;
6080 if (scenario->priv->actions || scenario->priv->non_blocking_running_actions ||
6081 scenario->priv->on_addition_actions) {
6082 guint nb_actions = 0;
6083 gchar *actions = g_strdup (""), *tmpconcat;
6085 GList *all_actions = g_list_concat (g_list_concat (scenario->priv->actions,
6086 scenario->priv->non_blocking_running_actions),
6087 scenario->priv->on_addition_actions);
6089 for (tmp = all_actions; tmp; tmp = tmp->next) {
6090 GstValidateAction *remaining_action = (GstValidateAction *) tmp->data;
6091 GstValidateActionType *type;
6093 if (remaining_action == action)
6096 type = _find_action_type (remaining_action->type);
6098 tmpconcat = actions;
6100 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
6101 remaining_action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
6102 remaining_action->priv->optional) {
6103 gst_validate_action_unref (remaining_action);
6110 actions = g_strdup_printf ("%s\n%*s- `%s` at %s:%d", actions, 20, "",
6111 remaining_action->type,
6112 GST_VALIDATE_ACTION_FILENAME (remaining_action),
6113 GST_VALIDATE_ACTION_LINENO (remaining_action));
6114 gst_validate_action_unref (remaining_action);
6117 g_list_free (all_actions);
6118 scenario->priv->actions = NULL;
6119 scenario->priv->non_blocking_running_actions = NULL;
6120 scenario->priv->on_addition_actions = NULL;
6123 if (nb_actions > 0) {
6124 GstClockTime position = GST_CLOCK_TIME_NONE;
6126 _get_position (scenario, NULL, &position);
6127 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
6128 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
6129 ")", nb_actions, actions, GST_TIME_ARGS (position));
6133 SCENARIO_UNLOCK (scenario);
6135 gst_validate_scenario_check_dropped (scenario);
6138 gst_message_new_request_state (GST_OBJECT_CAST (scenario),
6140 gst_object_unref (bus);
6141 gst_object_unref (pipeline);
6147 _action_set_done (GstValidateAction * action)
6149 gchar *repeat_message = NULL;
6150 JsonBuilder *jbuild;
6151 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6153 if (scenario == NULL || !action->priv->pending_set_done)
6154 return G_SOURCE_REMOVE;
6156 action->priv->execution_duration =
6157 gst_util_get_timestamp () - action->priv->execution_time;
6159 jbuild = json_builder_new ();
6160 json_builder_begin_object (jbuild);
6161 json_builder_set_member_name (jbuild, "type");
6162 json_builder_add_string_value (jbuild, "action-done");
6163 json_builder_set_member_name (jbuild, "action-type");
6164 json_builder_add_string_value (jbuild, action->type);
6165 json_builder_set_member_name (jbuild, "execution-duration");
6166 json_builder_add_double_value (jbuild,
6167 ((gdouble) action->priv->execution_duration / GST_SECOND));
6168 json_builder_end_object (jbuild);
6170 gst_validate_send (json_builder_get_root (jbuild));
6171 g_object_unref (jbuild);
6173 action->priv->pending_set_done = FALSE;
6174 switch (action->priv->state) {
6175 case GST_VALIDATE_EXECUTE_ACTION_ERROR:
6176 GST_VALIDATE_REPORT_ACTION (scenario, action,
6177 SCENARIO_ACTION_EXECUTION_ERROR, "Action %s failed", action->type);
6178 case GST_VALIDATE_EXECUTE_ACTION_ASYNC:
6179 case GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS:
6180 case GST_VALIDATE_EXECUTE_ACTION_NONE:
6181 case GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED:
6182 case GST_VALIDATE_EXECUTE_ACTION_OK:
6184 scenario->priv->actions = g_list_remove (scenario->priv->actions, action);
6186 _check_scenario_is_done (scenario);
6188 if (!gst_validate_parse_next_action_playback_time (scenario)) {
6189 gst_validate_error_structure (scenario->priv->actions ? scenario->
6190 priv->actions->data : NULL,
6191 "Could not determine next action playback time!");
6194 GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
6195 " executing next", action->structure);
6199 case GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING:
6203 if (GST_VALIDATE_ACTION_N_REPEATS (action))
6205 g_strdup_printf ("[%d/%d]", action->repeat,
6206 GST_VALIDATE_ACTION_N_REPEATS (action));
6208 gst_validate_printf (NULL,
6209 "%*c⇨ Action `%s` at %s:%d done '%s' %s (duration: %" GST_TIME_FORMAT
6210 ")\n\n", (action->priv->subaction_level * 2) - 1, ' ',
6211 gst_structure_get_name (action->priv->main_structure),
6212 GST_VALIDATE_ACTION_FILENAME (action),
6213 GST_VALIDATE_ACTION_LINENO (action),
6214 gst_validate_action_return_get_name (action->priv->state),
6215 repeat_message ? repeat_message : "",
6216 GST_TIME_ARGS (action->priv->execution_duration));
6217 g_free (repeat_message);
6219 g_signal_emit (scenario, scenario_signals[ACTION_DONE], 0, action);
6220 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING)
6221 /* We took the 'scenario' reference... unreffing it now */
6222 gst_validate_action_unref (action);
6224 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_DONE;
6225 gst_validate_scenario_execute_next_or_restart_looping (scenario);
6226 gst_object_unref (scenario);
6227 return G_SOURCE_REMOVE;
6230 /* gst_validate_action_set_done:
6231 * @action: The action that is done executing
6233 * Sets @action as "done", meaning that the next action can
6237 gst_validate_action_set_done (GstValidateAction * action)
6239 GMainContext *context = action->priv->context;
6240 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
6242 action->priv->context = NULL;
6243 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING) {
6247 SCENARIO_LOCK (scenario);
6248 item = g_list_find (scenario->priv->non_blocking_running_actions, action);
6249 scenario->priv->non_blocking_running_actions =
6250 g_list_delete_link (scenario->priv->non_blocking_running_actions,
6252 SCENARIO_UNLOCK (scenario);
6256 gst_validate_action_unref (action);
6259 g_assert (!action->priv->pending_set_done);
6260 action->priv->pending_set_done = TRUE;
6262 if (scenario && scenario->priv->wait_message_action == action)
6263 scenario->priv->wait_message_action = NULL;
6264 gst_clear_object (&scenario);
6266 g_main_context_invoke_full (action->priv->context,
6267 G_PRIORITY_DEFAULT_IDLE,
6268 (GSourceFunc) _action_set_done,
6269 gst_validate_action_ref (action),
6270 (GDestroyNotify) gst_validate_action_unref);
6273 g_main_context_unref (context);
6277 * gst_validate_action_get_scenario:
6278 * @action: The action for which to retrieve the scenario
6280 * Retrieve the scenario from which @action is executed.
6282 * Returns: (transfer full): The scenario from which the action is being executed.
6284 GstValidateScenario *
6285 gst_validate_action_get_scenario (GstValidateAction * action)
6287 return g_weak_ref_get (&action->priv->scenario);
6291 * gst_validate_register_action_type:
6292 * @type_name: The name of the new action type to add
6293 * @implementer_namespace: The namespace of the implementer of the action type.
6294 * That should always be the name of the GstPlugin as
6295 * retrieved with #gst_plugin_get_name when the action type
6296 * is registered inside a plugin.
6297 * @function: (scope notified): The function to be called to execute the action
6298 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6299 * @description: A description of the new type
6300 * @flags: The #GstValidateActionTypeFlags to set on the new action type
6302 * Register a new action type to the action type system. If the action type already
6303 * exists, it will be overridden by the new definition
6305 * Returns: (transfer none): The newly created action type or the already registered action type
6306 * if it had a higher rank
6308 GstValidateActionType *
6309 gst_validate_register_action_type (const gchar * type_name,
6310 const gchar * implementer_namespace,
6311 GstValidateExecuteAction function,
6312 GstValidateActionParameter * parameters,
6313 const gchar * description, GstValidateActionTypeFlags flags)
6315 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
6316 type_name, GST_RANK_NONE, function, parameters, description,
6319 g_free (type->implementer_namespace);
6320 type->implementer_namespace = g_strdup (implementer_namespace);
6326 _free_action_types (GList * _action_types)
6328 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
6332 * gst_validate_register_action_type_dynamic:
6333 * @plugin: (allow-none): The #GstPlugin that register the action type,
6334 * or NULL for a static element.
6335 * @rank: The ranking of that implementation of the action type called
6336 * @type_name. If an action type has been registered with the same
6337 * name with a higher rank, the new implementation will not be used,
6338 * and the already registered action type is returned.
6339 * If the already registered implementation has a lower rank, the
6340 * new implementation will be used and returned.
6341 * @type_name: The name of the new action type to add
6342 * @function: (scope notified): The function to be called to execute the action
6343 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
6344 * @description: A description of the new type
6345 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
6347 * Returns: (transfer none): The newly created action type or the already registered action type
6348 * if it had a higher rank
6350 GstValidateActionType *
6351 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
6352 const gchar * type_name, GstRank rank,
6353 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
6354 const gchar * description, GstValidateActionTypeFlags flags)
6356 GstValidateActionType *tmptype;
6357 GstValidateActionType *type = gst_validate_action_type_new ();
6358 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
6359 gint n_params = is_config ? 0 : 2;
6362 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
6367 type->parameters = g_new0 (GstValidateActionParameter, n_params);
6371 memcpy (type->parameters, parameters,
6372 sizeof (GstValidateActionParameter) * (n_params));
6375 type->prepare = gst_validate_action_default_prepare_func;
6376 type->execute = function;
6377 type->name = g_strdup (type_name);
6379 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
6381 type->implementer_namespace = g_strdup ("none");
6383 type->description = g_strdup (description);
6384 type->flags = flags;
6387 if ((tmptype = _find_action_type (type_name))) {
6388 if (tmptype->rank <= rank) {
6389 action_types = g_list_remove (action_types, tmptype);
6390 type->overriden_type = tmptype;
6392 gst_mini_object_unref (GST_MINI_OBJECT (type));
6398 if (type != tmptype)
6399 action_types = g_list_append (action_types, type);
6402 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
6403 "GstValidatePluginActionTypes");
6405 plugin_action_types = g_list_prepend (plugin_action_types,
6406 gst_mini_object_ref (GST_MINI_OBJECT (type)));
6408 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
6409 plugin_action_types, (GDestroyNotify) _free_action_types);
6415 GstValidateActionType *
6416 gst_validate_get_action_type (const gchar * type_name)
6418 GstValidateActionType *type = _find_action_type (type_name);
6422 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
6428 gst_validate_list_action_types (void)
6430 return action_types;
6434 * gst_validate_print_action_types:
6435 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
6436 * @num_wanted_types: Length of @wanted_types
6438 * Prints the action types details wanted in @wanted_types
6440 * Returns: True if all types could be printed
6443 gst_validate_print_action_types (const gchar ** wanted_types,
6444 gint num_wanted_types)
6448 gboolean print_all = (num_wanted_types == 1
6449 && !g_strcmp0 (wanted_types[0], "all"));
6452 gst_validate_printf (NULL, "# GstValidate action types");
6454 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
6455 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
6456 gboolean print = print_all;
6458 if (num_wanted_types) {
6461 for (n = 0; n < num_wanted_types; n++) {
6462 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
6463 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
6474 if (print && num_wanted_types) {
6475 gst_validate_printf (atype, "\n");
6478 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
6482 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
6483 atype->implementer_namespace, atype->name, desc);
6488 if (!print_all && num_wanted_types && num_wanted_types > nfound) {
6496 * gst_validate_scenario_get_actions:
6497 * @scenario: The scenario to retrieve remaining actions for
6499 * Get remaining actions from @scenario.
6501 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
6504 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
6507 gboolean main_context_acquired;
6509 main_context_acquired = g_main_context_acquire (g_main_context_default ());
6510 g_return_val_if_fail (main_context_acquired, NULL);
6512 ret = g_list_copy_deep (scenario->priv->actions,
6513 (GCopyFunc) gst_validate_action_ref, NULL);
6515 g_main_context_release (g_main_context_default ());
6521 * gst_validate_scenario_get_target_state:
6522 * @scenario: The scenario to retrieve the current target state for
6524 * Get current target state from @scenario.
6526 * Returns: Current target state.
6529 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
6531 return scenario->priv->target_state;
6535 init_scenarios (void)
6539 register_action_types ();
6541 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
6542 const gchar *action_typename;
6543 GstStructure *plug_conf = (GstStructure *) tmp->data;
6545 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
6546 GstValidateAction *action;
6547 GstValidateActionType *atype = _find_action_type (action_typename);
6550 gst_validate_error_structure (plug_conf,
6551 "[CONFIG ERROR] Action type %s not found", action_typename);
6557 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
6558 GST_INFO ("Action type %s from configuration files"
6559 " is handled.", action_typename);
6563 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
6564 !(_action_type_has_parameter (atype, "as-config"))) {
6565 gst_validate_error_structure (plug_conf,
6566 "[CONFIG ERROR] Action '%s' is not a config action",
6572 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
6573 gst_structure_set_name (plug_conf, action_typename);
6575 action = gst_validate_action_new (NULL, atype, plug_conf, FALSE);
6576 gst_validate_action_unref (action);
6582 register_action_types (void)
6584 GstValidateActionType *type;
6585 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
6586 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
6588 _gst_validate_action_type = gst_validate_action_get_type ();
6589 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
6592 REGISTER_ACTION_TYPE ("meta", NULL,
6593 ((GstValidateActionParameter []) {
6596 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
6599 .possible_variables = NULL,
6600 .def = "'Nothing'"},
6602 .name = "is-config",
6603 .description = "Whether the scenario is a config only scenario",
6606 .possible_variables = NULL,
6610 .name = "handles-states",
6611 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
6612 "in that case the application should not set the state of the pipeline to anything\n"
6613 "and the scenario action will be executed from the beginning",
6616 .possible_variables = NULL,
6620 .description = "Whether the scenario executes seek actions or not",
6623 .possible_variables = NULL,
6627 .name = "reverse-playback",
6628 .description = "Whether the scenario plays the stream backward",
6631 .possible_variables = NULL,
6635 .name = "need-clock-sync",
6636 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
6637 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
6640 .possible_variables = NULL,
6641 .def = "true if some action requires a playback-time false otherwise"
6644 .name = "min-media-duration",
6645 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
6649 .possible_variables = NULL,
6653 .name = "min-audio-track",
6654 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
6655 "for the scenario to be usable",
6658 .possible_variables = NULL,
6662 .name = "min-video-track",
6663 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
6664 "for the scenario to be usable",
6667 .possible_variables = NULL,
6672 .description = "Lets the user know the time the scenario needs to be fully executed",
6674 .types = "double, int",
6675 .possible_variables = NULL,
6676 .def = "infinite (GST_CLOCK_TIME_NONE)"
6679 .name = "pipeline-name",
6680 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
6681 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
6684 .possible_variables = NULL,
6688 .name = "max-latency",
6689 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
6690 "It can be overridden using core configuration, like for example by defining the "
6691 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
6693 .types = "double, int",
6694 .possible_variables = NULL,
6695 .def = "infinite (GST_CLOCK_TIME_NONE)"
6698 .name = "max-dropped",
6699 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
6700 "It can be overridden using core configuration, like for example by defining the "
6701 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
6704 .possible_variables = NULL,
6705 .def = "infinite (-1)"
6708 .name = "ignore-eos",
6709 .description = "Ignore EOS and keep executing the scenario when it happens.\n By default "
6710 "a 'stop' action is generated one EOS",
6713 .possible_variables = NULL,
6717 .name = "allow-errors",
6718 .description = "Ignore error messages and keep executing the scenario when it happens.\n By default "
6719 "a 'stop' action is generated one ERROR messages",
6722 .possible_variables = NULL,
6727 "Scenario metadata.\nNOTE: it used to be called \"description\"",
6728 GST_VALIDATE_ACTION_TYPE_CONFIG);
6730 REGISTER_ACTION_TYPE ("seek", _execute_seek,
6731 ((GstValidateActionParameter []) {
6734 .description = "The starting value of the seek",
6736 .types = "double or string (GstClockTime)",
6737 .possible_variables =
6738 "`position`: The current position in the stream\n"
6739 "`duration`: The duration of the stream",
6744 .description = "The GstSeekFlags to use",
6746 .types = "string describing the GstSeekFlags to set",
6751 .description = "The rate value of the seek",
6754 .possible_variables = NULL,
6758 .name = "start_type",
6759 .description = "The GstSeekType to use for the start of the seek, in:\n"
6760 " [none, set, end]",
6763 .possible_variables = NULL,
6767 .name = "stop_type",
6768 .description = "The GstSeekType to use for the stop of the seek, in:\n"
6769 " [none, set, end]",
6772 .possible_variables = NULL,
6777 .description = "The stop value of the seek",
6779 .types = "double or string (GstClockTime)",
6780 .possible_variables =
6781 "`position`: The current position in the stream\n"
6782 "`duration`: The duration of the stream",
6783 .def ="GST_CLOCK_TIME_NONE",
6787 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
6788 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
6789 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
6790 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
6793 REGISTER_ACTION_TYPE ("pause", _execute_pause,
6794 ((GstValidateActionParameter []) {
6797 .description = "The duration during which the stream will be paused",
6799 .types = "double or string (GstClockTime)",
6800 .possible_variables = NULL,
6805 "Sets pipeline to PAUSED. You can add a 'duration'\n"
6806 "parameter so the pipeline goes back to playing after that duration\n"
6808 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
6810 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
6811 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
6813 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
6814 "Stops the execution of the scenario. It will post a 'request-state'"
6815 " message on the bus with NULL as a requested state"
6816 " and the application is responsible for stopping itself."
6817 " If you override that action type, make sure to link up.",
6818 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6820 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
6821 "Sends an EOS event to the pipeline",
6822 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
6824 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
6825 ((GstValidateActionParameter []) {
6828 .description = "Selects which track type to change (can be 'audio', 'video',"
6832 .possible_variables = NULL,
6837 .description = "Selects which track of this type to use: it can be either a number,\n"
6838 "which will be the Nth track of the given type, or a number with a '+' or\n"
6839 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
6840 "'-1' means 'previous track')",
6842 .types = "string: to switch track relatively\n"
6843 "int: To use the actual index to use",
6844 .possible_variables = NULL,
6849 "The 'switch-track' command can be used to switch tracks."
6850 , GST_VALIDATE_ACTION_TYPE_NONE);
6852 REGISTER_ACTION_TYPE ("wait", _execute_wait,
6853 ((GstValidateActionParameter []) {
6856 .description = "the duration while no other action will be executed",
6858 .types = "double or string (GstClockTime)",
6861 .name = "target-element-name",
6862 .description = "The name of the GstElement to wait @signal-name on.",
6867 .name = "target-element-factory-name",
6868 .description = "The name factory for which to wait @signal-name on",
6874 .name = "signal-name",
6875 .description = "The name of the signal to wait for on @target-element-name."
6876 " To ensure that the signal is executed without blocking while waiting for it"
6877 " you can set the field 'non-blocking=true'.",
6883 .name = "non-blocking",
6884 .description = "**Only for signals**."
6885 " Ensures that the signal is emitted without a blocking waiting.",
6891 .name = "message-type",
6892 .description = "The name of the message type to wait for (on @target-element-name"
6899 .name = "expected-values",
6900 .description = "Expected values in the message structure (valid only when "
6901 "`message-type`). Example: "
6902 "wait, on-client=true, message-type=buffering, expected-values=[values, buffer-percent=100]",
6904 .types = "GstStructure",
6909 .description = "Wait until the test clock get a new pending entry"
6910 " see #gst_test_clock_wait_for_next_pending_id.",
6917 .description = "The check action to execute when non blocking signal is received",
6919 .types = "structure",
6924 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
6925 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
6927 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
6928 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
6929 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
6930 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
6931 GST_VALIDATE_ACTION_TYPE_NONE);
6933 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank_or_disable_feature,
6934 ((GstValidateActionParameter []) {
6937 .description = "The name of a GstFeature or GstPlugin",
6943 .description = "The GstRank to set on @name",
6945 .types = "string, int",
6949 "Changes the ranking of a particular plugin feature(s)",
6950 GST_VALIDATE_ACTION_TYPE_CONFIG);
6952 REGISTER_ACTION_TYPE ("remove-feature", _execute_set_rank_or_disable_feature,
6953 ((GstValidateActionParameter []) {
6956 .description = "The name of a GstFeature or GstPlugin to remove",
6963 "Remove a plugin feature(s) or a plugin from the registry",
6964 GST_VALIDATE_ACTION_TYPE_CONFIG);
6966 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank_or_disable_feature,
6967 ((GstValidateActionParameter []) {
6969 .name = "feature-name",
6970 .description = "The name of a GstFeature",
6976 .description = "The GstRank to set on @feature-name",
6978 .types = "string, int",
6982 "Changes the ranking of a particular plugin feature",
6983 GST_VALIDATE_ACTION_TYPE_CONFIG);
6985 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
6986 ((GstValidateActionParameter []) {
6989 .description = "A GstState as a string, should be in: \n"
6990 " * ['null', 'ready', 'paused', 'playing']",
6996 "Changes the state of the pipeline to any GstState",
6997 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
6999 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
7000 ((GstValidateActionParameter []) {
7003 "Define vars to be used in other actions.\n"
7004 "For example you can define vars for buffer checksum"
7005 " to be used in the \"check-last-sample\" action type as follow:\n\n"
7007 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
7008 " check-last-sample, checksum=frame1\n"
7010 GST_VALIDATE_ACTION_TYPE_NONE);
7012 GST_TYPE_INTERPOLATION_CONTROL_SOURCE;
7013 GST_TYPE_TRIGGER_CONTROL_SOURCE;
7014 REGISTER_ACTION_TYPE ("set-timed-value-properties", _set_timed_value_property,
7015 ((GstValidateActionParameter []) {
7017 .name = "binding-type",
7018 .description = "The name of the type of binding to use",
7024 .name = "source-type",
7025 .description = "The name of the type of ControlSource to use",
7028 .def = "GstInterpolationControlSource",
7031 .name = "interpolation-mode",
7032 .description = "The name of the GstInterpolationMode to on the source",
7038 .name = "timestamp",
7039 .description = "The timestamp of the keyframe",
7040 .types = "string or float (GstClockTime)",
7045 "Sets GstTimedValue on pads on elements properties using GstControlBindings \n"
7046 "and GstControlSource as defined in the parameters.\n"
7047 "The properties values to set will be defined as:\n\n"
7048 " element-name.padname::property-name=new-value\n\n"
7049 "> NOTE: `.padname` is not needed if setting a property on an element\n\n"
7050 "This action also adds necessary control source/control bindings.\n",
7051 GST_VALIDATE_ACTION_TYPE_NONE);
7053 REGISTER_ACTION_TYPE ("check-properties", _execute_set_or_check_properties,
7054 ((GstValidateActionParameter []) {
7057 "Check elements and pads properties values.\n"
7058 "The properties values to check will be defined as:\n\n"
7059 " element-name.padname::property-name\n\n"
7060 "> NOTE: `.padname` is not needed if checking an element property\n\n",
7061 GST_VALIDATE_ACTION_TYPE_CHECK);
7063 REGISTER_ACTION_TYPE ("set-properties", _execute_set_or_check_properties,
7064 ((GstValidateActionParameter []) {
7067 "Set elements and pads properties values.\n"
7068 "The properties values to set will be defined as:\n\n"
7069 " element-name.padname::property-name\n\n"
7070 "> NOTE: `.padname` is not needed if checking an element property\n\n",
7071 GST_VALIDATE_ACTION_TYPE_NONE);
7073 REGISTER_ACTION_TYPE ("set-property", _execute_set_or_check_property,
7074 ((GstValidateActionParameter []) {
7076 .name = "target-element-name",
7077 .description = "The name of the GstElement to set a property on",
7083 .name = "target-element-factory-name",
7084 .description = "The name factory for which to set a property on built elements",
7090 .name = "target-element-klass",
7091 .description = "The klass of the GstElements to set a property on",
7097 .name = "property-name",
7098 .description = "The name of the property to set on @target-element-name",
7104 .name = "property-value",
7105 .description = "The value of @property-name to be set on the element",
7107 .types = "The same type of @property-name",
7111 .name = "on-all-instances",
7112 .description = "Whether to set property on all instances matching "
7120 "Sets a property of an element or klass of elements in the pipeline.\n"
7121 "Besides property-name and value, either 'target-element-name' or\n"
7122 "'target-element-klass' needs to be defined",
7123 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
7124 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
7125 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
7126 type->prepare = gst_validate_set_property_prepare_func;
7128 REGISTER_ACTION_TYPE("check-property", _execute_set_or_check_property,
7129 ((GstValidateActionParameter[]) {
7130 { .name = "target-element-name",
7131 .description = "The name of the GstElement to check a property value",
7135 { .name = "target-element-factory-name",
7136 .description = "The name factory for which to check a property value on built elements",
7140 { .name = "target-element-klass",
7141 .description = "The klass of the GstElements to check a property on",
7145 { .name = "property-name",
7146 .description = "The name of the property to set on @target-element-name",
7150 { .name = "property-value",
7151 .description = "The expected value of @property-name",
7153 .types = "The same type of @property-name",
7156 "Check the value of property of an element or klass of elements in the pipeline.\n"
7157 "Besides property-name and value, either 'target-element-name' or\n"
7158 "'target-element-klass' needs to be defined",
7159 GST_VALIDATE_ACTION_TYPE_CHECK);
7161 REGISTER_ACTION_TYPE ("set-debug-threshold",
7162 _execute_set_debug_threshold,
7163 ((GstValidateActionParameter [])
7166 .name = "debug-threshold",
7167 .description = "String defining debug threshold\n"
7168 "See gst_debug_set_threshold_from_string",
7173 "Sets the debug level to be used, same format as\n"
7174 "setting the GST_DEBUG env variable",
7175 GST_VALIDATE_ACTION_TYPE_NONE);
7177 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
7178 ((GstValidateActionParameter [])
7181 .name = "target-element-name",
7182 .description = "The name of the GstElement to emit a signal on",
7187 .name = "signal-name",
7188 .description = "The name of the signal to emit on @target-element-name",
7195 .description = "The signal parametters",
7197 .types = "ValueArray",
7202 "Emits a signal to an element in the pipeline",
7203 GST_VALIDATE_ACTION_TYPE_NONE);
7205 REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
7206 ((GstValidateActionParameter [])
7209 .name = "target-element-name",
7210 .description = "The name of the appsrc to push data on",
7215 .name = "file-name",
7216 .description = "Relative path to a file whose contents will be pushed as a buffer",
7222 .description = "Offset within the file where the buffer will start",
7228 .description = "Number of bytes from the file that will be pushed as a buffer",
7234 .description = "Caps for the buffer to be pushed",
7240 "Queues a buffer in an appsrc. If the pipeline state allows flow of buffers, the next action is not run until the buffer has been pushed.",
7241 GST_VALIDATE_ACTION_TYPE_NONE);
7243 REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
7244 ((GstValidateActionParameter [])
7247 .name = "target-element-name",
7248 .description = "The name of the appsrc to emit EOS on",
7254 "Queues a EOS event in an appsrc.",
7255 GST_VALIDATE_ACTION_TYPE_NONE);
7257 REGISTER_ACTION_TYPE ("flush", _execute_flush,
7258 ((GstValidateActionParameter [])
7261 .name = "target-element-name",
7262 .description = "The name of the appsrc to flush on",
7267 .name = "reset-time",
7268 .description = "Whether the flush should reset running time",
7275 "Sends FLUSH_START and FLUSH_STOP events.",
7276 GST_VALIDATE_ACTION_TYPE_NONE);
7278 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
7279 ((GstValidateActionParameter [])
7282 .name = "plugin-name",
7283 .description = "The name of the GstPlugin to disable",
7288 .name = "as-config",
7289 .description = "Execute action as a config action (meaning when loading the scenario)",
7296 "Disables a GstPlugin",
7297 GST_VALIDATE_ACTION_TYPE_NONE);
7299 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
7300 ((GstValidateActionParameter []) {
7302 .name = "sink-name",
7303 .description = "The name of the sink element to check sample on.",
7309 .name = "sink-factory-name",
7310 .description = "The name of the factory of the sink element to check sample on.",
7316 .name = "sinkpad-caps",
7317 .description = "The caps (as string) of the sink to check.",
7324 .description = "The reference checksum of the buffer.",
7330 .name = "timecode-frame-number",
7331 .description = "The frame number of the buffer as specified on its"
7332 " GstVideoTimeCodeMeta",
7339 "Checks the last-sample checksum or frame number (set on its "
7340 " GstVideoTimeCodeMeta) on declared Sink element."
7341 " This allows checking the checksum of a buffer after a 'seek' or after a"
7342 " GESTimeline 'commit'"
7344 GST_VALIDATE_ACTION_TYPE_NON_BLOCKING | GST_VALIDATE_ACTION_TYPE_CHECK);
7346 REGISTER_ACTION_TYPE ("crank-clock", _execute_crank_clock,
7347 ((GstValidateActionParameter []) {
7349 .name = "expected-time",
7350 .description = "Expected clock time after cranking",
7352 .types = "GstClockTime",
7356 .name = "expected-elapsed-time",
7357 .description = "Check time elapsed during the clock cranking",
7359 .types = "GstClockTime",
7363 }), "Crank the clock, possibly checking how much time was supposed to be waited on the clock"
7364 " and/or the clock running time after the crank."
7365 " Using one `crank-clock` action in a scenario implies that the scenario is driving the "
7366 " clock and a #GstTestClock will be used. The user will need to crank it the number of "
7367 " time required (using the `repeat` parameter comes handy here).",
7368 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
7370 REGISTER_ACTION_TYPE ("video-request-key-unit", _execute_request_key_unit,
7371 ((GstValidateActionParameter []) {
7373 .name = "direction",
7374 .description = "The direction for the event to travel, should be in\n"
7375 " * [upstream, downstream]",
7381 .name = "running-time",
7382 .description = "The running_time can be set to request a new key unit at a specific running_time.\n"
7383 "If not set, GST_CLOCK_TIME_NONE will be used so upstream elements will produce a new key unit "
7384 "as soon as possible.",
7386 .types = "double or string",
7387 .possible_variables = "position: The current position in the stream\n"
7388 "duration: The duration of the stream",
7392 .name = "all-headers",
7393 .description = "TRUE to produce headers when starting a new key unit",
7401 .description = "integer that can be used to number key units",
7408 .name = "target-element-name",
7409 .description = "The name of the GstElement to send a send force-key-unit to",
7415 .name = "target-element-factory-name",
7416 .description = "The factory name of the GstElements to send a send force-key-unit to",
7422 .name = "target-element-klass",
7423 .description = "The klass of the GstElements to send a send force-key-unit to",
7425 .def = "Video/Encoder",
7431 .description = "The name of the GstPad to send a send force-key-unit to",
7439 .description = "The name of the GstPad to send a send force-key-unit to",
7447 "Request a video key unit", FALSE);
7449 REGISTER_ACTION_TYPE("check-position", _execute_check_position,
7450 ((GstValidateActionParameter[]) {
7451 { .name = "expected-position",
7452 .description = "The expected pipeline position",
7454 .types = "GstClockTime",
7458 "Check current pipeline position.\n",
7459 /* FIXME: Make MT safe so it can be marked as GST_VALIDATE_ACTION_TYPE_CHECK */
7460 GST_VALIDATE_ACTION_TYPE_NONE);
7462 REGISTER_ACTION_TYPE("check-current-pad-caps", _execute_check_pad_caps,
7463 ((GstValidateActionParameter[]) {
7465 .name = "expected-caps",
7466 .description = "The expected caps. If not present, expected no caps to be set",
7468 .types = "GstCaps,GstStructure",
7472 .name = "target-element-name",
7473 .description = "The name of the GstElement to send a send force-key-unit to",
7479 .name = "target-element-factory-name",
7480 .description = "The factory name of the GstElements to get pad from",
7486 .name = "target-element-klass",
7487 .description = "The klass of the GstElements to get pad from",
7494 .description = "The name of the GstPad to get pad from",
7500 .name = "comparison-type",
7503 .types = "string in [intersect, equal]",
7508 "Check current pipeline position.\n",
7509 GST_VALIDATE_ACTION_TYPE_NONE | GST_VALIDATE_ACTION_TYPE_CHECK );
7511 REGISTER_ACTION_TYPE("run-command", _run_command,
7512 ((GstValidateActionParameter[]) {
7515 .description = "The subprocess arguments",
7517 .types = "(string){array,}",
7522 .description = "Extra environment variables to set",
7524 .types = "GstStructure",
7529 "Check current pipeline position.\n",
7530 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL);
7532 REGISTER_ACTION_TYPE("foreach", NULL,
7533 ((GstValidateActionParameter[]) {
7534 { .name = "actions",
7535 .description = "The array of actions to repeat",
7540 "Run actions defined in the `actions` array the number of times specified\n"
7541 " with an iterator parameter passed in. The iterator can be\n"
7542 " a range like :`i=[start, end, step]` or array of values\n"
7543 " such as: `values=<value1, value2>`.\n"
7544 "One and only iterator field is required as parameter.",
7545 GST_VALIDATE_ACTION_TYPE_NONE);
7546 type->prepare = gst_validate_foreach_prepare;
7548 /* Internal actions types to test the validate scenario implementation */
7549 REGISTER_ACTION_TYPE("priv_check-action-type-calls",
7550 _execute_check_action_type_calls, NULL, NULL, 0);
7551 REGISTER_ACTION_TYPE("priv_check-subaction-level",
7552 _execute_check_subaction_level, NULL, NULL, 0);
7557 gst_validate_scenario_deinit (void)
7559 _free_action_types (action_types);
7560 action_types = NULL;