media-session: add config files

Remove -e and -d options
Manage modules in media-session.conf
Add alsa-monitor.conf that can match node/device properties with
a regex and update properties on it. All previous configuration and
more cane be done with this.
This commit is contained in:
Wim Taymans 2020-12-30 13:12:48 +01:00
parent 389a125488
commit 24c68b0067
6 changed files with 383 additions and 154 deletions

View file

@ -27,6 +27,7 @@
#include <errno.h>
#include <math.h>
#include <time.h>
#include <regex.h>
#include "config.h"
@ -40,6 +41,7 @@
#include <spa/utils/hook.h>
#include <spa/utils/names.h>
#include <spa/utils/keys.h>
#include <spa/utils/json.h>
#include <spa/param/props.h>
#include <spa/pod/builder.h>
#include <spa/pod/parser.h>
@ -55,6 +57,8 @@
#include "reserve.c"
#define SESSION_CONF "alsa-monitor.conf"
#define DEFAULT_JACK_SECONDS 1
struct node {
@ -102,6 +106,7 @@ struct device {
unsigned int first:1;
unsigned int appeared:1;
unsigned int probed:1;
unsigned int use_acp:1;
struct spa_list node_list;
};
@ -109,6 +114,8 @@ struct impl {
struct sm_media_session *session;
struct spa_hook session_listener;
struct pw_properties *conf;
DBusConnection *conn;
struct spa_handle *handle;
@ -120,8 +127,6 @@ struct impl {
struct spa_source *jack_timeout;
struct pw_proxy *jack_device;
unsigned int use_acp:1;
};
#undef NAME
@ -192,6 +197,111 @@ static const struct sm_object_methods node_methods = {
.release = node_release,
};
static bool find_match(struct spa_json *arr, struct pw_properties *props)
{
struct spa_json it[1];
while (spa_json_enter_object(arr, &it[0]) > 0) {
char key[256], val[1024];
const char *str, *value;
int match = 0, fail = 0;
int len;
while (spa_json_get_string(&it[0], key, sizeof(key)-1) > 0) {
bool success = false;
if ((len = spa_json_next(&it[0], &value)) <= 0)
break;
if (key[0] == '#')
continue;
str = pw_properties_get(props, key);
if (spa_json_is_null(value, len)) {
success = str == NULL;
}
else if (spa_json_is_string(value, len)) {
spa_json_parse_string(value, SPA_MIN(len, 1024), val);
value = val;
len = strlen(val);
}
if (str != NULL) {
if (value[0] == '~') {
regex_t preg;
if (regcomp(&preg, value+1, REG_EXTENDED | REG_NOSUB) == 0) {
if (regexec(&preg, str, 0, NULL, 0) == 0)
success = true;
regfree(&preg);
}
} else if (strncmp(str, value, len) == 0) {
success = true;
}
}
if (success) {
match++;
pw_log_debug("'%s' match '%s' < > '%.*s'", key, str, len, value);
}
else
fail++;
}
if (match > 0 && fail == 0)
return true;
}
return false;
}
static int apply_matches(struct impl *impl, struct pw_properties *props)
{
const char *rules, *val;
struct spa_json it[4], actions;;
if ((rules = pw_properties_get(impl->conf, "rules")) == NULL)
return 0;
spa_json_init(&it[0], rules, strlen(rules));
if (spa_json_enter_array(&it[0], &it[1]) < 0)
return 0;
while (spa_json_enter_object(&it[1], &it[2]) > 0) {
char key[64];
bool have_match = false, have_actions = false;
while (spa_json_get_string(&it[2], key, sizeof(key)-1) > 0) {
if (strcmp(key, "matches") == 0) {
if (spa_json_enter_array(&it[2], &it[3]) < 0)
break;
have_match = find_match(&it[3], props);
}
else if (strcmp(key, "actions") == 0) {
if (spa_json_enter_object(&it[2], &actions) > 0)
have_actions = true;
}
else if (spa_json_next(&it[2], &val) <= 0)
break;
}
if (!have_match || !have_actions)
continue;
while (spa_json_get_string(&actions, key, sizeof(key)-1) > 0) {
int len;
pw_log_debug("action %s", key);
if (strcmp(key, "update-props") == 0) {
if ((len = spa_json_next(&actions, &val)) <= 0)
continue;
if (!spa_json_is_object(val, len))
continue;
len = spa_json_container_len(&actions, val, len);
pw_properties_update_string(props, val, len);
}
else if (spa_json_next(&actions, &val) <= 0)
break;
}
}
return 1;
}
static struct node *alsa_create_node(struct device *device, uint32_t id,
const struct spa_device_object_info *info)
{
@ -314,6 +424,9 @@ static struct node *alsa_create_node(struct device *device, uint32_t id,
node->impl = impl;
node->device = device;
node->id = id;
apply_matches(impl, node->props);
node->snode = sm_media_session_create_node(impl->session,
"adapter",
&node->props->dict);
@ -517,11 +630,10 @@ static int update_device_props(struct device *device)
static void set_profile(struct device *device, int index)
{
struct impl *impl = device->impl;
char buf[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
if (impl->use_acp)
if (device->use_acp)
return;
pw_log_debug("%p: set profile %d id:%d", device, index, device->device_id);
@ -795,7 +907,7 @@ static struct device *alsa_create_device(struct impl *impl, uint32_t id,
{
struct device *device;
int res;
const char *str, *card, *factory_name, *name;
const char *str, *card;
pw_log_debug("new device %u", id);
@ -810,12 +922,6 @@ static struct device *alsa_create_device(struct impl *impl, uint32_t id,
goto exit;
}
if (impl->use_acp)
factory_name = SPA_NAME_API_ALSA_ACP_DEVICE;
else
factory_name = info->factory_name;
device->factory_name = strdup(factory_name);
device->impl = impl;
device->id = id;
device->props = pw_properties_new_dict(info->props);
@ -826,23 +932,14 @@ static struct device *alsa_create_device(struct impl *impl, uint32_t id,
device->pending_profile = 1;
spa_list_append(&impl->device_list, &device->link);
name = pw_properties_get(device->props, "device.name");
apply_matches(impl, device->props);
if ((str = pw_properties_get(impl->session->props, "alsa.soft-mixer")) != NULL &&
(strcmp(str, "*") == 0 ||
(name != NULL && strstr(str, name) != NULL))) {
pw_properties_set(device->props, "api.alsa.soft-mixer", "true");
}
if ((str = pw_properties_get(impl->session->props, "alsa.no-auto-port")) != NULL &&
(strcmp(str, "*") == 0 ||
(name != NULL && strstr(str, name) != NULL))) {
pw_properties_set(device->props, "api.acp.auto-port", "false");
}
if ((str = pw_properties_get(impl->session->props, "alsa.no-auto-profile")) != NULL &&
(strcmp(str, "*") == 0 ||
(name != NULL && strstr(str, name) != NULL))) {
pw_properties_set(device->props, "api.acp.auto-profile", "false");
}
str = pw_properties_get(device->props, "api.alsa.use-acp");
device->use_acp = str ? pw_properties_parse_bool(str) : true;
if (device->use_acp)
device->factory_name = strdup(SPA_NAME_API_ALSA_ACP_DEVICE);
else
device->factory_name = strdup(info->factory_name);
if (impl->conn &&
(card = spa_dict_lookup(info->props, SPA_KEY_API_ALSA_CARD)) != NULL) {
@ -941,6 +1038,7 @@ static void session_destroy(void *data)
spa_hook_remove(&impl->listener);
pw_proxy_destroy(impl->jack_device);
pw_unload_spa_handle(impl->handle);
pw_properties_free(impl->conf);
free(impl);
}
@ -954,7 +1052,6 @@ int sm_alsa_monitor_start(struct sm_media_session *session)
struct pw_context *context = session->context;
struct impl *impl;
void *iface;
const char *str;
int res;
impl = calloc(1, sizeof(struct impl));
@ -962,9 +1059,15 @@ int sm_alsa_monitor_start(struct sm_media_session *session)
return -errno;
impl->session = session;
impl->conf = pw_properties_new(NULL, NULL);
if (impl->conf == NULL) {
free(impl);
return -ENOMEM;
}
if ((str = pw_properties_get(session->props, "alsa.use-acp")) != NULL)
impl->use_acp = pw_properties_parse_bool(str);
if ((res = sm_media_session_load_conf(impl->session,
SESSION_CONF, impl->conf)) < 0)
pw_log_info("can't load "SESSION_CONF" config: %s", spa_strerror(res));
if (session->dbus_connection)
impl->conn = spa_dbus_connection_get(session->dbus_connection);