mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
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:
parent
389a125488
commit
24c68b0067
6 changed files with 383 additions and 154 deletions
57
src/daemon/media-session.d/alsa-monitor.conf
Normal file
57
src/daemon/media-session.d/alsa-monitor.conf
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"#": [ "rules for matching a device or node. It is an array of",
|
||||||
|
"properties that all need to match the regexp. If any of the",
|
||||||
|
"matches work, the actions are executed for the object."
|
||||||
|
],
|
||||||
|
|
||||||
|
"matches": [
|
||||||
|
{
|
||||||
|
"device.name": "~alsa_card.*"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": {
|
||||||
|
"#": [ "actions can update properties on the matched object."
|
||||||
|
],
|
||||||
|
|
||||||
|
"update-props": {
|
||||||
|
"api.alsa.use-acp": true,
|
||||||
|
"#api.alsa.use-ucm": true,
|
||||||
|
"#api.alsa.soft-mixer": false,
|
||||||
|
"#api.alsa.ignore-dB": false,
|
||||||
|
"#device.profile-set": "profileset-name",
|
||||||
|
"#device.profile": "default profile name",
|
||||||
|
"#api.acp.auto-port": true,
|
||||||
|
"#api.acp.auto-profile": true
|
||||||
|
"#device.nick": "My Device",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matches": [
|
||||||
|
{
|
||||||
|
"node.name": "~alsa_input.*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node.name": "~alsa_output.*"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": {
|
||||||
|
"update-props": {
|
||||||
|
"#node.nick": "My Node",
|
||||||
|
"#node.nick": null,
|
||||||
|
"#priority.driver": 100,
|
||||||
|
"#resample.quality": 4,
|
||||||
|
"#channelmix.normalize": false,
|
||||||
|
"#channelmix.mix-lfe": false,
|
||||||
|
"#audio.format": "S16LE",
|
||||||
|
"#audio.rate": 44100,
|
||||||
|
"#audio.position": "FL,FR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
55
src/daemon/media-session.d/media-session.conf
Normal file
55
src/daemon/media-session.d/media-session.conf
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
},
|
||||||
|
"spa-libs": {
|
||||||
|
"#": [ "Mapping from factory name to library."
|
||||||
|
],
|
||||||
|
|
||||||
|
"api.bluez5.*": "bluez5/libspa-bluez5",
|
||||||
|
"api.alsa.*": "alsa/libspa-alsa",
|
||||||
|
"api.v4l2.*": "v4l2/libspa-v4l2",
|
||||||
|
"api.libcamera.*": "libcamera/libspa-libcamera"
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"#": [ "These are the modules that are enabled when a file with",
|
||||||
|
"the key name is found in the media-session.d config directory.",
|
||||||
|
"the default bundle is always enabled."
|
||||||
|
],
|
||||||
|
|
||||||
|
"default": [
|
||||||
|
"flatpak", "# manages flatpak access",
|
||||||
|
"portal", "# manage portal permissions",
|
||||||
|
"v4l2", "# video for linux udev detection",
|
||||||
|
"#libcamera", "# libcamera udev detection",
|
||||||
|
"suspend-node", "# suspend inactive nodes",
|
||||||
|
"policy-node", "# configure and link nodes",
|
||||||
|
"#metadata", "# export metadata API",
|
||||||
|
"#default-nodes", "# restore default nodes",
|
||||||
|
"#default-profile", "# restore default profiles",
|
||||||
|
"#default-routes", "# restore default route",
|
||||||
|
"#alsa-seq", "# alsa seq midi support",
|
||||||
|
"#alsa-monitor", "# alsa udev detection",
|
||||||
|
"#bluez5", "# bluetooth support",
|
||||||
|
"#restore-stream", "#restore stream settings"
|
||||||
|
],
|
||||||
|
"with-audio": [
|
||||||
|
"metadata",
|
||||||
|
"default-nodes",
|
||||||
|
"default-profile",
|
||||||
|
"default-routes",
|
||||||
|
"alsa-seq",
|
||||||
|
"alsa-monitor"
|
||||||
|
],
|
||||||
|
"with-alsa": [
|
||||||
|
"with-audio"
|
||||||
|
],
|
||||||
|
"with-jack": [
|
||||||
|
"with-audio"
|
||||||
|
],
|
||||||
|
"with-pulseaudio": [
|
||||||
|
"with-audio",
|
||||||
|
"bluez5",
|
||||||
|
"restore-stream"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,11 @@ conf_config.set('pipewire_pulse_path', join_paths(pipewire_bindir, 'pipewire-pul
|
||||||
conf_install_dir = join_paths(get_option('sysconfdir'), 'pipewire')
|
conf_install_dir = join_paths(get_option('sysconfdir'), 'pipewire')
|
||||||
|
|
||||||
install_data(
|
install_data(
|
||||||
sources : ['media-session.d/with-jack', 'media-session.d/with-pulseaudio'],
|
sources : [
|
||||||
|
'media-session.d/with-jack',
|
||||||
|
'media-session.d/with-pulseaudio',
|
||||||
|
'media-session.d/media-session.conf',
|
||||||
|
'media-session.d/alsa-monitor.conf' ],
|
||||||
install_dir : join_paths(conf_install_dir, 'media-session.d'))
|
install_dir : join_paths(conf_install_dir, 'media-session.d'))
|
||||||
|
|
||||||
conf_config_uninstalled = conf_config
|
conf_config_uninstalled = conf_config
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@
|
||||||
#include <spa/utils/hook.h>
|
#include <spa/utils/hook.h>
|
||||||
#include <spa/utils/names.h>
|
#include <spa/utils/names.h>
|
||||||
#include <spa/utils/keys.h>
|
#include <spa/utils/keys.h>
|
||||||
|
#include <spa/utils/json.h>
|
||||||
#include <spa/param/props.h>
|
#include <spa/param/props.h>
|
||||||
#include <spa/pod/builder.h>
|
#include <spa/pod/builder.h>
|
||||||
#include <spa/pod/parser.h>
|
#include <spa/pod/parser.h>
|
||||||
|
|
@ -55,6 +57,8 @@
|
||||||
|
|
||||||
#include "reserve.c"
|
#include "reserve.c"
|
||||||
|
|
||||||
|
#define SESSION_CONF "alsa-monitor.conf"
|
||||||
|
|
||||||
#define DEFAULT_JACK_SECONDS 1
|
#define DEFAULT_JACK_SECONDS 1
|
||||||
|
|
||||||
struct node {
|
struct node {
|
||||||
|
|
@ -102,6 +106,7 @@ struct device {
|
||||||
unsigned int first:1;
|
unsigned int first:1;
|
||||||
unsigned int appeared:1;
|
unsigned int appeared:1;
|
||||||
unsigned int probed:1;
|
unsigned int probed:1;
|
||||||
|
unsigned int use_acp:1;
|
||||||
struct spa_list node_list;
|
struct spa_list node_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -109,6 +114,8 @@ struct impl {
|
||||||
struct sm_media_session *session;
|
struct sm_media_session *session;
|
||||||
struct spa_hook session_listener;
|
struct spa_hook session_listener;
|
||||||
|
|
||||||
|
struct pw_properties *conf;
|
||||||
|
|
||||||
DBusConnection *conn;
|
DBusConnection *conn;
|
||||||
|
|
||||||
struct spa_handle *handle;
|
struct spa_handle *handle;
|
||||||
|
|
@ -120,8 +127,6 @@ struct impl {
|
||||||
|
|
||||||
struct spa_source *jack_timeout;
|
struct spa_source *jack_timeout;
|
||||||
struct pw_proxy *jack_device;
|
struct pw_proxy *jack_device;
|
||||||
|
|
||||||
unsigned int use_acp:1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef NAME
|
#undef NAME
|
||||||
|
|
@ -192,6 +197,111 @@ static const struct sm_object_methods node_methods = {
|
||||||
.release = node_release,
|
.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,
|
static struct node *alsa_create_node(struct device *device, uint32_t id,
|
||||||
const struct spa_device_object_info *info)
|
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->impl = impl;
|
||||||
node->device = device;
|
node->device = device;
|
||||||
node->id = id;
|
node->id = id;
|
||||||
|
|
||||||
|
apply_matches(impl, node->props);
|
||||||
|
|
||||||
node->snode = sm_media_session_create_node(impl->session,
|
node->snode = sm_media_session_create_node(impl->session,
|
||||||
"adapter",
|
"adapter",
|
||||||
&node->props->dict);
|
&node->props->dict);
|
||||||
|
|
@ -517,11 +630,10 @@ static int update_device_props(struct device *device)
|
||||||
|
|
||||||
static void set_profile(struct device *device, int index)
|
static void set_profile(struct device *device, int index)
|
||||||
{
|
{
|
||||||
struct impl *impl = device->impl;
|
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||||
|
|
||||||
if (impl->use_acp)
|
if (device->use_acp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pw_log_debug("%p: set profile %d id:%d", device, index, device->device_id);
|
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;
|
struct device *device;
|
||||||
int res;
|
int res;
|
||||||
const char *str, *card, *factory_name, *name;
|
const char *str, *card;
|
||||||
|
|
||||||
pw_log_debug("new device %u", id);
|
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;
|
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->impl = impl;
|
||||||
device->id = id;
|
device->id = id;
|
||||||
device->props = pw_properties_new_dict(info->props);
|
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;
|
device->pending_profile = 1;
|
||||||
spa_list_append(&impl->device_list, &device->link);
|
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 &&
|
str = pw_properties_get(device->props, "api.alsa.use-acp");
|
||||||
(strcmp(str, "*") == 0 ||
|
device->use_acp = str ? pw_properties_parse_bool(str) : true;
|
||||||
(name != NULL && strstr(str, name) != NULL))) {
|
if (device->use_acp)
|
||||||
pw_properties_set(device->props, "api.alsa.soft-mixer", "true");
|
device->factory_name = strdup(SPA_NAME_API_ALSA_ACP_DEVICE);
|
||||||
}
|
else
|
||||||
if ((str = pw_properties_get(impl->session->props, "alsa.no-auto-port")) != NULL &&
|
device->factory_name = strdup(info->factory_name);
|
||||||
(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");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->conn &&
|
if (impl->conn &&
|
||||||
(card = spa_dict_lookup(info->props, SPA_KEY_API_ALSA_CARD)) != NULL) {
|
(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);
|
spa_hook_remove(&impl->listener);
|
||||||
pw_proxy_destroy(impl->jack_device);
|
pw_proxy_destroy(impl->jack_device);
|
||||||
pw_unload_spa_handle(impl->handle);
|
pw_unload_spa_handle(impl->handle);
|
||||||
|
pw_properties_free(impl->conf);
|
||||||
free(impl);
|
free(impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -954,7 +1052,6 @@ int sm_alsa_monitor_start(struct sm_media_session *session)
|
||||||
struct pw_context *context = session->context;
|
struct pw_context *context = session->context;
|
||||||
struct impl *impl;
|
struct impl *impl;
|
||||||
void *iface;
|
void *iface;
|
||||||
const char *str;
|
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
impl = calloc(1, sizeof(struct impl));
|
impl = calloc(1, sizeof(struct impl));
|
||||||
|
|
@ -962,9 +1059,15 @@ int sm_alsa_monitor_start(struct sm_media_session *session)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
impl->session = session;
|
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)
|
if ((res = sm_media_session_load_conf(impl->session,
|
||||||
impl->use_acp = pw_properties_parse_bool(str);
|
SESSION_CONF, impl->conf)) < 0)
|
||||||
|
pw_log_info("can't load "SESSION_CONF" config: %s", spa_strerror(res));
|
||||||
|
|
||||||
if (session->dbus_connection)
|
if (session->dbus_connection)
|
||||||
impl->conn = spa_dbus_connection_get(session->dbus_connection);
|
impl->conn = spa_dbus_connection_get(session->dbus_connection);
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
#include "media-session.h"
|
#include "media-session.h"
|
||||||
|
|
||||||
#define NAME "media-session"
|
#define NAME "media-session"
|
||||||
|
#define SESSION_CONF "media-session.conf"
|
||||||
|
|
||||||
#define sm_object_emit(o,m,v,...) spa_hook_list_call(&(o)->hooks, struct sm_object_events, m, v, ##__VA_ARGS__)
|
#define sm_object_emit(o,m,v,...) spa_hook_list_call(&(o)->hooks, struct sm_object_events, m, v, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
|
@ -115,9 +116,8 @@ struct sync {
|
||||||
struct impl {
|
struct impl {
|
||||||
struct sm_media_session this;
|
struct sm_media_session this;
|
||||||
|
|
||||||
const char *opt_default;
|
struct pw_properties *conf;
|
||||||
char *opt_enabled;
|
struct pw_properties *modules;
|
||||||
char *opt_disabled;
|
|
||||||
|
|
||||||
struct pw_main_loop *loop;
|
struct pw_main_loop *loop;
|
||||||
struct spa_dbus *dbus;
|
struct spa_dbus *dbus;
|
||||||
|
|
@ -1776,6 +1776,44 @@ int sm_media_session_remove_links(struct sm_media_session *sess,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sm_media_session_load_conf(struct sm_media_session *sess, const char *name,
|
||||||
|
struct pw_properties *conf)
|
||||||
|
{
|
||||||
|
const char *dir;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int count, fd;
|
||||||
|
struct stat sbuf;
|
||||||
|
char *data;
|
||||||
|
|
||||||
|
if ((dir = getenv("PIPEWIRE_CONFIG_DIR")) == NULL)
|
||||||
|
dir = PIPEWIRE_CONFIG_DIR;
|
||||||
|
if (dir == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path)-1, "%s/media-session.d/%s", dir, name);
|
||||||
|
if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) {
|
||||||
|
pw_log_warn(NAME" %p: error loading config '%s': %m", sess, path);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_log_info(NAME" %p: loading config '%s'", sess, path);
|
||||||
|
if (fstat(fd, &sbuf) < 0)
|
||||||
|
goto error_close;
|
||||||
|
if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
|
||||||
|
goto error_close;
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
count = pw_properties_update_string(conf, data, sbuf.st_size);
|
||||||
|
munmap(data, sbuf.st_size);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
error_close:
|
||||||
|
pw_log_debug("can't read file %s: %m", path);
|
||||||
|
close(fd);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
static int state_dir(struct sm_media_session *sess)
|
static int state_dir(struct sm_media_session *sess)
|
||||||
{
|
{
|
||||||
struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
|
struct impl *impl = SPA_CONTAINER_OF(sess, struct impl, this);
|
||||||
|
|
@ -2093,55 +2131,78 @@ static void do_quit(void *data, int signal_number)
|
||||||
pw_main_loop_quit(impl->loop);
|
pw_main_loop_quit(impl->loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFAULT_ENABLED "flatpak," \
|
static int load_spa_libs(struct impl *impl, const char *str)
|
||||||
"portal," \
|
|
||||||
"v4l2," \
|
|
||||||
"suspend-node," \
|
|
||||||
"policy-node"
|
|
||||||
#define AUDIO_ENABLED DEFAULT_ENABLED"," \
|
|
||||||
"metadata," \
|
|
||||||
"default-nodes," \
|
|
||||||
"default-profile," \
|
|
||||||
"default-routes," \
|
|
||||||
"alsa-acp," \
|
|
||||||
"alsa-seq"
|
|
||||||
#define PULSE_ENABLED AUDIO_ENABLED"," \
|
|
||||||
"bluez5," \
|
|
||||||
"restore-stream"
|
|
||||||
#define EXTRA_ENABLED ""
|
|
||||||
#define EXTRA_DISABLED ""
|
|
||||||
|
|
||||||
static int check_default_enabled(struct impl *impl)
|
|
||||||
{
|
{
|
||||||
const char *dir;
|
struct spa_json it[2];
|
||||||
char check_path[PATH_MAX];
|
char key[512], value[512];
|
||||||
uint32_t i;
|
|
||||||
struct stat statbuf;
|
spa_json_init(&it[0], str, strlen(str));
|
||||||
struct modules {
|
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||||
const char *file;
|
return -EINVAL;
|
||||||
const char *name;
|
|
||||||
const char *options;
|
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||||
} module_check[] = {
|
const char *val;
|
||||||
{ "with-jack", "Audio", AUDIO_ENABLED },
|
if (key[0] == '#') {
|
||||||
{ "with-alsa", "Audio", AUDIO_ENABLED },
|
if (spa_json_next(&it[1], &val) <= 0)
|
||||||
{ "with-pulseaudio", "PulseAudio", PULSE_ENABLED },
|
break;
|
||||||
};
|
}
|
||||||
|
else if (spa_json_get_string(&it[1], value, sizeof(value)-1) > 0) {
|
||||||
|
pw_log_debug("spa-libs: '%s' -> '%s'", key, value);
|
||||||
|
pw_context_add_spa_lib(impl->this.context, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_modules(struct impl *impl, const char *str)
|
||||||
|
{
|
||||||
|
struct spa_json it[3];
|
||||||
|
char key[512], value[512];
|
||||||
|
const char *dir, *val;
|
||||||
|
char check_path[PATH_MAX];
|
||||||
|
struct stat statbuf;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
impl->opt_default = DEFAULT_ENABLED;
|
|
||||||
if ((dir = getenv("PIPEWIRE_CONFIG_DIR")) == NULL)
|
if ((dir = getenv("PIPEWIRE_CONFIG_DIR")) == NULL)
|
||||||
dir = PIPEWIRE_CONFIG_DIR;
|
dir = PIPEWIRE_CONFIG_DIR;
|
||||||
if (dir == NULL)
|
if (dir == NULL)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
for (i = 0; i < SPA_N_ELEMENTS(module_check); i++) {
|
again:
|
||||||
|
spa_json_init(&it[0], str, strlen(str));
|
||||||
|
if (spa_json_enter_object(&it[0], &it[1]) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
while (spa_json_get_string(&it[1], key, sizeof(key)-1) > 0) {
|
||||||
|
bool add = false;
|
||||||
|
|
||||||
|
pw_log_info("%s", key);
|
||||||
|
if (key[0] == '#') {
|
||||||
|
add = false;
|
||||||
|
} else if (pw_properties_get(impl->modules, key) != NULL) {
|
||||||
|
add = true;
|
||||||
|
} else {
|
||||||
snprintf(check_path, sizeof(check_path),
|
snprintf(check_path, sizeof(check_path),
|
||||||
"%s/media-session.d/%s", dir, module_check[i].file);
|
"%s/media-session.d/%s", dir, key);
|
||||||
if (stat(check_path, &statbuf) == 0) {
|
add = (stat(check_path, &statbuf) == 0);
|
||||||
pw_log_info("found %s, enable %s", check_path,
|
}
|
||||||
module_check[i].name);
|
if (add) {
|
||||||
impl->opt_default = module_check[i].options;
|
if (spa_json_enter_array(&it[1], &it[2]) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (spa_json_get_string(&it[2], value, sizeof(value)-1) > 0) {
|
||||||
|
if (value[0] == '#')
|
||||||
|
continue;
|
||||||
|
pw_properties_set(impl->modules, value, "true");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (spa_json_next(&it[1], &val) <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* twice to resolve groups in module list */
|
||||||
|
if (count++ == 0)
|
||||||
|
goto again;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2160,8 +2221,7 @@ static const struct {
|
||||||
{ "default-routes", "restore default route", sm_default_routes_start, NULL },
|
{ "default-routes", "restore default route", sm_default_routes_start, NULL },
|
||||||
{ "restore-stream", "restore stream settings", sm_restore_stream_start, NULL },
|
{ "restore-stream", "restore stream settings", sm_restore_stream_start, NULL },
|
||||||
{ "alsa-seq", "alsa seq midi support", sm_alsa_midi_start, NULL },
|
{ "alsa-seq", "alsa seq midi support", sm_alsa_midi_start, NULL },
|
||||||
{ "alsa-pcm", "alsa pcm udev detection", sm_alsa_monitor_start, NULL },
|
{ "alsa-monitor", "alsa card udev detection", sm_alsa_monitor_start, NULL },
|
||||||
{ "alsa-acp", "alsa card profile udev detection", sm_alsa_monitor_start, "alsa.use-acp=true" },
|
|
||||||
{ "v4l2", "video for linux udev detection", sm_v4l2_monitor_start, NULL },
|
{ "v4l2", "video for linux udev detection", sm_v4l2_monitor_start, NULL },
|
||||||
{ "libcamera", "libcamera udev detection", sm_libcamera_monitor_start, NULL },
|
{ "libcamera", "libcamera udev detection", sm_libcamera_monitor_start, NULL },
|
||||||
{ "bluez5", "bluetooth support", sm_bluez5_monitor_start, NULL },
|
{ "bluez5", "bluetooth support", sm_bluez5_monitor_start, NULL },
|
||||||
|
|
@ -2170,41 +2230,10 @@ static const struct {
|
||||||
{ "pulse-bridge", "accept pulseaudio clients", sm_pulse_bridge_start, NULL },
|
{ "pulse-bridge", "accept pulseaudio clients", sm_pulse_bridge_start, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int opt_contains(const char *opt, const char *val)
|
static bool is_module_enabled(struct impl *impl, const char *val)
|
||||||
{
|
{
|
||||||
const char *s, *state = NULL;
|
const char *str = pw_properties_get(impl->modules, val);
|
||||||
size_t len;
|
return str ? pw_properties_parse_bool(str) : false;
|
||||||
while((s = pw_split_walk(opt, ",", &len, &state)) != NULL) {
|
|
||||||
if (strncmp(val, s, len) == 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_opt_enabled(struct impl *impl, const char *val)
|
|
||||||
{
|
|
||||||
return (opt_contains(impl->opt_default, val) || opt_contains(impl->opt_enabled, val)) &&
|
|
||||||
!opt_contains(impl->opt_disabled, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int append_opt_str(char **p, char *sep, char *str)
|
|
||||||
{
|
|
||||||
char *buf;
|
|
||||||
size_t size = strlen(*p) + strlen(sep) + strlen(str) + 1;
|
|
||||||
|
|
||||||
if (**p == '\0') {
|
|
||||||
free(*p);
|
|
||||||
*p = strdup(str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((buf = malloc(size)) == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
snprintf(buf, size, "%s%s%s", *p, sep, str);
|
|
||||||
free(*p);
|
|
||||||
*p = buf;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_help(const char *name, struct impl *impl)
|
static void show_help(const char *name, struct impl *impl)
|
||||||
|
|
@ -2214,16 +2243,14 @@ static void show_help(const char *name, struct impl *impl)
|
||||||
fprintf(stdout, "%s [options]\n"
|
fprintf(stdout, "%s [options]\n"
|
||||||
" -h, --help Show this help\n"
|
" -h, --help Show this help\n"
|
||||||
" --version Show version\n"
|
" --version Show version\n"
|
||||||
" -e, --enabled Extra comma separated enabled options ('%s')\n"
|
|
||||||
" -d, --disabled Extra comma separated disabled options ('%s')\n"
|
|
||||||
" -p, --properties Extra properties as 'key=value { key=value }'\n",
|
" -p, --properties Extra properties as 'key=value { key=value }'\n",
|
||||||
name, impl->opt_enabled, impl->opt_disabled);
|
name);
|
||||||
|
|
||||||
fprintf(stdout,
|
fprintf(stdout,
|
||||||
"\noptions: (*=enabled)\n");
|
"\noptions: (*=enabled)\n");
|
||||||
for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
|
for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
|
||||||
fprintf(stdout, "\t %c %-15.15s: %s\n",
|
fprintf(stdout, "\t %c %-15.15s: %s\n",
|
||||||
is_opt_enabled(impl, modules[i].name) ? '*' : ' ',
|
is_module_enabled(impl, modules[i].name) ? '*' : ' ',
|
||||||
modules[i].name, modules[i].desc);
|
modules[i].name, modules[i].desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2232,14 +2259,12 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct impl impl = { 0, };
|
struct impl impl = { 0, };
|
||||||
const struct spa_support *support;
|
const struct spa_support *support;
|
||||||
|
const char *str;
|
||||||
uint32_t n_support;
|
uint32_t n_support;
|
||||||
int res = 0, c;
|
int res = 0, c;
|
||||||
char *opt_properties;
|
|
||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "enabled", required_argument, NULL, 'e' },
|
|
||||||
{ "disabled", required_argument, NULL, 'd' },
|
|
||||||
{ "properties", required_argument, NULL, 'p' },
|
{ "properties", required_argument, NULL, 'p' },
|
||||||
{ NULL, 0, NULL, 0}
|
{ NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
@ -2248,13 +2273,20 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
pw_init(&argc, &argv);
|
pw_init(&argc, &argv);
|
||||||
|
|
||||||
check_default_enabled(&impl);
|
if ((impl.conf = pw_properties_new(NULL, NULL)) == NULL)
|
||||||
|
return -1;
|
||||||
|
sm_media_session_load_conf(&impl.this, SESSION_CONF, impl.conf);
|
||||||
|
|
||||||
impl.opt_enabled = strdup(EXTRA_ENABLED);
|
if ((impl.modules = pw_properties_new("default", "true", NULL)) == NULL)
|
||||||
impl.opt_disabled = strdup(EXTRA_DISABLED);
|
return -1;
|
||||||
opt_properties = strdup("");
|
if ((str = pw_properties_get(impl.conf, "modules")) != NULL)
|
||||||
|
collect_modules(&impl, str);
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "hVe:d:p:", long_options, NULL)) != -1) {
|
impl.this.props = pw_properties_new(NULL, NULL);
|
||||||
|
if (impl.this.props == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while ((c = getopt_long(argc, argv, "hVp:", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0], &impl);
|
show_help(argv[0], &impl);
|
||||||
|
|
@ -2267,17 +2299,8 @@ int main(int argc, char *argv[])
|
||||||
pw_get_headers_version(),
|
pw_get_headers_version(),
|
||||||
pw_get_library_version());
|
pw_get_library_version());
|
||||||
return 0;
|
return 0;
|
||||||
case 'e':
|
|
||||||
if (append_opt_str(&impl.opt_enabled, ",", optarg))
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
if (append_opt_str(&impl.opt_disabled, ",", optarg))
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 'p':
|
case 'p':
|
||||||
if (append_opt_str(&opt_properties, " ", optarg))
|
pw_properties_update_string(impl.this.props, optarg, strlen(optarg));
|
||||||
return -1;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -2286,11 +2309,6 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
impl.state_dir_fd = -1;
|
impl.state_dir_fd = -1;
|
||||||
|
|
||||||
impl.this.props = pw_properties_new_string(opt_properties);
|
|
||||||
free(opt_properties);
|
|
||||||
if (impl.this.props == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
spa_dict_for_each(item, &impl.this.props->dict)
|
spa_dict_for_each(item, &impl.this.props->dict)
|
||||||
pw_log_info(" '%s' = '%s'", item->key, item->value);
|
pw_log_info(" '%s' = '%s'", item->key, item->value);
|
||||||
|
|
||||||
|
|
@ -2311,10 +2329,8 @@ int main(int argc, char *argv[])
|
||||||
if (impl.this.context == NULL)
|
if (impl.this.context == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pw_context_add_spa_lib(impl.this.context, "api.bluez5.*", "bluez5/libspa-bluez5");
|
if ((str = pw_properties_get(impl.conf, "spa-libs")) != NULL)
|
||||||
pw_context_add_spa_lib(impl.this.context, "api.alsa.*", "alsa/libspa-alsa");
|
load_spa_libs(&impl, str);
|
||||||
pw_context_add_spa_lib(impl.this.context, "api.v4l2.*", "v4l2/libspa-v4l2");
|
|
||||||
pw_context_add_spa_lib(impl.this.context, "api.libcamera.*", "libcamera/libspa-libcamera");
|
|
||||||
|
|
||||||
pw_context_set_object(impl.this.context, SM_TYPE_MEDIA_SESSION, &impl);
|
pw_context_set_object(impl.this.context, SM_TYPE_MEDIA_SESSION, &impl);
|
||||||
|
|
||||||
|
|
@ -2343,15 +2359,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
|
for (i = 0; i < SPA_N_ELEMENTS(modules); i++) {
|
||||||
const char *name = modules[i].name;
|
const char *name = modules[i].name;
|
||||||
if (is_opt_enabled(&impl, name)) {
|
if (is_module_enabled(&impl, name)) {
|
||||||
if (modules[i].props) {
|
|
||||||
struct pw_properties *props;
|
|
||||||
props = pw_properties_new_string(modules[i].props);
|
|
||||||
if (props) {
|
|
||||||
pw_properties_update(impl.this.props, &props->dict);
|
|
||||||
pw_properties_free(props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pw_log_info("enable: %s", name);
|
pw_log_info("enable: %s", name);
|
||||||
modules[i].start(&impl.this);
|
modules[i].start(&impl.this);
|
||||||
}
|
}
|
||||||
|
|
@ -2370,14 +2378,13 @@ exit:
|
||||||
pw_map_clear(&impl.endpoint_links);
|
pw_map_clear(&impl.endpoint_links);
|
||||||
pw_map_clear(&impl.globals);
|
pw_map_clear(&impl.globals);
|
||||||
pw_properties_free(impl.this.props);
|
pw_properties_free(impl.this.props);
|
||||||
|
pw_properties_free(impl.conf);
|
||||||
|
pw_properties_free(impl.modules);
|
||||||
|
|
||||||
if (impl.state_dir_fd != -1)
|
if (impl.state_dir_fd != -1)
|
||||||
close(impl.state_dir_fd);
|
close(impl.state_dir_fd);
|
||||||
|
|
||||||
pw_deinit();
|
pw_deinit();
|
||||||
|
|
||||||
free(impl.opt_enabled);
|
|
||||||
free(impl.opt_disabled);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,9 @@ int sm_media_session_create_links(struct sm_media_session *sess,
|
||||||
int sm_media_session_remove_links(struct sm_media_session *sess,
|
int sm_media_session_remove_links(struct sm_media_session *sess,
|
||||||
const struct spa_dict *dict);
|
const struct spa_dict *dict);
|
||||||
|
|
||||||
|
int sm_media_session_load_conf(struct sm_media_session *sess,
|
||||||
|
const char *name, struct pw_properties *conf);
|
||||||
|
|
||||||
int sm_media_session_load_state(struct sm_media_session *sess,
|
int sm_media_session_load_state(struct sm_media_session *sess,
|
||||||
const char *name, const char *prefix, struct pw_properties *props);
|
const char *name, const char *prefix, struct pw_properties *props);
|
||||||
int sm_media_session_save_state(struct sm_media_session *sess,
|
int sm_media_session_save_state(struct sm_media_session *sess,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue