mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
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:
parent
fc44013d17
commit
7ee52b396d
1 changed files with 63 additions and 48 deletions
|
|
@ -208,6 +208,11 @@ static char *serialize_props(struct stream *str, const struct spa_pod *param)
|
||||||
|
|
||||||
fprintf(f, " }");
|
fprintf(f, " }");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
if (strlen(ptr) < 5) {
|
||||||
|
free(ptr);
|
||||||
|
ptr = NULL;
|
||||||
|
}
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,31 +262,42 @@ static int handle_props(struct stream *str, struct sm_param *p)
|
||||||
const char *key;
|
const char *key;
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
|
|
||||||
key = str->key;
|
if ((key = str->key) == NULL)
|
||||||
if (key == NULL)
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
if (p->param) {
|
if (p->param) {
|
||||||
char *val = serialize_props(str, 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);
|
changed += pw_properties_set(impl->props, key, val);
|
||||||
free(val);
|
free(val);
|
||||||
add_idle_timeout(impl);
|
add_idle_timeout(impl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
sync_metadata(impl);
|
sync_metadata(impl);
|
||||||
return 0;
|
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];
|
struct spa_json it[3];
|
||||||
const char *value;
|
const char *val, *value;
|
||||||
char buf[1024], key[128];
|
char buf[1024], key[128];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||||
struct spa_pod_frame f[2];
|
struct spa_pod_frame f[2];
|
||||||
struct spa_pod *param;
|
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));
|
spa_json_init(&it[0], val, strlen(val));
|
||||||
|
|
||||||
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
|
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;
|
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;
|
struct sm_param *p;
|
||||||
spa_list_for_each(p, &str->obj->param_list, link) {
|
spa_list_for_each(p, &str->obj->param_list, link) {
|
||||||
if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG))
|
if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG))
|
||||||
|
|
@ -439,7 +400,56 @@ static void object_update(void *data)
|
||||||
break;
|
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 = {
|
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 impl *impl = data;
|
||||||
struct stream *str;
|
struct stream *str;
|
||||||
const char *media_class;
|
const char *media_class, *routes;
|
||||||
|
|
||||||
if (strcmp(object->type, PW_TYPE_INTERFACE_Node) != 0 ||
|
if (strcmp(object->type, PW_TYPE_INTERFACE_Node) != 0 ||
|
||||||
object->props == NULL ||
|
object->props == NULL ||
|
||||||
(media_class = pw_properties_get(object->props, PW_KEY_MEDIA_CLASS)) == NULL ||
|
(media_class = pw_properties_get(object->props, PW_KEY_MEDIA_CLASS)) == NULL)
|
||||||
strstr(media_class, "Stream/") != media_class)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (strstr(media_class, "Stream/") == media_class) {
|
||||||
media_class += strlen("Stream/");
|
media_class += strlen("Stream/");
|
||||||
|
|
||||||
pw_log_debug(NAME " %p: add stream '%d' %s", impl, object->id, media_class);
|
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 = sm_object_add_data(object, SESSION_KEY, sizeof(struct stream));
|
||||||
str->obj = (struct sm_node*)object;
|
str->obj = (struct sm_node*)object;
|
||||||
str->id = object->id;
|
str->id = object->id;
|
||||||
str->impl = impl;
|
str->impl = impl;
|
||||||
str->media_class = strdup(media_class);
|
str->media_class = strdup(media_class);
|
||||||
update_key(str);
|
|
||||||
|
|
||||||
str->obj->obj.mask |= SM_OBJECT_CHANGE_MASK_PROPERTIES | SM_NODE_CHANGE_MASK_PARAMS;
|
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);
|
sm_object_add_listener(&str->obj->obj, &str->listener, &object_events, str);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue