mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
media-session: use separate keys for configured default nodes
Use separate metadata keys for the current effective default nodes (default.*), and user-configured ones (default.configured.*). default-nodes saves and restores the configured ones, and policy-node keeps the effective ones up to date. For pipewire users, the effective default values should be considered read-only, as changing them will not have an effect. To avoid confusion, policy-nodes will reset their values back immediately if they are changed.
This commit is contained in:
parent
6c02fd663a
commit
db6baf6188
2 changed files with 158 additions and 70 deletions
|
|
@ -48,9 +48,14 @@
|
|||
|
||||
#define SAVE_INTERVAL 1
|
||||
|
||||
#define DEFAULT_AUDIO_SINK "default.audio.sink"
|
||||
#define DEFAULT_AUDIO_SOURCE "default.audio.source"
|
||||
#define DEFAULT_VIDEO_SOURCE "default.video.source"
|
||||
#define DEFAULT_CONFIG_AUDIO_SINK_KEY "default.configured.audio.sink"
|
||||
#define DEFAULT_CONFIG_AUDIO_SOURCE_KEY "default.configured.audio.source"
|
||||
#define DEFAULT_CONFIG_VIDEO_SOURCE_KEY "default.configured.video.source"
|
||||
|
||||
struct default_node {
|
||||
char *key;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct timespec now;
|
||||
|
|
@ -63,9 +68,7 @@ struct impl {
|
|||
|
||||
struct spa_hook meta_listener;
|
||||
|
||||
uint32_t default_audio_source;
|
||||
uint32_t default_audio_sink;
|
||||
uint32_t default_video_source;
|
||||
struct default_node defaults[4];
|
||||
|
||||
struct pw_properties *properties;
|
||||
};
|
||||
|
|
@ -161,18 +164,13 @@ static int metadata_property(void *object, uint32_t subject,
|
|||
bool changed = false;
|
||||
|
||||
if (subject == PW_ID_CORE) {
|
||||
struct default_node *def;
|
||||
val = (key && value) ? (uint32_t)atoi(value) : SPA_ID_INVALID;
|
||||
if (key == NULL || strcmp(key, DEFAULT_AUDIO_SINK) == 0) {
|
||||
changed = val != impl->default_audio_sink;
|
||||
impl->default_audio_sink = val;
|
||||
}
|
||||
if (key == NULL || strcmp(key, DEFAULT_AUDIO_SOURCE) == 0) {
|
||||
changed = val != impl->default_audio_source;
|
||||
impl->default_audio_source = val;
|
||||
}
|
||||
if (key == NULL || strcmp(key, DEFAULT_VIDEO_SOURCE) == 0) {
|
||||
changed = val != impl->default_video_source;
|
||||
impl->default_video_source = val;
|
||||
for (def = impl->defaults; def->key != NULL; ++def) {
|
||||
if (key == NULL || strcmp(key, def->key) == 0) {
|
||||
changed = (def->value != val);
|
||||
def->value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
|
|
@ -220,6 +218,15 @@ static void session_create(void *data, struct sm_object *object)
|
|||
}
|
||||
d = (struct find_data){ impl, name, SPA_ID_INVALID };
|
||||
if (find_name(&d, object)) {
|
||||
const struct default_node *def;
|
||||
|
||||
/* Check that the item key is a valid default key */
|
||||
for (def = impl->defaults; def->key != NULL; ++def)
|
||||
if (item->key != NULL && strcmp(item->key, def->key) == 0)
|
||||
break;
|
||||
if (def->key == NULL)
|
||||
continue;
|
||||
|
||||
char val[16];
|
||||
snprintf(val, sizeof(val), "%u", d.id);
|
||||
pw_log_info("found %s with id:%s restore as %s",
|
||||
|
|
@ -233,24 +240,16 @@ static void session_create(void *data, struct sm_object *object)
|
|||
static void session_remove(void *data, struct sm_object *object)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
struct default_node *def;
|
||||
|
||||
if (strcmp(object->type, PW_TYPE_INTERFACE_Node) != 0)
|
||||
return;
|
||||
|
||||
if (impl->default_audio_sink == object->id) {
|
||||
impl->default_audio_sink = SPA_ID_INVALID;
|
||||
pw_metadata_set_property(impl->session->metadata,
|
||||
PW_ID_CORE, DEFAULT_AUDIO_SINK, SPA_TYPE_INFO_BASE"Id", NULL);
|
||||
}
|
||||
if (impl->default_audio_source == object->id) {
|
||||
impl->default_audio_source = SPA_ID_INVALID;
|
||||
pw_metadata_set_property(impl->session->metadata,
|
||||
PW_ID_CORE, DEFAULT_AUDIO_SOURCE, SPA_TYPE_INFO_BASE"Id", NULL);
|
||||
}
|
||||
if (impl->default_video_source == object->id) {
|
||||
impl->default_video_source = SPA_ID_INVALID;
|
||||
pw_metadata_set_property(impl->session->metadata,
|
||||
PW_ID_CORE, DEFAULT_VIDEO_SOURCE, SPA_TYPE_INFO_BASE"Id", NULL);
|
||||
for (def = impl->defaults; def->key != NULL; ++def) {
|
||||
if (def->value == object->id) {
|
||||
def->value = SPA_ID_INVALID;
|
||||
pw_metadata_set_property(impl->session->metadata, PW_ID_CORE, def->key, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,9 +283,10 @@ int sm_default_nodes_start(struct sm_media_session *session)
|
|||
impl->session = session;
|
||||
impl->context = session->context;
|
||||
|
||||
impl->default_audio_sink = SPA_ID_INVALID;
|
||||
impl->default_audio_source = SPA_ID_INVALID;
|
||||
impl->default_video_source = SPA_ID_INVALID;
|
||||
impl->defaults[0] = (struct default_node){ DEFAULT_CONFIG_AUDIO_SINK_KEY, SPA_ID_INVALID };
|
||||
impl->defaults[1] = (struct default_node){ DEFAULT_CONFIG_AUDIO_SOURCE_KEY, SPA_ID_INVALID };
|
||||
impl->defaults[2] = (struct default_node){ DEFAULT_CONFIG_VIDEO_SOURCE_KEY, SPA_ID_INVALID };
|
||||
impl->defaults[3] = (struct default_node){ NULL, SPA_ID_INVALID };
|
||||
|
||||
impl->properties = pw_properties_new(NULL, NULL);
|
||||
if (impl->properties == NULL) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,24 @@
|
|||
|
||||
#define DEFAULT_IDLE_SECONDS 3
|
||||
|
||||
#define DEFAULT_AUDIO_SINK_KEY "default.audio.sink"
|
||||
#define DEFAULT_AUDIO_SOURCE_KEY "default.audio.source"
|
||||
#define DEFAULT_VIDEO_SOURCE_KEY "default.video.source"
|
||||
#define DEFAULT_CONFIG_AUDIO_SINK_KEY "default.configured.audio.sink"
|
||||
#define DEFAULT_CONFIG_AUDIO_SOURCE_KEY "default.configured.audio.source"
|
||||
#define DEFAULT_CONFIG_VIDEO_SOURCE_KEY "default.configured.video.source"
|
||||
|
||||
#define DEFAULT_AUDIO_SINK 0
|
||||
#define DEFAULT_AUDIO_SOURCE 1
|
||||
#define DEFAULT_VIDEO_SOURCE 2
|
||||
|
||||
struct default_node {
|
||||
char *key;
|
||||
char *key_config;
|
||||
uint32_t value;
|
||||
uint32_t config;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct timespec now;
|
||||
|
||||
|
|
@ -61,9 +79,7 @@ struct impl {
|
|||
struct spa_list node_list;
|
||||
int seq;
|
||||
|
||||
uint32_t default_audio_sink;
|
||||
uint32_t default_audio_source;
|
||||
uint32_t default_video_source;
|
||||
struct default_node defaults[4];
|
||||
|
||||
bool streams_follow_default;
|
||||
};
|
||||
|
|
@ -401,6 +417,7 @@ static void session_create(void *data, struct sm_object *object)
|
|||
|
||||
static void session_remove(void *data, struct sm_object *object)
|
||||
{
|
||||
struct default_node *def;
|
||||
struct impl *impl = data;
|
||||
pw_log_debug(NAME " %p: remove global '%d'", impl, object->id);
|
||||
|
||||
|
|
@ -414,12 +431,10 @@ static void session_remove(void *data, struct sm_object *object)
|
|||
if (n->peer == node)
|
||||
n->peer = NULL;
|
||||
}
|
||||
if (impl->default_audio_sink == object->id)
|
||||
impl->default_audio_sink = SPA_ID_INVALID;
|
||||
if (impl->default_audio_source == object->id)
|
||||
impl->default_audio_source = SPA_ID_INVALID;
|
||||
if (impl->default_video_source == object->id)
|
||||
impl->default_video_source = SPA_ID_INVALID;
|
||||
|
||||
for (def = impl->defaults; def->key != NULL; ++def)
|
||||
if (def->config == object->id)
|
||||
def->config = SPA_ID_INVALID;
|
||||
}
|
||||
|
||||
sm_media_session_schedule_rescan(impl->session);
|
||||
|
|
@ -427,8 +442,12 @@ static void session_remove(void *data, struct sm_object *object)
|
|||
|
||||
struct find_data {
|
||||
struct impl *impl;
|
||||
struct node *target;
|
||||
struct node *node;
|
||||
|
||||
const char *media;
|
||||
bool capture_sink;
|
||||
enum pw_direction direction;
|
||||
|
||||
bool exclusive;
|
||||
int priority;
|
||||
uint64_t plugged;
|
||||
|
|
@ -442,6 +461,11 @@ static int find_node(void *data, struct node *node)
|
|||
uint64_t plugged = 0;
|
||||
struct sm_device *device = node->obj->device;
|
||||
|
||||
if (node->obj->info == NULL) {
|
||||
pw_log_debug(NAME " %p: skipping node '%d' with no node info", impl, node->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pw_log_debug(NAME " %p: looking at node '%d' enabled:%d state:%d peer:%p exclusive:%d",
|
||||
impl, node->id, node->enabled, node->obj->info->state, node->peer, node->exclusive);
|
||||
|
||||
|
|
@ -453,13 +477,13 @@ static int find_node(void *data, struct node *node)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if ((find->target->capture_sink && node->direction != PW_DIRECTION_INPUT) ||
|
||||
(!find->target->capture_sink && node->direction == find->target->direction)) {
|
||||
if ((find->capture_sink && node->direction != PW_DIRECTION_INPUT) ||
|
||||
(!find->capture_sink && node->direction == find->direction)) {
|
||||
pw_log_debug(".. same direction");
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(node->media, find->target->media) != 0) {
|
||||
pw_log_debug(".. incompatible media %s <-> %s", node->media, find->target->media);
|
||||
if (strcmp(node->media, find->media) != 0) {
|
||||
pw_log_debug(".. incompatible media %s <-> %s", node->media, find->media);
|
||||
return 0;
|
||||
}
|
||||
plugged = node->plugged;
|
||||
|
|
@ -469,12 +493,12 @@ static int find_node(void *data, struct node *node)
|
|||
bool is_default = false;
|
||||
if (strcmp(node->media, "Audio") == 0) {
|
||||
if (node->direction == PW_DIRECTION_INPUT)
|
||||
is_default = impl->default_audio_sink == node->id;
|
||||
is_default = impl->defaults[DEFAULT_AUDIO_SINK].config == node->id;
|
||||
else if (node->direction == PW_DIRECTION_OUTPUT)
|
||||
is_default = impl->default_audio_source == node->id;
|
||||
is_default = impl->defaults[DEFAULT_AUDIO_SOURCE].config == node->id;
|
||||
} else if (strcmp(node->media, "Video") == 0) {
|
||||
if (node->direction == PW_DIRECTION_OUTPUT)
|
||||
is_default = impl->default_video_source == node->id;
|
||||
is_default = impl->defaults[DEFAULT_VIDEO_SOURCE].config == node->id;
|
||||
}
|
||||
if (is_default)
|
||||
priority += 10000;
|
||||
|
|
@ -500,6 +524,35 @@ static int find_node(void *data, struct node *node)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct node *find_auto_default_node(struct impl *impl, const struct default_node *def)
|
||||
{
|
||||
struct node *node;
|
||||
struct find_data find;
|
||||
|
||||
spa_zero(find);
|
||||
find.impl = impl;
|
||||
find.capture_sink = false;
|
||||
find.exclusive = false;
|
||||
|
||||
if (strcmp(def->key, DEFAULT_AUDIO_SINK_KEY) == 0) {
|
||||
find.media = "Audio";
|
||||
find.direction = PW_DIRECTION_OUTPUT;
|
||||
} else if (strcmp(def->key, DEFAULT_AUDIO_SOURCE_KEY) == 0) {
|
||||
find.media = "Audio";
|
||||
find.direction = PW_DIRECTION_INPUT;
|
||||
} else if (strcmp(def->key, DEFAULT_VIDEO_SOURCE_KEY) == 0) {
|
||||
find.media = "Video";
|
||||
find.direction = PW_DIRECTION_INPUT;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spa_list_for_each(node, &impl->node_list, link)
|
||||
find_node(&find, node);
|
||||
|
||||
return find.node;
|
||||
}
|
||||
|
||||
static int link_nodes(struct node *node, struct node *peer)
|
||||
{
|
||||
struct impl *impl = node->impl;
|
||||
|
|
@ -648,7 +701,9 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
|
||||
spa_zero(find);
|
||||
find.impl = impl;
|
||||
find.target = n;
|
||||
find.media = n->media;
|
||||
find.capture_sink = n->capture_sink;
|
||||
find.direction = n->direction;
|
||||
find.exclusive = exclusive;
|
||||
|
||||
/* we always honour the target node asked for by the client */
|
||||
|
|
@ -751,6 +806,27 @@ static void session_info(void *data, const struct pw_core_info *info)
|
|||
}
|
||||
}
|
||||
|
||||
static void refresh_auto_default_nodes(struct impl *impl)
|
||||
{
|
||||
struct default_node *def;
|
||||
|
||||
/* Auto set default nodes */
|
||||
for (def = impl->defaults; def->key != NULL; ++def) {
|
||||
struct node *node;
|
||||
node = find_auto_default_node(impl, def);
|
||||
if (node == NULL && def->value != SPA_ID_INVALID) {
|
||||
def->value = SPA_ID_INVALID;
|
||||
pw_metadata_set_property(impl->session->metadata, PW_ID_CORE, def->key, NULL, NULL);
|
||||
} else if (node != NULL && def->value != node->id) {
|
||||
char buf[64];
|
||||
def->value = node->id;
|
||||
snprintf(buf, sizeof(buf), "%d", node->id);
|
||||
pw_metadata_set_property(impl->session->metadata, PW_ID_CORE, def->key,
|
||||
SPA_TYPE_INFO_BASE"Id", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void session_rescan(void *data, int seq)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
|
|
@ -760,6 +836,8 @@ static void session_rescan(void *data, int seq)
|
|||
|
||||
spa_list_for_each(node, &impl->node_list, link)
|
||||
rescan_node(impl, node);
|
||||
|
||||
refresh_auto_default_nodes(impl);
|
||||
}
|
||||
|
||||
static void session_destroy(void *data)
|
||||
|
|
@ -827,25 +905,27 @@ static int metadata_property(void *object, uint32_t subject,
|
|||
uint32_t val = (key && value) ? (uint32_t)atoi(value) : SPA_ID_INVALID;
|
||||
|
||||
if (subject == PW_ID_CORE) {
|
||||
struct default_node *def;
|
||||
bool changed = false;
|
||||
|
||||
if (key == NULL || strcmp(key, "default.audio.sink") == 0) {
|
||||
if (impl->default_audio_sink != val)
|
||||
changed = true;
|
||||
impl->default_audio_sink = val;
|
||||
}
|
||||
if (key == NULL || strcmp(key, "default.audio.source") == 0) {
|
||||
if (impl->default_audio_source != val)
|
||||
changed = true;
|
||||
impl->default_audio_source = val;
|
||||
}
|
||||
if (key == NULL || strcmp(key, "default.video.source") == 0) {
|
||||
if (impl->default_video_source != val)
|
||||
changed = true;
|
||||
impl->default_video_source = val;
|
||||
for (def = impl->defaults; def->key != NULL; ++def) {
|
||||
if (key == NULL || strcmp(key, def->key_config) == 0) {
|
||||
if (def->config != val)
|
||||
changed = true;
|
||||
def->config = val;
|
||||
}
|
||||
if (key == NULL || strcmp(key, def->key) == 0) {
|
||||
bool eff_changed = (def->value != val);
|
||||
def->value = val;
|
||||
|
||||
/* The effective value was changed. In case it was changed by
|
||||
* someone else than us, reset the value to avoid confusion. */
|
||||
if (eff_changed)
|
||||
refresh_auto_default_nodes(impl);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed && impl->streams_follow_default)
|
||||
if (changed)
|
||||
sm_media_session_schedule_rescan(impl->session);
|
||||
} else {
|
||||
if (val != SPA_ID_INVALID && strcmp(key, "target.node") == 0) {
|
||||
|
|
@ -889,9 +969,17 @@ int sm_policy_node_start(struct sm_media_session *session)
|
|||
impl->context = session->context;
|
||||
|
||||
impl->sample_rate = 48000;
|
||||
impl->default_audio_sink = SPA_ID_INVALID;
|
||||
impl->default_audio_source = SPA_ID_INVALID;
|
||||
impl->default_video_source = SPA_ID_INVALID;
|
||||
|
||||
impl->defaults[DEFAULT_AUDIO_SINK] = (struct default_node){
|
||||
DEFAULT_AUDIO_SINK_KEY, DEFAULT_CONFIG_AUDIO_SINK_KEY, SPA_ID_INVALID, SPA_ID_INVALID
|
||||
};
|
||||
impl->defaults[DEFAULT_AUDIO_SOURCE] = (struct default_node){
|
||||
DEFAULT_AUDIO_SOURCE_KEY, DEFAULT_CONFIG_AUDIO_SOURCE_KEY, SPA_ID_INVALID, SPA_ID_INVALID
|
||||
};
|
||||
impl->defaults[DEFAULT_VIDEO_SOURCE] = (struct default_node){
|
||||
DEFAULT_VIDEO_SOURCE_KEY, DEFAULT_CONFIG_VIDEO_SOURCE_KEY, SPA_ID_INVALID, SPA_ID_INVALID
|
||||
};
|
||||
impl->defaults[3] = (struct default_node){ NULL, NULL, SPA_ID_INVALID, SPA_ID_INVALID };
|
||||
|
||||
flag = pw_properties_get(session->props, NAME ".streams-follow-default");
|
||||
impl->streams_follow_default = (flag != NULL && pw_properties_parse_bool(flag));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue