From 2c88bbf07f99f8e48a5d2fcef6677a60e2f6f3d7 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 27 Aug 2024 17:48:25 -0400 Subject: [PATCH] validate: Allow overriding features rank early in testfiles meta Part-of: --- .../gst/validate/doc/meta-features-rank.md | 16 ++++ .../gst/validate/gst-validate-internal.h | 1 + .../gst/validate/gst-validate-scenario.c | 22 ++++++ .../gst/validate/gst-validate-utils.c | 15 +++- .../gst/validate/gst-validate-utils.h | 1 + .../validate/gst/validate/validate.c | 74 +++++++++++++++++-- .../validate/gst/validate/validate.res | 1 + .../gst-editing-services/tools/ges-launcher.c | 24 +++++- .../gst-editing-services/tools/ges-validate.c | 9 ++- 9 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 subprojects/gst-devtools/validate/gst/validate/doc/meta-features-rank.md diff --git a/subprojects/gst-devtools/validate/gst/validate/doc/meta-features-rank.md b/subprojects/gst-devtools/validate/gst/validate/doc/meta-features-rank.md new file mode 100644 index 0000000000..e1f24a9783 --- /dev/null +++ b/subprojects/gst-devtools/validate/gst/validate/doc/meta-features-rank.md @@ -0,0 +1,16 @@ +The `features-rank` field is an array of structures that defines how to +override `GstPluginFeature::rank` to ensure some features will be used, +or at contrary won't be used. + +For example: + +``` yaml +features-rank = { + [mandatory, glvideomixer=9999], + [optional, someoptionalfeature=0], +}, +``` + +One could also use the `set-feature-rank` scenario action, but that +happens after GStreamer or other components are initialized which might +be a problem in some cases. diff --git a/subprojects/gst-devtools/validate/gst/validate/gst-validate-internal.h b/subprojects/gst-devtools/validate/gst/validate/gst-validate-internal.h index c6134709c2..7fede688ae 100644 --- a/subprojects/gst-devtools/validate/gst/validate/gst-validate-internal.h +++ b/subprojects/gst-devtools/validate/gst/validate/gst-validate-internal.h @@ -64,6 +64,7 @@ G_GNUC_INTERNAL void gst_validate_deinit_runner (void); G_GNUC_INTERNAL void gst_validate_report_deinit (void); G_GNUC_INTERNAL gboolean gst_validate_send (JsonNode * root); G_GNUC_INTERNAL void gst_validate_set_test_file_globals (GstStructure* meta, const gchar* testfile, gboolean use_fakesinks); +G_GNUC_INTERNAL gboolean gst_validate_structure_file_field_is_metadata (const GstIdStr *field_id); G_GNUC_INTERNAL gboolean gst_validate_get_test_file_scenario (GList** structs, const gchar** scenario_name, gchar** original_name); G_GNUC_INTERNAL GstValidateScenario* gst_validate_scenario_from_structs (GstValidateRunner* runner, GstElement* pipeline, GList* structures, const gchar* origin_file); diff --git a/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c b/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c index 0827168057..2c937f730f 100644 --- a/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c +++ b/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c @@ -7698,6 +7698,9 @@ register_action_types (void) GBytes *meta_expected_issues_doc = g_resource_lookup_data (resource, "/validate/doc/meta-expected-issues.md", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); + GBytes *meta_features_rank_doc = + g_resource_lookup_data (resource, "/validate/doc/meta-features-rank.md", + G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); /* *INDENT-OFF* */ REGISTER_ACTION_TYPE ("meta", NULL, @@ -7874,12 +7877,31 @@ register_action_types (void) .possible_variables = NULL, .def = "{}" }, + { + .name="features-rank", + .description=g_bytes_get_data (meta_features_rank_doc, NULL), + .mandatory = FALSE, + .types = "bool", + .possible_variables = NULL, + .def = "false" + }, + { + .name="monitor-all-pipelines", + .description="This should only be used in `.validatetest` files, and allows forcing to monitor " + "all pipelines instead of only the one the tools wanted to monitor, for example to " + "use `validateflow` on auxilary pipelines", + .mandatory = FALSE, + .types = "bool", + .possible_variables = NULL, + .def = "false" + }, {NULL} }), "Scenario metadata.\n\nNOTE: it used to be called \"description\"", GST_VALIDATE_ACTION_TYPE_CONFIG); g_bytes_unref (meta_config_doc); g_bytes_unref (meta_expected_issues_doc); + g_bytes_unref (meta_features_rank_doc); REGISTER_ACTION_TYPE ("seek", _execute_seek, ((GstValidateActionParameter []) { diff --git a/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.c b/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.c index 72886b380a..39ae8063e0 100644 --- a/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.c +++ b/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.c @@ -1352,9 +1352,8 @@ done: g_clear_pointer (&match_info, g_match_info_free); } -static gboolean -_structure_set_variables (const GstIdStr * fieldname, GValue * value, - ReplaceData * data) +gboolean +gst_validate_structure_file_field_is_metadata (const GstIdStr * fieldname) { static const gchar *skip_fields[] = { "__filename__", @@ -1363,7 +1362,15 @@ _structure_set_variables (const GstIdStr * fieldname, GValue * value, NULL, }; - if (fieldname && g_strv_contains (skip_fields, gst_id_str_as_str (fieldname))) + return fieldname + && g_strv_contains (skip_fields, gst_id_str_as_str (fieldname)); +} + +static gboolean +_structure_set_variables (const GstIdStr * fieldname, GValue * value, + ReplaceData * data) +{ + if (gst_validate_structure_file_field_is_metadata (fieldname)) return TRUE; if (GST_VALUE_HOLDS_LIST (value)) { diff --git a/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.h b/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.h index 3ae12ba317..91d3e7879c 100644 --- a/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.h +++ b/subprojects/gst-devtools/validate/gst/validate/gst-validate-utils.h @@ -93,6 +93,7 @@ GST_VALIDATE_API void gst_validate_structure_resolve_variables (gpointer source, GstStructure *structure, GstStructure *local_variables, GstValidateStructureResolveVariablesFlags flags); void gst_validate_structure_set_variables_from_struct_file(GstStructure* vars, const gchar* struct_file); +GST_VALIDATE_API void gst_validate_set_globals(GstStructure* structure); GST_VALIDATE_API gboolean gst_validate_fail_on_missing_plugin(void); diff --git a/subprojects/gst-devtools/validate/gst/validate/validate.c b/subprojects/gst-devtools/validate/gst/validate/validate.c index ce9d36ffba..d142ea5b83 100644 --- a/subprojects/gst-devtools/validate/gst/validate/validate.c +++ b/subprojects/gst-devtools/validate/gst/validate/validate.c @@ -26,6 +26,7 @@ * @short_description: Initialize GstValidate */ +#include "gst/gstidstr.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif /* HAVE_CONFIG_H */ @@ -265,16 +266,23 @@ get_structures_from_array_in_meta (const gchar * fieldname) if (!meta) return NULL; - res = get_structures_from_array (meta, fieldname); - if (res) - return res; - gst_structure_get (meta, "__lineno__", G_TYPE_INT, ¤t_lineno, "__debug__", G_TYPE_STRING, &debug, "__filename__", G_TYPE_STRING, &filename, NULL); - strs = gst_validate_utils_get_strv (meta, fieldname); + res = get_structures_from_array (meta, fieldname); + if (res) { + for (GList * tmp = res; tmp; tmp = tmp->next) { + gst_structure_set (tmp->data, + "__lineno__", G_TYPE_INT, current_lineno, + "__filename__", G_TYPE_STRING, filename, + "__debug__", G_TYPE_STRING, debug, NULL); + } + goto done; + } + + strs = gst_validate_utils_get_strv (meta, fieldname); if (strs) { gint i; @@ -294,6 +302,7 @@ get_structures_from_array_in_meta (const gchar * fieldname) } } +done: g_free (filename); g_free (debug); g_strfreev (strs); @@ -612,6 +621,46 @@ validate_test_include_paths (const gchar * includer_file) return env_configdir; } +static gboolean +_set_feature_rank (const GstIdStr * fieldname, GValue * value, + GstStructure * structure) +{ + GstRegistry *registry = gst_registry_get (); + guint rank = 0; + + if (gst_validate_structure_file_field_is_metadata (fieldname)) + return TRUE; + + if (G_VALUE_TYPE (value) == G_TYPE_UINT) { + rank = (guint) g_value_get_uint (value); + } else if (G_VALUE_TYPE (value) == G_TYPE_INT) { + rank = g_value_get_int (value); + } else { + gst_validate_error_structure (structure, + "Invalid value %s for field '%s' (expecting int) in the 'features-rank' structure", + G_VALUE_TYPE_NAME (value), gst_value_serialize (value)); + + return FALSE; + } + + GstPluginFeature *feature = + gst_registry_lookup_feature (registry, gst_id_str_as_str (fieldname)); + if (!feature) { + if (gst_structure_has_name (structure, "mandatory")) { + gst_validate_error_structure (structure, + "Feature `%s` not found while its ranks has been requested to be set to %d", + gst_id_str_as_str (fieldname), rank); + return FALSE; + } + + return TRUE; + } + + gst_plugin_feature_set_rank (feature, rank); + + return TRUE; +} + /* Only the first monitor pipeline will be used */ GstStructure * gst_validate_setup_test_file (const gchar * testfile, gboolean use_fakesinks) @@ -653,6 +702,21 @@ gst_validate_setup_test_file (const gchar * testfile, gboolean use_fakesinks) register_action_types (); gst_validate_scenario_check_and_set_needs_clock_sync (testfile_structs, &res); + GList *feature_ranks_def = + get_structures_from_array_in_meta ("features-rank"); + for (GList * tmp = feature_ranks_def; tmp; tmp = tmp->next) { + GstStructure *feature_ranks = tmp->data; + if (!gst_structure_has_name (feature_ranks, "mandatory") + && !gst_structure_has_name (feature_ranks, "optional")) { + gst_validate_error_structure (res, + "Feature rank structures should have either `mandatory` or `optional` as a name, got: %s", + gst_structure_to_string (feature_ranks)); + return NULL; + } + gst_structure_filter_and_map_in_place_id_str (feature_ranks, + (GstStructureFilterMapIdStrFunc) _set_feature_rank, feature_ranks); + } + gst_validate_set_test_file_globals (res, global_testfile, use_fakesinks); gst_validate_structure_resolve_variables (NULL, res, NULL, 0); diff --git a/subprojects/gst-devtools/validate/gst/validate/validate.res b/subprojects/gst-devtools/validate/gst/validate/validate.res index 1568b88d40..987c4b56f8 100644 --- a/subprojects/gst-devtools/validate/gst/validate/validate.res +++ b/subprojects/gst-devtools/validate/gst/validate/validate.res @@ -3,6 +3,7 @@ doc/meta-configs.md doc/meta-expected-issues.md + doc/meta-features-rank.md diff --git a/subprojects/gst-editing-services/tools/ges-launcher.c b/subprojects/gst-editing-services/tools/ges-launcher.c index 17cb98d570..9dfa8d802e 100644 --- a/subprojects/gst-editing-services/tools/ges-launcher.c +++ b/subprojects/gst-editing-services/tools/ges-launcher.c @@ -33,6 +33,10 @@ #include "utils.h" #include "ges-launcher-kb.h" +#ifdef HAVE_GST_VALIDATE +#include +#endif + typedef enum { GST_PLAY_TRICK_MODE_NONE = 0, @@ -321,6 +325,19 @@ _parse_track_type (const gchar * option_name, const gchar * value, return TRUE; } + +#ifdef HAVE_GST_VALIDATE +static gboolean +_parse_test_file (const gchar * option_name, const gchar * value, + GESLauncherParsedOptions * opts, GError ** error) +{ + opts->testfile = g_strdup (value); + gst_validate_init_debug (); + gst_validate_setup_test_file (opts->testfile, FALSE); + return TRUE; +} +#endif + static gboolean _set_track_restriction_caps (GESTrack * track, const gchar * caps_str) { @@ -1037,6 +1054,8 @@ bus_message_cb (GstBus * bus, GstMessage * message, GESLauncher * self) break; } case GST_MESSAGE_EOS: + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->priv->pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "ges-launch.eos"); if (!self->priv->parsed_options.ignore_eos) { ges_ok ("\nDone\n"); g_application_quit (G_APPLICATION (self)); @@ -1177,7 +1196,8 @@ _run_pipeline (GESLauncher * self) bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline)); gst_bus_add_signal_watch (bus); - g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), self); + g_signal_connect_object (bus, "message", G_CALLBACK (bus_message_cb), self, + 0); g_application_hold (G_APPLICATION (self)); @@ -1433,7 +1453,7 @@ ges_launcher_parse_options (GESLauncher * self, "Specify the track restriction caps of the audio track.", }, #ifdef HAVE_GST_VALIDATE - {"set-test-file", 0, 0, G_OPTION_ARG_STRING, &opts->testfile, + {"set-test-file", 0, 0, G_OPTION_ARG_CALLBACK, &_parse_test_file, "ges-launch-1.0 exposes gst-validate functionalities, such as test files and scenarios." " Scenarios describe actions to execute, such as seeks or setting of " "properties. " diff --git a/subprojects/gst-editing-services/tools/ges-validate.c b/subprojects/gst-editing-services/tools/ges-validate.c index 5ea786ed01..692c9cf438 100644 --- a/subprojects/gst-editing-services/tools/ges-validate.c +++ b/subprojects/gst-editing-services/tools/ges-validate.c @@ -138,7 +138,14 @@ ges_validate_activate (GstPipeline * pipeline, GESLauncher * launcher, if (opts->testfile) { if (opts->scenario) g_error ("Can not specify scenario and testfile at the same time"); - gst_validate_setup_test_file (opts->testfile, opts->mute); + if (!opts->mute) { + gst_validate_set_globals (gst_structure_new ("globals", + "videosink", G_TYPE_STRING, + opts->videosink ? opts->videosink : "autovideosink", "audiosink", + G_TYPE_STRING, + opts->audiosink ? opts->audiosink : "autoaudiosink", NULL) + ); + } } else if (opts->scenario) { if (g_strcmp0 (opts->scenario, "none")) { gchar *scenario_name =