validate: scenario: Add a way to wait for a property to reach a specified value

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5666>
This commit is contained in:
Thibault Saunier 2023-07-20 09:30:30 -04:00 committed by GStreamer Marge Bot
parent 158f469de6
commit ca584da58d

View file

@ -1380,7 +1380,8 @@ _set_timed_value_property (GstValidateScenario * scenario,
static GstValidateExecuteActionReturn
_check_property (GstValidateScenario * scenario, GstValidateAction * action,
gpointer object, const gchar * propname, const GValue * expected_value)
gpointer object, const gchar * propname, const GValue * expected_value,
gboolean report_error)
{
GValue cvalue = G_VALUE_INIT;
@ -1388,6 +1389,9 @@ _check_property (GstValidateScenario * scenario, GstValidateAction * action,
g_object_get_property (object, propname, &cvalue);
if (gst_value_compare (&cvalue, expected_value) != GST_VALUE_EQUAL) {
if (!report_error)
return GST_VALIDATE_EXECUTE_ACTION_ERROR;
gchar *expected = gst_value_serialize (expected_value), *observed =
gst_value_serialize (&cvalue);
@ -1450,7 +1454,7 @@ _set_or_check_properties (GQuark field_id, const GValue * value,
gst_validate_object_set_property_full (GST_VALIDATE_REPORTER (scenario),
G_OBJECT (obj), paramspec->name, value, flags);
else
res = _check_property (scenario, action, obj, paramspec->name, value);
res = _check_property (scenario, action, obj, paramspec->name, value, TRUE);
done:
gst_clear_object (&obj);
@ -2831,30 +2835,84 @@ stop_waiting (GstValidateAction * action)
return G_SOURCE_REMOVE;
}
static void
stop_waiting_signal (GstStructure * data)
typedef struct
{
guint sigid = 0;
GstElement *target;
GstStructure *check = NULL;
GstValidateAction *action;
GstValidateScenario *scenario;
guint sigid;
gboolean check_done;
gboolean check_property;
GMutex lock;
} WaitingSignalData;
gst_structure_get (data, "target", G_TYPE_POINTER, &target,
"action", GST_TYPE_VALIDATE_ACTION, &action, "sigid", G_TYPE_UINT, &sigid,
NULL);
gst_structure_free (data);
static WaitingSignalData *
waiting_signal_data_new (GstElement * target, GstValidateAction * action)
{
WaitingSignalData *data = g_new0 (WaitingSignalData, 1);
scenario = gst_validate_action_get_scenario (action);
data->target = gst_object_ref (target);
data->action = gst_validate_action_ref (action);
return data;
}
static void
waiting_signal_data_free (WaitingSignalData * data)
{
GstValidateScenario *scenario =
gst_validate_action_get_scenario (data->action);
g_assert (scenario);
gst_object_unref (data->target);
gst_validate_action_unref (data->action);
g_free (data);
gst_object_unref (scenario);
}
static void
waiting_signal_data_disconnect (WaitingSignalData * data,
GstValidateScenario * scenario)
{
g_assert (scenario);
SCENARIO_LOCK (scenario);
g_signal_handler_disconnect (target,
sigid ? sigid : scenario->priv->signal_handler_id);
if (!sigid)
g_signal_handler_disconnect (data->target,
data->sigid ? data->sigid : scenario->priv->signal_handler_id);
if (!data->sigid)
scenario->priv->signal_handler_id = 0;
data->sigid = 0;
SCENARIO_UNLOCK (scenario);
}
static void
stop_waiting_signal_cb (WaitingSignalData * data)
{
GstStructure *check = NULL;
GstValidateAction *action = gst_validate_action_ref (data->action);
GstValidateScenario *scenario = NULL;
g_mutex_lock (&data->lock);
if (data->check_done) {
GST_INFO_OBJECT (data->action, "Check already done, ignoring signal");
g_mutex_unlock (&data->lock);
goto cleanup;
}
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"),
gst_structure_get_value (action->structure, "property-value"),
FALSE) != GST_VALIDATE_EXECUTE_ACTION_OK) {
GST_INFO_OBJECT (scenario, "Property check failed, keep waiting");
goto cleanup;
}
waiting_signal_data_disconnect (data, scenario);
if (gst_structure_get (action->structure, "check", GST_TYPE_STRUCTURE,
&check, NULL)) {
GstValidateAction *subact =
@ -2871,10 +2929,11 @@ stop_waiting_signal (GstStructure * data)
}
gst_validate_action_set_done (action);
gst_validate_action_unref (action);
_add_execute_actions_gsource (scenario);
gst_object_unref (scenario);
gst_object_unref (target);
cleanup:
gst_validate_action_unref (action);
gst_clear_object (&scenario);
}
static GstValidateExecuteActionReturn
@ -2933,54 +2992,77 @@ _execute_wait_for_signal (GstValidateScenario * scenario,
{
gboolean non_blocking;
GstValidateScenarioPrivate *priv = scenario->priv;
const gchar *signal_name = gst_structure_get_string
(action->structure, "signal-name");
gchar *signal_name = g_strdup (gst_structure_get_string
(action->structure, "signal-name"));
const gchar *property_name = gst_structure_get_string
(action->structure, "property-name");
GList *targets = NULL;
GstElement *target;
GstStructure *data;
WaitingSignalData *data;
GstValidateExecuteActionReturn res = GST_VALIDATE_EXECUTE_ACTION_OK;
const GValue *property_value =
gst_structure_get_value (action->structure, "property-value");
DECLARE_AND_GET_PIPELINE (scenario, action);
gboolean checking_property = signal_name == NULL;
REPORT_UNLESS (signal_name, err, "No signal-name given for wait action");
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), err,
REPORT_UNLESS ((g_list_length (targets) == 1), done,
"Could not find target element.");
gst_validate_printf (action, "Waiting for '%s' signal\n", signal_name);
gst_validate_printf (action, "Waiting for '%s'\n",
signal_name ? signal_name : property_name);
target = targets->data;
data = waiting_signal_data_new (target, action);
if (checking_property) {
signal_name = g_strdup_printf ("notify::%s", property_name);
g_mutex_lock (&data->lock);
}
SCENARIO_LOCK (scenario);
if (priv->execute_actions_source_id) {
g_source_remove (priv->execute_actions_source_id);
priv->execute_actions_source_id = 0;
}
target = targets->data;
data =
gst_structure_new ("a", "action", GST_TYPE_VALIDATE_ACTION, action,
"target", G_TYPE_POINTER, target, NULL);
SCENARIO_LOCK (scenario);
priv->signal_handler_id = g_signal_connect_swapped (target, signal_name,
(GCallback) stop_waiting_signal, data);
priv->signal_handler_id = g_signal_connect_data (target, signal_name,
(GCallback) stop_waiting_signal_cb, data,
(GClosureNotify) waiting_signal_data_free, G_CONNECT_SWAPPED);
non_blocking =
gst_structure_get_boolean (action->structure, "non-blocking",
&non_blocking);
if (non_blocking) {
gst_structure_set (data, "sigid", G_TYPE_UINT, priv->signal_handler_id,
NULL);
data->sigid = priv->signal_handler_id;
priv->signal_handler_id = 0;
}
SCENARIO_UNLOCK (scenario);
gst_object_unref (pipeline);
g_list_free (targets);
return non_blocking ? GST_VALIDATE_EXECUTE_ACTION_NON_BLOCKING :
res =
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,
property_value, FALSE) == GST_VALIDATE_EXECUTE_ACTION_OK) {
data->check_done = TRUE;
waiting_signal_data_disconnect (data, scenario);
err:
GST_ERROR ("Property check done, returning OK");
res = GST_VALIDATE_EXECUTE_ACTION_OK;
}
g_mutex_unlock (&data->lock);
}
done:
g_free (signal_name);
g_list_free_full (targets, gst_object_unref);
gst_object_unref (pipeline);
return res;
}
@ -3015,6 +3097,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, "property-name")) {
return _execute_wait_for_signal (scenario, action);
} else if (gst_structure_has_field (action->structure, "message-type")) {
return _execute_wait_for_message (scenario, action);
} else if (on_clock) {
@ -3437,7 +3521,8 @@ _execute_set_or_check_property (GstValidateScenario * scenario,
ret = tmpres;
} else {
ret =
_check_property (scenario, action, l->data, property, property_value);
_check_property (scenario, action, l->data, property, property_value,
TRUE);
}
}
@ -6334,8 +6419,8 @@ _action_set_done (GstValidateAction * action)
_check_scenario_is_done (scenario);
if (!gst_validate_parse_next_action_playback_time (scenario)) {
gst_validate_error_structure (scenario->priv->actions ? scenario->
priv->actions->data : NULL,
gst_validate_error_structure (scenario->priv->actions ? scenario->priv->
actions->data : NULL,
"Could not determine next action playback time!");
}
@ -7029,6 +7114,22 @@ register_action_types (void)
.types = "string",
NULL
},
{
.name = "property-name",
.description = "The name of the property to wait for value to be set to what is specified by @property-value.",
.mandatory = FALSE,
.types = "string",
NULL
},
{
.name = "property-value",
.description = "The value of the property to be waiting."
"\n Example: "
"\n `wait, property-name=current-uri, property-value=file:///some/value.mp4, target-element-name=uridecodebin`",
.mandatory = FALSE,
.types = "string",
NULL
},
{
.name = "non-blocking",
.description = "**Only for signals**."