pulse-server: improve module argument checking

Make the module valid_args a structure that includes the argument key,
description and some flags. Use this to enforce mandatory properties
in a more central place.

We should be able to generate the module usage from this as wel later to
have things a bit more structured.
This commit is contained in:
Wim Taymans 2026-05-13 10:21:19 +02:00
parent a5a3eaf2cb
commit e5ff44910e
9 changed files with 122 additions and 68 deletions

View file

@ -170,15 +170,15 @@ void module_args_add_props(struct pw_properties *props, const char *str)
}
}
static bool find_key(const char * const keys[], const char *key)
static bool find_key(const struct module_args args[], const char *key)
{
for (int i = 0; keys[i] != NULL; i++)
if (spa_streq(keys[i], key))
for (int i = 0; args[i].key != NULL; i++)
if (spa_streq(args[i].key, key))
return true;
return false;
}
static int module_args_check(struct pw_properties *props, const char * const valid_args[])
static int module_args_check(struct pw_properties *props, const struct module_args valid_args[])
{
if (valid_args != NULL) {
const struct spa_dict_item *it;
@ -188,6 +188,13 @@ static int module_args_check(struct pw_properties *props, const char * const val
return -EINVAL;
}
}
for (int i = 0; valid_args[i].key != NULL; i++)
if (SPA_FLAG_IS_SET(valid_args[i].flags, MODULE_ARG_MANDATORY) &&
pw_properties_get(props, valid_args[i].key) == NULL) {
pw_log_warn("missing mandatory module argument '%s'", valid_args[i].key);
return -EINVAL;
}
}
return 0;
}

View file

