pulse-server: move extension to modules

Add extension support to modules. This is a list of extension commands
that can be performed on the module.

Remove the custom registry of extensions and make proper modules that
implement the extensions.

This is more in line with what pulseaudio does. The advantage is that the
modules actually show up in the module list and that we can use the
module user_data to implement the extension later.
This commit is contained in:
Wim Taymans 2024-01-23 12:22:45 +01:00
parent 89077d16ec
commit d71fb40989
13 changed files with 269 additions and 102 deletions

View file

@ -63,6 +63,9 @@ context.exec = [
# ( flags = [ nofail ] )
pulse.cmd = [
{ cmd = "load-module" args = "module-always-sink" flags = [ ] }
{ cmd = "load-module" args = "module-device-manager" flags = [ ] }
{ cmd = "load-module" args = "module-device-restore" flags = [ ] }
{ cmd = "load-module" args = "module-stream-restore" flags = [ ] }
#{ cmd = "load-module" args = "module-switch-on-connect" }
#{ cmd = "load-module" args = "module-gsettings" flags = [ nofail ] }
]

View file

@ -342,9 +342,6 @@ pipewire_module_protocol_pulse_sources = [
'module-protocol-pulse/collect.c',
'module-protocol-pulse/cmd.c',
'module-protocol-pulse/extension.c',
'module-protocol-pulse/extensions/ext-device-manager.c',
'module-protocol-pulse/extensions/ext-device-restore.c',
'module-protocol-pulse/extensions/ext-stream-restore.c',
'module-protocol-pulse/format.c',
'module-protocol-pulse/manager.c',
'module-protocol-pulse/message.c',
@ -366,6 +363,8 @@ pipewire_module_protocol_pulse_sources = [
'module-protocol-pulse/modules/module-alsa-source.c',
'module-protocol-pulse/modules/module-always-sink.c',
'module-protocol-pulse/modules/module-combine-sink.c',
'module-protocol-pulse/modules/module-device-manager.c',
'module-protocol-pulse/modules/module-device-restore.c',
'module-protocol-pulse/modules/module-echo-cancel.c',
'module-protocol-pulse/modules/module-jackdbus-detect.c',
'module-protocol-pulse/modules/module-ladspa-sink.c',
@ -384,6 +383,7 @@ pipewire_module_protocol_pulse_sources = [
'module-protocol-pulse/modules/module-rtp-recv.c',
'module-protocol-pulse/modules/module-rtp-send.c',
'module-protocol-pulse/modules/module-simple-protocol-tcp.c',
'module-protocol-pulse/modules/module-stream-restore.c',
'module-protocol-pulse/modules/module-switch-on-connect.c',
'module-protocol-pulse/modules/module-tunnel-sink.c',
'module-protocol-pulse/modules/module-tunnel-source.c',

View file

@ -37,7 +37,6 @@
#define SCACHE_ENTRY_SIZE_MAX (1024*1024*16)
#define MODULE_INDEX_MASK 0xfffffffu
#define MODULE_EXTENSION_FLAG (1u << 28)
#define MODULE_FLAG (1u << 29)
#define DEFAULT_SINK "@DEFAULT_SINK@"

View file

@ -5,21 +5,45 @@
#include <spa/utils/defs.h>
#include <spa/utils/string.h>
#include "client.h"
#include "defs.h"
#include "extension.h"
#include "extensions/registry.h"
#include "message.h"
#include "module.h"
static const struct extension extensions[] = {
{ "module-stream-restore", 0 | MODULE_EXTENSION_FLAG, do_extension_stream_restore, },
{ "module-device-restore", 1 | MODULE_EXTENSION_FLAG, do_extension_device_restore, },
{ "module-device-manager", 2 | MODULE_EXTENSION_FLAG, do_extension_device_manager, },
};
const struct extension *extension_find(uint32_t index, const char *name)
static const struct extension *find_extension_command(struct module *module, uint32_t command)
{
SPA_FOR_EACH_ELEMENT_VAR(extensions, ext) {
if (index == ext->index || spa_streq(name, ext->name))
return ext;
uint32_t i;
if (module->info->extension == NULL)
return NULL;
for (i = 0; module->info->extension[i].name; i++) {
if (module->info->extension[i].command == command)
return &module->info->extension[i];
}
return NULL;
}
int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m)
{
uint32_t command;
const struct extension *ext;
int res;
if ((res = message_get(m,
TAG_U32, &command,
TAG_INVALID)) < 0)
return -EPROTO;
ext = find_extension_command(module, command);
if (ext == NULL)
return -ENOTSUP;
if (ext->process == NULL)
return -EPROTO;
pw_log_info("client %p [%s]: %s %s tag:%u",
client, client->name, module->info->name, ext->name, tag);
return ext->process(module, client, command, tag, m);
}

View file

@ -9,19 +9,15 @@
struct client;
struct message;
struct extension_sub {
const char *name;
uint32_t command;
int (*process)(struct client *client, uint32_t command, uint32_t tag, struct message *m);
};
struct module;
struct extension {
const char *name;
uint32_t index;
int (*process)(struct client *client, uint32_t tag, struct message *m);
uint32_t command;
int (*process)(struct module *module, struct client *client, uint32_t command,
uint32_t tag, struct message *m);
};
const struct extension *extension_find(uint32_t index, const char *name);
int extension_process(struct module *module, struct client *client, uint32_t tag, struct message *m);
#endif /* PULSE_SERVER_EXTENSION_H */

View file

@ -1,8 +0,0 @@
#include <errno.h>
#include "registry.h"
int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m)
{
return -ENOTSUP;
}

View file

@ -1,13 +0,0 @@
#ifndef PIPEWIRE_PULSE_EXTENSION_REGISTRY_H
#define PIPEWIRE_PULSE_EXTENSION_REGISTRY_H
#include <stdint.h>
struct client;
struct message;
int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m);
int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m);
int do_extension_device_manager(struct client *client, uint32_t tag, struct message *m);
#endif /* PIPEWIRE_PULSE_EXTENSION_REGISTRY_H */

View file

@ -375,3 +375,19 @@ error_free:
return NULL;
}
struct module *module_lookup(struct impl *impl, uint32_t index, const char *name)
{
union pw_map_item *item;
if (index != SPA_ID_INVALID)
return pw_map_lookup(&impl->modules, index);
pw_array_for_each(item, &impl->modules.items) {
struct module *m = item->data;
if (!pw_map_item_is_free(item) &&
spa_streq(m->info->name, name))
return m;
}
return NULL;
}

