media-session: use restore stream for routeless nodes

When a node does not have routes, treat it like a stream and use
the restore-stream logic to restore the volumes.

Rework some of the logic a little. Don't save empty strings. Wait
for param updates to save/restore values.

This makes volume restore work on virtual sinks/sources and
sinks/sources without any routes.

See #729
This commit is contained in:
Wim Taymans 2021-03-22 17:02:42 +01:00
parent fc44013d17
commit 7ee52b396d

View file

@ -208,6 +208,11 @@ static char *serialize_props(struct stream *str, const struct spa_pod *param)
fprintf(f, " }");
fclose(f);
if (strlen(ptr) < 5) {
free(ptr);
ptr = NULL;
}
return ptr;
}
@ -257,31 +262,42 @@ static int handle_props(struct stream *str, struct sm_param *p)
const char *key;
int changed = 0;
key = str->key;
if (key == NULL)
if ((key = str->key) == NULL)
return -EBUSY;
if (p->param) {
char *val = serialize_props(str, p->param);
pw_log_debug("stream %d: current props %s %s", str->id, key, val);
if (val) {
pw_log_info("stream %d: save props %s %s", str->id, key, val);
changed += pw_properties_set(impl->props, key, val);
free(val);
add_idle_timeout(impl);
}
}
if (changed)
sync_metadata(impl);
return 0;
}
static int restore_stream(struct stream *str, const char *val)
static int restore_stream(struct stream *str)
{
struct impl *impl = str->impl;
struct spa_json it[3];
const char *value;
const char *val, *value;
char buf[1024], key[128];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
struct spa_pod_frame f[2];
struct spa_pod *param;
if (str->key == NULL)
return -EBUSY;
val = pw_properties_get(impl->props, str->key);
if (val == NULL)
return -ENOENT;
pw_log_info("stream %d: restore '%s' to %s", str->id, str->key, val);
spa_json_init(&it[0], val, strlen(val));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
@ -369,63 +385,8 @@ static int restore_stream(struct stream *str, const char *val)
return 0;
}
static void update_key(struct stream *str)
static int save_stream(struct stream *str)
{
struct impl *impl = str->impl;
uint32_t i;
const char *p, *val;
char *key;
struct sm_object *obj = &str->obj->obj;
bool changed;
const char *keys[] = {
PW_KEY_MEDIA_ROLE,
PW_KEY_APP_ID,
PW_KEY_APP_NAME,
PW_KEY_MEDIA_NAME,
};
key = NULL;
for (i = 0; i < SPA_N_ELEMENTS(keys); i++) {
if ((p = pw_properties_get(obj->props, keys[i]))) {
key = spa_aprintf(PREFIX"%s.%s:%s", str->media_class, keys[i], p);
break;
}
}
if (key == NULL)
return;
pw_log_debug(NAME " %p: stream %p key '%s'", impl, str, key);
changed = str->key == NULL || strcmp(str->key, key) != 0;
free(str->key);
str->key = key;
if (!changed || str->restored)
return;
val = pw_properties_get(impl->props, key);
if (val != NULL) {
pw_log_info("stream %d: restore '%s' to %s", str->id, key, val);
restore_stream(str, val);
str->restored = true;
}
}
static void object_update(void *data)
{
struct stream *str = data;
struct impl *impl = str->impl;
bool rescan = false;
pw_log_debug(NAME" %p: stream %p %08x/%08x", impl, str,
str->obj->obj.changed, str->obj->obj.avail);
if (str->obj->obj.changed & SM_OBJECT_CHANGE_MASK_PROPERTIES) {
if (str->key == NULL)
update_key(str);
}
if (str->obj->obj.changed & SM_NODE_CHANGE_MASK_PARAMS)
rescan = true;
if (rescan) {
struct sm_param *p;
spa_list_for_each(p, &str->obj->param_list, link) {
if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG))
@ -439,7 +400,56 @@ static void object_update(void *data)
break;
}
}
return 0;
}
static void update_stream(struct stream *str)
{
struct impl *impl = str->impl;
uint32_t i;
const char *p;
char *key;
struct sm_object *obj = &str->obj->obj;
const char *keys[] = {
PW_KEY_MEDIA_ROLE,
PW_KEY_APP_ID,
PW_KEY_APP_NAME,
PW_KEY_MEDIA_NAME,
PW_KEY_NODE_NAME,
};
key = NULL;
for (i = 0; i < SPA_N_ELEMENTS(keys); i++) {
if ((p = pw_properties_get(obj->props, keys[i]))) {
key = spa_aprintf(PREFIX"%s.%s:%s", str->media_class, keys[i], p);
break;
}
}
if (key == NULL)
return;
pw_log_debug(NAME " %p: stream %p key '%s'", impl, str, key);
free(str->key);
str->key = key;
if (!str->restored) {
restore_stream(str);
str->restored = true;
} else {
save_stream(str);
}
}
static void object_update(void *data)
{
struct stream *str = data;
struct impl *impl = str->impl;
pw_log_info(NAME" %p: stream %p %08x/%08x", impl, str,
str->obj->obj.changed, str->obj->obj.avail);
if (str->obj->obj.changed & SM_NODE_CHANGE_MASK_PARAMS)
update_stream(str);
}
static const struct sm_object_events object_events = {
@ -451,24 +461,29 @@ static void session_create(void *data, struct sm_object *object)
{
struct impl *impl = data;
struct stream *str;
const char *media_class;
const char *media_class, *routes;
if (strcmp(object->type, PW_TYPE_INTERFACE_Node) != 0 ||
object->props == NULL ||
(media_class = pw_properties_get(object->props, PW_KEY_MEDIA_CLASS)) == NULL ||
strstr(media_class, "Stream/") != media_class)
(media_class = pw_properties_get(object->props, PW_KEY_MEDIA_CLASS)) == NULL)
return;
if (strstr(media_class, "Stream/") == media_class) {
media_class += strlen("Stream/");
pw_log_debug(NAME " %p: add stream '%d' %s", impl, object->id, media_class);
} else if (strstr(media_class, "Audio/") == media_class &&
((routes = pw_properties_get(object->props, "device.routes")) == NULL ||
atoi(routes) == 0)) {
pw_log_debug(NAME " %p: add node '%d' %s", impl, object->id, media_class);
} else {
return;
}
str = sm_object_add_data(object, SESSION_KEY, sizeof(struct stream));
str->obj = (struct sm_node*)object;
str->id = object->id;
str->impl = impl;
str->media_class = strdup(media_class);
update_key(str);
str->obj->obj.mask |= SM_OBJECT_CHANGE_MASK_PROPERTIES | SM_NODE_CHANGE_MASK_PARAMS;
sm_object_add_listener(&str->obj->obj, &str->listener, &object_events, str);