From daa1519e3da729d887d1d917747bf14758cccdab Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 13 Jul 2023 16:25:53 -0400 Subject: [PATCH] nle: Add validate tests support This allows us to start testing internal of the elements by linking the nle pluging directly with validate and implement validate tests with specific action types. Part-of: --- .../plugins/nle/meson.build | 12 +- .../plugins/nle/nlecomposition.c | 102 +++++++++++ .../plugins/nle/nlecomposition.h | 1 + .../plugins/nle/nleobject.c | 8 + .../plugins/nle/validate.c | 170 ++++++++++++++++++ .../gst-editing-services/tests/meson.build | 4 +- .../tests/validate/meson.build | 33 ++++ ...ource_in_composition_playback.validatetest | 15 ++ .../nle/simple_source_playback.validatetest | 15 ++ 9 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 subprojects/gst-editing-services/plugins/nle/validate.c create mode 100644 subprojects/gst-editing-services/tests/validate/nle/simple_source_in_composition_playback.validatetest create mode 100644 subprojects/gst-editing-services/tests/validate/nle/simple_source_playback.validatetest diff --git a/subprojects/gst-editing-services/plugins/nle/meson.build b/subprojects/gst-editing-services/plugins/nle/meson.build index ccbddcbcb7..13cbd45dd8 100644 --- a/subprojects/gst-editing-services/plugins/nle/meson.build +++ b/subprojects/gst-editing-services/plugins/nle/meson.build @@ -7,10 +7,18 @@ nle_sources = ['nleobject.c', 'gstnle.c' ] +deps = [gst_dep, gstbase_dep] +c_args = ges_c_args +if gstvalidate_dep.found() + deps += [gstvalidate_dep] + nle_sources += ['validate.c'] +endif + + nle = library('gstnle', nle_sources, - dependencies : [gst_dep, gstbase_dep], + dependencies : deps, include_directories: [configinc], - c_args : ges_c_args, + c_args : c_args, install : true, install_dir : plugins_install_dir, ) diff --git a/subprojects/gst-editing-services/plugins/nle/nlecomposition.c b/subprojects/gst-editing-services/plugins/nle/nlecomposition.c index 39ddec6b96..cb217e0e60 100644 --- a/subprojects/gst-editing-services/plugins/nle/nlecomposition.c +++ b/subprojects/gst-editing-services/plugins/nle/nlecomposition.c @@ -3729,3 +3729,105 @@ _nle_composition_remove_object (NleComposition * comp, NleObject * object) return TRUE; } + + + +GstElement *nle_composition_get_nle_object_by_name (NleComposition * comp, + const gchar * name); + +GstElement * +nle_find_object_in_bin_recurse (GstBin * object, const gchar * name) +{ + GstElement *res = gst_bin_get_by_name_recurse_up (GST_BIN (object), name); + + if (res) + return res; + + GstIterator *it = + gst_bin_iterate_all_by_element_factory_name (GST_BIN (object), + "nlecomposition"); + GValue item = G_VALUE_INIT; + + while (gst_iterator_next (it, &item) == GST_ITERATOR_OK) { + GstElement *compo = g_value_get_object (&item); + + if (NLE_IS_COMPOSITION (compo)) { + res = + nle_composition_get_nle_object_by_name (NLE_COMPOSITION (compo), + name); + + if (res) { + g_value_reset (&item); + break; + } + } + + g_value_reset (&item); + } + gst_iterator_free (it); + + return res; +} + +GstElement * +nle_composition_get_nle_object_by_name (NleComposition * comp, + const gchar * name) +{ + NleCompositionPrivate *priv = comp->priv; + GList *l; + GstElement *res = NULL; + + GST_INFO_OBJECT (comp, "Looking for child: %s", name); + + GST_OBJECT_LOCK (comp); + GList *objs = NULL; + + /* FIXME Implement a task to retrieve objects if needed */ + objs = + g_list_copy_deep (priv->objects_start, (GCopyFunc) gst_object_ref, NULL); + GST_OBJECT_UNLOCK (comp); + + /* Check in the list of objects, already added */ + for (l = objs; l; l = l->next) { + NleObject *obj = NLE_OBJECT (l->data); + + if (!g_strcmp0 (GST_OBJECT_NAME (obj), name)) { + res = gst_object_ref (GST_ELEMENT (obj)); + break; + } + + res = nle_find_object_in_bin_recurse (GST_BIN (obj), name); + if (res) { + break; + } + } + + g_list_free_full (objs, (GDestroyNotify) gst_object_unref); + if (res) + goto done; + + ACTIONS_LOCK (comp); + /* Check if the object is about to be added */ + for (GList * tmp = comp->priv->actions; tmp != NULL; tmp = tmp->next) { + Action *act = tmp->data; + + if (ACTION_CALLBACK (act) == G_CALLBACK (_add_object_func)) { + ChildIOData *d = ((GClosure *) act)->data; + + if (!g_strcmp0 (GST_OBJECT_NAME (d->object), name)) { + res = gst_object_ref (GST_ELEMENT (d->object)); + break; + } + + res = nle_find_object_in_bin_recurse (GST_BIN (d->object), name); + if (res) { + break; + } + + } + } + ACTIONS_UNLOCK (comp); + +done: + return res; +} diff --git a/subprojects/gst-editing-services/plugins/nle/nlecomposition.h b/subprojects/gst-editing-services/plugins/nle/nlecomposition.h index 9f5da3d8c3..a5daaf962b 100644 --- a/subprojects/gst-editing-services/plugins/nle/nlecomposition.h +++ b/subprojects/gst-editing-services/plugins/nle/nlecomposition.h @@ -61,6 +61,7 @@ struct _NleCompositionClass }; GType nle_composition_get_type (void) G_GNUC_INTERNAL; +GstElement * nle_find_object_in_bin_recurse (GstBin * object, const gchar *name); G_END_DECLS #endif /* __NLE_COMPOSITION_H__ */ diff --git a/subprojects/gst-editing-services/plugins/nle/nleobject.c b/subprojects/gst-editing-services/plugins/nle/nleobject.c index a1085619e1..aa76033b54 100644 --- a/subprojects/gst-editing-services/plugins/nle/nleobject.c +++ b/subprojects/gst-editing-services/plugins/nle/nleobject.c @@ -56,6 +56,10 @@ GST_DEBUG_CATEGORY_STATIC (nleobject_debug); static GObjectClass *parent_class = NULL; +#ifdef HAVE_GST_VALIDATE +extern void nle_validate_init (void); +#endif + /**************************************************** * Helper macros * ****************************************************/ @@ -334,6 +338,10 @@ nle_object_class_init (NleObjectClass * klass) G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); gst_type_mark_as_plugin_api (NLE_TYPE_OBJECT, 0); + +#ifdef HAVE_GST_VALIDATE + nle_validate_init (); +#endif } static void diff --git a/subprojects/gst-editing-services/plugins/nle/validate.c b/subprojects/gst-editing-services/plugins/nle/validate.c new file mode 100644 index 0000000000..a50b4954fb --- /dev/null +++ b/subprojects/gst-editing-services/plugins/nle/validate.c @@ -0,0 +1,170 @@ +/* Non Linear Engine plugin + * + * Copyright (C) 2023 Thibault Saunier + * + * validate.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include "nle.h" + +GST_DEBUG_CATEGORY_STATIC (nle_validate_debug); +#undef GST_CAT_DEFAULT +#define GST_CAT_DEFAULT nle_validate_debug + +void nle_validate_init (void); + +#ifdef G_HAVE_ISO_VARARGS +#define REPORT_UNLESS(condition, errpoint, ...) \ + G_STMT_START { \ + if (!(condition)) { \ + res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \ + gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \ + SCENARIO_ACTION_EXECUTION_ERROR, \ + __VA_ARGS__); \ + goto errpoint; \ + } \ + } \ + G_STMT_END +#else /* G_HAVE_GNUC_VARARGS */ +#ifdef G_HAVE_GNUC_VARARGS +#define REPORT_UNLESS(condition, errpoint, args...) \ + G_STMT_START { \ + if (!(condition)) { \ + res = GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED; \ + gst_validate_report_action(GST_VALIDATE_REPORTER(scenario), action, \ + SCENARIO_ACTION_EXECUTION_ERROR, ##args); \ + goto errpoint; \ + } \ + } \ + G_STMT_END +#endif /* G_HAVE_ISO_VARARGS */ +#endif /* G_HAVE_GNUC_VARARGS */ + +#define NLE_START_VALIDATE_ACTION(funcname) \ +static gint \ +funcname(GstValidateScenario *scenario, GstValidateAction *action) { \ + GstValidateActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK; \ + GstElement * pipeline = gst_validate_scenario_get_pipeline(scenario); + +#define NLE_END_VALIDATE_ACTION \ +done: \ + gst_clear_object(&pipeline); \ + return res; \ +} + +NLE_START_VALIDATE_ACTION (_add_object) +{ + GError *err = NULL; + GstElement *nleobj = NULL; + GstElement *child = + gst_parse_bin_from_description_full (gst_structure_get_string + (action->structure, "desc"), FALSE, NULL, + GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS | GST_PARSE_FLAG_PLACE_IN_BIN, + &err); + const gchar *objname = gst_structure_get_string (action->structure, + "object-name"); + + REPORT_UNLESS (child, clean, "Failed to create element from description: %s", + err ? err->message : "Unknown error"); + + nleobj = nle_find_object_in_bin_recurse (GST_BIN (pipeline), objname); + + REPORT_UNLESS (nleobj, clean, "Could not find object `%s`", objname); + + gboolean is_operation = NLE_IS_OPERATION (nleobj); + gboolean is_src = NLE_IS_SOURCE (nleobj); + if (GST_IS_BIN (child) && (is_src || is_operation)) { + if (child->numsrcpads == 0 && !gst_element_class_get_pad_template + (GST_ELEMENT_GET_CLASS (child), "src")) { + GstPad *srcpad = gst_bin_find_unlinked_pad (GST_BIN (child), GST_PAD_SRC); + if (srcpad) { + gst_element_add_pad (child, gst_ghost_pad_new ("src", srcpad)); + gst_object_unref (srcpad); + } + } + + if (is_operation && child->numsinkpads == 0 + && !gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (child), + "sink")) { + GstPad *sinkpad = + gst_bin_find_unlinked_pad (GST_BIN (child), GST_PAD_SINK); + if (sinkpad) { + gst_element_add_pad (child, gst_ghost_pad_new ("sink", sinkpad)); + gst_object_unref (sinkpad); + } + } + + } + REPORT_UNLESS (gst_bin_add (GST_BIN (nleobj), gst_object_ref (child)), clean, + "Could not add child to nle object"); + +clean: + g_clear_error (&err); + gst_clear_object (&child); + + gst_clear_object (&nleobj); + + goto done; +} + +NLE_END_VALIDATE_ACTION; + +static void +register_action_types () +{ + GST_DEBUG_CATEGORY_INIT (nle_validate_debug, "nlevalidate", + GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "NLE validate"); + +/* *INDENT-OFF* */ + gst_validate_register_action_type ("nle-add-child", "nle", + _add_object, + (GstValidateActionParameter []) { + { + .name = "object-name", + .description = "The name of the nle object to which to add child, will recurse, \n" + " potentially in `nlecompositions` to find the right object", + .mandatory = TRUE, + .types = "string", + }, + { + .name = "desc", + .description = "The 'bin description' of the child to add", + .mandatory = TRUE, + .types = "string", + }, + {NULL} + }, + "Add a child to a NleObject\n", + GST_VALIDATE_ACTION_TYPE_NONE); +/* *INDENT-ON* */ +} + + +void +nle_validate_init () +{ + register_action_types (); +} diff --git a/subprojects/gst-editing-services/tests/meson.build b/subprojects/gst-editing-services/tests/meson.build index 7dea3eb0ee..99e609c0cd 100644 --- a/subprojects/gst-editing-services/tests/meson.build +++ b/subprojects/gst-editing-services/tests/meson.build @@ -5,7 +5,7 @@ endif # FIXME: make check work on windows if host_system != 'windows' and gstcheck_dep.found() subdir('check') + subdir('validate') endif -subdir('validate') -subdir('benchmarks') \ No newline at end of file +subdir('benchmarks') diff --git a/subprojects/gst-editing-services/tests/validate/meson.build b/subprojects/gst-editing-services/tests/validate/meson.build index dc61b42962..ec4c883176 100644 --- a/subprojects/gst-editing-services/tests/validate/meson.build +++ b/subprojects/gst-editing-services/tests/validate/meson.build @@ -7,3 +7,36 @@ install_data (['geslaunch.py'], env = environment() env.prepend('GST_VALIDATE_APPS_DIR', meson.current_source_dir()) meson.add_devenv(env) + +gst_tester = find_program('gst-tester-@0@'.format(apiversion), required: get_option('tests')) +if not gst_tester.found() + subdir_done() +endif + +tests = [ + 'nle/simple_source_playback', + 'nle/simple_source_in_composition_playback', +] + +env = environment() +env.set('GST_PLUGIN_PATH_1_0', meson.global_build_root(), pluginsdirs) +env.set('GST_PLUGIN_SYSTEM_PATH_1_0', '') +env.set('GST_REGISTRY', '@0@/@1@.registry'.format(meson.current_build_dir(), 'validate')) +env.set('GST_PLUGIN_SCANNER_1_0', gst_plugin_scanner_path) +env.set('GST_PLUGIN_LOADING_WHITELIST', 'gstreamer', 'gst-validate', + 'gst-plugins-base@' + meson.project_build_root(), + 'gst-editing-services@' + meson.project_build_root(), +) + +foreach t: tests + test_dir_name = t.split('/') + test_name = 'validate' + foreach c: test_dir_name + test_name += '.' + c + endforeach + test_env = env + test_env.set('GST_VALIDATE_LOGSDIR', join_paths(meson.current_build_dir(), test_name)) + test_file = join_paths(meson.current_source_dir(), t + '.validatetest') + test(test_name, gst_tester, args: [test_file, '--use-fakesinks'], + env: test_env, timeout : 3 * 60, protocol: 'tap') +endforeach diff --git a/subprojects/gst-editing-services/tests/validate/nle/simple_source_in_composition_playback.validatetest b/subprojects/gst-editing-services/tests/validate/nle/simple_source_in_composition_playback.validatetest new file mode 100644 index 0000000000..e522a13556 --- /dev/null +++ b/subprojects/gst-editing-services/tests/validate/nle/simple_source_in_composition_playback.validatetest @@ -0,0 +1,15 @@ +meta, + handles-states=true, + ignore-eos=true, + args={ + "nlecomposition name=compo ! $(videosink)", + } + +nle-add-child, object-name="compo", desc="nlesource name=s inpoint=0 duration=200000000" +nle-add-child, object-name="s", desc="videotestsrc pattern=blue" + +play +check-position, on-message=eos, expected-position=0.2 + +stop + diff --git a/subprojects/gst-editing-services/tests/validate/nle/simple_source_playback.validatetest b/subprojects/gst-editing-services/tests/validate/nle/simple_source_playback.validatetest new file mode 100644 index 0000000000..0ad208d9b4 --- /dev/null +++ b/subprojects/gst-editing-services/tests/validate/nle/simple_source_playback.validatetest @@ -0,0 +1,15 @@ +meta, + handles-states=true, + ignore-eos=true, + args={ + "nlesource name=s inpoint=0 duration=200000000 ! $(videosink)", + } + +nle-add-child, + object-name="s", + desc="videotestsrc ! timeoverlay" + +play +check-position, on-message=eos, expected-position=0.2 + +stop