1 /* GStreamer
2 *
3 * Copyright (C) 2013 Collabora Ltd.
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
5 * Copyright (C) 2018-2019 Igalia S.L
6
7 *
8 * gst-validate-scenario.c - Validate Scenario class
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
24 */
25 /**
26 * SECTION:gst-validate-scenario
27 * @short_description: A GstValidateScenario represents a set of actions to be executed on a pipeline.
28 *
29 * A #GstValidateScenario represents the scenario that will be executed on a #GstPipeline.
30 * It is basically an ordered list of #GstValidateAction that will be executed during the
31 * execution of the pipeline.
32 *
33 * Possible configurations (see #GST_VALIDATE_CONFIG):
34 * * scenario-action-execution-interval: Sets the interval in
35 * milliseconds (1/1000ths of a second), between which actions
36 * will be executed, setting it to 0 means "execute in idle".
37 * The default value is 10ms.
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <gst/gst.h>
45 #include <gio/gio.h>
46 #include <string.h>
47 #include <errno.h>
48
49 #include "gst-validate-internal.h"
50 #include "gst-validate-scenario.h"
51 #include "gst-validate-reporter.h"
52 #include "gst-validate-report.h"
53 #include "gst-validate-utils.h"
54 #include "validate.h"
55 #include <gst/validate/gst-validate-override.h>
56 #include <gst/validate/gst-validate-override-registry.h>
57 #include <gst/validate/gst-validate-pipeline-monitor.h>
58
59 #define GST_VALIDATE_SCENARIO_SUFFIX ".scenario"
60 #define GST_VALIDATE_SCENARIO_DIRECTORY "scenarios"
61
62 #define DEFAULT_SEEK_TOLERANCE (1 * GST_MSECOND) /* tolerance seek interval
63 TODO make it overridable */
64
65 GST_DEBUG_CATEGORY_STATIC (gst_validate_scenario_debug);
66 #undef GST_CAT_DEFAULT
67 #define GST_CAT_DEFAULT gst_validate_scenario_debug
68
69 #define REGISTER_ACTION_TYPE(_tname, _function, _params, _desc, _is_config) G_STMT_START { \
70 gst_validate_register_action_type ((_tname), "core", (_function), (_params), (_desc), (_is_config)); \
71 } G_STMT_END
72
73 #define ACTION_EXPECTED_STREAM_QUARK g_quark_from_static_string ("ACTION_EXPECTED_STREAM_QUARK")
74
75 #define SCENARIO_LOCK(scenario) (g_mutex_lock(&scenario->priv->lock))
76 #define SCENARIO_UNLOCK(scenario) (g_mutex_unlock(&scenario->priv->lock))
77
78 #define DECLARE_AND_GET_PIPELINE(s,a) \
79 GstElement * pipeline = gst_validate_scenario_get_pipeline (s); \
80 if (pipeline == NULL) { \
81 GST_VALIDATE_REPORT (s, SCENARIO_ACTION_EXECUTION_ERROR, \
82 "Can't execute a '%s' action after the pipeline " \
83 "has been destroyed.", a->type); \
84 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \
85 }
86
87 enum
88 {
89 PROP_0,
90 PROP_RUNNER,
91 PROP_HANDLES_STATE,
92 PROP_EXECUTE_ON_IDLE,
93 PROP_LAST
94 };
95
96 enum
97 {
98 DONE,
99 LAST_SIGNAL
100 };
101
102 static guint scenario_signals[LAST_SIGNAL] = { 0 };
103
104 static GList *action_types = NULL;
105 static void gst_validate_scenario_dispose (GObject * object);
106 static void gst_validate_scenario_finalize (GObject * object);
107 static GstValidateActionType *_find_action_type (const gchar * type_name);
108 static GstValidateExecuteActionReturn
109 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
110 GstStructure * structure, gboolean add_to_lists);
111 static gboolean _action_set_done (GstValidateAction * action);
112
113 /* GstValidateScenario is not really thread safe and
114 * everything should be done from the thread GstValidate
115 * was inited from, unless stated otherwise.
116 */
117 struct _GstValidateScenarioPrivate
118 {
119 GstBus *bus;
120 GstValidateRunner *runner;
121 gboolean execute_on_idle;
122
123 GMutex lock;
124
125 GList *actions;
126 GList *interlaced_actions; /* MT safe. Protected with SCENARIO_LOCK */
127 GList *on_addition_actions; /* MT safe. Protected with SCENARIO_LOCK */
128
129 gboolean needs_playback_parsing;
130
131 /* List of action that need parsing when reaching ASYNC_DONE
132 * most probably to be able to query duration */
133
134 GstEvent *last_seek;
135 GstSeekFlags seek_flags;
136 GstClockTime segment_start;
137 GstClockTime segment_stop;
138 GstClockTime seek_pos_tol;
139
140 /* If we seeked in paused the position should be exactly what
141 * the seek value was (if accurate) */
142 gboolean seeked_in_pause;
143
144 guint num_actions;
145
146 gboolean handles_state;
147
148 guint execute_actions_source_id; /* MT safe. Protect with SCENARIO_LOCK */
149 guint wait_id;
150 guint signal_handler_id;
151 guint action_execution_interval;
152
153 /* Name of message the wait action is waiting for */
154 const gchar *message_type;
155
156 gboolean buffering;
157
158 gboolean got_eos;
159 gboolean changing_state;
160 gboolean needs_async_done;
161 gboolean ignore_eos;
162 GstState target_state;
163
164 GList *overrides;
165
166 gchar *pipeline_name;
167 GstClockTime max_latency;
168 gint dropped;
169 gint max_dropped;
170
171 /* 'switch-track action' currently waiting for
172 * GST_MESSAGE_STREAMS_SELECTED to be completed. */
173 GstValidateAction *pending_switch_track;
174
175 GstStructure *vars;
176
177 GWeakRef ref_pipeline;
178 };
179
180 typedef struct KeyFileGroupName
181 {
182 GKeyFile *kf;
183 gchar *group_name;
184 } KeyFileGroupName;
185
186 static GstValidateInterceptionReturn
gst_validate_scenario_intercept_report(GstValidateReporter * reporter,GstValidateReport * report)187 gst_validate_scenario_intercept_report (GstValidateReporter * reporter,
188 GstValidateReport * report)
189 {
190 GList *tmp;
191
192 for (tmp = GST_VALIDATE_SCENARIO (reporter)->priv->overrides; tmp;
193 tmp = tmp->next) {
194 GstValidateOverride *override = (GstValidateOverride *) tmp->data;
195 report->level =
196 gst_validate_override_get_severity (override,
197 gst_validate_issue_get_id (report->issue), report->level);
198 }
199
200 return GST_VALIDATE_REPORTER_REPORT;
201 }
202
203 /**
204 * gst_validate_scenario_get_pipeline:
205 * @scenario: The scenario to retrieve a pipeline from
206 *
207 * Returns: (transfer full): The #GstPipeline the scenario is running
208 * against
209 */
210 GstElement *
gst_validate_scenario_get_pipeline(GstValidateScenario * scenario)211 gst_validate_scenario_get_pipeline (GstValidateScenario * scenario)
212 {
213 return g_weak_ref_get (&scenario->priv->ref_pipeline);
214 }
215
216 static GstPipeline *
_get_pipeline(GstValidateReporter * reporter)217 _get_pipeline (GstValidateReporter * reporter)
218 {
219 return
220 GST_PIPELINE_CAST (gst_validate_scenario_get_pipeline
221 (GST_VALIDATE_SCENARIO (reporter)));
222 }
223
224 static void
_reporter_iface_init(GstValidateReporterInterface * iface)225 _reporter_iface_init (GstValidateReporterInterface * iface)
226 {
227 iface->intercept_report = gst_validate_scenario_intercept_report;
228 iface->get_pipeline = _get_pipeline;
229 }
230
231 G_DEFINE_TYPE_WITH_CODE (GstValidateScenario, gst_validate_scenario,
232 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateScenario)
233 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, _reporter_iface_init));
234
235 /* GstValidateAction implementation */
236 static GType _gst_validate_action_type = 0;
237
238 struct _GstValidateActionPrivate
239 {
240 GstStructure *main_structure;
241 GstValidateExecuteActionReturn state; /* Actually ActionState */
242 gboolean printed;
243 gboolean executing_last_subaction;
244 gboolean optional;
245
246 GstClockTime execution_time;
247 GstClockTime timeout;
248
249 GWeakRef scenario;
250 gboolean needs_playback_parsing;
251 gboolean pending_set_done;
252 };
253
254 static JsonNode *
gst_validate_action_serialize(GstValidateAction * action)255 gst_validate_action_serialize (GstValidateAction * action)
256 {
257 JsonNode *node = json_node_alloc ();
258 JsonObject *jreport = json_object_new ();
259 gchar *action_args = gst_structure_to_string (action->structure);
260
261 json_object_set_string_member (jreport, "type", "action");
262 json_object_set_string_member (jreport, "action-type", action->type);
263 json_object_set_int_member (jreport, "playback-time",
264 (gint64) action->playback_time);
265 json_object_set_string_member (jreport, "args", action_args);
266 g_free (action_args);
267
268 node = json_node_init_object (node, jreport);
269 json_object_unref (jreport);
270
271 return node;
272 }
273
274 GType
gst_validate_action_get_type(void)275 gst_validate_action_get_type (void)
276 {
277 if (_gst_validate_action_type == 0) {
278 _gst_validate_action_type =
279 g_boxed_type_register_static (g_intern_static_string
280 ("GstValidateAction"), (GBoxedCopyFunc) gst_mini_object_ref,
281 (GBoxedFreeFunc) gst_mini_object_unref);
282
283 json_boxed_register_serialize_func (_gst_validate_action_type,
284 JSON_NODE_OBJECT,
285 (JsonBoxedSerializeFunc) gst_validate_action_serialize);
286 }
287
288 return _gst_validate_action_type;
289 }
290
291 static gboolean execute_next_action (GstValidateScenario * scenario);
292 static gboolean
293 gst_validate_scenario_load (GstValidateScenario * scenario,
294 const gchar * scenario_name, const gchar * relative_scenario);
295
296 static GstValidateAction *
_action_copy(GstValidateAction * act)297 _action_copy (GstValidateAction * act)
298 {
299 GstValidateScenario *scenario = gst_validate_action_get_scenario (act);
300 GstValidateAction *copy = gst_validate_action_new (scenario,
301 _find_action_type (act->type), NULL, FALSE);
302
303 gst_object_unref (scenario);
304
305 if (act->structure) {
306 copy->structure = gst_structure_copy (act->structure);
307 copy->type = gst_structure_get_name (copy->structure);
308 if (!(act->name = gst_structure_get_string (copy->structure, "name")))
309 act->name = "";
310 }
311
312 if (act->priv->main_structure)
313 copy->priv->main_structure = gst_structure_copy (act->priv->main_structure);
314
315 copy->action_number = act->action_number;
316 copy->playback_time = act->playback_time;
317 copy->priv->timeout = act->priv->timeout;
318
319 return copy;
320 }
321
322 static void
_action_free(GstValidateAction * action)323 _action_free (GstValidateAction * action)
324 {
325 if (action->structure)
326 gst_structure_free (action->structure);
327
328 if (action->priv->main_structure)
329 gst_structure_free (action->priv->main_structure);
330
331 g_weak_ref_clear (&action->priv->scenario);
332
333 g_slice_free (GstValidateActionPrivate, action->priv);
334 g_slice_free (GstValidateAction, action);
335 }
336
337 static void
gst_validate_action_init(GstValidateAction * action)338 gst_validate_action_init (GstValidateAction * action)
339 {
340 gst_mini_object_init (((GstMiniObject *) action), 0,
341 _gst_validate_action_type, (GstMiniObjectCopyFunction) _action_copy, NULL,
342 (GstMiniObjectFreeFunction) _action_free);
343
344 action->priv = g_slice_new0 (GstValidateActionPrivate);
345
346 g_weak_ref_init (&action->priv->scenario, NULL);
347 }
348
349 GstValidateAction *
gst_validate_action_ref(GstValidateAction * action)350 gst_validate_action_ref (GstValidateAction * action)
351 {
352 return (GstValidateAction *) gst_mini_object_ref (GST_MINI_OBJECT (action));
353 }
354
355 void
gst_validate_action_unref(GstValidateAction * action)356 gst_validate_action_unref (GstValidateAction * action)
357 {
358 gst_mini_object_unref (GST_MINI_OBJECT (action));
359 }
360
361 GstValidateAction *
gst_validate_action_new(GstValidateScenario * scenario,GstValidateActionType * action_type,GstStructure * structure,gboolean add_to_lists)362 gst_validate_action_new (GstValidateScenario * scenario,
363 GstValidateActionType * action_type, GstStructure * structure,
364 gboolean add_to_lists)
365 {
366 GstValidateAction *action = g_slice_new0 (GstValidateAction);
367
368 gst_validate_action_init (action);
369 action->playback_time = GST_CLOCK_TIME_NONE;
370 action->priv->timeout = GST_CLOCK_TIME_NONE;
371 action->type = action_type->name;
372 action->repeat = -1;
373
374 g_weak_ref_set (&action->priv->scenario, scenario);
375 if (structure)
376 action->priv->state =
377 _fill_action (scenario, action, structure, add_to_lists);
378
379 return action;
380 }
381
382 gboolean
_action_check_and_set_printed(GstValidateAction * action)383 _action_check_and_set_printed (GstValidateAction * action)
384 {
385 if (action->priv->printed == FALSE) {
386 gst_validate_send (json_boxed_serialize (GST_MINI_OBJECT_TYPE
387 (action), action));
388
389 action->priv->printed = TRUE;
390
391 return FALSE;
392 }
393
394 return TRUE;
395 }
396
397 gboolean
gst_validate_action_is_subaction(GstValidateAction * action)398 gst_validate_action_is_subaction (GstValidateAction * action)
399 {
400 return !gst_structure_is_equal (action->structure,
401 action->priv->main_structure);
402 }
403
404 /* GstValidateActionType implementation */
405 GType _gst_validate_action_type_type;
406 GST_DEFINE_MINI_OBJECT_TYPE (GstValidateActionType, gst_validate_action_type);
407 static GstValidateActionType *gst_validate_action_type_new (void);
408
409 static void
_action_type_free(GstValidateActionType * type)410 _action_type_free (GstValidateActionType * type)
411 {
412 g_free (type->parameters);
413 g_free (type->description);
414 g_free (type->name);
415 g_free (type->implementer_namespace);
416
417 if (type->overriden_type)
418 gst_mini_object_unref (GST_MINI_OBJECT (type->overriden_type));
419
420 g_slice_free (GstValidateActionType, type);
421 }
422
423 static void
gst_validate_action_type_init(GstValidateActionType * type)424 gst_validate_action_type_init (GstValidateActionType * type)
425 {
426 gst_mini_object_init ((GstMiniObject *) type, 0,
427 _gst_validate_action_type_type, NULL, NULL,
428 (GstMiniObjectFreeFunction) _action_type_free);
429 }
430
431 GstValidateActionType *
gst_validate_action_type_new(void)432 gst_validate_action_type_new (void)
433 {
434 GstValidateActionType *type = g_slice_new0 (GstValidateActionType);
435
436 gst_validate_action_type_init (type);
437
438 /* action types are never freed */
439 GST_MINI_OBJECT_FLAG_SET (type, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
440 return type;
441 }
442
443 static GstValidateActionType *
_find_action_type(const gchar * type_name)444 _find_action_type (const gchar * type_name)
445 {
446 GList *tmp;
447
448 for (tmp = action_types; tmp; tmp = tmp->next) {
449 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
450 if (g_strcmp0 (atype->name, type_name) == 0)
451 return atype;
452 }
453
454 return NULL;
455 }
456
457 static void
_update_well_known_vars(GstValidateScenario * scenario)458 _update_well_known_vars (GstValidateScenario * scenario)
459 {
460 gint64 duration, position;
461 gdouble dduration, dposition;
462 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
463
464 gst_structure_remove_fields (scenario->priv->vars, "position", "duration",
465 NULL);
466
467 if (!pipeline)
468 return;
469
470 if (!gst_element_query_duration (pipeline, GST_FORMAT_TIME, &duration) ||
471 !GST_CLOCK_TIME_IS_VALID (duration)) {
472 GstValidateMonitor *monitor =
473 (GstValidateMonitor *) (g_object_get_data ((GObject *)
474 pipeline, "validate-monitor"));
475 GST_INFO_OBJECT (scenario,
476 "Could not query duration. Trying to get duration from media-info");
477 if (monitor && monitor->media_descriptor)
478 duration =
479 gst_validate_media_descriptor_get_duration
480 (monitor->media_descriptor);
481 }
482
483 if (!GST_CLOCK_TIME_IS_VALID (duration))
484 dduration = G_MAXDOUBLE;
485 else
486 dduration = ((double) duration / GST_SECOND);
487
488 gst_structure_set (scenario->priv->vars, "duration", G_TYPE_DOUBLE, dduration,
489 NULL);
490 if (gst_element_query_position (pipeline, GST_FORMAT_TIME, &position)) {
491
492 if (!GST_CLOCK_TIME_IS_VALID (position))
493 dposition = G_MAXDOUBLE;
494 else
495 dposition = ((double) position / GST_SECOND);
496
497 gst_structure_set (scenario->priv->vars, "position", G_TYPE_DOUBLE,
498 dposition, NULL);
499 } else {
500 GST_WARNING_OBJECT (scenario, "Could not query position");
501 }
502 }
503
504 static gchar *
_replace_variables_in_string(GstValidateScenario * scenario,GstValidateAction * action,const gchar * in_string)505 _replace_variables_in_string (GstValidateScenario * scenario,
506 GstValidateAction * action, const gchar * in_string)
507 {
508 GRegex *regex;
509 gint varname_len;
510 GMatchInfo *match_info;
511 const gchar *var_value;
512 gchar *tmpstring, *string = g_strdup (in_string);
513
514 _update_well_known_vars (scenario);
515 regex = g_regex_new ("\\$\\((\\w+)\\)", 0, 0, NULL);
516 g_regex_match (regex, string, 0, &match_info);
517 while (g_match_info_matches (match_info)) {
518 GRegex *replace_regex;
519 gchar *tmp, *varname, *pvarname = g_match_info_fetch (match_info, 0);
520
521 varname_len = strlen (pvarname);
522 varname = g_malloc (sizeof (gchar) * varname_len - 3);
523 strncpy (varname, &pvarname[2], varname_len - 3);
524 varname[varname_len - 3] = '\0';
525
526 if (gst_structure_has_field_typed (scenario->priv->vars, varname,
527 G_TYPE_DOUBLE)) {
528 var_value = varname;
529 } else {
530 var_value = gst_structure_get_string (scenario->priv->vars, varname);
531 if (!var_value) {
532 g_error ("Trying to use undefined variable : %s (%s)", varname,
533 gst_structure_to_string (scenario->priv->vars));
534
535 return NULL;
536 }
537 }
538
539 tmp = g_strdup_printf ("\\$\\(%s\\)", varname);
540 replace_regex = g_regex_new (tmp, 0, 0, NULL);
541 tmpstring = string;
542 string = g_regex_replace (replace_regex, string, -1, 0, var_value, 0, NULL);
543
544 GST_INFO_OBJECT (action, "Setting variable %s to %s", varname, var_value);
545 g_free (tmpstring);
546 g_regex_unref (replace_regex);
547 g_free (pvarname);
548
549 g_match_info_next (match_info, NULL);
550 }
551 g_match_info_free (match_info);
552 g_regex_unref (regex);
553
554 return string;
555 }
556
557 static gboolean
_set_variable_func(const gchar * name,double * value,gpointer user_data)558 _set_variable_func (const gchar * name, double *value, gpointer user_data)
559 {
560 GstValidateScenario *scenario = (GstValidateScenario *) user_data;
561
562 if (!gst_structure_get_double (scenario->priv->vars, name, value))
563 return FALSE;
564
565 return TRUE;
566 }
567
568 /* Check that @list doesn't contain any non-optional actions */
569 static gboolean
actions_list_is_done(GList * list)570 actions_list_is_done (GList * list)
571 {
572 GList *l;
573
574 for (l = list; l != NULL; l = g_list_next (l)) {
575 GstValidateAction *action = l->data;
576
577 if (!action->priv->optional)
578 return FALSE;
579 }
580
581 return TRUE;
582 }
583
584 static void
_check_scenario_is_done(GstValidateScenario * scenario)585 _check_scenario_is_done (GstValidateScenario * scenario)
586 {
587 SCENARIO_LOCK (scenario);
588 if (actions_list_is_done (scenario->priv->actions) &&
589 actions_list_is_done (scenario->priv->interlaced_actions) &&
590 actions_list_is_done (scenario->priv->on_addition_actions)) {
591 SCENARIO_UNLOCK (scenario);
592
593 g_signal_emit (scenario, scenario_signals[DONE], 0);
594 } else {
595 SCENARIO_UNLOCK (scenario);
596 }
597 }
598
599 /**
600 * gst_validate_action_get_clocktime:
601 * @scenario: The #GstValidateScenario from which to get a time
602 * for a parameter of an action
603 * @action: The action from which to retrieve the time for @name
604 * parameter.
605 * @name: The name of the parameter for which to retrieve a time
606 * @retval: (out): The return value for the wanted time
607 *
608 *
609 * Get a time value for the @name parameter of an action. This
610 * method should be called to retrieve and compute a timed value of a given
611 * action. It will first try to retrieve the value as a double,
612 * then get it as a string and execute any formula taking into account
613 * the 'position' and 'duration' variables. And it will always convert that
614 * value to a GstClockTime.
615 *
616 * Returns: %TRUE if the time value could be retrieved/computed or %FALSE otherwise
617 */
618 gboolean
gst_validate_action_get_clocktime(GstValidateScenario * scenario,GstValidateAction * action,const gchar * name,GstClockTime * retval)619 gst_validate_action_get_clocktime (GstValidateScenario * scenario,
620 GstValidateAction * action, const gchar * name, GstClockTime * retval)
621 {
622 if (!gst_validate_utils_get_clocktime (action->structure, name, retval)) {
623 gdouble val;
624 gchar *error = NULL, *strval;
625 const gchar *tmpvalue = gst_structure_get_string (action->structure, name);
626
627 if (!tmpvalue) {
628 GST_INFO_OBJECT (scenario, "Could not find %s (%" GST_PTR_FORMAT ")",
629 name, action->structure);
630 return -1;
631 }
632
633 strval = _replace_variables_in_string (scenario, action, tmpvalue);
634 if (!strval)
635 return FALSE;
636
637 val =
638 gst_validate_utils_parse_expression (strval, _set_variable_func,
639 scenario, &error);
640 if (error) {
641 GST_WARNING ("Error while parsing %s: %s (%" GST_PTR_FORMAT ")",
642 strval, error, scenario->priv->vars);
643 g_free (error);
644 g_free (strval);
645
646 return FALSE;
647 } else if (val == -1.0) {
648 *retval = GST_CLOCK_TIME_NONE;
649 } else {
650 *retval = val * GST_SECOND;
651 *retval = GST_ROUND_UP_4 (*retval);
652 }
653 gst_structure_set (action->structure, name, G_TYPE_UINT64, *retval, NULL);
654 g_free (strval);
655
656 return TRUE;
657 }
658
659 return TRUE;
660 }
661
662 /**
663 * gst_validate_scenario_execute_seek:
664 * @scenario: The #GstValidateScenario for which to execute a seek action
665 * @action: The seek action to execute
666 * @rate: The playback rate of the seek
667 * @format: The #GstFormat of the seek
668 * @flags: The #GstSeekFlags of the seek
669 * @start_type: The #GstSeekType of the start value of the seek
670 * @start: The start time of the seek
671 * @stop_type: The #GstSeekType of the stop value of the seek
672 * @stop: The stop time of the seek
673 *
674 * Executes a seek event on the scenario's pipeline. You should always use
675 * this method when you want to execute a seek inside a new action type
676 * so that the scenario state is updated taking into account that seek.
677 *
678 * For more information you should have a look at #gst_event_new_seek
679 *
680 * Returns: %TRUE if the seek could be executed, %FALSE otherwise
681 */
682 GstValidateExecuteActionReturn
gst_validate_scenario_execute_seek(GstValidateScenario * scenario,GstValidateAction * action,gdouble rate,GstFormat format,GstSeekFlags flags,GstSeekType start_type,GstClockTime start,GstSeekType stop_type,GstClockTime stop)683 gst_validate_scenario_execute_seek (GstValidateScenario * scenario,
684 GstValidateAction * action, gdouble rate, GstFormat format,
685 GstSeekFlags flags, GstSeekType start_type, GstClockTime start,
686 GstSeekType stop_type, GstClockTime stop)
687 {
688 GstEvent *seek;
689
690 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
691 GstValidateScenarioPrivate *priv = scenario->priv;
692 DECLARE_AND_GET_PIPELINE (scenario, action);
693
694 seek = gst_event_new_seek (rate, format, flags, start_type, start,
695 stop_type, stop);
696
697 gst_event_ref (seek);
698 if (gst_element_send_event (pipeline, seek)) {
699 gst_event_replace (&priv->last_seek, seek);
700 priv->seek_flags = flags;
701 } else {
702 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_NOT_HANDLED,
703 "Could not execute seek: '(position %" GST_TIME_FORMAT
704 "), %s (num %u, missing repeat: %i), seeking to: %" GST_TIME_FORMAT
705 " stop: %" GST_TIME_FORMAT " Rate %lf'",
706 GST_TIME_ARGS (action->playback_time), action->name,
707 action->action_number, action->repeat, GST_TIME_ARGS (start),
708 GST_TIME_ARGS (stop), rate);
709 ret = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
710 }
711 gst_event_unref (seek);
712 gst_object_unref (pipeline);
713
714 return ret;
715 }
716
717 static gint
_execute_seek(GstValidateScenario * scenario,GstValidateAction * action)718 _execute_seek (GstValidateScenario * scenario, GstValidateAction * action)
719 {
720 const char *str_format, *str_flags, *str_start_type, *str_stop_type;
721
722 gdouble rate = 1.0;
723 guint format = GST_FORMAT_TIME;
724 GstSeekFlags flags = 0;
725 guint start_type = GST_SEEK_TYPE_SET;
726 GstClockTime start;
727 guint stop_type = GST_SEEK_TYPE_SET;
728 GstClockTime stop = GST_CLOCK_TIME_NONE;
729
730 if (!gst_validate_action_get_clocktime (scenario, action, "start", &start))
731 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
732
733 gst_structure_get_double (action->structure, "rate", &rate);
734 if ((str_format = gst_structure_get_string (action->structure, "format")))
735 gst_validate_utils_enum_from_str (GST_TYPE_FORMAT, str_format, &format);
736
737 if ((str_start_type =
738 gst_structure_get_string (action->structure, "start_type")))
739 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_start_type,
740 &start_type);
741
742 if ((str_stop_type =
743 gst_structure_get_string (action->structure, "stop_type")))
744 gst_validate_utils_enum_from_str (GST_TYPE_SEEK_TYPE, str_stop_type,
745 &stop_type);
746
747 if ((str_flags = gst_structure_get_string (action->structure, "flags")))
748 flags = gst_validate_utils_flags_from_str (GST_TYPE_SEEK_FLAGS, str_flags);
749
750 gst_validate_action_get_clocktime (scenario, action, "stop", &stop);
751
752 return gst_validate_scenario_execute_seek (scenario, action, rate, format,
753 flags, start_type, start, stop_type, stop);
754 }
755
756 static gboolean
_pause_action_restore_playing(GstValidateScenario * scenario)757 _pause_action_restore_playing (GstValidateScenario * scenario)
758 {
759 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
760
761 if (!pipeline) {
762 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
763
764 return FALSE;
765 }
766
767 gst_validate_printf (scenario, "Back to playing\n");
768
769 if (gst_element_set_state (pipeline, GST_STATE_PLAYING) ==
770 GST_STATE_CHANGE_FAILURE) {
771 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
772 "Failed to set state to playing");
773 scenario->priv->target_state = GST_STATE_PLAYING;
774 }
775
776 gst_object_unref (pipeline);
777
778 return FALSE;
779 }
780
781 static gboolean
_set_const_func(GQuark field_id,const GValue * value,GstStructure * consts)782 _set_const_func (GQuark field_id, const GValue * value, GstStructure * consts)
783 {
784 gst_structure_id_set_value (consts, field_id, value);
785
786 return TRUE;
787 }
788
789 static GstValidateExecuteActionReturn
_execute_define_vars(GstValidateScenario * scenario,GstValidateAction * action)790 _execute_define_vars (GstValidateScenario * scenario,
791 GstValidateAction * action)
792 {
793 gst_structure_foreach (action->structure,
794 (GstStructureForeachFunc) _set_const_func, scenario->priv->vars);
795
796 return GST_VALIDATE_EXECUTE_ACTION_OK;
797 }
798
799 static GstValidateExecuteActionReturn
_execute_set_state(GstValidateScenario * scenario,GstValidateAction * action)800 _execute_set_state (GstValidateScenario * scenario, GstValidateAction * action)
801 {
802 guint state;
803 const gchar *str_state;
804 GstStateChangeReturn ret;
805 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
806
807 DECLARE_AND_GET_PIPELINE (scenario, action);
808
809 g_return_val_if_fail ((str_state =
810 gst_structure_get_string (action->structure, "state")), FALSE);
811
812 g_return_val_if_fail (gst_validate_utils_enum_from_str (GST_TYPE_STATE,
813 str_state, &state), FALSE);
814
815
816 scenario->priv->target_state = state;
817 scenario->priv->changing_state = TRUE;
818 scenario->priv->seeked_in_pause = FALSE;
819
820 ret = gst_element_set_state (pipeline, state);
821 if (ret == GST_STATE_CHANGE_FAILURE) {
822 scenario->priv->changing_state = FALSE;
823 GST_VALIDATE_REPORT (scenario, STATE_CHANGE_FAILURE,
824 "Failed to set state to %s", str_state);
825
826 /* Nothing async on failure, action will be removed automatically */
827 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
828 goto done;
829 } else if (ret == GST_STATE_CHANGE_ASYNC) {
830
831 scenario->priv->needs_async_done = TRUE;
832 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
833
834 goto done;
835 }
836
837 scenario->priv->changing_state = FALSE;
838
839 done:
840 gst_object_unref (pipeline);
841
842 return res;
843 }
844
845 static GstValidateExecuteActionReturn
_execute_pause(GstValidateScenario * scenario,GstValidateAction * action)846 _execute_pause (GstValidateScenario * scenario, GstValidateAction * action)
847 {
848 GstClockTime duration = 0;
849 GstValidateExecuteActionReturn ret;
850
851 gst_validate_action_get_clocktime (scenario, action, "duration", &duration);
852 gst_structure_set (action->structure, "state", G_TYPE_STRING, "paused", NULL);
853
854 GST_INFO_OBJECT (scenario, "Pausing for %" GST_TIME_FORMAT,
855 GST_TIME_ARGS (duration));
856
857 ret = _execute_set_state (scenario, action);
858
859 if (ret != GST_VALIDATE_EXECUTE_ACTION_ERROR && duration)
860 g_timeout_add (GST_TIME_AS_MSECONDS (duration),
861 (GSourceFunc) _pause_action_restore_playing, scenario);
862
863 return ret;
864 }
865
866 static GstValidateExecuteActionReturn
_execute_play(GstValidateScenario * scenario,GstValidateAction * action)867 _execute_play (GstValidateScenario * scenario, GstValidateAction * action)
868 {
869 GST_DEBUG ("Playing back");
870
871 gst_structure_set (action->structure, "state", G_TYPE_STRING,
872 "playing", NULL);
873
874
875 return _execute_set_state (scenario, action);
876 }
877
878 static gboolean
_action_sets_state(GstValidateAction * action)879 _action_sets_state (GstValidateAction * action)
880 {
881 if (action == NULL)
882 return FALSE;
883
884 if (g_strcmp0 (action->type, "set-state") == 0)
885 return TRUE;
886
887 if (g_strcmp0 (action->type, "play") == 0)
888 return TRUE;
889
890 if (g_strcmp0 (action->type, "pause") == 0)
891 return TRUE;
892
893 return FALSE;
894
895 }
896
897 static void
gst_validate_scenario_check_dropped(GstValidateScenario * scenario)898 gst_validate_scenario_check_dropped (GstValidateScenario * scenario)
899 {
900 GstValidateScenarioPrivate *priv = scenario->priv;
901
902 if (priv->max_dropped == -1 || priv->dropped == -1)
903 return;
904
905 GST_DEBUG_OBJECT (scenario, "Number of dropped buffers: %d (max allowed: %d)",
906 priv->dropped, priv->max_dropped);
907
908 if (priv->dropped > priv->max_dropped) {
909 GST_VALIDATE_REPORT (scenario, CONFIG_TOO_MANY_BUFFERS_DROPPED,
910 "Too many buffers have been dropped: %d (max allowed: %d)",
911 priv->dropped, priv->max_dropped);
912 }
913 }
914
915 static GstValidateExecuteActionReturn
_execute_stop(GstValidateScenario * scenario,GstValidateAction * action)916 _execute_stop (GstValidateScenario * scenario, GstValidateAction * action)
917 {
918 GstBus *bus;
919 GstValidateScenarioPrivate *priv = scenario->priv;
920
921 DECLARE_AND_GET_PIPELINE (scenario, action);
922
923 bus = gst_element_get_bus (pipeline);
924 SCENARIO_LOCK (scenario);
925 if (priv->execute_actions_source_id) {
926 g_source_remove (priv->execute_actions_source_id);
927 priv->execute_actions_source_id = 0;
928 }
929 SCENARIO_UNLOCK (scenario);
930
931 gst_validate_scenario_check_dropped (scenario);
932
933 gst_bus_post (bus,
934 gst_message_new_request_state (GST_OBJECT_CAST (scenario),
935 GST_STATE_NULL));
936 gst_object_unref (bus);
937 gst_object_unref (pipeline);
938
939 return TRUE;
940 }
941
942 static GstValidateExecuteActionReturn
_execute_eos(GstValidateScenario * scenario,GstValidateAction * action)943 _execute_eos (GstValidateScenario * scenario, GstValidateAction * action)
944 {
945 gboolean ret;
946
947 DECLARE_AND_GET_PIPELINE (scenario, action);
948
949 GST_DEBUG ("Sending EOS to pipeline at %" GST_TIME_FORMAT,
950 GST_TIME_ARGS (action->playback_time));
951
952 ret = gst_element_send_event (pipeline, gst_event_new_eos ());
953 gst_object_unref (pipeline);
954
955 return ret ? GST_VALIDATE_EXECUTE_ACTION_OK :
956 GST_VALIDATE_EXECUTE_ACTION_ERROR;
957 }
958
959 static int
find_input_selector(GValue * velement,const gchar * type)960 find_input_selector (GValue * velement, const gchar * type)
961 {
962 GstElement *element = g_value_get_object (velement);
963 int result = !0;
964
965 if (G_OBJECT_TYPE (element) == g_type_from_name ("GstInputSelector")) {
966 GstPad *srcpad = gst_element_get_static_pad (element, "src");
967
968 if (srcpad) {
969 GstCaps *caps = gst_pad_query_caps (srcpad, NULL);
970
971 if (caps) {
972 const char *mime =
973 gst_structure_get_name (gst_caps_get_structure (caps, 0));
974 gboolean found = FALSE;
975
976 if (g_strcmp0 (type, "audio") == 0)
977 found = g_str_has_prefix (mime, "audio/");
978 else if (g_strcmp0 (type, "video") == 0)
979 found = g_str_has_prefix (mime, "video/")
980 && !g_str_has_prefix (mime, "video/x-dvd-subpicture");
981 else if (g_strcmp0 (type, "text") == 0)
982 found = g_str_has_prefix (mime, "text/")
983 || g_str_has_prefix (mime, "subtitle/")
984 || g_str_has_prefix (mime, "video/x-dvd-subpicture");
985
986 if (found)
987 result = 0;
988 }
989
990 gst_caps_unref (caps);
991 gst_object_unref (srcpad);
992 }
993 }
994 return result;
995 }
996
997 static GstElement *
find_input_selector_with_type(GstBin * bin,const gchar * type)998 find_input_selector_with_type (GstBin * bin, const gchar * type)
999 {
1000 GValue result = { 0, };
1001 GstElement *input_selector = NULL;
1002 GstIterator *iterator = gst_bin_iterate_recurse (bin);
1003
1004 if (gst_iterator_find_custom (iterator,
1005 (GCompareFunc) find_input_selector, &result, (gpointer) type)) {
1006 input_selector = g_value_get_object (&result);
1007 }
1008 gst_iterator_free (iterator);
1009
1010 return input_selector;
1011 }
1012
1013 static GstPad *
find_nth_sink_pad(GstElement * element,int index)1014 find_nth_sink_pad (GstElement * element, int index)
1015 {
1016 GstIterator *iterator;
1017 gboolean done = FALSE;
1018 GstPad *pad = NULL;
1019 int dec_index = index;
1020 GValue data = { 0, };
1021
1022 iterator = gst_element_iterate_sink_pads (element);
1023 while (!done) {
1024 switch (gst_iterator_next (iterator, &data)) {
1025 case GST_ITERATOR_OK:
1026 if (!dec_index--) {
1027 done = TRUE;
1028 pad = g_value_get_object (&data);
1029 break;
1030 }
1031 g_value_reset (&data);
1032 break;
1033 case GST_ITERATOR_RESYNC:
1034 gst_iterator_resync (iterator);
1035 dec_index = index;
1036 break;
1037 case GST_ITERATOR_ERROR:
1038 done = TRUE;
1039 break;
1040 case GST_ITERATOR_DONE:
1041 done = TRUE;
1042 break;
1043 }
1044 }
1045 gst_iterator_free (iterator);
1046 return pad;
1047 }
1048
1049 static int
find_sink_pad_index(GstElement * element,GstPad * pad)1050 find_sink_pad_index (GstElement * element, GstPad * pad)
1051 {
1052 GstIterator *iterator;
1053 gboolean done = FALSE;
1054 int index = 0;
1055 GValue data = { 0, };
1056
1057 iterator = gst_element_iterate_sink_pads (element);
1058 while (!done) {
1059 switch (gst_iterator_next (iterator, &data)) {
1060 case GST_ITERATOR_OK:
1061 if (pad == g_value_get_object (&data)) {
1062 done = TRUE;
1063 } else {
1064 index++;
1065 }
1066 g_value_reset (&data);
1067 break;
1068 case GST_ITERATOR_RESYNC:
1069 gst_iterator_resync (iterator);
1070 index = 0;
1071 break;
1072 case GST_ITERATOR_ERROR:
1073 done = TRUE;
1074 break;
1075 case GST_ITERATOR_DONE:
1076 done = TRUE;
1077 break;
1078 }
1079 }
1080 gst_iterator_free (iterator);
1081 return index;
1082 }
1083
1084 static GstPadProbeReturn
_check_select_pad_done(GstPad * pad,GstPadProbeInfo * info,GstValidateAction * action)1085 _check_select_pad_done (GstPad * pad, GstPadProbeInfo * info,
1086 GstValidateAction * action)
1087 {
1088 if (GST_BUFFER_FLAG_IS_SET (info->data, GST_BUFFER_FLAG_DISCONT)) {
1089 gst_validate_action_set_done (action);
1090
1091 return GST_PAD_PROBE_REMOVE;
1092 }
1093
1094 return GST_PAD_PROBE_OK;
1095 }
1096
1097 static GstValidateExecuteActionReturn
execute_switch_track_default(GstValidateScenario * scenario,GstValidateAction * action)1098 execute_switch_track_default (GstValidateScenario * scenario,
1099 GstValidateAction * action)
1100 {
1101 guint index;
1102 gboolean relative = FALSE;
1103 const gchar *type, *str_index;
1104 GstElement *input_selector;
1105 GstValidateExecuteActionReturn ret = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1106
1107 DECLARE_AND_GET_PIPELINE (scenario, action);
1108
1109 if (!(type = gst_structure_get_string (action->structure, "type")))
1110 type = "audio";
1111
1112 /* First find an input selector that has the right type */
1113 input_selector = find_input_selector_with_type (GST_BIN (pipeline), type);
1114 if (input_selector) {
1115 GstState state, next;
1116 GstPad *pad, *cpad, *srcpad;
1117
1118 ret = GST_VALIDATE_EXECUTE_ACTION_OK;
1119 str_index = gst_structure_get_string (action->structure, "index");
1120
1121 if (str_index == NULL) {
1122 if (!gst_structure_get_uint (action->structure, "index", &index)) {
1123 GST_WARNING ("No index given, defaulting to +1");
1124 index = 1;
1125 relative = TRUE;
1126 }
1127 } else {
1128 relative = strchr ("+-", str_index[0]) != NULL;
1129 index = g_ascii_strtoll (str_index, NULL, 10);
1130 }
1131
1132 if (relative) { /* We are changing track relatively to current track */
1133 int npads;
1134
1135 g_object_get (input_selector, "active-pad", &pad, "n-pads", &npads, NULL);
1136 if (pad) {
1137 int current_index = find_sink_pad_index (input_selector, pad);
1138
1139 index = (current_index + index) % npads;
1140 gst_object_unref (pad);
1141 }
1142 }
1143
1144 pad = find_nth_sink_pad (input_selector, index);
1145 g_object_get (input_selector, "active-pad", &cpad, NULL);
1146 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1147 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1148 srcpad = gst_element_get_static_pad (input_selector, "src");
1149
1150 gst_pad_add_probe (srcpad,
1151 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
1152 (GstPadProbeCallback) _check_select_pad_done, action, NULL);
1153 ret = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1154 gst_object_unref (srcpad);
1155 }
1156
1157 g_object_set (input_selector, "active-pad", pad, NULL);
1158 gst_object_unref (pad);
1159 gst_object_unref (cpad);
1160 gst_object_unref (input_selector);
1161
1162 goto done;
1163 }
1164
1165 /* No selector found -> Failed */
1166 done:
1167 gst_object_unref (pipeline);
1168
1169 return ret;
1170 }
1171
1172 static GstPadProbeReturn
_check_pad_event_selection_done(GstPad * pad,GstPadProbeInfo * info,GstValidateAction * action)1173 _check_pad_event_selection_done (GstPad * pad, GstPadProbeInfo * info,
1174 GstValidateAction * action)
1175 {
1176 if (GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
1177 gst_validate_action_set_done (action);
1178 return GST_PAD_PROBE_REMOVE;
1179 }
1180 return GST_PAD_PROBE_OK;
1181 }
1182
1183 static GstValidateExecuteActionReturn
execute_switch_track_pb(GstValidateScenario * scenario,GstValidateAction * action)1184 execute_switch_track_pb (GstValidateScenario * scenario,
1185 GstValidateAction * action)
1186 {
1187 gint index, n;
1188 const gchar *type, *str_index;
1189
1190 gint flags, current, tflag;
1191 gchar *tmp, *current_txt;
1192
1193 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1194 gboolean relative = FALSE, disabling = FALSE;
1195
1196 DECLARE_AND_GET_PIPELINE (scenario, action);
1197
1198 if (!(type = gst_structure_get_string (action->structure, "type")))
1199 type = "audio";
1200
1201 tflag = gst_validate_utils_flags_from_str (g_type_from_name ("GstPlayFlags"),
1202 type);
1203 current_txt = g_strdup_printf ("current-%s", type);
1204
1205 tmp = g_strdup_printf ("n-%s", type);
1206 g_object_get (pipeline, "flags", &flags, tmp, &n, current_txt, ¤t,
1207 NULL);
1208
1209 /* Don't try to use -1 */
1210 if (current == -1)
1211 current = 0;
1212
1213 g_free (tmp);
1214
1215 if (gst_structure_has_field (action->structure, "disable")) {
1216 disabling = TRUE;
1217 flags &= ~tflag;
1218 index = -1;
1219 } else if (!(str_index =
1220 gst_structure_get_string (action->structure, "index"))) {
1221 if (!gst_structure_get_int (action->structure, "index", &index)) {
1222 GST_WARNING ("No index given, defaulting to +1");
1223 index = 1;
1224 relative = TRUE;
1225 }
1226 } else {
1227 relative = strchr ("+-", str_index[0]) != NULL;
1228 index = g_ascii_strtoll (str_index, NULL, 10);
1229 }
1230
1231 if (relative) { /* We are changing track relatively to current track */
1232 if (n == 0) {
1233 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1234 "Trying to execute a relative %s for %s track when there"
1235 " is no track of this type available on current stream.",
1236 action->type, type);
1237
1238 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1239 goto done;
1240 }
1241
1242 index = (current + index) % n;
1243 }
1244
1245 if (!disabling) {
1246 GstState state, next;
1247 GstPad *oldpad, *newpad;
1248 tmp = g_strdup_printf ("get-%s-pad", type);
1249 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, current, &oldpad);
1250 g_signal_emit_by_name (G_OBJECT (pipeline), tmp, index, &newpad);
1251
1252 gst_validate_printf (action, "Switching to track number: %i,"
1253 " (from %s:%s to %s:%s)\n", index, GST_DEBUG_PAD_NAME (oldpad),
1254 GST_DEBUG_PAD_NAME (newpad));
1255 flags |= tflag;
1256 g_free (tmp);
1257
1258 if (gst_element_get_state (pipeline, &state, &next, 0) &&
1259 state == GST_STATE_PLAYING && next == GST_STATE_VOID_PENDING) {
1260 GstPad *srcpad = NULL;
1261 GstElement *combiner = NULL;
1262 if (newpad == oldpad) {
1263 srcpad = gst_pad_get_peer (oldpad);
1264 } else if (newpad) {
1265 combiner = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (newpad)));
1266 if (combiner) {
1267 srcpad = gst_element_get_static_pad (combiner, "src");
1268 gst_object_unref (combiner);
1269 }
1270 }
1271
1272 if (srcpad) {
1273 gst_pad_add_probe (srcpad,
1274 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1275 (GstPadProbeCallback) _check_pad_event_selection_done, action,
1276 NULL);
1277 gst_object_unref (srcpad);
1278
1279 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1280 } else
1281 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1282 }
1283
1284 if (oldpad)
1285 gst_object_unref (oldpad);
1286 gst_object_unref (newpad);
1287 } else {
1288 gst_validate_printf (action, "Disabling track type %s", type);
1289 }
1290
1291 g_object_set (pipeline, "flags", flags, current_txt, index, NULL);
1292 g_free (current_txt);
1293
1294 done:
1295 gst_object_unref (pipeline);
1296 return res;
1297 }
1298
1299 static GstStreamType
stream_type_from_string(const gchar * type)1300 stream_type_from_string (const gchar * type)
1301 {
1302 if (!g_strcmp0 (type, "video"))
1303 return GST_STREAM_TYPE_VIDEO;
1304 else if (!g_strcmp0 (type, "text"))
1305 return GST_STREAM_TYPE_TEXT;
1306
1307 /* default */
1308 return GST_STREAM_TYPE_AUDIO;
1309 }
1310
1311 /* Return a list of stream ID all the currently selected streams but the ones
1312 * of type @type */
1313 static GList *
disable_stream(GstValidatePipelineMonitor * monitor,GstStreamType type)1314 disable_stream (GstValidatePipelineMonitor * monitor, GstStreamType type)
1315 {
1316 GList *streams = NULL, *l;
1317
1318 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1319 GstStream *s = l->data;
1320
1321 if (gst_stream_get_stream_type (s) != type) {
1322 streams = g_list_append (streams, (gpointer) s->stream_id);
1323 }
1324 }
1325
1326 return streams;
1327 }
1328
1329 static GList *
switch_stream(GstValidatePipelineMonitor * monitor,GstValidateAction * action,GstStreamType type,gint index,gboolean relative)1330 switch_stream (GstValidatePipelineMonitor * monitor, GstValidateAction * action,
1331 GstStreamType type, gint index, gboolean relative)
1332 {
1333 guint nb_streams;
1334 guint i, n = 0, current = 0;
1335 GList *result = NULL, *l;
1336 GstStream *streams[256], *s, *current_stream = NULL;
1337
1338 /* Keep all streams which are not @type */
1339 for (l = monitor->streams_selected; l; l = g_list_next (l)) {
1340 s = l->data;
1341
1342 if (gst_stream_get_stream_type (s) != type) {
1343 result = g_list_append (result, (gpointer) s->stream_id);
1344 } else if (!current_stream) {
1345 /* Assume the stream we want to switch from is the first one */
1346 current_stream = s;
1347 }
1348 }
1349
1350 /* Calculate the number of @type streams */
1351 nb_streams = gst_stream_collection_get_size (monitor->stream_collection);
1352 for (i = 0; i < nb_streams; i++) {
1353 s = gst_stream_collection_get_stream (monitor->stream_collection, i);
1354
1355 if (gst_stream_get_stream_type (s) == type) {
1356 streams[n] = s;
1357
1358 if (current_stream
1359 && !g_strcmp0 (s->stream_id, current_stream->stream_id))
1360 current = n;
1361
1362 n++;
1363 }
1364 }
1365
1366 if (G_UNLIKELY (n == 0)) {
1367 GST_ERROR ("No streams available of the required type");
1368 return result;
1369 }
1370
1371 if (relative) { /* We are changing track relatively to current track */
1372 index = (current + index) % n;
1373 } else
1374 index %= n;
1375
1376 /* Add the new stream we want to switch to */
1377 s = streams[index];
1378
1379 gst_validate_printf (action, "Switching from stream %s to %s",
1380 current_stream ? current_stream->stream_id : "", s->stream_id);
1381
1382 return g_list_append (result, (gpointer) s->stream_id);
1383 }
1384
1385 static GstValidateExecuteActionReturn
execute_switch_track_pb3(GstValidateScenario * scenario,GstValidateAction * action)1386 execute_switch_track_pb3 (GstValidateScenario * scenario,
1387 GstValidateAction * action)
1388 {
1389 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1390 GstValidateScenarioPrivate *priv = scenario->priv;
1391 gint index;
1392 GstStreamType stype;
1393 const gchar *type, *str_index;
1394 GList *new_streams = NULL;
1395 GstValidatePipelineMonitor *monitor;
1396 DECLARE_AND_GET_PIPELINE (scenario, action);
1397
1398 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1399 pipeline, "validate-monitor"));
1400
1401 if (!monitor->stream_collection) {
1402 GST_VALIDATE_REPORT (scenario,
1403 SCENARIO_ACTION_EXECUTION_ERROR,
1404 "No stream collection message received on the bus, "
1405 "can not switch track.");
1406 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1407 goto done;
1408 }
1409
1410 if (!monitor->streams_selected) {
1411 GST_VALIDATE_REPORT (scenario,
1412 SCENARIO_ACTION_EXECUTION_ERROR,
1413 "No streams selected message received on the bus");
1414 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1415 goto done;
1416 }
1417
1418 type = gst_structure_get_string (action->structure, "type");
1419 stype = stream_type_from_string (type);
1420
1421 if (gst_structure_has_field (action->structure, "disable")) {
1422 gst_validate_printf (action, "Disabling track type %s", type);
1423 new_streams = disable_stream (monitor, stype);
1424 } else {
1425 gboolean relative = FALSE;
1426
1427 if (!(str_index = gst_structure_get_string (action->structure, "index"))) {
1428 if (!gst_structure_get_int (action->structure, "index", &index)) {
1429 GST_WARNING ("No index given, defaulting to +1");
1430 index = 1;
1431 relative = TRUE;
1432 }
1433 } else {
1434 relative = strchr ("+-", str_index[0]) != NULL;
1435 index = g_ascii_strtoll (str_index, NULL, 10);
1436 }
1437
1438 new_streams = switch_stream (monitor, action, stype, index, relative);
1439 }
1440
1441 gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (action),
1442 ACTION_EXPECTED_STREAM_QUARK, g_list_copy (new_streams),
1443 (GDestroyNotify) g_list_free);
1444
1445 if (!gst_element_send_event (pipeline,
1446 gst_event_new_select_streams (new_streams))) {
1447 GST_VALIDATE_REPORT (scenario,
1448 SCENARIO_ACTION_EXECUTION_ERROR, "select-streams event not handled");
1449 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
1450 goto done;
1451 }
1452
1453 priv->pending_switch_track = action;
1454 if (scenario->priv->target_state > GST_STATE_PAUSED) {
1455 res = GST_VALIDATE_EXECUTE_ACTION_ASYNC;
1456 } else {
1457 gst_mini_object_ref ((GstMiniObject *) action);
1458 res = GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
1459 }
1460
1461 done:
1462 gst_object_unref (pipeline);
1463
1464 return res;
1465 }
1466
1467 static GstValidateExecuteActionReturn
_execute_switch_track(GstValidateScenario * scenario,GstValidateAction * action)1468 _execute_switch_track (GstValidateScenario * scenario,
1469 GstValidateAction * action)
1470 {
1471 GstValidatePipelineMonitor *monitor;
1472
1473 DECLARE_AND_GET_PIPELINE (scenario, action);
1474
1475 monitor = (GstValidatePipelineMonitor *) (g_object_get_data ((GObject *)
1476 pipeline, "validate-monitor"));
1477 gst_object_unref (pipeline);
1478
1479 if (monitor->is_playbin)
1480 return execute_switch_track_pb (scenario, action);
1481 else if (monitor->is_playbin3)
1482 return execute_switch_track_pb3 (scenario, action);
1483
1484 return execute_switch_track_default (scenario, action);
1485 }
1486
1487 static GstValidateExecuteActionReturn
_execute_set_rank(GstValidateScenario * scenario,GstValidateAction * action)1488 _execute_set_rank (GstValidateScenario * scenario, GstValidateAction * action)
1489 {
1490 guint rank;
1491 GList *features, *origlist;
1492 GstPlugin *plugin;
1493 GstPluginFeature *feature;
1494 const gchar *name;
1495
1496 if (!(name = gst_structure_get_string (action->structure, "feature-name")) &&
1497 !(name = gst_structure_get_string (action->structure, "name"))) {
1498 GST_ERROR ("Could not find the name of the plugin feature(s) to tweak");
1499
1500 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1501 }
1502
1503 if (!(gst_structure_get_uint (action->structure, "rank", &rank) ||
1504 gst_structure_get_int (action->structure, "rank", (gint *) & rank))) {
1505 GST_ERROR ("Could not get rank to set on %s", name);
1506
1507 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1508 }
1509
1510 feature = gst_registry_lookup_feature (gst_registry_get (), name);
1511 if (feature) {
1512 gst_plugin_feature_set_rank (feature, rank);
1513 gst_object_unref (feature);
1514
1515 return GST_VALIDATE_EXECUTE_ACTION_OK;
1516 }
1517
1518 plugin = gst_registry_find_plugin (gst_registry_get (), name);
1519 if (!plugin) {
1520 GST_ERROR ("Could not find %s", name);
1521
1522 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1523 }
1524
1525 origlist = features =
1526 gst_registry_get_feature_list_by_plugin (gst_registry_get (),
1527 gst_plugin_get_name (plugin));
1528 for (; features; features = features->next)
1529 gst_plugin_feature_set_rank (features->data, rank);
1530 gst_plugin_feature_list_free (origlist);
1531
1532 return GST_VALIDATE_EXECUTE_ACTION_OK;
1533 }
1534
1535 static inline gboolean
_add_execute_actions_gsource(GstValidateScenario * scenario)1536 _add_execute_actions_gsource (GstValidateScenario * scenario)
1537 {
1538 GstValidateScenarioPrivate *priv = scenario->priv;
1539
1540 SCENARIO_LOCK (scenario);
1541 if (priv->execute_actions_source_id == 0 && priv->wait_id == 0
1542 && priv->signal_handler_id == 0 && priv->message_type == NULL) {
1543 if (!scenario->priv->action_execution_interval)
1544 priv->execute_actions_source_id =
1545 g_idle_add ((GSourceFunc) execute_next_action, scenario);
1546 else
1547 priv->execute_actions_source_id =
1548 g_timeout_add (scenario->priv->action_execution_interval,
1549 (GSourceFunc) execute_next_action, scenario);
1550 SCENARIO_UNLOCK (scenario);
1551
1552 GST_DEBUG_OBJECT (scenario, "Start checking position again");
1553 return TRUE;
1554 }
1555 SCENARIO_UNLOCK (scenario);
1556
1557 GST_LOG_OBJECT (scenario, "No need to start a new gsource");
1558 return FALSE;
1559 }
1560
1561 static gboolean
_get_position(GstValidateScenario * scenario,GstValidateAction * act,GstClockTime * position)1562 _get_position (GstValidateScenario * scenario,
1563 GstValidateAction * act, GstClockTime * position)
1564 {
1565 gboolean has_pos = FALSE, has_dur = FALSE;
1566 GstClockTime duration = -1;
1567
1568 GstValidateScenarioPrivate *priv = scenario->priv;
1569 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
1570
1571 if (!pipeline) {
1572 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
1573
1574 return FALSE;
1575 }
1576
1577 has_pos = gst_element_query_position (pipeline, GST_FORMAT_TIME,
1578 (gint64 *) position)
1579 && GST_CLOCK_TIME_IS_VALID (*position);
1580 has_dur =
1581 gst_element_query_duration (pipeline, GST_FORMAT_TIME,
1582 (gint64 *) & duration)
1583 && GST_CLOCK_TIME_IS_VALID (duration);
1584
1585 if (!has_pos && GST_STATE (pipeline) >= GST_STATE_PAUSED &&
1586 act && GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1587 GST_INFO_OBJECT (scenario, "Unknown position: %" GST_TIME_FORMAT,
1588 GST_TIME_ARGS (*position));
1589
1590 goto fail;
1591 }
1592
1593 if (has_pos && has_dur && !priv->got_eos) {
1594 if (*position > duration) {
1595 _add_execute_actions_gsource (scenario);
1596
1597 GST_VALIDATE_REPORT (scenario,
1598 QUERY_POSITION_SUPERIOR_DURATION,
1599 "Reported position %" GST_TIME_FORMAT " > reported duration %"
1600 GST_TIME_FORMAT, GST_TIME_ARGS (*position), GST_TIME_ARGS (duration));
1601
1602 goto done;
1603 }
1604 }
1605
1606 done:
1607 gst_object_unref (pipeline);
1608 return TRUE;
1609
1610 fail:
1611 gst_object_unref (pipeline);
1612 return FALSE;
1613 }
1614
1615 static gboolean
_check_position(GstValidateScenario * scenario,GstValidateAction * act,GstClockTime * position,gdouble * rate)1616 _check_position (GstValidateScenario * scenario, GstValidateAction * act,
1617 GstClockTime * position, gdouble * rate)
1618 {
1619 GstQuery *query;
1620
1621 GstClockTime start_with_tolerance, stop_with_tolerance;
1622 GstValidateScenarioPrivate *priv = scenario->priv;
1623 GstElement *pipeline;
1624
1625 if (!_get_position (scenario, act, position))
1626 return FALSE;
1627
1628 GST_DEBUG_OBJECT (scenario, "Current position: %" GST_TIME_FORMAT,
1629 GST_TIME_ARGS (*position));
1630
1631 /* Check if playback is within seek segment */
1632 start_with_tolerance = (priv->segment_start <
1633 priv->seek_pos_tol) ? 0 : priv->segment_start - priv->seek_pos_tol;
1634 stop_with_tolerance =
1635 GST_CLOCK_TIME_IS_VALID (priv->segment_stop) ? priv->segment_stop +
1636 priv->seek_pos_tol : -1;
1637
1638 if ((GST_CLOCK_TIME_IS_VALID (stop_with_tolerance)
1639 && *position > stop_with_tolerance)
1640 || (priv->seek_flags & GST_SEEK_FLAG_ACCURATE
1641 && *position < start_with_tolerance)) {
1642
1643 GST_VALIDATE_REPORT (scenario, QUERY_POSITION_OUT_OF_SEGMENT,
1644 "Current position %" GST_TIME_FORMAT " not in the expected range [%"
1645 GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, GST_TIME_ARGS (*position),
1646 GST_TIME_ARGS (start_with_tolerance),
1647 GST_TIME_ARGS (stop_with_tolerance));
1648 }
1649
1650 pipeline = gst_validate_scenario_get_pipeline (scenario);
1651 if (pipeline == NULL) {
1652 GST_INFO_OBJECT (scenario, "No pipeline set anymore");
1653
1654 return TRUE;
1655 }
1656
1657 query = gst_query_new_segment (GST_FORMAT_DEFAULT);
1658 if (gst_element_query (GST_ELEMENT (pipeline), query))
1659 gst_query_parse_segment (query, rate, NULL, NULL, NULL);
1660 gst_query_unref (query);
1661 gst_object_unref (pipeline);
1662
1663 if (priv->seeked_in_pause && priv->seek_flags & GST_SEEK_FLAG_ACCURATE) {
1664 if ((*rate > 0 && (*position >= priv->segment_start + priv->seek_pos_tol ||
1665 *position < ((priv->segment_start <
1666 priv->seek_pos_tol) ? 0 : priv->segment_start -
1667 priv->seek_pos_tol)))
1668 || (*rate < 0 && (*position > priv->segment_start + priv->seek_pos_tol
1669 || *position < ((priv->segment_start <
1670 priv->seek_pos_tol) ? 0 : priv->segment_start -
1671 priv->seek_pos_tol)))) {
1672 priv->seeked_in_pause = FALSE;
1673 GST_VALIDATE_REPORT (scenario, EVENT_SEEK_RESULT_POSITION_WRONG,
1674 "Reported position after accurate seek in PAUSED state should be exactly"
1675 " what the user asked for. Position %" GST_TIME_FORMAT
1676 " is not not the expected one: %" GST_TIME_FORMAT,
1677 GST_TIME_ARGS (*position), GST_TIME_ARGS (priv->segment_start));
1678 }
1679 }
1680
1681 return TRUE;
1682 }
1683
1684 static gboolean
_check_message_type(GstValidateScenario * scenario,GstValidateAction * act,GstMessage * message)1685 _check_message_type (GstValidateScenario * scenario, GstValidateAction * act,
1686 GstMessage * message)
1687 {
1688 return act && message
1689 && !g_strcmp0 (gst_structure_get_string (act->structure, "on-message"),
1690 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
1691 }
1692
1693 static gboolean
_should_execute_action(GstValidateScenario * scenario,GstValidateAction * act,GstClockTime position,gdouble rate)1694 _should_execute_action (GstValidateScenario * scenario, GstValidateAction * act,
1695 GstClockTime position, gdouble rate)
1696 {
1697 GstElement *pipeline;
1698
1699 if (!act) {
1700 GST_DEBUG_OBJECT (scenario, "No action to execute");
1701
1702 return FALSE;
1703 }
1704
1705 pipeline = gst_validate_scenario_get_pipeline (scenario);
1706 if (pipeline == NULL) {
1707
1708 if (!(GST_VALIDATE_ACTION_GET_TYPE (act)->flags &
1709 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE)) {
1710 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1711 "Trying to execute an %s action after the pipeline has been destroyed"
1712 " but the type has not been marked as "
1713 "GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE", act->type);
1714
1715 return FALSE;
1716 } else if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1717 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
1718 "Trying to execute action %s with playback time %" GST_TIME_FORMAT
1719 " after the pipeline has been destroyed. It is impossible"
1720 " to execute an action with a playback time specified"
1721 " after the pipeline has been destroyed",
1722 act->type, GST_TIME_ARGS (act->playback_time));
1723
1724 goto no;
1725 }
1726
1727 GST_DEBUG_OBJECT (scenario, "No pipeline, go and execute action!");
1728
1729 goto yes;
1730 } else if (scenario->priv->got_eos) {
1731 GST_DEBUG_OBJECT (scenario, "Just got EOS go and execute next action!");
1732 scenario->priv->got_eos = FALSE;
1733 } else if (GST_STATE (pipeline) < GST_STATE_PAUSED) {
1734 GST_DEBUG_OBJECT (scenario, "Pipeline not even in paused, "
1735 "just executing actions");
1736
1737 goto yes;
1738 } else if (act->playback_time == GST_CLOCK_TIME_NONE) {
1739 GST_DEBUG_OBJECT (scenario, "No timing info, executing action");
1740
1741 goto yes;
1742 } else if ((rate > 0 && (GstClockTime) position < act->playback_time)) {
1743 GST_DEBUG_OBJECT (scenario, "positive rate and position %" GST_TIME_FORMAT
1744 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1745 GST_TIME_ARGS (act->playback_time));
1746
1747 goto no;
1748 } else if (rate < 0 && (GstClockTime) position > act->playback_time) {
1749 GST_DEBUG_OBJECT (scenario, "negative rate and position %" GST_TIME_FORMAT
1750 " < playback_time %" GST_TIME_FORMAT, GST_TIME_ARGS (position),
1751 GST_TIME_ARGS (act->playback_time));
1752
1753 goto no;
1754 }
1755
1756 yes:
1757 gst_object_unref (pipeline);
1758 return TRUE;
1759
1760 no:
1761 gst_object_unref (pipeline);
1762 return FALSE;
1763 }
1764
1765 static gboolean
_set_action_playback_time(GstValidateScenario * scenario,GstValidateAction * action)1766 _set_action_playback_time (GstValidateScenario * scenario,
1767 GstValidateAction * action)
1768 {
1769 if (!gst_validate_action_get_clocktime (scenario, action,
1770 "playback-time", &action->playback_time)) {
1771 gchar *str = gst_structure_to_string (action->structure);
1772
1773 g_error ("Could not parse playback-time on structure: %s", str);
1774 g_free (str);
1775
1776 return FALSE;
1777 }
1778
1779 gst_structure_set (action->structure, "playback-time", GST_TYPE_CLOCK_TIME,
1780 action->playback_time, NULL);
1781
1782 return TRUE;
1783 }
1784
1785 static gboolean
gst_validate_parse_next_action_playback_time(GstValidateScenario * self)1786 gst_validate_parse_next_action_playback_time (GstValidateScenario * self)
1787 {
1788 GstValidateAction *action;
1789 GstValidateScenarioPrivate *priv = self->priv;
1790
1791 if (!priv->actions)
1792 return TRUE;
1793
1794 action = (GstValidateAction *) priv->actions->data;
1795 if (!action->priv->needs_playback_parsing)
1796 return TRUE;
1797
1798 if (!_set_action_playback_time (self, action)) {
1799 GST_ERROR_OBJECT (self, "Could not set playback_time!");
1800
1801 return FALSE;
1802 }
1803 action->priv->needs_playback_parsing = FALSE;
1804
1805 return TRUE;
1806 }
1807
1808 GstValidateExecuteActionReturn
gst_validate_execute_action(GstValidateActionType * action_type,GstValidateAction * action)1809 gst_validate_execute_action (GstValidateActionType * action_type,
1810 GstValidateAction * action)
1811 {
1812 GstValidateExecuteActionReturn res;
1813 GstValidateScenario *scenario;
1814
1815 g_return_val_if_fail (g_strcmp0 (action_type->name, action->type) == 0,
1816 GST_VALIDATE_EXECUTE_ACTION_ERROR);
1817
1818 scenario = gst_validate_action_get_scenario (action);
1819
1820 if (action_type->prepare) {
1821 if (action_type->prepare (action) == FALSE) {
1822 GST_ERROR_OBJECT (scenario, "Action %" GST_PTR_FORMAT
1823 " could not be prepared", action->structure);
1824
1825 gst_object_unref (scenario);
1826 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1827 }
1828 }
1829
1830 gst_validate_print_action (action, NULL);
1831
1832 action->priv->execution_time = gst_util_get_timestamp ();
1833 action->priv->state = GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS;
1834 res = action_type->execute (scenario, action);
1835 gst_object_unref (scenario);
1836
1837 if (!gst_structure_has_field (action->structure, "sub-action")) {
1838 gst_structure_free (action->structure);
1839 action->priv->printed = FALSE;
1840 action->structure = gst_structure_copy (action->priv->main_structure);
1841
1842 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1843 action->name = "";
1844
1845 if (res == GST_VALIDATE_EXECUTE_ACTION_ASYNC)
1846 action->priv->executing_last_subaction = TRUE;
1847 }
1848
1849 return res;
1850 }
1851
1852 /* scenario can be NULL **only** if the action is a CONFIG action and
1853 * add_to_lists is FALSE */
1854 static GstValidateExecuteActionReturn
_fill_action(GstValidateScenario * scenario,GstValidateAction * action,GstStructure * structure,gboolean add_to_lists)1855 _fill_action (GstValidateScenario * scenario, GstValidateAction * action,
1856 GstStructure * structure, gboolean add_to_lists)
1857 {
1858 gdouble playback_time;
1859 gboolean is_config = FALSE;
1860 GstValidateActionType *action_type;
1861 const gchar *str_playback_time = NULL;
1862 GstValidateScenarioPrivate *priv = scenario ? scenario->priv : NULL;
1863 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_NONE;
1864 gboolean optional, needs_parsing;
1865
1866 action->type = gst_structure_get_name (structure);
1867 action_type = _find_action_type (action->type);
1868
1869 if (!action_type) {
1870 GST_ERROR_OBJECT (scenario, "Action type %s no found",
1871 gst_structure_get_name (structure));
1872
1873 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1874 }
1875
1876 if (gst_structure_get_double (structure, "playback-time", &playback_time) ||
1877 gst_structure_get_double (structure, "playback_time", &playback_time)) {
1878 action->playback_time = playback_time * GST_SECOND;
1879 } else if ((str_playback_time =
1880 gst_structure_get_string (structure, "playback-time")) ||
1881 (str_playback_time =
1882 gst_structure_get_string (structure, "playback_time"))) {
1883
1884 if (add_to_lists && priv) {
1885 action->priv->needs_playback_parsing = TRUE;
1886 needs_parsing = TRUE;
1887 }
1888 } else
1889 GST_INFO_OBJECT (scenario,
1890 "No playback time for action %" GST_PTR_FORMAT, structure);
1891
1892 if (!gst_validate_utils_get_clocktime (structure,
1893 "timeout", &action->priv->timeout)) {
1894 GST_INFO_OBJECT (scenario,
1895 "No timeout time for action %" GST_PTR_FORMAT, structure);
1896 }
1897
1898 action->structure = gst_structure_copy (structure);
1899
1900 if (!(action->name = gst_structure_get_string (action->structure, "name")))
1901 action->name = "";
1902
1903 if (!action->priv->main_structure)
1904 action->priv->main_structure = gst_structure_copy (structure);
1905
1906 if (gst_structure_get_boolean (structure, "optional", &optional)) {
1907 if ((action_type->flags & GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL) == 0) {
1908 GST_ERROR_OBJECT (scenario, "Action type %s can't be optional",
1909 gst_structure_get_name (structure));
1910 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
1911 }
1912 action->priv->optional = optional;
1913 }
1914
1915 if (IS_CONFIG_ACTION_TYPE (action_type->flags) ||
1916 (gst_structure_get_boolean (action->structure, "as-config",
1917 &is_config) && is_config == TRUE)) {
1918
1919 res = action_type->execute (scenario, action);
1920 gst_validate_print_action (action, NULL);
1921
1922 return res;
1923 }
1924
1925 if (!add_to_lists)
1926 return res;
1927
1928 if (priv != NULL) {
1929 GstValidateActionType *type = _find_action_type (action->type);
1930 gboolean can_execute_on_addition =
1931 type->flags & GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION
1932 && !GST_CLOCK_TIME_IS_VALID (action->playback_time);
1933
1934 if (needs_parsing)
1935 can_execute_on_addition = FALSE;
1936
1937 if (can_execute_on_addition) {
1938 GList *tmp;
1939
1940 for (tmp = priv->actions; tmp; tmp = tmp->next) {
1941 GstValidateAction *act = (GstValidateAction *) tmp->data;
1942 if (GST_CLOCK_TIME_IS_VALID (act->playback_time)) {
1943 can_execute_on_addition = FALSE;
1944 break;
1945 }
1946 }
1947
1948 }
1949
1950 if (can_execute_on_addition) {
1951 SCENARIO_LOCK (scenario);
1952 priv->on_addition_actions = g_list_append (priv->on_addition_actions,
1953 action);
1954 SCENARIO_UNLOCK (scenario);
1955
1956 } else {
1957 priv->actions = g_list_append (priv->actions, action);
1958 }
1959 }
1960
1961 return res;
1962 }
1963
1964 static GstValidateExecuteActionReturn
_execute_sub_action_action(GstValidateAction * action)1965 _execute_sub_action_action (GstValidateAction * action)
1966 {
1967 const gchar *subaction_str;
1968 GstStructure *subaction_struct = NULL;
1969 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
1970 GstValidateScenario *scenario = NULL;
1971
1972 if (action->priv->executing_last_subaction) {
1973 action->priv->executing_last_subaction = FALSE;
1974
1975 goto done;
1976 }
1977
1978 scenario = gst_validate_action_get_scenario (action);
1979 g_assert (scenario);
1980 subaction_str = gst_structure_get_string (action->structure, "sub-action");
1981 if (subaction_str) {
1982 subaction_struct = gst_structure_from_string (subaction_str, NULL);
1983
1984 if (subaction_struct == NULL) {
1985 GST_VALIDATE_REPORT (scenario, SCENARIO_FILE_MALFORMED,
1986 "Sub action %s could not be parsed", subaction_str);
1987
1988 res = GST_VALIDATE_EXECUTE_ACTION_ERROR;
1989 goto done;
1990 }
1991
1992 } else {
1993 gst_structure_get (action->structure, "sub-action", GST_TYPE_STRUCTURE,
1994 &subaction_struct, NULL);
1995 }
1996
1997 if (subaction_struct) {
1998 if (action->structure) {
1999 GST_INFO_OBJECT (scenario, "Clearing old action structure");
2000 gst_structure_free (action->structure);
2001 }
2002
2003 res = _fill_action (scenario, action, subaction_struct, FALSE);
2004 if (res == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2005 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2006 "Sub action %" GST_PTR_FORMAT " could not be filled",
2007 subaction_struct);
2008
2009 goto done;
2010 }
2011
2012 if (!GST_CLOCK_TIME_IS_VALID (action->playback_time)) {
2013 GstValidateActionType *action_type = _find_action_type (action->type);
2014
2015 action->priv->printed = FALSE;
2016 res = gst_validate_execute_action (action_type, action);
2017
2018 goto done;
2019 }
2020
2021 }
2022
2023 done:
2024 if (scenario)
2025 gst_object_unref (scenario);
2026 if (subaction_struct)
2027 gst_structure_free (subaction_struct);
2028 return res;
2029 }
2030
2031
2032 /* This is the main action execution function
2033 * it checks whether it is time to run the next action
2034 * and if it is the case executes it.
2035 *
2036 * If the 'execute-on-idle' property is not TRUE,
2037 * the function will recurse while the actions are run
2038 * synchronously
2039 */
2040 static gboolean
execute_next_action_full(GstValidateScenario * scenario,GstMessage * message)2041 execute_next_action_full (GstValidateScenario * scenario, GstMessage * message)
2042 {
2043 GList *tmp;
2044 gdouble rate = 1.0;
2045 GstClockTime position = -1;
2046 GstValidateAction *act = NULL;
2047 GstValidateActionType *type;
2048
2049 GstValidateScenarioPrivate *priv = scenario->priv;
2050
2051 if (priv->buffering) {
2052 GST_DEBUG_OBJECT (scenario, "Buffering not executing any action");
2053
2054 return G_SOURCE_CONTINUE;
2055 }
2056
2057 if (priv->changing_state || priv->needs_async_done) {
2058 GST_DEBUG_OBJECT (scenario, "Changing state, not executing any action");
2059 return G_SOURCE_CONTINUE;
2060 }
2061
2062 if (scenario->priv->actions)
2063 act = scenario->priv->actions->data;
2064
2065 if (act) {
2066
2067 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_IN_PROGRESS) {
2068 return G_SOURCE_CONTINUE;
2069 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK
2070 && act->repeat <= 0) {
2071 tmp = priv->actions;
2072 priv->actions = g_list_remove_link (priv->actions, tmp);
2073
2074 if (!gst_validate_parse_next_action_playback_time (scenario)) {
2075 g_error ("Could not determine next action playback time!");
2076
2077 return G_SOURCE_REMOVE;
2078 }
2079
2080
2081 GST_INFO_OBJECT (scenario, "Action %" GST_PTR_FORMAT " is DONE now"
2082 " executing next", act->structure);
2083
2084 gst_validate_action_unref (act);
2085 g_list_free (tmp);
2086
2087 if (scenario->priv->actions) {
2088 act = scenario->priv->actions->data;
2089 } else {
2090 _check_scenario_is_done (scenario);
2091 act = NULL;
2092 }
2093 } else if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
2094 if (GST_CLOCK_TIME_IS_VALID (act->priv->timeout)) {
2095 GstClockTime etime =
2096 gst_util_get_timestamp () - act->priv->execution_time;
2097
2098 if (etime > act->priv->timeout) {
2099 gchar *str = gst_structure_to_string (act->structure);
2100
2101 GST_VALIDATE_REPORT (scenario,
2102 SCENARIO_ACTION_EXECUTION_ERROR,
2103 "Action %s timed out after: %" GST_TIME_FORMAT, str,
2104 GST_TIME_ARGS (etime));
2105
2106 g_free (str);
2107 }
2108 }
2109 GST_LOG_OBJECT (scenario, "Action %" GST_PTR_FORMAT " still running",
2110 act->structure);
2111
2112 return G_SOURCE_CONTINUE;
2113 }
2114 }
2115
2116 if (message) {
2117 if (!_check_message_type (scenario, act, message))
2118 return G_SOURCE_CONTINUE;
2119 } else if (!_check_position (scenario, act, &position, &rate)) {
2120 return G_SOURCE_CONTINUE;
2121 }
2122
2123 if (!_should_execute_action (scenario, act, position, rate)) {
2124 _add_execute_actions_gsource (scenario);
2125
2126 return G_SOURCE_CONTINUE;
2127 }
2128
2129 type = _find_action_type (act->type);
2130
2131 GST_DEBUG_OBJECT (scenario, "Executing %" GST_PTR_FORMAT
2132 " at %" GST_TIME_FORMAT, act->structure, GST_TIME_ARGS (position));
2133 priv->seeked_in_pause = FALSE;
2134
2135 if (message)
2136 gst_structure_remove_field (act->structure, "playback-time");
2137 else
2138 gst_structure_remove_field (act->structure, "on-message");
2139
2140 act->priv->state = gst_validate_execute_action (type, act);
2141 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR) {
2142 gchar *str = gst_structure_to_string (act->structure);
2143
2144 GST_VALIDATE_REPORT (scenario,
2145 SCENARIO_ACTION_EXECUTION_ERROR, "Could not execute %s", str);
2146
2147 g_free (str);
2148 }
2149
2150 if (act->repeat > 0 && !gst_validate_action_is_subaction (act)) {
2151 act->repeat--;
2152 }
2153
2154 if (act->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK) {
2155 act->priv->state = _execute_sub_action_action (act);
2156 }
2157
2158 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
2159 tmp = priv->actions;
2160 priv->actions = g_list_remove_link (priv->actions, tmp);
2161
2162 if (!gst_validate_parse_next_action_playback_time (scenario)) {
2163 g_error ("Could not determine next action playback time!");
2164
2165 return G_SOURCE_REMOVE;
2166 }
2167
2168 if (act->priv->state != GST_VALIDATE_EXECUTE_ACTION_INTERLACED)
2169 gst_validate_action_unref (act);
2170 else {
2171 SCENARIO_LOCK (scenario);
2172 priv->interlaced_actions = g_list_append (priv->interlaced_actions, act);
2173 SCENARIO_UNLOCK (scenario);
2174 }
2175
2176 if (priv->actions == NULL)
2177 _check_scenario_is_done (scenario);
2178
2179 g_list_free (tmp);
2180
2181 /* Recurse to the next action if it is possible
2182 * to execute right away */
2183 if (!scenario->priv->execute_on_idle) {
2184 GST_DEBUG_OBJECT (scenario, "linking next action execution");
2185
2186 return execute_next_action (scenario);
2187 } else {
2188 _add_execute_actions_gsource (scenario);
2189 GST_DEBUG_OBJECT (scenario, "Executing only on idle, waiting for"
2190 " next dispatch");
2191
2192 return G_SOURCE_CONTINUE;
2193 }
2194 } else {
2195 GST_DEBUG_OBJECT (scenario, "Remove source, waiting for action"
2196 " to be done.");
2197
2198 SCENARIO_LOCK (scenario);
2199 priv->execute_actions_source_id = 0;
2200 SCENARIO_UNLOCK (scenario);
2201
2202 return G_SOURCE_CONTINUE;
2203 }
2204
2205 return G_SOURCE_CONTINUE;
2206 }
2207
2208 static gboolean
execute_next_action(GstValidateScenario * scenario)2209 execute_next_action (GstValidateScenario * scenario)
2210 {
2211 return execute_next_action_full (scenario, NULL);
2212 }
2213
2214 static gboolean
stop_waiting(GstValidateAction * action)2215 stop_waiting (GstValidateAction * action)
2216 {
2217 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2218
2219 gst_validate_printf (scenario, "Stop waiting\n");
2220
2221 SCENARIO_LOCK (scenario);
2222 scenario->priv->wait_id = 0;
2223 SCENARIO_UNLOCK (scenario);
2224
2225 gst_validate_action_set_done (action);
2226 _add_execute_actions_gsource (scenario);
2227 gst_object_unref (scenario);
2228
2229
2230 return G_SOURCE_REMOVE;
2231 }
2232
2233 static GstElement *_get_target_element (GstValidateScenario * scenario,
2234 GstValidateAction * action);
2235
2236 static void
stop_waiting_signal(GstBin * bin,GstElement * element,GstValidateAction * action)2237 stop_waiting_signal (GstBin * bin, GstElement * element,
2238 GstValidateAction * action)
2239 {
2240 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2241 GstValidateScenarioPrivate *priv = scenario->priv;
2242
2243 g_assert (scenario);
2244 gst_validate_printf (scenario, "Stop waiting for signal\n");
2245
2246 g_signal_handler_disconnect (bin, priv->signal_handler_id);
2247
2248 priv->signal_handler_id = 0;
2249 gst_validate_action_set_done (action);
2250 _add_execute_actions_gsource (scenario);
2251 gst_object_unref (scenario);
2252 }
2253
2254 static GstValidateExecuteActionReturn
_execute_timed_wait(GstValidateScenario * scenario,GstValidateAction * action)2255 _execute_timed_wait (GstValidateScenario * scenario, GstValidateAction * action)
2256 {
2257 GstValidateScenarioPrivate *priv = scenario->priv;
2258 GstClockTime duration;
2259
2260 gdouble wait_multiplier = 1;
2261 const gchar *str_wait_multiplier =
2262 g_getenv ("GST_VALIDATE_SCENARIO_WAIT_MULTIPLIER");
2263
2264 if (str_wait_multiplier) {
2265 errno = 0;
2266 wait_multiplier = g_ascii_strtod (str_wait_multiplier, NULL);
2267
2268 if (errno) {
2269 GST_ERROR ("Could not use the WAIT MULTIPLIER");
2270
2271 wait_multiplier = 1;
2272 }
2273
2274 if (wait_multiplier == 0) {
2275 GST_INFO_OBJECT (scenario, "I have been told not to wait...");
2276 return GST_VALIDATE_EXECUTE_ACTION_OK;
2277 }
2278 }
2279
2280 if (!gst_validate_action_get_clocktime (scenario, action,
2281 "duration", &duration)) {
2282 GST_DEBUG_OBJECT (scenario, "Duration could not be parsed");
2283
2284 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2285 }
2286
2287 duration *= wait_multiplier;
2288
2289 SCENARIO_LOCK (scenario);
2290 if (priv->execute_actions_source_id) {
2291 g_source_remove (priv->execute_actions_source_id);
2292 priv->execute_actions_source_id = 0;
2293 }
2294 SCENARIO_UNLOCK (scenario);
2295
2296 SCENARIO_LOCK (scenario);
2297 priv->wait_id = g_timeout_add (duration / G_USEC_PER_SEC,
2298 (GSourceFunc) stop_waiting, action);
2299 SCENARIO_UNLOCK (scenario);
2300
2301 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2302 }
2303
2304 static GstValidateExecuteActionReturn
_execute_wait_for_signal(GstValidateScenario * scenario,GstValidateAction * action)2305 _execute_wait_for_signal (GstValidateScenario * scenario,
2306 GstValidateAction * action)
2307 {
2308 GstValidateScenarioPrivate *priv = scenario->priv;
2309 const gchar *signal_name = gst_structure_get_string
2310 (action->structure, "signal-name");
2311 GstElement *target;
2312 DECLARE_AND_GET_PIPELINE (scenario, action);
2313
2314 if (signal_name == NULL) {
2315 GST_ERROR ("No signal-name given for wait action");
2316 return GST_VALIDATE_EXECUTE_ACTION_ERROR;
2317 }
2318
2319 target = _get_target_element (scenario, action);
2320 if (target == NULL) {
2321 gst_object_unref (pipeline);
2322
2323 return FALSE;
2324 }
2325
2326 gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
2327
2328 if (priv->execute_actions_source_id) {
2329 g_source_remove (priv->execute_actions_source_id);
2330 priv->execute_actions_source_id = 0;
2331 }
2332
2333 priv->signal_handler_id =
2334 g_signal_connect (target, signal_name, (GCallback) stop_waiting_signal,
2335 action);
2336
2337 gst_object_unref (target);
2338 gst_object_unref (pipeline);
2339
2340 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2341 }
2342
2343 static gboolean
_execute_wait_for_message(GstValidateScenario * scenario,GstValidateAction * action)2344 _execute_wait_for_message (GstValidateScenario * scenario,
2345 GstValidateAction * action)
2346 {
2347 GstValidateScenarioPrivate *priv = scenario->priv;
2348 const gchar *message_type = gst_structure_get_string
2349 (action->structure, "message-type");
2350 DECLARE_AND_GET_PIPELINE (scenario, action);
2351
2352 gst_validate_printf (action, "Waiting for '%s' message\n", message_type);
2353
2354 if (priv->execute_actions_source_id) {
2355 g_source_remove (priv->execute_actions_source_id);
2356 priv->execute_actions_source_id = 0;
2357 }
2358
2359 priv->message_type = g_strdup (message_type);
2360 gst_object_unref (pipeline);
2361
2362 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2363 }
2364
2365 static GstValidateExecuteActionReturn
_execute_wait(GstValidateScenario * scenario,GstValidateAction * action)2366 _execute_wait (GstValidateScenario * scenario, GstValidateAction * action)
2367 {
2368 if (gst_structure_has_field (action->structure, "signal-name")) {
2369 return _execute_wait_for_signal (scenario, action);
2370 } else if (gst_structure_has_field (action->structure, "message-type")) {
2371 return _execute_wait_for_message (scenario, action);
2372 } else {
2373 return _execute_timed_wait (scenario, action);
2374 }
2375
2376 return FALSE;
2377 }
2378
2379 static gboolean
_execute_dot_pipeline(GstValidateScenario * scenario,GstValidateAction * action)2380 _execute_dot_pipeline (GstValidateScenario * scenario,
2381 GstValidateAction * action)
2382 {
2383 gchar *dotname;
2384 gint details = GST_DEBUG_GRAPH_SHOW_ALL;
2385 const gchar *name = gst_structure_get_string (action->structure, "name");
2386 DECLARE_AND_GET_PIPELINE (scenario, action);
2387
2388 gst_structure_get_int (action->structure, "details", &details);
2389 if (name)
2390 dotname = g_strdup_printf ("validate.action.%s", name);
2391 else
2392 dotname = g_strdup ("validate.action.unnamed");
2393
2394 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), details, dotname);
2395
2396 g_free (dotname);
2397 gst_object_unref (pipeline);
2398
2399 return TRUE;
2400 }
2401
2402 static GstElement *
_get_target_element(GstValidateScenario * scenario,GstValidateAction * action)2403 _get_target_element (GstValidateScenario * scenario, GstValidateAction * action)
2404 {
2405 const gchar *name;
2406 GstElement *target;
2407 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2408
2409 if (!pipeline) {
2410 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2411
2412 return NULL;
2413 }
2414
2415 name = gst_structure_get_string (action->structure, "target-element-name");
2416 if (name == NULL) {
2417 gst_object_unref (pipeline);
2418
2419 return NULL;
2420 }
2421
2422 if (g_strcmp0 (GST_OBJECT_NAME (pipeline), name) == 0) {
2423 target = gst_object_ref (pipeline);
2424 } else {
2425 target = gst_bin_get_by_name (GST_BIN (pipeline), name);
2426 }
2427
2428 if (target == NULL)
2429 GST_ERROR ("Target element with given name (%s) not found", name);
2430 gst_object_unref (pipeline);
2431
2432 return target;
2433 }
2434
2435 /**
2436 * _get_target_elements_by_klass:
2437 * @scenario: a #GstValidateScenario
2438 * @action: a #GstValidateAction
2439 *
2440 * Returns all the elements in the pipeline whose GST_ELEMENT_METADATA_KLASS
2441 * matches the 'target-element-klass' of @action and the factory name matches
2442 * the 'target-element-factory-name'.
2443 *
2444 * Returns: (transfer full) (element-type GstElement): a list of #GstElement
2445 */
2446 static GList *
_get_target_elements_by_klass_or_factory_name(GstValidateScenario * scenario,GstValidateAction * action)2447 _get_target_elements_by_klass_or_factory_name (GstValidateScenario * scenario,
2448 GstValidateAction * action)
2449 {
2450 GList *result = NULL;
2451 GstIterator *it;
2452 const gchar *klass, *fname;
2453 GValue v = G_VALUE_INIT, param = G_VALUE_INIT;
2454 gboolean done = FALSE;
2455 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
2456
2457 if (!pipeline) {
2458 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
2459
2460 return NULL;
2461 }
2462
2463 klass = gst_structure_get_string (action->structure, "target-element-klass");
2464 fname =
2465 gst_structure_get_string (action->structure,
2466 "target-element-factory-name");
2467 if (!klass && !fname) {
2468 gst_object_unref (pipeline);
2469
2470 return NULL;
2471 }
2472
2473 if (klass && gst_validate_element_has_klass (pipeline, klass))
2474 result = g_list_prepend (result, gst_object_ref (pipeline));
2475
2476 if (fname && gst_element_get_factory (pipeline)
2477 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (pipeline)),
2478 fname))
2479 result = g_list_prepend (result, gst_object_ref (pipeline));
2480
2481 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
2482
2483 g_value_init (¶m, G_TYPE_STRING);
2484 g_value_set_string (¶m, klass);
2485
2486 while (!done) {
2487 switch (gst_iterator_next (it, &v)) {
2488 case GST_ITERATOR_OK:{
2489 GstElement *child = g_value_get_object (&v);
2490
2491 if (g_list_find (result, child))
2492 goto next;
2493
2494 if (klass && gst_validate_element_has_klass (child, klass)) {
2495 result = g_list_prepend (result, gst_object_ref (child));
2496 goto next;
2497 }
2498
2499 if (fname && gst_element_get_factory (child)
2500 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (child)),
2501 fname))
2502 result = g_list_prepend (result, gst_object_ref (child));
2503 next:
2504 g_value_reset (&v);
2505 }
2506 break;
2507 case GST_ITERATOR_RESYNC:
2508 gst_iterator_resync (it);
2509 break;
2510 case GST_ITERATOR_ERROR:
2511 case GST_ITERATOR_DONE:
2512 done = TRUE;
2513 }
2514 }
2515
2516 g_value_reset (&v);
2517 g_value_reset (¶m);
2518 gst_iterator_free (it);
2519 gst_object_unref (pipeline);
2520
2521 return result;
2522 }
2523
2524 static gboolean
_execute_set_property(GstValidateScenario * scenario,GstValidateAction * action)2525 _execute_set_property (GstValidateScenario * scenario,
2526 GstValidateAction * action)
2527 {
2528 GstElement *target;
2529 GList *targets = NULL, *l;
2530 const gchar *property;
2531 const GValue *property_value;
2532 gboolean ret = TRUE;
2533
2534 /* set-property can be applied on either:
2535 * - a single element having target-element-name as name
2536 * - all the elements having target-element-klass as klass
2537 */
2538 if (gst_structure_get_string (action->structure, "target-element-name")) {
2539 target = _get_target_element (scenario, action);
2540 if (target == NULL)
2541 return FALSE;
2542
2543 targets = g_list_append (targets, target);
2544 } else if (gst_structure_get_string (action->structure,
2545 "target-element-klass") ||
2546 gst_structure_get_string (action->structure,
2547 "target-element-factory-name")) {
2548 targets = _get_target_elements_by_klass_or_factory_name (scenario, action);
2549 } else {
2550 g_assert_not_reached ();
2551 }
2552
2553 property = gst_structure_get_string (action->structure, "property-name");
2554 property_value = gst_structure_get_value (action->structure,
2555 "property-value");
2556
2557 for (l = targets; l != NULL; l = g_list_next (l)) {
2558 GstValidateActionReturn tmpres =
2559 gst_validate_object_set_property (GST_VALIDATE_REPORTER (scenario),
2560 G_OBJECT (l->data), property,
2561 property_value, action->priv->optional);
2562
2563 if (!tmpres)
2564 ret = tmpres;
2565 }
2566
2567 g_list_free_full (targets, gst_object_unref);
2568 return ret;
2569 }
2570
2571 static gboolean
_execute_set_debug_threshold(GstValidateScenario * scenario,GstValidateAction * action)2572 _execute_set_debug_threshold (GstValidateScenario * scenario,
2573 GstValidateAction * action)
2574 {
2575 gchar *str = NULL;
2576 gboolean reset = TRUE;
2577 const gchar *threshold_str;
2578
2579 threshold_str =
2580 gst_structure_get_string (action->structure, "debug-threshold");
2581 if (threshold_str == NULL) {
2582 gint threshold;
2583
2584 if (gst_structure_get_int (action->structure, "debug-threshold",
2585 &threshold))
2586 threshold_str = str = g_strdup_printf ("%i", threshold);
2587 else
2588 return FALSE;
2589 }
2590
2591 gst_structure_get_boolean (action->structure, "reset", &reset);
2592
2593 gst_debug_set_threshold_from_string (threshold_str, reset);
2594
2595 g_free (str);
2596
2597 return TRUE;
2598 }
2599
2600 static gboolean
_execute_emit_signal(GstValidateScenario * scenario,GstValidateAction * action)2601 _execute_emit_signal (GstValidateScenario * scenario,
2602 GstValidateAction * action)
2603 {
2604 GstElement *target;
2605 const gchar *signal_name;
2606
2607 target = _get_target_element (scenario, action);
2608 if (target == NULL) {
2609 return FALSE;
2610 }
2611
2612 signal_name = gst_structure_get_string (action->structure, "signal-name");
2613
2614 /* Right now we don't support arguments to signals as there weren't any use
2615 * cases to cover yet but it should be possible to do so */
2616 g_signal_emit_by_name (target, signal_name, NULL);
2617
2618 gst_object_unref (target);
2619 return TRUE;
2620 }
2621
2622 typedef GstFlowReturn (*ChainWrapperFunction) (GstPad * pad, GstObject * parent,
2623 GstBuffer * buffer, gpointer * user_data, gboolean * remove_wrapper);
2624
2625 typedef struct _ChainWrapperFunctionData
2626 {
2627 GstPadChainFunction wrapped_chain_func;
2628 gpointer wrapped_chain_data;
2629 GDestroyNotify wrapped_chain_notify;
2630 ChainWrapperFunction wrapper_function;
2631 gpointer wrapper_function_user_data;
2632 } ChainWrapperFunctionData;
2633
2634 static GstFlowReturn
_pad_chain_wrapper(GstPad * pad,GstObject * parent,GstBuffer * buffer)2635 _pad_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer)
2636 {
2637 ChainWrapperFunctionData *data = pad->chaindata;
2638 GstFlowReturn ret;
2639 gboolean remove_wrapper = FALSE;
2640
2641 pad->chainfunc = data->wrapped_chain_func;
2642 pad->chaindata = data->wrapped_chain_data;
2643 pad->chainnotify = data->wrapped_chain_notify;
2644
2645 ret = data->wrapper_function (pad, parent, buffer,
2646 data->wrapper_function_user_data, &remove_wrapper);
2647
2648 if (!remove_wrapper) {
2649 /* The chain function may have changed during the calling (e.g. if it was
2650 * a nested wrapper that decided to remove itself) so we need to update the
2651 * wrapped function just in case. */
2652 data->wrapped_chain_func = pad->chainfunc;
2653 data->wrapped_chain_data = pad->chaindata;
2654 data->wrapped_chain_notify = pad->chainnotify;
2655
2656 /* Restore the wrapper as chain function */
2657 pad->chainfunc = _pad_chain_wrapper;
2658 pad->chaindata = data;
2659 pad->chainnotify = g_free;
2660 } else
2661 g_free (data);
2662
2663 return ret;
2664 }
2665
2666 static void
wrap_pad_chain_function(GstPad * pad,ChainWrapperFunction new_function,gpointer user_data)2667 wrap_pad_chain_function (GstPad * pad, ChainWrapperFunction new_function,
2668 gpointer user_data)
2669 {
2670 ChainWrapperFunctionData *data = g_new (ChainWrapperFunctionData, 1);
2671 data->wrapped_chain_func = pad->chainfunc;
2672 data->wrapped_chain_data = pad->chaindata;
2673 data->wrapped_chain_notify = pad->chainnotify;
2674 data->wrapper_function = new_function;
2675 data->wrapper_function_user_data = user_data;
2676
2677 pad->chainfunc = _pad_chain_wrapper;
2678 pad->chaindata = data;
2679 pad->chainnotify = g_free;
2680 }
2681
2682 static GstFlowReturn
appsrc_push_chain_wrapper(GstPad * pad,GstObject * parent,GstBuffer * buffer,gpointer * user_data,gboolean * remove_wrapper)2683 appsrc_push_chain_wrapper (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2684 gpointer * user_data, gboolean * remove_wrapper)
2685 {
2686 GstValidateAction *action = (GstValidateAction *) user_data;
2687 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2688 GstFlowReturn ret;
2689 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
2690 ret = pad->chainfunc (pad, parent, buffer);
2691 gst_validate_action_set_done (action);
2692 gst_validate_action_unref (action);
2693 *remove_wrapper = TRUE;
2694 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
2695 g_object_unref (scenario);
2696 return ret;
2697 }
2698
2699 static gboolean
structure_get_uint64_permissive(const GstStructure * structure,const gchar * fieldname,guint64 * dest)2700 structure_get_uint64_permissive (const GstStructure * structure,
2701 const gchar * fieldname, guint64 * dest)
2702 {
2703 const GValue *original;
2704 GValue transformed = G_VALUE_INIT;
2705
2706 original = gst_structure_get_value (structure, fieldname);
2707 if (!original)
2708 return FALSE;
2709
2710 g_value_init (&transformed, G_TYPE_UINT64);
2711 if (!g_value_transform (original, &transformed))
2712 return FALSE;
2713
2714 *dest = g_value_get_uint64 (&transformed);
2715 g_value_unset (&transformed);
2716 return TRUE;
2717 }
2718
2719 static gint
_execute_appsrc_push(GstValidateScenario * scenario,GstValidateAction * action)2720 _execute_appsrc_push (GstValidateScenario * scenario,
2721 GstValidateAction * action)
2722 {
2723 GstElement *target;
2724 gchar *file_name;
2725 gchar *file_contents;
2726 gsize file_length;
2727 GError *error = NULL;
2728 GstBuffer *buffer;
2729 guint64 offset = 0;
2730 guint64 size = -1;
2731 gint push_buffer_ret;
2732 gboolean wait;
2733
2734 /* We will only wait for the the buffer to be pushed if we are in a state
2735 * that allows flow of buffers (>=PAUSED). Otherwise the buffer will just
2736 * be enqueued. */
2737 wait = scenario->priv->target_state >= GST_STATE_PAUSED;
2738
2739 target = _get_target_element (scenario, action);
2740 if (target == NULL) {
2741 gchar *structure_string = gst_structure_to_string (action->structure);
2742 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2743 "No element found for action: %s", structure_string);
2744 g_free (structure_string);
2745 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2746 }
2747
2748 file_name =
2749 g_strdup (gst_structure_get_string (action->structure, "file-name"));
2750 if (file_name == NULL) {
2751 gchar *structure_string = gst_structure_to_string (action->structure);
2752 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2753 "Missing file-name property: %s", structure_string);
2754 g_free (structure_string);
2755 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2756 }
2757
2758 structure_get_uint64_permissive (action->structure, "offset", &offset);
2759 structure_get_uint64_permissive (action->structure, "size", &size);
2760
2761 g_file_get_contents (file_name, &file_contents, &file_length, &error);
2762 if (error != NULL) {
2763 gchar *structure_string = gst_structure_to_string (action->structure);
2764 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2765 "Could not open file for action: %s. Error: %s", structure_string,
2766 error->message);
2767 g_free (structure_string);
2768 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2769 }
2770 buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, file_contents,
2771 file_length, offset, (size == -1 ? file_length : size), NULL, g_free);
2772
2773 {
2774 const GValue *caps_value;
2775 caps_value = gst_structure_get_value (action->structure, "caps");
2776 if (caps_value)
2777 g_object_set (target, "caps", gst_value_get_caps (caps_value), NULL);
2778 }
2779
2780 /* We temporarily override the peer pad chain function to finish the action
2781 * once the buffer chain actually ends. */
2782 {
2783 GstPad *appsrc_pad = gst_element_get_static_pad (target, "src");
2784 GstPad *peer_pad = gst_pad_get_peer (appsrc_pad);
2785 if (!peer_pad) {
2786 gchar *structure_string = gst_structure_to_string (action->structure);
2787 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2788 "Action failed, pad not linked: %s", structure_string);
2789 g_free (structure_string);
2790 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2791 }
2792
2793 wrap_pad_chain_function (peer_pad, appsrc_push_chain_wrapper, action);
2794
2795 gst_object_unref (appsrc_pad);
2796 gst_object_unref (peer_pad);
2797 }
2798
2799 /* Keep the action alive until set done is called. */
2800 gst_validate_action_ref (action);
2801
2802 g_signal_emit_by_name (target, "push-buffer", buffer, &push_buffer_ret);
2803 if (push_buffer_ret != GST_FLOW_OK) {
2804 gchar *structure_string = gst_structure_to_string (action->structure);
2805 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2806 "push-buffer signal failed in action: %s", structure_string);
2807 g_free (structure_string);
2808 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2809 }
2810
2811 g_free (file_name);
2812 gst_object_unref (target);
2813
2814 if (wait) {
2815 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
2816 } else {
2817 gst_validate_printf (NULL,
2818 "Pipeline is not ready to push buffers, interlacing appsrc-push action...");
2819 return GST_VALIDATE_EXECUTE_ACTION_INTERLACED;
2820 }
2821 }
2822
2823 static gint
_execute_appsrc_eos(GstValidateScenario * scenario,GstValidateAction * action)2824 _execute_appsrc_eos (GstValidateScenario * scenario, GstValidateAction * action)
2825 {
2826 GstElement *target;
2827 gint eos_ret;
2828
2829 target = _get_target_element (scenario, action);
2830 if (target == NULL) {
2831 gchar *structure_string = gst_structure_to_string (action->structure);
2832 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2833 "No element found for action: %s", structure_string);
2834 g_free (structure_string);
2835 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2836 }
2837
2838 g_signal_emit_by_name (target, "end-of-stream", &eos_ret);
2839 if (eos_ret != GST_FLOW_OK) {
2840 gchar *structure_string = gst_structure_to_string (action->structure);
2841 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2842 "Failed to emit end-of-stream signal for action: %s", structure_string);
2843 g_free (structure_string);
2844 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2845 }
2846
2847 gst_object_unref (target);
2848 return GST_VALIDATE_EXECUTE_ACTION_OK;
2849 }
2850
2851 static gint
_execute_flush(GstValidateScenario * scenario,GstValidateAction * action)2852 _execute_flush (GstValidateScenario * scenario, GstValidateAction * action)
2853 {
2854 GstElement *target;
2855 GstEvent *event;
2856 gboolean reset_time = TRUE;
2857
2858 target = _get_target_element (scenario, action);
2859 if (target == NULL) {
2860 gchar *structure_string = gst_structure_to_string (action->structure);
2861 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2862 "No element found for action: %s", structure_string);
2863 g_free (structure_string);
2864 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2865 }
2866
2867 gst_structure_get_boolean (action->structure, "reset-time", &reset_time);
2868
2869 event = gst_event_new_flush_start ();
2870 if (!gst_element_send_event (target, event)) {
2871 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2872 "FLUSH_START event was not handled");
2873 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2874 }
2875
2876 event = gst_event_new_flush_stop (reset_time);
2877 if (!gst_element_send_event (target, event)) {
2878 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2879 "FLUSH_STOP event was not handled");
2880 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2881 }
2882
2883 return GST_VALIDATE_EXECUTE_ACTION_OK;
2884 }
2885
2886 static GstValidateExecuteActionReturn
_execute_disable_plugin(GstValidateScenario * scenario,GstValidateAction * action)2887 _execute_disable_plugin (GstValidateScenario * scenario,
2888 GstValidateAction * action)
2889 {
2890 GstPlugin *plugin;
2891 const gchar *plugin_name;
2892
2893 plugin_name = gst_structure_get_string (action->structure, "plugin-name");
2894
2895 plugin = gst_registry_find_plugin (gst_registry_get (), plugin_name);
2896
2897 if (plugin == NULL) {
2898 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
2899 "Could not find plugin to disable: %s", plugin_name);
2900
2901 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
2902 }
2903
2904 gst_validate_printf (action, "Disabling plugin \"%s\"\n", plugin_name);
2905 gst_registry_remove_plugin (gst_registry_get (), plugin);
2906
2907 return GST_VALIDATE_EXECUTE_ACTION_OK;
2908 }
2909
2910 static void
gst_validate_scenario_update_segment_from_seek(GstValidateScenario * scenario,GstEvent * seek)2911 gst_validate_scenario_update_segment_from_seek (GstValidateScenario * scenario,
2912 GstEvent * seek)
2913 {
2914 GstValidateScenarioPrivate *priv = scenario->priv;
2915 gint64 start, stop;
2916 GstSeekType start_type, stop_type;
2917
2918 gst_event_parse_seek (seek, NULL, NULL, NULL, &start_type, &start,
2919 &stop_type, &stop);
2920
2921 if (start_type == GST_SEEK_TYPE_SET) {
2922 priv->segment_start = start;
2923 } else if (start_type == GST_SEEK_TYPE_END) {
2924 /* TODO fill me */
2925 }
2926
2927 if (stop_type == GST_SEEK_TYPE_SET) {
2928 priv->segment_stop = stop;
2929 } else if (stop_type == GST_SEEK_TYPE_END) {
2930 /* TODO fill me */
2931 }
2932 }
2933
2934 static gboolean
_structure_set_variables(GQuark field_id,GValue * value,GstValidateAction * action)2935 _structure_set_variables (GQuark field_id, GValue * value,
2936 GstValidateAction * action)
2937 {
2938 gchar *str;
2939 GstValidateScenario *scenario;
2940
2941 if (!G_VALUE_HOLDS_STRING (value))
2942 return TRUE;
2943
2944 scenario = gst_validate_action_get_scenario (action);
2945 if (!scenario)
2946 return TRUE;
2947
2948 str =
2949 _replace_variables_in_string (scenario, action,
2950 g_value_get_string (value));
2951 if (str) {
2952 g_value_set_string (value, str);
2953 g_free (str);
2954 }
2955 gst_object_unref (scenario);
2956
2957 return TRUE;
2958 }
2959
2960 static gboolean
gst_validate_action_default_prepare_func(GstValidateAction * action)2961 gst_validate_action_default_prepare_func (GstValidateAction * action)
2962 {
2963 gint i;
2964 GstClockTime tmp;
2965 gchar *repeat_expr;
2966 gchar *error = NULL;
2967 GstValidateActionType *type = gst_validate_get_action_type (action->type);
2968 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
2969
2970 gst_structure_filter_and_map_in_place (action->structure,
2971 (GstStructureFilterMapFunc) _structure_set_variables, action);
2972
2973 for (i = 0; type->parameters[i].name; i++) {
2974 if (g_str_has_suffix (type->parameters[i].types, "(GstClockTime)"))
2975 gst_validate_action_get_clocktime (scenario, action,
2976 type->parameters[i].name, &tmp);
2977 }
2978
2979 if (action->repeat > 0)
2980 return TRUE;
2981
2982 if (!gst_structure_has_field (action->structure, "repeat"))
2983 return TRUE;
2984
2985 if (gst_structure_get_int (action->structure, "repeat", &action->repeat))
2986 return TRUE;
2987
2988 if (gst_structure_get_double (action->structure, "repeat",
2989 (gdouble *) & action->repeat))
2990 return TRUE;
2991
2992 repeat_expr =
2993 g_strdup (gst_structure_get_string (action->structure, "repeat"));
2994 if (!repeat_expr) {
2995 g_error ("Invalid value for 'repeat' in %s",
2996 gst_structure_to_string (action->structure));
2997
2998 return FALSE;
2999 }
3000
3001 action->repeat =
3002 gst_validate_utils_parse_expression (repeat_expr, _set_variable_func,
3003 scenario, &error);
3004 if (error) {
3005 g_error ("Invalid value for 'repeat' in %s: %s",
3006 gst_structure_to_string (action->structure), error);
3007
3008 return FALSE;
3009 }
3010 g_free (repeat_expr);
3011
3012 gst_structure_set (action->structure, "repeat", G_TYPE_INT, action->repeat,
3013 NULL);
3014 gst_structure_set (action->priv->main_structure, "repeat", G_TYPE_INT,
3015 action->repeat, NULL);
3016
3017 if (scenario)
3018 gst_object_unref (scenario);
3019
3020 return TRUE;
3021 }
3022
3023 static void
_check_waiting_for_message(GstValidateScenario * scenario,GstMessage * message)3024 _check_waiting_for_message (GstValidateScenario * scenario,
3025 GstMessage * message)
3026 {
3027 GstValidateScenarioPrivate *priv = scenario->priv;
3028
3029 if (!g_strcmp0 (priv->message_type,
3030 gst_message_type_get_name (GST_MESSAGE_TYPE (message)))) {
3031 GstValidateAction *action = scenario->priv->actions->data;
3032
3033 g_free ((gpointer) priv->message_type);
3034 priv->message_type = NULL;
3035
3036 gst_validate_printf (scenario, "Stop waiting for message\n");
3037
3038 gst_validate_action_set_done (action);
3039 _add_execute_actions_gsource (scenario);
3040 }
3041 }
3042
3043 static gboolean
streams_list_contain(GList * streams,const gchar * stream_id)3044 streams_list_contain (GList * streams, const gchar * stream_id)
3045 {
3046 GList *l;
3047
3048 for (l = streams; l; l = g_list_next (l)) {
3049 GstStream *s = l->data;
3050
3051 if (!g_strcmp0 (s->stream_id, stream_id))
3052 return TRUE;
3053 }
3054
3055 return FALSE;
3056 }
3057
3058 static void
gst_validate_scenario_check_latency(GstValidateScenario * scenario,GstElement * pipeline)3059 gst_validate_scenario_check_latency (GstValidateScenario * scenario,
3060 GstElement * pipeline)
3061 {
3062 GstValidateScenarioPrivate *priv = scenario->priv;
3063 GstQuery *query;
3064 GstClockTime min_latency;
3065
3066 query = gst_query_new_latency ();
3067 if (!gst_element_query (GST_ELEMENT_CAST (pipeline), query)) {
3068 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3069 "Failed to perfom LATENCY query");
3070 gst_query_unref (query);
3071 return;
3072 }
3073
3074 gst_query_parse_latency (query, NULL, &min_latency, NULL);
3075 GST_DEBUG_OBJECT (scenario, "Pipeline latency: %" GST_TIME_FORMAT
3076 " max allowed: %" GST_TIME_FORMAT,
3077 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (priv->max_latency));
3078
3079 if (priv->max_latency != GST_CLOCK_TIME_NONE &&
3080 min_latency > priv->max_latency) {
3081 GST_VALIDATE_REPORT (scenario, CONFIG_LATENCY_TOO_HIGH,
3082 "Pipeline latency is too high: %" GST_TIME_FORMAT " (max allowed %"
3083 GST_TIME_FORMAT ")", GST_TIME_ARGS (min_latency),
3084 GST_TIME_ARGS (priv->max_latency));
3085 }
3086 }
3087
3088 static gboolean
message_cb(GstBus * bus,GstMessage * message,GstValidateScenario * scenario)3089 message_cb (GstBus * bus, GstMessage * message, GstValidateScenario * scenario)
3090 {
3091 gboolean is_error = FALSE;
3092 GstValidateScenarioPrivate *priv = scenario->priv;
3093 GstElement *pipeline = gst_validate_scenario_get_pipeline (scenario);
3094
3095 if (!pipeline) {
3096 GST_ERROR_OBJECT (scenario, "No pipeline set anymore!");
3097
3098 return FALSE;
3099 }
3100
3101 switch (GST_MESSAGE_TYPE (message)) {
3102 case GST_MESSAGE_ASYNC_DONE:
3103 if (priv->last_seek) {
3104 gst_validate_scenario_update_segment_from_seek (scenario,
3105 priv->last_seek);
3106
3107 if (priv->target_state == GST_STATE_PAUSED)
3108 priv->seeked_in_pause = TRUE;
3109
3110 gst_event_replace (&priv->last_seek, NULL);
3111 gst_validate_action_set_done (priv->actions->data);
3112 } else if (scenario->priv->needs_async_done) {
3113 scenario->priv->needs_async_done = FALSE;
3114 if (priv->actions && _action_sets_state (priv->actions->data)
3115 && !priv->changing_state)
3116 gst_validate_action_set_done (priv->actions->data);
3117
3118 }
3119
3120 if (scenario->priv->needs_playback_parsing) {
3121 scenario->priv->needs_playback_parsing = FALSE;
3122 if (!gst_validate_parse_next_action_playback_time (scenario))
3123 return FALSE;
3124 }
3125 _add_execute_actions_gsource (scenario);
3126 break;
3127 case GST_MESSAGE_STATE_CHANGED:
3128 {
3129 if (pipeline && GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
3130 GstState nstate, pstate;
3131
3132 gst_message_parse_state_changed (message, &pstate, &nstate, NULL);
3133
3134 if (scenario->priv->changing_state &&
3135 scenario->priv->target_state == nstate) {
3136 scenario->priv->changing_state = FALSE;
3137
3138 if (priv->actions && _action_sets_state (priv->actions->data) &&
3139 !priv->needs_async_done)
3140 gst_validate_action_set_done (priv->actions->data);
3141 }
3142
3143 if (pstate == GST_STATE_READY && nstate == GST_STATE_PAUSED)
3144 _add_execute_actions_gsource (scenario);
3145
3146 /* GstBin only send a new latency message when reaching PLAYING if
3147 * async-handling=true so check the latency manually. */
3148 if (nstate == GST_STATE_PLAYING)
3149 gst_validate_scenario_check_latency (scenario, pipeline);
3150 }
3151 break;
3152 }
3153 case GST_MESSAGE_ERROR:
3154 is_error = TRUE;
3155
3156 /* Passtrough */
3157 case GST_MESSAGE_EOS:
3158 {
3159 GstValidateAction *stop_action;
3160 GstValidateActionType *stop_action_type;
3161 GstStructure *s;
3162
3163 if (!is_error && scenario->priv->ignore_eos) {
3164 GST_INFO_OBJECT (scenario, "Got EOS but ignoring it!");
3165 goto done;
3166 }
3167
3168 GST_VALIDATE_SCENARIO_EOS_HANDLING_LOCK (scenario);
3169 {
3170 /* gst_validate_action_set_done() does not finish the action
3171 * immediately. Instead, it posts a task to the main thread to do most
3172 * of the work in _action_set_done().
3173 *
3174 * While the EOS handling lock guarantees that if an action had to call
3175 * gst_validate_action_set_done() it has done so, it does not guarantee
3176 * that _action_set_done() has been called.
3177 *
3178 * Is it possible that this handler is run before _action_set_done(), so
3179 * we check at this point for actions that have a pending_set_done and
3180 * call it before continuing. */
3181 GList *actions = g_list_copy (priv->actions);
3182 GList *i;
3183 for (i = actions; i; i = i->next) {
3184 GstValidateAction *action = (GstValidateAction *) i->data;
3185 if (action->priv->pending_set_done)
3186 _action_set_done (action);
3187 }
3188 g_list_free (actions);
3189 }
3190
3191 if (!is_error) {
3192 priv->got_eos = TRUE;
3193 if (priv->message_type) {
3194
3195 if (priv->actions->next) {
3196 GST_DEBUG_OBJECT (scenario,
3197 "Waiting for a message and got a next action"
3198 " to execute, letting it a chance!");
3199 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3200 goto done;
3201 } else {
3202 /* Clear current message wait if waiting for EOS */
3203 _check_waiting_for_message (scenario, message);
3204 }
3205 }
3206 }
3207
3208 SCENARIO_LOCK (scenario);
3209 if (scenario->priv->actions || scenario->priv->interlaced_actions ||
3210 scenario->priv->on_addition_actions) {
3211 guint nb_actions = 0;
3212 gchar *actions = g_strdup (""), *tmpconcat;
3213 GList *tmp;
3214 GList *all_actions =
3215 g_list_concat (g_list_concat (scenario->priv->actions,
3216 scenario->priv->interlaced_actions),
3217 scenario->priv->on_addition_actions);
3218
3219 for (tmp = all_actions; tmp; tmp = tmp->next) {
3220 gchar *action_string;
3221 GstValidateAction *action = (GstValidateAction *) tmp->data;
3222 GstValidateActionType *type = _find_action_type (action->type);
3223
3224 tmpconcat = actions;
3225
3226 if (type->flags & GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL ||
3227 action->priv->state == GST_VALIDATE_EXECUTE_ACTION_OK ||
3228 action->priv->optional) {
3229 gst_validate_action_unref (action);
3230
3231 continue;
3232 }
3233
3234 nb_actions++;
3235
3236 action_string = gst_structure_to_string (action->structure);
3237 actions =
3238 g_strdup_printf ("%s\n%*s%s", actions, 20, "", action_string);
3239 gst_validate_action_unref (action);
3240 g_free (tmpconcat);
3241 g_free (action_string);
3242 }
3243 g_list_free (all_actions);
3244 scenario->priv->actions = NULL;
3245 scenario->priv->interlaced_actions = NULL;
3246 scenario->priv->on_addition_actions = NULL;
3247
3248
3249 if (nb_actions > 0) {
3250 GstClockTime position = GST_CLOCK_TIME_NONE;
3251
3252 _get_position (scenario, NULL, &position);
3253 GST_VALIDATE_REPORT (scenario, SCENARIO_NOT_ENDED,
3254 "%i actions were not executed: %s (position: %" GST_TIME_FORMAT
3255 ")", nb_actions, actions, GST_TIME_ARGS (position));
3256 }
3257 g_free (actions);
3258 }
3259 /* Make sure that if there is an ASYNC_DONE in the message queue, we do not
3260 take it into account */
3261 gst_event_replace (&priv->last_seek, NULL);
3262 SCENARIO_UNLOCK (scenario);
3263
3264 GST_DEBUG_OBJECT (scenario, "Got EOS; generate 'stop' action");
3265
3266 stop_action_type = _find_action_type ("stop");
3267 s = gst_structure_from_string ("stop, generated-after-eos=true;", NULL);
3268 stop_action = gst_validate_action_new (scenario, stop_action_type,
3269 s, FALSE);
3270 gst_structure_free (s);
3271 gst_validate_execute_action (stop_action_type, stop_action);
3272 gst_mini_object_unref (GST_MINI_OBJECT (stop_action));
3273
3274 GST_VALIDATE_SCENARIO_EOS_HANDLING_UNLOCK (scenario);
3275 break;
3276 }
3277 case GST_MESSAGE_BUFFERING:
3278 {
3279 gint percent;
3280
3281 gst_message_parse_buffering (message, &percent);
3282
3283 if (percent == 100)
3284 priv->buffering = FALSE;
3285 else
3286 priv->buffering = TRUE;
3287 break;
3288 }
3289 case GST_MESSAGE_STREAMS_SELECTED:
3290 {
3291 guint i;
3292 GList *streams_selected = NULL;
3293
3294 for (i = 0; i < gst_message_streams_selected_get_size (message); i++) {
3295 GstStream *stream =
3296 gst_message_streams_selected_get_stream (message, i);
3297
3298 streams_selected = g_list_append (streams_selected, stream);
3299 }
3300
3301 /* Is there a pending switch-track action waiting for the new streams to
3302 * be selected? */
3303 if (priv->pending_switch_track) {
3304 GList *expected, *l;
3305 GstValidateScenario *scenario =
3306 gst_validate_action_get_scenario (priv->pending_switch_track);
3307
3308 expected =
3309 gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST
3310 (priv->pending_switch_track), ACTION_EXPECTED_STREAM_QUARK);
3311
3312 if (g_list_length (expected) != g_list_length (streams_selected)) {
3313 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
3314 "Was expecting %d selected streams but got %d",
3315 g_list_length (expected), g_list_length (streams_selected));
3316 goto action_done;
3317 }
3318
3319 for (l = expected; l; l = g_list_next (l)) {
3320 const gchar *stream_id = l->data;
3321
3322 if (!streams_list_contain (streams_selected, stream_id)) {
3323 GST_VALIDATE_REPORT (scenario,
3324 SCENARIO_ACTION_EXECUTION_ERROR,
3325 "Stream %s has not be activated", stream_id);
3326 goto action_done;
3327 }
3328 }
3329
3330 action_done:
3331 gst_object_unref (scenario);
3332 gst_validate_action_set_done (priv->pending_switch_track);
3333 priv->pending_switch_track = NULL;
3334 }
3335
3336 g_list_free_full (streams_selected, gst_object_unref);
3337 break;
3338 }
3339 case GST_MESSAGE_LATENCY:
3340 gst_validate_scenario_check_latency (scenario, pipeline);
3341 break;
3342
3343 case GST_MESSAGE_QOS:
3344 {
3345 guint64 dropped;
3346
3347 /* Check the maximum allowed when scenario is terminating so the report
3348 * will include the actual number of dropped buffers. */
3349 gst_message_parse_qos_stats (message, NULL, NULL, &dropped);
3350 if (dropped != -1)
3351 priv->dropped = dropped;
3352 break;
3353 }
3354
3355 default:
3356 break;
3357 }
3358
3359 done:
3360 gst_object_unref (pipeline);
3361 /* Check if we got the message expected by a wait action */
3362 if (priv->message_type)
3363 _check_waiting_for_message (scenario, message);
3364
3365 execute_next_action_full (scenario, message);
3366
3367 return TRUE;
3368 }
3369
3370 static gboolean
_action_type_has_parameter(GstValidateActionType * atype,const gchar * paramname)3371 _action_type_has_parameter (GstValidateActionType * atype,
3372 const gchar * paramname)
3373 {
3374 gint i;
3375
3376 if (!atype->parameters)
3377 return FALSE;
3378
3379 for (i = 0; atype->parameters[i].name; i++)
3380 if (g_strcmp0 (atype->parameters[i].name, paramname) == 0)
3381 return TRUE;
3382
3383 return FALSE;
3384 }
3385
3386 static gboolean
_load_scenario_file(GstValidateScenario * scenario,const gchar * scenario_file,gboolean * is_config)3387 _load_scenario_file (GstValidateScenario * scenario,
3388 const gchar * scenario_file, gboolean * is_config)
3389 {
3390 gboolean ret = TRUE;
3391 GList *structures, *tmp;
3392 GstValidateScenarioPrivate *priv = scenario->priv;
3393 GList *config;
3394
3395 *is_config = FALSE;
3396
3397 structures = gst_validate_utils_structs_parse_from_filename (scenario_file);
3398 if (structures == NULL)
3399 goto failed;
3400
3401 for (tmp = structures; tmp; tmp = tmp->next) {
3402 GstValidateAction *action;
3403 GstValidateActionType *action_type;
3404 const gchar *type;
3405 GstStructure *structure = (GstStructure *) tmp->data;
3406
3407 type = gst_structure_get_name (structure);
3408 if (!g_strcmp0 (type, "description")) {
3409 const gchar *pipeline_name;
3410
3411 gst_structure_get_boolean (structure, "is-config", is_config);
3412 gst_structure_get_boolean (structure, "handles-states",
3413 &priv->handles_state);
3414 gst_structure_get_boolean (structure, "ignore-eos", &priv->ignore_eos);
3415
3416 if (!priv->handles_state)
3417 priv->target_state = GST_STATE_PLAYING;
3418
3419 pipeline_name = gst_structure_get_string (structure, "pipeline-name");
3420 if (pipeline_name) {
3421 g_free (priv->pipeline_name);
3422 priv->pipeline_name = g_strdup (pipeline_name);
3423 }
3424
3425 gst_validate_utils_get_clocktime (structure, "max-latency",
3426 &priv->max_latency);
3427
3428 gst_structure_get_int (structure, "max-dropped", &priv->max_dropped);
3429
3430 continue;
3431 } else if (!g_strcmp0 (type, "include")) {
3432 const gchar *location = gst_structure_get_string (structure, "location");
3433
3434 if (!location) {
3435 GST_ERROR_OBJECT (scenario,
3436 "Mandatory field 'location' not present in structure: %"
3437 GST_PTR_FORMAT, structure);
3438 goto failed;
3439 }
3440
3441 if (!gst_validate_scenario_load (scenario, location, scenario_file)) {
3442 GST_ERROR ("Failed including scenario %s", location);
3443 goto failed;
3444 }
3445
3446 continue;
3447 } else if (!(action_type = _find_action_type (type))) {
3448 if (gst_structure_has_field (structure, "optional-action-type")) {
3449 GST_INFO_OBJECT (scenario,
3450 "Action type not found %s but marked as not mandatory", type);
3451 continue;
3452 }
3453
3454 GST_ERROR_OBJECT (scenario, "We do not handle action types %s", type);
3455 goto failed;
3456 }
3457
3458 if (action_type->parameters) {
3459 guint i;
3460
3461 for (i = 0; action_type->parameters[i].name; i++) {
3462 if (action_type->parameters[i].mandatory &&
3463 gst_structure_has_field (structure,
3464 action_type->parameters[i].name) == FALSE) {
3465 GST_ERROR_OBJECT (scenario,
3466 "Mandatory field '%s' not present in structure: %" GST_PTR_FORMAT,
3467 action_type->parameters[i].name, structure);
3468 goto failed;
3469 }
3470 }
3471 }
3472
3473 action = gst_validate_action_new (scenario, action_type, structure, TRUE);
3474 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_ERROR)
3475 goto failed;
3476
3477 action->action_number = priv->num_actions++;
3478 }
3479
3480 /* max-latency and max-dropped can be overriden using config */
3481 for (config = gst_validate_plugin_get_config (NULL); config;
3482 config = g_list_next (config)) {
3483 GstClockTime max_latency;
3484
3485 gst_validate_utils_get_clocktime (config->data, "max-latency",
3486 &max_latency);
3487 if (GST_CLOCK_TIME_IS_VALID (max_latency))
3488 priv->max_latency = max_latency;
3489
3490 gst_structure_get_int (config->data, "max-dropped", &priv->max_dropped);
3491 }
3492
3493 done:
3494 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
3495
3496 return ret;
3497
3498 failed:
3499 ret = FALSE;
3500
3501 goto done;
3502 }
3503
3504 static gboolean
gst_validate_scenario_load(GstValidateScenario * scenario,const gchar * scenario_name,const gchar * relative_scenario)3505 gst_validate_scenario_load (GstValidateScenario * scenario,
3506 const gchar * scenario_name, const gchar * relative_scenario)
3507 {
3508 gchar **scenarios = NULL;
3509 guint i;
3510 gboolean found_actions = FALSE, is_config, ret = TRUE;
3511 gchar *scenarios_path = g_strdup (g_getenv ("GST_VALIDATE_SCENARIOS_PATH"));
3512
3513 gchar **env_scenariodir;
3514
3515 if (relative_scenario) {
3516 gchar *relative_dir = g_path_get_dirname (relative_scenario);
3517 gchar *tmp_scenarios_path =
3518 g_strdup_printf ("%s%c%s", scenarios_path, G_SEARCHPATH_SEPARATOR,
3519 relative_dir);
3520
3521 g_free (scenarios_path);
3522 scenarios_path = tmp_scenarios_path;
3523 }
3524
3525 env_scenariodir =
3526 scenarios_path ? g_strsplit (scenarios_path, G_SEARCHPATH_SEPARATOR_S,
3527 0) : NULL;
3528 g_free (scenarios_path);
3529
3530 if (!scenario_name)
3531 goto invalid_name;
3532
3533 scenarios = g_strsplit (scenario_name, ":", -1);
3534
3535 for (i = 0; scenarios[i]; i++) {
3536 gchar *lfilename = NULL, *tldir = NULL;
3537
3538 /* First check if the scenario name is not a full path to the
3539 * actual scenario */
3540 if (g_file_test (scenarios[i], G_FILE_TEST_IS_REGULAR)) {
3541 GST_DEBUG_OBJECT (scenario, "Scenario: %s is a full path to a scenario. "
3542 "Trying to load it", scenarios[i]);
3543 if ((ret = _load_scenario_file (scenario, scenarios[i], &is_config)))
3544 goto check_scenario;
3545 }
3546
3547 if (g_str_has_suffix (scenarios[i], GST_VALIDATE_SCENARIO_SUFFIX))
3548 lfilename = g_strdup (scenarios[i]);
3549 else
3550 lfilename =
3551 g_strdup_printf ("%s" GST_VALIDATE_SCENARIO_SUFFIX, scenarios[i]);
3552
3553 if (env_scenariodir) {
3554 guint i;
3555
3556 for (i = 0; env_scenariodir[i]; i++) {
3557 tldir = g_build_filename (env_scenariodir[i], lfilename, NULL);
3558 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3559 goto check_scenario;
3560 g_free (tldir);
3561 }
3562 }
3563
3564 tldir = g_build_filename ("data", "scenarios", lfilename, NULL);
3565
3566 if ((ret = _load_scenario_file (scenario, tldir, &is_config)))
3567 goto check_scenario;
3568
3569 g_free (tldir);
3570
3571 /* Try from local profiles */
3572 tldir =
3573 g_build_filename (g_get_user_data_dir (),
3574 "gstreamer-" GST_API_VERSION, "validate",
3575 GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3576
3577 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3578 g_free (tldir);
3579 /* Try from system-wide profiles */
3580 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
3581 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, lfilename, NULL);
3582
3583 if (!(ret = _load_scenario_file (scenario, tldir, &is_config))) {
3584 goto error;
3585 }
3586 }
3587 /* else check scenario */
3588 check_scenario:
3589 g_free (tldir);
3590 g_free (lfilename);
3591
3592 if (!is_config) {
3593 if (found_actions == TRUE)
3594 goto one_actions_scenario_max;
3595 else
3596 found_actions = TRUE;
3597 }
3598 }
3599
3600 done:
3601
3602 if (env_scenariodir)
3603 g_strfreev (env_scenariodir);
3604
3605 g_strfreev (scenarios);
3606
3607 if (ret == FALSE)
3608 g_error ("Could not set scenario %s => EXIT\n", scenario_name);
3609
3610 return ret;
3611
3612 invalid_name:
3613 {
3614 GST_ERROR ("Invalid name for scenario '%s'", scenario_name);
3615 error:
3616 ret = FALSE;
3617 goto done;
3618 }
3619 one_actions_scenario_max:
3620 {
3621 GST_ERROR ("You can set at most only one action scenario. "
3622 "You can have several config scenarios though (a config scenario's "
3623 "file must have is-config=true, and all its actions must be executable "
3624 "at parsing time).");
3625 ret = FALSE;
3626 goto done;
3627
3628 }
3629 }
3630
3631
3632 static void
gst_validate_scenario_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)3633 gst_validate_scenario_set_property (GObject * object, guint prop_id,
3634 const GValue * value, GParamSpec * pspec)
3635 {
3636 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3637
3638 switch (prop_id) {
3639 case PROP_RUNNER:
3640 /* we assume the runner is valid as long as this scenario is,
3641 * no ref taken */
3642 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
3643 g_value_get_object (value));
3644 break;
3645 case PROP_HANDLES_STATE:
3646 g_assert_not_reached ();
3647 break;
3648 case PROP_EXECUTE_ON_IDLE:
3649 self->priv->execute_on_idle = g_value_get_boolean (value);
3650 break;
3651 default:
3652 break;
3653 }
3654 }
3655
3656 static void
gst_validate_scenario_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)3657 gst_validate_scenario_get_property (GObject * object, guint prop_id,
3658 GValue * value, GParamSpec * pspec)
3659 {
3660 GstValidateScenario *self = GST_VALIDATE_SCENARIO (object);
3661
3662 switch (prop_id) {
3663 case PROP_RUNNER:
3664 /* we assume the runner is valid as long as this scenario is,
3665 * no ref taken */
3666 g_value_take_object (value,
3667 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
3668 break;
3669 case PROP_HANDLES_STATE:
3670 g_value_set_boolean (value, self->priv->handles_state);
3671 break;
3672 case PROP_EXECUTE_ON_IDLE:
3673 g_value_set_boolean (value, self->priv->execute_on_idle);
3674 break;
3675 default:
3676 break;
3677 }
3678 }
3679
3680 static void
gst_validate_scenario_class_init(GstValidateScenarioClass * klass)3681 gst_validate_scenario_class_init (GstValidateScenarioClass * klass)
3682 {
3683 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3684
3685 object_class->dispose = gst_validate_scenario_dispose;
3686 object_class->finalize = gst_validate_scenario_finalize;
3687
3688 object_class->get_property = gst_validate_scenario_get_property;
3689 object_class->set_property = gst_validate_scenario_set_property;
3690
3691 g_object_class_install_property (object_class, PROP_RUNNER,
3692 g_param_spec_object ("validate-runner", "VALIDATE Runner",
3693 "The Validate runner to report errors to",
3694 GST_TYPE_VALIDATE_RUNNER,
3695 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
3696
3697 g_object_class_install_property (object_class, PROP_HANDLES_STATE,
3698 g_param_spec_boolean ("handles-states", "Handles state",
3699 "True if the application should not handle the first state change. "
3700 "False if it is application responsibility",
3701 FALSE, G_PARAM_READABLE));
3702
3703 g_object_class_install_property (object_class,
3704 PROP_EXECUTE_ON_IDLE,
3705 g_param_spec_boolean ("execute-on-idle",
3706 "Force waiting between actions",
3707 "Always execute actions on idle and do not chain them to execute as"
3708 " fast as possible. Setting this property is useful if action"
3709 " execution can lead to the addition of new sources on the same main"
3710 " loop as it provides these new GSource a chance to be dispatched"
3711 " between actions", FALSE, G_PARAM_READWRITE));
3712
3713 /**
3714 * GstValidateScenario::done:
3715 * @scenario: The scenario runing
3716 *
3717 * Emitted once all actions have been executed
3718 */
3719 scenario_signals[DONE] =
3720 g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
3721 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
3722 }
3723
3724 static void
gst_validate_scenario_init(GstValidateScenario * scenario)3725 gst_validate_scenario_init (GstValidateScenario * scenario)
3726 {
3727 GstValidateScenarioPrivate *priv = scenario->priv =
3728 gst_validate_scenario_get_instance_private (scenario);
3729
3730 priv->seek_pos_tol = DEFAULT_SEEK_TOLERANCE;
3731 priv->segment_start = 0;
3732 priv->segment_stop = GST_CLOCK_TIME_NONE;
3733 priv->action_execution_interval = 10;
3734 priv->vars = gst_structure_new_empty ("vars");
3735 priv->needs_playback_parsing = TRUE;
3736 g_weak_ref_init (&scenario->priv->ref_pipeline, NULL);
3737 priv->max_latency = GST_CLOCK_TIME_NONE;
3738 priv->max_dropped = -1;
3739
3740 g_mutex_init (&priv->lock);
3741 }
3742
3743 static void
gst_validate_scenario_dispose(GObject * object)3744 gst_validate_scenario_dispose (GObject * object)
3745 {
3746 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3747
3748 if (priv->last_seek)
3749 gst_event_unref (priv->last_seek);
3750 g_weak_ref_clear (&priv->ref_pipeline);
3751
3752 if (priv->bus) {
3753 gst_bus_remove_signal_watch (priv->bus);
3754 gst_object_unref (priv->bus);
3755 priv->bus = NULL;
3756 }
3757
3758 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->dispose (object);
3759 }
3760
3761 static void
gst_validate_scenario_finalize(GObject * object)3762 gst_validate_scenario_finalize (GObject * object)
3763 {
3764 GstValidateScenarioPrivate *priv = GST_VALIDATE_SCENARIO (object)->priv;
3765
3766 /* Because g_object_add_weak_pointer() is used, this MUST be on the
3767 * main thread. */
3768 g_assert (g_main_context_acquire (g_main_context_default ()));
3769 g_main_context_release (g_main_context_default ());
3770
3771 g_list_free_full (priv->actions, (GDestroyNotify) gst_mini_object_unref);
3772 g_list_free_full (priv->interlaced_actions,
3773 (GDestroyNotify) gst_mini_object_unref);
3774 g_list_free_full (priv->on_addition_actions,
3775 (GDestroyNotify) gst_mini_object_unref);
3776 g_free (priv->pipeline_name);
3777 gst_structure_free (priv->vars);
3778 g_mutex_clear (&priv->lock);
3779
3780 G_OBJECT_CLASS (gst_validate_scenario_parent_class)->finalize (object);
3781 }
3782
3783 static void _element_added_cb (GstBin * bin, GstElement * element,
3784 GstValidateScenario * scenario);
3785
3786 static void
iterate_children(GstValidateScenario * scenario,GstBin * bin)3787 iterate_children (GstValidateScenario * scenario, GstBin * bin)
3788 {
3789 GstIterator *it;
3790 GValue v = G_VALUE_INIT;
3791 gboolean done = FALSE;
3792 GHashTable *called; /* set of GstElement on which we already called _element_added_cb() */
3793
3794 called = g_hash_table_new (NULL, NULL);
3795 it = gst_bin_iterate_elements (bin);
3796
3797 while (!done) {
3798 switch (gst_iterator_next (it, &v)) {
3799 case GST_ITERATOR_OK:{
3800 GstElement *child = g_value_get_object (&v);
3801
3802 if (g_hash_table_lookup (called, child) == NULL) {
3803 _element_added_cb (bin, child, scenario);
3804 g_hash_table_add (called, child);
3805 }
3806 g_value_reset (&v);
3807 }
3808 break;
3809 case GST_ITERATOR_RESYNC:
3810 gst_iterator_resync (it);
3811 break;
3812 case GST_ITERATOR_ERROR:
3813 case GST_ITERATOR_DONE:
3814 done = TRUE;
3815 }
3816 }
3817 g_value_reset (&v);
3818 gst_iterator_free (it);
3819 g_hash_table_unref (called);
3820 }
3821
3822 static gboolean
should_execute_action(GstElement * element,GstValidateAction * action)3823 should_execute_action (GstElement * element, GstValidateAction * action)
3824 {
3825 return gst_validate_element_matches_target (element, action->structure);
3826 }
3827
3828 static void
_element_added_cb(GstBin * bin,GstElement * element,GstValidateScenario * scenario)3829 _element_added_cb (GstBin * bin, GstElement * element,
3830 GstValidateScenario * scenario)
3831 {
3832 GList *tmp;
3833
3834 GstValidateScenarioPrivate *priv = scenario->priv;
3835
3836 /* Check if it's an element we track for a set-property action */
3837 SCENARIO_LOCK (scenario);
3838 tmp = priv->on_addition_actions;
3839 while (tmp) {
3840 GstValidateAction *action = (GstValidateAction *) tmp->data;
3841
3842 if (action->playback_time != GST_CLOCK_TIME_NONE)
3843 break;
3844 if (g_strcmp0 (action->type, "set-property"))
3845 break;
3846
3847 GST_DEBUG_OBJECT (bin, "Checking action #%d %p (%s)", action->action_number,
3848 action, action->type);
3849 if (should_execute_action (element, action)) {
3850 GstValidateActionType *action_type;
3851 action_type = _find_action_type (action->type);
3852 GST_DEBUG_OBJECT (element, "Executing set-property action");
3853 if (gst_validate_execute_action (action_type, action)) {
3854 priv->on_addition_actions =
3855 g_list_remove_link (priv->on_addition_actions, tmp);
3856 gst_mini_object_unref (GST_MINI_OBJECT (action));
3857 g_list_free (tmp);
3858 tmp = priv->on_addition_actions;
3859 } else
3860 tmp = tmp->next;
3861 } else
3862 tmp = tmp->next;
3863 }
3864 SCENARIO_UNLOCK (scenario);
3865
3866 _check_scenario_is_done (scenario);
3867
3868 /* If it's a bin, listen to the child */
3869 if (GST_IS_BIN (element)) {
3870 g_signal_connect (element, "element-added", (GCallback) _element_added_cb,
3871 scenario);
3872 iterate_children (scenario, GST_BIN (element));
3873 }
3874 }
3875
3876 /**
3877 * gst_validate_scenario_factory_create:
3878 * @runner: The #GstValidateRunner to use to report issues
3879 * @pipeline: The pipeline to run the scenario on
3880 * @scenario_name: The name (or path) of the scenario to run
3881 *
3882 * Returns: (transfer full): A #GstValidateScenario or NULL
3883 */
3884 GstValidateScenario *
gst_validate_scenario_factory_create(GstValidateRunner * runner,GstElement * pipeline,const gchar * scenario_name)3885 gst_validate_scenario_factory_create (GstValidateRunner *
3886 runner, GstElement * pipeline, const gchar * scenario_name)
3887 {
3888 GList *config;
3889 GstValidateScenario *scenario =
3890 g_object_new (GST_TYPE_VALIDATE_SCENARIO, "validate-runner",
3891 runner, NULL);
3892
3893 GST_LOG ("Creating scenario %s", scenario_name);
3894 if (!gst_validate_scenario_load (scenario, scenario_name, NULL)) {
3895 g_object_unref (scenario);
3896
3897 return NULL;
3898 }
3899
3900 if (scenario->priv->pipeline_name &&
3901 !g_pattern_match_simple (scenario->priv->pipeline_name,
3902 GST_OBJECT_NAME (pipeline))) {
3903 GST_INFO ("Scenario %s only applies on pipeline %s not %s",
3904 scenario_name, scenario->priv->pipeline_name,
3905 GST_OBJECT_NAME (pipeline));
3906
3907 gst_object_unref (scenario);
3908
3909 return NULL;
3910 }
3911
3912 g_weak_ref_init (&scenario->priv->ref_pipeline, pipeline);
3913 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (scenario),
3914 g_strdup (scenario_name));
3915
3916 g_signal_connect (pipeline, "element-added", (GCallback) _element_added_cb,
3917 scenario);
3918
3919 iterate_children (scenario, GST_BIN (pipeline));
3920
3921 scenario->priv->bus = gst_element_get_bus (pipeline);
3922 gst_bus_add_signal_watch (scenario->priv->bus);
3923 g_signal_connect (scenario->priv->bus, "message", (GCallback) message_cb,
3924 scenario);
3925
3926 for (config = gst_validate_plugin_get_config (NULL); config;
3927 config = config->next) {
3928 gint interval;
3929
3930 if (gst_structure_get_uint (config->data,
3931 "scenario-action-execution-interval",
3932 &scenario->priv->action_execution_interval)) {
3933 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3934 scenario->priv->action_execution_interval);
3935 break;
3936 } else if (gst_structure_get_int (config->data,
3937 "scenario-action-execution-interval", &interval)) {
3938 if (interval > 0) {
3939 scenario->priv->action_execution_interval = (guint) interval;
3940 GST_DEBUG_OBJECT (scenario, "Setting action execution interval to %d",
3941 scenario->priv->action_execution_interval);
3942
3943 break;
3944 } else {
3945 GST_WARNING_OBJECT (scenario, "Interval is negative: %d", interval);
3946 }
3947 }
3948 }
3949
3950 if (scenario->priv->handles_state) {
3951 GST_INFO_OBJECT (scenario, "Scenario handles state."
3952 " Starting the get position source");
3953 _add_execute_actions_gsource (scenario);
3954 }
3955
3956 gst_validate_printf (NULL,
3957 "\n**-> Running scenario %s on pipeline %s**\n\n", scenario_name,
3958 GST_OBJECT_NAME (pipeline));
3959
3960 scenario->priv->overrides =
3961 gst_validate_override_registry_get_override_for_names
3962 (gst_validate_override_registry_get (), "scenarios", NULL);
3963
3964 return scenario;
3965 }
3966
3967 static gboolean
_add_description(GQuark field_id,const GValue * value,KeyFileGroupName * kfg)3968 _add_description (GQuark field_id, const GValue * value, KeyFileGroupName * kfg)
3969 {
3970 gchar *tmp = gst_value_serialize (value);
3971
3972 g_key_file_set_string (kfg->kf, kfg->group_name,
3973 g_quark_to_string (field_id), g_strcompress (tmp));
3974
3975 g_free (tmp);
3976
3977 return TRUE;
3978 }
3979
3980
3981 static gboolean
_parse_scenario(GFile * f,GKeyFile * kf)3982 _parse_scenario (GFile * f, GKeyFile * kf)
3983 {
3984 gboolean ret = FALSE;
3985 gchar *fname = g_file_get_basename (f);
3986
3987 if (g_str_has_suffix (fname, GST_VALIDATE_SCENARIO_SUFFIX)) {
3988 gboolean needs_clock_sync = FALSE;
3989 GstStructure *desc = NULL;
3990
3991 gchar **name = g_strsplit (fname, GST_VALIDATE_SCENARIO_SUFFIX, 0);
3992 GList *tmp, *structures = gst_validate_structs_parse_from_gfile (f);
3993
3994 for (tmp = structures; tmp; tmp = tmp->next) {
3995 GstStructure *_struct = (GstStructure *) tmp->data;
3996 GstValidateActionType *type =
3997 _find_action_type (gst_structure_get_name (_struct));
3998
3999 if (!desc && gst_structure_has_name (_struct, "description"))
4000 desc = gst_structure_copy (_struct);
4001 else if (type && type->flags & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK)
4002 needs_clock_sync = TRUE;
4003 }
4004
4005 if (needs_clock_sync) {
4006 if (desc)
4007 gst_structure_set (desc, "need-clock-sync", G_TYPE_BOOLEAN, TRUE, NULL);
4008 else
4009 desc = gst_structure_from_string ("description, need-clock-sync=true;",
4010 NULL);
4011 }
4012
4013 if (desc) {
4014 KeyFileGroupName kfg;
4015
4016 kfg.group_name = name[0];
4017 kfg.kf = kf;
4018
4019 gst_structure_foreach (desc,
4020 (GstStructureForeachFunc) _add_description, &kfg);
4021 gst_structure_free (desc);
4022 } else {
4023 g_key_file_set_string (kf, name[0], "noinfo", "nothing");
4024 }
4025 g_list_free_full (structures, (GDestroyNotify) gst_structure_free);
4026 g_strfreev (name);
4027
4028 ret = TRUE;
4029 }
4030
4031 g_free (fname);
4032 return ret;
4033 }
4034
4035 static void
_list_scenarios_in_dir(GFile * dir,GKeyFile * kf)4036 _list_scenarios_in_dir (GFile * dir, GKeyFile * kf)
4037 {
4038 GFileEnumerator *fenum;
4039 GFileInfo *info;
4040
4041 fenum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME,
4042 G_FILE_QUERY_INFO_NONE, NULL, NULL);
4043
4044 if (fenum == NULL)
4045 return;
4046
4047 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
4048 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
4049 GFile *f = g_file_enumerator_get_child (fenum, info);
4050
4051 _parse_scenario (f, kf);
4052 gst_object_unref (f);
4053 }
4054
4055 gst_object_unref (fenum);
4056 }
4057
4058 gboolean
gst_validate_list_scenarios(gchar ** scenarios,gint num_scenarios,gchar * output_file)4059 gst_validate_list_scenarios (gchar ** scenarios, gint num_scenarios,
4060 gchar * output_file)
4061 {
4062 gchar *result;
4063 gsize datalength;
4064
4065 GError *err = NULL;
4066 GKeyFile *kf = NULL;
4067 gint res = 0;
4068 const gchar *envvar;
4069 gchar **env_scenariodir = NULL;
4070 gchar *tldir = g_build_filename (g_get_user_data_dir (),
4071 "gstreamer-" GST_API_VERSION, "validate", GST_VALIDATE_SCENARIO_DIRECTORY,
4072 NULL);
4073 GFile *dir = g_file_new_for_path (tldir);
4074
4075 kf = g_key_file_new ();
4076 if (num_scenarios > 0) {
4077 gint i;
4078 GFile *file;
4079
4080 for (i = 0; i < num_scenarios; i++) {
4081 file = g_file_new_for_path (scenarios[i]);
4082 if (!_parse_scenario (file, kf)) {
4083 GST_ERROR ("Could not parse scenario: %s", scenarios[i]);
4084
4085 gst_object_unref (file);
4086 res = 1;
4087 }
4088 }
4089
4090 goto done;
4091 }
4092
4093 envvar = g_getenv ("GST_VALIDATE_SCENARIOS_PATH");
4094 if (envvar)
4095 env_scenariodir = g_strsplit (envvar, ":", 0);
4096
4097 _list_scenarios_in_dir (dir, kf);
4098 g_object_unref (dir);
4099 g_free (tldir);
4100
4101 tldir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
4102 "validate", GST_VALIDATE_SCENARIO_DIRECTORY, NULL);
4103 dir = g_file_new_for_path (tldir);
4104 _list_scenarios_in_dir (dir, kf);
4105 g_object_unref (dir);
4106 g_free (tldir);
4107
4108 if (env_scenariodir) {
4109 guint i;
4110
4111 for (i = 0; env_scenariodir[i]; i++) {
4112 dir = g_file_new_for_path (env_scenariodir[i]);
4113 _list_scenarios_in_dir (dir, kf);
4114 g_object_unref (dir);
4115 }
4116 }
4117
4118 /* Hack to make it work uninstalled */
4119 dir = g_file_new_for_path ("data/scenarios");
4120 _list_scenarios_in_dir (dir, kf);
4121 g_object_unref (dir);
4122
4123 done:
4124 result = g_key_file_to_data (kf, &datalength, &err);
4125 g_print ("All scenarios available:\n%s", result);
4126
4127 if (output_file && !err)
4128 if (!g_file_set_contents (output_file, result, datalength, &err)) {
4129 GST_WARNING ("Error writing to file '%s'", output_file);
4130 }
4131
4132 if (env_scenariodir)
4133 g_strfreev (env_scenariodir);
4134
4135 if (err) {
4136 GST_WARNING ("Got error '%s' listing scenarios", err->message);
4137 g_clear_error (&err);
4138
4139 res = FALSE;
4140 }
4141
4142 g_key_file_free (kf);
4143
4144 return res;
4145 }
4146
4147 static GstValidateActionReturn
check_last_sample_internal(GstValidateScenario * scenario,GstValidateAction * action,GstElement * sink)4148 check_last_sample_internal (GstValidateScenario * scenario,
4149 GstValidateAction * action, GstElement * sink)
4150 {
4151 GstSample *sample;
4152 gchar *sum;
4153 GstMapInfo map;
4154 GstBuffer *buffer;
4155 const gchar *target_sum;
4156 GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
4157
4158 g_object_get (sink, "last-sample", &sample, NULL);
4159 if (sample == NULL) {
4160 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4161 "Could not \"check-last-sample\" as %" GST_PTR_FORMAT
4162 " 'last-sample' property is NULL"
4163 ". MAKE SURE THE 'enable-last-sample' PROPERTY IS SET TO 'TRUE'!",
4164 sink);
4165
4166 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4167 }
4168
4169 buffer = gst_sample_get_buffer (sample);
4170 if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
4171 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4172 "Last sample buffer could not be mapped, action can't run.");
4173 goto done;
4174 }
4175
4176 sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
4177 gst_buffer_unmap (buffer, &map);
4178
4179 target_sum = gst_structure_get_string (action->structure, "checksum");
4180 if (g_strcmp0 (sum, target_sum)) {
4181 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4182 "Last buffer checksum '%s' is different than the expected one: '%s'",
4183 sum, target_sum);
4184
4185 res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4186 }
4187 g_free (sum);
4188
4189 done:
4190 return res;
4191 }
4192
4193 static void
sink_last_sample_notify_cb(GstElement * sink,GParamSpec * arg G_GNUC_UNUSED,GstValidateAction * action)4194 sink_last_sample_notify_cb (GstElement * sink, GParamSpec * arg G_GNUC_UNUSED,
4195 GstValidateAction * action)
4196 {
4197 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4198
4199 if (!scenario) {
4200 GST_VALIDATE_REPORT (scenario,
4201 SCENARIO_ACTION_EXECUTION_ERROR,
4202 "No pipeline anymore, can't check last sample");
4203 goto done;
4204 }
4205
4206 check_last_sample_internal (scenario, action, sink);
4207 gst_object_unref (scenario);
4208
4209 done:
4210 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
4211 action);
4212 gst_validate_action_set_done (action);
4213 gst_validate_action_unref (action);
4214 }
4215
4216 static GstValidateExecuteActionReturn
_check_last_sample_checksum(GstValidateScenario * scenario,GstValidateAction * action,GstElement * sink)4217 _check_last_sample_checksum (GstValidateScenario * scenario,
4218 GstValidateAction * action, GstElement * sink)
4219 {
4220 GstSample *sample;
4221
4222 /* Connect before checking last sample to avoid a race where
4223 * the sample is set between the time we connect and the time
4224 * the time we get it */
4225 g_signal_connect (sink, "notify::last-sample",
4226 G_CALLBACK (sink_last_sample_notify_cb),
4227 gst_validate_action_ref (action));
4228
4229 g_object_get (sink, "last-sample", &sample, NULL);
4230 if (sample == NULL)
4231 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
4232
4233 g_signal_handlers_disconnect_by_func (sink, sink_last_sample_notify_cb,
4234 action);
4235
4236 return check_last_sample_internal (scenario, action, sink);
4237 }
4238
4239 static gboolean
_sink_matches_last_sample_specs(GstElement * sink,const gchar * name,const gchar * fname,GstCaps * sinkpad_caps)4240 _sink_matches_last_sample_specs (GstElement * sink, const gchar * name,
4241 const gchar * fname, GstCaps * sinkpad_caps)
4242 {
4243 GstCaps *tmpcaps;
4244 GstPad *sinkpad;
4245 GObjectClass *klass = G_OBJECT_GET_CLASS (sink);
4246 GParamSpec *paramspec = g_object_class_find_property (klass, "last-sample");
4247
4248 if (!paramspec)
4249 return FALSE;
4250
4251 if (paramspec->value_type != GST_TYPE_SAMPLE)
4252 return FALSE;
4253
4254 if (!name && !fname && !sinkpad_caps)
4255 return TRUE;
4256
4257 if (name && !g_strcmp0 (GST_OBJECT_NAME (sink), name))
4258 return TRUE;
4259
4260 if (fname
4261 && !g_strcmp0 (GST_OBJECT_NAME (gst_element_get_factory (sink)), fname))
4262 return TRUE;
4263
4264 if (!sinkpad_caps)
4265 return FALSE;
4266
4267 sinkpad = gst_element_get_static_pad (sink, "sink");
4268 if (!sinkpad)
4269 return FALSE;
4270
4271 tmpcaps = gst_pad_get_current_caps (sinkpad);
4272 if (tmpcaps) {
4273 gboolean res = gst_caps_can_intersect (tmpcaps, sinkpad_caps);
4274
4275 GST_DEBUG_OBJECT (sink, "Matches caps: %" GST_PTR_FORMAT, tmpcaps);
4276 gst_caps_unref (tmpcaps);
4277
4278 return res;
4279 } else {
4280 GST_INFO_OBJECT (sink, "No caps set yet, can't check it.");
4281 }
4282
4283 return FALSE;
4284 }
4285
4286 static GstValidateExecuteActionReturn
_execute_check_last_sample(GstValidateScenario * scenario,GstValidateAction * action)4287 _execute_check_last_sample (GstValidateScenario * scenario,
4288 GstValidateAction * action)
4289 {
4290 GstIterator *it;
4291 GValue data = { 0, };
4292 gboolean done = FALSE;
4293 GstCaps *caps = NULL;
4294 GstElement *sink = NULL, *tmpelement;
4295 const gchar *name = gst_structure_get_string (action->structure, "sink-name"),
4296 *factory_name =
4297 gst_structure_get_string (action->structure, "sink-factory-name"),
4298 *caps_str = gst_structure_get_string (action->structure, "sinkpad-caps");
4299 DECLARE_AND_GET_PIPELINE (scenario, action);
4300
4301 if (caps_str) {
4302 caps = gst_caps_from_string (caps_str);
4303
4304 g_assert (caps);
4305 }
4306
4307 it = gst_bin_iterate_recurse (GST_BIN (pipeline));
4308 while (!done) {
4309 switch (gst_iterator_next (it, &data)) {
4310 case GST_ITERATOR_OK:
4311 tmpelement = g_value_get_object (&data);
4312 if (_sink_matches_last_sample_specs (tmpelement, name, factory_name,
4313 caps)) {
4314 if (sink) {
4315
4316 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4317 "Could not \"check-last-sample\" as several elements were found "
4318 "from describing string: '%" GST_PTR_FORMAT
4319 "' (%s and %s match)", action->structure,
4320 GST_OBJECT_NAME (sink), GST_OBJECT_NAME (tmpelement));
4321
4322 gst_object_unref (sink);
4323 }
4324
4325 sink = gst_object_ref (tmpelement);
4326 }
4327 g_value_reset (&data);
4328 break;
4329 case GST_ITERATOR_RESYNC:
4330 gst_iterator_resync (it);
4331 g_clear_object (&sink);
4332 break;
4333 case GST_ITERATOR_ERROR:
4334 /* Fallthrough */
4335 case GST_ITERATOR_DONE:
4336 done = TRUE;
4337 break;
4338 }
4339 }
4340 gst_iterator_free (it);
4341 if (caps)
4342 gst_caps_unref (caps);
4343
4344 if (!sink) {
4345 GST_VALIDATE_REPORT (scenario, SCENARIO_ACTION_EXECUTION_ERROR,
4346 "Could not \"check-last-sample\" as no sink was found from description: '%"
4347 GST_PTR_FORMAT "'", action->structure);
4348
4349 goto error;
4350 }
4351
4352 return _check_last_sample_checksum (scenario, action, sink);
4353
4354 error:
4355 g_clear_object (&sink);
4356 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
4357 }
4358
4359 static gboolean
_action_set_done(GstValidateAction * action)4360 _action_set_done (GstValidateAction * action)
4361 {
4362 JsonBuilder *jbuild;
4363 GstClockTime execution_duration;
4364 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4365
4366 if (scenario == NULL || !action->priv->pending_set_done)
4367 return G_SOURCE_REMOVE;
4368
4369 execution_duration = gst_util_get_timestamp () - action->priv->execution_time;
4370
4371 jbuild = json_builder_new ();
4372 json_builder_begin_object (jbuild);
4373 json_builder_set_member_name (jbuild, "type");
4374 json_builder_add_string_value (jbuild, "action-done");
4375 json_builder_set_member_name (jbuild, "action-type");
4376 json_builder_add_string_value (jbuild, action->type);
4377 json_builder_set_member_name (jbuild, "execution-duration");
4378 json_builder_add_double_value (jbuild,
4379 ((gdouble) execution_duration / GST_SECOND));
4380 json_builder_end_object (jbuild);
4381
4382 gst_validate_send (json_builder_get_root (jbuild));
4383 g_object_unref (jbuild);
4384
4385 gst_validate_printf (NULL, " -> Action %s done (duration: %" GST_TIME_FORMAT
4386 ")\n", action->type, GST_TIME_ARGS (execution_duration));
4387 action->priv->execution_time = GST_CLOCK_TIME_NONE;
4388 action->priv->state = _execute_sub_action_action (action);
4389
4390 if (action->priv->state != GST_VALIDATE_EXECUTE_ACTION_ASYNC) {
4391
4392 GST_DEBUG_OBJECT (scenario, "Sub action executed ASYNC");
4393 execute_next_action (scenario);
4394 }
4395 gst_object_unref (scenario);
4396
4397 action->priv->pending_set_done = FALSE;
4398 return G_SOURCE_REMOVE;
4399 }
4400
4401 /* gst_validate_action_set_done:
4402 * @action: The action that is done executing
4403 *
4404 * Sets @action as "done", meaning that the next action can
4405 * now be executed.
4406 */
4407 void
gst_validate_action_set_done(GstValidateAction * action)4408 gst_validate_action_set_done (GstValidateAction * action)
4409 {
4410
4411 if (action->priv->state == GST_VALIDATE_EXECUTE_ACTION_INTERLACED) {
4412 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
4413 GList *item = NULL;
4414
4415 if (scenario) {
4416 SCENARIO_LOCK (scenario);
4417 item = g_list_find (scenario->priv->interlaced_actions, action);
4418 scenario->priv->interlaced_actions =
4419 g_list_delete_link (scenario->priv->interlaced_actions, item);
4420 SCENARIO_UNLOCK (scenario);
4421 g_object_unref (scenario);
4422 }
4423
4424 if (item)
4425 gst_validate_action_unref (action);
4426 }
4427
4428 g_assert (!action->priv->pending_set_done);
4429 action->priv->pending_set_done = TRUE;
4430
4431 g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT_IDLE,
4432 (GSourceFunc) _action_set_done,
4433 gst_mini_object_ref (GST_MINI_OBJECT (action)),
4434 (GDestroyNotify) gst_validate_action_unref);
4435 }
4436
4437 /**
4438 * gst_validate_action_get_scenario:
4439 * @action: The action for which to retrieve the scenario
4440 *
4441 * Retrieve the scenario from which @action is executed.
4442 *
4443 * Returns: (transfer full): The scenario from which the action is being executed.
4444 */
4445 GstValidateScenario *
gst_validate_action_get_scenario(GstValidateAction * action)4446 gst_validate_action_get_scenario (GstValidateAction * action)
4447 {
4448 return g_weak_ref_get (&action->priv->scenario);
4449 }
4450
4451 /**
4452 * gst_validate_register_action_type:
4453 * @type_name: The name of the new action type to add
4454 * @implementer_namespace: The namespace of the implementer of the action type.
4455 * That should always be the name of the GstPlugin as
4456 * retrieved with #gst_plugin_get_name when the action type
4457 * is registered inside a plugin.
4458 * @function: (scope notified): The function to be called to execute the action
4459 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4460 * @description: A description of the new type
4461 * @flags: The #GstValidateActionTypeFlags to set on the new action type
4462 *
4463 * Register a new action type to the action type system. If the action type already
4464 * exists, it will be overridden by the new definition
4465 *
4466 * Returns: (transfer none): The newly created action type or the already registered action type
4467 * if it had a higher rank
4468 */
4469 GstValidateActionType *
gst_validate_register_action_type(const gchar * type_name,const gchar * implementer_namespace,GstValidateExecuteAction function,GstValidateActionParameter * parameters,const gchar * description,GstValidateActionTypeFlags flags)4470 gst_validate_register_action_type (const gchar * type_name,
4471 const gchar * implementer_namespace,
4472 GstValidateExecuteAction function,
4473 GstValidateActionParameter * parameters,
4474 const gchar * description, GstValidateActionTypeFlags flags)
4475 {
4476 GstValidateActionType *type = gst_validate_register_action_type_dynamic (NULL,
4477 type_name, GST_RANK_NONE, function, parameters, description,
4478 flags);
4479
4480 g_free (type->implementer_namespace);
4481 type->implementer_namespace = g_strdup (implementer_namespace);
4482
4483 return type;
4484 }
4485
4486 static void
_free_action_types(GList * _action_types)4487 _free_action_types (GList * _action_types)
4488 {
4489 g_list_free_full (_action_types, (GDestroyNotify) gst_mini_object_unref);
4490 }
4491
4492 /**
4493 * gst_validate_register_action_type_dynamic:
4494 * @plugin: (allow-none): The #GstPlugin that register the action type,
4495 * or NULL for a static element.
4496 * @rank: The ranking of that implementation of the action type called
4497 * @type_name. If an action type has been registered with the same
4498 * name with a higher rank, the new implementation will not be used,
4499 * and the already registered action type is returned.
4500 * If the already registered implementation has a lower rank, the
4501 * new implementation will be used and returned.
4502 * @type_name: The name of the new action type to add
4503 * @function: (scope notified): The function to be called to execute the action
4504 * @parameters: (allow-none) (array zero-terminated=1) (element-type GstValidateActionParameter): The #GstValidateActionParameter usable as parameter of the type
4505 * @description: A description of the new type
4506 * @flags: The #GstValidateActionTypeFlags to be set on the new action type
4507 *
4508 * Returns: (transfer none): The newly created action type or the already registered action type
4509 * if it had a higher rank
4510 */
4511 GstValidateActionType *
gst_validate_register_action_type_dynamic(GstPlugin * plugin,const gchar * type_name,GstRank rank,GstValidateExecuteAction function,GstValidateActionParameter * parameters,const gchar * description,GstValidateActionTypeFlags flags)4512 gst_validate_register_action_type_dynamic (GstPlugin * plugin,
4513 const gchar * type_name, GstRank rank,
4514 GstValidateExecuteAction function, GstValidateActionParameter * parameters,
4515 const gchar * description, GstValidateActionTypeFlags flags)
4516 {
4517 GstValidateActionType *tmptype;
4518 GstValidateActionType *type = gst_validate_action_type_new ();
4519 gboolean is_config = IS_CONFIG_ACTION_TYPE (flags);
4520 gint n_params = is_config ? 0 : 2;
4521
4522 if (parameters) {
4523 for (n_params = 0; parameters[n_params].name != NULL; n_params++);
4524 n_params += 1;
4525 }
4526
4527 if (n_params) {
4528 type->parameters = g_new0 (GstValidateActionParameter, n_params);
4529 }
4530
4531 if (parameters) {
4532 memcpy (type->parameters, parameters,
4533 sizeof (GstValidateActionParameter) * (n_params));
4534 }
4535
4536 type->prepare = gst_validate_action_default_prepare_func;
4537 type->execute = function;
4538 type->name = g_strdup (type_name);
4539 if (plugin)
4540 type->implementer_namespace = g_strdup (gst_plugin_get_name (plugin));
4541 else
4542 type->implementer_namespace = g_strdup ("none");
4543
4544 type->description = g_strdup (description);
4545 type->flags = flags;
4546 type->rank = rank;
4547
4548 if ((tmptype = _find_action_type (type_name))) {
4549 if (tmptype->rank <= rank) {
4550 action_types = g_list_remove (action_types, tmptype);
4551 type->overriden_type = tmptype;
4552 } else {
4553 gst_mini_object_unref (GST_MINI_OBJECT (type));
4554
4555 type = tmptype;
4556 }
4557 }
4558
4559 if (type != tmptype)
4560 action_types = g_list_append (action_types, type);
4561
4562 if (plugin) {
4563 GList *plugin_action_types = g_object_steal_data (G_OBJECT (plugin),
4564 "GstValidatePluginActionTypes");
4565
4566 plugin_action_types = g_list_prepend (plugin_action_types,
4567 gst_mini_object_ref (GST_MINI_OBJECT (type)));
4568
4569 g_object_set_data_full (G_OBJECT (plugin), "GstValidatePluginActionTypes",
4570 plugin_action_types, (GDestroyNotify) _free_action_types);
4571 }
4572
4573 return type;
4574 }
4575
4576 GstValidateActionType *
gst_validate_get_action_type(const gchar * type_name)4577 gst_validate_get_action_type (const gchar * type_name)
4578 {
4579 GstValidateActionType *type = _find_action_type (type_name);
4580
4581 if (type)
4582 return
4583 GST_VALIDATE_ACTION_TYPE (gst_mini_object_ref (GST_MINI_OBJECT (type)));
4584
4585 return NULL;
4586 }
4587
4588 static GList *
gst_validate_list_action_types(void)4589 gst_validate_list_action_types (void)
4590 {
4591 return action_types;
4592 }
4593
4594 /**
4595 * gst_validate_print_action_types:
4596 * @wanted_types: (array length=num_wanted_types): (optional): List of types to be printed
4597 * @num_wanted_types: Length of @wanted_types
4598 *
4599 * Prints the action types details wanted in @wanted_types
4600 *
4601 * Returns: True if all types could be printed
4602 */
4603 gboolean
gst_validate_print_action_types(const gchar ** wanted_types,gint num_wanted_types)4604 gst_validate_print_action_types (const gchar ** wanted_types,
4605 gint num_wanted_types)
4606 {
4607 GList *tmp;
4608 gint nfound = 0;
4609
4610 for (tmp = gst_validate_list_action_types (); tmp; tmp = tmp->next) {
4611 GstValidateActionType *atype = (GstValidateActionType *) tmp->data;
4612 gboolean print = FALSE;
4613
4614 if (num_wanted_types) {
4615 gint n;
4616
4617 for (n = 0; n < num_wanted_types; n++) {
4618 if (g_strcmp0 (atype->name, wanted_types[n]) == 0 ||
4619 g_strcmp0 (atype->implementer_namespace, wanted_types[n]) == 0) {
4620 nfound++;
4621 print = TRUE;
4622
4623 break;
4624 }
4625 }
4626 } else {
4627 print = TRUE;
4628 }
4629
4630 if (print && num_wanted_types) {
4631 gst_validate_printf (atype, "\n");
4632 } else if (print) {
4633 gchar *desc =
4634 g_regex_replace (newline_regex, atype->description, -1, 0, "\n ",
4635 0,
4636 NULL);
4637
4638 gst_validate_printf (NULL, "\n%s: %s:\n %s\n",
4639 atype->implementer_namespace, atype->name, desc);
4640 g_free (desc);
4641 }
4642 }
4643
4644 if (num_wanted_types && num_wanted_types > nfound) {
4645 return FALSE;
4646 }
4647
4648 return TRUE;
4649 }
4650
4651 /**
4652 * gst_validate_scenario_get_actions:
4653 * @scenario: The scenario to retrieve remaining actions for
4654 *
4655 * Get remaining actions from @scenario.
4656 *
4657 * Returns: (transfer full) (element-type GstValidateAction): A list of #GstValidateAction.
4658 */
4659 GList *
gst_validate_scenario_get_actions(GstValidateScenario * scenario)4660 gst_validate_scenario_get_actions (GstValidateScenario * scenario)
4661 {
4662 GList *ret;
4663 gboolean main_context_acquired;
4664
4665 main_context_acquired = g_main_context_acquire (g_main_context_default ());
4666 g_return_val_if_fail (main_context_acquired, NULL);
4667
4668 ret = g_list_copy_deep (scenario->priv->actions,
4669 (GCopyFunc) gst_mini_object_ref, NULL);
4670
4671 g_main_context_release (g_main_context_default ());
4672
4673 return ret;
4674 }
4675
4676 /**
4677 * gst_validate_scenario_get_target_state:
4678 * @scenario: The scenario to retrieve the current target state for
4679 *
4680 * Get current target state from @scenario.
4681 *
4682 * Returns: Current target state.
4683 */
4684 GstState
gst_validate_scenario_get_target_state(GstValidateScenario * scenario)4685 gst_validate_scenario_get_target_state (GstValidateScenario * scenario)
4686 {
4687 return scenario->priv->target_state;
4688 }
4689
4690 void
init_scenarios(void)4691 init_scenarios (void)
4692 {
4693 GList *tmp;
4694 GST_DEBUG_CATEGORY_INIT (gst_validate_scenario_debug, "gstvalidatescenario",
4695 GST_DEBUG_FG_YELLOW, "Gst validate scenarios");
4696
4697 _gst_validate_action_type = gst_validate_action_get_type ();
4698 _gst_validate_action_type_type = gst_validate_action_type_get_type ();
4699
4700 /* *INDENT-OFF* */
4701 REGISTER_ACTION_TYPE ("description", NULL,
4702 ((GstValidateActionParameter []) {
4703 {
4704 .name = "summary",
4705 .description = "Whether the scenario is a config only scenario (ie. explain what it does)",
4706 .mandatory = FALSE,
4707 .types = "string",
4708 .possible_variables = NULL,
4709 .def = "'Nothing'"},
4710 {
4711 .name = "is-config",
4712 .description = "Whether the scenario is a config only scenario",
4713 .mandatory = FALSE,
4714 .types = "boolean",
4715 .possible_variables = NULL,
4716 .def = "false"
4717 },
4718 {
4719 .name = "handles-states",
4720 .description = "Whether the scenario handles pipeline state changes from the beginning\n"
4721 "in that case the application should not set the state of the pipeline to anything\n"
4722 "and the scenario action will be executed from the beginning",
4723 .mandatory = FALSE,
4724 .types = "boolean",
4725 .possible_variables = NULL,
4726 .def = "false"},
4727 {
4728 .name = "seek",
4729 .description = "Whether the scenario executes seek actions or not",
4730 .mandatory = FALSE,
4731 .types = "boolean",
4732 .possible_variables = NULL,
4733 .def = "false"
4734 },
4735 {
4736 .name = "reverse-playback",
4737 .description = "Whether the scenario plays the stream backward",
4738 .mandatory = FALSE,
4739 .types = "boolean",
4740 .possible_variables = NULL,
4741 .def = "false"
4742 },
4743 {
4744 .name = "need-clock-sync",
4745 .description = "Whether the scenario needs the execution to be synchronized with the pipeline's\n"
4746 "clock. Letting the user know if it can be used with a 'fakesink sync=false' sink",
4747 .mandatory = FALSE,
4748 .types = "boolean",
4749 .possible_variables = NULL,
4750 .def = "false"
4751 },
4752 {
4753 .name = "min-media-duration",
4754 .description = "Lets the user know the minimum duration of the stream for the scenario\n"
4755 "to be usable",
4756 .mandatory = FALSE,
4757 .types = "double",
4758 .possible_variables = NULL,
4759 .def = "0.0"
4760 },
4761 {
4762 .name = "min-audio-track",
4763 .description = "Lets the user know the minimum number of audio tracks the stream needs to contain\n"
4764 "for the scenario to be usable",
4765 .mandatory = FALSE,
4766 .types = "int",
4767 .possible_variables = NULL,
4768 .def = "0"
4769 },
4770 {
4771 .name = "min-video-track",
4772 .description = "Lets the user know the minimum number of video tracks the stream needs to contain\n"
4773 "for the scenario to be usable",
4774 .mandatory = FALSE,
4775 .types = "int",
4776 .possible_variables = NULL,
4777 .def = "0"
4778 },
4779 {
4780 .name = "duration",
4781 .description = "Lets the user know the time the scenario needs to be fully executed",
4782 .mandatory = FALSE,
4783 .types = "double, int",
4784 .possible_variables = NULL,
4785 .def = "infinite (GST_CLOCK_TIME_NONE)"
4786 },
4787 {
4788 .name = "pipeline-name",
4789 .description = "The name of the GstPipeline on which the scenario should be executed.\n"
4790 "It has the same effect as setting the pipeline using pipeline_name->scenario_name.",
4791 .mandatory = FALSE,
4792 .types = "string",
4793 .possible_variables = NULL,
4794 .def = "NULL"
4795 },
4796 {
4797 .name = "max-latency",
4798 .description = "The maximum latency in nanoseconds allowed for this pipeline.\n"
4799 "It can be overriden using core configuration, like for example by defining the "
4800 "env variable GST_VALIDATE_CONFIG=core,max-latency=33000000",
4801 .mandatory = FALSE,
4802 .types = "double, int",
4803 .possible_variables = NULL,
4804 .def = "infinite (GST_CLOCK_TIME_NONE)"
4805 },
4806 {
4807 .name = "max-dropped",
4808 .description = "The maximum number of buffers which can be dropped by the QoS system allowed for this pipeline.\n"
4809 "It can be overriden using core configuration, like for example by defining the "
4810 "env variable GST_VALIDATE_CONFIG=core,max-dropped=100",
4811 .mandatory = FALSE,
4812 .types = "int",
4813 .possible_variables = NULL,
4814 .def = "infinite (-1)"
4815 },
4816 {
4817 .name = "ignore-eos",
4818 .description = "Ignore EOS and keep executing the scenario when it happens.\n By default "
4819 "a 'stop' action is generated one EOS",
4820 .mandatory = FALSE,
4821 .types = "boolean",
4822 .possible_variables = NULL,
4823 .def = "false"
4824 },
4825 {NULL}
4826 }),
4827 "Allows to describe the scenario in various ways",
4828 GST_VALIDATE_ACTION_TYPE_CONFIG);
4829
4830 REGISTER_ACTION_TYPE ("seek", _execute_seek,
4831 ((GstValidateActionParameter []) {
4832 {
4833 .name = "start",
4834 .description = "The starting value of the seek",
4835 .mandatory = TRUE,
4836 .types = "double or string (GstClockTime)",
4837 .possible_variables = "position: The current position in the stream\n"
4838 "duration: The duration of the stream",
4839 NULL
4840 },
4841 {
4842 .name = "flags",
4843 .description = "The GstSeekFlags to use",
4844 .mandatory = TRUE,
4845 .types = "string describing the GstSeekFlags to set",
4846 NULL,
4847 },
4848 {
4849 .name = "rate",
4850 .description = "The rate value of the seek",
4851 .mandatory = FALSE,
4852 .types = "double",
4853 .possible_variables = NULL,
4854 .def = "1.0"
4855 },
4856 {
4857 .name = "start_type",
4858 .description = "The GstSeekType to use for the start of the seek, in:\n"
4859 " [none, set, end]",
4860 .mandatory = FALSE,
4861 .types = "string",
4862 .possible_variables = NULL,
4863 .def = "set"
4864 },
4865 {
4866 .name = "stop_type",
4867 .description = "The GstSeekType to use for the stop of the seek, in:\n"
4868 " [none, set, end]",
4869 .mandatory = FALSE,
4870 .types = "string",
4871 .possible_variables = NULL,
4872 .def = "set"
4873 },
4874 {
4875 .name = "stop",
4876 .description = "The stop value of the seek",
4877 .mandatory = FALSE,
4878 .types = "double or string (GstClockTime)",
4879 .possible_variables = "position: The current position in the stream\n"
4880 "duration: The duration of the stream",
4881 .def ="GST_CLOCK_TIME_NONE",
4882 },
4883 {NULL}
4884 }),
4885 "Seeks into the stream. This is an example of a seek happening when the stream reaches 5 seconds\n"
4886 "or 1 eighth of its duration and seeks to 10s or 2 eighths of its duration:\n"
4887 " seek, playback-time=\"min(5.0, (duration/8))\", start=\"min(10, 2*(duration/8))\", flags=accurate+flush",
4888 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK
4889 );
4890
4891 REGISTER_ACTION_TYPE ("pause", _execute_pause,
4892 ((GstValidateActionParameter []) {
4893 {
4894 .name = "duration",
4895 .description = "The duration during which the stream will be paused",
4896 .mandatory = FALSE,
4897 .types = "double or string (GstClockTime)",
4898 .possible_variables = NULL,
4899 .def = "0.0",
4900 },
4901 {NULL}
4902 }),
4903 "Sets pipeline to PAUSED. You can add a 'duration'\n"
4904 "parameter so the pipeline goes back to playing after that duration\n"
4905 "(in second)",
4906 GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK & GST_VALIDATE_ACTION_TYPE_ASYNC);
4907
4908 REGISTER_ACTION_TYPE ("play", _execute_play, NULL,
4909 "Sets the pipeline state to PLAYING", GST_VALIDATE_ACTION_TYPE_NONE);
4910
4911 REGISTER_ACTION_TYPE ("stop", _execute_stop, NULL,
4912 "Stops the execution of the scenario. It will post a 'request-state'"
4913 " message on the bus with NULL as a requested state"
4914 " and the application is responsible for stopping itself."
4915 " If you override that action type, make sure to link up.",
4916 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4917
4918 REGISTER_ACTION_TYPE ("eos", _execute_eos, NULL,
4919 "Sends an EOS event to the pipeline",
4920 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL);
4921
4922 REGISTER_ACTION_TYPE ("switch-track", _execute_switch_track,
4923 ((GstValidateActionParameter []) {
4924 {
4925 .name = "type",
4926 .description = "Selects which track type to change (can be 'audio', 'video',"
4927 " or 'text').",
4928 .mandatory = FALSE,
4929 .types = "string",
4930 .possible_variables = NULL,
4931 .def = "audio",
4932 },
4933 {
4934 .name = "index",
4935 .description = "Selects which track of this type to use: it can be either a number,\n"
4936 "which will be the Nth track of the given type, or a number with a '+' or\n"
4937 "'-' prefix, which means a relative change (eg, '+1' means 'next track',\n"
4938 "'-1' means 'previous track')",
4939 .mandatory = FALSE,
4940 .types = "string: to switch track relatively\n"
4941 "int: To use the actual index to use",
4942 .possible_variables = NULL,
4943 .def = "+1",
4944 },
4945 {NULL}
4946 }),
4947 "The 'switch-track' command can be used to switch tracks."
4948 , GST_VALIDATE_ACTION_TYPE_NONE);
4949
4950 REGISTER_ACTION_TYPE ("wait", _execute_wait,
4951 ((GstValidateActionParameter []) {
4952 {
4953 .name = "duration",
4954 .description = "the duration while no other action will be executed",
4955 .mandatory = FALSE,
4956 .types = "double or string (GstClockTime)",
4957 NULL},
4958 {
4959 .name = "target-element-name",
4960 .description = "The name of the GstElement to wait @signal-name on.",
4961 .mandatory = FALSE,
4962 .types = "string"
4963 },
4964 {
4965 .name = "signal-name",
4966 .description = "The name of the signal to wait for on @target-element-name",
4967 .mandatory = FALSE,
4968 .types = "string",
4969 NULL
4970 },
4971 {
4972 .name = "message-type",
4973 .description = "The name of the message type to wait for (on @target-element-name"
4974 " if specified)",
4975 .mandatory = FALSE,
4976 .types = "string",
4977 NULL
4978 },
4979 {NULL}
4980 }),
4981 "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",
4982 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
4983
4984 REGISTER_ACTION_TYPE ("dot-pipeline", _execute_dot_pipeline, NULL,
4985 "Dots the pipeline (the 'name' property will be used in the dot filename).\n"
4986 "For more information have a look at the GST_DEBUG_BIN_TO_DOT_FILE documentation.\n"
4987 "Note that the GST_DEBUG_DUMP_DOT_DIR env variable needs to be set",
4988 GST_VALIDATE_ACTION_TYPE_NONE);
4989
4990 REGISTER_ACTION_TYPE ("set-rank", _execute_set_rank,
4991 ((GstValidateActionParameter []) {
4992 {
4993 .name = "name",
4994 .description = "The name of a GstFeature or GstPlugin",
4995 .mandatory = TRUE,
4996 .types = "string",
4997 NULL},
4998 {
4999 .name = "rank",
5000 .description = "The GstRank to set on @name",
5001 .mandatory = TRUE,
5002 .types = "string, int",
5003 NULL},
5004 {NULL}
5005 }),
5006 "Changes the ranking of a particular plugin feature(s)",
5007 GST_VALIDATE_ACTION_TYPE_CONFIG);
5008
5009 REGISTER_ACTION_TYPE ("set-feature-rank", _execute_set_rank,
5010 ((GstValidateActionParameter []) {
5011 {
5012 .name = "feature-name",
5013 .description = "The name of a GstFeature",
5014 .mandatory = TRUE,
5015 .types = "string",
5016 NULL},
5017 {
5018 .name = "rank",
5019 .description = "The GstRank to set on @feature-name",
5020 .mandatory = TRUE,
5021 .types = "string, int",
5022 NULL},
5023 {NULL}
5024 }),
5025 "Changes the ranking of a particular plugin feature",
5026 GST_VALIDATE_ACTION_TYPE_CONFIG);
5027
5028 REGISTER_ACTION_TYPE ("set-state", _execute_set_state,
5029 ((GstValidateActionParameter []) {
5030 {
5031 .name = "state",
5032 .description = "A GstState as a string, should be in: \n"
5033 " * ['null', 'ready', 'paused', 'playing']",
5034 .mandatory = TRUE,
5035 .types = "string",
5036 },
5037 {NULL}
5038 }),
5039 "Changes the state of the pipeline to any GstState",
5040 GST_VALIDATE_ACTION_TYPE_ASYNC & GST_VALIDATE_ACTION_TYPE_NEEDS_CLOCK);
5041
5042 REGISTER_ACTION_TYPE ("set-vars", _execute_define_vars,
5043 ((GstValidateActionParameter []) {
5044 {NULL}
5045 }),
5046 "Define vars to be used in other actions.\n"
5047 "For example you can define vars for buffer checksum"
5048 " to be used in the \"check-last-sample\" action type as follow:\n\n"
5049 "```\n"
5050 " set-vars, frame1=SomeRandomHash1,frame2=Anotherhash...\n"
5051 " check-last-sample, checksum=frame1\n"
5052 "```\n",
5053 GST_VALIDATE_ACTION_TYPE_NONE);
5054
5055 REGISTER_ACTION_TYPE ("set-property", _execute_set_property,
5056 ((GstValidateActionParameter []) {
5057 {
5058 .name = "target-element-name",
5059 .description = "The name of the GstElement to set a property on",
5060 .mandatory = FALSE,
5061 .types = "string",
5062 NULL
5063 },
5064 {
5065 .name = "target-element-factory-name",
5066 .description = "The name factory for which to set a property on built elements",
5067 .mandatory = FALSE,
5068 .types = "string",
5069 NULL
5070 },
5071 {
5072 .name = "target-element-klass",
5073 .description = "The klass of the GstElements to set a property on",
5074 .mandatory = FALSE,
5075 .types = "string",
5076 NULL
5077 },
5078 {
5079 .name = "property-name",
5080 .description = "The name of the property to set on @target-element-name",
5081 .mandatory = TRUE,
5082 .types = "string",
5083 NULL
5084 },
5085 {
5086 .name = "property-value",
5087 .description = "The value of @property-name to be set on the element",
5088 .mandatory = TRUE,
5089 .types = "The same type of @property-name",
5090 NULL
5091 },
5092 {NULL}
5093 }),
5094 "Sets a property of an element or klass of elements in the pipeline.\n"
5095 "Besides property-name and value, either 'target-element-name' or\n"
5096 "'target-element-klass' needs to be defined",
5097 GST_VALIDATE_ACTION_TYPE_CAN_EXECUTE_ON_ADDITION |
5098 GST_VALIDATE_ACTION_TYPE_CAN_BE_OPTIONAL |
5099 GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG);
5100
5101 REGISTER_ACTION_TYPE ("set-debug-threshold",
5102 _execute_set_debug_threshold,
5103 ((GstValidateActionParameter [])
5104 {
5105 {
5106 .name = "debug-threshold",
5107 .description = "String defining debug threshold\n"
5108 "See gst_debug_set_threshold_from_string",
5109 .mandatory = TRUE,
5110 .types = "string"},
5111 {NULL}
5112 }),
5113 "Sets the debug level to be used, same format as\n"
5114 "setting the GST_DEBUG env variable",
5115 GST_VALIDATE_ACTION_TYPE_NONE);
5116
5117 REGISTER_ACTION_TYPE ("include",
5118 NULL, /* This is handled directly when loading a scenario */
5119 ((GstValidateActionParameter [])
5120 {
5121 {
5122 .name = "location",
5123 .description = "The location of the sub scenario to include.",
5124 .mandatory = TRUE,
5125 .types = "string"},
5126 {NULL}
5127 }),
5128 "Include a sub scenario file.",
5129 GST_VALIDATE_ACTION_TYPE_NONE);
5130
5131 REGISTER_ACTION_TYPE ("emit-signal", _execute_emit_signal,
5132 ((GstValidateActionParameter [])
5133 {
5134 {
5135 .name = "target-element-name",
5136 .description = "The name of the GstElement to emit a signal on",
5137 .mandatory = TRUE,
5138 .types = "string"
5139 },
5140 {
5141 .name = "signal-name",
5142 .description = "The name of the signal to emit on @target-element-name",
5143 .mandatory = TRUE,
5144 .types = "string",
5145 NULL
5146 },
5147 {NULL}
5148 }),
5149 "Emits a signal to an element in the pipeline",
5150 GST_VALIDATE_ACTION_TYPE_NONE);
5151
5152 REGISTER_ACTION_TYPE ("appsrc-push", _execute_appsrc_push,
5153 ((GstValidateActionParameter [])
5154 {
5155 {
5156 .name = "target-element-name",
5157 .description = "The name of the appsrc to push data on",
5158 .mandatory = TRUE,
5159 .types = "string"
5160 },
5161 {
5162 .name = "file-name",
5163 .description = "Relative path to a file whose contents will be pushed as a buffer",
5164 .mandatory = TRUE,
5165 .types = "string"
5166 },
5167 {
5168 .name = "offset",
5169 .description = "Offset within the file where the buffer will start",
5170 .mandatory = FALSE,
5171 .types = "uint64"
5172 },
5173 {
5174 .name = "size",
5175 .description = "Number of bytes from the file that will be pushed as a buffer",
5176 .mandatory = FALSE,
5177 .types = "uint64"
5178 },
5179 {
5180 .name = "caps",
5181 .description = "Caps for the buffer to be pushed",
5182 .mandatory = FALSE,
5183 .types = "caps"
5184 },
5185 {NULL}
5186 }),
5187 "Queues a buffer in an appsrc. If the pipeline state allows flow of buffers, the next action is not run until the buffer has been pushed.",
5188 GST_VALIDATE_ACTION_TYPE_NONE);
5189
5190 REGISTER_ACTION_TYPE ("appsrc-eos", _execute_appsrc_eos,
5191 ((GstValidateActionParameter [])
5192 {
5193 {
5194 .name = "target-element-name",
5195 .description = "The name of the appsrc to emit EOS on",
5196 .mandatory = TRUE,
5197 .types = "string"
5198 },
5199 {NULL}
5200 }),
5201 "Queues a EOS event in an appsrc.",
5202 GST_VALIDATE_ACTION_TYPE_NONE);
5203
5204 REGISTER_ACTION_TYPE ("flush", _execute_flush,
5205 ((GstValidateActionParameter [])
5206 {
5207 {
5208 .name = "target-element-name",
5209 .description = "The name of the appsrc to flush on",
5210 .mandatory = TRUE,
5211 .types = "string"
5212 },
5213 {
5214 .name = "reset-time",
5215 .description = "Whether the flush should reset running time",
5216 .mandatory = FALSE,
5217 .types = "boolean",
5218 .def = "TRUE"
5219 },
5220 {NULL}
5221 }),
5222 "Sends FLUSH_START and FLUSH_STOP events.",
5223 GST_VALIDATE_ACTION_TYPE_NONE);
5224
5225 REGISTER_ACTION_TYPE ("disable-plugin", _execute_disable_plugin,
5226 ((GstValidateActionParameter [])
5227 {
5228 {
5229 .name = "plugin-name",
5230 .description = "The name of the GstPlugin to disable",
5231 .mandatory = TRUE,
5232 .types = "string"
5233 },
5234 {
5235 .name = "as-config",
5236 .description = "Execute action as a config action (meaning when loading the scenario)",
5237 .mandatory = FALSE,
5238 .types = "boolean",
5239 .def = "false"
5240 },
5241 {NULL}
5242 }),
5243 "Disables a GstPlugin",
5244 GST_VALIDATE_ACTION_TYPE_NONE);
5245
5246 REGISTER_ACTION_TYPE ("check-last-sample", _execute_check_last_sample,
5247 ((GstValidateActionParameter []) {
5248 {
5249 .name = "sink-name",
5250 .description = "The name of the sink element to check sample on.",
5251 .mandatory = FALSE,
5252 .types = "string",
5253 NULL
5254 },
5255 {
5256 .name = "sink-factory-name",
5257 .description = "The name of the factory of the sink element to check sample on.",
5258 .mandatory = FALSE,
5259 .types = "string",
5260 NULL
5261 },
5262 {
5263 .name = "sinkpad-caps",
5264 .description = "The caps (as string) of the sink to check.",
5265 .mandatory = FALSE,
5266 .types = "string",
5267 NULL
5268 },
5269 {
5270 .name = "checksum",
5271 .description = "The reference checksum of the buffer.",
5272 .mandatory = TRUE,
5273 .types = "string",
5274 NULL
5275 },
5276 {NULL}
5277 }),
5278 "Checks the last-sample checksum on declared Sink element."
5279 " This allows checking the checksum of a buffer after a 'seek' or after a GESTimeline 'commit'"
5280 " for example",
5281 GST_VALIDATE_ACTION_TYPE_INTERLACED);
5282
5283 /* *INDENT-ON* */
5284
5285 for (tmp = gst_validate_plugin_get_config (NULL); tmp; tmp = tmp->next) {
5286 const gchar *action_typename;
5287 GstStructure *plug_conf = (GstStructure *) tmp->data;
5288
5289 if ((action_typename = gst_structure_get_string (plug_conf, "action"))) {
5290 GstValidateAction *action;
5291 GstValidateActionType *atype = _find_action_type (action_typename);
5292
5293 if (!atype) {
5294 g_error ("[CONFIG ERROR] Action type %s not found", action_typename);
5295
5296 continue;
5297 }
5298
5299
5300 if (atype->flags & GST_VALIDATE_ACTION_TYPE_HANDLED_IN_CONFIG) {
5301 GST_INFO ("Action type %s from configuration files"
5302 " is handled.", action_typename);
5303 continue;
5304 }
5305
5306 if (!(atype->flags & GST_VALIDATE_ACTION_TYPE_CONFIG) &&
5307 !(_action_type_has_parameter (atype, "as-config"))) {
5308 g_error ("[CONFIG ERROR] Action '%s' is not a config action",
5309 action_typename);
5310
5311 continue;
5312 }
5313
5314 gst_structure_set (plug_conf, "as-config", G_TYPE_BOOLEAN, TRUE, NULL);
5315 gst_structure_set_name (plug_conf, action_typename);
5316
5317 action = gst_validate_action_new (NULL, atype, plug_conf, FALSE);
5318 gst_validate_action_unref (action);
5319 }
5320 }
5321 }
5322
5323 void
gst_validate_scenario_deinit(void)5324 gst_validate_scenario_deinit (void)
5325 {
5326 _free_action_types (action_types);
5327 action_types = NULL;
5328 }
5329