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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5035>
This commit is contained in:
Thibault Saunier 2023-07-13 16:25:53 -04:00 committed by GStreamer Marge Bot
parent a39ac67d70
commit daa1519e3d
9 changed files with 356 additions and 4 deletions

View file

@ -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,
)

View file

@ -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;
}

View file

@ -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__ */

View file

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

View file

@ -0,0 +1,170 @@
/* Non Linear Engine plugin
*
* Copyright (C) 2023 Thibault Saunier <tsaunier@igalia.com>
*
* 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 <gst/gst.h>
#include <gst/validate/validate.h>
#include <gst/validate/gst-validate-utils.h>
#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 ();
}

View file

@ -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')

View file

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

View file

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

View file

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