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.
50 #include "gst-validate-internal.h"
51 #include "gst-validate-scenario.h"
52 #include "gst-validate-reporter.h"
53 #include "gst-validate-report.h"
54 #include "gst-validate-utils.h"
55 #include "gst-validate-internal.h"
57 #include <gst/validate/gst-validate-override.h>
58 #include <gst/validate/gst-validate-override-registry.h>
59 #include <gst/validate/gst-validate-pipeline-monitor.h>
61 #define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
62 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
64 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval
65 TODO make it overridable */
67 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
68 #undef GST_CAT_DEFAULT
69 #define GST_CAT_DEFAULT gst_validate_scenario_debug
71 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
72 gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
75 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
77 #define SCENARIO_LOCK(scenario) (g_mutex_lock(&scenario->priv->lock))
78 #define SCENARIO_UNLOCK(scenario) (g_mutex_unlock(&scenario->priv->lock))
80 #define DECLARE_AND_GET_PIPELINE(s,a) \
81 GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
82 if (pipeline == NULL) { \
83 GST_VALIDATE_REPORT_ACTION (s, a, SCENARIO_ACTION_EXECUTION_ERROR, \
84 "Can't execute a '%s' action after the pipeline " \
85 "has been destroyed.", a->type); \
86 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
104 static guint scenario_signals[LAST_SIGNAL] = { 0 };
106 static GList *action_types = NULL;
107 static void gst_validate_scenario_dispose (GObject * object);
108 static void gst_validate_scenario_finalize (GObject * object);
109 static GstValidateActionType *_find_action_type (const gchar * type_name);
110 static GstValidateExecuteActionReturn
111 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
112 GstStructure * structure, gboolean add_to_lists);
113 static gboolean _action_set_done (GstValidateAction * action);
115 /* GstValidateScenario is not really thread safe and
116 * everything should be done from the thread GstValidate
117 * was inited from, unless stated otherwise.
119 struct _GstValidateScenarioPrivate
122 GstValidateRunner *runner;
123 gboolean execute_on_idle;
128 GList *interlaced_actions; /* MT safe. Protected with SCENARIO_LOCK */
129 GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */
131 gboolean needs_playback_parsing;
133 /* List of action that need parsing when reaching ASYNC_DONE
134 * most probably to be able to query duration */
137 GstSeekFlags seek_flags;
138 GstFormat seek_format;
139 GstClockTime segment_start;
140 GstClockTime segment_stop;
141 GstClockTime seek_pos_tol;
143 /* If we seeked in paused the position should be exactly what
144 * the seek value was (if accurate) */
145 gboolean seeked_in_pause;
149 gboolean handles_state;
151 guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */
153 guint signal_handler_id;
154 guint action_execution_interval;
156 /* Name of message the wait action is waiting for */
157 const gchar *message_type;
162 gboolean changing_state;
163 gboolean needs_async_done;
165 GstState target_state;
169 gchar *pipeline_name;
170 GstClockTime max_latency;
174 /* 'switch-track action' currently waiting for
175 * GST_MESSAGE_STREAMS_SELECTED to be completed. */
176 GstValidateAction *pending_switch_track;
180 GWeakRef ref_pipeline;
183 typedef struct KeyFileGroupName
189 #define NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE 1
191 #if !GLIB_CHECK_VERSION(2,54,0)
192 #define g_enum_to_string gst_validate_g_enum_to_string
194 gst_validate_g_enum_to_string (GType g_enum_type, gint value)
197 GEnumClass *enum_class;
198 GEnumValue *enum_value;
200 g_return_val_if_fail (G_TYPE_IS_ENUM (g_enum_type), NULL);
202 enum_class = g_type_class_ref (g_enum_type);
205 if (enum_class == NULL)
206 return g_strdup_printf ("%d", value);
208 enum_value = g_enum_get_value (enum_class, value);
210 if (enum_value == NULL)
211 result = g_strdup_printf ("%d", value);
213 result = g_strdup (enum_value->value_name);
215 g_type_class_unref (enum_class);
220 static GstValidateInterceptionReturn
221 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
222 GstValidateReport * report)
226 for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
228 GstValidateOverride *override = (GstValidateOverride *) tmp->data;
230 gst_validate_override_get_severity (override,
231 gst_validate_issue_get_id (report->issue), report->level);
234 return GST_VALIDATE_REPORTER_REPORT;
238 * gst_validate_scenario_get_pipeline:
239 * @scenario: The scenario to retrieve a pipeline from
241 * Returns: (transfer full): The #GstPipeline the scenario is running
245 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
247 return g_weak_ref_get (&scenario->priv->ref_pipeline);
251 _get_pipeline (GstValidateReporter * reporter)
254 GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
255 (GST_VALIDATE_SCENARIO (reporter)));
259 _reporter_iface_init (GstValidateReporterInterface * iface)
261 iface->intercept_report = gst_validate_scenario_intercept_report;
262 iface->get_pipeline = _get_pipeline;
265 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
266 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
267 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init));
269 /* GstValidateAction implementation */
270 static GType _gst_validate_action_type = 0;
272 struct _GstValidateActionPrivate
274 GstStructure *main_structure;
275 GstValidateExecuteActionReturn state; /* Actually ActionState */
277 gboolean executing_last_subaction;
280 GstClockTime execution_time;
281 GstClockTime timeout;
284 gboolean needs_playback_parsing;
285 gboolean pending_set_done;
289 gst_validate_action_serialize (GstValidateAction * action)
291 JsonNode *node = json_node_alloc ();
292 JsonObject *jreport = json_object_new ();
293 gchar *action_args = gst_structure_to_string (action->structure);
295 json_object_set_string_member (jreport, "type", "action");
296 json_object_set_string_member (jreport, "action-type", action->type);
297 json_object_set_int_member (jreport, "playback-time",
298 (gint64) action->playback_time);
299 json_object_set_string_member (jreport, "args", action_args);
300 g_free (action_args);
302 node = json_node_init_object (node, jreport);
303 json_object_unref (jreport);
309 gst_validate_action_get_type (void)
311 if (_gst_validate_action_type == 0) {
312 _gst_validate_action_type =
313 g_boxed_type_register_static (g_intern_static_string
314 ("GstValidateAction"), (GBoxedCopyFunc) gst_mini_object_ref,
315 (GBoxedFreeFunc) gst_mini_object_unref);
317 json_boxed_register_serialize_func (_gst_validate_action_type,
319 (JsonBoxedSerializeFunc) gst_validate_action_serialize);
322 return _gst_validate_action_type;
325 static gboolean execute_next_action (GstValidateScenario * scenario);
327 gst_validate_scenario_load (GstValidateScenario * scenario,
328 const gchar * scenario_name, const gchar * relative_scenario);
330 static GstValidateAction *
331 _action_copy (GstValidateAction * act)
333 GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
334 GstValidateAction *copy = gst_validate_action_new (scenario,
335 _find_action_type (act->type), NULL, FALSE);
337 gst_object_unref (scenario);
339 if (act->structure) {
340 copy->structure = gst_structure_copy (act->structure);
341 copy->type = gst_structure_get_name (copy->structure);
342 if (!(act->name = gst_structure_get_string (copy->structure, "name")))
346 if (act->priv->main_structure)
347 copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
349 copy->action_number = act->action_number;
350 copy->playback_time = act->playback_time;
351 copy->priv->timeout = act->priv->timeout;
352 GST_VALIDATE_ACTION_LINENO (copy) = GST_VALIDATE_ACTION_LINENO (act);
353 GST_VALIDATE_ACTION_FILENAME (copy) =
354 g_strdup (GST_VALIDATE_ACTION_FILENAME (act));
360 _action_free (GstValidateAction * action)
362 if (action->structure)
363 gst_structure_free (action->structure);
365 if (action->priv->main_structure)
366 gst_structure_free (action->priv->main_structure);
368 g_weak_ref_clear (&action->priv->scenario);
369 g_free (GST_VALIDATE_ACTION_FILENAME (action));
371 g_slice_free (GstValidateActionPrivate, action->priv);
372 g_slice_free (GstValidateAction, action);
376 gst_validate_action_init (GstValidateAction * action)
378 gst_mini_object_init (((GstMiniObject *) action), 0,
379 _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
380 (GstMiniObjectFreeFunction) _action_free);
382 action->priv = g_slice_new0 (GstValidateActionPrivate);
384 g_weak_ref_init (&action->priv->scenario, NULL);
388 gst_validate_action_ref (GstValidateAction * action)
390 return (GstValidateAction *) gst_mini_object_ref (GST_MINI_OBJECT (action));
394 gst_validate_action_unref (GstValidateAction * action)
396 gst_mini_object_unref (GST_MINI_OBJECT (action));
400 gst_validate_action_new (GstValidateScenario * scenario,
401 GstValidateActionType * action_type, GstStructure * structure,
402 gboolean add_to_lists)
404 GstValidateAction *action = g_slice_new0 (GstValidateAction);
406 gst_validate_action_init (action);
407 action->playback_time = GST_CLOCK_TIME_NONE;
408 action->priv->timeout = GST_CLOCK_TIME_NONE;
409 action->type = action_type->name;
411 gst_structure_get (structure,
412 "__lineno__", G_TYPE_INT, &GST_VALIDATE_ACTION_LINENO (action),
413 "__filename__", G_TYPE_STRING, &GST_VALIDATE_ACTION_FILENAME (action),
415 gst_structure_remove_fields (structure, "__lineno__", "__filename__", NULL);
417 g_weak_ref_set (&action->priv->scenario, scenario);
419 action->priv->state =
420 _fill_action (scenario, action, structure, add_to_lists);
426 _action_check_and_set_printed (GstValidateAction * action)
428 if (action->priv->printed == FALSE) {
429 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
432 action->priv->printed = TRUE;
441 gst_validate_action_is_subaction (GstValidateAction * action)
443 return !gst_structure_is_equal (action->structure,
444 action->priv->main_structure);
447 /* GstValidateActionType implementation */
448 GType _gst_validate_action_type_type;
449 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
450 static GstValidateActionType *gst_validate_action_type_new (void);
453 _action_type_free (GstValidateActionType * type)
455 g_free (type->parameters);
456 g_free (type->description);
458 g_free (type->implementer_namespace);
460 if (type->overriden_type)
461 gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
463 g_slice_free (GstValidateActionType, type);
467 gst_validate_action_type_init (GstValidateActionType * type)
469 gst_mini_object_init ((GstMiniObject *) type, 0,
470 _gst_validate_action_type_type, NULL, NULL,
471 (GstMiniObjectFreeFunction) _action_type_free);
474 GstValidateActionType *
475 gst_validate_action_type_new (void)
477 GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
479 gst_validate_action_type_init (type);
481 /* action types are never freed */
482 GST_MINI_OBJECT_FLAG_SET (type, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
486 static GstValidateActionType *
487 _find_action_type (const gchar * type_name)
491 for (tmp = action_types; tmp; tmp = tmp->next) {
492 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
493 if (g_strcmp0 (atype->name, type_name) == 0)
501 _update_well_known_vars (GstValidateScenario * scenario)
503 gint64 duration, position;
504 gdouble dduration, dposition;
505 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
507 gst_structure_remove_fields (scenario->priv->vars, "position", "duration",
513 if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration) ||
514 !GST_CLOCK_TIME_IS_VALID (duration)) {
515 GstValidateMonitor *monitor =
516 (GstValidateMonitor *) (g_object_get_data ((GObject *)
517 pipeline, "validate-monitor"));
518 GST_INFO_OBJECT (scenario,
519 "Could not query duration. Trying to get duration from media-info");
520 if (monitor && monitor->media_descriptor)
522 gst_validate_media_descriptor_get_duration
523 (monitor->media_descriptor);
526 if (!GST_CLOCK_TIME_IS_VALID (duration))
527 dduration = G_MAXDOUBLE;
529 dduration = ((double) duration / GST_SECOND);
531 gst_structure_set (scenario->priv->vars, "duration", G_TYPE_DOUBLE, dduration,
533 if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
535 if (!GST_CLOCK_TIME_IS_VALID (position))
536 dposition = G_MAXDOUBLE;
538 dposition = ((double) position / GST_SECOND);
540 gst_structure_set (scenario->priv->vars, "position", G_TYPE_DOUBLE,
543 GST_WARNING_OBJECT (scenario, "Could not query position");
546 gst_object_unref (pipeline);
550 _set_variable_func (const gchar * name, double *value, gpointer user_data)
552 GstValidateScenario *scenario = (GstValidateScenario *) user_data;
554 if (!gst_structure_get_double (scenario->priv->vars, name, value))
560 /* Check that @list doesn't contain any non-optional actions */
562 actions_list_is_done (GList * list)
566 for (l = list; l != NULL; l = g_list_next (l)) {
567 GstValidateAction *action = l->data;
569 if (!action->priv->optional)
577 _check_scenario_is_done (GstValidateScenario * scenario)
579 SCENARIO_LOCK (scenario);
580 if (actions_list_is_done (scenario->priv->actions) &&
581 actions_list_is_done (scenario->priv->interlaced_actions) &&
582 actions_list_is_done (scenario->priv->on_addition_actions)) {
583 SCENARIO_UNLOCK (scenario);
585 g_signal_emit (scenario, scenario_signals[DONE], 0);
587 SCENARIO_UNLOCK (scenario);
592 * gst_validate_action_get_clocktime:
593 * @scenario: The #GstValidateScenario from which to get a time
594 * for a parameter of an action
595 * @action: The action from which to retrieve the time for @name
597 * @name: The name of the parameter for which to retrieve a time
598 * @retval: (out): The return value for the wanted time
600 * Get a time value for the @name parameter of an action. This
601 * method should be called to retrieve and compute a timed value of a given
602 * action. It will first try to retrieve the value as a double,
603 * then get it as a string and execute any formula taking into account
604 * the 'position' and 'duration' variables. And it will always convert that
605 * value to a GstClockTime.
607 * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
610 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
611 GstValidateAction * action, const gchar * name, GstClockTime * retval)
613 if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
615 gchar *error = NULL, *strval;
616 const gchar *tmpvalue = gst_structure_get_string (action->structure, name);
619 GST_INFO_OBJECT (scenario, "Could not find %s (%" GST_PTR_FORMAT ")",
620 name, action->structure);
624 _update_well_known_vars (scenario);
626 gst_validate_replace_variables_in_string (scenario->priv->vars,
632 gst_validate_utils_parse_expression (strval, _set_variable_func,
635 GST_WARNING ("Error while parsing %s: %s (%" GST_PTR_FORMAT ")",
636 strval, error, scenario->priv->vars);
641 } else if (val == -1.0) {
642 *retval = GST_CLOCK_TIME_NONE;
644 *retval = val * GST_SECOND;
646 gst_structure_set (action->structure, name, G_TYPE_UINT64, *retval, NULL);
656 * gst_validate_scenario_execute_seek:
657 * @scenario: The #GstValidateScenario for which to execute a seek action
658 * @action: The seek action to execute
659 * @rate: The playback rate of the seek
660 * @format: The #GstFormat of the seek
661 * @flags: The #GstSeekFlags of the seek
662 * @start_type: The #GstSeekType of the start value of the seek
663 * @start: The start time of the seek
664 * @stop_type: The #GstSeekType of the stop value of the seek
665 * @stop: The stop time of the seek
667 * Executes a seek event on the scenario's pipeline. You should always use
668 * this method when you want to execute a seek inside a new action type
669 * so that the scenario state is updated taking into account that seek.
671 * For more information you should have a look at #gst_event_new_seek
673 * Returns: %TRUE if the seek could be executed, %FALSE otherwise
675 GstValidateExecuteActionReturn
676 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
677 GstValidateAction * action, gdouble rate, GstFormat format,
678 GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
679 GstSeekType stop_type, GstClockTime stop)
683 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
684 GstValidateScenarioPrivate *priv = scenario->priv;
685 DECLARE_AND_GET_PIPELINE (scenario, action);
687 seek = gst_event_new_seek (rate, format, flags, start_type, start,
690 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
691 GST_VALIDATE_REPORT_ACTION (scenario, action,
692 SCENARIO_ACTION_EXECUTION_ERROR,
693 "Trying to seek in format %d, but not support yet!", format);
697 gst_event_ref (seek);
698 if (gst_element_send_event (pipeline, seek)) {
699 gst_event_replace (&priv->last_seek, seek);
700 priv->seek_flags = flags;
701 priv->seek_format = format;
704 case GST_FORMAT_TIME:
705 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
706 "Could not execute seek: '(position %" GST_TIME_FORMAT
707 "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
708 " stop: %" GST_TIME_FORMAT " Rate %lf'",
709 GST_TIME_ARGS (action->playback_time), action->name,
710 action->action_number, action->repeat, GST_TIME_ARGS (start),
711 GST_TIME_ARGS (stop), rate);
715 gchar *format_str = g_enum_to_string (GST_TYPE_FORMAT, format);
717 GST_VALIDATE_REPORT_ACTION (scenario, action, EVENT_SEEK_NOT_HANDLED,
718 "Could not execute seek in format %s '(position %" GST_TIME_FORMAT
719 "), %s (num %u, missing repeat: %i), seeking to: %" G_GINT64_FORMAT
720 " stop: %" G_GINT64_FORMAT " Rate %lf'", format_str,
721 GST_TIME_ARGS (action->playback_time), action->name,
722 action->action_number, action->repeat, start, stop, rate);
727 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
729 gst_event_unref (seek);
730 gst_object_unref (pipeline);
736 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
738 const char *str_format, *str_flags, *str_start_type, *str_stop_type;
741 guint format = GST_FORMAT_TIME;
742 GstSeekFlags flags = 0;
743 guint start_type = GST_SEEK_TYPE_SET;
745 guint stop_type = GST_SEEK_TYPE_SET;
746 GstClockTime stop = GST_CLOCK_TIME_NONE;
748 if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
749 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
751 gst_structure_get_double (action->structure, "rate", &rate);
752 if ((str_format = gst_structure_get_string (action->structure, "format")))
753 gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
755 if ((str_start_type =
756 gst_structure_get_string (action->structure, "start_type")))
757 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
761 gst_structure_get_string (action->structure, "stop_type")))
762 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
765 if ((str_flags = gst_structure_get_string (action->structure, "flags")))
766 flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
768 gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
770 return gst_validate_scenario_execute_seek (scenario, action, rate, format,
771 flags, start_type, start, stop_type, stop);
775 _pause_action_restore_playing (GstValidateScenario * scenario)
777 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
780 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
785 gst_validate_printf (scenario, "Back to playing\n");
787 if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
788 GST_STATE_CHANGE_FAILURE) {
789 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
790 "Failed to set state to playing");
791 scenario->priv->target_state = GST_STATE_PLAYING;
794 gst_object_unref (pipeline);
800 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
802 gst_structure_id_set_value (consts, field_id, value);
807 static GstValidateExecuteActionReturn
808 _execute_define_vars (GstValidateScenario * scenario,
809 GstValidateAction * action)
811 gst_structure_foreach (action->structure,
812 (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
814 return GST_VALIDATE_EXECUTE_ACTION_OK;
817 static GstValidateExecuteActionReturn
818 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
821 const gchar *str_state;
822 GstStateChangeReturn ret;
823 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
825 DECLARE_AND_GET_PIPELINE (scenario, action);
827 g_return_val_if_fail ((str_state =
828 gst_structure_get_string (action->structure, "state")), FALSE);
830 g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
831 str_state, &state), FALSE);
834 scenario->priv->target_state = state;
835 scenario->priv->changing_state = TRUE;
836 scenario->priv->seeked_in_pause = FALSE;
838 ret = gst_element_set_state (pipeline, state);
839 if (ret == GST_STATE_CHANGE_FAILURE) {
840 scenario->priv->changing_state = FALSE;
841 GST_VALIDATE_REPORT_ACTION (scenario, action, STATE_CHANGE_FAILURE,
842 "Failed to set state to %s", str_state);
844 /* Nothing async on failure, action will be removed automatically */
845 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
847 } else if (ret == GST_STATE_CHANGE_ASYNC) {
849 scenario->priv->needs_async_done = TRUE;
850 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
855 scenario->priv->changing_state = FALSE;
858 gst_object_unref (pipeline);
863 static GstValidateExecuteActionReturn
864 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
866 GstClockTime duration = 0;
867 GstValidateExecuteActionReturn ret;
869 gst_validate_action_get_clocktime (scenario, action, "duration", &duration);
870 gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
872 GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
873 GST_TIME_ARGS (duration));
875 ret = _execute_set_state (scenario, action);
877 if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
878 g_timeout_add (GST_TIME_AS_MSECONDS (duration),
879 (GSourceFunc) _pause_action_restore_playing, scenario);
884 static GstValidateExecuteActionReturn
885 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
887 GST_DEBUG ("Playing back");
889 gst_structure_set (action->structure, "state", G_TYPE_STRING,
893 return _execute_set_state (scenario, action);
897 _action_sets_state (GstValidateAction * action)
902 if (g_strcmp0 (action->type, "set-state") == 0)
905 if (g_strcmp0 (action->type, "play") == 0)
908 if (g_strcmp0 (action->type, "pause") == 0)
916 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
918 GstValidateScenarioPrivate *priv = scenario->priv;
920 if (priv->max_dropped == -1 || priv->dropped == -1)
923 GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
924 priv->dropped, priv->max_dropped);
926 if (priv->dropped > priv->max_dropped) {
927 GST_VALIDATE_REPORT (scenario, CONFIG_TOO_MANY_BUFFERS_DROPPED,
928 "Too many buffers have been dropped: %d (max allowed: %d)",
929 priv->dropped, priv->max_dropped);
933 static GstValidateExecuteActionReturn
934 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
937 GstValidateScenarioPrivate *priv = scenario->priv;
939 DECLARE_AND_GET_PIPELINE (scenario, action);
941 bus = gst_element_get_bus (pipeline);
942 SCENARIO_LOCK (scenario);
943 if (priv->execute_actions_source_id) {
944 g_source_remove (priv->execute_actions_source_id);
945 priv->execute_actions_source_id = 0;
947 SCENARIO_UNLOCK (scenario);
949 gst_validate_scenario_check_dropped (scenario);
952 gst_message_new_request_state (GST_OBJECT_CAST (scenario),
954 gst_object_unref (bus);
955 gst_object_unref (pipeline);
960 static GstValidateExecuteActionReturn
961 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
965 DECLARE_AND_GET_PIPELINE (scenario, action);
967 GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
968 GST_TIME_ARGS (action->playback_time));
970 ret = gst_element_send_event (pipeline, gst_event_new_eos ());
971 gst_object_unref (pipeline);
973 return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
974 GST_VALIDATE_EXECUTE_ACTION_ERROR;
978 find_input_selector (GValue * velement, const gchar * type)
980 GstElement *element = g_value_get_object (velement);
983 if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
984 GstPad *srcpad = gst_element_get_static_pad (element, "src");
987 GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
991 gst_structure_get_name (gst_caps_get_structure (caps, 0));
992 gboolean found = FALSE;
994 if (g_strcmp0 (type, "audio") == 0)
995 found = g_str_has_prefix (mime, "audio/");
996 else if (g_strcmp0 (type, "video") == 0)
997 found = g_str_has_prefix (mime, "video/")
998 && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
999 else if (g_strcmp0 (type, "text") == 0)
1000 found = g_str_has_prefix (mime, "text/")
1001 || g_str_has_prefix (mime, "subtitle/")
1002 || g_str_has_prefix (mime, "video/x-dvd-subpicture");
1008 gst_caps_unref (caps);
1009 gst_object_unref (srcpad);
1016 find_input_selector_with_type (GstBin * bin, const gchar * type)
1018 GValue result = { 0, };
1019 GstElement *input_selector = NULL;
1020 GstIterator *iterator = gst_bin_iterate_recurse (bin);
1022 if (gst_iterator_find_custom (iterator,
1023 (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
1024 input_selector = g_value_get_object (&result);
1026 gst_iterator_free (iterator);
1028 return input_selector;
1032 find_nth_sink_pad (GstElement * element, int index)
1034 GstIterator *iterator;
1035 gboolean done = FALSE;
1037 int dec_index = index;
1038 GValue data = { 0, };
1040 iterator = gst_element_iterate_sink_pads (element);
1042 switch (gst_iterator_next (iterator, &data)) {
1043 case GST_ITERATOR_OK:
1046 pad = g_value_get_object (&data);
1049 g_value_reset (&data);
1051 case GST_ITERATOR_RESYNC:
1052 gst_iterator_resync (iterator);
1055 case GST_ITERATOR_ERROR:
1058 case GST_ITERATOR_DONE:
1063 gst_iterator_free (iterator);
1068 find_sink_pad_index (GstElement * element, GstPad * pad)
1070 GstIterator *iterator;
1071 gboolean done = FALSE;
1073 GValue data = { 0, };
1075 iterator = gst_element_iterate_sink_pads (element);
1077 switch (gst_iterator_next (iterator, &data)) {
1078 case GST_ITERATOR_OK:
1079 if (pad == g_value_get_object (&data)) {
1084 g_value_reset (&data);
1086 case GST_ITERATOR_RESYNC:
1087 gst_iterator_resync (iterator);
1090 case GST_ITERATOR_ERROR:
1093 case GST_ITERATOR_DONE:
1098 gst_iterator_free (iterator);
1102 static GstPadProbeReturn
1103 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1104 GstValidateAction * action)
1106 if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1107 gst_validate_action_set_done (action);
1109 return GST_PAD_PROBE_REMOVE;
1112 return GST_PAD_PROBE_OK;
1115 static GstValidateExecuteActionReturn
1116 execute_switch_track_default (GstValidateScenario * scenario,
1117 GstValidateAction * action)
1120 gboolean relative = FALSE;
1121 const gchar *type, *str_index;
1122 GstElement *input_selector;
1123 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1125 DECLARE_AND_GET_PIPELINE (scenario, action);
1127 if (!(type = gst_structure_get_string (action->structure, "type")))
1130 /* First find an input selector that has the right type */
1131 input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1132 if (input_selector) {
1133 GstState state, next;
1134 GstPad *pad, *cpad, *srcpad;
1136 ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1137 str_index = gst_structure_get_string (action->structure, "index");
1139 if (str_index == NULL) {
1140 if (!gst_structure_get_uint (action->structure, "index", &index)) {
1141 GST_WARNING ("No index given, defaulting to +1");
1146 relative = strchr ("+-", str_index[0]) != NULL;
1147 index = g_ascii_strtoll (str_index, NULL, 10);
1150 if (relative) { /* We are changing track relatively to current track */
1153 g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1155 int current_index = find_sink_pad_index (input_selector, pad);
1157 index = (current_index + index) % npads;
1158 gst_object_unref (pad);
1162 pad = find_nth_sink_pad (input_selector, index);
1163 g_object_get (input_selector, "active-pad", &cpad, NULL);
1164 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1165 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1166 srcpad = gst_element_get_static_pad (input_selector, "src");
1168 gst_pad_add_probe (srcpad,
1169 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1170 (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1171 ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1172 gst_object_unref (srcpad);
1175 g_object_set (input_selector, "active-pad", pad, NULL);
1176 gst_object_unref (pad);
1177 gst_object_unref (cpad);
1178 gst_object_unref (input_selector);
1183 /* No selector found -> Failed */
1185 gst_object_unref (pipeline);
1190 static GstPadProbeReturn
1191 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1192 GstValidateAction * action)
1194 if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1195 gst_validate_action_set_done (action);
1196 return GST_PAD_PROBE_REMOVE;
1198 return GST_PAD_PROBE_OK;
1201 static GstValidateExecuteActionReturn
1202 execute_switch_track_pb (GstValidateScenario * scenario,
1203 GstValidateAction * action)
1206 const gchar *type, *str_index;
1208 gint flags, current, tflag;
1209 gchar *tmp, *current_txt;
1211 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1212 gboolean relative = FALSE, disabling = FALSE;
1214 DECLARE_AND_GET_PIPELINE (scenario, action);
1216 if (!(type = gst_structure_get_string (action->structure, "type")))
1219 tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1221 current_txt = g_strdup_printf ("current-%s", type);
1223 tmp = g_strdup_printf ("n-%s", type);
1224 g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, ¤t,
1227 /* Don't try to use -1 */
1233 if (gst_structure_has_field (action->structure, "disable")) {
1237 } else if (!(str_index =
1238 gst_structure_get_string (action->structure, "index"))) {
1239 if (!gst_structure_get_int (action->structure, "index", &index)) {
1240 GST_WARNING ("No index given, defaulting to +1");
1245 relative = strchr ("+-", str_index[0]) != NULL;
1246 index = g_ascii_strtoll (str_index, NULL, 10);
1249 if (relative) { /* We are changing track relatively to current track */
1251 GST_VALIDATE_REPORT_ACTION (scenario, action,
1252 SCENARIO_ACTION_EXECUTION_ERROR,
1253 "Trying to execute a relative %s for %s track when there"
1254 " is no track of this type available on current stream.",
1255 action->type, type);
1257 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1261 index = (current + index) % n;
1265 GstState state, next;
1266 GstPad *oldpad, *newpad;
1267 tmp = g_strdup_printf ("get-%s-pad", type);
1268 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1269 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1271 gst_validate_printf (action, "Switching to track number: %i,"
1272 " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1273 GST_DEBUG_PAD_NAME (newpad));
1277 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1278 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1279 GstPad *srcpad = NULL;
1280 GstElement *combiner = NULL;
1281 if (newpad == oldpad) {
1282 srcpad = gst_pad_get_peer (oldpad);
1283 } else if (newpad) {
1284 combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1286 srcpad = gst_element_get_static_pad (combiner, "src");
1287 gst_object_unref (combiner);
1292 gst_pad_add_probe (srcpad,
1293 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1294 (GstPadProbeCallback) _check_pad_event_selection_done, action,
1296 gst_object_unref (srcpad);
1298 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1300 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1304 gst_object_unref (oldpad);
1305 gst_object_unref (newpad);
1307 gst_validate_printf (action, "Disabling track type %s", type);
1310 g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1311 g_free (current_txt);
1314 gst_object_unref (pipeline);
1318 static GstStreamType
1319 stream_type_from_string (const gchar * type)
1321 if (!g_strcmp0 (type, "video"))
1322 return GST_STREAM_TYPE_VIDEO;
1323 else if (!g_strcmp0 (type, "text"))
1324 return GST_STREAM_TYPE_TEXT;
1327 return GST_STREAM_TYPE_AUDIO;
1330 /* Return a list of stream ID all the currently selected streams but the ones
1333 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1335 GList *streams = NULL, *l;
1337 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1338 GstStream *s = l->data;
1340 if (gst_stream_get_stream_type (s) != type) {
1341 streams = g_list_append (streams, (gpointer) s->stream_id);
1349 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1350 GstStreamType type, gint index, gboolean relative)
1353 guint i, n = 0, current = 0;
1354 GList *result = NULL, *l;
1355 GstStream *streams[256], *s, *current_stream = NULL;
1357 /* Keep all streams which are not @type */
1358 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1361 if (gst_stream_get_stream_type (s) != type) {
1362 result = g_list_append (result, (gpointer) s->stream_id);
1363 } else if (!current_stream) {
1364 /* Assume the stream we want to switch from is the first one */
1369 /* Calculate the number of @type streams */
1370 nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1371 for (i = 0; i < nb_streams; i++) {
1372 s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1374 if (gst_stream_get_stream_type (s) == type) {
1378 && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1385 if (G_UNLIKELY (n == 0)) {
1386 GST_ERROR ("No streams available of the required type");
1390 if (relative) { /* We are changing track relatively to current track */
1391 index = (current + index) % n;
1395 /* Add the new stream we want to switch to */
1398 gst_validate_printf (action, "Switching from stream %s to %s",
1399 current_stream ? current_stream->stream_id : "", s->stream_id);
1401 return g_list_append (result, (gpointer) s->stream_id);
1404 static GstValidateExecuteActionReturn
1405 execute_switch_track_pb3 (GstValidateScenario * scenario,
1406 GstValidateAction * action)
1408 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1409 GstValidateScenarioPrivate *priv = scenario->priv;
1411 GstStreamType stype;
1412 const gchar *type, *str_index;
1413 GList *new_streams = NULL;
1414 GstValidatePipelineMonitor *monitor;
1415 DECLARE_AND_GET_PIPELINE (scenario, action);
1417 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1418 pipeline, "validate-monitor"));
1420 if (!monitor->stream_collection) {
1421 GST_VALIDATE_REPORT_ACTION (scenario, action,
1422 SCENARIO_ACTION_EXECUTION_ERROR,
1423 "No stream collection message received on the bus, "
1424 "can not switch track.");
1425 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1429 if (!monitor->streams_selected) {
1430 GST_VALIDATE_REPORT_ACTION (scenario, action,
1431 SCENARIO_ACTION_EXECUTION_ERROR,
1432 "No streams selected message received on the bus");
1433 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1437 type = gst_structure_get_string (action->structure, "type");
1438 stype = stream_type_from_string (type);
1440 if (gst_structure_has_field (action->structure, "disable")) {
1441 gst_validate_printf (action, "Disabling track type %s", type);
1442 new_streams = disable_stream (monitor, stype);
1444 gboolean relative = FALSE;
1446 if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
1447 if (!gst_structure_get_int (action->structure, "index", &index)) {
1448 GST_WARNING ("No index given, defaulting to +1");
1453 relative = strchr ("+-", str_index[0]) != NULL;
1454 index = g_ascii_strtoll (str_index, NULL, 10);
1457 new_streams = switch_stream (monitor, action, stype, index, relative);
1460 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
1461 ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
1462 (GDestroyNotify) g_list_free);
1464 if (!gst_element_send_event (pipeline,
1465 gst_event_new_select_streams (new_streams))) {
1466 GST_VALIDATE_REPORT_ACTION (scenario, action,
1467 SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
1468 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1472 priv->pending_switch_track = action;
1473 if (scenario->priv->target_state > GST_STATE_PAUSED) {
1474 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1476 gst_mini_object_ref ((GstMiniObject *) action);
1477 res = GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
1481 gst_object_unref (pipeline);
1486 static GstValidateExecuteActionReturn
1487 _execute_switch_track (GstValidateScenario * scenario,
1488 GstValidateAction * action)
1490 GstValidatePipelineMonitor *monitor;
1492 DECLARE_AND_GET_PIPELINE (scenario, action);
1494 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1495 pipeline, "validate-monitor"));
1496 gst_object_unref (pipeline);
1498 if (monitor->is_playbin)
1499 return execute_switch_track_pb (scenario, action);
1500 else if (monitor->is_playbin3)
1501 return execute_switch_track_pb3 (scenario, action);
1503 return execute_switch_track_default (scenario, action);
1506 static GstValidateExecuteActionReturn
1507 _execute_set_rank (GstValidateScenario * scenario, GstValidateAction * action)
1510 GList *features, *origlist;
1512 GstPluginFeature *feature;
1515 if (!(name = gst_structure_get_string (action->structure, "feature-name")) &&
1516 !(name = gst_structure_get_string (action->structure, "name"))) {
1517 GST_ERROR ("Could not find the name of the plugin feature(s) to tweak");
1519 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1522 if (!(gst_structure_get_uint (action->structure, "rank", &rank) ||
1523 gst_structure_get_int (action->structure, "rank", (gint *) & rank))) {
1524 GST_ERROR ("Could not get rank to set on %s", name);
1526 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1529 feature = gst_registry_lookup_feature (gst_registry_get (), name);
1531 gst_plugin_feature_set_rank (feature, rank);
1532 gst_object_unref (feature);
1534 return GST_VALIDATE_EXECUTE_ACTION_OK;
1537 plugin = gst_registry_find_plugin (gst_registry_get (), name);
1539 GST_ERROR ("Could not find %s", name);
1541 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1544 origlist = features =
1545 gst_registry_get_feature_list_by_plugin (gst_registry_get (),
1546 gst_plugin_get_name (plugin));
1547 for (; features; features = features->next)
1548 gst_plugin_feature_set_rank (features->data, rank);
1549 gst_plugin_feature_list_free (origlist);
1551 return GST_VALIDATE_EXECUTE_ACTION_OK;
1554 static inline gboolean
1555 _add_execute_actions_gsource (GstValidateScenario * scenario)
1557 GstValidateScenarioPrivate *priv = scenario->priv;
1559 SCENARIO_LOCK (scenario);
1560 if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
1561 && priv->signal_handler_id == 0 && priv->message_type == NULL) {
1562 if (!scenario->priv->action_execution_interval)
1563 priv->execute_actions_source_id =
1564 g_idle_add ((GSourceFunc) execute_next_action, scenario);
1566 priv->execute_actions_source_id =
1567 g_timeout_add (scenario->priv->action_execution_interval,
1568 (GSourceFunc) execute_next_action, scenario);
1569 SCENARIO_UNLOCK (scenario);
1571 GST_DEBUG_OBJECT (scenario, "Start checking position again");
1574 SCENARIO_UNLOCK (scenario);
1576 GST_LOG_OBJECT (scenario, "No need to start a new gsource");
1581 _get_position (GstValidateScenario * scenario,
1582 GstValidateAction * act, GstClockTime * position)
1584 gboolean has_pos = FALSE, has_dur = FALSE;
1585 GstClockTime duration = -1;
1587 GstValidateScenarioPrivate *priv = scenario->priv;
1588 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1591 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1596 has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
1597 (gint64 *) position)
1598 && GST_CLOCK_TIME_IS_VALID (*position);
1600 gst_element_query_duration (pipeline, GST_FORMAT_TIME,
1601 (gint64 *) & duration)
1602 && GST_CLOCK_TIME_IS_VALID (duration);
1604 if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
1605 act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1606 GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
1607 GST_TIME_ARGS (*position));
1612 if (has_pos && has_dur && !priv->got_eos) {
1613 if (*position > duration) {
1614 _add_execute_actions_gsource (scenario);
1615 GST_VALIDATE_REPORT (scenario,
1616 QUERY_POSITION_SUPERIOR_DURATION,
1617 "Reported position %" GST_TIME_FORMAT " > reported duration %"
1618 GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
1625 gst_object_unref (pipeline);
1629 gst_object_unref (pipeline);
1634 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
1635 GstClockTime * position, gdouble * rate)
1639 GstClockTime start_with_tolerance, stop_with_tolerance;
1640 GstValidateScenarioPrivate *priv = scenario->priv;
1641 GstElement *pipeline;
1643 if (!_get_position (scenario, act, position))
1646 GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
1647 GST_TIME_ARGS (*position));
1649 /* Check if playback is within seek segment */
1650 start_with_tolerance = (priv->segment_start <
1651 priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
1652 stop_with_tolerance =
1653 GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
1654 priv->seek_pos_tol : -1;
1656 if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
1657 && *position > stop_with_tolerance)
1658 || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
1659 && *position < start_with_tolerance
1660 && priv->seek_format == GST_FORMAT_TIME)) {
1662 GST_VALIDATE_REPORT_ACTION (scenario, act, QUERY_POSITION_OUT_OF_SEGMENT,
1663 "Current position %" GST_TIME_FORMAT " not in the expected range [%"
1664 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
1665 GST_TIME_ARGS (start_with_tolerance),
1666 GST_TIME_ARGS (stop_with_tolerance));
1669 pipeline = gst_validate_scenario_get_pipeline (scenario);
1670 if (pipeline == NULL) {
1671 GST_INFO_OBJECT (scenario, "No pipeline set anymore");
1676 query = gst_query_new_segment (GST_FORMAT_DEFAULT);
1677 if (gst_element_query (GST_ELEMENT (pipeline), query))
1678 gst_query_parse_segment (query, rate, NULL, NULL, NULL);
1679 gst_query_unref (query);
1680 gst_object_unref (pipeline);
1682 if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE &&
1683 priv->seek_format == GST_FORMAT_TIME) {
1685 && (GstClockTime) ABS (GST_CLOCK_DIFF (*position,
1686 priv->segment_start)) > priv->seek_pos_tol) {
1687 priv->seeked_in_pause = FALSE;
1688 GST_VALIDATE_REPORT_ACTION (scenario, act,
1689 EVENT_SEEK_RESULT_POSITION_WRONG,
1690 "Reported position after accurate seek in PAUSED state should be exactly"
1691 " what the user asked for. Position %" GST_TIME_FORMAT
1692 " is not not the expected one: %" GST_TIME_FORMAT,
1693 GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
1701 _check_message_type (GstValidateScenario * scenario, GstValidateAction * act,
1702 GstMessage * message)
1704 return act && message
1705 && !g_strcmp0 (gst_structure_get_string (act->structure, "on-message"),
1706 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
1710 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
1711 GstClockTime position, gdouble rate)
1713 GstElement *pipeline;
1716 GST_DEBUG_OBJECT (scenario, "No action to execute");
1721 pipeline = gst_validate_scenario_get_pipeline (scenario);
1722 if (pipeline == NULL) {
1724 if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
1725 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
1726 GST_VALIDATE_REPORT_ACTION (scenario, act,
1727 SCENARIO_ACTION_EXECUTION_ERROR,
1728 "Trying to execute an %s action after the pipeline has been destroyed"
1729 " but the type has not been marked as "
1730 "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
1733 } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1734 GST_VALIDATE_REPORT_ACTION (scenario, act,
1735 SCENARIO_ACTION_EXECUTION_ERROR,
1736 "Trying to execute action %s with playback time %" GST_TIME_FORMAT
1737 " after the pipeline has been destroyed. It is impossible"
1738 " to execute an action with a playback time specified"
1739 " after the pipeline has been destroyed", act->type,
1740 GST_TIME_ARGS (act->playback_time));
1745 GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
1748 } else if (scenario->priv->got_eos) {
1749 GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
1750 scenario->priv->got_eos = FALSE;
1751 } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
1752 GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
1753 "just executing actions");
1756 } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
1757 GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
1760 } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
1761 GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
1762 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1763 GST_TIME_ARGS (act->playback_time));
1766 } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
1767 GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
1768 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1769 GST_TIME_ARGS (act->playback_time));
1775 gst_object_unref (pipeline);
1779 gst_object_unref (pipeline);
1784 _set_action_playback_time (GstValidateScenario * scenario,
1785 GstValidateAction * action)
1787 if (!gst_validate_action_get_clocktime (scenario, action,
1788 "playback-time", &action->playback_time)) {
1789 gchar *str = gst_structure_to_string (action->structure);
1791 g_error ("Could not parse playback-time on structure: %s", str);
1797 gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
1798 action->playback_time, NULL);
1804 gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
1806 GstValidateAction *action;
1807 GstValidateScenarioPrivate *priv = self->priv;
1812 action = (GstValidateAction *) priv->actions->data;
1813 if (!action->priv->needs_playback_parsing)
1816 if (!_set_action_playback_time (self, action)) {
1817 GST_ERROR_OBJECT (self, "Could not set playback_time!");
1821 action->priv->needs_playback_parsing = FALSE;
1826 GstValidateExecuteActionReturn
1827 gst_validate_execute_action (GstValidateActionType * action_type,
1828 GstValidateAction * action)
1830 GstValidateExecuteActionReturn res;
1831 GstValidateScenario *scenario;
1833 g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
1834 GST_VALIDATE_EXECUTE_ACTION_ERROR);
1836 scenario = gst_validate_action_get_scenario (action);
1838 if (action_type->prepare) {
1839 res = action_type->prepare (action);
1840 if (res != GST_VALIDATE_EXECUTE_ACTION_OK) {
1841 GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
1842 " could not be prepared", action->structure);
1844 gst_object_unref (scenario);
1849 gst_validate_print_action (action, NULL);
1851 action->priv->execution_time = gst_util_get_timestamp ();
1852 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
1853 res = action_type->execute (scenario, action);
1854 gst_object_unref (scenario);
1856 if (!gst_structure_has_field (action->structure, "sub-action")) {
1857 gst_structure_free (action->structure);
1858 action->priv->printed = FALSE;
1859 action->structure = gst_structure_copy (action->priv->main_structure);
1861 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1864 if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC)
1865 action->priv->executing_last_subaction = TRUE;
1871 /* scenario can be NULL **only** if the action is a CONFIG action and
1872 * add_to_lists is FALSE */
1873 static GstValidateExecuteActionReturn
1874 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
1875 GstStructure * structure, gboolean add_to_lists)
1877 gdouble playback_time;
1878 gboolean is_config = FALSE;
1879 GstValidateActionType *action_type;
1880 const gchar *str_playback_time = NULL;
1881 GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
1882 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_NONE;
1883 gboolean optional, needs_parsing = FALSE;
1885 action->type = gst_structure_get_name (structure);
1886 action_type = _find_action_type (action->type);
1889 GST_ERROR_OBJECT (scenario, "Action type %s no found",
1890 gst_structure_get_name (structure));
1892 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1895 if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
1896 gst_structure_get_double (structure, "playback_time", &playback_time)) {
1897 action->playback_time = playback_time * GST_SECOND;
1898 } else if ((str_playback_time =
1899 gst_structure_get_string (structure, "playback-time")) ||
1900 (str_playback_time =
1901 gst_structure_get_string (structure, "playback_time"))) {
1903 if (add_to_lists && priv) {
1904 action->priv->needs_playback_parsing = TRUE;
1905 needs_parsing = TRUE;
1908 GST_INFO_OBJECT (scenario,
1909 "No playback time for action %" GST_PTR_FORMAT, structure);
1911 if (!gst_validate_utils_get_clocktime (structure,
1912 "timeout", &action->priv->timeout)) {
1913 GST_INFO_OBJECT (scenario,
1914 "No timeout time for action %" GST_PTR_FORMAT, structure);
1917 action->structure = gst_structure_copy (structure);
1919 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1922 if (!action->priv->main_structure)
1923 action->priv->main_structure = gst_structure_copy (structure);
1925 if (gst_structure_get_boolean (structure, "optional", &optional)) {
1926 if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
1927 GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
1928 gst_structure_get_name (structure));
1929 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1931 action->priv->optional = optional;
1934 if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
1935 (gst_structure_get_boolean (action->structure, "as-config",
1936 &is_config) && is_config == TRUE)) {
1938 res = action_type->execute (scenario, action);
1939 gst_validate_print_action (action, NULL);
1948 GstValidateActionType *type = _find_action_type (action->type);
1949 gboolean can_execute_on_addition =
1950 type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
1951 && !GST_CLOCK_TIME_IS_VALID (action->playback_time)
1952 && !gst_structure_has_field (action->structure, "on-message");
1955 can_execute_on_addition = FALSE;
1957 if (can_execute_on_addition) {
1960 for (tmp = priv->actions; tmp; tmp = tmp->next) {
1961 GstValidateAction *act = (GstValidateAction *) tmp->data;
1962 if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1963 can_execute_on_addition = FALSE;
1970 if (can_execute_on_addition) {
1971 SCENARIO_LOCK (scenario);
1972 priv->on_addition_actions = g_list_append (priv->on_addition_actions,
1974 SCENARIO_UNLOCK (scenario);
1977 priv->actions = g_list_append (priv->actions, action);
1984 static GstValidateExecuteActionReturn
1985 _execute_sub_action_action (GstValidateAction * action)
1987 const gchar *subaction_str;
1988 GstStructure *subaction_struct = NULL;
1989 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1990 GstValidateScenario *scenario = NULL;
1992 if (action->priv->executing_last_subaction) {
1993 action->priv->executing_last_subaction = FALSE;
1998 scenario = gst_validate_action_get_scenario (action);
1999 g_assert (scenario);
2000 subaction_str = gst_structure_get_string (action->structure, "sub-action");
2001 if (subaction_str) {
2002 subaction_struct = gst_structure_from_string (subaction_str, NULL);
2004 if (subaction_struct == NULL) {
2005 GST_VALIDATE_REPORT_ACTION (scenario, action, SCENARIO_FILE_MALFORMED,
2006 "Sub action %s could not be parsed", subaction_str);
2008 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
2013 gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE,
2014 &subaction_struct, NULL);
2017 if (subaction_struct) {
2018 if (action->structure) {
2019 GST_INFO_OBJECT (scenario, "Clearing old action structure");
2020 gst_structure_free (action->structure);
2023 res = _fill_action (scenario, action, subaction_struct, FALSE);
2024 if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2025 GST_VALIDATE_REPORT_ACTION (scenario, action,
2026 SCENARIO_ACTION_EXECUTION_ERROR,
2027 "Sub action %" GST_PTR_FORMAT " could not be filled",
2033 if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) {
2034 GstValidateActionType *action_type = _find_action_type (action->type);
2036 action->priv->printed = FALSE;
2037 res = gst_validate_execute_action (action_type, action);
2046 gst_object_unref (scenario);
2047 if (subaction_struct)
2048 gst_structure_free (subaction_struct);
2053 /* This is the main action execution function
2054 * it checks whether it is time to run the next action
2055 * and if it is the case executes it.
2057 * If the 'execute-on-idle' property is not TRUE,
2058 * the function will recurse while the actions are run
2062 execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
2066 GstClockTime position = -1;
2067 GstValidateAction *act = NULL;
2068 GstValidateActionType *type;
2070 GstValidateScenarioPrivate *priv = scenario->priv;
2072 if (priv->buffering) {
2073 GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
2075 return G_SOURCE_CONTINUE;
2078 if (priv->changing_state || priv->needs_async_done) {
2079 GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
2080 return G_SOURCE_CONTINUE;
2083 if (scenario->priv->actions)
2084 act = scenario->priv->actions->data;
2088 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS) {
2089 return G_SOURCE_CONTINUE;
2090 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK
2091 && act->repeat <= 0) {
2092 tmp = priv->actions;
2093 priv->actions = g_list_remove_link (priv->actions, tmp);
2095 if (!gst_validate_parse_next_action_playback_time (scenario)) {
2096 g_error ("Could not determine next action playback time!");
2098 return G_SOURCE_REMOVE;
2102 GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
2103 " executing next", act->structure);
2105 gst_validate_action_unref (act);
2108 if (scenario->priv->actions) {
2109 act = scenario->priv->actions->data;
2111 _check_scenario_is_done (scenario);
2114 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
2115 if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
2116 GstClockTime etime =
2117 gst_util_get_timestamp () - act->priv->execution_time;
2119 if (etime > act->priv->timeout) {
2120 gchar *str = gst_structure_to_string (act->structure);
2122 GST_VALIDATE_REPORT_ACTION (scenario, act,
2123 SCENARIO_ACTION_EXECUTION_ERROR,
2124 "Action %s timed out after: %" GST_TIME_FORMAT, str,
2125 GST_TIME_ARGS (etime));
2130 GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2133 return G_SOURCE_CONTINUE;
2138 if (!_check_message_type (scenario, act, message))
2139 return G_SOURCE_CONTINUE;
2140 } else if ((act && gst_structure_get_string (act->structure, "on-message") &&
2141 !GST_CLOCK_TIME_IS_VALID (act->playback_time)) ||
2142 (!_check_position (scenario, act, &position, &rate))) {
2143 return G_SOURCE_CONTINUE;
2146 if (!_should_execute_action (scenario, act, position, rate)) {
2147 _add_execute_actions_gsource (scenario);
2149 return G_SOURCE_CONTINUE;
2152 type = _find_action_type (act->type);
2154 GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2155 " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2156 priv->seeked_in_pause = FALSE;
2159 gst_structure_remove_field (act->structure, "playback-time");
2161 gst_structure_remove_field (act->structure, "on-message");
2163 act->priv->state = gst_validate_execute_action (type, act);
2164 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2165 gchar *str = gst_structure_to_string (act->structure);
2167 GST_VALIDATE_REPORT_ACTION (scenario, act,
2168 SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str);
2173 if (act->repeat > 0 && !gst_validate_action_is_subaction (act)) {
2177 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
2178 act->priv->state = _execute_sub_action_action (act);
2181 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC && act->repeat <= 0) {
2182 tmp = priv->actions;
2183 priv->actions = g_list_remove_link (priv->actions, tmp);
2185 if (!gst_validate_parse_next_action_playback_time (scenario)) {
2186 g_error ("Could not determine next action playback time!");
2188 return G_SOURCE_REMOVE;
2191 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED)
2192 gst_validate_action_unref (act);
2194 SCENARIO_LOCK (scenario);
2195 priv->interlaced_actions = g_list_append (priv->interlaced_actions, act);
2196 SCENARIO_UNLOCK (scenario);
2199 if (priv->actions == NULL)
2200 _check_scenario_is_done (scenario);
2204 /* Recurse to the next action if it is possible
2205 * to execute right away */
2206 if (!scenario->priv->execute_on_idle) {
2207 GST_DEBUG_OBJECT (scenario, "linking next action execution");
2209 return execute_next_action (scenario);
2211 _add_execute_actions_gsource (scenario);
2212 GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2215 return G_SOURCE_CONTINUE;
2218 GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2221 SCENARIO_LOCK (scenario);
2222 priv->execute_actions_source_id = 0;
2223 SCENARIO_UNLOCK (scenario);
2225 return G_SOURCE_CONTINUE;
2228 return G_SOURCE_CONTINUE;
2232 execute_next_action (GstValidateScenario * scenario)
2234 return execute_next_action_full (scenario, NULL);
2238 stop_waiting (GstValidateAction * action)
2240 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2242 gst_validate_printf (scenario, "Stop waiting\n");
2244 SCENARIO_LOCK (scenario);
2245 scenario->priv->wait_id = 0;
2246 SCENARIO_UNLOCK (scenario);
2248 gst_validate_action_set_done (action);
2249 _add_execute_actions_gsource (scenario);
2250 gst_object_unref (scenario);
2253 return G_SOURCE_REMOVE;
2256 static GstElement *_get_target_element (GstValidateScenario * scenario,
2257 GstValidateAction * action);
2260 stop_waiting_signal (GstBin * bin, GstElement * element,
2261 GstValidateAction * action)
2263 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2264 GstValidateScenarioPrivate *priv = scenario->priv;
2266 g_assert (scenario);
2267 gst_validate_printf (scenario, "Stop waiting for signal\n");
2269 g_signal_handler_disconnect (bin, priv->signal_handler_id);
2271 priv->signal_handler_id = 0;
2272 gst_validate_action_set_done (action);
2273 _add_execute_actions_gsource (scenario);
2274 gst_object_unref (scenario);
2277 static GstValidateExecuteActionReturn
2278 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2280 GstValidateScenarioPrivate *priv = scenario->priv;
2281 GstClockTime duration;
2283 gdouble wait_multiplier = 1;
2284 const gchar *str_wait_multiplier =
2285 g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2287 if (str_wait_multiplier) {
2289 wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2292 GST_ERROR ("Could not use the WAIT MULTIPLIER");
2294 wait_multiplier = 1;
2297 if (wait_multiplier == 0) {
2298 GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2299 return GST_VALIDATE_EXECUTE_ACTION_OK;
2303 if (!gst_validate_action_get_clocktime (scenario, action,
2304 "duration", &duration)) {
2305 GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2307 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2310 duration *= wait_multiplier;
2312 SCENARIO_LOCK (scenario);
2313 if (priv->execute_actions_source_id) {
2314 g_source_remove (priv->execute_actions_source_id);
2315 priv->execute_actions_source_id = 0;
2317 SCENARIO_UNLOCK (scenario);
2319 SCENARIO_LOCK (scenario);
2320 priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2321 (GSourceFunc) stop_waiting, action);
2322 SCENARIO_UNLOCK (scenario);
2324 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2327 static GstValidateExecuteActionReturn
2328 _execute_wait_for_signal (GstValidateScenario * scenario,
2329 GstValidateAction * action)
2331 GstValidateScenarioPrivate *priv = scenario->priv;
2332 const gchar *signal_name = gst_structure_get_string
2333 (action->structure, "signal-name");
2335 DECLARE_AND_GET_PIPELINE (scenario, action);
2337 if (signal_name == NULL) {
2338 GST_ERROR ("No signal-name given for wait action");
2339 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2342 target = _get_target_element (scenario, action);
2343 if (target == NULL) {
2344 gst_object_unref (pipeline);
2349 gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2351 if (priv->execute_actions_source_id) {
2352 g_source_remove (priv->execute_actions_source_id);
2353 priv->execute_actions_source_id = 0;
2356 priv->signal_handler_id =
2357 g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal,
2360 gst_object_unref (target);
2361 gst_object_unref (pipeline);
2363 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2367 _execute_wait_for_message (GstValidateScenario * scenario,
2368 GstValidateAction * action)
2370 GstValidateScenarioPrivate *priv = scenario->priv;
2371 const gchar *message_type = gst_structure_get_string
2372 (action->structure, "message-type");
2373 DECLARE_AND_GET_PIPELINE (scenario, action);
2375 gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2377 if (priv->execute_actions_source_id) {
2378 g_source_remove (priv->execute_actions_source_id);
2379 priv->execute_actions_source_id = 0;
2382 priv->message_type = g_strdup (message_type);
2383 gst_object_unref (pipeline);
2385 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2388 static GstValidateExecuteActionReturn
2389 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2391 if (gst_structure_has_field (action->structure, "signal-name")) {
2392 return _execute_wait_for_signal (scenario, action);
2393 } else if (gst_structure_has_field (action->structure, "message-type")) {
2394 return _execute_wait_for_message (scenario, action);
2396 return _execute_timed_wait (scenario, action);
2403 _execute_dot_pipeline (GstValidateScenario * scenario,
2404 GstValidateAction * action)
2407 gint details = GST_DEBUG_GRAPH_SHOW_ALL;
2408 const gchar *name = gst_structure_get_string (action->structure, "name");
2409 DECLARE_AND_GET_PIPELINE (scenario, action);
2411 gst_structure_get_int (action->structure, "details", &details);
2413 dotname = g_strdup_printf ("validate.action.%s", name);
2415 dotname = g_strdup ("validate.action.unnamed");
2417 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
2420 gst_object_unref (pipeline);
2426 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
2430 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2433 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2438 name = gst_structure_get_string (action->structure, "target-element-name");
2440 gst_object_unref (pipeline);
2445 if (g_strcmp0 (GST_OBJECT_NAME (pipeline), name) == 0) {
2446 target = gst_object_ref (pipeline);
2448 target = gst_bin_get_by_name (GST_BIN (pipeline), name);
2452 GST_ERROR ("Target element with given name (%s) not found", name);
2453 gst_object_unref (pipeline);
2458 /* _get_target_elements_by_klass_or_factory_name:
2459 * @scenario: a #GstValidateScenario
2460 * @action: a #GstValidateAction
2462 * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
2463 * matches the 'target-element-klass' of @action and the factory name matches
2464 * the 'target-element-factory-name'.
2466 * Returns: (transfer full) (element-type GstElement): a list of #GstElement
2469 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
2470 GstValidateAction * action)
2472 GList *result = NULL;
2474 const gchar *klass, *fname;
2475 GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
2476 gboolean done = FALSE;
2477 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2480 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2485 klass = gst_structure_get_string (action->structure, "target-element-klass");
2487 gst_structure_get_string (action->structure,
2488 "target-element-factory-name");
2489 if (!klass && !fname) {
2490 gst_object_unref (pipeline);
2495 if (klass && gst_validate_element_has_klass (pipeline, klass))
2496 result = g_list_prepend (result, gst_object_ref (pipeline));
2498 if (fname && gst_element_get_factory (pipeline)
2499 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
2501 result = g_list_prepend (result, gst_object_ref (pipeline));
2503 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
2505 g_value_init (¶m, G_TYPE_STRING);
2506 g_value_set_string (¶m, klass);
2509 switch (gst_iterator_next (it, &v)) {
2510 case GST_ITERATOR_OK:{
2511 GstElement *child = g_value_get_object (&v);
2513 if (g_list_find (result, child))
2516 if (klass && gst_validate_element_has_klass (child, klass)) {
2517 result = g_list_prepend (result, gst_object_ref (child));
2521 if (fname && gst_element_get_factory (child)
2522 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
2524 result = g_list_prepend (result, gst_object_ref (child));
2529 case GST_ITERATOR_RESYNC:
2530 gst_iterator_resync (it);
2532 case GST_ITERATOR_ERROR:
2533 case GST_ITERATOR_DONE:
2539 g_value_reset (¶m);
2540 gst_iterator_free (it);
2541 gst_object_unref (pipeline);
2547 _find_elements_defined_in_action (GstValidateScenario * scenario,
2548 GstValidateAction * action)
2551 GList *targets = NULL;
2553 /* set-property can be applied on either:
2554 * - a single element having target-element-name as name
2555 * - all the elements having target-element-klass as klass
2557 if (gst_structure_get_string (action->structure, "target-element-name")) {
2558 target = _get_target_element (scenario, action);
2562 targets = g_list_append (targets, target);
2563 } else if (gst_structure_get_string (action->structure,
2564 "target-element-klass") ||
2565 gst_structure_get_string (action->structure,
2566 "target-element-factory-name")) {
2567 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
2573 static GstValidateExecuteActionReturn
2574 _execute_set_or_check_property (GstValidateScenario * scenario,
2575 GstValidateAction * action)
2578 const gchar *property;
2579 const GValue *property_value;
2580 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_OK;
2581 gboolean check = gst_structure_has_name (action->structure, "check-property");
2583 targets = _find_elements_defined_in_action (scenario, action);
2585 GST_VALIDATE_REPORT_ACTION (scenario, action,
2586 SCENARIO_ACTION_EXECUTION_ERROR,
2587 "No element found for action: %" GST_PTR_FORMAT, action->structure);
2589 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2592 property = gst_structure_get_string (action->structure, "property-name");
2593 property_value = gst_structure_get_value (action->structure,
2596 for (l = targets; l != NULL; l = g_list_next (l)) {
2598 GstValidateActionReturn tmpres;
2601 gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
2602 G_OBJECT (l->data), property, property_value, action->priv->optional);
2607 GValue cvalue = G_VALUE_INIT;
2609 g_value_init (&cvalue, G_VALUE_TYPE (property_value));
2610 g_object_get_property (l->data, property, &cvalue);
2612 if (gst_value_compare (&cvalue, property_value) != GST_VALUE_EQUAL) {
2613 gchar *expected = gst_value_serialize (property_value), *observed =
2614 gst_value_serialize (&cvalue);
2616 GST_VALIDATE_REPORT_ACTION (scenario, action,
2617 SCENARIO_ACTION_EXECUTION_ERROR,
2618 "%s::%s expected value: '%s' different than observed: '%s'",
2619 GST_OBJECT_NAME (l->data), property, expected, observed);
2624 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2626 g_value_reset (&cvalue);
2630 g_list_free_full (targets, gst_object_unref);
2635 _execute_set_debug_threshold (GstValidateScenario * scenario,
2636 GstValidateAction * action)
2639 gboolean reset = TRUE;
2640 const gchar *threshold_str;
2643 gst_structure_get_string (action->structure, "debug-threshold");
2644 if (threshold_str == NULL) {
2647 if (gst_structure_get_int (action->structure, "debug-threshold",
2649 threshold_str = str = g_strdup_printf ("%i", threshold);
2654 gst_structure_get_boolean (action->structure, "reset", &reset);
2656 gst_debug_set_threshold_from_string (threshold_str, reset);
2664 _execute_emit_signal (GstValidateScenario * scenario,
2665 GstValidateAction * action)
2668 const gchar *signal_name;
2670 target = _get_target_element (scenario, action);
2671 if (target == NULL) {
2675 signal_name = gst_structure_get_string (action->structure, "signal-name");
2677 /* Right now we don't support arguments to signals as there weren't any use
2678 * cases to cover yet but it should be possible to do so */
2679 g_signal_emit_by_name (target, signal_name, NULL);
2681 gst_object_unref (target);
2685 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
2686 GstBuffer * buffer, gpointer * user_data, gboolean * remove_wrapper);
2688 typedef struct _ChainWrapperFunctionData
2690 GstPadChainFunction wrapped_chain_func;
2691 gpointer wrapped_chain_data;
2692 GDestroyNotify wrapped_chain_notify;
2693 ChainWrapperFunction wrapper_function;
2694 gpointer wrapper_function_user_data;
2695 } ChainWrapperFunctionData;
2697 static GstFlowReturn
2698 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
2700 ChainWrapperFunctionData *data = pad->chaindata;
2702 gboolean remove_wrapper = FALSE;
2704 pad->chainfunc = data->wrapped_chain_func;
2705 pad->chaindata = data->wrapped_chain_data;
2706 pad->chainnotify = data->wrapped_chain_notify;
2708 ret = data->wrapper_function (pad, parent, buffer,
2709 data->wrapper_function_user_data, &remove_wrapper);
2711 if (!remove_wrapper) {
2712 /* The chain function may have changed during the calling (e.g. if it was
2713 * a nested wrapper that decided to remove itself) so we need to update the
2714 * wrapped function just in case. */
2715 data->wrapped_chain_func = pad->chainfunc;
2716 data->wrapped_chain_data = pad->chaindata;
2717 data->wrapped_chain_notify = pad->chainnotify;
2719 /* Restore the wrapper as chain function */
2720 pad->chainfunc = _pad_chain_wrapper;
2721 pad->chaindata = data;
2722 pad->chainnotify = g_free;
2730 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
2733 ChainWrapperFunctionData *data = g_new (ChainWrapperFunctionData, 1);
2734 data->wrapped_chain_func = pad->chainfunc;
2735 data->wrapped_chain_data = pad->chaindata;
2736 data->wrapped_chain_notify = pad->chainnotify;
2737 data->wrapper_function = new_function;
2738 data->wrapper_function_user_data = user_data;
2740 pad->chainfunc = _pad_chain_wrapper;
2741 pad->chaindata = data;
2742 pad->chainnotify = g_free;
2745 static GstFlowReturn
2746 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2747 gpointer * user_data, gboolean * remove_wrapper)
2749 GstValidateAction *action = (GstValidateAction *) user_data;
2750 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2752 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
2753 ret = pad->chainfunc (pad, parent, buffer);
2754 gst_validate_action_set_done (action);
2755 gst_validate_action_unref (action);
2756 *remove_wrapper = TRUE;
2757 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
2758 g_object_unref (scenario);
2763 structure_get_uint64_permissive (const GstStructure * structure,
2764 const gchar * fieldname, guint64 * dest)
2766 const GValue *original;
2767 GValue transformed = G_VALUE_INIT;
2769 original = gst_structure_get_value (structure, fieldname);
2773 g_value_init (&transformed, G_TYPE_UINT64);
2774 if (!g_value_transform (original, &transformed))
2777 *dest = g_value_get_uint64 (&transformed);
2778 g_value_unset (&transformed);
2783 _execute_appsrc_push (GstValidateScenario * scenario,
2784 GstValidateAction * action)
2788 gchar *file_contents;
2790 GError *error = NULL;
2794 gint push_buffer_ret;
2797 /* We will only wait for the the buffer to be pushed if we are in a state
2798 * that allows flow of buffers (>=PAUSED). Otherwise the buffer will just
2800 wait = scenario->priv->target_state >= GST_STATE_PAUSED;
2802 target = _get_target_element (scenario, action);
2803 if (target == NULL) {
2804 gchar *structure_string = gst_structure_to_string (action->structure);
2805 GST_VALIDATE_REPORT_ACTION (scenario, action,
2806 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
2808 g_free (structure_string);
2809 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2813 g_strdup (gst_structure_get_string (action->structure, "file-name"));
2814 if (file_name == NULL) {
2815 gchar *structure_string = gst_structure_to_string (action->structure);
2816 GST_VALIDATE_REPORT_ACTION (scenario, action,
2817 SCENARIO_ACTION_EXECUTION_ERROR, "Missing file-name property: %s",
2819 g_free (structure_string);
2820 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2823 structure_get_uint64_permissive (action->structure, "offset", &offset);
2824 structure_get_uint64_permissive (action->structure, "size", &size);
2826 g_file_get_contents (file_name, &file_contents, &file_length, &error);
2827 if (error != NULL) {
2828 gchar *structure_string = gst_structure_to_string (action->structure);
2829 GST_VALIDATE_REPORT_ACTION (scenario, action,
2830 SCENARIO_ACTION_EXECUTION_ERROR,
2831 "Could not open file for action: %s. Error: %s", structure_string,
2833 g_free (structure_string);
2834 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2836 buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, file_contents,
2837 file_length, offset, (size == -1 ? file_length : size), NULL, g_free);
2840 const GValue *caps_value;
2841 caps_value = gst_structure_get_value (action->structure, "caps");
2843 g_object_set (target, "caps", gst_value_get_caps (caps_value), NULL);
2846 /* We temporarily override the peer pad chain function to finish the action
2847 * once the buffer chain actually ends. */
2849 GstPad *appsrc_pad = gst_element_get_static_pad (target, "src");
2850 GstPad *peer_pad = gst_pad_get_peer (appsrc_pad);
2852 gchar *structure_string = gst_structure_to_string (action->structure);
2853 GST_VALIDATE_REPORT_ACTION (scenario, action,
2854 SCENARIO_ACTION_EXECUTION_ERROR, "Action failed, pad not linked: %s",
2856 g_free (structure_string);
2857 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2860 wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
2862 gst_object_unref (appsrc_pad);
2863 gst_object_unref (peer_pad);
2866 /* Keep the action alive until set done is called. */
2867 gst_validate_action_ref (action);
2869 g_signal_emit_by_name (target, "push-buffer", buffer, &push_buffer_ret);
2870 gst_buffer_unref (buffer);
2871 if (push_buffer_ret != GST_FLOW_OK) {
2872 gchar *structure_string = gst_structure_to_string (action->structure);
2873 GST_VALIDATE_REPORT_ACTION (scenario, action,
2874 SCENARIO_ACTION_EXECUTION_ERROR,
2875 "push-buffer signal failed in action: %s", structure_string);
2876 g_free (structure_string);
2877 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2881 gst_object_unref (target);
2884 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2886 gst_validate_printf (NULL,
2887 "Pipeline is not ready to push buffers, interlacing appsrc-push action...");
2888 return GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
2893 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
2898 target = _get_target_element (scenario, action);
2899 if (target == NULL) {
2900 gchar *structure_string = gst_structure_to_string (action->structure);
2901 GST_VALIDATE_REPORT_ACTION (scenario, action,
2902 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
2904 g_free (structure_string);
2905 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2908 g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
2909 if (eos_ret != GST_FLOW_OK) {
2910 gchar *structure_string = gst_structure_to_string (action->structure);
2911 GST_VALIDATE_REPORT_ACTION (scenario, action,
2912 SCENARIO_ACTION_EXECUTION_ERROR,
2913 "Failed to emit end-of-stream signal for action: %s", structure_string);
2914 g_free (structure_string);
2915 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2918 gst_object_unref (target);
2919 return GST_VALIDATE_EXECUTE_ACTION_OK;
2923 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
2927 gboolean reset_time = TRUE;
2929 target = _get_target_element (scenario, action);
2930 if (target == NULL) {
2931 gchar *structure_string = gst_structure_to_string (action->structure);
2932 GST_VALIDATE_REPORT_ACTION (scenario, action,
2933 SCENARIO_ACTION_EXECUTION_ERROR, "No element found for action: %s",
2935 g_free (structure_string);
2936 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2939 gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
2941 event = gst_event_new_flush_start ();
2942 if (!gst_element_send_event (target, event)) {
2943 GST_VALIDATE_REPORT_ACTION (scenario, action,
2944 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_START event was not handled");
2945 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2948 event = gst_event_new_flush_stop (reset_time);
2949 if (!gst_element_send_event (target, event)) {
2950 GST_VALIDATE_REPORT_ACTION (scenario, action,
2951 SCENARIO_ACTION_EXECUTION_ERROR, "FLUSH_STOP event was not handled");
2952 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2955 return GST_VALIDATE_EXECUTE_ACTION_OK;
2958 static GstValidateExecuteActionReturn
2959 _execute_disable_plugin (GstValidateScenario * scenario,
2960 GstValidateAction * action)
2963 const gchar *plugin_name;
2965 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
2967 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
2969 if (plugin == NULL) {
2970 GST_VALIDATE_REPORT_ACTION (scenario, action,
2971 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find plugin to disable: %s",
2974 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2977 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
2978 gst_registry_remove_plugin (gst_registry_get (), plugin);
2980 return GST_VALIDATE_EXECUTE_ACTION_OK;
2984 gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario,
2987 GstValidateScenarioPrivate *priv = scenario->priv;
2989 GstSeekType start_type, stop_type;
2991 gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start,
2994 if (start_type == GST_SEEK_TYPE_SET) {
2995 priv->segment_start = start;
2996 } else if (start_type == GST_SEEK_TYPE_END) {
3000 if (stop_type == GST_SEEK_TYPE_SET) {
3001 priv->segment_stop = stop;
3002 } else if (stop_type == GST_SEEK_TYPE_END) {
3007 static GstValidateExecuteActionReturn
3008 gst_validate_action_default_prepare_func (GstValidateAction * action)
3013 gchar *error = NULL;
3014 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3015 GstValidateActionType *type = gst_validate_get_action_type (action->type);
3016 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3018 _update_well_known_vars (scenario);
3019 gst_validate_structure_resolve_variables (action->structure,
3020 scenario->priv->vars);
3021 for (i = 0; type->parameters[i].name; i++) {
3022 if (type->parameters[i].types &&
3023 g_str_has_suffix (type->parameters[i].types, "(GstClockTime)"))
3024 gst_validate_action_get_clocktime (scenario, action,
3025 type->parameters[i].name, &tmp);
3028 if (action->repeat > 0)
3031 if (!gst_structure_has_field (action->structure, "repeat"))
3034 if (gst_structure_get_int (action->structure, "repeat", &action->repeat))
3037 if (gst_structure_get_double (action->structure, "repeat",
3038 (gdouble *) & action->repeat))
3042 g_strdup (gst_structure_get_string (action->structure, "repeat"));
3044 g_error ("Invalid value for 'repeat' in %s",
3045 gst_structure_to_string (action->structure));
3051 gst_validate_utils_parse_expression (repeat_expr, _set_variable_func,
3054 g_error ("Invalid value for 'repeat' in %s: %s",
3055 gst_structure_to_string (action->structure), error);
3059 g_free (repeat_expr);
3061 gst_structure_set (action->structure, "repeat", G_TYPE_INT, action->repeat,
3063 gst_structure_set (action->priv->main_structure, "repeat", G_TYPE_INT,
3064 action->repeat, NULL);
3068 gst_object_unref (scenario);
3072 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
3077 _check_waiting_for_message (GstValidateScenario * scenario,
3078 GstMessage * message)
3080 GstValidateScenarioPrivate *priv = scenario->priv;
3082 if (!g_strcmp0 (priv->message_type,
3083 gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
3084 GstValidateAction *action = scenario->priv->actions->data;
3086 g_free ((gpointer) priv->message_type);
3087 priv->message_type = NULL;
3089 gst_validate_printf (scenario, "Stop waiting for message\n");
3091 gst_validate_action_set_done (action);
3092 _add_execute_actions_gsource (scenario);
3097 streams_list_contain (GList * streams, const gchar * stream_id)
3101 for (l = streams; l; l = g_list_next (l)) {
3102 GstStream *s = l->data;
3104 if (!g_strcmp0 (s->stream_id, stream_id))
3112 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
3113 GstElement * pipeline)
3115 GstValidateScenarioPrivate *priv = scenario->priv;
3117 GstClockTime min_latency;
3119 query = gst_query_new_latency ();
3120 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
3121 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3122 "Failed to perform LATENCY query");
3123 gst_query_unref (query);
3127 gst_query_parse_latency (query, NULL, &min_latency, NULL);
3128 gst_query_unref (query);
3129 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
3130 " max allowed: %" GST_TIME_FORMAT,
3131 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
3133 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
3134 min_latency > priv->max_latency) {
3135 GST_VALIDATE_REPORT (scenario, CONFIG_LATENCY_TOO_HIGH,
3136 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
3137 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
3138 GST_TIME_ARGS (priv->max_latency));
3143 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
3145 gboolean is_error = FALSE;
3146 GstValidateScenarioPrivate *priv = scenario->priv;
3147 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3150 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3155 switch (GST_MESSAGE_TYPE (message)) {
3156 case GST_MESSAGE_ASYNC_DONE:
3157 if (priv->last_seek) {
3158 gst_validate_scenario_update_segment_from_seek (scenario,
3161 if (priv->target_state == GST_STATE_PAUSED)
3162 priv->seeked_in_pause = TRUE;
3164 gst_event_replace (&priv->last_seek, NULL);
3165 gst_validate_action_set_done (priv->actions->data);
3166 } else if (scenario->priv->needs_async_done) {
3167 scenario->priv->needs_async_done = FALSE;
3168 if (priv->actions && _action_sets_state (priv->actions->data)
3169 && !priv->changing_state)
3170 gst_validate_action_set_done (priv->actions->data);
3174 if (scenario->priv->needs_playback_parsing) {
3175 scenario->priv->needs_playback_parsing = FALSE;
3176 if (!gst_validate_parse_next_action_playback_time (scenario))
3179 _add_execute_actions_gsource (scenario);
3181 case GST_MESSAGE_STATE_CHANGED:
3183 if (pipeline && GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
3184 GstState nstate, pstate;
3186 gst_message_parse_state_changed (message, &pstate, &nstate, NULL);
3188 if (scenario->priv->changing_state &&
3189 scenario->priv->target_state == nstate) {
3190 scenario->priv->changing_state = FALSE;
3192 if (priv->actions && _action_sets_state (priv->actions->data) &&
3193 !priv->needs_async_done)
3194 gst_validate_action_set_done (priv->actions->data);
3197 if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED)
3198 _add_execute_actions_gsource (scenario);
3200 /* GstBin only send a new latency message when reaching PLAYING if
3201 * async-handling=true so check the latency manually. */
3202 if (nstate == GST_STATE_PLAYING)
3203 gst_validate_scenario_check_latency (scenario, pipeline);
3207 case GST_MESSAGE_ERROR:
3211 case GST_MESSAGE_EOS:
3213 GstValidateAction *stop_action;
3214 GstValidateActionType *stop_action_type;
3217 if (!is_error && scenario->priv->ignore_eos) {
3218 GST_INFO_OBJECT (scenario, "Got EOS but ignoring it!");
3222 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
3224 /* gst_validate_action_set_done() does not finish the action
3225 * immediately. Instead, it posts a task to the main thread to do most
3226 * of the work in _action_set_done().
3228 * While the EOS handling lock guarantees that if an action had to call
3229 * gst_validate_action_set_done() it has done so, it does not guarantee
3230 * that _action_set_done() has been called.
3232 * Is it possible that this handler is run before _action_set_done(), so
3233 * we check at this point for actions that have a pending_set_done and
3234 * call it before continuing. */
3235 GList *actions = g_list_copy (priv->actions);
3237 for (i = actions; i; i = i->next) {
3238 GstValidateAction *action = (GstValidateAction *) i->data;
3239 if (action->priv->pending_set_done)
3240 _action_set_done (action);
3242 g_list_free (actions);
3246 priv->got_eos = TRUE;
3247 if (priv->message_type) {
3249 if (priv->actions->next) {
3250 GST_DEBUG_OBJECT (scenario,
3251 "Waiting for a message and got a next action"
3252 " to execute, letting it a chance!");
3253 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3256 /* Clear current message wait if waiting for EOS */
3257 _check_waiting_for_message (scenario, message);
3262 SCENARIO_LOCK (scenario);
3263 if (scenario->priv->actions || scenario->priv->interlaced_actions ||
3264 scenario->priv->on_addition_actions) {
3265 guint nb_actions = 0;
3266 gchar *actions = g_strdup (""), *tmpconcat;
3268 GList *all_actions =
3269 g_list_concat (g_list_concat (scenario->priv->actions,
3270 scenario->priv->interlaced_actions),
3271 scenario->priv->on_addition_actions);
3273 for (tmp = all_actions; tmp; tmp = tmp->next) {
3274 gchar *action_string;
3275 GstValidateAction *action = (GstValidateAction *) tmp->data;
3276 GstValidateActionType *type = _find_action_type (action->type);
3278 tmpconcat = actions;
3280 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
3281 action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
3282 action->priv->optional) {
3283 gst_validate_action_unref (action);
3290 action_string = gst_structure_to_string (action->structure);
3292 g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string);
3293 gst_validate_action_unref (action);
3295 g_free (action_string);
3297 g_list_free (all_actions);
3298 scenario->priv->actions = NULL;
3299 scenario->priv->interlaced_actions = NULL;
3300 scenario->priv->on_addition_actions = NULL;
3303 if (nb_actions > 0) {
3304 GstClockTime position = GST_CLOCK_TIME_NONE;
3306 _get_position (scenario, NULL, &position);
3307 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
3308 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
3309 ")", nb_actions, actions, GST_TIME_ARGS (position));
3313 /* Make sure that if there is an ASYNC_DONE in the message queue, we do not
3314 take it into account */
3315 gst_event_replace (&priv->last_seek, NULL);
3316 SCENARIO_UNLOCK (scenario);
3318 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
3320 stop_action_type = _find_action_type ("stop");
3321 s = gst_structure_from_string ("stop, generated-after-eos=true;", NULL);
3322 stop_action = gst_validate_action_new (scenario, stop_action_type,
3324 gst_structure_free (s);
3325 gst_validate_execute_action (stop_action_type, stop_action);
3326 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
3328 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3331 case GST_MESSAGE_BUFFERING:
3335 gst_message_parse_buffering (message, &percent);
3338 priv->buffering = FALSE;
3340 priv->buffering = TRUE;
3343 case GST_MESSAGE_STREAMS_SELECTED:
3346 GList *streams_selected = NULL;
3348 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
3350 gst_message_streams_selected_get_stream (message, i);
3352 streams_selected = g_list_append (streams_selected, stream);
3355 /* Is there a pending switch-track action waiting for the new streams to
3357 if (priv->pending_switch_track) {
3358 GList *expected, *l;
3359 GstValidateScenario *scenario =
3360 gst_validate_action_get_scenario (priv->pending_switch_track);
3363 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
3364 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
3366 if (g_list_length (expected) != g_list_length (streams_selected)) {
3367 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
3368 SCENARIO_ACTION_EXECUTION_ERROR,
3369 "Was expecting %d selected streams but got %d",
3370 g_list_length (expected), g_list_length (streams_selected));
3374 for (l = expected; l; l = g_list_next (l)) {
3375 const gchar *stream_id = l->data;
3377 if (!streams_list_contain (streams_selected, stream_id)) {
3378 GST_VALIDATE_REPORT_ACTION (scenario, priv->pending_switch_track,
3379 SCENARIO_ACTION_EXECUTION_ERROR,
3380 "Stream %s has not be activated", stream_id);
3386 gst_object_unref (scenario);
3387 gst_validate_action_set_done (priv->pending_switch_track);
3388 priv->pending_switch_track = NULL;
3391 g_list_free_full (streams_selected, gst_object_unref);
3394 case GST_MESSAGE_LATENCY:
3395 gst_validate_scenario_check_latency (scenario, pipeline);
3398 case GST_MESSAGE_QOS:
3402 /* Check the maximum allowed when scenario is terminating so the report
3403 * will include the actual number of dropped buffers. */
3404 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
3406 priv->dropped = dropped;
3415 gst_object_unref (pipeline);
3416 /* Check if we got the message expected by a wait action */
3417 if (priv->message_type)
3418 _check_waiting_for_message (scenario, message);
3420 execute_next_action_full (scenario, message);
3426 _action_type_has_parameter (GstValidateActionType * atype,
3427 const gchar * paramname)
3431 if (!atype->parameters)
3434 for (i = 0; atype->parameters[i].name; i++)
3435 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
3442 gst_validate_scenario_load_structures (GstValidateScenario * scenario,
3443 GList * structures, gboolean * is_config, gchar * origin_file)
3445 gboolean ret = TRUE;
3447 GstValidateScenarioPrivate *priv = scenario->priv;
3453 GST_INFO_OBJECT (scenario, "No structures provided");
3457 for (tmp = structures; tmp; tmp = tmp->next) {
3458 GstValidateAction *action;
3459 GstValidateActionType *action_type;
3461 GstStructure *structure = (GstStructure *) tmp->data;
3463 type = gst_structure_get_name (structure);
3464 if (!g_strcmp0 (type, "description") || !g_strcmp0 (type, "meta")) {
3465 const gchar *pipeline_name;
3467 gst_structure_get_boolean (structure, "is-config", is_config);
3468 gst_structure_get_boolean (structure, "handles-states",
3469 &priv->handles_state);
3470 gst_structure_get_boolean (structure, "ignore-eos", &priv->ignore_eos);
3472 if (!priv->handles_state)
3473 priv->target_state = GST_STATE_PLAYING;
3475 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
3476 if (pipeline_name) {
3477 g_free (priv->pipeline_name);
3478 priv->pipeline_name = g_strdup (pipeline_name);
3481 gst_validate_utils_get_clocktime (structure, "max-latency",
3482 &priv->max_latency);
3484 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
3485 scenario->description = gst_structure_copy (structure);
3488 } else if (!g_strcmp0 (type, "include")) {
3489 const gchar *location = gst_structure_get_string (structure, "location");
3492 GST_ERROR_OBJECT (scenario,
3493 "Mandatory field 'location' not present in structure: %"
3494 GST_PTR_FORMAT, structure);
3498 if (!gst_validate_scenario_load (scenario, location, origin_file)) {
3499 GST_ERROR ("Failed including scenario %s", location);
3504 } else if (!(action_type = _find_action_type (type))) {
3505 if (gst_structure_has_field (structure, "optional-action-type")) {
3506 GST_INFO_OBJECT (scenario,
3507 "Action type not found %s but marked as not mandatory", type);
3511 GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type);
3515 if (action_type->parameters) {
3518 for (i = 0; action_type->parameters[i].name; i++) {
3519 if (action_type->parameters[i].mandatory &&
3520 gst_structure_has_field (structure,
3521 action_type->parameters[i].name) == FALSE) {
3522 GST_ERROR_OBJECT (scenario,
3523 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
3524 action_type->parameters[i].name, structure);
3530 action = gst_validate_action_new (scenario, action_type, structure, TRUE);
3531 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
3532 GST_ERROR_OBJECT (scenario, "Newly created action: %" GST_PTR_FORMAT
3533 " was in error state", structure);
3538 action->action_number = priv->num_actions++;
3541 /* max-latency and max-dropped can be overridden using config */
3542 for (config = gst_validate_plugin_get_config (NULL); config;
3543 config = g_list_next (config)) {
3544 GstClockTime max_latency;
3546 gst_validate_utils_get_clocktime (config->data, "max-latency",
3548 if (GST_CLOCK_TIME_IS_VALID (max_latency))
3549 priv->max_latency = max_latency;
3551 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
3555 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3566 _load_scenario_file (GstValidateScenario * scenario,
3567 gchar * scenario_file, gboolean * is_config)
3569 return gst_validate_scenario_load_structures (scenario,
3570 gst_validate_utils_structs_parse_from_filename (scenario_file, NULL),
3571 is_config, scenario_file);
3576 gst_validate_scenario_load (GstValidateScenario * scenario,
3577 const gchar * scenario_name, const gchar * relative_scenario)
3579 gchar **scenarios = NULL;
3581 gboolean found_actions = FALSE, is_config, ret = TRUE;
3582 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
3584 gchar **env_scenariodir;
3586 if (relative_scenario) {
3587 gchar *relative_dir = g_path_get_dirname (relative_scenario);
3588 gchar *tmp_scenarios_path =
3589 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
3591 g_free (relative_dir);
3593 g_free (scenarios_path);
3594 scenarios_path = tmp_scenarios_path;
3598 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
3600 g_free (scenarios_path);
3605 scenarios = g_strsplit (scenario_name, ":", -1);
3607 for (i = 0; scenarios[i]; i++) {
3608 gchar *lfilename = NULL, *tldir = NULL, *scenario_file = NULL;
3610 /* First check if the scenario name is not a full path to the
3611 * actual scenario */
3612 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
3613 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
3614 "Trying to load it", scenarios[i]);
3615 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config))) {
3616 scenario_file = scenarios[i];
3617 goto check_scenario;
3621 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
3622 lfilename = g_strdup (scenarios[i]);
3625 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
3627 if (env_scenariodir) {
3630 for (i = 0; env_scenariodir[i]; i++) {
3631 tldir = g_build_filename (env_scenariodir[i], lfilename, NULL);
3632 if ((ret = _load_scenario_file (scenario, tldir, &is_config))) {
3633 scenario_file = tldir;
3634 goto check_scenario;
3640 tldir = g_build_filename ("data", "scenarios", lfilename, NULL);
3642 if ((ret = _load_scenario_file (scenario, tldir, &is_config))) {
3643 scenario_file = tldir;
3644 goto check_scenario;
3649 /* Try from local profiles */
3651 g_build_filename (g_get_user_data_dir (),
3652 "gstreamer-" GST_API_VERSION, "validate",
3653 GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3655 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3657 /* Try from system-wide profiles */
3658 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3659 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3661 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3666 scenario_file = tldir;
3668 /* else check scenario */
3671 gchar *scenario_dir = g_path_get_dirname (scenario_file);
3672 gchar *scenario_fname = g_path_get_basename (scenario_file);
3673 gchar **scenario_name =
3674 g_regex_split_simple ("\\.scenario", scenario_fname, 0, 0);
3676 gst_structure_set (scenario->priv->vars,
3677 "SCENARIO_DIR", G_TYPE_STRING, scenario_dir,
3678 "SCENARIO_NAME", G_TYPE_STRING, scenario_name[0],
3679 "SCENARIO_PATH", G_TYPE_STRING, scenario_file, NULL);
3681 g_free (scenario_dir);
3682 g_free (scenario_fname);
3683 g_strfreev (scenario_name);
3690 if (found_actions == TRUE)
3691 goto one_actions_scenario_max;
3693 found_actions = TRUE;
3699 if (env_scenariodir)
3700 g_strfreev (env_scenariodir);
3702 g_strfreev (scenarios);
3705 g_error ("Could not set scenario %s => EXIT\n", scenario_name);
3711 GST_ERROR ("Invalid name for scenario '%s'", GST_STR_NULL (scenario_name));
3716 one_actions_scenario_max:
3718 GST_ERROR ("You can set at most only one action scenario. "
3719 "You can have several config scenarios though (a config scenario's "
3720 "file must have is-config=true, and all its actions must be executable "
3721 "at parsing time).");
3730 gst_validate_scenario_set_property (GObject * object, guint prop_id,
3731 const GValue * value, GParamSpec * pspec)
3733 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3737 /* we assume the runner is valid as long as this scenario is,
3739 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
3740 g_value_get_object (value));
3742 case PROP_HANDLES_STATE:
3743 g_assert_not_reached ();
3745 case PROP_EXECUTE_ON_IDLE:
3746 self->priv->execute_on_idle = g_value_get_boolean (value);
3754 gst_validate_scenario_get_property (GObject * object, guint prop_id,
3755 GValue * value, GParamSpec * pspec)
3757 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3761 /* we assume the runner is valid as long as this scenario is,
3763 g_value_take_object (value,
3764 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
3766 case PROP_HANDLES_STATE:
3767 g_value_set_boolean (value, self->priv->handles_state);
3769 case PROP_EXECUTE_ON_IDLE:
3770 g_value_set_boolean (value, self->priv->execute_on_idle);
3778 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
3780 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3782 object_class->dispose = gst_validate_scenario_dispose;
3783 object_class->finalize = gst_validate_scenario_finalize;
3785 object_class->get_property = gst_validate_scenario_get_property;
3786 object_class->set_property = gst_validate_scenario_set_property;
3788 g_object_class_install_property (object_class, PROP_RUNNER,
3789 g_param_spec_object ("validate-runner", "VALIDATE Runner",
3790 "The Validate runner to report errors to",
3791 GST_TYPE_VALIDATE_RUNNER,
3792 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
3794 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
3795 g_param_spec_boolean ("handles-states", "Handles state",
3796 "True if the application should not handle the first state change. "
3797 "False if it is application responsibility",
3798 FALSE, G_PARAM_READABLE));
3800 g_object_class_install_property (object_class,
3801 PROP_EXECUTE_ON_IDLE,
3802 g_param_spec_boolean ("execute-on-idle",
3803 "Force waiting between actions",
3804 "Always execute actions on idle and do not chain them to execute as"
3805 " fast as possible. Setting this property is useful if action"
3806 " execution can lead to the addition of new sources on the same main"
3807 " loop as it provides these new GSource a chance to be dispatched"
3808 " between actions", FALSE, G_PARAM_READWRITE));
3811 * GstValidateScenario::done:
3812 * @scenario: The scenario running
3814 * Emitted once all actions have been executed
3816 scenario_signals[DONE] =
3817 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
3818 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
3822 gst_validate_scenario_init (GstValidateScenario * scenario)
3824 GstValidateScenarioPrivate *priv = scenario->priv =
3825 gst_validate_scenario_get_instance_private (scenario);
3827 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
3828 priv->segment_start = 0;
3829 priv->segment_stop = GST_CLOCK_TIME_NONE;
3830 priv->action_execution_interval = 10;
3831 priv->vars = gst_structure_new_empty ("vars");
3832 priv->needs_playback_parsing = TRUE;
3833 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
3834 priv->max_latency = GST_CLOCK_TIME_NONE;
3835 priv->max_dropped = -1;
3837 g_mutex_init (&priv->lock);
3841 gst_validate_scenario_dispose (GObject * object)
3843 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3845 if (priv->last_seek)
3846 gst_event_unref (priv->last_seek);
3847 g_weak_ref_clear (&priv->ref_pipeline);
3850 gst_bus_remove_signal_watch (priv->bus);
3851 gst_object_unref (priv->bus);
3855 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
3859 gst_validate_scenario_finalize (GObject * object)
3861 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3862 GstValidateScenarioPrivate *priv = self->priv;
3864 /* Because g_object_add_weak_pointer() is used, this MUST be on the
3866 g_assert (g_main_context_acquire (g_main_context_default ()));
3867 g_main_context_release (g_main_context_default ());
3869 g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref);
3870 g_list_free_full (priv->interlaced_actions,
3871 (GDestroyNotify) gst_mini_object_unref);
3872 g_list_free_full (priv->on_addition_actions,
3873 (GDestroyNotify) gst_mini_object_unref);
3874 g_free (priv->pipeline_name);
3875 gst_structure_free (priv->vars);
3876 if (self->description)
3877 gst_structure_free (self->description);
3878 g_mutex_clear (&priv->lock);
3880 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
3883 static void _element_added_cb (GstBin * bin, GstElement * element,
3884 GstValidateScenario * scenario);
3887 iterate_children (GstValidateScenario * scenario, GstBin * bin)
3890 GValue v = G_VALUE_INIT;
3891 gboolean done = FALSE;
3892 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
3894 called = g_hash_table_new (NULL, NULL);
3895 it = gst_bin_iterate_elements (bin);
3898 switch (gst_iterator_next (it, &v)) {
3899 case GST_ITERATOR_OK:{
3900 GstElement *child = g_value_get_object (&v);
3902 if (g_hash_table_lookup (called, child) == NULL) {
3903 _element_added_cb (bin, child, scenario);
3904 g_hash_table_add (called, child);
3909 case GST_ITERATOR_RESYNC:
3910 gst_iterator_resync (it);
3912 case GST_ITERATOR_ERROR:
3913 case GST_ITERATOR_DONE:
3918 gst_iterator_free (it);
3919 g_hash_table_unref (called);
3923 should_execute_action (GstElement * element, GstValidateAction * action)
3925 return gst_validate_element_matches_target (element, action->structure);
3929 _element_added_cb (GstBin * bin, GstElement * element,
3930 GstValidateScenario * scenario)
3934 GstValidateScenarioPrivate *priv = scenario->priv;
3936 /* Check if it's an element we track for a set-property action */
3937 SCENARIO_LOCK (scenario);
3938 tmp = priv->on_addition_actions;
3940 GstValidateAction *action = (GstValidateAction *) tmp->data;
3942 if (action->playback_time != GST_CLOCK_TIME_NONE)
3944 if (g_strcmp0 (action->type, "set-property"))
3947 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
3948 action, action->type);
3949 if (should_execute_action (element, action)) {
3950 GstValidateActionType *action_type;
3951 action_type = _find_action_type (action->type);
3952 GST_DEBUG_OBJECT (element, "Executing set-property action");
3953 if (gst_validate_execute_action (action_type, action)) {
3954 priv->on_addition_actions =
3955 g_list_remove_link (priv->on_addition_actions, tmp);
3956 gst_mini_object_unref (GST_MINI_OBJECT (action));
3958 tmp = priv->on_addition_actions;
3964 SCENARIO_UNLOCK (scenario);
3966 _check_scenario_is_done (scenario);
3968 /* If it's a bin, listen to the child */
3969 if (GST_IS_BIN (element)) {
3970 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
3972 iterate_children (scenario, GST_BIN (element));
3976 static GstValidateScenario *
3977 gst_validate_scenario_new (GstValidateRunner *
3978 runner, GstElement * pipeline, gchar * scenario_name, GList * structures)
3981 GstValidateScenario *scenario =
3982 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
3987 gst_validate_scenario_load_structures (scenario, structures, &is_config,
3991 GST_LOG ("Creating scenario %s", scenario_name);
3992 if (!gst_validate_scenario_load (scenario, scenario_name, NULL)) {
3993 g_object_unref (scenario);
3999 if (scenario->priv->pipeline_name &&
4000 !g_pattern_match_simple (scenario->priv->pipeline_name,
4001 GST_OBJECT_NAME (pipeline))) {
4002 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
4003 scenario_name, scenario->priv->pipeline_name,
4004 GST_OBJECT_NAME (pipeline));
4006 gst_object_unref (scenario);
4011 gst_validate_printf (NULL,
4012 "\n**-> Running scenario %s on pipeline %s**\n\n", scenario_name,
4013 GST_OBJECT_NAME (pipeline));
4015 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
4016 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
4017 g_strdup (scenario_name));
4019 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
4022 iterate_children (scenario, GST_BIN (pipeline));
4024 scenario->priv->bus = gst_element_get_bus (pipeline);
4025 gst_bus_add_signal_watch (scenario->priv->bus);
4026 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
4029 for (config = gst_validate_plugin_get_config (NULL); config;
4030 config = config->next) {
4033 if (gst_structure_get_uint (config->data,
4034 "scenario-action-execution-interval",
4035 &scenario->priv->action_execution_interval)) {
4036 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
4037 scenario->priv->action_execution_interval);
4039 } else if (gst_structure_get_int (config->data,
4040 "scenario-action-execution-interval", &interval)) {
4042 scenario->priv->action_execution_interval = (guint) interval;
4043 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
4044 scenario->priv->action_execution_interval);
4048 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
4053 if (scenario->priv->handles_state) {
4054 GST_INFO_OBJECT (scenario, "Scenario handles state."
4055 " Starting the get position source");
4056 _add_execute_actions_gsource (scenario);
4059 scenario->priv->overrides =
4060 gst_validate_override_registry_get_override_for_names
4061 (gst_validate_override_registry_get (), "scenarios", NULL);
4066 GstValidateScenario *
4067 gst_validate_scenario_from_structs (GstValidateRunner * runner,
4068 GstElement * pipeline, GList * structures, gchar * origin_file)
4070 g_return_val_if_fail (structures, NULL);
4072 return gst_validate_scenario_new (runner, pipeline, origin_file, structures);
4076 * gst_validate_scenario_factory_create:
4077 * @runner: The #GstValidateRunner to use to report issues
4078 * @pipeline: The pipeline to run the scenario on
4079 * @scenario_name: The name (or path) of the scenario to run
4081 * Returns: (transfer full): A #GstValidateScenario or NULL
4083 GstValidateScenario *
4084 gst_validate_scenario_factory_create (GstValidateRunner *
4085 runner, GstElement * pipeline, const gchar * scenario_name)
4087 return gst_validate_scenario_new (runner, pipeline, (gchar *) scenario_name,
4092 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
4094 gchar *tmp = gst_value_serialize (value);
4095 gchar *tmpcompress = g_strcompress (tmp);
4097 g_key_file_set_string (kfg->kf, kfg->group_name,
4098 g_quark_to_string (field_id), tmpcompress);
4100 g_free (tmpcompress);
4107 gst_validate_scenario_check_and_set_needs_clock_sync (GList * structures,
4108 GstStructure ** meta)
4110 gboolean needs_clock_sync = FALSE;
4113 for (tmp = structures; tmp; tmp = tmp->next) {
4114 GstStructure *_struct = (GstStructure *) tmp->data;
4115 gboolean is_meta = gst_structure_has_name (_struct, "description")
4116 || gst_structure_has_name (_struct, "meta");
4119 GstValidateActionType *type =
4120 _find_action_type (gst_structure_get_name (_struct));
4122 if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
4123 needs_clock_sync = TRUE;
4128 *meta = gst_structure_copy (_struct);
4131 if (needs_clock_sync) {
4133 gst_structure_set (*meta, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
4135 *meta = gst_structure_from_string ("description, need-clock-sync=true;",
4139 return needs_clock_sync;
4143 _parse_scenario (GFile * f, GKeyFile * kf)
4145 gboolean ret = FALSE;
4146 gchar *path = g_file_get_path (f);
4148 if (g_str_has_suffix (path, GST_VALIDATE_SCENARIO_SUFFIX)) {
4149 GstStructure *meta = NULL;
4150 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f);
4152 gst_validate_scenario_check_and_set_needs_clock_sync (structures, &meta);
4153 for (tmp = structures; tmp; tmp = tmp->next)
4154 gst_structure_remove_fields (tmp->data, "__lineno__", "__filename__",
4158 KeyFileGroupName kfg;
4160 kfg.group_name = g_file_get_path (f);
4163 gst_structure_foreach (meta,
4164 (GstStructureForeachFunc) _add_description, &kfg);
4165 gst_structure_free (meta);
4167 g_key_file_set_string (kf, path, "noinfo", "nothing");
4169 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
4179 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
4181 GFileEnumerator *fenum;
4184 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
4185 G_FILE_QUERY_INFO_NONE, NULL, NULL);
4190 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
4191 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
4192 GFile *f = g_file_enumerator_get_child (fenum, info);
4194 _parse_scenario (f, kf);
4195 gst_object_unref (f);
4198 gst_object_unref (fenum);
4202 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
4203 gchar * output_file)
4209 GKeyFile *kf = NULL;
4211 const gchar *envvar;
4212 gchar **env_scenariodir = NULL;
4213 gchar *tldir = g_build_filename (g_get_user_data_dir (),
4214 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
4216 GFile *dir = g_file_new_for_path (tldir);
4219 kf = g_key_file_new ();
4220 if (num_scenarios > 0) {
4224 for (i = 0; i < num_scenarios; i++) {
4225 file = g_file_new_for_path (scenarios[i]);
4226 if (!_parse_scenario (file, kf)) {
4227 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
4229 gst_object_unref (file);
4237 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
4239 env_scenariodir = g_strsplit (envvar, ":", 0);
4241 _list_scenarios_in_dir (dir, kf);
4242 g_object_unref (dir);
4244 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
4245 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4246 dir = g_file_new_for_path (tldir);
4247 _list_scenarios_in_dir (dir, kf);
4248 g_object_unref (dir);
4251 if (env_scenariodir) {
4254 for (i = 0; env_scenariodir[i]; i++) {
4255 dir = g_file_new_for_path (env_scenariodir[i]);
4256 _list_scenarios_in_dir (dir, kf);
4257 g_object_unref (dir);
4261 /* Hack to make it work uninstalled */
4262 dir = g_file_new_for_path ("data/scenarios");
4263 _list_scenarios_in_dir (dir, kf);
4264 g_object_unref (dir);
4267 result = g_key_file_to_data (kf, &datalength, &err);
4268 g_print ("All scenarios available:\n%s", result);
4270 if (output_file && !err) {
4271 if (!g_file_set_contents (output_file, result, datalength, &err)) {
4272 GST_WARNING ("Error writing to file '%s'", output_file);
4278 if (env_scenariodir)
4279 g_strfreev (env_scenariodir);
4282 GST_WARNING ("Got error '%s' listing scenarios", err->message);
4283 g_clear_error (&err);
4288 g_key_file_free (kf);
4293 static GstValidateActionReturn
4294 check_last_sample_internal (GstValidateScenario * scenario,
4295 GstValidateAction * action, GstElement * sink)
4300 const gchar *target_sum;
4301 guint64 frame_number;
4302 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
4303 GstVideoTimeCodeMeta *tc_meta;
4305 g_object_get (sink, "last-sample", &sample, NULL);
4306 if (sample == NULL) {
4307 GST_VALIDATE_REPORT_ACTION (scenario, action,
4308 SCENARIO_ACTION_EXECUTION_ERROR,
4309 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
4310 " 'last-sample' property is NULL"
4311 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
4314 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4317 buffer = gst_sample_get_buffer (sample);
4318 target_sum = gst_structure_get_string (action->structure, "checksum");
4322 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
4323 GST_VALIDATE_REPORT_ACTION (scenario, action,
4324 SCENARIO_ACTION_EXECUTION_ERROR,
4325 "Last sample buffer could not be mapped, action can't run.");
4326 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4329 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
4330 gst_buffer_unmap (buffer, &map);
4332 if (g_strcmp0 (sum, target_sum)) {
4333 GST_VALIDATE_REPORT_ACTION (scenario, action,
4334 SCENARIO_ACTION_EXECUTION_ERROR,
4335 "Last buffer checksum '%s' is different than the expected one: '%s'",
4338 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4345 if (!gst_structure_get_uint64 (action->structure, "timecode-frame-number",
4349 if (!gst_structure_get_int (action->structure, "timecode-frame-number",
4351 GST_VALIDATE_REPORT_ACTION (scenario, action,
4352 SCENARIO_ACTION_EXECUTION_ERROR,
4353 "The 'checksum' or 'time-code-frame-number' parameters of the "
4354 "`check-last-sample` action type needs to be specified, none found");
4356 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4360 frame_number = (guint64) iframe_number;
4363 tc_meta = gst_buffer_get_video_time_code_meta (buffer);
4365 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4366 "Could not \"check-last-sample\" as the buffer doesn't contain a TimeCode"
4368 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4372 if (gst_video_time_code_frames_since_daily_jam (&tc_meta->tc) != frame_number) {
4373 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4374 "Last buffer frame number '%" G_GINT64_FORMAT
4375 "' is different than the expected one: '%" G_GINT64_FORMAT "'",
4376 gst_video_time_code_frames_since_daily_jam (&tc_meta->tc),
4378 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4382 gst_sample_unref (sample);
4387 sink_last_sample_notify_cb (GstElement * sink, GParamSpec * arg G_GNUC_UNUSED,
4388 GstValidateAction * action)
4390 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4393 GST_VALIDATE_REPORT_ACTION (scenario, action,
4394 SCENARIO_ACTION_EXECUTION_ERROR,
4395 "No pipeline anymore, can't check last sample");
4399 check_last_sample_internal (scenario, action, sink);
4400 gst_object_unref (scenario);
4403 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
4405 gst_validate_action_set_done (action);
4406 gst_validate_action_unref (action);
4409 static GstValidateExecuteActionReturn
4410 _check_last_sample_value (GstValidateScenario * scenario,
4411 GstValidateAction * action, GstElement * sink)
4415 /* Connect before checking last sample to avoid a race where
4416 * the sample is set between the time we connect and the time
4417 * the time we get it */
4418 g_signal_connect (sink, "notify::last-sample",
4419 G_CALLBACK (sink_last_sample_notify_cb),
4420 gst_validate_action_ref (action));
4422 g_object_get (sink, "last-sample", &sample, NULL);
4424 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4425 gst_sample_unref (sample);
4426 gst_validate_action_unref (action);
4428 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
4431 return check_last_sample_internal (scenario, action, sink);
4435 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
4436 const gchar * fname, GstCaps * sinkpad_caps)
4440 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
4441 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
4446 if (paramspec->value_type != GST_TYPE_SAMPLE)
4449 if (!name && !fname && !sinkpad_caps)
4452 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
4456 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
4462 sinkpad = gst_element_get_static_pad (sink, "sink");
4466 tmpcaps = gst_pad_get_current_caps (sinkpad);
4468 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
4470 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
4471 gst_caps_unref (tmpcaps);
4475 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
4481 static GstValidateExecuteActionReturn
4482 _execute_check_last_sample (GstValidateScenario * scenario,
4483 GstValidateAction * action)
4486 GValue data = { 0, };
4487 gboolean done = FALSE;
4488 GstCaps *caps = NULL;
4489 GstElement *sink = NULL, *tmpelement;
4490 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
4492 gst_structure_get_string (action->structure, "sink-factory-name"),
4493 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
4494 DECLARE_AND_GET_PIPELINE (scenario, action);
4497 caps = gst_caps_from_string (caps_str);
4502 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
4504 switch (gst_iterator_next (it, &data)) {
4505 case GST_ITERATOR_OK:
4506 tmpelement = g_value_get_object (&data);
4507 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
4510 if (!gst_object_has_as_ancestor (GST_OBJECT (tmpelement),
4511 GST_OBJECT (sink))) {
4512 gchar *tmp = gst_structure_to_string (action->structure);
4514 GST_VALIDATE_REPORT_ACTION (scenario, action,
4515 SCENARIO_ACTION_EXECUTION_ERROR,
4516 "Could not \"check-last-sample\" as several elements were found "
4517 "from describing string: '%s' (%s and %s match)", tmp,
4518 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
4523 gst_object_unref (sink);
4526 sink = gst_object_ref (tmpelement);
4528 g_value_reset (&data);
4530 case GST_ITERATOR_RESYNC:
4531 gst_iterator_resync (it);
4532 g_clear_object (&sink);
4534 case GST_ITERATOR_ERROR:
4536 case GST_ITERATOR_DONE:
4541 gst_iterator_free (it);
4543 gst_caps_unref (caps);
4546 GST_VALIDATE_REPORT_ACTION (scenario, action,
4547 SCENARIO_ACTION_EXECUTION_ERROR,
4548 "Could not \"check-last-sample\" as no sink was found from description: '%"
4549 GST_PTR_FORMAT "'", action->structure);
4554 g_clear_object (&pipeline);
4555 return _check_last_sample_value (scenario, action, sink);
4558 g_clear_object (&sink);
4559 g_clear_object (&pipeline);
4560 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4563 static GstPadProbeReturn
4564 _check_is_key_unit_cb (GstPad * pad, GstPadProbeInfo * info,
4565 GstValidateAction * action)
4567 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4568 GstClockTime target_running_time = GST_CLOCK_TIME_NONE;
4569 gint count_bufs = 0;
4571 gst_validate_action_get_clocktime (scenario, action,
4572 "running-time", &target_running_time);
4573 if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) {
4574 if (gst_video_event_is_force_key_unit (GST_PAD_PROBE_INFO_DATA (info)))
4575 gst_structure_set (action->structure, "__priv_seen_event", G_TYPE_BOOLEAN,
4577 else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT
4578 && GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
4579 const GstSegment *segment = NULL;
4581 gst_event_parse_segment (info->data, &segment);
4582 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
4585 } else if (GST_IS_BUFFER (GST_PAD_PROBE_INFO_DATA (info))
4586 && gst_structure_has_field_typed (action->structure, "__priv_seen_event",
4588 GstSegment *segment = NULL;
4590 if (GST_CLOCK_TIME_IS_VALID (target_running_time)) {
4591 GstClockTime running_time;
4593 gst_structure_get (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
4596 gst_segment_to_running_time (segment, GST_FORMAT_TIME,
4597 GST_BUFFER_TIMESTAMP (info->data));
4599 if (running_time < target_running_time)
4603 gst_structure_get_int (action->structure, "__priv_count_bufs", &count_bufs);
4604 if (GST_BUFFER_FLAG_IS_SET (GST_PAD_PROBE_INFO_BUFFER (info),
4605 GST_BUFFER_FLAG_DELTA_UNIT)) {
4606 if (count_bufs >= NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE) {
4607 GST_VALIDATE_REPORT_ACTION (scenario, action,
4608 SCENARIO_ACTION_EXECUTION_ERROR,
4609 "Did not receive a key frame after requested one, "
4610 "at running_time %" GST_TIME_FORMAT " (with a %i "
4611 "frame tolerance)", GST_TIME_ARGS (target_running_time),
4612 NOT_KF_AFTER_FORCE_KF_EVT_TOLERANCE);
4614 gst_validate_action_set_done (action);
4615 gst_object_unref (scenario);
4616 return GST_PAD_PROBE_REMOVE;
4619 gst_structure_set (action->structure, "__priv_count_bufs", G_TYPE_INT,
4620 count_bufs++, NULL);
4622 GST_INFO_OBJECT (pad,
4623 "Properly got keyframe after \"force-keyframe\" event "
4624 "with running_time %" GST_TIME_FORMAT " (latency %d frame(s))",
4625 GST_TIME_ARGS (target_running_time), count_bufs);
4627 gst_structure_remove_fields (action->structure, "__priv_count_bufs",
4628 "__priv_segment", "__priv_seen_event", NULL);
4629 gst_validate_action_set_done (action);
4630 gst_object_unref (scenario);
4631 return GST_PAD_PROBE_REMOVE;
4635 gst_object_unref (scenario);
4637 return GST_PAD_PROBE_OK;
4641 _execute_request_key_unit (GstValidateScenario * scenario,
4642 GstValidateAction * action)
4645 gboolean all_headers = FALSE;
4646 gboolean ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4647 GstEvent *event = NULL;
4648 GstQuery *segment_query;
4649 GList *targets = NULL, *tmp;
4650 GstElement *video_encoder = NULL;
4651 GstPad *pad = NULL, *encoder_srcpad = NULL;
4652 GstClockTime running_time = GST_CLOCK_TIME_NONE;
4653 GstSegment segment = { 0, };
4654 const gchar *direction = gst_structure_get_string (action->structure,
4655 "direction"), *pad_name, *srcpad_name;
4657 DECLARE_AND_GET_PIPELINE (scenario, action);
4659 if (gst_structure_get_string (action->structure, "target-element-name")) {
4660 GstElement *target = _get_target_element (scenario, action);
4664 targets = g_list_append (targets, target);
4666 if (!gst_structure_get_string (action->structure,
4667 "target-element-klass") &&
4668 !gst_structure_get_string (action->structure,
4669 "target-element-factory-name")) {
4670 gst_structure_set (action->structure, "target-element-klass",
4671 G_TYPE_STRING, "Video/Encoder", NULL);
4674 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
4678 GST_VALIDATE_REPORT_ACTION (scenario, action,
4679 SCENARIO_ACTION_EXECUTION_ERROR,
4680 "Could not find any element from action: %" GST_PTR_FORMAT,
4685 gst_validate_action_get_clocktime (scenario, action,
4686 "running-time", &running_time);
4687 gst_structure_get_boolean (action->structure, "all-headers", &all_headers);
4688 if (!gst_structure_get_uint (action->structure, "count", &count)) {
4689 gst_structure_get_int (action->structure, "count", (gint *) & count);
4691 pad_name = gst_structure_get_string (action->structure, "pad");
4692 srcpad_name = gst_structure_get_string (action->structure, "srcpad");
4694 srcpad_name = "src";
4696 for (tmp = targets; tmp; tmp = tmp->next) {
4697 video_encoder = tmp->data;
4698 encoder_srcpad = gst_element_get_static_pad (video_encoder, srcpad_name);
4699 if (!encoder_srcpad) {
4700 GST_VALIDATE_REPORT_ACTION (scenario, action,
4701 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
4706 if (g_strcmp0 (direction, "upstream") == 0) {
4707 event = gst_video_event_new_upstream_force_key_unit (running_time,
4708 all_headers, count);
4710 pad = gst_element_get_static_pad (video_encoder, srcpad_name);
4712 GST_VALIDATE_REPORT_ACTION (scenario, action,
4713 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s",
4718 GST_ERROR_OBJECT (encoder_srcpad, "Sending RequestKeyUnit event");
4719 gst_pad_add_probe (encoder_srcpad,
4720 GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
4721 (GstPadProbeCallback) _check_is_key_unit_cb,
4722 gst_validate_action_ref (action),
4723 (GDestroyNotify) gst_validate_action_unref);
4724 } else if (g_strcmp0 (direction, "downstream") == 0) {
4725 GstClockTime timestamp = GST_CLOCK_TIME_NONE,
4726 stream_time = GST_CLOCK_TIME_NONE;
4731 pad = gst_element_get_static_pad (video_encoder, pad_name);
4733 GST_VALIDATE_REPORT_ACTION (scenario, action,
4734 SCENARIO_ACTION_EXECUTION_ERROR, "Could not find pad %s", pad_name);
4739 gst_validate_action_get_clocktime (scenario, action,
4740 "timestamp", ×tamp);
4742 gst_validate_action_get_clocktime (scenario, action,
4743 "stream-time", &stream_time);
4746 gst_video_event_new_downstream_force_key_unit (timestamp, stream_time,
4747 running_time, all_headers, count);
4749 gst_pad_add_probe (pad,
4750 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
4751 (GstPadProbeCallback) _check_is_key_unit_cb,
4752 gst_validate_action_ref (action),
4753 (GDestroyNotify) gst_validate_action_unref);
4755 GST_VALIDATE_REPORT_ACTION (scenario, action,
4756 SCENARIO_ACTION_EXECUTION_ERROR,
4757 "request keyunit direction %s invalid (should be in"
4758 " [downstrean, upstream]", direction);
4763 gst_validate_printf (action, "Sending a \"force key unit\" event %s\n",
4766 segment_query = gst_query_new_segment (GST_FORMAT_TIME);
4767 gst_pad_query (encoder_srcpad, segment_query);
4769 gst_query_parse_segment (segment_query, &(segment.rate),
4770 &(segment.format), (gint64 *) & (segment.start),
4771 (gint64 *) & (segment.stop));
4772 gst_structure_set (action->structure, "__priv_segment", GST_TYPE_SEGMENT,
4775 gst_pad_add_probe (encoder_srcpad,
4776 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
4777 (GstPadProbeCallback) _check_is_key_unit_cb,
4778 gst_validate_action_ref (action),
4779 (GDestroyNotify) gst_validate_action_unref);
4782 if (!gst_pad_send_event (pad, event)) {
4783 GST_VALIDATE_REPORT_ACTION (scenario, action,
4784 SCENARIO_ACTION_EXECUTION_ERROR,
4785 "Could not send \"force key unit\" event %s", direction);
4789 gst_clear_object (&pad);
4790 gst_clear_object (&encoder_srcpad);
4794 g_list_free_full (targets, gst_object_unref);
4795 gst_clear_object (&pad);
4796 gst_clear_object (&encoder_srcpad);
4801 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4806 _action_set_done (GstValidateAction * action)
4808 JsonBuilder *jbuild;
4809 GstClockTime execution_duration;
4810 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4812 if (scenario == NULL || !action->priv->pending_set_done)
4813 return G_SOURCE_REMOVE;
4815 execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
4817 jbuild = json_builder_new ();
4818 json_builder_begin_object (jbuild);
4819 json_builder_set_member_name (jbuild, "type");
4820 json_builder_add_string_value (jbuild, "action-done");
4821 json_builder_set_member_name (jbuild, "action-type");
4822 json_builder_add_string_value (jbuild, action->type);
4823 json_builder_set_member_name (jbuild, "execution-duration");
4824 json_builder_add_double_value (jbuild,
4825 ((gdouble) execution_duration / GST_SECOND));
4826 json_builder_end_object (jbuild);
4828 gst_validate_send (json_builder_get_root (jbuild));
4829 g_object_unref (jbuild);
4831 gst_validate_printf (NULL, " -> Action %s done (duration: %" GST_TIME_FORMAT
4832 ")\n", action->type, GST_TIME_ARGS (execution_duration));
4833 action->priv->execution_time = GST_CLOCK_TIME_NONE;
4834 action->priv->state = _execute_sub_action_action (action);
4836 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
4838 GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
4839 execute_next_action (scenario);
4841 gst_object_unref (scenario);
4843 action->priv->pending_set_done = FALSE;
4844 return G_SOURCE_REMOVE;
4847 /* gst_validate_action_set_done:
4848 * @action: The action that is done executing
4850 * Sets @action as "done", meaning that the next action can
4854 gst_validate_action_set_done (GstValidateAction * action)
4857 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) {
4858 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4862 SCENARIO_LOCK (scenario);
4863 item = g_list_find (scenario->priv->interlaced_actions, action);
4864 scenario->priv->interlaced_actions =
4865 g_list_delete_link (scenario->priv->interlaced_actions, item);
4866 SCENARIO_UNLOCK (scenario);
4867 g_object_unref (scenario);
4871 gst_validate_action_unref (action);
4874 g_assert (!action->priv->pending_set_done);
4875 action->priv->pending_set_done = TRUE;
4877 g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
4878 (GSourceFunc) _action_set_done,
4879 gst_mini_object_ref (GST_MINI_OBJECT (action)),
4880 (GDestroyNotify) gst_validate_action_unref);
4884 * gst_validate_action_get_scenario:
4885 * @action: The action for which to retrieve the scenario
4887 * Retrieve the scenario from which @action is executed.
4889 * Returns: (transfer full): The scenario from which the action is being executed.
4891 GstValidateScenario *
4892 gst_validate_action_get_scenario (GstValidateAction * action)
4894 return g_weak_ref_get (&action->priv->scenario);
4898 * gst_validate_register_action_type:
4899 * @type_name: The name of the new action type to add
4900 * @implementer_namespace: The namespace of the implementer of the action type.
4901 * That should always be the name of the GstPlugin as
4902 * retrieved with #gst_plugin_get_name when the action type
4903 * is registered inside a plugin.
4904 * @function: (scope notified): The function to be called to execute the action
4905 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4906 * @description: A description of the new type
4907 * @flags: The #GstValidateActionTypeFlags to set on the new action type
4909 * Register a new action type to the action type system. If the action type already
4910 * exists, it will be overridden by the new definition
4912 * Returns: (transfer none): The newly created action type or the already registered action type
4913 * if it had a higher rank
4915 GstValidateActionType *
4916 gst_validate_register_action_type (const gchar * type_name,
4917 const gchar * implementer_namespace,
4918 GstValidateExecuteAction function,
4919 GstValidateActionParameter * parameters,
4920 const gchar * description, GstValidateActionTypeFlags flags)
4922 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
4923 type_name, GST_RANK_NONE, function, parameters, description,
4926 g_free (type->implementer_namespace);
4927 type->implementer_namespace = g_strdup (implementer_namespace);
4933 _free_action_types (GList * _action_types)
4935 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
4939 * gst_validate_register_action_type_dynamic:
4940 * @plugin: (allow-none): The #GstPlugin that register the action type,
4941 * or NULL for a static element.
4942 * @rank: The ranking of that implementation of the action type called
4943 * @type_name. If an action type has been registered with the same
4944 * name with a higher rank, the new implementation will not be used,
4945 * and the already registered action type is returned.
4946 * If the already registered implementation has a lower rank, the
4947 * new implementation will be used and returned.
4948 * @type_name: The name of the new action type to add
4949 * @function: (scope notified): The function to be called to execute the action
4950 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4951 * @description: A description of the new type
4952 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
4954 * Returns: (transfer none): The newly created action type or the already registered action type
4955 * if it had a higher rank
4957 GstValidateActionType *
4958 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
4959 const gchar * type_name, GstRank rank,
4960 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
4961 const gchar * description, GstValidateActionTypeFlags flags)
4963 GstValidateActionType *tmptype;
4964 GstValidateActionType *type = gst_validate_action_type_new ();
4965 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
4966 gint n_params = is_config ? 0 : 2;
4969 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
4974 type->parameters = g_new0 (GstValidateActionParameter, n_params);
4978 memcpy (type->parameters, parameters,
4979 sizeof (GstValidateActionParameter) * (n_params));
4982 type->prepare = gst_validate_action_default_prepare_func;
4983 type->execute = function;
4984 type->name = g_strdup (type_name);
4986 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
4988 type->implementer_namespace = g_strdup ("none");
4990 type->description = g_strdup (description);
4991 type->flags = flags;
4994 if ((tmptype = _find_action_type (type_name))) {
4995 if (tmptype->rank <= rank) {
4996 action_types = g_list_remove (action_types, tmptype);
4997 type->overriden_type = tmptype;
4999 gst_mini_object_unref (GST_MINI_OBJECT (type));
5005 if (type != tmptype)
5006 action_types = g_list_append (action_types, type);
5009 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
5010 "GstValidatePluginActionTypes");
5012 plugin_action_types = g_list_prepend (plugin_action_types,
5013 gst_mini_object_ref (GST_MINI_OBJECT (type)));
5015 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
5016 plugin_action_types, (GDestroyNotify) _free_action_types);
5022 GstValidateActionType *
5023 gst_validate_get_action_type (const gchar * type_name)
5025 GstValidateActionType *type = _find_action_type (type_name);
5029 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
5035 gst_validate_list_action_types (void)
5037 return action_types;
5041 * gst_validate_print_action_types:
5042 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
5043 * @num_wanted_types: Length of @wanted_types
5045 * Prints the action types details wanted in @wanted_types
5047 * Returns: True if all types could be printed
5050 gst_validate_print_action_types (const gchar ** wanted_types,
5051 gint num_wanted_types)
5055 gboolean print_all = (num_wanted_types == 1
5056 && !g_strcmp0 (wanted_types[0], "all"));
5059 gst_validate_printf (NULL, "# GstValidate action types");
5061 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
5062 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
5063 gboolean print = print_all;
5065 if (num_wanted_types) {
5068 for (n = 0; n < num_wanted_types; n++) {
5069 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
5070 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
5081 if (print && num_wanted_types) {
5082 gst_validate_printf (atype, "\n");
5085 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
5089 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
5090 atype->implementer_namespace, atype->name, desc);
5095 if (!print_all && num_wanted_types && num_wanted_types > nfound) {
5103 * gst_validate_scenario_get_actions:
5104 * @scenario: The scenario to retrieve remaining actions for
5106 * Get remaining actions from @scenario.
5108 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
5111 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
5114 gboolean main_context_acquired;
5116 main_context_acquired = g_main_context_acquire (g_main_context_default ());
5117 g_return_val_if_fail (main_context_acquired, NULL);
5119 ret = g_list_copy_deep (scenario->priv->actions,
5120 (GCopyFunc) gst_mini_object_ref, NULL);
5122 g_main_context_release (g_main_context_default ());
5128 * gst_validate_scenario_get_target_state:
5129 * @scenario: The scenario to retrieve the current target state for
5131 * Get current target state from @scenario.
5133 * Returns: Current target state.
5136 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
5138 return scenario->priv->target_state;
5142 init_scenarios (void)
5146 register_action_types ();
5148 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
5149 const gchar *action_typename;
5150 GstStructure *plug_conf = (GstStructure *) tmp->data;
5152 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
5153 GstValidateAction *action;
5154 GstValidateActionType *atype = _find_action_type (action_typename);
5157 g_error ("[CONFIG ERROR] Action type %s not found", action_typename);
5163 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
5164 GST_INFO ("Action type %s from configuration files"
5165 " is handled.", action_typename);
5169 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
5170 !(_action_type_has_parameter (atype, "as-config"))) {
5171 g_error ("[CONFIG ERROR] Action '%s' is not a config action",
5177 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
5178 gst_structure_set_name (plug_conf, action_typename);
5180 action = gst_validate_action_new (NULL, atype, plug_conf, FALSE);
5181 gst_validate_action_unref (action);
5187 register_action_types (void)
5189 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
5190 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
5192 _gst_validate_action_type = gst_validate_action_get_type ();
5193 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
5196 REGISTER_ACTION_TYPE ("meta", NULL,
5197 ((GstValidateActionParameter []) {
5200 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
5203 .possible_variables = NULL,
5204 .def = "'Nothing'"},
5206 .name = "is-config",
5207 .description = "Whether the scenario is a config only scenario",
5210 .possible_variables = NULL,
5214 .name = "handles-states",
5215 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
5216 "in that case the application should not set the state of the pipeline to anything\n"
5217 "and the scenario action will be executed from the beginning",
5220 .possible_variables = NULL,
5224 .description = "Whether the scenario executes seek actions or not",
5227 .possible_variables = NULL,
5231 .name = "reverse-playback",
5232 .description = "Whether the scenario plays the stream backward",
5235 .possible_variables = NULL,
5239 .name = "need-clock-sync",
5240 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
5241 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
5244 .possible_variables = NULL,
5248 .name = "min-media-duration",
5249 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
5253 .possible_variables = NULL,
5257 .name = "min-audio-track",
5258 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
5259 "for the scenario to be usable",
5262 .possible_variables = NULL,
5266 .name = "min-video-track",
5267 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
5268 "for the scenario to be usable",
5271 .possible_variables = NULL,
5276 .description = "Lets the user know the time the scenario needs to be fully executed",
5278 .types = "double, int",
5279 .possible_variables = NULL,
5280 .def = "infinite (GST_CLOCK_TIME_NONE)"
5283 .name = "pipeline-name",
5284 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
5285 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
5288 .possible_variables = NULL,
5292 .name = "max-latency",
5293 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
5294 "It can be overridden using core configuration, like for example by defining the "
5295 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
5297 .types = "double, int",
5298 .possible_variables = NULL,
5299 .def = "infinite (GST_CLOCK_TIME_NONE)"
5302 .name = "max-dropped",
5303 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
5304 "It can be overridden using core configuration, like for example by defining the "
5305 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
5308 .possible_variables = NULL,
5309 .def = "infinite (-1)"
5312 .name = "ignore-eos",
5313 .description = "Ignore EOS and keep executing the scenario when it happens.\n By default "
5314 "a 'stop' action is generated one EOS",
5317 .possible_variables = NULL,
5322 "Scenario metadata.\nNOTE: it used to be called \"description\"",
5323 GST_VALIDATE_ACTION_TYPE_CONFIG);
5325 REGISTER_ACTION_TYPE ("seek", _execute_seek,
5326 ((GstValidateActionParameter []) {
5329 .description = "The starting value of the seek",
5331 .types = "double or string (GstClockTime)",
5332 .possible_variables =
5333 "`position`: The current position in the stream\n"
5334 "`duration`: The duration of the stream",
5339 .description = "The GstSeekFlags to use",
5341 .types = "string describing the GstSeekFlags to set",
5346 .description = "The rate value of the seek",
5349 .possible_variables = NULL,
5353 .name = "start_type",
5354 .description = "The GstSeekType to use for the start of the seek, in:\n"
5355 " [none, set, end]",
5358 .possible_variables = NULL,
5362 .name = "stop_type",
5363 .description = "The GstSeekType to use for the stop of the seek, in:\n"
5364 " [none, set, end]",
5367 .possible_variables = NULL,
5372 .description = "The stop value of the seek",
5374 .types = "double or string (GstClockTime)",
5375 .possible_variables =
5376 "`position`: The current position in the stream\n"
5377 "`duration`: The duration of the stream",
5378 .def ="GST_CLOCK_TIME_NONE",
5382 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
5383 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
5384 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
5385 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
5388 REGISTER_ACTION_TYPE ("pause", _execute_pause,
5389 ((GstValidateActionParameter []) {
5392 .description = "The duration during which the stream will be paused",
5394 .types = "double or string (GstClockTime)",
5395 .possible_variables = NULL,
5400 "Sets pipeline to PAUSED. You can add a 'duration'\n"
5401 "parameter so the pipeline goes back to playing after that duration\n"
5403 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
5405 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
5406 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
5408 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
5409 "Stops the execution of the scenario. It will post a 'request-state'"
5410 " message on the bus with NULL as a requested state"
5411 " and the application is responsible for stopping itself."
5412 " If you override that action type, make sure to link up.",
5413 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
5415 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
5416 "Sends an EOS event to the pipeline",
5417 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
5419 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
5420 ((GstValidateActionParameter []) {
5423 .description = "Selects which track type to change (can be 'audio', 'video',"
5427 .possible_variables = NULL,
5432 .description = "Selects which track of this type to use: it can be either a number,\n"
5433 "which will be the Nth track of the given type, or a number with a '+' or\n"
5434 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
5435 "'-1' means 'previous track')",
5437 .types = "string: to switch track relatively\n"
5438 "int: To use the actual index to use",
5439 .possible_variables = NULL,
5444 "The 'switch-track' command can be used to switch tracks."
5445 , GST_VALIDATE_ACTION_TYPE_NONE);
5447 REGISTER_ACTION_TYPE ("wait", _execute_wait,
5448 ((GstValidateActionParameter []) {
5451 .description = "the duration while no other action will be executed",
5453 .types = "double or string (GstClockTime)",
5456 .name = "target-element-name",
5457 .description = "The name of the GstElement to wait @signal-name on.",
5462 .name = "signal-name",
5463 .description = "The name of the signal to wait for on @target-element-name",
5469 .name = "message-type",
5470 .description = "The name of the message type to wait for (on @target-element-name"
5478 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
5479 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
5481 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
5482 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
5483 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
5484 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
5485 GST_VALIDATE_ACTION_TYPE_NONE);
5487 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank,
5488 ((GstValidateActionParameter []) {
5491 .description = "The name of a GstFeature or GstPlugin",
5497 .description = "The GstRank to set on @name",
5499 .types = "string, int",
5503 "Changes the ranking of a particular plugin feature(s)",
5504 GST_VALIDATE_ACTION_TYPE_CONFIG);
5506 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank,
5507 ((GstValidateActionParameter []) {
5509 .name = "feature-name",
5510 .description = "The name of a GstFeature",
5516 .description = "The GstRank to set on @feature-name",
5518 .types = "string, int",
5522 "Changes the ranking of a particular plugin feature",
5523 GST_VALIDATE_ACTION_TYPE_CONFIG);
5525 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
5526 ((GstValidateActionParameter []) {
5529 .description = "A GstState as a string, should be in: \n"
5530 " * ['null', 'ready', 'paused', 'playing']",
5536 "Changes the state of the pipeline to any GstState",
5537 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
5539 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
5540 ((GstValidateActionParameter []) {
5543 "Define vars to be used in other actions.\n"
5544 "For example you can define vars for buffer checksum"
5545 " to be used in the \"check-last-sample\" action type as follow:\n\n"
5547 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
5548 " check-last-sample, checksum=frame1\n"
5550 GST_VALIDATE_ACTION_TYPE_NONE);
5552 REGISTER_ACTION_TYPE ("set-property", _execute_set_or_check_property,
5553 ((GstValidateActionParameter []) {
5555 .name = "target-element-name",
5556 .description = "The name of the GstElement to set a property on",
5562 .name = "target-element-factory-name",
5563 .description = "The name factory for which to set a property on built elements",
5569 .name = "target-element-klass",
5570 .description = "The klass of the GstElements to set a property on",
5576 .name = "property-name",
5577 .description = "The name of the property to set on @target-element-name",
5583 .name = "property-value",
5584 .description = "The value of @property-name to be set on the element",
5586 .types = "The same type of @property-name",
5591 "Sets a property of an element or klass of elements in the pipeline.\n"
5592 "Besides property-name and value, either 'target-element-name' or\n"
5593 "'target-element-klass' needs to be defined",
5594 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
5595 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
5596 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
5598 REGISTER_ACTION_TYPE("check-property", _execute_set_or_check_property,
5599 ((GstValidateActionParameter[]) {
5600 { .name = "target-element-name",
5601 .description = "The name of the GstElement to check a property value",
5605 { .name = "target-element-factory-name",
5606 .description = "The name factory for which to check a property value on built elements",
5610 { .name = "target-element-klass",
5611 .description = "The klass of the GstElements to check a property on",
5615 { .name = "property-name",
5616 .description = "The name of the property to set on @target-element-name",
5620 { .name = "property-value",
5621 .description = "The expected value of @property-name",
5623 .types = "The same type of @property-name",
5626 "Check the value of property of an element or klass of elements in the pipeline.\n"
5627 "Besides property-name and value, either 'target-element-name' or\n"
5628 "'target-element-klass' needs to be defined",
5629 GST_VALIDATE_ACTION_TYPE_NONE);
5631 REGISTER_ACTION_TYPE ("set-debug-threshold",
5632 _execute_set_debug_threshold,
5633 ((GstValidateActionParameter [])
5636 .name = "debug-threshold",
5637 .description = "String defining debug threshold\n"
5638 "See gst_debug_set_threshold_from_string",
5643 "Sets the debug level to be used, same format as\n"
5644 "setting the GST_DEBUG env variable",
5645 GST_VALIDATE_ACTION_TYPE_NONE);
5647 REGISTER_ACTION_TYPE ("include",
5648 NULL, /* This is handled directly when loading a scenario */
5649 ((GstValidateActionParameter [])
5653 .description = "The location of the sub scenario to include.",
5658 "Include a sub scenario file.",
5659 GST_VALIDATE_ACTION_TYPE_CONFIG);
5661 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
5662 ((GstValidateActionParameter [])
5665 .name = "target-element-name",
5666 .description = "The name of the GstElement to emit a signal on",
5671 .name = "signal-name",
5672 .description = "The name of the signal to emit on @target-element-name",
5679 "Emits a signal to an element in the pipeline",
5680 GST_VALIDATE_ACTION_TYPE_NONE);
5682 REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
5683 ((GstValidateActionParameter [])
5686 .name = "target-element-name",
5687 .description = "The name of the appsrc to push data on",
5692 .name = "file-name",
5693 .description = "Relative path to a file whose contents will be pushed as a buffer",
5699 .description = "Offset within the file where the buffer will start",
5705 .description = "Number of bytes from the file that will be pushed as a buffer",
5711 .description = "Caps for the buffer to be pushed",
5717 "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.",
5718 GST_VALIDATE_ACTION_TYPE_NONE);
5720 REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
5721 ((GstValidateActionParameter [])
5724 .name = "target-element-name",
5725 .description = "The name of the appsrc to emit EOS on",
5731 "Queues a EOS event in an appsrc.",
5732 GST_VALIDATE_ACTION_TYPE_NONE);
5734 REGISTER_ACTION_TYPE ("flush", _execute_flush,
5735 ((GstValidateActionParameter [])
5738 .name = "target-element-name",
5739 .description = "The name of the appsrc to flush on",
5744 .name = "reset-time",
5745 .description = "Whether the flush should reset running time",
5752 "Sends FLUSH_START and FLUSH_STOP events.",
5753 GST_VALIDATE_ACTION_TYPE_NONE);
5755 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
5756 ((GstValidateActionParameter [])
5759 .name = "plugin-name",
5760 .description = "The name of the GstPlugin to disable",
5765 .name = "as-config",
5766 .description = "Execute action as a config action (meaning when loading the scenario)",
5773 "Disables a GstPlugin",
5774 GST_VALIDATE_ACTION_TYPE_NONE);
5776 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
5777 ((GstValidateActionParameter []) {
5779 .name = "sink-name",
5780 .description = "The name of the sink element to check sample on.",
5786 .name = "sink-factory-name",
5787 .description = "The name of the factory of the sink element to check sample on.",
5793 .name = "sinkpad-caps",
5794 .description = "The caps (as string) of the sink to check.",
5801 .description = "The reference checksum of the buffer.",
5807 .name = "timecode-frame-number",
5808 .description = "The frame number of the buffer as specified on its"
5809 " GstVideoTimeCodeMeta",
5816 "Checks the last-sample checksum or frame number (set on its "
5817 " GstVideoTimeCodeMeta) on declared Sink element."
5818 " This allows checking the checksum of a buffer after a 'seek' or after a"
5819 " GESTimeline 'commit'"
5821 GST_VALIDATE_ACTION_TYPE_INTERLACED);
5823 REGISTER_ACTION_TYPE ("video-request-key-unit", _execute_request_key_unit,
5824 ((GstValidateActionParameter []) {
5826 .name = "direction",
5827 .description = "The direction for the event to travel, should be in\n"
5828 " * [upstream, downstream]",
5834 .name = "running-time",
5835 .description = "The running_time can be set to request a new key unit at a specific running_time.\n"
5836 "If not set, GST_CLOCK_TIME_NONE will be used so upstream elements will produce a new key unit "
5837 "as soon as possible.",
5839 .types = "double or string",
5840 .possible_variables = "position: The current position in the stream\n"
5841 "duration: The duration of the stream",
5845 .name = "all-headers",
5846 .description = "TRUE to produce headers when starting a new key unit",
5854 .description = "integer that can be used to number key units",
5861 .name = "target-element-name",
5862 .description = "The name of the GstElement to send a send force-key-unit to",
5868 .name = "target-element-factory-name",
5869 .description = "The factory name of the GstElements to send a send force-key-unit to",
5875 .name = "target-element-klass",
5876 .description = "The klass of the GstElements to send a send force-key-unit to",
5878 .def = "Video/Encoder",
5884 .description = "The name of the GstPad to send a send force-key-unit to",
5892 .description = "The name of the GstPad to send a send force-key-unit to",
5900 "Request a video key unit", FALSE);
5905 gst_validate_scenario_deinit (void)
5907 _free_action_types (action_types);
5908 action_types = NULL;