module.c 7.45 KB
Newer Older
1
2
3
/* PipeWire
 *
 * Copyright © 2020 Georges Basile Stavracas Neto
4
 * Copyright © 2021 Wim Taymans <wim.taymans@gmail.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

26
#include "module.h"
27

28
29
#include <spa/utils/string.h>

30
31
static int module_unload(struct client *client, struct module *module);

32
static void on_module_unload(void *obj, void *data, int res, uint32_t id)
33
{
34
	struct module *module = obj;
35
36
37
	module_unload(NULL, module);
}

38
39
void module_schedule_unload(struct module *module)
{
40
41
	struct impl *impl = module->impl;
	pw_work_queue_add(impl->work_queue, module, 0, on_module_unload, impl);
42
43
}

44
struct module *module_new(struct impl *impl, const struct module_methods *methods, size_t user_data)
45
{
46
47
48
49
50
	struct module *module;

	module = calloc(1, sizeof(struct module) + user_data);
	if (module == NULL)
		return NULL;
51

52
53
54
	module->impl = impl;
	module->methods = methods;
	spa_hook_list_init(&module->hooks);
55
	module->user_data = SPA_PTROFF(module, sizeof(struct module), void);
56

57
	return module;
58
59
}

60
61
62
static void module_add_listener(struct module *module,
		struct spa_hook *listener,
		const struct module_events *events, void *data)
63
{
64
	spa_hook_list_append(&module->hooks, listener, events, data);
65
66
}

67
static int module_load(struct client *client, struct module *module)
68
{
69
70
71
	pw_log_info("load module id:%u name:%s", module->idx, module->name);
	if (module->methods->load == NULL)
		return -ENOTSUP;
72
73
	/* subscription event is sent when the module does a
	 * module_emit_loaded() */
74
	return module->methods->load(client, module);
75
76
}

77
static void module_free(struct module *module)
78
{
79
80
81
82
	struct impl *impl = module->impl;
	if (module->idx != SPA_ID_INVALID)
		pw_map_remove(&impl->modules, module->idx & INDEX_MASK);

83
	pw_work_queue_cancel(impl->work_queue, module, SPA_ID_INVALID);
84
85
86
87
88
89
	free((char*)module->name);
	free((char*)module->args);
	if (module->props)
		pw_properties_free(module->props);
	free(module);
}
90

91
92
static int module_unload(struct client *client, struct module *module)
{
93
94
	struct impl *impl = module->impl;
	uint32_t module_idx = module->idx;
95
	int res = 0;
96

97
98
99
	/* Note that client can be NULL (when the module is being unloaded
	 * internally and not by a client request */

100
	pw_log_info("unload module id:%u name:%s", module->idx, module->name);
101

102
103
	if (module->methods->unload)
		res = module->methods->unload(client, module);
104

105
	module_free(module);
106
107
108
109
110
111

	broadcast_subscribe_event(impl,
			SUBSCRIPTION_MASK_MODULE,
			SUBSCRIPTION_EVENT_REMOVE | SUBSCRIPTION_EVENT_MODULE,
			module_idx);

112
	return res;
113
114
}

115
/** utils */
116
void module_args_add_props(struct pw_properties *props, const char *str)
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
{
	char *s = strdup(str), *p = s, *e, f;
	const char *k, *v;

	while (*p) {
		e = strchr(p, '=');
		if (e == NULL)
			break;
		*e = '\0';
		k = p;
		p = e+1;

		if (*p == '\"') {
			p++;
			f = '\"';
132
133
134
		} else if (*p == '\'') {
			p++;
			f = '\'';
135
136
137
		} else {
			f = ' ';
		}
138
		v = p;
139
140
141
142
143
144
		for (e = p; *e ; e++) {
			if (*e == f)
				break;
			if (*e == '\\')
				e++;
		}
145
146
147
		p = e;
		if (*e != '\0')
			p++;
148
149
150
151
152
153
		*e = '\0';
		pw_properties_set(props, k, v);
	}
	free(s);
}

