Commit a8369ca9 authored by Julian Bouzas's avatar Julian Bouzas
Browse files

m-default-routes: restore defout routes as soon as device is added

The route direction is also stored in case the route device name is the same for
both sink and source routes.
parent bdfe4ce3
Pipeline #326018 passed with stages
in 1 minute and 8 seconds
......@@ -18,6 +18,7 @@
#define SAVE_INTERVAL_MS 1000
G_DEFINE_QUARK (wp-module-default-routes-routes, routes);
G_DEFINE_QUARK (wp-module-default-routes-self, self);
/* Signals */
enum
......@@ -60,6 +61,36 @@ G_DEFINE_TYPE_WITH_PRIVATE (WpDefaultRoutes, wp_default_routes,
#define ALLOW_CHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_."
#define MAX_JSON_STRING_LEN 256
struct route_data {
gchar *name;
gint direction;
};
static struct route_data *
route_data_new (const gchar *name, gint direction)
{
struct route_data *res = NULL;
g_return_val_if_fail (name, NULL);
res = g_slice_new0 (struct route_data);
res->name = g_strdup (name);
res->direction = direction;
return res;
}
static void
route_data_free (gpointer p)
{
struct route_data *data = p;
g_return_if_fail (data);
g_return_if_fail (data->name);
g_free (data->name);
g_slice_free (struct route_data, data);
}
static gint
find_device_route (WpPipewireObject *device, const gchar *lookup_name,
gint lookup_device)
......@@ -401,8 +432,8 @@ static gchar *
serialize_routes (GHashTable *routes)
{
GHashTableIter iter;
const gchar *key;
gpointer value;
gpointer key;
struct route_data *value;
gchar *ptr;
FILE *f;
size_t size;
......@@ -418,11 +449,12 @@ serialize_routes (GHashTable *routes)
fprintf (f, "[ ");
g_hash_table_iter_init (&iter, routes);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, &value)) {
while (g_hash_table_iter_next (&iter, &key, (gpointer *)&value)) {
if (!first)
fprintf (f, ", ");
/* Each route is stored as a JSON object with "name" and "device" attributes */
fprintf (f, "{ \"name\": \"%s\", \"device\": %d }", key, GPOINTER_TO_INT (value));
fprintf (f, "{ \"name\": \"%s\", \"direction\": %d, \"device\": %d }",
value->name, value->direction, GPOINTER_TO_INT (key));
first = FALSE;
}
fprintf (f, " ]");
......@@ -443,30 +475,34 @@ parse_routes (const gchar *routes_str)
if (spa_json_enter_array (&array, &item) <= 0)
return NULL;
routes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
routes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
route_data_free);
while (spa_json_enter_object (&item, &object) > 0) {
const char *prop;
char name[MAX_JSON_STRING_LEN];
int dev_id, res;
int dev_id, direction, res;
direction = -1;
dev_id = -1;
name[0] = 0;
while ((res = spa_json_next (&object, &prop)) > 0) {
if (strncmp (prop, "\"name\"", res) == 0)
res = spa_json_get_string (&object, name, MAX_JSON_STRING_LEN);
else if (strncmp (prop, "\"direction\"", res) == 0)
res = spa_json_get_int (&object, &direction);
else if (strncmp (prop, "\"device\"", res) == 0)
res = spa_json_get_int (&object, &dev_id);
if (res <= 0) {
g_critical ("unable to parse route");
g_hash_table_destroy (routes);
return NULL;
}
}
if (dev_id >= 0 && strlen(name) > 0)
g_hash_table_insert (routes, g_strdup(name), GINT_TO_POINTER (dev_id));
if (direction >= 0 && dev_id >= 0 && strlen(name) > 0)
g_hash_table_insert (routes, GINT_TO_POINTER(dev_id),
route_data_new (name, direction));
}
return routes;
......@@ -476,9 +512,9 @@ static void
hash_table_copy_elements (gpointer key, gpointer value, gpointer data)
{
GHashTable *ht = data;
gchar *route = key;
struct route_data *route = value;
g_hash_table_insert (ht, g_strdup(route), value);
g_hash_table_insert (ht, key, route_data_new (route->name, route->direction));
}
static gboolean
......@@ -501,9 +537,10 @@ timeout_save_routes_cb (WpDefaultRoutes *self)
while (g_hash_table_iter_next (&iter, (gpointer*) &device, (gpointer*) &ht)) {
const gchar *dev_name;
gchar *routes;
GHashTable *copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
NULL);
g_autoptr (GHashTable) copy = NULL;
if (!ht || g_hash_table_size (ht) == 0)
continue;
dev_name = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
if (!dev_name)
continue;
......@@ -514,8 +551,10 @@ timeout_save_routes_cb (WpDefaultRoutes *self)
g_free (routes);
}
copy = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
route_data_free);
g_hash_table_foreach (ht, hash_table_copy_elements, copy);
g_hash_table_insert (priv->default_routes, device, copy);
g_hash_table_insert (priv->default_routes, device, g_steal_pointer (&copy));
}
if (!wp_state_save (priv->state, "routes", priv->routes))
......@@ -574,16 +613,16 @@ update_routes (WpDefaultRoutes *self, WpPipewireObject *device,
if (curr_routes) {
if (g_hash_table_size (curr_routes) == g_hash_table_size (new_routes)) {
gboolean identical = TRUE;
const gchar *key;
gpointer value;
gpointer key = NULL;
struct route_data *value = NULL;
gint index;
g_hash_table_iter_init (&iter, new_routes);
while (g_hash_table_iter_next (&iter, (gpointer*) &key, &value)) {
while (g_hash_table_iter_next (&iter, &key, (gpointer*)&value)) {
/* Make sure the route is valid */
index = find_device_route (device, key, GPOINTER_TO_INT (value));
index = find_device_route (device, value->name, GPOINTER_TO_INT (key));
if (index < 0) {
wp_info_object (self, "route '%s' (%d) is not valid", key, index);
wp_info_object (self, "route '%s' is not valid", value->name);
return;
}
if (!g_hash_table_contains (curr_routes, key)) {
......@@ -622,7 +661,8 @@ on_device_routes_notified (WpPipewireObject *device, GAsyncResult *res,
return;
}
new_routes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
new_routes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
route_data_free);
if (!new_routes) {
wp_warning_object (self, "failed to allocate new hash table");
return;
......@@ -644,20 +684,11 @@ on_device_routes_notified (WpPipewireObject *device, GAsyncResult *res,
continue;
}
if (ht) {
if (g_hash_table_contains (ht, name))
save_routes_properties (self, device, name, direction, props);
else if (g_hash_table_size (ht) > 0)
/*
* Apply route properties only once we have filled the `current_routes`
* table for this device. This prevents race conditions when a device
* appears.
*/
apply_routes_properties (self, device, name, direction, device_id);
}
if (ht && g_hash_table_contains (ht, GINT_TO_POINTER(device_id)))
save_routes_properties (self, device, name, direction, props);
g_hash_table_insert (new_routes, g_strdup (name),
GINT_TO_POINTER (device_id));
g_hash_table_insert (new_routes, GINT_TO_POINTER(device_id),
route_data_new (name, direction));
}
/* Update the routes list */
......@@ -673,15 +704,75 @@ on_device_param_info_notified (WpPipewireObject * device, GParamSpec * param,
(GAsyncReadyCallback) on_device_routes_notified, self);
}
static void
on_sync_done (WpCore * core, GAsyncResult * res, WpPipewireObject *device)
{
WpDefaultRoutes *self = g_object_get_qdata (G_OBJECT (device), self_quark ());
WpDefaultRoutesPrivate *priv = wp_default_routes_get_instance_private (self);
GHashTable *routes_table = NULL;
GHashTableIter iter;
gpointer key = NULL;
struct route_data *value = NULL;
g_autoptr (GError) error = NULL;
if (!wp_core_sync_finish (core, res, &error)) {
wp_warning_object (core, "core sync error: %s", error->message);
return;
}
routes_table = g_hash_table_lookup (priv->default_routes, device);
if (!routes_table)
return;
/* apply route properties */
g_hash_table_iter_init (&iter, routes_table);
while (g_hash_table_iter_next (&iter, &key, (gpointer *) &value))
apply_routes_properties (self, device, value->name, value->direction,
GPOINTER_TO_INT (key));
wp_info_object (self, "restored default routes on device: %s",
wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME));
}
static void
load_and_apply_default_routes (WpDefaultRoutes *self, WpPipewireObject *device)
{
WpDefaultRoutesPrivate *priv = wp_default_routes_get_instance_private (self);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
const gchar *dev_name = NULL;
const gchar *routes_str = NULL;
dev_name = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
g_return_if_fail (dev_name);
/* get the routes JSON string */
routes_str = wp_properties_get (priv->routes, dev_name);
if (!routes_str)
return;
/* parse routes */
GHashTable *routes_table = parse_routes (routes_str);
if (!routes_table) {
wp_warning_object (self, "Unable to parse routes");
return;
}
/* add the device routes in the default routes table */
g_hash_table_insert (priv->default_routes, device, routes_table);
/* Keep a reference of self in the device object */
g_object_set_qdata_full (G_OBJECT (device), self_quark (), self, NULL);
/* sync to make sure device is ready, and then apply the routes */
g_return_if_fail (core);
wp_core_sync (core, NULL, (GAsyncReadyCallback) on_sync_done, device);
}
static void
on_device_added (WpObjectManager *om, WpPipewireObject *device, gpointer d)
{
WpDefaultRoutes *self = WP_DEFAULT_ROUTES (d);
WpDefaultRoutesPrivate *priv = wp_default_routes_get_instance_private (self);
g_autoptr (WpIterator) it_routes = NULL;
const gchar *dev_name = NULL;
const gchar *routes = NULL;
GHashTable *ht;
wp_debug_object (self, "device " WP_OBJECT_FORMAT " added",
WP_OBJECT_ARGS (device));
......@@ -699,18 +790,8 @@ on_device_added (WpObjectManager *om, WpPipewireObject *device, gpointer d)
g_signal_connect_object (device, "notify::param-info",
G_CALLBACK (on_device_param_info_notified), self, 0);
/* Load default routes for device */
dev_name = wp_pipewire_object_get_property (device, PW_KEY_DEVICE_NAME);
g_return_if_fail (dev_name);
routes = wp_properties_get (priv->routes, dev_name);
if (!routes)
return;
ht = parse_routes (routes);
g_return_if_fail (ht);
g_hash_table_insert (priv->default_routes, device, ht);
/* Load and apply default routes */
load_and_apply_default_routes (self, device);
}
static void
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment