diff --git a/doc/dox/config/pipewire.conf.5.md b/doc/dox/config/pipewire.conf.5.md index 3c1868480..75e242a0b 100644 --- a/doc/dox/config/pipewire.conf.5.md +++ b/doc/dox/config/pipewire.conf.5.md @@ -167,6 +167,11 @@ PipeWire socket clients can connect to. Configures the CPU to zero denormals automatically. This will be enabled for the data processing thread only, when enabled. +@PAR@ pipewire.conf cpu.vm.name = null +This will be set automatically when the context is created and will +contain the name of the VM. It is typically used to write match rules +to set extra properties. + @PAR@ pipewire.conf default.clock.rate = 48000 The default clock rate determines the real time duration of the min/max/default quantums. You might want to change the quantums when @@ -245,7 +250,8 @@ it. Disable this if you want to globally disable DBus support in the process. @PAR@ pipewire.conf vm.overrides = { default.clock.min-quantum = 1024 } Any property in the vm.overrides property object will override the property -in the context.properties when PipeWire detects it is running in a VM. +in the context.properties when PipeWire detects it is running in a VM. This +is deprected, use the context.properties.rules instead. The context properties may also contain custom values. For example, the `context.modules` and `context.objects` sections can declare @@ -436,6 +442,28 @@ The available actions and their values depend on the specific rule that is used. Usually it is possible to update some properties or set some quirks on the object. +# CONTEXT PROPERTIES RULES @IDX@ pipewire.conf + +`context.properties.rules` can be used to dynamically update the properties +based on other properties. + +A typical case is to update custom settings when running inside a VM. +The `cpu.vm.name` is automatically set when running in a VM with the name of +the VM. A match rule can be written to set custom properties like this: + +``` +context.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + default.clock.min-quantum = 1024 + } + } + } +} +``` + # NODE RULES @IDX@ pipewire.conf The node.rules are evaluated every time the properties on a node are set diff --git a/spa/include/spa/support/cpu.h b/spa/include/spa/support/cpu.h index 7ec80006b..350dee78f 100644 --- a/spa/include/spa/support/cpu.h +++ b/spa/include/spa/support/cpu.h @@ -87,6 +87,46 @@ struct spa_cpu { struct spa_interface iface; }; #define SPA_CPU_VM_ACRN (1 << 13) #define SPA_CPU_VM_POWERVM (1 << 14) +static inline const char *spa_cpu_vm_type_to_string(uint32_t vm_type) +{ + switch(vm_type) { + case SPA_CPU_VM_NONE: + return NULL; + case SPA_CPU_VM_KVM: + return "kvm"; + case SPA_CPU_VM_QEMU: + return "qemu"; + case SPA_CPU_VM_BOCHS: + return "bochs"; + case SPA_CPU_VM_XEN: + return "xen"; + case SPA_CPU_VM_UML: + return "uml"; + case SPA_CPU_VM_VMWARE: + return "vmware"; + case SPA_CPU_VM_ORACLE: + return "oracle"; + case SPA_CPU_VM_MICROSOFT: + return "microsoft"; + case SPA_CPU_VM_ZVM: + return "zvm"; + case SPA_CPU_VM_PARALLELS: + return "parallels"; + case SPA_CPU_VM_BHYVE: + return "bhyve"; + case SPA_CPU_VM_QNX: + return "qnx"; + case SPA_CPU_VM_ACRN: + return "acrn"; + case SPA_CPU_VM_POWERVM: + return "powervm"; + case SPA_CPU_VM_OTHER: + return "other"; + default: + return "unknown"; + } +} + /** * methods */ diff --git a/src/daemon/minimal.conf.in b/src/daemon/minimal.conf.in index 08a9f38e1..f79ea4170 100644 --- a/src/daemon/minimal.conf.in +++ b/src/daemon/minimal.conf.in @@ -40,11 +40,6 @@ context.properties = { # settings.check-quantum = true settings.check-rate = true - # - # These overrides are only applied when running in a vm. - vm.overrides = { - default.clock.min-quantum = 1024 - } # This config can use udev or hardcoded ALSA devices. Make sure to # change the alsa device below when disabling udev @@ -54,6 +49,17 @@ context.properties = { minimal.use-pulse = true } +context.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + default.clock.min-quantum = 1024 + } + } + } +} + context.spa-libs = { # = # diff --git a/src/daemon/pipewire-avb.conf.in b/src/daemon/pipewire-avb.conf.in index 3752a4ec7..afb99f96d 100644 --- a/src/daemon/pipewire-avb.conf.in +++ b/src/daemon/pipewire-avb.conf.in @@ -67,7 +67,14 @@ avb.properties = { # the addresses this server listens on #ifname = "eth0.2" ifname = "enp3s0" - # These overrides are only applied when running in a vm. - vm.overrides = { +} + +avb.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + } + } } } diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index 1379ecf3c..8d72489d7 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -110,12 +110,19 @@ pulse.properties = { #pulse.idle.timeout = 0 # don't pause after underruns #pulse.default.format = F32 #pulse.default.position = [ FL FR ] - # These overrides are only applied when running in a vm. - vm.overrides = { - pulse.min.quantum = 1024/48000 # 22ms - } } +pulse.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + pulse.min.quantum = 1024/48000 # 22ms + } + } + } +] + # client/stream specific properties pulse.rules = [ { diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index 71926f6a0..fb38ac255 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -40,11 +40,6 @@ context.properties = { # #settings.check-quantum = false #settings.check-rate = false - # - # These overrides are only applied when running in a vm. - vm.overrides = { - default.clock.min-quantum = 1024 - } # keys checked below to disable module loading module.x11.bell = true @@ -55,6 +50,17 @@ context.properties = { module.jackdbus-detect = true } +context.properties.rules = [ + { matches = [ { cpu.vm.name = !null } ] + actions = { + update-props = { + # These overrides are only applied when running in a vm. + default.clock.min-quantum = 1024 + } + } + } +} + context.spa-libs = { # = # diff --git a/src/modules/module-avb/avb.c b/src/modules/module-avb/avb.c index 61ee48eaa..7bfa85eb9 100644 --- a/src/modules/module-avb/avb.c +++ b/src/modules/module-avb/avb.c @@ -32,6 +32,8 @@ struct pw_avb *pw_avb_new(struct pw_context *context, pw_context_conf_update_props(context, "avb.properties", props); if ((str = pw_properties_get(props, "vm.overrides")) != NULL) { + pw_log_warn("vm.overrides in avb.properties are deprecated, " + "use avb.properties.rules instead"); if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE) pw_properties_update_string(props, str, strlen(str)); pw_properties_set(props, "vm.overrides", NULL); diff --git a/src/modules/module-protocol-pulse.c b/src/modules/module-protocol-pulse.c index e50dc752c..30b72d112 100644 --- a/src/modules/module-protocol-pulse.c +++ b/src/modules/module-protocol-pulse.c @@ -71,11 +71,17 @@ * #pulse.min.quantum = 128/48000 # 2.7ms * #pulse.default.format = F32 * #pulse.default.position = [ FL FR ] - * # These overrides are only applied when running in a vm. - * vm.overrides = { - * pulse.min.quantum = 1024/48000 # 22ms - * } * } + * pulse.properties.rules = [ + * { matches = [ { cpu.vm.name = !null } ] + * actions = { + * update-props = { + * # These overrides are only applied when running in a vm. + * pulse.min.quantum = 1024/48000 # 22ms + * } + * } + * } + * ] *\endcode * * ### Connection options @@ -185,19 +191,6 @@ * This is equivalent to the PulseAudio `default-sample-channels` and * `default-channel-map` options in `/etc/pulse/daemon.conf`. * - * ### VM options - * - *\code{.unparsed} - * vm.overrides = { - * pulse.min.quantum = 1024/48000 # 22ms - * } - *\endcode - * - * When running in a VM, the `vm.override` section will override the properties - * in pulse.properties with the given values. This might be interesting because - * VMs usually can't support the low latency settings that are possible on real - * hardware. - * * ### Quirk options * *\code{.unparsed} @@ -240,6 +233,30 @@ * #{ cmd = "load-module" args = "module-gsettings" flags = [ "nofail" ] } * ] *\endcode + + * ## Dynamic properties + * + * The pulse.properties can be dyanmically updated with rules. It supports + * an `update-props` action. The matches will be performed on the values in + * context.properties. + * + *\code{.unparsed} + * pulse.properties.rules = [ + * { matches = [ { cpu.vm.name = !null } ] + * actions = { + * update-props = { + * # These overrides are only applied when running in a vm. + * pulse.min.quantum = 1024/48000 # 22ms + * } + * } + * } + * ] + *\endcode + * + * In the above example, when running in a VM, the rule will override the properties + * in pulse.properties with the given values. This might be interesting because + * VMs usually can't support the low latency settings that are possible on real + * hardware. * * ## Stream settings and rules * diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 9bfae0f0a..d7e4e10ab 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -5502,6 +5502,8 @@ struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context, pw_context_conf_update_props(context, "pulse.properties", props); if ((str = pw_properties_get(props, "vm.overrides")) != NULL) { + pw_log_warn("vm.overrides in pulse.properties are deprecated, " + "use pulse.properties.rules instead"); if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE) pw_properties_update_string(props, str, strlen(str)); pw_properties_set(props, "vm.overrides", NULL); diff --git a/src/pipewire/conf.c b/src/pipewire/conf.c index d01a28d6e..3c04e13a2 100644 --- a/src/pipewire/conf.c +++ b/src/pipewire/conf.c @@ -986,26 +986,38 @@ static int update_props(void *user_data, const char *location, const char *key, } SPA_EXPORT -int pw_conf_section_update_props(const struct spa_dict *conf, - const char *section, struct pw_properties *props) +int pw_conf_section_update_props_rules(const struct spa_dict *conf, + const struct spa_dict *context, const char *section, + struct pw_properties *props) { struct data data = { .props = props }; int res; const char *str; + char key[128]; res = pw_conf_section_for_each(conf, section, update_props, &data); str = pw_properties_get(props, "config.ext"); if (res == 0 && str != NULL) { - char key[128]; snprintf(key, sizeof(key), "%s.%s", section, str); res = pw_conf_section_for_each(conf, key, update_props, &data); } + if (res == 0 && context != NULL) { + snprintf(key, sizeof(key), "%s.rules", section); + res = pw_conf_section_match_rules(conf, key, context, update_props, &data); + } return res == 0 ? data.count : res; } +SPA_EXPORT +int pw_conf_section_update_props(const struct spa_dict *conf, + const char *section, struct pw_properties *props) +{ + return pw_conf_section_update_props_rules(conf, NULL, section, props); +} + static bool valid_conf_name(const char *str) { return spa_streq(str, "null") || spa_strendswith(str, ".conf"); @@ -1208,8 +1220,8 @@ SPA_EXPORT int pw_context_conf_update_props(struct pw_context *context, const char *section, struct pw_properties *props) { - return pw_conf_section_update_props(&context->conf->dict, - section, props); + return pw_conf_section_update_props_rules(&context->conf->dict, + &context->properties->dict, section, props); } SPA_EXPORT diff --git a/src/pipewire/conf.h b/src/pipewire/conf.h index 6031f3c44..66898b1f9 100644 --- a/src/pipewire/conf.h +++ b/src/pipewire/conf.h @@ -24,6 +24,10 @@ int pw_conf_save_state(const char *prefix, const char *name, const struct pw_pro int pw_conf_section_update_props(const struct spa_dict *conf, const char *section, struct pw_properties *props); +int pw_conf_section_update_props_rules(const struct spa_dict *conf, + const struct spa_dict *context, const char *section, + struct pw_properties *props); + int pw_conf_section_for_each(const struct spa_dict *conf, const char *section, int (*callback) (void *data, const char *location, const char *section, const char *str, size_t len), diff --git a/src/pipewire/context.c b/src/pipewire/context.c index eec4f2225..a94119af9 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -180,7 +180,7 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, struct pw_context *this; const char *lib, *str; void *dbus_iface = NULL; - uint32_t n_support; + uint32_t n_support, vm_type; struct pw_properties *pr, *conf; struct spa_cpu *cpu; int res = 0; @@ -244,6 +244,10 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, n_support = pw_get_support(this->support, SPA_N_ELEMENTS(this->support) - 6); cpu = spa_support_find(this->support, n_support, SPA_TYPE_INTERFACE_CPU); + vm_type = SPA_CPU_VM_NONE; + if (cpu != NULL && (vm_type = spa_cpu_get_vm_type(cpu)) != SPA_CPU_VM_NONE) + pw_properties_set(properties, "cpu.vm.name", spa_cpu_vm_type_to_string(vm_type)); + res = pw_context_conf_update_props(this, "context.properties", properties); pw_log_info("%p: parsed %d context.properties items", this, res); @@ -253,7 +257,9 @@ struct pw_context *pw_context_new(struct pw_loop *main_loop, } if ((str = pw_properties_get(properties, "vm.overrides")) != NULL) { - if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE) + pw_log_warn("vm.overrides in context.properties are deprecated, " + "use context.properties.rules instead"); + if (vm_type != SPA_CPU_VM_NONE) pw_properties_update_string(properties, str, strlen(str)); pw_properties_set(properties, "vm.overrides", NULL); }