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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7700>
This commit is contained in:
Thibault Saunier 2024-08-27 20:41:18 -04:00 committed by GStreamer Marge Bot
parent 2c88bbf07f
commit 302797a965

View file

@ -3257,14 +3257,22 @@ typedef struct
guint sigid; guint sigid;
gboolean check_done; gboolean check_done;
gboolean check_property; gboolean check_property;
gchar *parent_name;
gchar *object_name;
gchar *property_name;
GMutex lock; GMutex lock;
} WaitingSignalData; } WaitingSignalData;
static 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); 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->target = gst_object_ref (target);
data->action = gst_validate_action_ref (action); data->action = gst_validate_action_ref (action);
@ -3279,6 +3287,9 @@ waiting_signal_data_free (WaitingSignalData * data)
g_assert (scenario); g_assert (scenario);
g_free (data->object_name);
g_free (data->parent_name);
g_free (data->property_name);
gst_object_unref (data->target); gst_object_unref (data->target);
gst_validate_action_unref (data->action); gst_validate_action_unref (data->action);
g_free (data); g_free (data);
@ -3302,11 +3313,15 @@ waiting_signal_data_disconnect (WaitingSignalData * data,
} }
static void static void
stop_waiting_signal_cb (WaitingSignalData * data) stop_waiting_signal_cb (WaitingSignalData * data, GstObject * prop_object,
GParamSpec * prop, GstObject * object)
{ {
GstStructure *check = NULL; GstStructure *check = NULL;
GstValidateAction *action = gst_validate_action_ref (data->action); GstValidateAction *action = gst_validate_action_ref (data->action);
GstValidateScenario *scenario = NULL; 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); g_mutex_lock (&data->lock);
if (data->check_done) { if (data->check_done) {
@ -3315,10 +3330,40 @@ stop_waiting_signal_cb (WaitingSignalData * data)
goto cleanup; 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); scenario = gst_validate_action_get_scenario (data->action);
if (data->check_property && if (check_property &&
_check_property (scenario, action, data->target, _check_property (scenario, action, property_check_target,
gst_structure_get_string (action->structure, "property-name"), property_name,
gst_structure_get_value (action->structure, "property-value"), gst_structure_get_value (action->structure, "property-value"),
FALSE) != GST_VALIDATE_EXECUTE_ACTION_OK) { FALSE) != GST_VALIDATE_EXECUTE_ACTION_OK) {
@ -3326,6 +3371,7 @@ stop_waiting_signal_cb (WaitingSignalData * data)
goto cleanup; goto cleanup;
} }
data->check_done = TRUE;
waiting_signal_data_disconnect (data, scenario); waiting_signal_data_disconnect (data, scenario);
if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE, if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE,
@ -3349,6 +3395,7 @@ stop_waiting_signal_cb (WaitingSignalData * data)
cleanup: cleanup:
gst_validate_action_unref (action); gst_validate_action_unref (action);
gst_clear_object (&scenario); gst_clear_object (&scenario);
g_mutex_unlock (&data->lock);
} }
static GstValidateExecuteActionReturn static GstValidateExecuteActionReturn
@ -3418,21 +3465,56 @@ _execute_wait_for_signal (GstValidateScenario * scenario,
const GValue *property_value = const GValue *property_value =
gst_structure_get_value (action->structure, "property-value"); gst_structure_get_value (action->structure, "property-value");
DECLARE_AND_GET_PIPELINE (scenario, action); 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;
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);
REPORT_UNLESS (object_prop_name[1], done,
"Property specification %s is missing a `::propename` part",
deep_property_path);
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, REPORT_UNLESS (signal_name || property_name, done,
"No signal-name or property-name given for wait action"); "No signal-name or property-name given for wait action");
REPORT_UNLESS (!property_name || (property_name && property_value), done, REPORT_UNLESS (!property_name || (property_name && property_value), done,
"`property-name` specified without a `property-value`"); "`property-name` specified without a `property-value`");
targets = _find_elements_defined_in_action (scenario, action); targets = _find_elements_defined_in_action (scenario, action);
REPORT_UNLESS ((g_list_length (targets) == 1), done, REPORT_UNLESS ((g_list_length (targets) == 1), done,
"Could not find target element."); "Could not find target element.");
target = targets->data;
gst_validate_printf (action, "Waiting for '%s'\n", gst_validate_printf (action, "Waiting for '%s'\n",
signal_name ? signal_name : property_name); signal_name ? signal_name : property_name);
}
target = targets->data;
data = waiting_signal_data_new (target, action); data =
waiting_signal_data_new (target, action, parent_name, object_name,
deep_property_name);
if (checking_property) { if (checking_property) {
signal_name = g_strdup_printf ("notify::%s", property_name); 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 : non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING :
GST_VALIDATE_EXECUTE_ACTION_ASYNC; GST_VALIDATE_EXECUTE_ACTION_ASYNC;
if (checking_property) { if (checking_property) {
GST_ERROR ("Checking property value"); if (_check_property (scenario, action, data->target, property_name,
if (_check_property (scenario, action, target, property_name,
property_value, FALSE) == GST_VALIDATE_EXECUTE_ACTION_OK) { property_value, FALSE) == GST_VALIDATE_EXECUTE_ACTION_OK) {
data->check_done = TRUE; data->check_done = TRUE;
waiting_signal_data_disconnect (data, scenario); 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); gst_structure_get_boolean (action->structure, "on-clock", &on_clock);
if (gst_structure_has_field (action->structure, "signal-name")) { if (gst_structure_has_field (action->structure, "signal-name")) {
return _execute_wait_for_signal (scenario, action); 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")) { } else if (gst_structure_has_field (action->structure, "property-name")) {
return _execute_wait_for_signal (scenario, action); return _execute_wait_for_signal (scenario, action);
} else if (gst_structure_has_field (action->structure, "message-type")) { } else if (gst_structure_has_field (action->structure, "message-type")) {
@ -8146,6 +8229,19 @@ register_action_types (void)
.types = "structure", .types = "structure",
NULL 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} {NULL}
}), }),
"Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds", "Waits for signal 'signal-name', message 'message-type', or during 'duration' seconds",