@ -17,6 +17,13 @@ struct client;
struct message;
struct extension;
struct module_args {
const char *key;
const char *description;
#define MODULE_ARG_MANDATORY (1u<<0)
uint32_t flags;
};
struct module_info {
const char *name;
@ -27,7 +34,7 @@ struct module_info {
int (*unload) (struct module *module);
const struct extension *extension;
const char* const *valid_args;
const struct module_args *valid_args;
const struct spa_dict *properties;
size_t data_size;
};

View file

@ -42,6 +42,24 @@ static const char *const pulse_module_options =
"input_ladspaport_map=<comma separated list of input LADSPA port names> "
"output_ladspaport_map=<comma separated list of output LADSPA port names> ";
static const struct module_args valid_args[] = {
{ "sink_name", "name for the sink", },
{ "sink_properties", "properties for the sink", },
{ "sink_input_properties", "properties for the sink input", },
{ "master", "name of sink to filter", },
{ "sink_master", "name of sink to filter", },
{ "format", "sample format", },
{ "rate", "sample rate", },
{ "channels", "number of channels", },
{ "channel_map", "channel map", },
{ "plugin", "LADSPA plugin name", MODULE_ARG_MANDATORY },
{ "label", "LADSPA plugin label", MODULE_ARG_MANDATORY },
{ "control", "comma separated list of input control values", },
{ "input_ladspaport_map", "comma separated list of input LADSPA port names", },
{ "output_ladspaport_map", "comma separated list of output LADSPA port names", },
{ NULL, }
};
#define NAME "ladspa-sink"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -196,7 +214,7 @@ static int module_ladspa_sink_prepare(struct module * const module)
capture_props = pw_properties_new(NULL, NULL);
playback_props = pw_properties_new(NULL, NULL);
if (!capture_props || !playback_props) {
res = -EINVAL;
res = -ENOMEM;
goto out;
}
@ -226,6 +244,7 @@ static int module_ladspa_sink_prepare(struct module * const module)
(str = pw_properties_get(props, "sink_master")) != NULL) {
pw_properties_set(playback_props, PW_KEY_TARGET_OBJECT, str);
pw_properties_set(props, "master", NULL);
pw_properties_set(props, "sink_master", NULL);
}
if (module_args_to_audioinfo_keys(module->impl, props,
@ -255,6 +274,7 @@ out:
DEFINE_MODULE_INFO(module_ladspa_sink) = {
.name = "module-ladspa-sink",
.valid_args = valid_args,
.prepare = module_ladspa_sink_prepare,
.load = module_ladspa_sink_load,
.unload = module_ladspa_sink_unload,

View file

@ -42,6 +42,24 @@ static const char *const pulse_module_options =
"input_ladspaport_map=<comma separated list of input LADSPA port names> "
"output_ladspaport_map=<comma separated list of output LADSPA port names> ";
static const struct module_args valid_args[] = {
{ "source_name", "name for the source", },
{ "source_properties", "properties for the source", },
{ "source_output_properties", "properties for the source output", },
{ "master", "name of source to filter", },
{ "source_master", "name of source to filter", },
{ "format", "sample format", },
{ "rate", "sample rate", },
{ "channels", "number of channels", },
{ "channel_map", "channel map", },
{ "plugin", "LADSPA plugin name", MODULE_ARG_MANDATORY },
{ "label", "LADSPA plugin label", MODULE_ARG_MANDATORY },
{ "control", "comma separated list of input control values", },
{ "input_ladspaport_map", "comma separated list of input LADSPA port names", },
{ "output_ladspaport_map", "comma separated list of output LADSPA port names", },
{ NULL, }
};
#define NAME "ladspa-source"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -263,6 +281,7 @@ out:
DEFINE_MODULE_INFO(module_ladspa_source) = {
.name = "module-ladspa-source",
.valid_args = valid_args,
.prepare = module_ladspa_source_prepare,
.load = module_ladspa_source_load,
.unload = module_ladspa_source_unload,

View file

@ -37,6 +37,19 @@ static const char *const pulse_module_options =
"local_control_port=<local receiver port for control packets> "
;
static const struct module_args valid_args[] = {
{ "sink", "name for the sink", },
{ "sink_input_properties", "properties for the sink_input", },
{ "resampler_profile", "empty|high|medium|low", },
{ "fec_code", "empty|disable|rs8m|ldpc", },
{ "sess_latency_msec", "target network latency in milliseconds", },
{ "local_ip", "local receiver ip", },
{ "local_source_port", "local receiver port for source packets", },
{ "local_repair_port", "local receiver port for repair packets", },
{ "local_control_port", "local receiver port for control packets", },
{ NULL, },
};
#define NAME "roc-sink-input"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -199,19 +212,6 @@ out:
return res;
}
static const char* const valid_args[] = {
"sink",
"sink_input_properties",
"resampler_profile",
"fec_code",
"sess_latency_msec",
"local_ip",
"local_source_port",
"local_repair_port",
"local_control_port",
NULL
};
DEFINE_MODULE_INFO(module_roc_sink_input) = {
.name = "module-roc-sink-input",
.valid_args = valid_args,

View file

@ -35,6 +35,17 @@ static const char *const pulse_module_options =
"remote_control_port=<remote receiver port for control packets> "
;
static const struct module_args valid_args[] = {
{ "sink_name", "name for the sink", },
{ "sink_properties", "properties for the sink", },
{ "fec_code", "empty|disable|rs8m|ldpc", },
{ "remote_ip", "remote receiver ip", },
{ "remote_source_port", "remote receiver port for source packets", },
{ "remote_repair_port", "remote receiver port for repair packets", },
{ "remote_control_port", "remote receiver port for control packets", },
{ NULL, },
};
#define NAME "roc-sink"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -117,16 +128,6 @@ static int module_roc_sink_unload(struct module *module)
return 0;
}
static const char* const valid_args[] = {
"sink_name",
"sink_properties",
"fec_code",
"remote_ip",
"remote_source_port",
"remote_repair_port",
"remote_control_port",
NULL
};
static const struct spa_dict_item module_roc_sink_info[] = {
{ PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
{ PW_KEY_MODULE_DESCRIPTION, "roc sink" },

View file

@ -37,6 +37,19 @@ static const char *const pulse_module_options =
"local_control_port=<local receiver port for control packets> "
;
static const struct module_args valid_args[] = {
{ "source_name", "name for the source", },
{ "source_properties", "properties for the source", },
{ "resampler_profile", "empty|high|medium|low", },
{ "fec_code", "empty|disable|rs8m|ldpc", },
{ "sess_latency_msec", "target network latency in milliseconds", },
{ "local_ip", "local receiver ip", },
{ "local_source_port", "local receiver port for source packets", },
{ "local_repair_port", "local receiver port for repair packets", },
{ "local_control_port", "local receiver port for control packets", },
{ NULL, },
};
#define NAME "roc-source"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -119,19 +132,6 @@ static int module_roc_source_unload(struct module *module)
return 0;
}
static const char* const valid_args[] = {
"source_name",
"source_properties",
"resampler_profile",
"fec_code",
"sess_latency_msec",
"local_ip",
"local_source_port",
"local_repair_port",
"local_control_port",
NULL
};
static const struct spa_dict_item module_roc_source_info[] = {
{ PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" },
{ PW_KEY_MODULE_DESCRIPTION, "roc source" },

View file

@ -30,6 +30,14 @@ static const char *const pulse_module_options =
"latency_msec=<latency in ms> "
"stream_properties=<properties for the stream> ";
static const struct module_args valid_args[] = {
{ "sink", "name of the sink", },
{ "sap_address", "multicast address to listen on", },
{ "latency_msec", "latency in ms", },
{ "stream_properties", "properties for the stream", },
{ NULL, }
};
#define NAME "rtp-recv"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -168,14 +176,6 @@ out:
return res;
}
static const char* const valid_args[] = {
"sink",
"sap_address",
"latency_msec",
"stream_properties",
NULL
};
DEFINE_MODULE_INFO(module_rtp_recv) = {
.name = "module-rtp-recv",
.valid_args = valid_args,

View file

@ -40,6 +40,24 @@ static const char *const pulse_module_options =
"stream_properties=<properties for the stream> "
"enable_opus=<enable OPUS codec>";
static const struct module_args valid_args[] = {
{ "source", "name of the source", },
{ "format", "sample format", },
{ "channels", "number of channels", },
{ "rate", "sample rate", },
{ "destination_ip", "destination IP address", },
{ "source_ip", "source IP address", },
{ "port", "port number", },
{ "mtu", "maximum transfer unit", },
{ "loop", "loopback to local host", },
{ "ttl", "ttl value", },
{ "inhibit_auto_suspend", "always|never|only_with_non_monitor_sources", },
{ "stream_name", "name of the stream", },
{ "stream_properties", "properties for the stream", },
{ "enable_opus", "enable OPUS codec", },
{ NULL, },
};
#define NAME "rtp-send"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
@ -266,24 +284,6 @@ out:
return res;
}
static const char* const valid_args[] = {
"source",
"format",
"channels",
"rate",
"destination_ip",
"source_ip",
"port",
"mtu",
"loop",
"ttl",
"inhibit_auto_suspend",
"stream_name",
"stream_properties",
"enable_opus",
NULL
};
DEFINE_MODULE_INFO(module_rtp_send) = {
.name = "module-rtp-send",
.valid_args = valid_args,