View file

@ -13,6 +13,9 @@
struct module;
struct pw_properties;
struct client;
struct message;
struct extension;
struct module_info {
const char *name;
@ -23,6 +26,7 @@ struct module_info {
int (*load) (struct module *module);
int (*unload) (struct module *module);
const struct extension *extension;
const char* const *valid_args;
const struct spa_dict *properties;
size_t data_size;
@ -64,6 +68,8 @@ int module_load(struct module *module);
int module_unload(struct module *module);
void module_schedule_unload(struct module *module);
struct module *module_lookup(struct impl *impl, uint32_t index, const char *name);
void module_add_listener(struct module *module,
struct spa_hook *listener,
const struct module_events *events, void *data);

View file

@ -0,0 +1,63 @@
/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
/* SPDX-License-Identifier: MIT */
#include <pipewire/pipewire.h>
#include "../module.h"
/** \page page_pulse_module_device_manager Device manager extension
*
* ## Module Name
*
* `module-device-manager`
*
* ## Module Options
*
* @pulse_module_options@
*/
static const char *const pulse_module_options =
"do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
"on_hotplug=<When new device becomes available, recheck streams?> "
"on_rescue=<When device becomes unavailable, recheck streams?>";
#define NAME "device-manager"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
struct module_device_manager_data {
struct module *module;
};
static const struct spa_dict_item module_device_manager_info[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Keep track of devices (and their descriptions) both past and present and prioritise by role" },
{ PW_KEY_MODULE_USAGE, pulse_module_options },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
static int module_device_manager_prepare(struct module * const module)
{
PW_LOG_TOPIC_INIT(mod_topic);
struct module_device_manager_data * const data = module->user_data;
data->module = module;
return 0;
}
static int module_device_manager_load(struct module *module)
{
return 0;
}
DEFINE_MODULE_INFO(module_device_manager) = {
.name = "module-device-manager",
.load_once = true,
.prepare = module_device_manager_prepare,
.load = module_device_manager_load,
.properties = &SPA_DICT_INIT_ARRAY(module_device_manager_info),
.data_size = sizeof(struct module_device_manager_data),
};

View file

@ -1,7 +1,22 @@
/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */
/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
/* SPDX-License-Identifier: MIT */
#include <pipewire/pipewire.h>
#include "../module.h"
/** \page page_pulse_module_device_restore Device restore extension
*
* ## Module Name
*
* `module-device-restore`
*
* ## Module Options
*
* @pulse_module_options@
*/
#define EXT_DEVICE_RESTORE_VERSION 1
#include <stdbool.h>
@ -30,16 +45,33 @@
#include "../message.h"
#include "../reply.h"
#include "../volume.h"
#include "registry.h"
PW_LOG_TOPIC_EXTERN(pulse_ext_dev_restore);
#undef PW_LOG_TOPIC_DEFAULT
#define PW_LOG_TOPIC_DEFAULT pulse_ext_dev_restore
static const char *const pulse_module_options =
"restore_port=<Save/restore port?> "
"restore_volume=<Save/restore volumes?> "
"restore_muted=<Save/restore muted states?> "
"restore_formats=<Save/restore saved formats?>";
#define NAME "device-restore"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
struct module_device_restore_data {
struct module *module;
};
static const struct spa_dict_item module_device_restore_info[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute state of devices" },
{ PW_KEY_MODULE_USAGE, pulse_module_options },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
#define DEVICE_TYPE_SINK 0
#define DEVICE_TYPE_SOURCE 1
static int do_extension_device_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_device_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct message *reply;
@ -51,7 +83,7 @@ static int do_extension_device_restore_test(struct client *client, uint32_t comm
return client_queue_message(client, reply);
}
static int do_extension_device_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_device_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
return reply_simple_ack(client, tag);
}
@ -103,7 +135,7 @@ static int do_sink_read_format(void *data, struct pw_manager_object *o)
return 0;
}
static int do_extension_device_restore_read_formats_all(struct client *client,
static int do_extension_device_restore_read_formats_all(struct module *module, struct client *client,
uint32_t command, uint32_t tag, struct message *m)
{
struct pw_manager *manager = client->manager;
@ -118,7 +150,7 @@ static int do_extension_device_restore_read_formats_all(struct client *client,
return client_queue_message(client, data.reply);
}
static int do_extension_device_restore_read_formats(struct client *client,
static int do_extension_device_restore_read_formats(struct module *module, struct client *client,
uint32_t command, uint32_t tag, struct message *m)
{
struct pw_manager *manager = client->manager;
@ -217,7 +249,7 @@ static int set_node_codecs(struct pw_manager_object *o, uint32_t n_codecs, uint3
}
static int do_extension_device_restore_save_formats(struct client *client,
static int do_extension_device_restore_save_formats(struct module *module, struct client *client,
uint32_t command, uint32_t tag, struct message *m)
{
struct pw_manager *manager = client->manager;
@ -285,32 +317,37 @@ static int do_extension_device_restore_save_formats(struct client *client,
return reply_simple_ack(client, tag);
}
static const struct extension_sub ext_device_restore[] = {
static const struct extension module_device_restore_extension[] = {
{ "TEST", 0, do_extension_device_restore_test, },
{ "SUBSCRIBE", 1, do_extension_device_restore_subscribe, },
{ "EVENT", 2, },
{ "READ_FORMATS_ALL", 3, do_extension_device_restore_read_formats_all, },
{ "READ_FORMATS", 4, do_extension_device_restore_read_formats, },
{ "SAVE_FORMATS", 5, do_extension_device_restore_save_formats, },
{ NULL },
};
int do_extension_device_restore(struct client *client, uint32_t tag, struct message *m)
static int module_device_restore_prepare(struct module * const module)
{
uint32_t command;
int res;
PW_LOG_TOPIC_INIT(mod_topic);
if ((res = message_get(m,
TAG_U32, &command,
TAG_INVALID)) < 0)
return -EPROTO;
struct module_device_restore_data * const data = module->user_data;
data->module = module;
if (command >= SPA_N_ELEMENTS(ext_device_restore))
return -ENOTSUP;
if (ext_device_restore[command].process == NULL)
return -EPROTO;
pw_log_info("client %p [%s]: EXT_DEVICE_RESTORE_%s tag:%u",
client, client->name, ext_device_restore[command].name, tag);
return ext_device_restore[command].process(client, command, tag, m);
return 0;
}
static int module_device_restore_load(struct module *module)
{
return 0;
}
DEFINE_MODULE_INFO(module_device_restore) = {
.name = "module-device-restore",
.load_once = true,
.prepare = module_device_restore_prepare,
.load = module_device_restore_load,
.extension = module_device_restore_extension,
.properties = &SPA_DICT_INIT_ARRAY(module_device_restore_info),
.data_size = sizeof(struct module_device_restore_data),
};

View file

@ -1,7 +1,46 @@
/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans <wim.taymans@gmail.com> */
/* SPDX-License-Identifier: MIT */
#include <pipewire/pipewire.h>
#include "../module.h"
/** \page page_pulse_module_stream_restore Stream restore extension
*
* ## Module Name
*
* `module-stream-restore`
*
* ## Module Options
*
* @pulse_module_options@
*/
static const char *const pulse_module_options =
"restore_device=<Save/restore sinks/sources?> "
"restore_volume=<Save/restore volumes?> "
"restore_muted=<Save/restore muted states?> "
"on_hotplug=<This argument is obsolete, please remove it from configuration> "
"on_rescue=<This argument is obsolete, please remove it from configuration> "
"fallback_table=<filename>";
#define NAME "stream-restore"
PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
#define PW_LOG_TOPIC_DEFAULT mod_topic
struct module_stream_restore_data {
struct module *module;
};
static const struct spa_dict_item module_stream_restore_info[] = {
{ PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_KEY_MODULE_DESCRIPTION, "Automatically restore the volume/mute/device state of streams" },
{ PW_KEY_MODULE_USAGE, pulse_module_options },
{ PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
};
#define EXT_STREAM_RESTORE_VERSION 1
#include <stdbool.h>
@ -26,13 +65,12 @@
#include "../remap.h"
#include "../reply.h"
#include "../volume.h"
#include "registry.h"
PW_LOG_TOPIC_EXTERN(pulse_ext_stream_restore);
#undef PW_LOG_TOPIC_DEFAULT
#define PW_LOG_TOPIC_DEFAULT pulse_ext_stream_restore
static int do_extension_stream_restore_test(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_stream_restore_test(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct message *reply;
@ -119,7 +157,7 @@ static int key_to_name(const char *key, char *name, size_t maxlen)
}
static int do_extension_stream_restore_read(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_stream_restore_read(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct message *reply;
const struct spa_dict_item *item;
@ -194,7 +232,7 @@ static int do_extension_stream_restore_read(struct client *client, uint32_t comm
return client_queue_message(client, reply);
}
static int do_extension_stream_restore_write(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_stream_restore_write(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
int res;
uint32_t mode;
@ -269,42 +307,47 @@ static int do_extension_stream_restore_write(struct client *client, uint32_t com
return reply_simple_ack(client, tag);
}
static int do_extension_stream_restore_delete(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_stream_restore_delete(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
return reply_simple_ack(client, tag);
}
static int do_extension_stream_restore_subscribe(struct client *client, uint32_t command, uint32_t tag, struct message *m)
static int do_extension_stream_restore_subscribe(struct module *module, struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
return reply_simple_ack(client, tag);
}
static const struct extension_sub ext_stream_restore[] = {
static const struct extension module_stream_restore_extension[] = {
{ "TEST", 0, do_extension_stream_restore_test, },
{ "READ", 1, do_extension_stream_restore_read, },
{ "WRITE", 2, do_extension_stream_restore_write, },
{ "DELETE", 3, do_extension_stream_restore_delete, },
{ "SUBSCRIBE", 4, do_extension_stream_restore_subscribe, },
{ "EVENT", 5, },
{ NULL, },
};
int do_extension_stream_restore(struct client *client, uint32_t tag, struct message *m)
static int module_stream_restore_prepare(struct module * const module)
{
uint32_t command;
int res;
PW_LOG_TOPIC_INIT(mod_topic);
if ((res = message_get(m,
TAG_U32, &command,
TAG_INVALID)) < 0)
return -EPROTO;
struct module_stream_restore_data * const data = module->user_data;
data->module = module;
if (command >= SPA_N_ELEMENTS(ext_stream_restore))
return -ENOTSUP;
if (ext_stream_restore[command].process == NULL)
return -EPROTO;
pw_log_info("client %p [%s]: EXT_STREAM_RESTORE_%s tag:%u",
client, client->name, ext_stream_restore[command].name, tag);
return ext_stream_restore[command].process(client, command, tag, m);
return 0;
}
static int module_stream_restore_load(struct module *module)
{
return 0;
}
DEFINE_MODULE_INFO(module_stream_restore) = {
.name = "module-stream-restore",
.load_once = true,
.prepare = module_stream_restore_prepare,
.load = module_stream_restore_load,
.extension = module_stream_restore_extension,
.properties = &SPA_DICT_INIT_ARRAY(module_stream_restore_info),
.data_size = sizeof(struct module_stream_restore_data),
};

View file

@ -4196,7 +4196,7 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st
if (command == COMMAND_GET_MODULE_INFO && (sel.index & MODULE_FLAG) != 0) {
struct module *module;
module = pw_map_lookup(&impl->modules, sel.index & MODULE_INDEX_MASK);
module = module_lookup(impl, sel.index & MODULE_INDEX_MASK, NULL);
if (module == NULL)
goto error_noentity;
fill_ext_module_info(client, reply, module);
@ -4564,9 +4564,10 @@ static int do_update_stream_sample_rate(struct client *client, uint32_t command,
static int do_extension(struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct impl *impl = client->impl;
uint32_t index;
const char *name;
const struct extension *ext;
struct module *module;
if (message_get(m,
TAG_U32, &index,
@ -4581,11 +4582,11 @@ static int do_extension(struct client *client, uint32_t command, uint32_t tag, s
(index != SPA_ID_INVALID && name != NULL))
return -EINVAL;
ext = extension_find(index, name);
if (ext == NULL)
module = module_lookup(impl, index, name);
if (module == NULL)
return -ENOENT;
return ext->process(client, tag, m);
return extension_process(module, client, tag, m);
}
static int do_set_profile(struct client *client, uint32_t command, uint32_t tag, struct message *m)
@ -5069,7 +5070,7 @@ static int do_unload_module(struct client *client, uint32_t command, uint32_t ta
if ((module_index & MODULE_FLAG) == 0)
return -EPERM;
module = pw_map_lookup(&impl->modules, module_index & MODULE_INDEX_MASK);
module = module_lookup(impl, module_index & MODULE_INDEX_MASK, NULL);
if (module == NULL)
return -ENOENT;