3 * Copyright (C) 2013 Collabora Ltd.
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
5 * Copyright (C) 2018 Thibault Saunier <tsaunier@igalia.com>
7 * gst-validate-scenario.c - Validate Scenario class
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 * SECTION:gst-validate-scenario
26 * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
28 * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
29 * It is basically an ordered list of #GstValidateAction that will be executed during the
30 * execution of the pipeline.
32 * Possible configurations (see #GST_VALIDATE_CONFIG):
33 * * scenario-action-execution-interval: Sets the interval in
34 * milliseconds (1/1000ths of a second), between which actions
35 * will be executed, setting it to 0 means "execute in idle".
36 * The default value is 10ms.
48 #include "gst-validate-internal.h"
49 #include "gst-validate-scenario.h"
50 #include "gst-validate-reporter.h"
51 #include "gst-validate-report.h"
52 #include "gst-validate-utils.h"
54 #include <gst/validate/gst-validate-override.h>
55 #include <gst/validate/gst-validate-override-registry.h>
56 #include <gst/validate/gst-validate-pipeline-monitor.h>
58 #define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
59 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
61 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval
62 TODO make it overridable */
64 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
65 #undef GST_CAT_DEFAULT
66 #define GST_CAT_DEFAULT gst_validate_scenario_debug
68 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
69 gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
72 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
74 #define SCENARIO_LOCK(scenario) (g_mutex_lock(&scenario->priv->lock))
75 #define SCENARIO_UNLOCK(scenario) (g_mutex_unlock(&scenario->priv->lock))
77 #define DECLARE_AND_GET_PIPELINE(s,a) \
78 GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
79 if (pipeline == NULL) { \
80 GST_VALIDATE_REPORT (s, SCENARIO_ACTION_EXECUTION_ERROR, \
81 "Can't execute a '%s' action after the pipeline " \
82 "has been destroyed.", a->type); \
83 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
101 static guint scenario_signals[LAST_SIGNAL] = { 0 };
103 static GList *action_types = NULL;
104 static void gst_validate_scenario_dispose (GObject * object);
105 static void gst_validate_scenario_finalize (GObject * object);
106 static GstValidateActionType *_find_action_type (const gchar * type_name);
108 /* GstValidateScenario is not really thread safe and
109 * everything should be done from the thread GstValidate
110 * was inited from, unless stated otherwise.
112 struct _GstValidateScenarioPrivate
115 GstValidateRunner *runner;
116 gboolean execute_on_idle;
121 GList *interlaced_actions; /* MT safe. Protected with SCENARIO_LOCK */
122 GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */
124 /* List of action that need parsing when reaching ASYNC_DONE
125 * most probably to be able to query duration */
126 GList *needs_parsing;
129 GstSeekFlags seek_flags;
130 GstClockTime segment_start;
131 GstClockTime segment_stop;
132 GstClockTime seek_pos_tol;
134 /* If we seeked in paused the position should be exactly what
135 * the seek value was (if accurate) */
136 gboolean seeked_in_pause;
140 gboolean handles_state;
142 guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */
144 guint signal_handler_id;
145 guint action_execution_interval;
147 /* Name of message the wait action is waiting for */
148 const gchar *message_type;
153 gboolean changing_state;
154 gboolean needs_async_done;
155 GstState target_state;
159 gchar *pipeline_name;
160 GstClockTime max_latency;
164 /* 'switch-track action' currently waiting for
165 * GST_MESSAGE_STREAMS_SELECTED to be completed. */
166 GstValidateAction *pending_switch_track;
170 GWeakRef ref_pipeline;
173 typedef struct KeyFileGroupName
179 static GstValidateInterceptionReturn
180 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
181 GstValidateReport * report)
185 for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
187 GstValidateOverride *override = (GstValidateOverride *) tmp->data;
189 gst_validate_override_get_severity (override,
190 gst_validate_issue_get_id (report->issue), report->level);
193 return GST_VALIDATE_REPORTER_REPORT;
197 * gst_validate_scenario_get_pipeline:
198 * @scenario: The scenario to retrieve a pipeline from
200 * Returns: (transfer full): The #GstPipeline the scenario is running
204 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
206 return g_weak_ref_get (&scenario->priv->ref_pipeline);
210 _get_pipeline (GstValidateReporter * reporter)
213 GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
214 (GST_VALIDATE_SCENARIO (reporter)));
218 _reporter_iface_init (GstValidateReporterInterface * iface)
220 iface->intercept_report = gst_validate_scenario_intercept_report;
221 iface->get_pipeline = _get_pipeline;
224 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
225 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
226 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init));
228 /* GstValidateAction implementation */
229 static GType _gst_validate_action_type = 0;
231 struct _GstValidateActionPrivate
233 GstStructure *main_structure;
234 GstValidateExecuteActionReturn state; /* Actually ActionState */
236 gboolean executing_last_subaction;
239 GstClockTime execution_time;
240 GstClockTime timeout;
246 gst_validate_action_serialize (GstValidateAction * action)
248 JsonNode *node = json_node_alloc ();
249 JsonObject *jreport = json_object_new ();
250 gchar *action_args = gst_structure_to_string (action->structure);
252 json_object_set_string_member (jreport, "type", "action");
253 json_object_set_string_member (jreport, "action-type", action->type);
254 json_object_set_int_member (jreport, "playback-time",
255 (gint64) action->playback_time);
256 json_object_set_string_member (jreport, "args", action_args);
257 g_free (action_args);
259 node = json_node_init_object (node, jreport);
260 json_object_unref (jreport);
266 gst_validate_action_get_type (void)
268 if (_gst_validate_action_type == 0) {
269 _gst_validate_action_type =
270 g_boxed_type_register_static (g_intern_static_string
271 ("GstValidateAction"), (GBoxedCopyFunc) gst_mini_object_ref,
272 (GBoxedFreeFunc) gst_mini_object_unref);
274 json_boxed_register_serialize_func (_gst_validate_action_type,
276 (JsonBoxedSerializeFunc) gst_validate_action_serialize);
279 return _gst_validate_action_type;
282 static GstValidateAction *gst_validate_action_new (GstValidateScenario *
283 scenario, GstValidateActionType * type);
284 static gboolean execute_next_action (GstValidateScenario * scenario);
286 gst_validate_scenario_load (GstValidateScenario * scenario,
287 const gchar * scenario_name, const gchar * relative_scenario);
289 static GstValidateAction *
290 _action_copy (GstValidateAction * act)
292 GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
293 GstValidateAction *copy = gst_validate_action_new (scenario,
294 _find_action_type (act->type));
296 gst_object_unref (scenario);
298 if (act->structure) {
299 copy->structure = gst_structure_copy (act->structure);
300 copy->type = gst_structure_get_name (copy->structure);
301 if (!(act->name = gst_structure_get_string (copy->structure, "name")))
305 if (act->priv->main_structure)
306 copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
308 copy->action_number = act->action_number;
309 copy->playback_time = act->playback_time;
310 copy->priv->timeout = act->priv->timeout;
316 _action_free (GstValidateAction * action)
318 if (action->structure)
319 gst_structure_free (action->structure);
321 if (action->priv->main_structure)
322 gst_structure_free (action->priv->main_structure);
324 g_weak_ref_clear (&action->priv->scenario);
326 g_slice_free (GstValidateActionPrivate, action->priv);
327 g_slice_free (GstValidateAction, action);
331 gst_validate_action_init (GstValidateAction * action)
333 gst_mini_object_init (((GstMiniObject *) action), 0,
334 _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
335 (GstMiniObjectFreeFunction) _action_free);
337 action->priv = g_slice_new0 (GstValidateActionPrivate);
339 g_weak_ref_init (&action->priv->scenario, NULL);
343 gst_validate_action_unref (GstValidateAction * action)
345 gst_mini_object_unref (GST_MINI_OBJECT (action));
348 static GstValidateAction *
349 gst_validate_action_new (GstValidateScenario * scenario,
350 GstValidateActionType * action_type)
352 GstValidateAction *action = g_slice_new0 (GstValidateAction);
354 gst_validate_action_init (action);
355 action->playback_time = GST_CLOCK_TIME_NONE;
356 action->priv->timeout = GST_CLOCK_TIME_NONE;
357 action->type = action_type->name;
360 g_weak_ref_set (&action->priv->scenario, scenario);
366 _action_check_and_set_printed (GstValidateAction * action)
368 if (action->priv->printed == FALSE) {
369 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
372 action->priv->printed = TRUE;
381 gst_validate_action_is_subaction (GstValidateAction * action)
383 return !gst_structure_is_equal (action->structure,
384 action->priv->main_structure);
387 /* GstValidateActionType implementation */
388 GType _gst_validate_action_type_type;
389 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
390 static GstValidateActionType *gst_validate_action_type_new (void);
393 _action_type_free (GstValidateActionType * type)
395 g_free (type->parameters);
396 g_free (type->description);
398 g_free (type->implementer_namespace);
400 if (type->overriden_type)
401 gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
403 g_slice_free (GstValidateActionType, type);
407 gst_validate_action_type_init (GstValidateActionType * type)
409 gst_mini_object_init ((GstMiniObject *) type, 0,
410 _gst_validate_action_type_type, NULL, NULL,
411 (GstMiniObjectFreeFunction) _action_type_free);
414 GstValidateActionType *
415 gst_validate_action_type_new (void)
417 GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
419 gst_validate_action_type_init (type);
421 /* action types are never freed */
422 GST_MINI_OBJECT_FLAG_SET (type, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
426 static GstValidateActionType *
427 _find_action_type (const gchar * type_name)
431 for (tmp = action_types; tmp; tmp = tmp->next) {
432 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
433 if (g_strcmp0 (atype->name, type_name) == 0)
441 _set_variable_func (const gchar * name, double *value, gpointer user_data)
444 GstValidateScenario *scenario = GST_VALIDATE_SCENARIO (user_data);
445 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
448 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
453 if (!g_strcmp0 (name, "duration")) {
457 gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration))
458 || !GST_CLOCK_TIME_IS_VALID (duration)) {
459 GstValidateMonitor *monitor =
460 (GstValidateMonitor *) (g_object_get_data ((GObject *)
461 pipeline, "validate-monitor"));
462 GST_WARNING_OBJECT (scenario,
463 "Could not query duration. Trying to get duration from media-info");
464 if (monitor && monitor->media_descriptor)
466 gst_validate_media_descriptor_get_duration
467 (monitor->media_descriptor);
469 GST_ERROR_OBJECT (scenario, "Media-info not set");
475 if (!GST_CLOCK_TIME_IS_VALID (duration))
476 *value = G_MAXDOUBLE;
478 *value = ((double) duration / GST_SECOND);
481 } else if (!g_strcmp0 (name, "position")) {
484 if (!gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
485 GST_WARNING_OBJECT (scenario, "Could not query position");
489 if (!GST_CLOCK_TIME_IS_VALID (position))
490 *value = G_MAXDOUBLE;
492 *value = ((double) position / GST_SECOND);
498 gst_object_unref (pipeline);
502 gst_object_unref (pipeline);
506 /* Check that @list doesn't contain any non-optional actions */
508 actions_list_is_done (GList * list)
512 for (l = list; l != NULL; l = g_list_next (l)) {
513 GstValidateAction *action = l->data;
515 if (!action->priv->optional)
523 _check_scenario_is_done (GstValidateScenario * scenario)
525 SCENARIO_LOCK (scenario);
526 if (actions_list_is_done (scenario->priv->actions) &&
527 actions_list_is_done (scenario->priv->interlaced_actions) &&
528 actions_list_is_done (scenario->priv->on_addition_actions)) {
529 SCENARIO_UNLOCK (scenario);
531 g_signal_emit (scenario, scenario_signals[DONE], 0);
533 SCENARIO_UNLOCK (scenario);
538 * gst_validate_action_get_clocktime:
539 * @scenario: The #GstValidateScenario from which to get a time
540 * for a parameter of an action
541 * @action: The action from which to retrieve the time for @name
543 * @name: The name of the parameter for which to retrieve a time
544 * @retval: (out): The return value for the wanted time
547 * Get a time value for the @name parameter of an action. This
548 * method should be called to retrieve and compute a timed value of a given
549 * action. It will first try to retrieve the value as a double,
550 * then get it as a string and execute any formula taking into account
551 * the 'position' and 'duration' variables. And it will always convert that
552 * value to a GstClockTime.
554 * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
557 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
558 GstValidateAction * action, const gchar * name, GstClockTime * retval)
560 if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
565 if (!(strval = gst_structure_get_string (action->structure, name))) {
566 GST_INFO_OBJECT (scenario, "Could not find %s", name);
570 val = gst_validate_utils_parse_expression (strval, _set_variable_func,
574 GST_WARNING ("Error while parsing %s: %s", strval, error);
578 } else if (val == -1.0) {
579 *retval = GST_CLOCK_TIME_NONE;
581 *retval = val * GST_SECOND;
582 *retval = GST_ROUND_UP_4 (*retval);
592 * gst_validate_scenario_execute_seek:
593 * @scenario: The #GstValidateScenario for which to execute a seek action
594 * @action: The seek action to execute
595 * @rate: The playback rate of the seek
596 * @format: The #GstFormat of the seek
597 * @flags: The #GstSeekFlags of the seek
598 * @start_type: The #GstSeekType of the start value of the seek
599 * @start: The start time of the seek
600 * @stop_type: The #GstSeekType of the stop value of the seek
601 * @stop: The stop time of the seek
603 * Executes a seek event on the scenario's pipeline. You should always use
604 * this method when you want to execute a seek inside a new action type
605 * so that the scenario state is updated taking into account that seek.
607 * For more information you should have a look at #gst_event_new_seek
609 * Returns: %TRUE if the seek could be executed, %FALSE otherwise
611 GstValidateExecuteActionReturn
612 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
613 GstValidateAction * action, gdouble rate, GstFormat format,
614 GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
615 GstSeekType stop_type, GstClockTime stop)
619 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
620 GstValidateScenarioPrivate *priv = scenario->priv;
621 DECLARE_AND_GET_PIPELINE (scenario, action);
623 seek = gst_event_new_seek (rate, format, flags, start_type, start,
626 gst_event_ref (seek);
627 if (gst_element_send_event (pipeline, seek)) {
628 gst_event_replace (&priv->last_seek, seek);
629 priv->seek_flags = flags;
631 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_NOT_HANDLED,
632 "Could not execute seek: '(position %" GST_TIME_FORMAT
633 "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
634 " stop: %" GST_TIME_FORMAT " Rate %lf'",
635 GST_TIME_ARGS (action->playback_time), action->name,
636 action->action_number, action->repeat, GST_TIME_ARGS (start),
637 GST_TIME_ARGS (stop), rate);
638 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
640 gst_event_unref (seek);
641 gst_object_unref (pipeline);
647 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
649 const char *str_format, *str_flags, *str_start_type, *str_stop_type;
652 guint format = GST_FORMAT_TIME;
653 GstSeekFlags flags = 0;
654 guint start_type = GST_SEEK_TYPE_SET;
656 guint stop_type = GST_SEEK_TYPE_SET;
657 GstClockTime stop = GST_CLOCK_TIME_NONE;
659 if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
660 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
662 gst_structure_get_double (action->structure, "rate", &rate);
663 if ((str_format = gst_structure_get_string (action->structure, "format")))
664 gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
666 if ((str_start_type =
667 gst_structure_get_string (action->structure, "start_type")))
668 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
672 gst_structure_get_string (action->structure, "stop_type")))
673 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
676 if ((str_flags = gst_structure_get_string (action->structure, "flags")))
677 flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
679 gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
681 return gst_validate_scenario_execute_seek (scenario, action, rate, format,
682 flags, start_type, start, stop_type, stop);
686 _pause_action_restore_playing (GstValidateScenario * scenario)
688 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
691 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
696 gst_validate_printf (scenario, "Back to playing\n");
698 if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
699 GST_STATE_CHANGE_FAILURE) {
700 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
701 "Failed to set state to playing");
702 scenario->priv->target_state = GST_STATE_PLAYING;
705 gst_object_unref (pipeline);
711 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
713 gst_structure_id_set_value (consts, field_id, value);
718 static GstValidateExecuteActionReturn
719 _execute_define_vars (GstValidateScenario * scenario,
720 GstValidateAction * action)
722 gst_structure_foreach (action->structure,
723 (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
725 return GST_VALIDATE_EXECUTE_ACTION_OK;
728 static GstValidateExecuteActionReturn
729 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
732 const gchar *str_state;
733 GstStateChangeReturn ret;
734 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
736 DECLARE_AND_GET_PIPELINE (scenario, action);
738 g_return_val_if_fail ((str_state =
739 gst_structure_get_string (action->structure, "state")), FALSE);
741 g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
742 str_state, &state), FALSE);
745 scenario->priv->target_state = state;
746 scenario->priv->changing_state = TRUE;
747 scenario->priv->seeked_in_pause = FALSE;
749 ret = gst_element_set_state (pipeline, state);
750 if (ret == GST_STATE_CHANGE_FAILURE) {
751 scenario->priv->changing_state = FALSE;
752 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
753 "Failed to set state to %s", str_state);
755 /* Nothing async on failure, action will be removed automatically */
756 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
758 } else if (ret == GST_STATE_CHANGE_ASYNC) {
760 scenario->priv->needs_async_done = TRUE;
761 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
766 scenario->priv->changing_state = FALSE;
769 gst_object_unref (pipeline);
774 static GstValidateExecuteActionReturn
775 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
777 GstClockTime duration = 0;
778 GstValidateExecuteActionReturn ret;
780 gst_structure_get (action->structure, "duration", G_TYPE_UINT64, &duration,
782 gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
784 GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
785 GST_TIME_ARGS (duration));
787 ret = _execute_set_state (scenario, action);
789 if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
790 g_timeout_add (GST_TIME_AS_MSECONDS (duration),
791 (GSourceFunc) _pause_action_restore_playing, scenario);
796 static GstValidateExecuteActionReturn
797 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
799 GST_DEBUG ("Playing back");
801 gst_structure_set (action->structure, "state", G_TYPE_STRING,
805 return _execute_set_state (scenario, action);
809 _action_sets_state (GstValidateAction * action)
814 if (g_strcmp0 (action->type, "set-state") == 0)
817 if (g_strcmp0 (action->type, "play") == 0)
820 if (g_strcmp0 (action->type, "pause") == 0)
828 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
830 GstValidateScenarioPrivate *priv = scenario->priv;
833 dropped = g_atomic_int_get (&priv->dropped);
835 if (priv->max_dropped == -1 || dropped != -1)
838 GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
839 dropped, priv->max_dropped);
841 if (dropped > priv->max_dropped) {
842 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_TOO_MANY_BUFFERS_DROPPED,
843 "Too many buffers have been dropped: %d (max allowed: %d)",
844 dropped, priv->max_dropped);
848 static GstValidateExecuteActionReturn
849 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
852 GstValidateScenarioPrivate *priv = scenario->priv;
854 DECLARE_AND_GET_PIPELINE (scenario, action);
856 bus = gst_element_get_bus (pipeline);
857 SCENARIO_LOCK (scenario);
858 if (priv->execute_actions_source_id) {
859 g_source_remove (priv->execute_actions_source_id);
860 priv->execute_actions_source_id = 0;
862 SCENARIO_UNLOCK (scenario);
864 gst_validate_scenario_check_dropped (scenario);
867 gst_message_new_request_state (GST_OBJECT_CAST (scenario),
869 gst_object_unref (bus);
870 gst_object_unref (pipeline);
875 static GstValidateExecuteActionReturn
876 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
880 DECLARE_AND_GET_PIPELINE (scenario, action);
882 GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
883 GST_TIME_ARGS (action->playback_time));
885 ret = gst_element_send_event (pipeline, gst_event_new_eos ());
886 gst_object_unref (pipeline);
888 return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
889 GST_VALIDATE_EXECUTE_ACTION_ERROR;
893 find_input_selector (GValue * velement, const gchar * type)
895 GstElement *element = g_value_get_object (velement);
898 if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
899 GstPad *srcpad = gst_element_get_static_pad (element, "src");
902 GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
906 gst_structure_get_name (gst_caps_get_structure (caps, 0));
907 gboolean found = FALSE;
909 if (g_strcmp0 (type, "audio") == 0)
910 found = g_str_has_prefix (mime, "audio/");
911 else if (g_strcmp0 (type, "video") == 0)
912 found = g_str_has_prefix (mime, "video/")
913 && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
914 else if (g_strcmp0 (type, "text") == 0)
915 found = g_str_has_prefix (mime, "text/")
916 || g_str_has_prefix (mime, "subtitle/")
917 || g_str_has_prefix (mime, "video/x-dvd-subpicture");
923 gst_caps_unref (caps);
924 gst_object_unref (srcpad);
931 find_input_selector_with_type (GstBin * bin, const gchar * type)
933 GValue result = { 0, };
934 GstElement *input_selector = NULL;
935 GstIterator *iterator = gst_bin_iterate_recurse (bin);
937 if (gst_iterator_find_custom (iterator,
938 (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
939 input_selector = g_value_get_object (&result);
941 gst_iterator_free (iterator);
943 return input_selector;
947 find_nth_sink_pad (GstElement * element, int index)
949 GstIterator *iterator;
950 gboolean done = FALSE;
952 int dec_index = index;
953 GValue data = { 0, };
955 iterator = gst_element_iterate_sink_pads (element);
957 switch (gst_iterator_next (iterator, &data)) {
958 case GST_ITERATOR_OK:
961 pad = g_value_get_object (&data);
964 g_value_reset (&data);
966 case GST_ITERATOR_RESYNC:
967 gst_iterator_resync (iterator);
970 case GST_ITERATOR_ERROR:
973 case GST_ITERATOR_DONE:
978 gst_iterator_free (iterator);
983 find_sink_pad_index (GstElement * element, GstPad * pad)
985 GstIterator *iterator;
986 gboolean done = FALSE;
988 GValue data = { 0, };
990 iterator = gst_element_iterate_sink_pads (element);
992 switch (gst_iterator_next (iterator, &data)) {
993 case GST_ITERATOR_OK:
994 if (pad == g_value_get_object (&data)) {
999 g_value_reset (&data);
1001 case GST_ITERATOR_RESYNC:
1002 gst_iterator_resync (iterator);
1005 case GST_ITERATOR_ERROR:
1008 case GST_ITERATOR_DONE:
1013 gst_iterator_free (iterator);
1017 static GstPadProbeReturn
1018 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1019 GstValidateAction * action)
1021 if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1022 gst_validate_action_set_done (action);
1024 return GST_PAD_PROBE_REMOVE;
1027 return GST_PAD_PROBE_OK;
1030 static GstValidateExecuteActionReturn
1031 execute_switch_track_default (GstValidateScenario * scenario,
1032 GstValidateAction * action)
1035 gboolean relative = FALSE;
1036 const gchar *type, *str_index;
1037 GstElement *input_selector;
1038 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1040 DECLARE_AND_GET_PIPELINE (scenario, action);
1042 if (!(type = gst_structure_get_string (action->structure, "type")))
1045 /* First find an input selector that has the right type */
1046 input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1047 if (input_selector) {
1048 GstState state, next;
1049 GstPad *pad, *cpad, *srcpad;
1051 ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1052 str_index = gst_structure_get_string (action->structure, "index");
1054 if (str_index == NULL) {
1055 if (!gst_structure_get_uint (action->structure, "index", &index)) {
1056 GST_WARNING ("No index given, defaulting to +1");
1061 relative = strchr ("+-", str_index[0]) != NULL;
1062 index = g_ascii_strtoll (str_index, NULL, 10);
1065 if (relative) { /* We are changing track relatively to current track */
1068 g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1070 int current_index = find_sink_pad_index (input_selector, pad);
1072 index = (current_index + index) % npads;
1073 gst_object_unref (pad);
1077 pad = find_nth_sink_pad (input_selector, index);
1078 g_object_get (input_selector, "active-pad", &cpad, NULL);
1079 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1080 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1081 srcpad = gst_element_get_static_pad (input_selector, "src");
1083 gst_pad_add_probe (srcpad,
1084 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1085 (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1086 ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1087 gst_object_unref (srcpad);
1090 g_object_set (input_selector, "active-pad", pad, NULL);
1091 gst_object_unref (pad);
1092 gst_object_unref (cpad);
1093 gst_object_unref (input_selector);
1098 /* No selector found -> Failed */
1100 gst_object_unref (pipeline);
1105 static GstPadProbeReturn
1106 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1107 GstValidateAction * action)
1109 if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1110 gst_validate_action_set_done (action);
1111 return GST_PAD_PROBE_REMOVE;
1113 return GST_PAD_PROBE_OK;
1116 static GstValidateExecuteActionReturn
1117 execute_switch_track_pb (GstValidateScenario * scenario,
1118 GstValidateAction * action)
1121 const gchar *type, *str_index;
1123 gint flags, current, tflag;
1124 gchar *tmp, *current_txt;
1126 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1127 gboolean relative = FALSE, disabling = FALSE;
1129 DECLARE_AND_GET_PIPELINE (scenario, action);
1131 if (!(type = gst_structure_get_string (action->structure, "type")))
1134 tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1136 current_txt = g_strdup_printf ("current-%s", type);
1138 tmp = g_strdup_printf ("n-%s", type);
1139 g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, ¤t,
1142 /* Don't try to use -1 */
1148 if (gst_structure_has_field (action->structure, "disable")) {
1152 } else if (!(str_index =
1153 gst_structure_get_string (action->structure, "index"))) {
1154 if (!gst_structure_get_int (action->structure, "index", &index)) {
1155 GST_WARNING ("No index given, defaulting to +1");
1160 relative = strchr ("+-", str_index[0]) != NULL;
1161 index = g_ascii_strtoll (str_index, NULL, 10);
1164 if (relative) { /* We are changing track relatively to current track */
1166 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1167 "Trying to execute a relative %s for %s track when there"
1168 " is no track of this type available on current stream.",
1169 action->type, type);
1171 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1175 index = (current + index) % n;
1179 GstState state, next;
1180 GstPad *oldpad, *newpad;
1181 tmp = g_strdup_printf ("get-%s-pad", type);
1182 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1183 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1185 gst_validate_printf (action, "Switching to track number: %i,"
1186 " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1187 GST_DEBUG_PAD_NAME (newpad));
1191 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1192 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1193 GstPad *srcpad = NULL;
1194 GstElement *combiner = NULL;
1195 if (newpad == oldpad) {
1196 srcpad = gst_pad_get_peer (oldpad);
1197 } else if (newpad) {
1198 combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1200 srcpad = gst_element_get_static_pad (combiner, "src");
1201 gst_object_unref (combiner);
1206 gst_pad_add_probe (srcpad,
1207 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1208 (GstPadProbeCallback) _check_pad_event_selection_done, action,
1210 gst_object_unref (srcpad);
1212 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1214 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1218 gst_object_unref (oldpad);
1219 gst_object_unref (newpad);
1221 gst_validate_printf (action, "Disabling track type %s", type);
1224 g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1225 g_free (current_txt);
1228 gst_object_unref (pipeline);
1232 static GstStreamType
1233 stream_type_from_string (const gchar * type)
1235 if (!g_strcmp0 (type, "video"))
1236 return GST_STREAM_TYPE_VIDEO;
1237 else if (!g_strcmp0 (type, "text"))
1238 return GST_STREAM_TYPE_TEXT;
1241 return GST_STREAM_TYPE_AUDIO;
1244 /* Return a list of stream ID all the currently selected streams but the ones
1247 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1249 GList *streams = NULL, *l;
1251 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1252 GstStream *s = l->data;
1254 if (gst_stream_get_stream_type (s) != type) {
1255 streams = g_list_append (streams, (gpointer) s->stream_id);
1263 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1264 GstStreamType type, gint index, gboolean relative)
1267 guint i, n = 0, current = 0;
1268 GList *result = NULL, *l;
1269 GstStream *streams[256], *s, *current_stream = NULL;
1271 /* Keep all streams which are not @type */
1272 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1275 if (gst_stream_get_stream_type (s) != type) {
1276 result = g_list_append (result, (gpointer) s->stream_id);
1277 } else if (!current_stream) {
1278 /* Assume the stream we want to switch from is the first one */
1283 /* Calculate the number of @type streams */
1284 nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1285 for (i = 0; i < nb_streams; i++) {
1286 s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1288 if (gst_stream_get_stream_type (s) == type) {
1292 && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1299 if (G_UNLIKELY (n == 0)) {
1300 GST_ERROR ("No streams available of the required type");
1304 if (relative) { /* We are changing track relatively to current track */
1305 index = (current + index) % n;
1309 /* Add the new stream we want to switch to */
1312 gst_validate_printf (action, "Switching from stream %s to %s",
1313 current_stream ? current_stream->stream_id : "", s->stream_id);
1315 return g_list_append (result, (gpointer) s->stream_id);
1318 static GstValidateExecuteActionReturn
1319 execute_switch_track_pb3 (GstValidateScenario * scenario,
1320 GstValidateAction * action)
1322 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1323 GstValidateScenarioPrivate *priv = scenario->priv;
1325 GstStreamType stype;
1326 const gchar *type, *str_index;
1327 GList *new_streams = NULL;
1328 GstValidatePipelineMonitor *monitor;
1329 DECLARE_AND_GET_PIPELINE (scenario, action);
1331 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1332 pipeline, "validate-monitor"));
1334 if (!monitor->stream_collection) {
1335 GST_VALIDATE_REPORT (scenario,
1336 SCENARIO_ACTION_EXECUTION_ERROR,
1337 "No stream collection message received on the bus, "
1338 "can not switch track.");
1339 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1343 if (!monitor->streams_selected) {
1344 GST_VALIDATE_REPORT (scenario,
1345 SCENARIO_ACTION_EXECUTION_ERROR,
1346 "No streams selected message received on the bus");
1347 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1351 type = gst_structure_get_string (action->structure, "type");
1352 stype = stream_type_from_string (type);
1354 if (gst_structure_has_field (action->structure, "disable")) {
1355 gst_validate_printf (action, "Disabling track type %s", type);
1356 new_streams = disable_stream (monitor, stype);
1358 gboolean relative = FALSE;
1360 if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
1361 if (!gst_structure_get_int (action->structure, "index", &index)) {
1362 GST_WARNING ("No index given, defaulting to +1");
1367 relative = strchr ("+-", str_index[0]) != NULL;
1368 index = g_ascii_strtoll (str_index, NULL, 10);
1371 new_streams = switch_stream (monitor, action, stype, index, relative);
1374 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
1375 ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
1376 (GDestroyNotify) g_list_free);
1378 if (!gst_element_send_event (pipeline,
1379 gst_event_new_select_streams (new_streams))) {
1380 GST_VALIDATE_REPORT (scenario,
1381 SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
1382 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1386 priv->pending_switch_track = action;
1387 if (scenario->priv->target_state > GST_STATE_PAUSED) {
1388 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1390 gst_mini_object_ref ((GstMiniObject *) action);
1391 res = GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
1395 gst_object_unref (pipeline);
1400 static GstValidateExecuteActionReturn
1401 _execute_switch_track (GstValidateScenario * scenario,
1402 GstValidateAction * action)
1404 GstValidatePipelineMonitor *monitor;
1406 DECLARE_AND_GET_PIPELINE (scenario, action);
1408 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1409 pipeline, "validate-monitor"));
1410 gst_object_unref (pipeline);
1412 if (monitor->is_playbin)
1413 return execute_switch_track_pb (scenario, action);
1414 else if (monitor->is_playbin3)
1415 return execute_switch_track_pb3 (scenario, action);
1417 return execute_switch_track_default (scenario, action);
1420 static GstValidateExecuteActionReturn
1421 _execute_set_rank (GstValidateScenario * scenario, GstValidateAction * action)
1424 GList *features, *origlist;
1426 GstPluginFeature *feature;
1429 if (!(name = gst_structure_get_string (action->structure, "feature-name")) &&
1430 !(name = gst_structure_get_string (action->structure, "name"))) {
1431 GST_ERROR ("Could not find the name of the plugin feature(s) to tweak");
1433 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1436 if (!(gst_structure_get_uint (action->structure, "rank", &rank) ||
1437 gst_structure_get_int (action->structure, "rank", (gint *) & rank))) {
1438 GST_ERROR ("Could not get rank to set on %s", name);
1440 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1443 feature = gst_registry_lookup_feature (gst_registry_get (), name);
1445 gst_plugin_feature_set_rank (feature, rank);
1446 gst_object_unref (feature);
1448 return GST_VALIDATE_EXECUTE_ACTION_OK;
1451 plugin = gst_registry_find_plugin (gst_registry_get (), name);
1453 GST_ERROR ("Could not find %s", name);
1455 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1458 origlist = features =
1459 gst_registry_get_feature_list_by_plugin (gst_registry_get (),
1460 gst_plugin_get_name (plugin));
1461 for (; features; features = features->next)
1462 gst_plugin_feature_set_rank (features->data, rank);
1463 gst_plugin_feature_list_free (origlist);
1465 return GST_VALIDATE_EXECUTE_ACTION_OK;
1468 static inline gboolean
1469 _add_execute_actions_gsource (GstValidateScenario * scenario)
1471 GstValidateScenarioPrivate *priv = scenario->priv;
1473 SCENARIO_LOCK (scenario);
1474 if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
1475 && priv->signal_handler_id == 0 && priv->message_type == NULL) {
1476 if (!scenario->priv->action_execution_interval)
1477 priv->execute_actions_source_id =
1478 g_idle_add ((GSourceFunc) execute_next_action, scenario);
1480 priv->execute_actions_source_id =
1481 g_timeout_add (scenario->priv->action_execution_interval,
1482 (GSourceFunc) execute_next_action, scenario);
1483 SCENARIO_UNLOCK (scenario);
1485 GST_DEBUG_OBJECT (scenario, "Start checking position again");
1488 SCENARIO_UNLOCK (scenario);
1490 GST_LOG_OBJECT (scenario, "No need to start a new gsource");
1495 _get_position (GstValidateScenario * scenario,
1496 GstValidateAction * act, GstClockTime * position)
1498 gboolean has_pos = FALSE, has_dur = FALSE;
1499 GstClockTime duration = -1;
1501 GstValidateScenarioPrivate *priv = scenario->priv;
1502 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1505 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1510 has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
1511 (gint64 *) position)
1512 && GST_CLOCK_TIME_IS_VALID (*position);
1514 gst_element_query_duration (pipeline, GST_FORMAT_TIME,
1515 (gint64 *) & duration)
1516 && GST_CLOCK_TIME_IS_VALID (duration);
1518 if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
1519 act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1520 GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
1521 GST_TIME_ARGS (*position));
1526 if (has_pos && has_dur && !priv->got_eos) {
1527 if (*position > duration) {
1528 _add_execute_actions_gsource (scenario);
1530 GST_VALIDATE_REPORT (scenario,
1531 QUERY_POSITION_SUPERIOR_DURATION,
1532 "Reported position %" GST_TIME_FORMAT " > reported duration %"
1533 GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
1540 gst_object_unref (pipeline);
1544 gst_object_unref (pipeline);
1549 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
1550 GstClockTime * position, gdouble * rate)
1554 GstClockTime start_with_tolerance, stop_with_tolerance;
1555 GstValidateScenarioPrivate *priv = scenario->priv;
1556 GstElement *pipeline;
1558 if (!_get_position (scenario, act, position))
1561 GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
1562 GST_TIME_ARGS (*position));
1564 /* Check if playback is within seek segment */
1565 start_with_tolerance = (priv->segment_start <
1566 priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
1567 stop_with_tolerance =
1568 GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
1569 priv->seek_pos_tol : -1;
1571 if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
1572 && *position > stop_with_tolerance)
1573 || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
1574 && *position < start_with_tolerance)) {
1576 GST_VALIDATE_REPORT (scenario, QUERY_POSITION_OUT_OF_SEGMENT,
1577 "Current position %" GST_TIME_FORMAT " not in the expected range [%"
1578 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
1579 GST_TIME_ARGS (start_with_tolerance),
1580 GST_TIME_ARGS (stop_with_tolerance));
1583 pipeline = gst_validate_scenario_get_pipeline (scenario);
1584 if (pipeline == NULL) {
1585 GST_INFO_OBJECT (scenario, "No pipeline set anymore");
1590 query = gst_query_new_segment (GST_FORMAT_DEFAULT);
1591 if (gst_element_query (GST_ELEMENT (pipeline), query))
1592 gst_query_parse_segment (query, rate, NULL, NULL, NULL);
1593 gst_query_unref (query);
1594 gst_object_unref (pipeline);
1596 if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE) {
1597 if ((*rate > 0 && (*position >= priv->segment_start + priv->seek_pos_tol ||
1598 *position < ((priv->segment_start <
1599 priv->seek_pos_tol) ? 0 : priv->segment_start -
1600 priv->seek_pos_tol)))
1601 || (*rate < 0 && (*position > priv->segment_start + priv->seek_pos_tol
1602 || *position < ((priv->segment_start <
1603 priv->seek_pos_tol) ? 0 : priv->segment_start -
1604 priv->seek_pos_tol)))) {
1605 priv->seeked_in_pause = FALSE;
1606 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_RESULT_POSITION_WRONG,
1607 "Reported position after accurate seek in PAUSED state should be exactly"
1608 " what the user asked for. Position %" GST_TIME_FORMAT
1609 " is not not the expected one: %" GST_TIME_FORMAT,
1610 GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
1618 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
1619 GstClockTime position, gdouble rate)
1621 GstElement *pipeline;
1624 GST_DEBUG_OBJECT (scenario, "No action to execute");
1629 pipeline = gst_validate_scenario_get_pipeline (scenario);
1630 if (pipeline == NULL) {
1632 if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
1633 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
1634 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1635 "Trying to execute an %s action after the pipeline has been destroyed"
1636 " but the type has not been marked as "
1637 "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
1640 } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1641 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1642 "Trying to execute action %s with playback time %" GST_TIME_FORMAT
1643 " after the pipeline has been destroyed. It is impossible"
1644 " to execute an action with a playback time specified"
1645 " after the pipeline has been destroyed",
1646 act->type, GST_TIME_ARGS (act->playback_time));
1651 GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
1654 } else if (scenario->priv->got_eos) {
1655 GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
1656 scenario->priv->got_eos = FALSE;
1657 } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
1658 GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
1659 "just executing actions");
1662 } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
1663 GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
1666 } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
1667 GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
1668 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1669 GST_TIME_ARGS (act->playback_time));
1672 } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
1673 GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
1674 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1675 GST_TIME_ARGS (act->playback_time));
1681 gst_object_unref (pipeline);
1685 gst_object_unref (pipeline);
1689 GstValidateExecuteActionReturn
1690 gst_validate_execute_action (GstValidateActionType * action_type,
1691 GstValidateAction * action)
1693 GstValidateExecuteActionReturn res;
1694 GstValidateScenario *scenario;
1696 g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
1697 GST_VALIDATE_EXECUTE_ACTION_ERROR);
1699 scenario = gst_validate_action_get_scenario (action);
1701 if (action_type->prepare) {
1702 if (action_type->prepare (action) == FALSE) {
1703 GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
1704 " could not be prepared", action->structure);
1706 gst_object_unref (scenario);
1707 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1711 gst_validate_print_action (action, NULL);
1713 action->priv->execution_time = gst_util_get_timestamp ();
1714 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
1715 res = action_type->execute (scenario, action);
1716 gst_object_unref (scenario);
1718 if (!gst_structure_has_field (action->structure, "sub-action")) {
1719 gst_structure_free (action->structure);
1721 action->priv->printed = FALSE;
1722 action->structure = gst_structure_copy (action->priv->main_structure);
1724 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1727 if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC)
1728 action->priv->executing_last_subaction = TRUE;
1735 _set_action_playback_time (GstValidateScenario * scenario,
1736 GstValidateAction * action)
1738 if (!gst_validate_action_get_clocktime (scenario, action,
1739 "playback-time", &action->playback_time)) {
1740 gchar *str = gst_structure_to_string (action->structure);
1742 g_error ("Could not parse playback-time on structure: %s", str);
1748 gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
1749 action->playback_time, NULL);
1754 /* scenario can be NULL **only** if the action is a CONFIG action and
1755 * add_to_lists is FALSE */
1756 static GstValidateExecuteActionReturn
1757 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
1758 GstStructure * structure, gboolean add_to_lists)
1760 gdouble playback_time;
1761 gboolean is_config = FALSE;
1762 GstValidateActionType *action_type;
1763 const gchar *str_playback_time = NULL;
1764 GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
1765 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1768 action->type = gst_structure_get_name (structure);
1769 action_type = _find_action_type (action->type);
1772 GST_ERROR_OBJECT (scenario, "Action type %s no found",
1773 gst_structure_get_name (structure));
1775 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1778 if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
1779 gst_structure_get_double (structure, "playback_time", &playback_time)) {
1780 action->playback_time = playback_time * GST_SECOND;
1781 } else if ((str_playback_time =
1782 gst_structure_get_string (structure, "playback-time")) ||
1783 (str_playback_time =
1784 gst_structure_get_string (structure, "playback_time"))) {
1786 if (add_to_lists && priv)
1787 priv->needs_parsing = g_list_append (priv->needs_parsing, action);
1788 else if (!_set_action_playback_time (scenario, action))
1789 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1792 GST_INFO_OBJECT (scenario,
1793 "No playback time for action %" GST_PTR_FORMAT, structure);
1795 if (!gst_validate_utils_get_clocktime (structure,
1796 "timeout", &action->priv->timeout)) {
1797 GST_INFO_OBJECT (scenario,
1798 "No timeout time for action %" GST_PTR_FORMAT, structure);
1801 action->structure = gst_structure_copy (structure);
1803 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1806 if (!action->priv->main_structure)
1807 action->priv->main_structure = gst_structure_copy (structure);
1809 if (gst_structure_get_boolean (structure, "optional", &optional)) {
1810 if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
1811 GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
1812 gst_structure_get_name (structure));
1813 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1815 action->priv->optional = optional;
1818 if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
1819 (gst_structure_get_boolean (action->structure, "as-config",
1820 &is_config) && is_config == TRUE)) {
1822 res = action_type->execute (scenario, action);
1823 gst_validate_print_action (action, NULL);
1831 if (str_playback_time == NULL && priv != NULL) {
1832 GstValidateActionType *type = _find_action_type (action->type);
1833 gboolean can_execute_on_addition =
1834 type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
1835 && !GST_CLOCK_TIME_IS_VALID (action->playback_time);
1837 if (priv->needs_parsing)
1838 can_execute_on_addition = FALSE;
1840 if (can_execute_on_addition) {
1843 for (tmp = priv->actions; tmp; tmp = tmp->next) {
1844 GstValidateAction *act = (GstValidateAction *) tmp->data;
1845 if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1846 can_execute_on_addition = FALSE;
1853 if (can_execute_on_addition) {
1854 SCENARIO_LOCK (scenario);
1855 priv->on_addition_actions = g_list_append (priv->on_addition_actions,
1857 SCENARIO_UNLOCK (scenario);
1860 priv->actions = g_list_append (priv->actions, action);
1867 static GstValidateExecuteActionReturn
1868 _execute_sub_action_action (GstValidateAction * action)
1870 const gchar *subaction_str;
1871 GstStructure *subaction_struct = NULL;
1872 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1873 GstValidateScenario *scenario = NULL;
1875 if (action->priv->executing_last_subaction) {
1876 action->priv->executing_last_subaction = FALSE;
1881 scenario = gst_validate_action_get_scenario (action);
1882 g_assert (scenario);
1883 subaction_str = gst_structure_get_string (action->structure, "sub-action");
1884 if (subaction_str) {
1885 subaction_struct = gst_structure_from_string (subaction_str, NULL);
1887 if (subaction_struct == NULL) {
1888 GST_VALIDATE_REPORT (scenario, SCENARIO_FILE_MALFORMED,
1889 "Sub action %s could not be parsed", subaction_str);
1891 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1896 gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE,
1897 &subaction_struct, NULL);
1900 if (subaction_struct) {
1901 if (action->structure) {
1902 GST_INFO_OBJECT (scenario, "Clearing old action structure");
1903 gst_structure_free (action->structure);
1906 res = _fill_action (scenario, action, subaction_struct, FALSE);
1907 if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
1908 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1909 "Sub action %" GST_PTR_FORMAT " could not be filled",
1915 if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) {
1916 GstValidateActionType *action_type = _find_action_type (action->type);
1918 action->priv->printed = FALSE;
1919 res = gst_validate_execute_action (action_type, action);
1928 gst_object_unref (scenario);
1929 if (subaction_struct)
1930 gst_structure_free (subaction_struct);
1935 /* This is the main action execution function
1936 * it checks whether it is time to run the next action
1937 * and if it is the case executes it.
1939 * If the 'execute-on-idle' property is not TRUE,
1940 * the function will recurse while the actions are run
1944 execute_next_action (GstValidateScenario * scenario)
1948 GstClockTime position = -1;
1949 GstValidateAction *act = NULL;
1950 GstValidateActionType *type;
1952 GstValidateScenarioPrivate *priv = scenario->priv;
1954 if (priv->buffering) {
1955 GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
1957 return G_SOURCE_CONTINUE;
1960 if (priv->changing_state || priv->needs_async_done) {
1961 GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
1962 return G_SOURCE_CONTINUE;
1965 if (scenario->priv->actions)
1966 act = scenario->priv->actions->data;
1970 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS) {
1971 return G_SOURCE_CONTINUE;
1972 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK
1973 && act->repeat <= 0) {
1974 tmp = priv->actions;
1975 priv->actions = g_list_remove_link (priv->actions, tmp);
1977 GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
1978 " executing next", act->structure);
1980 gst_validate_action_unref (act);
1983 if (scenario->priv->actions) {
1984 act = scenario->priv->actions->data;
1986 _check_scenario_is_done (scenario);
1989 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
1990 if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
1991 GstClockTime etime =
1992 gst_util_get_timestamp () - act->priv->execution_time;
1994 if (etime > act->priv->timeout) {
1995 gchar *str = gst_structure_to_string (act->structure);
1997 GST_VALIDATE_REPORT (scenario,
1998 SCENARIO_ACTION_EXECUTION_ERROR,
1999 "Action %s timed out after: %" GST_TIME_FORMAT, str,
2000 GST_TIME_ARGS (etime));
2005 GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2008 return G_SOURCE_CONTINUE;
2012 if (!_check_position (scenario, act, &position, &rate))
2013 return G_SOURCE_CONTINUE;
2015 if (!_should_execute_action (scenario, act, position, rate)) {
2016 _add_execute_actions_gsource (scenario);
2018 return G_SOURCE_CONTINUE;
2021 type = _find_action_type (act->type);
2023 if (act->repeat == -1 &&
2024 !gst_structure_get_int (act->structure, "repeat", &act->repeat)) {
2025 gchar *error = NULL;
2026 const gchar *repeat_expr = gst_structure_get_string (act->structure,
2031 gst_validate_utils_parse_expression (repeat_expr,
2032 _set_variable_func, scenario, &error);
2036 GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2037 " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2038 priv->seeked_in_pause = FALSE;
2040 act->priv->state = gst_validate_execute_action (type, act);
2041 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2042 gchar *str = gst_structure_to_string (act->structure);
2044 GST_VALIDATE_REPORT (scenario,
2045 SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str);
2050 if (act->repeat > 0 && !gst_validate_action_is_subaction (act)) {
2054 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
2055 act->priv->state = _execute_sub_action_action (act);
2058 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
2059 tmp = priv->actions;
2060 priv->actions = g_list_remove_link (priv->actions, tmp);
2062 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED)
2063 gst_validate_action_unref (act);
2065 SCENARIO_LOCK (scenario);
2066 priv->interlaced_actions = g_list_append (priv->interlaced_actions, act);
2067 SCENARIO_UNLOCK (scenario);
2070 if (priv->actions == NULL)
2071 _check_scenario_is_done (scenario);
2075 /* Recurse to the next action if it is possible
2076 * to execute right away */
2077 if (!scenario->priv->execute_on_idle) {
2078 GST_DEBUG_OBJECT (scenario, "linking next action execution");
2080 return execute_next_action (scenario);
2082 _add_execute_actions_gsource (scenario);
2083 GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2086 return G_SOURCE_CONTINUE;
2089 GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2092 SCENARIO_LOCK (scenario);
2093 priv->execute_actions_source_id = 0;
2094 SCENARIO_UNLOCK (scenario);
2096 return G_SOURCE_CONTINUE;
2099 return G_SOURCE_CONTINUE;
2103 stop_waiting (GstValidateAction * action)
2105 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2107 gst_validate_printf (scenario, "Stop waiting\n");
2109 SCENARIO_LOCK (scenario);
2110 scenario->priv->wait_id = 0;
2111 SCENARIO_UNLOCK (scenario);
2113 gst_validate_action_set_done (action);
2114 _add_execute_actions_gsource (scenario);
2115 gst_object_unref (scenario);
2118 return G_SOURCE_REMOVE;
2121 static GstElement *_get_target_element (GstValidateScenario * scenario,
2122 GstValidateAction * action);
2125 stop_waiting_signal (GstBin * bin, GstElement * element,
2126 GstValidateAction * action)
2128 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2129 GstValidateScenarioPrivate *priv = scenario->priv;
2131 g_assert (scenario);
2132 gst_validate_printf (scenario, "Stop waiting for signal\n");
2134 g_signal_handler_disconnect (bin, priv->signal_handler_id);
2136 priv->signal_handler_id = 0;
2137 gst_validate_action_set_done (action);
2138 _add_execute_actions_gsource (scenario);
2139 gst_object_unref (scenario);
2142 static GstValidateExecuteActionReturn
2143 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2145 GstValidateScenarioPrivate *priv = scenario->priv;
2146 GstClockTime duration;
2148 gdouble wait_multiplier = 1;
2149 const gchar *str_wait_multiplier =
2150 g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2152 if (str_wait_multiplier) {
2154 wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2157 GST_ERROR ("Could not use the WAIT MULTIPLIER");
2159 wait_multiplier = 1;
2162 if (wait_multiplier == 0) {
2163 GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2164 return GST_VALIDATE_EXECUTE_ACTION_OK;
2168 if (!gst_validate_action_get_clocktime (scenario, action,
2169 "duration", &duration)) {
2170 GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2172 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2175 duration *= wait_multiplier;
2177 SCENARIO_LOCK (scenario);
2178 if (priv->execute_actions_source_id) {
2179 g_source_remove (priv->execute_actions_source_id);
2180 priv->execute_actions_source_id = 0;
2182 SCENARIO_UNLOCK (scenario);
2184 SCENARIO_LOCK (scenario);
2185 priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2186 (GSourceFunc) stop_waiting, action);
2187 SCENARIO_UNLOCK (scenario);
2189 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2192 static GstValidateExecuteActionReturn
2193 _execute_wait_for_signal (GstValidateScenario * scenario,
2194 GstValidateAction * action)
2196 GstValidateScenarioPrivate *priv = scenario->priv;
2197 const gchar *signal_name = gst_structure_get_string
2198 (action->structure, "signal-name");
2200 DECLARE_AND_GET_PIPELINE (scenario, action);
2202 if (signal_name == NULL) {
2203 GST_ERROR ("No signal-name given for wait action");
2204 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2207 target = _get_target_element (scenario, action);
2208 if (target == NULL) {
2209 gst_object_unref (pipeline);
2214 gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2216 if (priv->execute_actions_source_id) {
2217 g_source_remove (priv->execute_actions_source_id);
2218 priv->execute_actions_source_id = 0;
2221 priv->signal_handler_id =
2222 g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal,
2225 gst_object_unref (target);
2226 gst_object_unref (pipeline);
2228 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2232 _execute_wait_for_message (GstValidateScenario * scenario,
2233 GstValidateAction * action)
2235 GstValidateScenarioPrivate *priv = scenario->priv;
2236 const gchar *message_type = gst_structure_get_string
2237 (action->structure, "message-type");
2238 DECLARE_AND_GET_PIPELINE (scenario, action);
2240 gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2242 if (priv->execute_actions_source_id) {
2243 g_source_remove (priv->execute_actions_source_id);
2244 priv->execute_actions_source_id = 0;
2247 priv->message_type = g_strdup (message_type);
2248 gst_object_unref (pipeline);
2250 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2253 static GstValidateExecuteActionReturn
2254 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2256 if (gst_structure_has_field (action->structure, "signal-name")) {
2257 return _execute_wait_for_signal (scenario, action);
2258 } else if (gst_structure_has_field (action->structure, "message-type")) {
2259 return _execute_wait_for_message (scenario, action);
2261 return _execute_timed_wait (scenario, action);
2268 _execute_dot_pipeline (GstValidateScenario * scenario,
2269 GstValidateAction * action)
2272 gint details = GST_DEBUG_GRAPH_SHOW_ALL;
2273 const gchar *name = gst_structure_get_string (action->structure, "name");
2274 DECLARE_AND_GET_PIPELINE (scenario, action);
2276 gst_structure_get_int (action->structure, "details", &details);
2278 dotname = g_strdup_printf ("validate.action.%s", name);
2280 dotname = g_strdup ("validate.action.unnamed");
2282 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
2285 gst_object_unref (pipeline);
2291 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
2295 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2298 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2303 name = gst_structure_get_string (action->structure, "target-element-name");
2305 gst_object_unref (pipeline);
2310 if (strcmp (GST_OBJECT_NAME (pipeline), name) == 0) {
2311 target = gst_object_ref (pipeline);
2313 target = gst_bin_get_by_name (GST_BIN (pipeline), name);
2317 GST_ERROR ("Target element with given name (%s) not found", name);
2318 gst_object_unref (pipeline);
2324 * _get_target_elements_by_klass:
2325 * @scenario: a #GstValidateScenario
2326 * @action: a #GstValidateAction
2328 * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
2329 * matches the 'target-element-klass' of @action and the factory name matches
2330 * the 'target-element-factory-name'.
2332 * Returns: (transfer full) (element-type GstElement): a list of #GstElement
2335 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
2336 GstValidateAction * action)
2338 GList *result = NULL;
2340 const gchar *klass, *fname;
2341 GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
2342 gboolean done = FALSE;
2343 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2346 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2351 klass = gst_structure_get_string (action->structure, "target-element-klass");
2353 gst_structure_get_string (action->structure,
2354 "target-element-factory-name");
2355 if (!klass && !fname) {
2356 gst_object_unref (pipeline);
2361 if (klass && gst_validate_element_has_klass (pipeline, klass))
2362 result = g_list_prepend (result, gst_object_ref (pipeline));
2364 if (fname && gst_element_get_factory (pipeline)
2365 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
2367 result = g_list_prepend (result, gst_object_ref (pipeline));
2369 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
2371 g_value_init (¶m, G_TYPE_STRING);
2372 g_value_set_string (¶m, klass);
2375 switch (gst_iterator_next (it, &v)) {
2376 case GST_ITERATOR_OK:{
2377 GstElement *child = g_value_get_object (&v);
2379 if (g_list_find (result, child))
2382 if (klass && gst_validate_element_has_klass (child, klass)) {
2383 result = g_list_prepend (result, gst_object_ref (child));
2387 if (fname && gst_element_get_factory (child)
2388 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
2390 result = g_list_prepend (result, gst_object_ref (child));
2395 case GST_ITERATOR_RESYNC:
2396 gst_iterator_resync (it);
2398 case GST_ITERATOR_ERROR:
2399 case GST_ITERATOR_DONE:
2405 g_value_reset (¶m);
2406 gst_iterator_free (it);
2407 gst_object_unref (pipeline);
2413 _execute_set_property (GstValidateScenario * scenario,
2414 GstValidateAction * action)
2417 GList *targets = NULL, *l;
2418 const gchar *property;
2419 const GValue *property_value;
2420 gboolean ret = TRUE;
2422 /* set-property can be applied on either:
2423 * - a single element having target-element-name as name
2424 * - all the elements having target-element-klass as klass
2426 if (gst_structure_get_string (action->structure, "target-element-name")) {
2427 target = _get_target_element (scenario, action);
2431 targets = g_list_append (targets, target);
2432 } else if (gst_structure_get_string (action->structure,
2433 "target-element-klass") ||
2434 gst_structure_get_string (action->structure,
2435 "target-element-factory-name")) {
2436 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
2438 g_assert_not_reached ();
2441 property = gst_structure_get_string (action->structure, "property-name");
2442 property_value = gst_structure_get_value (action->structure,
2445 for (l = targets; l != NULL; l = g_list_next (l)) {
2446 GstValidateActionReturn tmpres =
2447 gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
2448 G_OBJECT (l->data), property,
2449 property_value, action->priv->optional);
2455 g_list_free_full (targets, gst_object_unref);
2460 _execute_set_debug_threshold (GstValidateScenario * scenario,
2461 GstValidateAction * action)
2464 gboolean reset = TRUE;
2465 const gchar *threshold_str;
2468 gst_structure_get_string (action->structure, "debug-threshold");
2469 if (threshold_str == NULL) {
2472 if (gst_structure_get_int (action->structure, "debug-threshold",
2474 threshold_str = str = g_strdup_printf ("%i", threshold);
2479 gst_structure_get_boolean (action->structure, "reset", &reset);
2481 gst_debug_set_threshold_from_string (threshold_str, reset);
2489 _execute_emit_signal (GstValidateScenario * scenario,
2490 GstValidateAction * action)
2493 const gchar *signal_name;
2495 target = _get_target_element (scenario, action);
2496 if (target == NULL) {
2500 signal_name = gst_structure_get_string (action->structure, "signal-name");
2502 /* Right now we don't support arguments to signals as there weren't any use
2503 * cases to cover yet but it should be possible to do so */
2504 g_signal_emit_by_name (target, signal_name, NULL);
2506 gst_object_unref (target);
2510 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
2511 GstBuffer * buffer, gpointer * user_data, gboolean * remove_wrapper);
2513 typedef struct _ChainWrapperFunctionData
2515 GstPadChainFunction wrapped_chain_func;
2516 gpointer wrapped_chain_data;
2517 GDestroyNotify wrapped_chain_notify;
2518 ChainWrapperFunction wrapper_function;
2519 gpointer wrapper_function_user_data;
2520 } ChainWrapperFunctionData;
2522 static GstFlowReturn
2523 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
2525 ChainWrapperFunctionData *data = pad->chaindata;
2527 gboolean remove_wrapper = FALSE;
2529 pad->chainfunc = data->wrapped_chain_func;
2530 pad->chaindata = data->wrapped_chain_data;
2531 pad->chainnotify = data->wrapped_chain_notify;
2533 ret = data->wrapper_function (pad, parent, buffer,
2534 data->wrapper_function_user_data, &remove_wrapper);
2536 if (!remove_wrapper) {
2537 /* The chain function may have changed during the calling (e.g. if it was
2538 * a nested wrapper that decided to remove itself) so we need to update the
2539 * wrapped function just in case. */
2540 data->wrapped_chain_func = pad->chainfunc;
2541 data->wrapped_chain_data = pad->chaindata;
2542 data->wrapped_chain_notify = pad->chainnotify;
2544 /* Restore the wrapper as chain function */
2545 pad->chainfunc = _pad_chain_wrapper;
2546 pad->chaindata = data;
2547 pad->chainnotify = g_free;
2555 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
2558 ChainWrapperFunctionData *data = g_new (ChainWrapperFunctionData, 1);
2559 data->wrapped_chain_func = pad->chainfunc;
2560 data->wrapped_chain_data = pad->chaindata;
2561 data->wrapped_chain_notify = pad->chainnotify;
2562 data->wrapper_function = new_function;
2563 data->wrapper_function_user_data = user_data;
2565 pad->chainfunc = _pad_chain_wrapper;
2566 pad->chaindata = data;
2567 pad->chainnotify = g_free;
2570 static GstFlowReturn
2571 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2572 gpointer * user_data, gboolean * remove_wrapper)
2574 GstValidateAction *action = (GstValidateAction *) user_data;
2575 GstFlowReturn ret = pad->chainfunc (pad, parent, buffer);
2576 gst_validate_action_set_done (action);
2577 *remove_wrapper = TRUE;
2582 structure_get_uint64_permissive (const GstStructure * structure,
2583 const gchar * fieldname, guint64 * dest)
2585 const GValue *original;
2586 GValue transformed = G_VALUE_INIT;
2588 original = gst_structure_get_value (structure, fieldname);
2592 g_value_init (&transformed, G_TYPE_UINT64);
2593 if (!g_value_transform (original, &transformed))
2596 *dest = g_value_get_uint64 (&transformed);
2597 g_value_unset (&transformed);
2602 _execute_appsrc_push (GstValidateScenario * scenario,
2603 GstValidateAction * action)
2607 gchar *file_contents;
2609 GError *error = NULL;
2613 gint push_buffer_ret;
2615 target = _get_target_element (scenario, action);
2616 if (target == NULL) {
2617 gchar *structure_string = gst_structure_to_string (action->structure);
2618 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2619 "No element found for action: %s", structure_string);
2620 g_free (structure_string);
2621 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2625 g_strdup (gst_structure_get_string (action->structure, "file-name"));
2626 if (file_name == NULL) {
2627 gchar *structure_string = gst_structure_to_string (action->structure);
2628 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2629 "Missing file-name property: %s", structure_string);
2630 g_free (structure_string);
2631 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2634 structure_get_uint64_permissive (action->structure, "offset", &offset);
2635 structure_get_uint64_permissive (action->structure, "size", &size);
2637 g_file_get_contents (file_name, &file_contents, &file_length, &error);
2638 if (error != NULL) {
2639 gchar *structure_string = gst_structure_to_string (action->structure);
2640 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2641 "Could not open file for action: %s. Error: %s", structure_string,
2643 g_free (structure_string);
2644 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2646 buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, file_contents,
2647 file_length, offset, (size == -1 ? file_length : size), NULL, g_free);
2650 const GValue *caps_value;
2651 caps_value = gst_structure_get_value (action->structure, "caps");
2653 g_object_set (target, "caps", gst_value_get_caps (caps_value), NULL);
2656 /* We temporarily override the peer pad chain function to finish the action
2657 * once the buffer chain actually ends. */
2659 GstPad *appsrc_pad = gst_element_get_static_pad (target, "src");
2660 GstPad *peer_pad = gst_pad_get_peer (appsrc_pad);
2662 gchar *structure_string = gst_structure_to_string (action->structure);
2663 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2664 "Action failed, pad not linked: %s", structure_string);
2665 g_free (structure_string);
2666 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2669 wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
2671 gst_object_unref (appsrc_pad);
2672 gst_object_unref (peer_pad);
2675 g_signal_emit_by_name (target, "push-buffer", buffer, &push_buffer_ret);
2676 if (push_buffer_ret != GST_FLOW_OK) {
2677 gchar *structure_string = gst_structure_to_string (action->structure);
2678 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2679 "push-buffer signal failed in action: %s", structure_string);
2680 g_free (structure_string);
2681 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2685 gst_object_unref (target);
2686 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2690 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
2695 target = _get_target_element (scenario, action);
2696 if (target == NULL) {
2697 gchar *structure_string = gst_structure_to_string (action->structure);
2698 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2699 "No element found for action: %s", structure_string);
2700 g_free (structure_string);
2701 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2704 g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
2705 if (eos_ret != GST_FLOW_OK) {
2706 gchar *structure_string = gst_structure_to_string (action->structure);
2707 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2708 "Failed to emit end-of-stream signal for action: %s", structure_string);
2709 g_free (structure_string);
2710 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2713 gst_object_unref (target);
2714 return GST_VALIDATE_EXECUTE_ACTION_OK;
2718 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
2722 gboolean reset_time = TRUE;
2724 target = _get_target_element (scenario, action);
2725 if (target == NULL) {
2726 gchar *structure_string = gst_structure_to_string (action->structure);
2727 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2728 "No element found for action: %s", structure_string);
2729 g_free (structure_string);
2730 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2733 gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
2735 event = gst_event_new_flush_start ();
2736 if (!gst_element_send_event (target, event)) {
2737 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2738 "FLUSH_START event was not handled");
2739 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2742 event = gst_event_new_flush_stop (reset_time);
2743 if (!gst_element_send_event (target, event)) {
2744 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2745 "FLUSH_STOP event was not handled");
2746 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2749 return GST_VALIDATE_EXECUTE_ACTION_OK;
2752 static GstValidateExecuteActionReturn
2753 _execute_disable_plugin (GstValidateScenario * scenario,
2754 GstValidateAction * action)
2757 const gchar *plugin_name;
2759 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
2761 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
2763 if (plugin == NULL) {
2764 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2765 "Could not find plugin to disable: %s", plugin_name);
2767 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2770 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
2771 gst_registry_remove_plugin (gst_registry_get (), plugin);
2773 return GST_VALIDATE_EXECUTE_ACTION_OK;
2777 gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario,
2780 GstValidateScenarioPrivate *priv = scenario->priv;
2782 GstSeekType start_type, stop_type;
2784 gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start,
2787 if (start_type == GST_SEEK_TYPE_SET) {
2788 priv->segment_start = start;
2789 } else if (start_type == GST_SEEK_TYPE_END) {
2793 if (stop_type == GST_SEEK_TYPE_SET) {
2794 priv->segment_stop = stop;
2795 } else if (stop_type == GST_SEEK_TYPE_END) {
2801 _compare_actions (GstValidateAction * a, GstValidateAction * b)
2803 if (a->action_number < b->action_number)
2805 else if (a->action_number == b->action_number)
2812 _structure_set_variables (GQuark field_id, GValue * value,
2813 GstValidateAction * action)
2815 GstValidateScenario *scenario;
2816 const gchar *var_value;
2818 if (!G_VALUE_HOLDS_STRING (value))
2821 scenario = gst_validate_action_get_scenario (action);
2826 gst_structure_get_string (scenario->priv->vars,
2827 g_value_get_string (value));
2829 GST_INFO_OBJECT (action, "Setting variable %s to %s",
2830 g_value_get_string (value), var_value);
2831 g_value_set_string (value, var_value);
2834 g_clear_object (&scenario);
2840 gst_validate_action_default_prepare_func (GstValidateAction * action)
2844 const gchar *vars[] = { "duration", "start", "stop" };
2845 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2847 for (i = 0; i < G_N_ELEMENTS (vars); i++) {
2848 gint res = gst_validate_action_get_clocktime (scenario, action, vars[i],
2851 GST_ERROR_OBJECT (scenario, "Could not get clocktime for"
2852 " variable %s", vars[i]);
2854 gst_object_unref (scenario);
2856 } else if (res == -1) {
2860 gst_structure_set (action->structure, vars[i], GST_TYPE_CLOCK_TIME,
2863 gst_object_unref (scenario);
2864 gst_structure_filter_and_map_in_place (action->structure,
2865 (GstStructureFilterMapFunc) _structure_set_variables, action);
2871 _check_waiting_for_message (GstValidateScenario * scenario,
2872 GstMessage * message)
2874 GstValidateScenarioPrivate *priv = scenario->priv;
2876 if (!g_strcmp0 (priv->message_type,
2877 gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
2878 GstValidateAction *action = scenario->priv->actions->data;
2880 g_free ((gpointer) priv->message_type);
2881 priv->message_type = NULL;
2883 gst_validate_printf (scenario, "Stop waiting for message\n");
2885 gst_validate_action_set_done (action);
2886 _add_execute_actions_gsource (scenario);
2891 streams_list_contain (GList * streams, const gchar * stream_id)
2895 for (l = streams; l; l = g_list_next (l)) {
2896 GstStream *s = l->data;
2898 if (!g_strcmp0 (s->stream_id, stream_id))
2906 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
2907 GstElement * pipeline)
2909 GstValidateScenarioPrivate *priv = scenario->priv;
2911 GstClockTime min_latency;
2913 query = gst_query_new_latency ();
2914 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
2915 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2916 "Failed to perfom LATENCY query");
2917 gst_query_unref (query);
2921 gst_query_parse_latency (query, NULL, &min_latency, NULL);
2922 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
2923 " max allowed: %" GST_TIME_FORMAT,
2924 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
2926 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
2927 min_latency > priv->max_latency) {
2928 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_LATENCY_TOO_HIGH,
2929 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
2930 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
2931 GST_TIME_ARGS (priv->max_latency));
2936 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
2938 gboolean is_error = FALSE;
2939 GstValidateScenarioPrivate *priv = scenario->priv;
2940 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2943 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2948 switch (GST_MESSAGE_TYPE (message)) {
2949 case GST_MESSAGE_ASYNC_DONE:
2950 if (priv->last_seek) {
2951 gst_validate_scenario_update_segment_from_seek (scenario,
2954 if (priv->target_state == GST_STATE_PAUSED)
2955 priv->seeked_in_pause = TRUE;
2957 gst_event_replace (&priv->last_seek, NULL);
2958 gst_validate_action_set_done (priv->actions->data);
2959 } else if (scenario->priv->needs_async_done) {
2960 scenario->priv->needs_async_done = FALSE;
2961 if (priv->actions && _action_sets_state (priv->actions->data)
2962 && !priv->changing_state)
2963 gst_validate_action_set_done (priv->actions->data);
2967 if (priv->needs_parsing) {
2970 for (tmp = priv->needs_parsing; tmp; tmp = tmp->next) {
2971 GstValidateAction *action = (GstValidateAction *) tmp->data;
2973 if (!_set_action_playback_time (scenario, action))
2976 priv->actions = g_list_insert_sorted (priv->actions, action,
2977 (GCompareFunc) _compare_actions);
2980 g_list_free (priv->needs_parsing);
2981 priv->needs_parsing = NULL;
2983 _add_execute_actions_gsource (scenario);
2985 case GST_MESSAGE_STATE_CHANGED:
2987 if (pipeline && GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
2988 GstState nstate, pstate;
2990 gst_message_parse_state_changed (message, &pstate, &nstate, NULL);
2992 if (scenario->priv->changing_state &&
2993 scenario->priv->target_state == nstate) {
2994 scenario->priv->changing_state = FALSE;
2996 if (priv->actions && _action_sets_state (priv->actions->data) &&
2997 !priv->needs_async_done)
2998 gst_validate_action_set_done (priv->actions->data);
3001 if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED)
3002 _add_execute_actions_gsource (scenario);
3004 /* GstBin only send a new latency message when reaching PLAYING if
3005 * async-handling=true so check the latency manually. */
3006 if (nstate == GST_STATE_PLAYING)
3007 gst_validate_scenario_check_latency (scenario, pipeline);
3011 case GST_MESSAGE_ERROR:
3015 case GST_MESSAGE_EOS:
3017 GstValidateAction *stop_action;
3018 GstValidateActionType *stop_action_type;
3022 priv->got_eos = TRUE;
3023 if (priv->message_type) {
3025 if (priv->actions->next) {
3026 GST_DEBUG_OBJECT (scenario,
3027 "Waiting for a message and got a next action"
3028 " to execute, letting it a chance!");
3031 /* Clear current message wait if waiting for EOS */
3032 _check_waiting_for_message (scenario, message);
3037 SCENARIO_LOCK (scenario);
3038 if (scenario->priv->actions || scenario->priv->interlaced_actions ||
3039 scenario->priv->on_addition_actions) {
3040 guint nb_actions = 0;
3041 gchar *actions = g_strdup (""), *tmpconcat;
3043 GList *all_actions =
3044 g_list_concat (g_list_concat (scenario->priv->actions,
3045 scenario->priv->interlaced_actions),
3046 scenario->priv->on_addition_actions);
3048 for (tmp = all_actions; tmp; tmp = tmp->next) {
3049 gchar *action_string;
3050 GstValidateAction *action = (GstValidateAction *) tmp->data;
3051 GstValidateActionType *type = _find_action_type (action->type);
3053 tmpconcat = actions;
3055 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
3056 action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
3057 action->priv->optional) {
3058 gst_validate_action_unref (action);
3065 action_string = gst_structure_to_string (action->structure);
3067 g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string);
3068 gst_validate_action_unref (action);
3070 g_free (action_string);
3072 g_list_free (all_actions);
3073 scenario->priv->actions = NULL;
3074 scenario->priv->interlaced_actions = NULL;
3075 scenario->priv->on_addition_actions = NULL;
3078 if (nb_actions > 0) {
3079 GstClockTime position = GST_CLOCK_TIME_NONE;
3081 _get_position (scenario, NULL, &position);
3082 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
3083 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
3084 ")", nb_actions, actions, GST_TIME_ARGS (position));
3088 SCENARIO_UNLOCK (scenario);
3090 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
3092 stop_action_type = _find_action_type ("stop");
3093 stop_action = gst_validate_action_new (scenario, stop_action_type);
3094 s = gst_structure_from_string ("stop, generated-after-eos=true;", NULL);
3095 _fill_action (scenario, stop_action, s, FALSE);
3096 gst_structure_free (s);
3097 gst_validate_execute_action (stop_action_type, stop_action);
3098 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
3102 case GST_MESSAGE_BUFFERING:
3106 gst_message_parse_buffering (message, &percent);
3109 priv->buffering = FALSE;
3111 priv->buffering = TRUE;
3114 case GST_MESSAGE_STREAMS_SELECTED:
3117 GList *streams_selected = NULL;
3119 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
3121 gst_message_streams_selected_get_stream (message, i);
3123 streams_selected = g_list_append (streams_selected, stream);
3126 /* Is there a pending switch-track action waiting for the new streams to
3128 if (priv->pending_switch_track) {
3129 GList *expected, *l;
3130 GstValidateScenario *scenario =
3131 gst_validate_action_get_scenario (priv->pending_switch_track);
3134 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
3135 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
3137 if (g_list_length (expected) != g_list_length (streams_selected)) {
3138 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3139 "Was expecting %d selected streams but got %d",
3140 g_list_length (expected), g_list_length (streams_selected));
3144 for (l = expected; l; l = g_list_next (l)) {
3145 const gchar *stream_id = l->data;
3147 if (!streams_list_contain (streams_selected, stream_id)) {
3148 GST_VALIDATE_REPORT (scenario,
3149 SCENARIO_ACTION_EXECUTION_ERROR,
3150 "Stream %s has not be activated", stream_id);
3156 gst_object_unref (scenario);
3157 gst_validate_action_set_done (priv->pending_switch_track);
3158 priv->pending_switch_track = NULL;
3161 g_list_free_full (streams_selected, gst_object_unref);
3164 case GST_MESSAGE_LATENCY:
3165 gst_validate_scenario_check_latency (scenario, pipeline);
3168 case GST_MESSAGE_QOS:
3172 /* Check the maximum allowed when scenario is terminating so the report
3173 * will include the actual number of dropped buffers. */
3174 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
3176 g_atomic_int_set (&priv->dropped, dropped);
3185 gst_object_unref (pipeline);
3186 /* Check if we got the message expected by a wait action */
3187 if (priv->message_type)
3188 _check_waiting_for_message (scenario, message);
3194 _action_type_has_parameter (GstValidateActionType * atype,
3195 const gchar * paramname)
3199 if (!atype->parameters)
3202 for (i = 0; atype->parameters[i].name; i++)
3203 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
3210 _load_scenario_file (GstValidateScenario * scenario,
3211 const gchar * scenario_file, gboolean * is_config)
3213 gboolean ret = TRUE;
3214 GList *structures, *tmp;
3215 GstValidateScenarioPrivate *priv = scenario->priv;
3220 structures = gst_validate_utils_structs_parse_from_filename (scenario_file);
3221 if (structures == NULL)
3224 for (tmp = structures; tmp; tmp = tmp->next) {
3225 GstValidateAction *action;
3226 GstValidateActionType *action_type;
3228 GstStructure *structure = (GstStructure *) tmp->data;
3230 type = gst_structure_get_name (structure);
3231 if (!g_strcmp0 (type, "description")) {
3232 const gchar *pipeline_name;
3234 gst_structure_get_boolean (structure, "is-config", is_config);
3235 gst_structure_get_boolean (structure, "handles-states",
3236 &priv->handles_state);
3238 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
3239 if (pipeline_name) {
3240 g_free (priv->pipeline_name);
3241 priv->pipeline_name = g_strdup (pipeline_name);
3244 gst_validate_utils_get_clocktime (structure, "max-latency",
3245 &priv->max_latency);
3247 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
3250 } else if (!g_strcmp0 (type, "include")) {
3251 const gchar *location = gst_structure_get_string (structure, "location");
3254 GST_ERROR_OBJECT (scenario,
3255 "Mandatory field 'location' not present in structure: %"
3256 GST_PTR_FORMAT, structure);
3260 if (!gst_validate_scenario_load (scenario, location, scenario_file)) {
3261 GST_ERROR ("Failed including scenario %s", location);
3266 } else if (!(action_type = _find_action_type (type))) {
3267 if (gst_structure_has_field (structure, "optional-action-type")) {
3268 GST_INFO_OBJECT (scenario,
3269 "Action type not found %s but marked as not mandatory", type);
3273 GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type);
3277 if (action_type->parameters) {
3280 for (i = 0; action_type->parameters[i].name; i++) {
3281 if (action_type->parameters[i].mandatory &&
3282 gst_structure_has_field (structure,
3283 action_type->parameters[i].name) == FALSE) {
3284 GST_ERROR_OBJECT (scenario,
3285 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
3286 action_type->parameters[i].name, structure);
3292 action = gst_validate_action_new (scenario, action_type);
3293 if (_fill_action (scenario, action,
3294 structure, TRUE) == GST_VALIDATE_EXECUTE_ACTION_ERROR)
3297 action->action_number = priv->num_actions++;
3300 /* max-latency and max-dropped can be overriden using config */
3301 for (config = gst_validate_plugin_get_config (NULL); config;
3302 config = g_list_next (config)) {
3303 gst_validate_utils_get_clocktime (config->data, "max-latency",
3304 &priv->max_latency);
3306 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
3310 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3321 gst_validate_scenario_load (GstValidateScenario * scenario,
3322 const gchar * scenario_name, const gchar * relative_scenario)
3324 gchar **scenarios = NULL;
3326 gboolean found_actions = FALSE, is_config, ret = TRUE;
3327 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
3329 gchar **env_scenariodir;
3331 if (relative_scenario) {
3332 gchar *relative_dir = g_path_get_dirname (relative_scenario);
3333 gchar *tmp_scenarios_path =
3334 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
3337 g_free (scenarios_path);
3338 scenarios_path = tmp_scenarios_path;
3342 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
3344 g_free (scenarios_path);
3349 scenarios = g_strsplit (scenario_name, ":", -1);
3351 for (i = 0; scenarios[i]; i++) {
3352 gchar *lfilename = NULL, *tldir = NULL;
3354 /* First check if the scenario name is not a full path to the
3355 * actual scenario */
3356 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
3357 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
3358 "Trying to load it", scenarios[i]);
3359 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config)))
3360 goto check_scenario;
3363 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
3364 lfilename = g_strdup (scenarios[i]);
3367 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
3369 if (env_scenariodir) {
3372 for (i = 0; env_scenariodir[i]; i++) {
3373 tldir = g_build_filename (env_scenariodir[i], lfilename, NULL);
3374 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3375 goto check_scenario;
3380 tldir = g_build_filename ("data", "scenarios", lfilename, NULL);
3382 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3383 goto check_scenario;
3387 /* Try from local profiles */
3389 g_build_filename (g_get_user_data_dir (),
3390 "gstreamer-" GST_API_VERSION, "validate",
3391 GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3393 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3395 /* Try from system-wide profiles */
3396 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3397 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3399 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3403 /* else check scenario */
3409 if (found_actions == TRUE)
3410 goto one_actions_scenario_max;
3412 found_actions = TRUE;
3418 if (env_scenariodir)
3419 g_strfreev (env_scenariodir);
3421 g_strfreev (scenarios);
3424 g_error ("Could not set scenario %s => EXIT\n", scenario_name);
3430 GST_ERROR ("Invalid name for scenario '%s'", scenario_name);
3435 one_actions_scenario_max:
3437 GST_ERROR ("You can set at most only one action scenario. "
3438 "You can have several config scenarios though (a config scenario's "
3439 "file must have is-config=true, and all its actions must be executable "
3440 "at parsing time).");
3449 gst_validate_scenario_set_property (GObject * object, guint prop_id,
3450 const GValue * value, GParamSpec * pspec)
3452 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3456 /* we assume the runner is valid as long as this scenario is,
3458 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
3459 g_value_get_object (value));
3461 case PROP_HANDLES_STATE:
3462 g_assert_not_reached ();
3464 case PROP_EXECUTE_ON_IDLE:
3465 self->priv->execute_on_idle = g_value_get_boolean (value);
3473 gst_validate_scenario_get_property (GObject * object, guint prop_id,
3474 GValue * value, GParamSpec * pspec)
3476 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3480 /* we assume the runner is valid as long as this scenario is,
3482 g_value_take_object (value,
3483 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
3485 case PROP_HANDLES_STATE:
3486 g_value_set_boolean (value, self->priv->handles_state);
3488 case PROP_EXECUTE_ON_IDLE:
3489 g_value_set_boolean (value, self->priv->execute_on_idle);
3497 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
3499 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3501 object_class->dispose = gst_validate_scenario_dispose;
3502 object_class->finalize = gst_validate_scenario_finalize;
3504 object_class->get_property = gst_validate_scenario_get_property;
3505 object_class->set_property = gst_validate_scenario_set_property;
3507 g_object_class_install_property (object_class, PROP_RUNNER,
3508 g_param_spec_object ("validate-runner", "VALIDATE Runner",
3509 "The Validate runner to report errors to",
3510 GST_TYPE_VALIDATE_RUNNER,
3511 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
3513 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
3514 g_param_spec_boolean ("handles-states", "Handles state",
3515 "True if the application should not handle the first state change. "
3516 "False if it is application responsibility",
3517 FALSE, G_PARAM_READABLE));
3519 g_object_class_install_property (object_class,
3520 PROP_EXECUTE_ON_IDLE,
3521 g_param_spec_boolean ("execute-on-idle",
3522 "Force waiting between actions",
3523 "Always execute actions on idle and do not chain them to execute as"
3524 " fast as possible. Setting this property is useful if action"
3525 " execution can lead to the addition of new sources on the same main"
3526 " loop as it provides these new GSource a chance to be dispatched"
3527 " between actions", FALSE, G_PARAM_READWRITE));
3530 * GstValidateScenario::done:
3531 * @scenario: The scenario runing
3533 * Emitted once all actions have been executed
3535 scenario_signals[DONE] =
3536 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
3537 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
3541 gst_validate_scenario_init (GstValidateScenario * scenario)
3543 GstValidateScenarioPrivate *priv = scenario->priv =
3544 gst_validate_scenario_get_instance_private (scenario);
3546 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
3547 priv->segment_start = 0;
3548 priv->segment_stop = GST_CLOCK_TIME_NONE;
3549 priv->action_execution_interval = 10;
3550 priv->vars = gst_structure_new_empty ("vars");
3551 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
3552 priv->max_latency = GST_CLOCK_TIME_NONE;
3553 priv->max_dropped = -1;
3555 g_mutex_init (&priv->lock);
3559 gst_validate_scenario_dispose (GObject * object)
3561 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3563 if (priv->last_seek)
3564 gst_event_unref (priv->last_seek);
3565 g_weak_ref_clear (&priv->ref_pipeline);
3568 gst_bus_remove_signal_watch (priv->bus);
3569 gst_object_unref (priv->bus);
3573 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
3577 gst_validate_scenario_finalize (GObject * object)
3579 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3581 /* Because g_object_add_weak_pointer() is used, this MUST be on the
3583 g_assert (g_main_context_acquire (g_main_context_default ()));
3584 g_main_context_release (g_main_context_default ());
3586 g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref);
3587 g_list_free_full (priv->interlaced_actions,
3588 (GDestroyNotify) gst_mini_object_unref);
3589 g_list_free_full (priv->on_addition_actions,
3590 (GDestroyNotify) gst_mini_object_unref);
3591 g_list_free_full (priv->needs_parsing,
3592 (GDestroyNotify) gst_mini_object_unref);
3593 g_free (priv->pipeline_name);
3594 gst_structure_free (priv->vars);
3595 g_mutex_clear (&priv->lock);
3597 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
3600 static void _element_added_cb (GstBin * bin, GstElement * element,
3601 GstValidateScenario * scenario);
3604 iterate_children (GstValidateScenario * scenario, GstBin * bin)
3607 GValue v = G_VALUE_INIT;
3608 gboolean done = FALSE;
3609 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
3611 called = g_hash_table_new (NULL, NULL);
3612 it = gst_bin_iterate_elements (bin);
3615 switch (gst_iterator_next (it, &v)) {
3616 case GST_ITERATOR_OK:{
3617 GstElement *child = g_value_get_object (&v);
3619 if (g_hash_table_lookup (called, child) == NULL) {
3620 _element_added_cb (bin, child, scenario);
3621 g_hash_table_add (called, child);
3626 case GST_ITERATOR_RESYNC:
3627 gst_iterator_resync (it);
3629 case GST_ITERATOR_ERROR:
3630 case GST_ITERATOR_DONE:
3635 gst_iterator_free (it);
3636 g_hash_table_unref (called);
3640 should_execute_action (GstElement * element, GstValidateAction * action)
3644 tmp = gst_structure_get_string (action->structure, "target-element-name");
3645 if (tmp != NULL && !strcmp (tmp, GST_ELEMENT_NAME (element)))
3648 tmp = gst_structure_get_string (action->structure, "target-element-klass");
3649 if (tmp != NULL && gst_validate_element_has_klass (element, tmp))
3653 gst_structure_get_string (action->structure,
3654 "target-element-factory-name");
3655 if (tmp != NULL && gst_element_get_factory (element)
3656 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)), tmp))
3663 _element_added_cb (GstBin * bin, GstElement * element,
3664 GstValidateScenario * scenario)
3668 GstValidateScenarioPrivate *priv = scenario->priv;
3670 /* Check if it's an element we track for a set-property action */
3671 SCENARIO_LOCK (scenario);
3672 tmp = priv->on_addition_actions;
3674 GstValidateAction *action = (GstValidateAction *) tmp->data;
3676 if (action->playback_time != GST_CLOCK_TIME_NONE)
3678 if (g_strcmp0 (action->type, "set-property"))
3681 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
3682 action, action->type);
3683 if (should_execute_action (element, action)) {
3684 GstValidateActionType *action_type;
3685 action_type = _find_action_type (action->type);
3686 GST_DEBUG_OBJECT (element, "Executing set-property action");
3687 if (gst_validate_execute_action (action_type, action)) {
3688 priv->on_addition_actions =
3689 g_list_remove_link (priv->on_addition_actions, tmp);
3690 gst_mini_object_unref (GST_MINI_OBJECT (action));
3692 tmp = priv->on_addition_actions;
3698 SCENARIO_UNLOCK (scenario);
3700 _check_scenario_is_done (scenario);
3702 /* If it's a bin, listen to the child */
3703 if (GST_IS_BIN (element)) {
3704 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
3706 iterate_children (scenario, GST_BIN (element));
3711 * gst_validate_scenario_factory_create:
3712 * @runner: The #GstValidateRunner to use to report issues
3713 * @pipeline: The pipeline to run the scenario on
3714 * @scenario_name: The name (or path) of the scenario to run
3716 * Returns: (transfer full): A #GstValidateScenario or NULL
3718 GstValidateScenario *
3719 gst_validate_scenario_factory_create (GstValidateRunner *
3720 runner, GstElement * pipeline, const gchar * scenario_name)
3723 GstValidateScenario *scenario =
3724 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
3727 GST_LOG ("Creating scenario %s", scenario_name);
3728 if (!gst_validate_scenario_load (scenario, scenario_name, NULL)) {
3729 g_object_unref (scenario);
3734 if (scenario->priv->pipeline_name &&
3735 !g_pattern_match_simple (scenario->priv->pipeline_name,
3736 GST_OBJECT_NAME (pipeline))) {
3737 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
3738 scenario_name, scenario->priv->pipeline_name,
3739 GST_OBJECT_NAME (pipeline));
3741 gst_object_unref (scenario);
3746 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
3747 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
3748 g_strdup (scenario_name));
3750 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
3753 iterate_children (scenario, GST_BIN (pipeline));
3755 scenario->priv->bus = gst_element_get_bus (pipeline);
3756 gst_bus_add_signal_watch (scenario->priv->bus);
3757 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
3760 for (config = gst_validate_plugin_get_config (NULL); config;
3761 config = config->next) {
3764 if (gst_structure_get_uint (config->data,
3765 "scenario-action-execution-interval",
3766 &scenario->priv->action_execution_interval)) {
3767 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3768 scenario->priv->action_execution_interval);
3770 } else if (gst_structure_get_int (config->data,
3771 "scenario-action-execution-interval", &interval)) {
3773 scenario->priv->action_execution_interval = (guint) interval;
3774 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3775 scenario->priv->action_execution_interval);
3779 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
3784 if (scenario->priv->handles_state) {
3785 GST_INFO_OBJECT (scenario, "Scenario handles state."
3786 " Starting the get position source");
3787 _add_execute_actions_gsource (scenario);
3790 gst_validate_printf (NULL,
3791 "\n=========================================\n"
3792 "Running scenario %s on pipeline %s"
3793 "\n=========================================\n", scenario_name,
3794 GST_OBJECT_NAME (pipeline));
3796 scenario->priv->overrides =
3797 gst_validate_override_registry_get_override_for_names
3798 (gst_validate_override_registry_get (), "scenarios", NULL);
3804 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
3806 gchar *tmp = gst_value_serialize (value);
3808 g_key_file_set_string (kfg->kf, kfg->group_name,
3809 g_quark_to_string (field_id), g_strcompress (tmp));
3818 _parse_scenario (GFile * f, GKeyFile * kf)
3820 gboolean ret = FALSE;
3821 gchar *fname = g_file_get_basename (f);
3823 if (g_str_has_suffix (fname, GST_VALIDATE_SCENARIO_SUFFIX)) {
3824 gboolean needs_clock_sync = FALSE;
3825 GstStructure *desc = NULL;
3827 gchar **name = g_strsplit (fname, GST_VALIDATE_SCENARIO_SUFFIX, 0);
3828 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f);
3830 for (tmp = structures; tmp; tmp = tmp->next) {
3831 GstStructure *_struct = (GstStructure *) tmp->data;
3832 GstValidateActionType *type =
3833 _find_action_type (gst_structure_get_name (_struct));
3835 if (!desc && gst_structure_has_name (_struct, "description"))
3836 desc = gst_structure_copy (_struct);
3837 else if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
3838 needs_clock_sync = TRUE;
3841 if (needs_clock_sync) {
3843 gst_structure_set (desc, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
3845 desc = gst_structure_from_string ("description, need-clock-sync=true;",
3850 KeyFileGroupName kfg;
3852 kfg.group_name = name[0];
3855 gst_structure_foreach (desc,
3856 (GstStructureForeachFunc) _add_description, &kfg);
3857 gst_structure_free (desc);
3859 g_key_file_set_string (kf, name[0], "noinfo", "nothing");
3861 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3872 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
3874 GFileEnumerator *fenum;
3877 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
3878 G_FILE_QUERY_INFO_NONE, NULL, NULL);
3883 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
3884 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
3885 GFile *f = g_file_enumerator_get_child (fenum, info);
3887 _parse_scenario (f, kf);
3888 gst_object_unref (f);
3891 gst_object_unref (fenum);
3895 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
3896 gchar * output_file)
3902 GKeyFile *kf = NULL;
3904 const gchar *envvar;
3905 gchar **env_scenariodir = NULL;
3906 gchar *tldir = g_build_filename (g_get_user_data_dir (),
3907 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
3909 GFile *dir = g_file_new_for_path (tldir);
3911 kf = g_key_file_new ();
3912 if (num_scenarios > 0) {
3916 for (i = 0; i < num_scenarios; i++) {
3917 file = g_file_new_for_path (scenarios[i]);
3918 if (!_parse_scenario (file, kf)) {
3919 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
3921 gst_object_unref (file);
3929 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
3931 env_scenariodir = g_strsplit (envvar, ":", 0);
3933 _list_scenarios_in_dir (dir, kf);
3934 g_object_unref (dir);
3937 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3938 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
3939 dir = g_file_new_for_path (tldir);
3940 _list_scenarios_in_dir (dir, kf);
3941 g_object_unref (dir);
3944 if (env_scenariodir) {
3947 for (i = 0; env_scenariodir[i]; i++) {
3948 dir = g_file_new_for_path (env_scenariodir[i]);
3949 _list_scenarios_in_dir (dir, kf);
3950 g_object_unref (dir);
3954 /* Hack to make it work uninstalled */
3955 dir = g_file_new_for_path ("data/scenarios");
3956 _list_scenarios_in_dir (dir, kf);
3957 g_object_unref (dir);
3960 result = g_key_file_to_data (kf, &datalength, &err);
3961 g_print ("All scenarios available:\n%s", result);
3963 if (output_file && !err)
3964 if (!g_file_set_contents (output_file, result, datalength, &err)) {
3965 GST_WARNING ("Error writing to file '%s'", output_file);
3968 if (env_scenariodir)
3969 g_strfreev (env_scenariodir);
3972 GST_WARNING ("Got error '%s' listing scenarios", err->message);
3973 g_clear_error (&err);
3978 g_key_file_free (kf);
3983 static GstValidateExecuteActionReturn
3984 _check_last_sample_checksum (GstValidateScenario * scenario,
3985 GstValidateAction * action, GstElement * sink)
3991 const gchar *target_sum;
3992 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3994 target_sum = gst_structure_get_string (action->structure, "checksum");
3995 g_object_get (sink, "last-sample", &sample, NULL);
3996 if (sample == NULL) {
3997 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3998 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
3999 " 'last-sample' property is NULL"
4000 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
4002 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4006 buffer = gst_sample_get_buffer (sample);
4007 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
4008 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4009 "Last sample buffer could not be mapped, action can't run.");
4013 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
4014 gst_buffer_unmap (buffer, &map);
4016 if (g_strcmp0 (sum, target_sum)) {
4017 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4018 "Last buffer checksum '%s' is different than the expected one: '%s'",
4021 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4030 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
4031 const gchar * fname, GstCaps * sinkpad_caps)
4035 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
4036 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
4041 if (paramspec->value_type != GST_TYPE_SAMPLE)
4044 if (!name && !fname && !sinkpad_caps)
4047 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
4051 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
4057 sinkpad = gst_element_get_static_pad (sink, "sink");
4061 tmpcaps = gst_pad_get_current_caps (sinkpad);
4063 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
4065 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
4066 gst_caps_unref (tmpcaps);
4070 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
4076 static GstValidateExecuteActionReturn
4077 _execute_check_last_sample (GstValidateScenario * scenario,
4078 GstValidateAction * action)
4081 GValue data = { 0, };
4082 gboolean done = FALSE;
4083 GstCaps *caps = NULL;
4084 GstElement *sink = NULL, *tmpelement;
4085 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
4087 gst_structure_get_string (action->structure, "sink-factory-name"),
4088 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
4089 DECLARE_AND_GET_PIPELINE (scenario, action);
4092 caps = gst_caps_from_string (caps_str);
4097 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
4099 switch (gst_iterator_next (it, &data)) {
4100 case GST_ITERATOR_OK:
4101 tmpelement = g_value_get_object (&data);
4102 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
4106 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4107 "Could not \"check-last-sample\" as several elements were found "
4108 "from describing string: '%" GST_PTR_FORMAT
4109 "' (%s and %s match)", action->structure,
4110 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
4112 gst_object_unref (sink);
4115 sink = gst_object_ref (tmpelement);
4117 g_value_reset (&data);
4119 case GST_ITERATOR_RESYNC:
4120 gst_iterator_resync (it);
4121 g_clear_object (&sink);
4123 case GST_ITERATOR_ERROR:
4125 case GST_ITERATOR_DONE:
4130 gst_iterator_free (it);
4132 gst_caps_unref (caps);
4135 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4136 "Could not \"check-last-sample\" as no sink was found from description: '%"
4137 GST_PTR_FORMAT "'", action->structure);
4142 return _check_last_sample_checksum (scenario, action, sink);
4145 g_clear_object (&sink);
4146 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4150 _action_set_done (GstValidateAction * action)
4152 JsonBuilder *jbuild;
4153 GstClockTime execution_duration;
4154 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4156 if (scenario == NULL)
4157 return G_SOURCE_REMOVE;
4159 execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
4161 jbuild = json_builder_new ();
4162 json_builder_begin_object (jbuild);
4163 json_builder_set_member_name (jbuild, "type");
4164 json_builder_add_string_value (jbuild, "action-done");
4165 json_builder_set_member_name (jbuild, "action-type");
4166 json_builder_add_string_value (jbuild, action->type);
4167 json_builder_set_member_name (jbuild, "execution-duration");
4168 json_builder_add_double_value (jbuild,
4169 ((gdouble) execution_duration / GST_SECOND));
4170 json_builder_end_object (jbuild);
4172 gst_validate_send (json_builder_get_root (jbuild));
4173 g_object_unref (jbuild);
4175 gst_validate_printf (NULL, " -> Action %s done (duration: %" GST_TIME_FORMAT
4176 ")\n", action->type, GST_TIME_ARGS (execution_duration));
4177 action->priv->execution_time = GST_CLOCK_TIME_NONE;
4178 action->priv->state = _execute_sub_action_action (action);
4180 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
4181 GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
4183 execute_next_action (scenario);
4185 gst_object_unref (scenario);
4187 return G_SOURCE_REMOVE;
4190 /* gst_validate_action_set_done:
4191 * @action: The action that is done executing
4193 * Sets @action as "done", meaning that the next action can
4197 gst_validate_action_set_done (GstValidateAction * action)
4200 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) {
4201 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4205 SCENARIO_LOCK (scenario);
4206 item = g_list_find (scenario->priv->interlaced_actions, action);
4207 scenario->priv->interlaced_actions =
4208 g_list_delete_link (scenario->priv->interlaced_actions, item);
4209 SCENARIO_UNLOCK (scenario);
4210 g_object_unref (scenario);
4214 gst_validate_action_unref (action);
4217 g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
4218 (GSourceFunc) _action_set_done,
4219 gst_mini_object_ref (GST_MINI_OBJECT (action)),
4220 (GDestroyNotify) gst_validate_action_unref);
4224 * gst_validate_action_get_scenario:
4225 * @action: The action for which to retrieve the scenario
4227 * Retrieve the scenario from which @action is executed.
4229 * Returns: (transfer full): The scenario from which the action is being executed.
4231 GstValidateScenario *
4232 gst_validate_action_get_scenario (GstValidateAction * action)
4234 return g_weak_ref_get (&action->priv->scenario);
4238 * gst_validate_register_action_type:
4239 * @type_name: The name of the new action type to add
4240 * @implementer_namespace: The namespace of the implementer of the action type.
4241 * That should always be the name of the GstPlugin as
4242 * retrieved with #gst_plugin_get_name when the action type
4243 * is registered inside a plugin.
4244 * @function: (scope notified): The function to be called to execute the action
4245 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4246 * @description: A description of the new type
4247 * @flags: The #GstValidateActionTypeFlags to set on the new action type
4249 * Register a new action type to the action type system. If the action type already
4250 * exists, it will be overridden by the new definition
4252 * Returns: (transfer none): The newly created action type or the already registered action type
4253 * if it had a higher rank
4255 GstValidateActionType *
4256 gst_validate_register_action_type (const gchar * type_name,
4257 const gchar * implementer_namespace,
4258 GstValidateExecuteAction function,
4259 GstValidateActionParameter * parameters,
4260 const gchar * description, GstValidateActionTypeFlags flags)
4262 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
4263 type_name, GST_RANK_NONE, function, parameters, description,
4266 g_free (type->implementer_namespace);
4267 type->implementer_namespace = g_strdup (implementer_namespace);
4273 _free_action_types (GList * _action_types)
4275 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
4279 * gst_validate_register_action_type_dynamic:
4280 * @plugin: (allow-none): The #GstPlugin that register the action type,
4281 * or NULL for a static element.
4282 * @rank: The ranking of that implementation of the action type called
4283 * @type_name. If an action type has been registered with the same
4284 * name with a higher rank, the new implementation will not be used,
4285 * and the already registered action type is returned.
4286 * If the already registered implementation has a lower rank, the
4287 * new implementation will be used and returned.
4288 * @type_name: The name of the new action type to add
4289 * @function: (scope notified): The function to be called to execute the action
4290 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4291 * @description: A description of the new type
4292 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
4294 * Returns: (transfer none): The newly created action type or the already registered action type
4295 * if it had a higher rank
4297 GstValidateActionType *
4298 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
4299 const gchar * type_name, GstRank rank,
4300 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
4301 const gchar * description, GstValidateActionTypeFlags flags)
4303 GstValidateActionType *tmptype;
4304 GstValidateActionType *type = gst_validate_action_type_new ();
4305 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
4306 gint n_params = is_config ? 0 : 2;
4309 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
4314 type->parameters = g_new0 (GstValidateActionParameter, n_params);
4318 memcpy (type->parameters, parameters,
4319 sizeof (GstValidateActionParameter) * (n_params));
4322 type->prepare = gst_validate_action_default_prepare_func;
4323 type->execute = function;
4324 type->name = g_strdup (type_name);
4326 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
4328 type->implementer_namespace = g_strdup ("none");
4330 type->description = g_strdup (description);
4331 type->flags = flags;
4334 if ((tmptype = _find_action_type (type_name))) {
4335 if (tmptype->rank <= rank) {
4336 action_types = g_list_remove (action_types, tmptype);
4337 type->overriden_type = tmptype;
4339 gst_mini_object_unref (GST_MINI_OBJECT (type));
4345 if (type != tmptype)
4346 action_types = g_list_append (action_types, type);
4349 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
4350 "GstValidatePluginActionTypes");
4352 plugin_action_types = g_list_prepend (plugin_action_types,
4353 gst_mini_object_ref (GST_MINI_OBJECT (type)));
4355 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
4356 plugin_action_types, (GDestroyNotify) _free_action_types);
4362 GstValidateActionType *
4363 gst_validate_get_action_type (const gchar * type_name)
4365 GstValidateActionType *type = _find_action_type (type_name);
4369 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
4375 gst_validate_list_action_types (void)
4377 return action_types;
4381 * gst_validate_print_action_types:
4382 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
4383 * @num_wanted_types: Length of @wanted_types
4385 * Prints the action types details wanted in @wanted_types
4387 * Returns: True if all types could be printed
4390 gst_validate_print_action_types (const gchar ** wanted_types,
4391 gint num_wanted_types)
4396 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
4397 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
4398 gboolean print = FALSE;
4400 if (num_wanted_types) {
4403 for (n = 0; n < num_wanted_types; n++) {
4404 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
4405 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
4416 if (print && num_wanted_types) {
4417 gst_validate_printf (atype, "\n");
4420 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
4424 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
4425 atype->implementer_namespace, atype->name, desc);
4430 if (num_wanted_types && num_wanted_types > nfound) {
4438 * gst_validate_scenario_get_actions:
4439 * @scenario: The scenario to retrieve remaining actions for
4441 * Get remaining actions from @scenario.
4443 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
4446 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
4449 gboolean main_context_acquired;
4451 main_context_acquired = g_main_context_acquire (g_main_context_default ());
4452 g_return_val_if_fail (main_context_acquired, NULL);
4454 ret = g_list_copy_deep (scenario->priv->actions,
4455 (GCopyFunc) gst_mini_object_ref, NULL);
4457 g_main_context_release (g_main_context_default ());
4463 * gst_validate_scenario_get_target_state:
4464 * @scenario: The scenario to retrieve the current target state for
4466 * Get current target state from @scenario.
4468 * Returns: Current target state.
4471 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
4473 return scenario->priv->target_state;
4477 init_scenarios (void)
4480 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
4481 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
4483 _gst_validate_action_type = gst_validate_action_get_type ();
4484 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
4487 REGISTER_ACTION_TYPE ("description", NULL,
4488 ((GstValidateActionParameter []) {
4491 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
4494 .possible_variables = NULL,
4495 .def = "'Nothing'"},
4497 .name = "is-config",
4498 .description = "Whether the scenario is a config only scenario",
4501 .possible_variables = NULL,
4505 .name = "handles-states",
4506 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
4507 "in that case the application should not set the state of the pipeline to anything\n"
4508 "and the scenario action will be executed from the beginning",
4511 .possible_variables = NULL,
4515 .description = "Whether the scenario executes seek actions or not",
4518 .possible_variables = NULL,
4522 .name = "reverse-playback",
4523 .description = "Whether the scenario plays the stream backward",
4526 .possible_variables = NULL,
4530 .name = "need-clock-sync",
4531 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
4532 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
4535 .possible_variables = NULL,
4539 .name = "min-media-duration",
4540 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
4544 .possible_variables = NULL,
4548 .name = "min-audio-track",
4549 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
4550 "for the scenario to be usable",
4553 .possible_variables = NULL,
4557 .name = "min-video-track",
4558 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
4559 "for the scenario to be usable",
4562 .possible_variables = NULL,
4567 .description = "Lets the user know the time the scenario needs to be fully executed",
4569 .types = "double, int",
4570 .possible_variables = NULL,
4571 .def = "infinite (GST_CLOCK_TIME_NONE)"
4574 .name = "pipeline-name",
4575 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
4576 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
4579 .possible_variables = NULL,
4583 .name = "max-latency",
4584 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
4585 "It can be overriden using core configuration, like for example by defining the "
4586 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
4588 .types = "double, int",
4589 .possible_variables = NULL,
4590 .def = "infinite (GST_CLOCK_TIME_NONE)"
4593 .name = "max-dropped",
4594 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
4595 "It can be overriden using core configuration, like for example by defining the "
4596 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
4599 .possible_variables = NULL,
4600 .def = "infinite (-1)"
4604 "Allows to describe the scenario in various ways",
4605 GST_VALIDATE_ACTION_TYPE_CONFIG);
4607 REGISTER_ACTION_TYPE ("seek", _execute_seek,
4608 ((GstValidateActionParameter []) {
4611 .description = "The starting value of the seek",
4613 .types = "double or string",
4614 .possible_variables = "position: The current position in the stream\n"
4615 "duration: The duration of the stream",
4620 .description = "The GstSeekFlags to use",
4622 .types = "string describing the GstSeekFlags to set",
4627 .description = "The rate value of the seek",
4630 .possible_variables = NULL,
4634 .name = "start_type",
4635 .description = "The GstSeekType to use for the start of the seek, in:\n"
4636 " [none, set, end]",
4639 .possible_variables = NULL,
4643 .name = "stop_type",
4644 .description = "The GstSeekType to use for the stop of the seek, in:\n"
4645 " [none, set, end]",
4648 .possible_variables = NULL,
4651 {"stop", "The stop value of the seek", FALSE, "double or ",
4652 "position: The current position in the stream\n"
4653 "duration: The duration of the stream"
4654 "GST_CLOCK_TIME_NONE",
4658 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
4659 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
4660 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
4661 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
4664 REGISTER_ACTION_TYPE ("pause", _execute_pause,
4665 ((GstValidateActionParameter []) {
4668 .description = "The duration during which the stream will be paused",
4671 .possible_variables = NULL,
4676 "Sets pipeline to PAUSED. You can add a 'duration'\n"
4677 "parameter so the pipeline goes back to playing after that duration\n"
4679 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
4681 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
4682 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
4684 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
4685 "Stops the execution of the scenario. It will post a 'request-state'"
4686 " message on the bus with NULL as a requested state"
4687 " and the application is responsible for stopping itself."
4688 " If you override that action type, make sure to link up.",
4689 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4691 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
4692 "Sends an EOS event to the pipeline",
4693 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4695 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
4696 ((GstValidateActionParameter []) {
4699 .description = "Selects which track type to change (can be 'audio', 'video',"
4703 .possible_variables = NULL,
4708 .description = "Selects which track of this type to use: it can be either a number,\n"
4709 "which will be the Nth track of the given type, or a number with a '+' or\n"
4710 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
4711 "'-1' means 'previous track')",
4713 .types = "string: to switch track relatively\n"
4714 "int: To use the actual index to use",
4715 .possible_variables = NULL,
4720 "The 'switch-track' command can be used to switch tracks."
4721 , GST_VALIDATE_ACTION_TYPE_NONE);
4723 REGISTER_ACTION_TYPE ("wait", _execute_wait,
4724 ((GstValidateActionParameter []) {
4727 .description = "the duration while no other action will be executed",
4731 .name = "target-element-name",
4732 .description = "The name of the GstElement to wait @signal-name on.",
4737 .name = "signal-name",
4738 .description = "The name of the signal to wait for on @target-element-name",
4744 .name = "message-type",
4745 .description = "The name of the message type to wait for (on @target-element-name"
4753 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
4754 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
4756 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
4757 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
4758 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
4759 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
4760 GST_VALIDATE_ACTION_TYPE_NONE);
4762 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank,
4763 ((GstValidateActionParameter []) {
4766 .description = "The name of a GstFeature or GstPlugin",
4772 .description = "The GstRank to set on @name",
4774 .types = "string, int",
4778 "Changes the ranking of a particular plugin feature(s)",
4779 GST_VALIDATE_ACTION_TYPE_CONFIG);
4781 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank,
4782 ((GstValidateActionParameter []) {
4784 .name = "feature-name",
4785 .description = "The name of a GstFeature",
4791 .description = "The GstRank to set on @feature-name",
4793 .types = "string, int",
4797 "Changes the ranking of a particular plugin feature",
4798 GST_VALIDATE_ACTION_TYPE_CONFIG);
4800 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
4801 ((GstValidateActionParameter []) {
4804 .description = "A GstState as a string, should be in: \n"
4805 " * ['null', 'ready', 'paused', 'playing']",
4811 "Changes the state of the pipeline to any GstState",
4812 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
4814 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
4815 ((GstValidateActionParameter []) {
4818 "Define vars to be used in other actions.\n"
4819 "For example you can define vars for buffer checksum"
4820 " to be used in the \"check-last-sample\" action type as follow:\n\n"
4822 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
4823 " check-last-sample, checksum=frame1\n"
4825 GST_VALIDATE_ACTION_TYPE_NONE);
4827 REGISTER_ACTION_TYPE ("set-property", _execute_set_property,
4828 ((GstValidateActionParameter []) {
4830 .name = "target-element-name",
4831 .description = "The name of the GstElement to set a property on",
4837 .name = "target-element-factory-name",
4838 .description = "The name factory for which to set a property on built elements",
4844 .name = "target-element-klass",
4845 .description = "The klass of the GstElements to set a property on",
4851 .name = "property-name",
4852 .description = "The name of the property to set on @target-element-name",
4858 .name = "property-value",
4859 .description = "The value of @property-name to be set on the element",
4861 .types = "The same type of @property-name",
4866 "Sets a property of an element or klass of elements in the pipeline.\n"
4867 "Besides property-name and value, either 'target-element-name' or\n"
4868 "'target-element-klass' needs to be defined",
4869 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
4870 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
4871 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
4873 REGISTER_ACTION_TYPE ("set-debug-threshold",
4874 _execute_set_debug_threshold,
4875 ((GstValidateActionParameter [])
4878 .name = "debug-threshold",
4879 .description = "String defining debug threshold\n"
4880 "See gst_debug_set_threshold_from_string",
4885 "Sets the debug level to be used, same format as\n"
4886 "setting the GST_DEBUG env variable",
4887 GST_VALIDATE_ACTION_TYPE_NONE);
4889 REGISTER_ACTION_TYPE ("include",
4890 NULL, /* This is handled directly when loading a scenario */
4891 ((GstValidateActionParameter [])
4895 .description = "The location of the sub scenario to include.",
4900 "Include a sub scenario file.",
4901 GST_VALIDATE_ACTION_TYPE_NONE);
4903 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
4904 ((GstValidateActionParameter [])
4907 .name = "target-element-name",
4908 .description = "The name of the GstElement to emit a signal on",
4913 .name = "signal-name",
4914 .description = "The name of the signal to emit on @target-element-name",
4921 "Emits a signal to an element in the pipeline",
4922 GST_VALIDATE_ACTION_TYPE_NONE);
4924 REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
4925 ((GstValidateActionParameter [])
4928 .name = "target-element-name",
4929 .description = "The name of the appsrc to push data on",
4934 .name = "file-name",
4935 .description = "Relative path to a file whose contents will be pushed as a buffer",
4941 .description = "Offset within the file where the buffer will start",
4947 .description = "Number of bytes from the file that will be pushed as a buffer",
4953 .description = "Caps for the buffer to be pushed",
4959 "Queues a buffer from an appsrc and waits for it to be handled by downstream elements in the same streaming thread.",
4960 GST_VALIDATE_ACTION_TYPE_NONE);
4962 REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
4963 ((GstValidateActionParameter [])
4966 .name = "target-element-name",
4967 .description = "The name of the appsrc to emit EOS on",
4973 "Queues a EOS event in an appsrc.",
4974 GST_VALIDATE_ACTION_TYPE_NONE);
4976 REGISTER_ACTION_TYPE ("flush", _execute_flush,
4977 ((GstValidateActionParameter [])
4980 .name = "target-element-name",
4981 .description = "The name of the appsrc to flush on",
4986 .name = "reset-time",
4987 .description = "Whether the flush should reset running time",
4994 "Sends FLUSH_START and FLUSH_STOP events.",
4995 GST_VALIDATE_ACTION_TYPE_NONE);
4997 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
4998 ((GstValidateActionParameter [])
5001 .name = "plugin-name",
5002 .description = "The name of the GstPlugin to disable",
5007 .name = "as-config",
5008 .description = "Execute action as a config action (meaning when loading the scenario)",
5015 "Disables a GstPlugin",
5016 GST_VALIDATE_ACTION_TYPE_NONE);
5018 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
5019 ((GstValidateActionParameter []) {
5021 .name = "sink-name",
5022 .description = "The name of the sink element to check sample on.",
5028 .name = "sink-factory-name",
5029 .description = "The name of the factory of the sink element to check sample on.",
5035 .name = "sinkpad-caps",
5036 .description = "The caps (as string) of the sink to check.",
5043 .description = "The reference checksum of the buffer.",
5050 "Checks the last-sample checksum on declared Sink element."
5051 " This allows checking the checksum of a buffer after a 'seek' or after a GESTimeline 'commit'"
5053 GST_VALIDATE_ACTION_TYPE_INTERLACED);
5057 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
5058 const gchar *action_typename;
5059 GstStructure *plug_conf = (GstStructure *) tmp->data;
5061 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
5062 GstValidateAction *action;
5063 GstValidateActionType *atype = _find_action_type (action_typename);
5066 g_error ("[CONFIG ERROR] Action type %s not found", action_typename);
5072 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
5073 GST_INFO ("Action type %s from configuration files"
5074 " is handled.", action_typename);
5078 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
5079 !(_action_type_has_parameter (atype, "as-config"))) {
5080 g_error ("[CONFIG ERROR] Action '%s' is not a config action",
5086 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
5087 gst_structure_set_name (plug_conf, action_typename);
5089 action = gst_validate_action_new (NULL, atype);
5090 _fill_action (NULL, action, plug_conf, FALSE);
5096 gst_validate_scenario_deinit (void)
5098 _free_action_types (action_types);
5099 action_types = NULL;