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 static GstValidateExecuteActionReturn
2511 _execute_disable_plugin (GstValidateScenario * scenario,
2512 GstValidateAction * action)
2515 const gchar *plugin_name;
2517 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
2519 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
2521 if (plugin == NULL) {
2522 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2523 "Could not find plugin to disable: %s", plugin_name);
2525 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2528 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
2529 gst_registry_remove_plugin (gst_registry_get (), plugin);
2531 return GST_VALIDATE_EXECUTE_ACTION_OK;
2535 gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario,
2538 GstValidateScenarioPrivate *priv = scenario->priv;
2540 GstSeekType start_type, stop_type;
2542 gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start,
2545 if (start_type == GST_SEEK_TYPE_SET) {
2546 priv->segment_start = start;
2547 } else if (start_type == GST_SEEK_TYPE_END) {
2551 if (stop_type == GST_SEEK_TYPE_SET) {
2552 priv->segment_stop = stop;
2553 } else if (stop_type == GST_SEEK_TYPE_END) {
2559 _compare_actions (GstValidateAction * a, GstValidateAction * b)
2561 if (a->action_number < b->action_number)
2563 else if (a->action_number == b->action_number)
2570 _structure_set_variables (GQuark field_id, GValue * value,
2571 GstValidateAction * action)
2573 GstValidateScenario *scenario;
2574 const gchar *var_value;
2576 if (!G_VALUE_HOLDS_STRING (value))
2579 scenario = gst_validate_action_get_scenario (action);
2584 gst_structure_get_string (scenario->priv->vars,
2585 g_value_get_string (value));
2587 GST_INFO_OBJECT (action, "Setting variable %s to %s",
2588 g_value_get_string (value), var_value);
2589 g_value_set_string (value, var_value);
2592 g_clear_object (&scenario);
2598 gst_validate_action_default_prepare_func (GstValidateAction * action)
2602 const gchar *vars[] = { "duration", "start", "stop" };
2603 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2605 for (i = 0; i < G_N_ELEMENTS (vars); i++) {
2606 gint res = gst_validate_action_get_clocktime (scenario, action, vars[i],
2609 GST_ERROR_OBJECT (scenario, "Could not get clocktime for"
2610 " variable %s", vars[i]);
2612 gst_object_unref (scenario);
2614 } else if (res == -1) {
2618 gst_structure_set (action->structure, vars[i], GST_TYPE_CLOCK_TIME,
2621 gst_object_unref (scenario);
2622 gst_structure_filter_and_map_in_place (action->structure,
2623 (GstStructureFilterMapFunc) _structure_set_variables, action);
2629 _check_waiting_for_message (GstValidateScenario * scenario,
2630 GstMessage * message)
2632 GstValidateScenarioPrivate *priv = scenario->priv;
2634 if (!g_strcmp0 (priv->message_type,
2635 gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
2636 GstValidateAction *action = scenario->priv->actions->data;
2638 g_free ((gpointer) priv->message_type);
2639 priv->message_type = NULL;
2641 gst_validate_printf (scenario, "Stop waiting for message\n");
2643 gst_validate_action_set_done (action);
2644 _add_execute_actions_gsource (scenario);
2649 streams_list_contain (GList * streams, const gchar * stream_id)
2653 for (l = streams; l; l = g_list_next (l)) {
2654 GstStream *s = l->data;
2656 if (!g_strcmp0 (s->stream_id, stream_id))
2664 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
2665 GstElement * pipeline)
2667 GstValidateScenarioPrivate *priv = scenario->priv;
2669 GstClockTime min_latency;
2671 query = gst_query_new_latency ();
2672 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
2673 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2674 "Failed to perfom LATENCY query");
2675 gst_query_unref (query);
2679 gst_query_parse_latency (query, NULL, &min_latency, NULL);
2680 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
2681 " max allowed: %" GST_TIME_FORMAT,
2682 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
2684 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
2685 min_latency > priv->max_latency) {
2686 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_LATENCY_TOO_HIGH,
2687 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
2688 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
2689 GST_TIME_ARGS (priv->max_latency));
2694 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
2696 gboolean is_error = FALSE;
2697 GstValidateScenarioPrivate *priv = scenario->priv;
2698 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2701 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2706 switch (GST_MESSAGE_TYPE (message)) {
2707 case GST_MESSAGE_ASYNC_DONE:
2708 if (priv->last_seek) {
2709 gst_validate_scenario_update_segment_from_seek (scenario,
2712 if (priv->target_state == GST_STATE_PAUSED)
2713 priv->seeked_in_pause = TRUE;
2715 gst_event_replace (&priv->last_seek, NULL);
2716 gst_validate_action_set_done (priv->actions->data);
2717 } else if (scenario->priv->needs_async_done) {
2718 scenario->priv->needs_async_done = FALSE;
2719 if (priv->actions && _action_sets_state (priv->actions->data)
2720 && !priv->changing_state)
2721 gst_validate_action_set_done (priv->actions->data);
2725 if (priv->needs_parsing) {
2728 for (tmp = priv->needs_parsing; tmp; tmp = tmp->next) {
2729 GstValidateAction *action = (GstValidateAction *) tmp->data;
2731 if (!_set_action_playback_time (scenario, action))
2734 priv->actions = g_list_insert_sorted (priv->actions, action,
2735 (GCompareFunc) _compare_actions);
2738 g_list_free (priv->needs_parsing);
2739 priv->needs_parsing = NULL;
2741 _add_execute_actions_gsource (scenario);
2743 case GST_MESSAGE_STATE_CHANGED:
2745 if (pipeline && GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
2746 GstState nstate, pstate;
2748 gst_message_parse_state_changed (message, &pstate, &nstate, NULL);
2750 if (scenario->priv->changing_state &&
2751 scenario->priv->target_state == nstate) {
2752 scenario->priv->changing_state = FALSE;
2754 if (priv->actions && _action_sets_state (priv->actions->data) &&
2755 !priv->needs_async_done)
2756 gst_validate_action_set_done (priv->actions->data);
2759 if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED)
2760 _add_execute_actions_gsource (scenario);
2762 /* GstBin only send a new latency message when reaching PLAYING if
2763 * async-handling=true so check the latency manually. */
2764 if (nstate == GST_STATE_PLAYING)
2765 gst_validate_scenario_check_latency (scenario, pipeline);
2769 case GST_MESSAGE_ERROR:
2773 case GST_MESSAGE_EOS:
2775 GstValidateAction *stop_action;
2776 GstValidateActionType *stop_action_type;
2780 priv->got_eos = TRUE;
2781 if (priv->message_type) {
2783 if (priv->actions->next) {
2784 GST_DEBUG_OBJECT (scenario,
2785 "Waiting for a message and got a next action"
2786 " to execute, letting it a chance!");
2789 /* Clear current message wait if waiting for EOS */
2790 _check_waiting_for_message (scenario, message);
2795 SCENARIO_LOCK (scenario);
2796 if (scenario->priv->actions || scenario->priv->interlaced_actions ||
2797 scenario->priv->on_addition_actions) {
2798 guint nb_actions = 0;
2799 gchar *actions = g_strdup (""), *tmpconcat;
2801 GList *all_actions =
2802 g_list_concat (g_list_concat (scenario->priv->actions,
2803 scenario->priv->interlaced_actions),
2804 scenario->priv->on_addition_actions);
2806 for (tmp = all_actions; tmp; tmp = tmp->next) {
2807 gchar *action_string;
2808 GstValidateAction *action = (GstValidateAction *) tmp->data;
2809 GstValidateActionType *type = _find_action_type (action->type);
2811 tmpconcat = actions;
2813 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
2814 action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
2815 action->priv->optional) {
2816 gst_validate_action_unref (action);
2823 action_string = gst_structure_to_string (action->structure);
2825 g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string);
2826 gst_validate_action_unref (action);
2828 g_free (action_string);
2830 g_list_free (all_actions);
2831 scenario->priv->actions = NULL;
2832 scenario->priv->interlaced_actions = NULL;
2833 scenario->priv->on_addition_actions = NULL;
2836 if (nb_actions > 0) {
2837 GstClockTime position = GST_CLOCK_TIME_NONE;
2839 _get_position (scenario, NULL, &position);
2840 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
2841 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
2842 ")", nb_actions, actions, GST_TIME_ARGS (position));
2846 SCENARIO_UNLOCK (scenario);
2848 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
2850 stop_action_type = _find_action_type ("stop");
2851 stop_action = gst_validate_action_new (scenario, stop_action_type);
2852 s = gst_structure_from_string ("stop, generated-after-eos=true;", NULL);
2853 _fill_action (scenario, stop_action, s, FALSE);
2854 gst_structure_free (s);
2855 gst_validate_execute_action (stop_action_type, stop_action);
2856 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
2860 case GST_MESSAGE_BUFFERING:
2864 gst_message_parse_buffering (message, &percent);
2867 priv->buffering = FALSE;
2869 priv->buffering = TRUE;
2872 case GST_MESSAGE_STREAMS_SELECTED:
2875 GList *streams_selected = NULL;
2877 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
2879 gst_message_streams_selected_get_stream (message, i);
2881 streams_selected = g_list_append (streams_selected, stream);
2884 /* Is there a pending switch-track action waiting for the new streams to
2886 if (priv->pending_switch_track) {
2887 GList *expected, *l;
2888 GstValidateScenario *scenario =
2889 gst_validate_action_get_scenario (priv->pending_switch_track);
2892 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
2893 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
2895 if (g_list_length (expected) != g_list_length (streams_selected)) {
2896 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2897 "Was expecting %d selected streams but got %d",
2898 g_list_length (expected), g_list_length (streams_selected));
2902 for (l = expected; l; l = g_list_next (l)) {
2903 const gchar *stream_id = l->data;
2905 if (!streams_list_contain (streams_selected, stream_id)) {
2906 GST_VALIDATE_REPORT (scenario,
2907 SCENARIO_ACTION_EXECUTION_ERROR,
2908 "Stream %s has not be activated", stream_id);
2914 gst_object_unref (scenario);
2915 gst_validate_action_set_done (priv->pending_switch_track);
2916 priv->pending_switch_track = NULL;
2919 g_list_free_full (streams_selected, gst_object_unref);
2922 case GST_MESSAGE_LATENCY:
2923 gst_validate_scenario_check_latency (scenario, pipeline);
2926 case GST_MESSAGE_QOS:
2930 /* Check the maximum allowed when scenario is terminating so the report
2931 * will include the actual number of dropped buffers. */
2932 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
2934 g_atomic_int_set (&priv->dropped, dropped);
2943 gst_object_unref (pipeline);
2944 /* Check if we got the message expected by a wait action */
2945 if (priv->message_type)
2946 _check_waiting_for_message (scenario, message);
2952 _action_type_has_parameter (GstValidateActionType * atype,
2953 const gchar * paramname)
2957 if (!atype->parameters)
2960 for (i = 0; atype->parameters[i].name; i++)
2961 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
2968 _load_scenario_file (GstValidateScenario * scenario,
2969 const gchar * scenario_file, gboolean * is_config)
2971 gboolean ret = TRUE;
2972 GList *structures, *tmp;
2973 GstValidateScenarioPrivate *priv = scenario->priv;
2978 structures = gst_validate_utils_structs_parse_from_filename (scenario_file);
2979 if (structures == NULL)
2982 for (tmp = structures; tmp; tmp = tmp->next) {
2983 GstValidateAction *action;
2984 GstValidateActionType *action_type;
2986 GstStructure *structure = (GstStructure *) tmp->data;
2988 type = gst_structure_get_name (structure);
2989 if (!g_strcmp0 (type, "description")) {
2990 const gchar *pipeline_name;
2992 gst_structure_get_boolean (structure, "is-config", is_config);
2993 gst_structure_get_boolean (structure, "handles-states",
2994 &priv->handles_state);
2996 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
2997 if (pipeline_name) {
2998 g_free (priv->pipeline_name);
2999 priv->pipeline_name = g_strdup (pipeline_name);
3002 gst_validate_utils_get_clocktime (structure, "max-latency",
3003 &priv->max_latency);
3005 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
3008 } else if (!g_strcmp0 (type, "include")) {
3009 const gchar *location = gst_structure_get_string (structure, "location");
3012 GST_ERROR_OBJECT (scenario,
3013 "Mandatory field 'location' not present in structure: %"
3014 GST_PTR_FORMAT, structure);
3018 if (!gst_validate_scenario_load (scenario, location, scenario_file)) {
3019 GST_ERROR ("Failed including scenario %s", location);
3024 } else if (!(action_type = _find_action_type (type))) {
3025 if (gst_structure_has_field (structure, "optional-action-type")) {
3026 GST_INFO_OBJECT (scenario,
3027 "Action type not found %s but marked as not mandatory", type);
3031 GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type);
3035 if (action_type->parameters) {
3038 for (i = 0; action_type->parameters[i].name; i++) {
3039 if (action_type->parameters[i].mandatory &&
3040 gst_structure_has_field (structure,
3041 action_type->parameters[i].name) == FALSE) {
3042 GST_ERROR_OBJECT (scenario,
3043 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
3044 action_type->parameters[i].name, structure);
3050 action = gst_validate_action_new (scenario, action_type);
3051 if (_fill_action (scenario, action,
3052 structure, TRUE) == GST_VALIDATE_EXECUTE_ACTION_ERROR)
3055 action->action_number = priv->num_actions++;
3058 /* max-latency and max-dropped can be overriden using config */
3059 for (config = gst_validate_plugin_get_config (NULL); config;
3060 config = g_list_next (config)) {
3061 gst_validate_utils_get_clocktime (config->data, "max-latency",
3062 &priv->max_latency);
3064 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
3068 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3079 gst_validate_scenario_load (GstValidateScenario * scenario,
3080 const gchar * scenario_name, const gchar * relative_scenario)
3082 gchar **scenarios = NULL;
3084 gboolean found_actions = FALSE, is_config, ret = TRUE;
3085 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
3087 gchar **env_scenariodir;
3089 if (relative_scenario) {
3090 gchar *relative_dir = g_path_get_dirname (relative_scenario);
3091 gchar *tmp_scenarios_path =
3092 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
3095 g_free (scenarios_path);
3096 scenarios_path = tmp_scenarios_path;
3100 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
3102 g_free (scenarios_path);
3107 scenarios = g_strsplit (scenario_name, ":", -1);
3109 for (i = 0; scenarios[i]; i++) {
3110 gchar *lfilename = NULL, *tldir = NULL;
3112 /* First check if the scenario name is not a full path to the
3113 * actual scenario */
3114 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
3115 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
3116 "Trying to load it", scenarios[i]);
3117 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config)))
3118 goto check_scenario;
3121 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
3122 lfilename = g_strdup (scenarios[i]);
3125 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
3127 if (env_scenariodir) {
3130 for (i = 0; env_scenariodir[i]; i++) {
3131 tldir = g_build_filename (env_scenariodir[i], lfilename, NULL);
3132 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3133 goto check_scenario;
3138 tldir = g_build_filename ("data", "scenarios", lfilename, NULL);
3140 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3141 goto check_scenario;
3145 /* Try from local profiles */
3147 g_build_filename (g_get_user_data_dir (),
3148 "gstreamer-" GST_API_VERSION, "validate",
3149 GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3151 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3153 /* Try from system-wide profiles */
3154 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3155 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3157 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3161 /* else check scenario */
3167 if (found_actions == TRUE)
3168 goto one_actions_scenario_max;
3170 found_actions = TRUE;
3176 if (env_scenariodir)
3177 g_strfreev (env_scenariodir);
3179 g_strfreev (scenarios);
3182 g_error ("Could not set scenario %s => EXIT\n", scenario_name);
3188 GST_ERROR ("Invalid name for scenario '%s'", scenario_name);
3193 one_actions_scenario_max:
3195 GST_ERROR ("You can set at most only one action scenario. "
3196 "You can have several config scenarios though (a config scenario's "
3197 "file must have is-config=true, and all its actions must be executable "
3198 "at parsing time).");
3207 gst_validate_scenario_set_property (GObject * object, guint prop_id,
3208 const GValue * value, GParamSpec * pspec)
3210 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3214 /* we assume the runner is valid as long as this scenario is,
3216 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
3217 g_value_get_object (value));
3219 case PROP_HANDLES_STATE:
3220 g_assert_not_reached ();
3222 case PROP_EXECUTE_ON_IDLE:
3223 self->priv->execute_on_idle = g_value_get_boolean (value);
3231 gst_validate_scenario_get_property (GObject * object, guint prop_id,
3232 GValue * value, GParamSpec * pspec)
3234 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3238 /* we assume the runner is valid as long as this scenario is,
3240 g_value_take_object (value,
3241 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
3243 case PROP_HANDLES_STATE:
3244 g_value_set_boolean (value, self->priv->handles_state);
3246 case PROP_EXECUTE_ON_IDLE:
3247 g_value_set_boolean (value, self->priv->execute_on_idle);
3255 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
3257 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3259 object_class->dispose = gst_validate_scenario_dispose;
3260 object_class->finalize = gst_validate_scenario_finalize;
3262 object_class->get_property = gst_validate_scenario_get_property;
3263 object_class->set_property = gst_validate_scenario_set_property;
3265 g_object_class_install_property (object_class, PROP_RUNNER,
3266 g_param_spec_object ("validate-runner", "VALIDATE Runner",
3267 "The Validate runner to report errors to",
3268 GST_TYPE_VALIDATE_RUNNER,
3269 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
3271 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
3272 g_param_spec_boolean ("handles-states", "Handles state",
3273 "True if the application should not handle the first state change. "
3274 "False if it is application responsibility",
3275 FALSE, G_PARAM_READABLE));
3277 g_object_class_install_property (object_class,
3278 PROP_EXECUTE_ON_IDLE,
3279 g_param_spec_boolean ("execute-on-idle",
3280 "Force waiting between actions",
3281 "Always execute actions on idle and do not chain them to execute as"
3282 " fast as possible. Setting this property is useful if action"
3283 " execution can lead to the addition of new sources on the same main"
3284 " loop as it provides these new GSource a chance to be dispatched"
3285 " between actions", FALSE, G_PARAM_READWRITE));
3288 * GstValidateScenario::done:
3289 * @scenario: The scenario runing
3291 * Emitted once all actions have been executed
3293 scenario_signals[DONE] =
3294 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
3295 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
3299 gst_validate_scenario_init (GstValidateScenario * scenario)
3301 GstValidateScenarioPrivate *priv = scenario->priv =
3302 gst_validate_scenario_get_instance_private (scenario);
3304 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
3305 priv->segment_start = 0;
3306 priv->segment_stop = GST_CLOCK_TIME_NONE;
3307 priv->action_execution_interval = 10;
3308 priv->vars = gst_structure_new_empty ("vars");
3309 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
3310 priv->max_latency = GST_CLOCK_TIME_NONE;
3311 priv->max_dropped = -1;
3313 g_mutex_init (&priv->lock);
3317 gst_validate_scenario_dispose (GObject * object)
3319 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3321 if (priv->last_seek)
3322 gst_event_unref (priv->last_seek);
3323 g_weak_ref_clear (&priv->ref_pipeline);
3326 gst_bus_remove_signal_watch (priv->bus);
3327 gst_object_unref (priv->bus);
3331 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
3335 gst_validate_scenario_finalize (GObject * object)
3337 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3339 /* Because g_object_add_weak_pointer() is used, this MUST be on the
3341 g_assert (g_main_context_acquire (g_main_context_default ()));
3342 g_main_context_release (g_main_context_default ());
3344 g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref);
3345 g_list_free_full (priv->interlaced_actions,
3346 (GDestroyNotify) gst_mini_object_unref);
3347 g_list_free_full (priv->on_addition_actions,
3348 (GDestroyNotify) gst_mini_object_unref);
3349 g_list_free_full (priv->needs_parsing,
3350 (GDestroyNotify) gst_mini_object_unref);
3351 g_free (priv->pipeline_name);
3352 gst_structure_free (priv->vars);
3353 g_mutex_clear (&priv->lock);
3355 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
3358 static void _element_added_cb (GstBin * bin, GstElement * element,
3359 GstValidateScenario * scenario);
3362 iterate_children (GstValidateScenario * scenario, GstBin * bin)
3365 GValue v = G_VALUE_INIT;
3366 gboolean done = FALSE;
3367 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
3369 called = g_hash_table_new (NULL, NULL);
3370 it = gst_bin_iterate_elements (bin);
3373 switch (gst_iterator_next (it, &v)) {
3374 case GST_ITERATOR_OK:{
3375 GstElement *child = g_value_get_object (&v);
3377 if (g_hash_table_lookup (called, child) == NULL) {
3378 _element_added_cb (bin, child, scenario);
3379 g_hash_table_add (called, child);
3384 case GST_ITERATOR_RESYNC:
3385 gst_iterator_resync (it);
3387 case GST_ITERATOR_ERROR:
3388 case GST_ITERATOR_DONE:
3393 gst_iterator_free (it);
3394 g_hash_table_unref (called);
3398 should_execute_action (GstElement * element, GstValidateAction * action)
3402 tmp = gst_structure_get_string (action->structure, "target-element-name");
3403 if (tmp != NULL && !strcmp (tmp, GST_ELEMENT_NAME (element)))
3406 tmp = gst_structure_get_string (action->structure, "target-element-klass");
3407 if (tmp != NULL && gst_validate_element_has_klass (element, tmp))
3411 gst_structure_get_string (action->structure,
3412 "target-element-factory-name");
3413 if (tmp != NULL && gst_element_get_factory (element)
3414 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (element)), tmp))
3421 _element_added_cb (GstBin * bin, GstElement * element,
3422 GstValidateScenario * scenario)
3426 GstValidateScenarioPrivate *priv = scenario->priv;
3428 /* Check if it's an element we track for a set-property action */
3429 SCENARIO_LOCK (scenario);
3430 tmp = priv->on_addition_actions;
3432 GstValidateAction *action = (GstValidateAction *) tmp->data;
3434 if (action->playback_time != GST_CLOCK_TIME_NONE)
3436 if (g_strcmp0 (action->type, "set-property"))
3439 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
3440 action, action->type);
3441 if (should_execute_action (element, action)) {
3442 GstValidateActionType *action_type;
3443 action_type = _find_action_type (action->type);
3444 GST_DEBUG_OBJECT (element, "Executing set-property action");
3445 if (gst_validate_execute_action (action_type, action)) {
3446 priv->on_addition_actions =
3447 g_list_remove_link (priv->on_addition_actions, tmp);
3448 gst_mini_object_unref (GST_MINI_OBJECT (action));
3450 tmp = priv->on_addition_actions;
3456 SCENARIO_UNLOCK (scenario);
3458 _check_scenario_is_done (scenario);
3460 /* If it's a bin, listen to the child */
3461 if (GST_IS_BIN (element)) {
3462 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
3464 iterate_children (scenario, GST_BIN (element));
3469 * gst_validate_scenario_factory_create:
3470 * @runner: The #GstValidateRunner to use to report issues
3471 * @pipeline: The pipeline to run the scenario on
3472 * @scenario_name: The name (or path) of the scenario to run
3474 * Returns: (transfer full): A #GstValidateScenario or NULL
3476 GstValidateScenario *
3477 gst_validate_scenario_factory_create (GstValidateRunner *
3478 runner, GstElement * pipeline, const gchar * scenario_name)
3481 GstValidateScenario *scenario =
3482 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
3485 GST_LOG ("Creating scenario %s", scenario_name);
3486 if (!gst_validate_scenario_load (scenario, scenario_name, NULL)) {
3487 g_object_unref (scenario);
3492 if (scenario->priv->pipeline_name &&
3493 !g_pattern_match_simple (scenario->priv->pipeline_name,
3494 GST_OBJECT_NAME (pipeline))) {
3495 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
3496 scenario_name, scenario->priv->pipeline_name,
3497 GST_OBJECT_NAME (pipeline));
3499 gst_object_unref (scenario);
3504 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
3505 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
3506 g_strdup (scenario_name));
3508 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
3511 iterate_children (scenario, GST_BIN (pipeline));
3513 scenario->priv->bus = gst_element_get_bus (pipeline);
3514 gst_bus_add_signal_watch (scenario->priv->bus);
3515 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
3518 for (config = gst_validate_plugin_get_config (NULL); config;
3519 config = config->next) {
3522 if (gst_structure_get_uint (config->data,
3523 "scenario-action-execution-interval",
3524 &scenario->priv->action_execution_interval)) {
3525 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3526 scenario->priv->action_execution_interval);
3528 } else if (gst_structure_get_int (config->data,
3529 "scenario-action-execution-interval", &interval)) {
3531 scenario->priv->action_execution_interval = (guint) interval;
3532 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3533 scenario->priv->action_execution_interval);
3537 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
3542 if (scenario->priv->handles_state) {
3543 GST_INFO_OBJECT (scenario, "Scenario handles state."
3544 " Starting the get position source");
3545 _add_execute_actions_gsource (scenario);
3548 gst_validate_printf (NULL,
3549 "\n=========================================\n"
3550 "Running scenario %s on pipeline %s"
3551 "\n=========================================\n", scenario_name,
3552 GST_OBJECT_NAME (pipeline));
3554 scenario->priv->overrides =
3555 gst_validate_override_registry_get_override_for_names
3556 (gst_validate_override_registry_get (), "scenarios", NULL);
3562 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
3564 gchar *tmp = gst_value_serialize (value);
3566 g_key_file_set_string (kfg->kf, kfg->group_name,
3567 g_quark_to_string (field_id), g_strcompress (tmp));
3576 _parse_scenario (GFile * f, GKeyFile * kf)
3578 gboolean ret = FALSE;
3579 gchar *fname = g_file_get_basename (f);
3581 if (g_str_has_suffix (fname, GST_VALIDATE_SCENARIO_SUFFIX)) {
3582 gboolean needs_clock_sync = FALSE;
3583 GstStructure *desc = NULL;
3585 gchar **name = g_strsplit (fname, GST_VALIDATE_SCENARIO_SUFFIX, 0);
3586 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f);
3588 for (tmp = structures; tmp; tmp = tmp->next) {
3589 GstStructure *_struct = (GstStructure *) tmp->data;
3590 GstValidateActionType *type =
3591 _find_action_type (gst_structure_get_name (_struct));
3593 if (!desc && gst_structure_has_name (_struct, "description"))
3594 desc = gst_structure_copy (_struct);
3595 else if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
3596 needs_clock_sync = TRUE;
3599 if (needs_clock_sync) {
3601 gst_structure_set (desc, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
3603 desc = gst_structure_from_string ("description, need-clock-sync=true;",
3608 KeyFileGroupName kfg;
3610 kfg.group_name = name[0];
3613 gst_structure_foreach (desc,
3614 (GstStructureForeachFunc) _add_description, &kfg);
3615 gst_structure_free (desc);
3617 g_key_file_set_string (kf, name[0], "noinfo", "nothing");
3619 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3630 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
3632 GFileEnumerator *fenum;
3635 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
3636 G_FILE_QUERY_INFO_NONE, NULL, NULL);
3641 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
3642 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
3643 GFile *f = g_file_enumerator_get_child (fenum, info);
3645 _parse_scenario (f, kf);
3646 gst_object_unref (f);
3649 gst_object_unref (fenum);
3653 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
3654 gchar * output_file)
3660 GKeyFile *kf = NULL;
3662 const gchar *envvar;
3663 gchar **env_scenariodir = NULL;
3664 gchar *tldir = g_build_filename (g_get_user_data_dir (),
3665 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
3667 GFile *dir = g_file_new_for_path (tldir);
3669 kf = g_key_file_new ();
3670 if (num_scenarios > 0) {
3674 for (i = 0; i < num_scenarios; i++) {
3675 file = g_file_new_for_path (scenarios[i]);
3676 if (!_parse_scenario (file, kf)) {
3677 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
3679 gst_object_unref (file);
3687 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
3689 env_scenariodir = g_strsplit (envvar, ":", 0);
3691 _list_scenarios_in_dir (dir, kf);
3692 g_object_unref (dir);
3695 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3696 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
3697 dir = g_file_new_for_path (tldir);
3698 _list_scenarios_in_dir (dir, kf);
3699 g_object_unref (dir);
3702 if (env_scenariodir) {
3705 for (i = 0; env_scenariodir[i]; i++) {
3706 dir = g_file_new_for_path (env_scenariodir[i]);
3707 _list_scenarios_in_dir (dir, kf);
3708 g_object_unref (dir);
3712 /* Hack to make it work uninstalled */
3713 dir = g_file_new_for_path ("data/scenarios");
3714 _list_scenarios_in_dir (dir, kf);
3715 g_object_unref (dir);
3718 result = g_key_file_to_data (kf, &datalength, &err);
3719 g_print ("All scenarios available:\n%s", result);
3721 if (output_file && !err)
3722 if (!g_file_set_contents (output_file, result, datalength, &err)) {
3723 GST_WARNING ("Error writing to file '%s'", output_file);
3726 if (env_scenariodir)
3727 g_strfreev (env_scenariodir);
3730 GST_WARNING ("Got error '%s' listing scenarios", err->message);
3731 g_clear_error (&err);
3736 g_key_file_free (kf);
3741 static GstValidateExecuteActionReturn
3742 _check_last_sample_checksum (GstValidateScenario * scenario,
3743 GstValidateAction * action, GstElement * sink)
3749 const gchar *target_sum;
3750 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
3752 target_sum = gst_structure_get_string (action->structure, "checksum");
3753 g_object_get (sink, "last-sample", &sample, NULL);
3754 if (sample == NULL) {
3755 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3756 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
3757 " 'last-sample' property is NULL"
3758 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
3760 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3764 buffer = gst_sample_get_buffer (sample);
3765 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
3766 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3767 "Last sample buffer could not be mapped, action can't run.");
3771 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
3772 gst_buffer_unmap (buffer, &map);
3774 if (g_strcmp0 (sum, target_sum)) {
3775 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3776 "Last buffer checksum '%s' is different than the expected one: '%s'",
3779 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3788 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
3789 const gchar * fname, GstCaps * sinkpad_caps)
3793 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
3794 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
3799 if (paramspec->value_type != GST_TYPE_SAMPLE)
3802 if (!name && !fname && !sinkpad_caps)
3805 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
3809 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
3815 sinkpad = gst_element_get_static_pad (sink, "sink");
3819 tmpcaps = gst_pad_get_current_caps (sinkpad);
3821 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
3823 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
3824 gst_caps_unref (tmpcaps);
3828 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
3834 static GstValidateExecuteActionReturn
3835 _execute_check_last_sample (GstValidateScenario * scenario,
3836 GstValidateAction * action)
3839 GValue data = { 0, };
3840 gboolean done = FALSE;
3841 GstCaps *caps = NULL;
3842 GstElement *sink = NULL, *tmpelement;
3843 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
3845 gst_structure_get_string (action->structure, "sink-factory-name"),
3846 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
3847 DECLARE_AND_GET_PIPELINE (scenario, action);
3850 caps = gst_caps_from_string (caps_str);
3855 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
3857 switch (gst_iterator_next (it, &data)) {
3858 case GST_ITERATOR_OK:
3859 tmpelement = g_value_get_object (&data);
3860 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
3864 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3865 "Could not \"check-last-sample\" as several elements were found "
3866 "from describing string: '%" GST_PTR_FORMAT
3867 "' (%s and %s match)", action->structure,
3868 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
3870 gst_object_unref (sink);
3873 sink = gst_object_ref (tmpelement);
3875 g_value_reset (&data);
3877 case GST_ITERATOR_RESYNC:
3878 gst_iterator_resync (it);
3879 g_clear_object (&sink);
3881 case GST_ITERATOR_ERROR:
3883 case GST_ITERATOR_DONE:
3888 gst_iterator_free (it);
3890 gst_caps_unref (caps);
3893 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3894 "Could not \"check-last-sample\" as no sink was found from description: '%"
3895 GST_PTR_FORMAT "'", action->structure);
3900 return _check_last_sample_checksum (scenario, action, sink);
3903 g_clear_object (&sink);
3904 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
3908 _action_set_done (GstValidateAction * action)
3910 JsonBuilder *jbuild;
3911 GstClockTime execution_duration;
3912 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3914 if (scenario == NULL)
3915 return G_SOURCE_REMOVE;
3917 execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
3919 jbuild = json_builder_new ();
3920 json_builder_begin_object (jbuild);
3921 json_builder_set_member_name (jbuild, "type");
3922 json_builder_add_string_value (jbuild, "action-done");
3923 json_builder_set_member_name (jbuild, "action-type");
3924 json_builder_add_string_value (jbuild, action->type);
3925 json_builder_set_member_name (jbuild, "execution-duration");
3926 json_builder_add_double_value (jbuild,
3927 ((gdouble) execution_duration / GST_SECOND));
3928 json_builder_end_object (jbuild);
3930 gst_validate_send (json_builder_get_root (jbuild));
3931 g_object_unref (jbuild);
3933 gst_validate_printf (NULL, " -> Action %s done (duration: %" GST_TIME_FORMAT
3934 ")\n", action->type, GST_TIME_ARGS (execution_duration));
3935 action->priv->execution_time = GST_CLOCK_TIME_NONE;
3936 action->priv->state = _execute_sub_action_action (action);
3938 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
3939 GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
3941 execute_next_action (scenario);
3943 gst_object_unref (scenario);
3945 return G_SOURCE_REMOVE;
3948 /* gst_validate_action_set_done:
3949 * @action: The action that is done executing
3951 * Sets @action as "done", meaning that the next action can
3955 gst_validate_action_set_done (GstValidateAction * action)
3958 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) {
3959 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
3963 SCENARIO_LOCK (scenario);
3964 item = g_list_find (scenario->priv->interlaced_actions, action);
3965 scenario->priv->interlaced_actions =
3966 g_list_delete_link (scenario->priv->interlaced_actions, item);
3967 SCENARIO_UNLOCK (scenario);
3968 g_object_unref (scenario);
3972 gst_validate_action_unref (action);
3975 g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
3976 (GSourceFunc) _action_set_done,
3977 gst_mini_object_ref (GST_MINI_OBJECT (action)),
3978 (GDestroyNotify) gst_validate_action_unref);
3982 * gst_validate_action_get_scenario:
3983 * @action: The action for which to retrieve the scenario
3985 * Retrieve the scenario from which @action is executed.
3987 * Returns: (transfer full): The scenario from which the action is being executed.
3989 GstValidateScenario *
3990 gst_validate_action_get_scenario (GstValidateAction * action)
3992 return g_weak_ref_get (&action->priv->scenario);
3996 * gst_validate_register_action_type:
3997 * @type_name: The name of the new action type to add
3998 * @implementer_namespace: The namespace of the implementer of the action type.
3999 * That should always be the name of the GstPlugin as
4000 * retrieved with #gst_plugin_get_name when the action type
4001 * is registered inside a plugin.
4002 * @function: (scope notified): The function to be called to execute the action
4003 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4004 * @description: A description of the new type
4005 * @flags: The #GstValidateActionTypeFlags to set on the new action type
4007 * Register a new action type to the action type system. If the action type already
4008 * exists, it will be overridden by the new definition
4010 * Returns: (transfer none): The newly created action type or the already registered action type
4011 * if it had a higher rank
4013 GstValidateActionType *
4014 gst_validate_register_action_type (const gchar * type_name,
4015 const gchar * implementer_namespace,
4016 GstValidateExecuteAction function,
4017 GstValidateActionParameter * parameters,
4018 const gchar * description, GstValidateActionTypeFlags flags)
4020 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
4021 type_name, GST_RANK_NONE, function, parameters, description,
4024 g_free (type->implementer_namespace);
4025 type->implementer_namespace = g_strdup (implementer_namespace);
4031 _free_action_types (GList * _action_types)
4033 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
4037 * gst_validate_register_action_type_dynamic:
4038 * @plugin: (allow-none): The #GstPlugin that register the action type,
4039 * or NULL for a static element.
4040 * @rank: The ranking of that implementation of the action type called
4041 * @type_name. If an action type has been registered with the same
4042 * name with a higher rank, the new implementation will not be used,
4043 * and the already registered action type is returned.
4044 * If the already registered implementation has a lower rank, the
4045 * new implementation will be used and returned.
4046 * @type_name: The name of the new action type to add
4047 * @function: (scope notified): The function to be called to execute the action
4048 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4049 * @description: A description of the new type
4050 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
4052 * Returns: (transfer none): The newly created action type or the already registered action type
4053 * if it had a higher rank
4055 GstValidateActionType *
4056 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
4057 const gchar * type_name, GstRank rank,
4058 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
4059 const gchar * description, GstValidateActionTypeFlags flags)
4061 GstValidateActionType *tmptype;
4062 GstValidateActionType *type = gst_validate_action_type_new ();
4063 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
4064 gint n_params = is_config ? 0 : 2;
4067 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
4072 type->parameters = g_new0 (GstValidateActionParameter, n_params);
4076 memcpy (type->parameters, parameters,
4077 sizeof (GstValidateActionParameter) * (n_params));
4080 type->prepare = gst_validate_action_default_prepare_func;
4081 type->execute = function;
4082 type->name = g_strdup (type_name);
4084 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
4086 type->implementer_namespace = g_strdup ("none");
4088 type->description = g_strdup (description);
4089 type->flags = flags;
4092 if ((tmptype = _find_action_type (type_name))) {
4093 if (tmptype->rank <= rank) {
4094 action_types = g_list_remove (action_types, tmptype);
4095 type->overriden_type = tmptype;
4097 gst_mini_object_unref (GST_MINI_OBJECT (type));
4103 if (type != tmptype)
4104 action_types = g_list_append (action_types, type);
4107 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
4108 "GstValidatePluginActionTypes");
4110 plugin_action_types = g_list_prepend (plugin_action_types,
4111 gst_mini_object_ref (GST_MINI_OBJECT (type)));
4113 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
4114 plugin_action_types, (GDestroyNotify) _free_action_types);
4120 GstValidateActionType *
4121 gst_validate_get_action_type (const gchar * type_name)
4123 GstValidateActionType *type = _find_action_type (type_name);
4127 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
4133 gst_validate_list_action_types (void)
4135 return action_types;
4139 * gst_validate_print_action_types:
4140 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
4141 * @num_wanted_types: Length of @wanted_types
4143 * Prints the action types details wanted in @wanted_types
4145 * Returns: True if all types could be printed
4148 gst_validate_print_action_types (const gchar ** wanted_types,
4149 gint num_wanted_types)
4154 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
4155 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
4156 gboolean print = FALSE;
4158 if (num_wanted_types) {
4161 for (n = 0; n < num_wanted_types; n++) {
4162 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
4163 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
4174 if (print && num_wanted_types) {
4175 gst_validate_printf (atype, "\n");
4178 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
4182 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
4183 atype->implementer_namespace, atype->name, desc);
4188 if (num_wanted_types && num_wanted_types > nfound) {
4196 * gst_validate_scenario_get_actions:
4197 * @scenario: The scenario to retrieve remaining actions for
4199 * Get remaining actions from @scenario.
4201 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
4204 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
4207 gboolean main_context_acquired;
4209 main_context_acquired = g_main_context_acquire (g_main_context_default ());
4210 g_return_val_if_fail (main_context_acquired, NULL);
4212 ret = g_list_copy_deep (scenario->priv->actions,
4213 (GCopyFunc) gst_mini_object_ref, NULL);
4215 g_main_context_release (g_main_context_default ());
4221 * gst_validate_scenario_get_target_state:
4222 * @scenario: The scenario to retrieve the current target state for
4224 * Get current target state from @scenario.
4226 * Returns: Current target state.
4229 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
4231 return scenario->priv->target_state;
4235 init_scenarios (void)
4238 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
4239 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
4241 _gst_validate_action_type = gst_validate_action_get_type ();
4242 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
4245 REGISTER_ACTION_TYPE ("description", NULL,
4246 ((GstValidateActionParameter []) {
4249 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
4252 .possible_variables = NULL,
4253 .def = "'Nothing'"},
4255 .name = "is-config",
4256 .description = "Whether the scenario is a config only scenario",
4259 .possible_variables = NULL,
4263 .name = "handles-states",
4264 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
4265 "in that case the application should not set the state of the pipeline to anything\n"
4266 "and the scenario action will be executed from the beginning",
4269 .possible_variables = NULL,
4273 .description = "Whether the scenario executes seek actions or not",
4276 .possible_variables = NULL,
4280 .name = "reverse-playback",
4281 .description = "Whether the scenario plays the stream backward",
4284 .possible_variables = NULL,
4288 .name = "need-clock-sync",
4289 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
4290 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
4293 .possible_variables = NULL,
4297 .name = "min-media-duration",
4298 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
4302 .possible_variables = NULL,
4306 .name = "min-audio-track",
4307 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
4308 "for the scenario to be usable",
4311 .possible_variables = NULL,
4315 .name = "min-video-track",
4316 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
4317 "for the scenario to be usable",
4320 .possible_variables = NULL,
4325 .description = "Lets the user know the time the scenario needs to be fully executed",
4327 .types = "double, int",
4328 .possible_variables = NULL,
4329 .def = "infinite (GST_CLOCK_TIME_NONE)"
4332 .name = "pipeline-name",
4333 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
4334 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
4337 .possible_variables = NULL,
4341 .name = "max-latency",
4342 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
4343 "It can be overriden using core configuration, like for example by defining the "
4344 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
4346 .types = "double, int",
4347 .possible_variables = NULL,
4348 .def = "infinite (GST_CLOCK_TIME_NONE)"
4351 .name = "max-dropped",
4352 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
4353 "It can be overriden using core configuration, like for example by defining the "
4354 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
4357 .possible_variables = NULL,
4358 .def = "infinite (-1)"
4362 "Allows to describe the scenario in various ways",
4363 GST_VALIDATE_ACTION_TYPE_CONFIG);
4365 REGISTER_ACTION_TYPE ("seek", _execute_seek,
4366 ((GstValidateActionParameter []) {
4369 .description = "The starting value of the seek",
4371 .types = "double or string",
4372 .possible_variables = "position: The current position in the stream\n"
4373 "duration: The duration of the stream",
4378 .description = "The GstSeekFlags to use",
4380 .types = "string describing the GstSeekFlags to set",
4385 .description = "The rate value of the seek",
4388 .possible_variables = NULL,
4392 .name = "start_type",
4393 .description = "The GstSeekType to use for the start of the seek, in:\n"
4394 " [none, set, end]",
4397 .possible_variables = NULL,
4401 .name = "stop_type",
4402 .description = "The GstSeekType to use for the stop of the seek, in:\n"
4403 " [none, set, end]",
4406 .possible_variables = NULL,
4409 {"stop", "The stop value of the seek", FALSE, "double or ",
4410 "position: The current position in the stream\n"
4411 "duration: The duration of the stream"
4412 "GST_CLOCK_TIME_NONE",
4416 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
4417 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
4418 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
4419 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
4422 REGISTER_ACTION_TYPE ("pause", _execute_pause,
4423 ((GstValidateActionParameter []) {
4426 .description = "The duration during which the stream will be paused",
4429 .possible_variables = NULL,
4434 "Sets pipeline to PAUSED. You can add a 'duration'\n"
4435 "parameter so the pipeline goes back to playing after that duration\n"
4437 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
4439 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
4440 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
4442 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
4443 "Stops the execution of the scenario. It will post a 'request-state'"
4444 " message on the bus with NULL as a requested state"
4445 " and the application is responsible for stopping itself."
4446 " If you override that action type, make sure to link up.",
4447 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4449 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
4450 "Sends an EOS event to the pipeline",
4451 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4453 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
4454 ((GstValidateActionParameter []) {
4457 .description = "Selects which track type to change (can be 'audio', 'video',"
4461 .possible_variables = NULL,
4466 .description = "Selects which track of this type to use: it can be either a number,\n"
4467 "which will be the Nth track of the given type, or a number with a '+' or\n"
4468 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
4469 "'-1' means 'previous track')",
4471 .types = "string: to switch track relatively\n"
4472 "int: To use the actual index to use",
4473 .possible_variables = NULL,
4478 "The 'switch-track' command can be used to switch tracks."
4479 , GST_VALIDATE_ACTION_TYPE_NONE);
4481 REGISTER_ACTION_TYPE ("wait", _execute_wait,
4482 ((GstValidateActionParameter []) {
4485 .description = "the duration while no other action will be executed",
4489 .name = "target-element-name",
4490 .description = "The name of the GstElement to wait @signal-name on.",
4495 .name = "signal-name",
4496 .description = "The name of the signal to wait for on @target-element-name",
4502 .name = "message-type",
4503 .description = "The name of the message type to wait for (on @target-element-name"
4511 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
4512 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
4514 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
4515 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
4516 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
4517 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
4518 GST_VALIDATE_ACTION_TYPE_NONE);
4520 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank,
4521 ((GstValidateActionParameter []) {
4524 .description = "The name of a GstFeature or GstPlugin",
4530 .description = "The GstRank to set on @name",
4532 .types = "string, int",
4536 "Changes the ranking of a particular plugin feature(s)",
4537 GST_VALIDATE_ACTION_TYPE_CONFIG);
4539 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank,
4540 ((GstValidateActionParameter []) {
4542 .name = "feature-name",
4543 .description = "The name of a GstFeature",
4549 .description = "The GstRank to set on @feature-name",
4551 .types = "string, int",
4555 "Changes the ranking of a particular plugin feature",
4556 GST_VALIDATE_ACTION_TYPE_CONFIG);
4558 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
4559 ((GstValidateActionParameter []) {
4562 .description = "A GstState as a string, should be in: \n"
4563 " * ['null', 'ready', 'paused', 'playing']",
4569 "Changes the state of the pipeline to any GstState",
4570 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
4572 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
4573 ((GstValidateActionParameter []) {
4576 "Define vars to be used in other actions.\n"
4577 "For example you can define vars for buffer checksum"
4578 " to be used in the \"check-last-sample\" action type as follow:\n\n"
4580 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
4581 " check-last-sample, checksum=frame1\n"
4583 GST_VALIDATE_ACTION_TYPE_NONE);
4585 REGISTER_ACTION_TYPE ("set-property", _execute_set_property,
4586 ((GstValidateActionParameter []) {
4588 .name = "target-element-name",
4589 .description = "The name of the GstElement to set a property on",
4595 .name = "target-element-factory-name",
4596 .description = "The name factory for which to set a property on built elements",
4602 .name = "target-element-klass",
4603 .description = "The klass of the GstElements to set a property on",
4609 .name = "property-name",
4610 .description = "The name of the property to set on @target-element-name",
4616 .name = "property-value",
4617 .description = "The value of @property-name to be set on the element",
4619 .types = "The same type of @property-name",
4624 "Sets a property of an element or klass of elements in the pipeline.\n"
4625 "Besides property-name and value, either 'target-element-name' or\n"
4626 "'target-element-klass' needs to be defined",
4627 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
4628 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
4629 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
4631 REGISTER_ACTION_TYPE ("set-debug-threshold",
4632 _execute_set_debug_threshold,
4633 ((GstValidateActionParameter [])
4636 .name = "debug-threshold",
4637 .description = "String defining debug threshold\n"
4638 "See gst_debug_set_threshold_from_string",
4643 "Sets the debug level to be used, same format as\n"
4644 "setting the GST_DEBUG env variable",
4645 GST_VALIDATE_ACTION_TYPE_NONE);
4647 REGISTER_ACTION_TYPE ("include",
4648 NULL, /* This is handled directly when loading a scenario */
4649 ((GstValidateActionParameter [])
4653 .description = "The location of the sub scenario to include.",
4658 "Include a sub scenario file.",
4659 GST_VALIDATE_ACTION_TYPE_NONE);
4661 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
4662 ((GstValidateActionParameter [])
4665 .name = "target-element-name",
4666 .description = "The name of the GstElement to emit a signal on",
4671 .name = "signal-name",
4672 .description = "The name of the signal to emit on @target-element-name",
4679 "Emits a signal to an element in the pipeline",
4680 GST_VALIDATE_ACTION_TYPE_NONE);
4682 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
4683 ((GstValidateActionParameter [])
4686 .name = "plugin-name",
4687 .description = "The name of the GstPlugin to disable",
4692 .name = "as-config",
4693 .description = "Execute action as a config action (meaning when loading the scenario)",
4700 "Disables a GstPlugin",
4701 GST_VALIDATE_ACTION_TYPE_NONE);
4703 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
4704 ((GstValidateActionParameter []) {
4706 .name = "sink-name",
4707 .description = "The name of the sink element to check sample on.",
4713 .name = "sink-factory-name",
4714 .description = "The name of the factory of the sink element to check sample on.",
4720 .name = "sinkpad-caps",
4721 .description = "The caps (as string) of the sink to check.",
4728 .description = "The reference checksum of the buffer.",
4735 "Checks the last-sample checksum on declared Sink element."
4736 " This allows checking the checksum of a buffer after a 'seek' or after a GESTimeline 'commit'"
4738 GST_VALIDATE_ACTION_TYPE_INTERLACED);
4742 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
4743 const gchar *action_typename;
4744 GstStructure *plug_conf = (GstStructure *) tmp->data;
4746 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
4747 GstValidateAction *action;
4748 GstValidateActionType *atype = _find_action_type (action_typename);
4751 g_error ("[CONFIG ERROR] Action type %s not found", action_typename);
4757 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
4758 GST_INFO ("Action type %s from configuration files"
4759 " is handled.", action_typename);
4763 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
4764 !(_action_type_has_parameter (atype, "as-config"))) {
4765 g_error ("[CONFIG ERROR] Action '%s' is not a config action",
4771 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
4772 gst_structure_set_name (plug_conf, action_typename);
4774 action = gst_validate_action_new (NULL, atype);
4775 _fill_action (NULL, action, plug_conf, FALSE);
4781 gst_validate_scenario_deinit (void)
4783 _free_action_types (action_types);
4784 action_types = NULL;