validate: Allow overriding features rank early in testfiles meta

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7700>
This commit is contained in:
Thibault Saunier 2024-08-27 17:48:25 -04:00 committed by GStreamer Marge Bot
parent f5b0f91d33
commit 2c88bbf07f
9 changed files with 151 additions and 12 deletions

View file

@ -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.

View file

@ -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 void gst_validate_report_deinit (void);
G_GNUC_INTERNAL gboolean gst_validate_send (JsonNode * root); 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 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 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, G_GNUC_INTERNAL GstValidateScenario* gst_validate_scenario_from_structs (GstValidateRunner* runner, GstElement* pipeline, GList* structures,
const gchar* origin_file); const gchar* origin_file);

View file

@ -7698,6 +7698,9 @@ register_action_types (void)
GBytes *meta_expected_issues_doc = GBytes *meta_expected_issues_doc =
g_resource_lookup_data (resource, "/validate/doc/meta-expected-issues.md", g_resource_lookup_data (resource, "/validate/doc/meta-expected-issues.md",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL); 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* */ /* *INDENT-OFF* */
REGISTER_ACTION_TYPE ("meta", NULL, REGISTER_ACTION_TYPE ("meta", NULL,
@ -7874,12 +7877,31 @@ register_action_types (void)
.possible_variables = NULL, .possible_variables = NULL,
.def = "{}" .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} {NULL}
}), }),
"Scenario metadata.\n\nNOTE: it used to be called \"description\"", "Scenario metadata.\n\nNOTE: it used to be called \"description\"",
GST_VALIDATE_ACTION_TYPE_CONFIG); GST_VALIDATE_ACTION_TYPE_CONFIG);
g_bytes_unref (meta_config_doc); g_bytes_unref (meta_config_doc);
g_bytes_unref (meta_expected_issues_doc); g_bytes_unref (meta_expected_issues_doc);
g_bytes_unref (meta_features_rank_doc);
REGISTER_ACTION_TYPE ("seek", _execute_seek, REGISTER_ACTION_TYPE ("seek", _execute_seek,
((GstValidateActionParameter []) { ((GstValidateActionParameter []) {

View file

@ -1352,9 +1352,8 @@ done:
g_clear_pointer (&match_info, g_match_info_free); g_clear_pointer (&match_info, g_match_info_free);
} }
static gboolean gboolean
_structure_set_variables (const GstIdStr * fieldname, GValue * value, gst_validate_structure_file_field_is_metadata (const GstIdStr * fieldname)
ReplaceData * data)
{ {
static const gchar *skip_fields[] = { static const gchar *skip_fields[] = {
"__filename__", "__filename__",
@ -1363,7 +1362,15 @@ _structure_set_variables (const GstIdStr * fieldname, GValue * value,
NULL, 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; return TRUE;
if (GST_VALUE_HOLDS_LIST (value)) { if (GST_VALUE_HOLDS_LIST (value)) {

View file

@ -93,6 +93,7 @@ GST_VALIDATE_API
void gst_validate_structure_resolve_variables (gpointer source, GstStructure *structure, GstStructure *local_variables, void gst_validate_structure_resolve_variables (gpointer source, GstStructure *structure, GstStructure *local_variables,
GstValidateStructureResolveVariablesFlags flags); GstValidateStructureResolveVariablesFlags flags);
void gst_validate_structure_set_variables_from_struct_file(GstStructure* vars, const gchar* struct_file); 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); void gst_validate_set_globals(GstStructure* structure);
GST_VALIDATE_API GST_VALIDATE_API
gboolean gst_validate_fail_on_missing_plugin(void); gboolean gst_validate_fail_on_missing_plugin(void);

View file

@ -26,6 +26,7 @@
* @short_description: Initialize GstValidate * @short_description: Initialize GstValidate
*/ */
#include "gst/gstidstr.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
# include "config.h" # include "config.h"
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
@ -265,16 +266,23 @@ get_structures_from_array_in_meta (const gchar * fieldname)
if (!meta) if (!meta)
return NULL; return NULL;
res = get_structures_from_array (meta, fieldname);
if (res)
return res;
gst_structure_get (meta, gst_structure_get (meta,
"__lineno__", G_TYPE_INT, &current_lineno, "__lineno__", G_TYPE_INT, &current_lineno,
"__debug__", G_TYPE_STRING, &debug, "__debug__", G_TYPE_STRING, &debug,
"__filename__", G_TYPE_STRING, &filename, NULL); "__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) { if (strs) {
gint i; gint i;
@ -294,6 +302,7 @@ get_structures_from_array_in_meta (const gchar * fieldname)
} }
} }
done:
g_free (filename); g_free (filename);
g_free (debug); g_free (debug);
g_strfreev (strs); g_strfreev (strs);
@ -612,6 +621,46 @@ validate_test_include_paths (const gchar * includer_file)
return env_configdir; 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 */ /* Only the first monitor pipeline will be used */
GstStructure * GstStructure *
gst_validate_setup_test_file (const gchar * testfile, gboolean use_fakesinks) 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 (); register_action_types ();
gst_validate_scenario_check_and_set_needs_clock_sync (testfile_structs, &res); 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_set_test_file_globals (res, global_testfile, use_fakesinks);
gst_validate_structure_resolve_variables (NULL, res, NULL, 0); gst_validate_structure_resolve_variables (NULL, res, NULL, 0);

View file

@ -3,6 +3,7 @@
<gresource prefix="/validate/"> <gresource prefix="/validate/">
<file>doc/meta-configs.md</file> <file>doc/meta-configs.md</file>
<file>doc/meta-expected-issues.md</file> <file>doc/meta-expected-issues.md</file>
<file>doc/meta-features-rank.md</file>
</gresource> </gresource>
</gresources> </gresources>

View file

@ -33,6 +33,10 @@
#include "utils.h" #include "utils.h"
#include "ges-launcher-kb.h" #include "ges-launcher-kb.h"
#ifdef HAVE_GST_VALIDATE
#include <gst/validate/validate.h>
#endif
typedef enum typedef enum
{ {
GST_PLAY_TRICK_MODE_NONE = 0, GST_PLAY_TRICK_MODE_NONE = 0,
@ -321,6 +325,19 @@ _parse_track_type (const gchar * option_name, const gchar * value,
return TRUE; 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 static gboolean
_set_track_restriction_caps (GESTrack * track, const gchar * caps_str) _set_track_restriction_caps (GESTrack * track, const gchar * caps_str)
{ {
@ -1037,6 +1054,8 @@ bus_message_cb (GstBus * bus, GstMessage * message, GESLauncher * self)
break; break;
} }
case GST_MESSAGE_EOS: 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) { if (!self->priv->parsed_options.ignore_eos) {
ges_ok ("\nDone\n"); ges_ok ("\nDone\n");
g_application_quit (G_APPLICATION (self)); g_application_quit (G_APPLICATION (self));
@ -1177,7 +1196,8 @@ _run_pipeline (GESLauncher * self)
bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline)); bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline));
gst_bus_add_signal_watch (bus); 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)); 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.", "Specify the track restriction caps of the audio track.",
}, },
#ifdef HAVE_GST_VALIDATE #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." "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 " " Scenarios describe actions to execute, such as seeks or setting of "
"properties. " "properties. "

View file

@ -138,7 +138,14 @@ ges_validate_activate (GstPipeline * pipeline, GESLauncher * launcher,
if (opts->testfile) { if (opts->testfile) {
if (opts->scenario) if (opts->scenario)
g_error ("Can not specify scenario and testfile at the same time"); 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) { } else if (opts->scenario) {
if (g_strcmp0 (opts->scenario, "none")) { if (g_strcmp0 (opts->scenario, "none")) {
gchar *scenario_name = gchar *scenario_name =