154
int module_args_to_audioinfo(struct impl *impl, struct pw_properties *props, struct spa_audio_info_raw *info)
155
156
{
	const char *str;
157
	uint32_t i;
158
159

	/* We don't use any incoming format setting and use our native format */
160
	spa_zero(*info);
161
162
163
164
	info->format = SPA_AUDIO_FORMAT_F32P;

	if ((str = pw_properties_get(props, "channels")) != NULL) {
		info->channels = pw_properties_parse_int(str);
165
166
167
168
		if (info->channels == 0 || info->channels > SPA_AUDIO_MAX_CHANNELS) {
			pw_log_error("invalid channels '%s'", str);
			return -EINVAL;
		}
169
170
171
172
173
174
		pw_properties_set(props, "channels", NULL);
	}
	if ((str = pw_properties_get(props, "channel_map")) != NULL) {
		struct channel_map map;

		channel_map_parse(str, &map);
175
176
177
178
179
180
		if (map.channels == 0 || map.channels > SPA_AUDIO_MAX_CHANNELS) {
			pw_log_error("invalid channel_map '%s'", str);
			return -EINVAL;
		}
		if (info->channels == 0)
			info->channels = map.channels;
181
182
183
184
185
186
187
		if (info->channels != map.channels) {
			pw_log_error("Mismatched channel map");
			return -EINVAL;
		}
		channel_map_to_positions(&map, info->position);
		pw_properties_set(props, "channel_map", NULL);
	} else {
188
189
190
		if (info->channels == 0)
			info->channels = impl->defs.sample_spec.channels;

191
192
193
194
195
196
197
198
		if (info->channels == impl->defs.channel_map.channels) {
			channel_map_to_positions(&impl->defs.channel_map, info->position);
		} else if (info->channels == 1) {
			info->position[0] = SPA_AUDIO_CHANNEL_MONO;
		} else if (info->channels == 2) {
			info->position[0] = SPA_AUDIO_CHANNEL_FL;
			info->position[1] = SPA_AUDIO_CHANNEL_FR;
		} else {
199
200
201
			/* FIXME add more mappings */
			for (i = 0; i < info->channels; i++)
				info->position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
202
203
204
		}
	}

205
206
207
208
209
210
	if ((str = pw_properties_get(props, "rate")) != NULL) {
		info->rate = pw_properties_parse_int(str);
		pw_properties_set(props, "rate", NULL);
	} else {
		info->rate = 0;
	}
211
212
213
	return 0;
}

214
#include "modules/registry.h"
215

216
static const struct module_info module_list[] = {
217
218
	{ "module-ladspa-sink", create_module_ladspa_sink, },
	{ "module-ladspa-source", create_module_ladspa_source, },
219
	{ "module-loopback", create_module_loopback, },
220
221
	{ "module-null-sink", create_module_null_sink, },
	{ "module-native-protocol-tcp", create_module_native_protocol_tcp, },
222
	{ "module-pipe-sink", create_module_pipe_sink, },
223
224
	{ "module-remap-sink", create_module_remap_sink, },
	{ "module-remap-source", create_module_remap_source, },
225
	{ "module-simple-protocol-tcp", create_module_simple_protocol_tcp, },
226
227
	{ "module-tunnel-sink", create_module_tunnel_sink, },
	{ "module-tunnel-source", create_module_tunnel_source, },
228
	{ "module-zeroconf-discover", create_module_zeroconf_discover, },
229
230
	{ NULL, }
};
231

232
static const struct module_info *find_module_info(const char *name)
233
{
234
235
	int i;
	for (i = 0; module_list[i].name != NULL; i++) {
236
		if (spa_streq(module_list[i].name, name))
237
238
239
			return &module_list[i];
	}
	return NULL;
240
241
}

242
static struct module *create_module(struct client *client, const char *name, const char *args)
243
{
244
245
246
247
248
249
	struct impl *impl = client->impl;
	const struct module_info *info;
	struct module *module;

	info = find_module_info(name);
	if (info == NULL) {
250
		errno = ENOENT;
251
252
253
254
255
256
257
258
259
260
261
262
		return NULL;
	}
	module = info->create(impl, args);
	if (module == NULL)
		return NULL;

	module->idx = pw_map_insert_new(&impl->modules, module);
	if (module->idx == SPA_ID_INVALID) {
		module_unload(client, module);
		return NULL;
	}
	module->name = strdup(name);
263
	module->args = args ? strdup(args) : NULL;
264
265
	module->idx |= MODULE_FLAG;
	return module;
266
}