From 24c68b00677dac8accd9eb929dad2efc76520ebb Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 30 Dec 2020 13:12:48 +0100 Subject: [PATCH] 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. --- src/daemon/media-session.d/alsa-monitor.conf | 57 ++++ src/daemon/media-session.d/media-session.conf | 55 ++++ src/daemon/meson.build | 6 +- src/examples/media-session/alsa-monitor.c | 163 ++++++++--- src/examples/media-session/media-session.c | 253 +++++++++--------- src/examples/media-session/media-session.h | 3 + 6 files changed, 383 insertions(+), 154 deletions(-) create mode 100644 src/daemon/media-session.d/alsa-monitor.conf create mode 100644 src/daemon/media-session.d/media-session.conf diff --git a/src/daemon/media-session.d/alsa-monitor.conf b/src/daemon/media-session.d/alsa-monitor.conf new file mode 100644 index 000000000..4516b65ff --- /dev/null +++ b/src/daemon/media-session.d/alsa-monitor.conf @@ -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" + } + } + } + ] +} diff --git a/src/daemon/media-session.d/media-session.conf b/src/daemon/media-session.d/media-session.conf new file mode 100644 index 000000000..577166dd1 --- /dev/null +++ b/src/daemon/media-session.d/media-session.conf @@ -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" + ] + } +} diff --git a/src/daemon/meson.build b/src/daemon/meson.build index fa6eaf4d1..0cf14b5f2 100644 --- a/src/daemon/meson.build +++ b/src/daemon/meson.build @@ -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') 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')) conf_config_uninstalled = conf_config diff --git a/src/examples/media-session/alsa-monitor.c b/src/examples/media-session/alsa-monitor.c index 9769580cb..53b918ef9 100644 --- a/src/examples/media-session/alsa-monitor.c +++ b/src/examples/media-session/alsa-monitor.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "config.h" @@ -40,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 5bee0f588..975017c74 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -60,7 +60,8 @@ #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__) @@ -115,9 +116,8 @@ struct sync { struct impl { struct sm_media_session this; - const char *opt_default; - char *opt_enabled; - char *opt_disabled; + struct pw_properties *conf; + struct pw_properties *modules; struct pw_main_loop *loop; struct spa_dbus *dbus; @@ -1776,6 +1776,44 @@ int sm_media_session_remove_links(struct sm_media_session *sess, 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) { 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); } -#define DEFAULT_ENABLED "flatpak," \ - "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) +static int load_spa_libs(struct impl *impl, const char *str) { - const char *dir; - char check_path[PATH_MAX]; - uint32_t i; - struct stat statbuf; - struct modules { - const char *file; - const char *name; - const char *options; - } module_check[] = { - { "with-jack", "Audio", AUDIO_ENABLED }, - { "with-alsa", "Audio", AUDIO_ENABLED }, - { "with-pulseaudio", "PulseAudio", PULSE_ENABLED }, - }; + struct spa_json it[2]; + char key[512], value[512]; + + 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) { + const char *val; + if (key[0] == '#') { + if (spa_json_next(&it[1], &val) <= 0) + 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) dir = PIPEWIRE_CONFIG_DIR; if (dir == NULL) return -ENOENT; - for (i = 0; i < SPA_N_ELEMENTS(module_check); i++) { - snprintf(check_path, sizeof(check_path), - "%s/media-session.d/%s", dir, module_check[i].file); - if (stat(check_path, &statbuf) == 0) { - pw_log_info("found %s, enable %s", check_path, - module_check[i].name); - impl->opt_default = module_check[i].options; +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), + "%s/media-session.d/%s", dir, key); + add = (stat(check_path, &statbuf) == 0); } + if (add) { + 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; } @@ -2160,8 +2221,7 @@ static const struct { { "default-routes", "restore default route", sm_default_routes_start, NULL }, { "restore-stream", "restore stream settings", sm_restore_stream_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-acp", "alsa card profile udev detection", sm_alsa_monitor_start, "alsa.use-acp=true" }, + { "alsa-monitor", "alsa card udev detection", sm_alsa_monitor_start, NULL }, { "v4l2", "video for linux udev detection", sm_v4l2_monitor_start, NULL }, { "libcamera", "libcamera udev detection", sm_libcamera_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 }, }; -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; - size_t len; - 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; + const char *str = pw_properties_get(impl->modules, val); + return str ? pw_properties_parse_bool(str) : false; } 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" " -h, --help Show this help\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", - name, impl->opt_enabled, impl->opt_disabled); + name); fprintf(stdout, "\noptions: (*=enabled)\n"); for (i = 0; i < SPA_N_ELEMENTS(modules); i++) { 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); } } @@ -2232,14 +2259,12 @@ int main(int argc, char *argv[]) { struct impl impl = { 0, }; const struct spa_support *support; + const char *str; uint32_t n_support; int res = 0, c; - char *opt_properties; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, - { "enabled", required_argument, NULL, 'e' }, - { "disabled", required_argument, NULL, 'd' }, { "properties", required_argument, NULL, 'p' }, { NULL, 0, NULL, 0} }; @@ -2248,13 +2273,20 @@ int main(int argc, char *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); - impl.opt_disabled = strdup(EXTRA_DISABLED); - opt_properties = strdup(""); + if ((impl.modules = pw_properties_new("default", "true", NULL)) == NULL) + return -1; + 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) { case 'h': show_help(argv[0], &impl); @@ -2267,17 +2299,8 @@ int main(int argc, char *argv[]) pw_get_headers_version(), pw_get_library_version()); 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': - if (append_opt_str(&opt_properties, " ", optarg)) - return -1; + pw_properties_update_string(impl.this.props, optarg, strlen(optarg)); break; default: return -1; @@ -2286,11 +2309,6 @@ int main(int argc, char *argv[]) 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) pw_log_info(" '%s' = '%s'", item->key, item->value); @@ -2311,10 +2329,8 @@ int main(int argc, char *argv[]) if (impl.this.context == NULL) return -1; - pw_context_add_spa_lib(impl.this.context, "api.bluez5.*", "bluez5/libspa-bluez5"); - pw_context_add_spa_lib(impl.this.context, "api.alsa.*", "alsa/libspa-alsa"); - 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"); + if ((str = pw_properties_get(impl.conf, "spa-libs")) != NULL) + load_spa_libs(&impl, str); 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++) { const char *name = modules[i].name; - if (is_opt_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); - } - } + if (is_module_enabled(&impl, name)) { pw_log_info("enable: %s", name); modules[i].start(&impl.this); } @@ -2370,14 +2378,13 @@ exit: pw_map_clear(&impl.endpoint_links); pw_map_clear(&impl.globals); pw_properties_free(impl.this.props); + pw_properties_free(impl.conf); + pw_properties_free(impl.modules); if (impl.state_dir_fd != -1) close(impl.state_dir_fd); pw_deinit(); - free(impl.opt_enabled); - free(impl.opt_disabled); - return res; } diff --git a/src/examples/media-session/media-session.h b/src/examples/media-session/media-session.h index d28bfdd55..f1bb1a2e9 100644 --- a/src/examples/media-session/media-session.h +++ b/src/examples/media-session/media-session.h @@ -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, 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, const char *name, const char *prefix, struct pw_properties *props); int sm_media_session_save_state(struct sm_media_session *sess,