From 302797a9657db1c529560b3a414298903300cbda Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 27 Aug 2024 20:41:18 -0400 Subject: [PATCH] validate: Add a `deep-property-path` parameter to the `wait` signal Allowing wait actions to wait on any property of any element in the pipeline, even for elements that might be added later in the pipeline. This also works for pads which can be pretty useful Part-of: --- .../gst/validate/gst-validate-scenario.c | 134 +++++++++++++++--- 1 file changed, 115 insertions(+), 19 deletions(-) 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 2c937f730f..efdca8ffae 100644 --- a/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c +++ b/subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c @@ -3257,14 +3257,22 @@ typedef struct guint sigid; gboolean check_done; gboolean check_property; + + gchar *parent_name; + gchar *object_name; + gchar *property_name; GMutex lock; } WaitingSignalData; static WaitingSignalData * -waiting_signal_data_new (GstElement * target, GstValidateAction * action) +waiting_signal_data_new (GstElement * target, GstValidateAction * action, + gchar * parent_name, gchar * object_name, gchar * property_name) { WaitingSignalData *data = g_new0 (WaitingSignalData, 1); + data->parent_name = parent_name; + data->object_name = object_name; + data->property_name = property_name; data->target = gst_object_ref (target); data->action = gst_validate_action_ref (action); @@ -3279,6 +3287,9 @@ waiting_signal_data_free (WaitingSignalData * data) g_assert (scenario); + g_free (data->object_name); + g_free (data->parent_name); + g_free (data->property_name); gst_object_unref (data->target); gst_validate_action_unref (data->action); g_free (data); @@ -3302,11 +3313,15 @@ waiting_signal_data_disconnect (WaitingSignalData * data, } static void -stop_waiting_signal_cb (WaitingSignalData * data) +stop_waiting_signal_cb (WaitingSignalData * data, GstObject * prop_object, + GParamSpec * prop, GstObject * object) { GstStructure *check = NULL; GstValidateAction *action = gst_validate_action_ref (data->action); GstValidateScenario *scenario = NULL; + const gchar *property_name = NULL; + gboolean check_property = data->check_property; + GstObject *property_check_target = GST_OBJECT_CAST (data->target); g_mutex_lock (&data->lock); if (data->check_done) { @@ -3315,10 +3330,40 @@ stop_waiting_signal_cb (WaitingSignalData * data) goto cleanup; } + + if (data->object_name) { + if (g_strcmp0 (data->object_name, GST_OBJECT_NAME (prop_object)) != 0) { + goto cleanup; + } + + if (g_strcmp0 (data->property_name, prop->name) != 0) { + goto cleanup; + } + + if (data->parent_name) { + GstObject *parent = gst_object_get_parent (prop_object); + + if (parent && g_strcmp0 (data->parent_name, GST_OBJECT_NAME (parent))) { + goto cleanup; + } + + gst_clear_object (&parent); + } + + property_check_target = prop_object; + property_name = data->property_name; + check_property = + gst_structure_has_field (action->structure, "property-value"); + + } else { + property_name = + gst_structure_get_string (action->structure, "property-name"); + } + scenario = gst_validate_action_get_scenario (data->action); - if (data->check_property && - _check_property (scenario, action, data->target, - gst_structure_get_string (action->structure, "property-name"), + if (check_property && + _check_property (scenario, action, property_check_target, + property_name, gst_structure_get_value (action->structure, "property-value"), FALSE) != GST_VALIDATE_EXECUTE_ACTION_OK) { @@ -3326,6 +3371,7 @@ stop_waiting_signal_cb (WaitingSignalData * data) goto cleanup; } + data->check_done = TRUE; waiting_signal_data_disconnect (data, scenario); if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE, @@ -3349,6 +3395,7 @@ stop_waiting_signal_cb (WaitingSignalData * data) cleanup: gst_validate_action_unref (action); gst_clear_object (&scenario); + g_mutex_unlock (&data->lock); } static GstValidateExecuteActionReturn @@ -3418,21 +3465,56 @@ _execute_wait_for_signal (GstValidateScenario * scenario, const GValue *property_value = gst_structure_get_value (action->structure, "property-value"); DECLARE_AND_GET_PIPELINE (scenario, action); - gboolean checking_property = signal_name == NULL; + const gchar *deep_property_path = + gst_structure_get_string (action->structure, "deep-property-path"); + gboolean checking_property = signal_name == NULL + && deep_property_path == NULL; + gchar *parent_name = NULL, *object_name = NULL, *deep_property_name = NULL; - REPORT_UNLESS (signal_name || property_name, done, - "No signal-name or property-name given for wait action"); - REPORT_UNLESS (!property_name || (property_name && property_value), done, - "`property-name` specified without a `property-value`"); - targets = _find_elements_defined_in_action (scenario, action); - REPORT_UNLESS ((g_list_length (targets) == 1), done, - "Could not find target element."); + if (deep_property_path && *deep_property_path) { + gchar **elem_pad_name = g_strsplit (deep_property_path, ".", 2); + gchar **object_prop_name = + g_strsplit (elem_pad_name[1] ? elem_pad_name[1] : elem_pad_name[0], + "::", + -1); - gst_validate_printf (action, "Waiting for '%s'\n", - signal_name ? signal_name : property_name); + REPORT_UNLESS (object_prop_name[1], done, + "Property specification %s is missing a `::propename` part", + deep_property_path); - target = targets->data; - data = waiting_signal_data_new (target, action); + deep_property_name = g_strdup (object_prop_name[1]); + object_name = g_strdup (object_prop_name[0]); + if (elem_pad_name[1]) { + parent_name = g_strdup (elem_pad_name[0]); + } + + g_strfreev (elem_pad_name); + g_strfreev (object_prop_name); + + target = gst_object_ref (pipeline); + signal_name = g_strdup ("deep-notify"); + + gst_validate_printf (action, "Waiting for 'deep-notify::%s'\n", + deep_property_name); + } else { + + REPORT_UNLESS (signal_name || property_name, done, + "No signal-name or property-name given for wait action"); + REPORT_UNLESS (!property_name || (property_name && property_value), done, + "`property-name` specified without a `property-value`"); + + targets = _find_elements_defined_in_action (scenario, action); + REPORT_UNLESS ((g_list_length (targets) == 1), done, + "Could not find target element."); + target = targets->data; + gst_validate_printf (action, "Waiting for '%s'\n", + signal_name ? signal_name : property_name); + } + + + data = + waiting_signal_data_new (target, action, parent_name, object_name, + deep_property_name); if (checking_property) { signal_name = g_strdup_printf ("notify::%s", property_name); @@ -3461,8 +3543,7 @@ _execute_wait_for_signal (GstValidateScenario * scenario, non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING : GST_VALIDATE_EXECUTE_ACTION_ASYNC; if (checking_property) { - GST_ERROR ("Checking property value"); - if (_check_property (scenario, action, target, property_name, + if (_check_property (scenario, action, data->target, property_name, property_value, FALSE) == GST_VALIDATE_EXECUTE_ACTION_OK) { data->check_done = TRUE; waiting_signal_data_disconnect (data, scenario); @@ -3558,6 +3639,8 @@ _execute_wait (GstValidateScenario * scenario, GstValidateAction * action) gst_structure_get_boolean (action->structure, "on-clock", &on_clock); if (gst_structure_has_field (action->structure, "signal-name")) { return _execute_wait_for_signal (scenario, action); + } else if (gst_structure_has_field (action->structure, "deep-property-path")) { + return _execute_wait_for_signal (scenario, action); } else if (gst_structure_has_field (action->structure, "property-name")) { return _execute_wait_for_signal (scenario, action); } else if (gst_structure_has_field (action->structure, "message-type")) { @@ -8146,6 +8229,19 @@ register_action_types (void) .types = "structure", NULL }, + { + .name = "deep-property-path", + .description = "The property to wait to be set on the object inside the pipeline that matches" + " the path defined as:\n\n" + "```\n" + "element-name.padname::property-name=new-value\n" + "```\n\n" + "> NOTE: `.padname` is not needed if setting a property on an element\n\n", + .mandatory = FALSE, + .types = "string", + NULL + }, + {NULL} }), "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",