From 96c3ada6f22a604e37abe8b52c3002e6d4e7d23b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 30 Apr 2026 13:23:23 +0200 Subject: [PATCH] JSON: use the json builder instead of memstream and fprintf Use the JSON builder to prepare arguments for modules and metadata instead of custom memopen and fprintf. This makes it easier to ensure the strings are all properly escaped. This removes the use of spa_json_encode_string(), which could return a truncated, non-zero terminated result, which we needed to check everywhere. --- src/modules/module-avb/maap.c | 21 ++--- src/modules/module-jackdbus-detect.c | 14 ++-- src/modules/module-parametric-equalizer.c | 60 +++++++------- src/modules/module-protocol-pulse/format.c | 17 ++-- .../modules/module-always-sink.c | 22 +++--- .../modules/module-combine-sink.c | 54 +++++++------ .../modules/module-echo-cancel.c | 68 ++++++++-------- .../modules/module-jackdbus-detect.c | 26 ++++--- .../modules/module-ladspa-sink.c | 78 +++++++++++-------- .../modules/module-ladspa-source.c | 76 +++++++++++------- .../modules/module-loopback.c | 27 ++++--- .../modules/module-native-protocol-tcp.c | 26 +++---- .../modules/module-pipe-sink.c | 21 ++--- .../modules/module-pipe-source.c | 21 ++--- .../modules/module-raop-discover.c | 16 ++-- .../modules/module-remap-sink.c | 27 ++++--- .../modules/module-remap-source.c | 27 ++++--- .../modules/module-roc-sink-input.c | 21 ++--- .../modules/module-roc-sink.c | 21 ++--- .../modules/module-roc-source.c | 21 ++--- .../modules/module-rtp-recv.c | 35 ++++++--- .../modules/module-rtp-send.c | 49 +++++++----- .../modules/module-simple-protocol-tcp.c | 35 ++++++--- .../modules/module-stream-restore.c | 40 +++++----- .../modules/module-switch-on-connect.c | 26 +++++-- .../modules/module-tunnel-sink.c | 22 +++--- .../modules/module-tunnel-source.c | 22 +++--- .../modules/module-virtual-sink.c | 27 ++++--- .../modules/module-virtual-source.c | 27 ++++--- .../modules/module-x11-bell.c | 40 ++++------ .../modules/module-zeroconf-discover.c | 16 ++-- .../module-protocol-pulse/pulse-server.c | 19 ++++- src/modules/module-protocol-simple.c | 16 ++-- src/modules/module-raop-discover.c | 15 ++-- src/modules/module-rtp-sap.c | 63 +++++++-------- src/modules/module-snapcast-discover.c | 15 ++-- src/modules/module-zeroconf-discover.c | 18 ++--- src/tools/pw-loopback.c | 40 +++++----- 38 files changed, 661 insertions(+), 528 deletions(-) diff --git a/src/modules/module-avb/maap.c b/src/modules/module-avb/maap.c index 866d234c9..b66eb9437 100644 --- a/src/modules/module-avb/maap.c +++ b/src/modules/module-avb/maap.c @@ -4,7 +4,7 @@ #include -#include +#include #include @@ -298,23 +298,26 @@ static int load_state(struct maap *maap) static int save_state(struct maap *maap) { + struct spa_json_builder b; char *ptr; size_t size; - FILE *f; char key[512]; uint32_t count; + int res; - if ((f = open_memstream(&ptr, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &ptr, &size, 0)) < 0) + return res; - fprintf(f, "[ "); - fprintf(f, "{ \"start\": \"%02x:%02x:%02x:%02x:%02x:%02x\", ", + spa_json_builder_array_push(&b, "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_stringf(&b, "start", "%02x:%02x:%02x:%02x:%02x:%02x", maap_base[0], maap_base[1], maap_base[2], maap_base[3], (maap->offset >> 8) & 0xff, maap->offset & 0xff); - fprintf(f, " \"count\": %u } ", maap->count); - fprintf(f, "]"); - fclose(f); + spa_json_builder_object_uint(&b, "count", maap->count); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_close(&b); count = pw_properties_set(maap->props, "maap.addresses", ptr); free(ptr); diff --git a/src/modules/module-jackdbus-detect.c b/src/modules/module-jackdbus-detect.c index 622ca6efe..11ad5bdb3 100644 --- a/src/modules/module-jackdbus-detect.c +++ b/src/modules/module-jackdbus-detect.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -116,22 +117,21 @@ static const struct pw_impl_module_events tunnelmodule_events = { static int load_jack_tunnel(struct impl *impl) { - FILE *f; + struct spa_json_builder b; char *args; size_t size; int res = 0; - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); goto done; } - fprintf(f, "{"); + spa_json_builder_array_push(&b, "{"); if (impl->properties != NULL) - pw_properties_serialize_dict(f, &impl->properties->dict, 0); - fprintf(f, " }"); - fclose(f); + pw_properties_serialize_dict(b.f, &impl->properties->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading module args:'%s'", args); impl->jack_tunnel = pw_context_load_module(impl->context, diff --git a/src/modules/module-parametric-equalizer.c b/src/modules/module-parametric-equalizer.c index d0f2885ef..9f0ef7247 100644 --- a/src/modules/module-parametric-equalizer.c +++ b/src/modules/module-parametric-equalizer.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -162,7 +162,7 @@ static const struct pw_impl_module_events filter_chain_module_events = { static int enhance_properties(struct pw_properties *props, const char *key, ...) { - FILE *f; + struct spa_json_builder b; spa_autoptr(pw_properties) p = NULL; char *args = NULL; const char *str; @@ -187,13 +187,12 @@ static int enhance_properties(struct pw_properties *props, const char *key, ...) } va_end(varargs); - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); return res; } - pw_properties_serialize_dict(f, &p->dict, PW_PROPERTIES_FLAG_ENCLOSE); - fclose(f); + pw_properties_serialize_dict(b.f, &p->dict, PW_PROPERTIES_FLAG_ENCLOSE); + spa_json_builder_close(&b); pw_properties_set(props, key, args); free(args); @@ -202,12 +201,11 @@ static int enhance_properties(struct pw_properties *props, const char *key, ...) static int create_eq_filter(struct impl *impl, const char *filename) { - FILE *f = NULL; + struct spa_json_builder b; const char* str; char *args = NULL; size_t size; int32_t res = 0; - char path[PATH_MAX]; if ((str = pw_properties_get(impl->props, "equalizer.description")) != NULL) { if (pw_properties_get(impl->props, PW_KEY_NODE_DESCRIPTION) == NULL) @@ -216,47 +214,47 @@ static int create_eq_filter(struct impl *impl, const char *filename) pw_properties_set(impl->props, PW_KEY_MEDIA_NAME, str); } - spa_json_encode_string(path, sizeof(path), filename); - pw_properties_setf(impl->props, "filter.graph", - "{" - " nodes = [ " - " { type = builtin name = eq label = param_eq " - " config = { filename = %s } " - " } " - " ] " - "}", path); - enhance_properties(impl->props, "capture.props", PW_KEY_MEDIA_CLASS, "Audio/Sink", NULL); enhance_properties(impl->props, "playback.props", PW_KEY_NODE_PASSIVE, "true", NULL); - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); - goto done; + return res; } - pw_properties_serialize_dict(f, &impl->props->dict, PW_PROPERTIES_FLAG_ENCLOSE); - fclose(f); + + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_push(&b, "filter.graph", "{"); + spa_json_builder_object_push(&b, "nodes", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "type", "builtin"); + spa_json_builder_object_string(&b, "name", "eq"); + spa_json_builder_object_string(&b, "label", "param_eq"); + spa_json_builder_object_push(&b, "config", "{"); + spa_json_builder_object_string(&b, "filename", filename); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_pop(&b, "}"); + pw_properties_serialize_dict(b.f, &impl->props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading new module-filter-chain with args: %s", args); impl->eq_module = pw_context_load_module(impl->context, "libpipewire-module-filter-chain", args, NULL); + free(args); + if (!impl->eq_module) { - res = -errno; pw_log_error("Can't load module: %m"); - goto done; + return -errno; } pw_log_info("loaded new module-filter-chain"); pw_impl_module_add_listener(impl->eq_module, &impl->eq_module_listener, &filter_chain_module_events, impl); - - res = 0; - -done: - free(args); - return res; + return 0; } static void core_error(void *data, uint32_t id, int seq, int res, const char *message) diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 99e8919c7..56f164469 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include "format.h" @@ -703,18 +703,19 @@ static int add_int(struct format_info *info, const char *k, struct spa_pod *para break; case SPA_CHOICE_Enum: { + struct spa_json_builder b; char *ptr; size_t size; - FILE *f; + int res; - if ((f = open_memstream(&ptr, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &ptr, &size, 0)) < 0) + return res; - fprintf(f, "["); + spa_json_builder_array_push(&b, "["); for (i = 1; i < n_values; i++) - fprintf(f, "%s %d", i == 1 ? "" : ",", values[i]); - fprintf(f, " ]"); - fclose(f); + spa_json_builder_array_int(&b, values[i]); + spa_json_builder_pop(&b, "]"); + spa_json_builder_close(&b); pw_properties_set(info->props, k, ptr); free(ptr); diff --git a/src/modules/module-protocol-pulse/modules/module-always-sink.c b/src/modules/module-protocol-pulse/modules/module-always-sink.c index f51c39df8..a57c263af 100644 --- a/src/modules/module-protocol-pulse/modules/module-always-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-always-sink.c @@ -2,7 +2,7 @@ /* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ /* SPDX-License-Identifier: MIT */ -#include +#include #include #include "../module.h" @@ -48,22 +48,20 @@ static const struct pw_impl_module_events module_events = { static int module_always_sink_load(struct module *module) { struct module_always_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; const char *str; - char encoded[1024]; size_t size; + int res; - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - if ((str = pw_properties_get(module->props, "sink_name")) != NULL) { - spa_json_encode_string(encoded, sizeof(encoded), str); - fprintf(f, " sink.name = %s", encoded); - } - fprintf(f, " }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + if ((str = pw_properties_get(module->props, "sink_name")) != NULL) + spa_json_builder_object_string(&b, "sink.name", str); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-fallback-sink", diff --git a/src/modules/module-protocol-pulse/modules/module-combine-sink.c b/src/modules/module-protocol-pulse/modules/module-combine-sink.c index b19d0b552..809dd7bd0 100644 --- a/src/modules/module-protocol-pulse/modules/module-combine-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-combine-sink.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -155,8 +156,9 @@ static const struct pw_impl_module_events module_events = { static int module_combine_sink_load(struct module *module) { struct module_combine_sink_data *data = module->user_data; + struct spa_json_builder b; uint32_t i; - FILE *f; + int res; char *args; size_t size; @@ -169,33 +171,41 @@ static int module_combine_sink_load(struct module *module) pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->props->dict, 0); - fprintf(f, " combine.props = {"); - pw_properties_serialize_dict(f, &data->combine_props->dict, 0); - fprintf(f, " } stream.props = {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } stream.rules = ["); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->props->dict, 0); + spa_json_builder_object_push(&b, "combine.props", "{"); + pw_properties_serialize_dict(b.f, &data->combine_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "stream.rules", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_push(&b, "matches", "["); if (data->sink_names == NULL) { - fprintf(f, " { matches = [ { media.class = \"Audio/Sink\" } ]"); - fprintf(f, " actions = { create-stream = { } } }"); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "media.class", "Audio/Sink"); + spa_json_builder_pop(&b, "}"); } else { for (i = 0; data->sink_names[i] != NULL; i++) { - char name[1024]; - if (spa_json_encode_string(name, sizeof(name), data->sink_names[i]) >= (int)sizeof(name)) - continue; - - fprintf(f, " { matches = [ { media.class = \"Audio/Sink\" "); - fprintf(f, " node.name = %s } ]", name); - fprintf(f, " actions = { create-stream = { } } }"); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "media.class", "Audio/Sink"); + spa_json_builder_object_string(&b, "node.name", data->sink_names[i]); + spa_json_builder_pop(&b, "}"); } } - fprintf(f, " ]"); - fprintf(f, "}"); - fclose(f); + spa_json_builder_pop(&b, "]"); + spa_json_builder_object_push(&b, "actions", "{"); + spa_json_builder_object_push(&b, "create-stream", "{"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-combine-stream", diff --git a/src/modules/module-protocol-pulse/modules/module-echo-cancel.c b/src/modules/module-protocol-pulse/modules/module-echo-cancel.c index b3a11c339..ee1eb1671 100644 --- a/src/modules/module-protocol-pulse/modules/module-echo-cancel.c +++ b/src/modules/module-protocol-pulse/modules/module-echo-cancel.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -87,33 +87,38 @@ static const struct pw_impl_module_events module_events = { static int module_echo_cancel_load(struct module *module) { struct module_echo_cancel_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->sink_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " aec.args = {"); - pw_properties_serialize_dict(f, &data->props->dict, 0); - fprintf(f, " }"); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } source.props = {"); - pw_properties_serialize_dict(f, &data->source_props->dict, 0); - fprintf(f, " } sink.props = {"); - pw_properties_serialize_dict(f, &data->sink_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "aec.args", "{"); + pw_properties_serialize_dict(b.f, &data->props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "source.props", "{"); + pw_properties_serialize_dict(b.f, &data->source_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "sink.props", "{"); + pw_properties_serialize_dict(b.f, &data->sink_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-echo-cancel", @@ -176,40 +181,41 @@ static int parse_point(const char **point, float f[3]) static int rename_geometry(struct pw_properties *props, const char *pa_key, const char *pw_key) { const char *str; - int i = 0, len; + int i = 0, len, res; char *args; size_t size; - FILE *f; + struct spa_json_builder b; if ((str = pw_properties_get(props, pa_key)) == NULL) return 0; pw_log_info("geometry: %s", str); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "["); + spa_json_builder_array_push(&b, "["); while (true) { float p[3]; - char ps0[64], ps1[64], ps2[64]; if ((len = parse_point(&str, p)) < 0) break; pw_log_info("Got mic #%d position: (%g, %g, %g)", i, p[0], p[1], p[2]); - fprintf(f, "%s [ %s, %s, %s ]", i == 0 ? "" : ",", - spa_dtoa(ps0, sizeof(ps0), p[0]), - spa_dtoa(ps1, sizeof(ps1), p[1]), - spa_dtoa(ps2, sizeof(ps2), p[2])); + spa_json_builder_array_push(&b, "["); + spa_json_builder_array_double(&b, p[0]); + spa_json_builder_array_double(&b, p[1]); + spa_json_builder_array_double(&b, p[2]); + spa_json_builder_pop(&b, "]"); + str += len; if (*str != ',') break; str++; i++; } - fprintf(f, " ]"); - fclose(f); + spa_json_builder_pop(&b, "]"); + spa_json_builder_close(&b); pw_properties_set(props, pw_key, args); free(args); diff --git a/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c b/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c index c015673f9..cf07a7bcb 100644 --- a/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c +++ b/src/modules/module-protocol-pulse/modules/module-jackdbus-detect.c @@ -3,6 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -70,26 +71,29 @@ static const struct pw_impl_module_events module_events = { static int module_jackdbus_detect_load(struct module *module) { struct module_jackdbus_detect_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->sink_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->props->dict, 0); - fprintf(f, " source.props = {"); - pw_properties_serialize_dict(f, &data->source_props->dict, 0); - fprintf(f, " } sink.props = {"); - pw_properties_serialize_dict(f, &data->sink_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->props->dict, 0); + spa_json_builder_object_push(&b, "source.props", "{"); + pw_properties_serialize_dict(b.f, &data->source_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "sink.props", "{"); + pw_properties_serialize_dict(b.f, &data->sink_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-jackdbus-detect", diff --git a/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c b/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c index 4c9f33c47..049433add 100644 --- a/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-ladspa-sink.c @@ -3,7 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include -#include +#include #include #include @@ -73,11 +73,11 @@ static const struct pw_impl_module_events module_events = { static int module_ladspa_sink_load(struct module *module) { struct module_ladspa_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; const char *str, *plugin, *label; - char encoded_plugin[1024], encoded_label[1024]; size_t size; + int res; if ((plugin = pw_properties_get(module->props, "plugin")) == NULL) return -EINVAL; @@ -89,43 +89,59 @@ static int module_ladspa_sink_load(struct module *module) pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " filter.graph = {"); - fprintf(f, " nodes = [ { "); - spa_json_encode_string(encoded_plugin, sizeof(encoded_plugin), plugin); - spa_json_encode_string(encoded_label, sizeof(encoded_label), label); - - fprintf(f, " type = ladspa "); - fprintf(f, " plugin = %s ", encoded_plugin); - fprintf(f, " label = %s ", encoded_label); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "filter.graph", "{"); + spa_json_builder_object_push(&b, "nodes", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "type", "ladspa"); + spa_json_builder_object_string(&b, "plugin", plugin); + spa_json_builder_object_string(&b, "label", label); if ((str = pw_properties_get(module->props, "control")) != NULL) { + int count = 0; size_t len; const char *s, *state = NULL; - int count = 0; - fprintf(f, " control = {"); + spa_json_builder_object_push(&b, "control", "{"); while ((s = pw_split_walk(str, ", ", &len, &state))) { - fprintf(f, " \"%d\" = %.*s", count, (int)len, s); + char key[16]; + snprintf(key, sizeof(key), "%d", count); + spa_json_builder_object_value_full(&b, false, + key, INT_MAX, s, len); count++; } - fprintf(f, " }"); + spa_json_builder_pop(&b, "}"); } - fprintf(f, " } ]"); - if ((str = pw_properties_get(module->props, "inputs")) != NULL) - fprintf(f, " inputs = [ %s ] ", str); - if ((str = pw_properties_get(module->props, "outputs")) != NULL) - fprintf(f, " outputs = [ %s ] ", str); - fprintf(f, " }"); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + if ((str = pw_properties_get(module->props, "input_ladspaport_map")) != NULL) { + const char *s, *state = NULL; + size_t len; + spa_json_builder_object_push(&b, "inputs", "["); + while ((s = pw_split_walk(str, ", ", &len, &state))) + spa_json_builder_add_simple(&b, NULL, 0, 'S', s, len); + spa_json_builder_pop(&b, "]"); + } + if ((str = pw_properties_get(module->props, "output_ladspaport_map")) != NULL) { + const char *s, *state = NULL; + size_t len; + spa_json_builder_object_push(&b, "outputs", "["); + while ((s = pw_split_walk(str, ", ", &len, &state))) + spa_json_builder_add_simple(&b, NULL, 0, 'S', s, len); + spa_json_builder_pop(&b, "]"); + } + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-filter-chain", diff --git a/src/modules/module-protocol-pulse/modules/module-ladspa-source.c b/src/modules/module-protocol-pulse/modules/module-ladspa-source.c index d4883b551..94b3c428c 100644 --- a/src/modules/module-protocol-pulse/modules/module-ladspa-source.c +++ b/src/modules/module-protocol-pulse/modules/module-ladspa-source.c @@ -3,7 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include -#include +#include #include #include @@ -73,11 +73,11 @@ static const struct pw_impl_module_events module_events = { static int module_ladspa_source_load(struct module *module) { struct module_ladspa_source_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; const char *str, *plugin, *label; - char encoded_plugin[1024], encoded_label[1024]; size_t size; + int res; if ((plugin = pw_properties_get(module->props, "plugin")) == NULL) return -EINVAL; @@ -89,43 +89,59 @@ static int module_ladspa_source_load(struct module *module) pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " filter.graph = {"); - fprintf(f, " nodes = [ { "); - spa_json_encode_string(encoded_plugin, sizeof(encoded_plugin), plugin); - spa_json_encode_string(encoded_label, sizeof(encoded_label), label); - - fprintf(f, " type = ladspa "); - fprintf(f, " plugin = %s ", encoded_plugin); - fprintf(f, " label = %s ", encoded_label); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "filter.graph", "{"); + spa_json_builder_object_push(&b, "nodes", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "type", "ladspa"); + spa_json_builder_object_string(&b, "plugin", plugin); + spa_json_builder_object_string(&b, "label", label); if ((str = pw_properties_get(module->props, "control")) != NULL) { size_t len; const char *s, *state = NULL; int count = 0; - fprintf(f, " control = {"); + spa_json_builder_object_push(&b, "control", "{"); while ((s = pw_split_walk(str, ", ", &len, &state))) { - fprintf(f, " \"%d\" = %.*s", count, (int)len, s); + char key[16]; + snprintf(key, sizeof(key), "%d", count); + spa_json_builder_object_value_full(&b, false, + key, INT_MAX, s, len); count++; } - fprintf(f, " }"); + spa_json_builder_pop(&b, "}"); } - fprintf(f, " } ]"); - if ((str = pw_properties_get(module->props, "inputs")) != NULL) - fprintf(f, " inputs = [ %s ] ", str); - if ((str = pw_properties_get(module->props, "outputs")) != NULL) - fprintf(f, " outputs = [ %s ] ", str); - fprintf(f, " }"); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + if ((str = pw_properties_get(module->props, "input_ladspaport_map")) != NULL) { + const char *s, *state = NULL; + size_t len; + spa_json_builder_object_push(&b, "inputs", "["); + while ((s = pw_split_walk(str, ", ", &len, &state))) + spa_json_builder_add_simple(&b, NULL, 0, 'S', s, len); + spa_json_builder_pop(&b, "]"); + } + if ((str = pw_properties_get(module->props, "output_ladspaport_map")) != NULL) { + const char *s, *state = NULL; + size_t len; + spa_json_builder_object_push(&b, "outputs", "["); + while ((s = pw_split_walk(str, ", ", &len, &state))) + spa_json_builder_add_simple(&b, NULL, 0, 'S', s, len); + spa_json_builder_pop(&b, "]"); + } + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-filter-chain", diff --git a/src/modules/module-protocol-pulse/modules/module-loopback.c b/src/modules/module-protocol-pulse/modules/module-loopback.c index 2ef85fa5e..5808e196e 100644 --- a/src/modules/module-protocol-pulse/modules/module-loopback.c +++ b/src/modules/module-protocol-pulse/modules/module-loopback.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -71,26 +71,29 @@ static const struct pw_impl_module_events module_events = { static int module_loopback_load(struct module *module) { struct module_loopback_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "loopback-%u", module->index); pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-loopback", diff --git a/src/modules/module-protocol-pulse/modules/module-native-protocol-tcp.c b/src/modules/module-protocol-pulse/modules/module-native-protocol-tcp.c index 1d0fbb5ab..071322658 100644 --- a/src/modules/module-protocol-pulse/modules/module-native-protocol-tcp.c +++ b/src/modules/module-protocol-pulse/modules/module-native-protocol-tcp.c @@ -2,7 +2,7 @@ /* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ /* SPDX-License-Identifier: MIT */ -#include +#include #include #include "../module.h" @@ -79,10 +79,10 @@ static int module_native_protocol_tcp_prepare(struct module * const module) struct module_native_protocol_tcp_data * const d = module->user_data; struct pw_properties * const props = module->props; const char *port, *listen, *auth; - char address[1024], encoded[1024]; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; PW_LOG_TOPIC_INIT(mod_topic); @@ -93,20 +93,18 @@ static int module_native_protocol_tcp_prepare(struct module * const module) auth = pw_properties_get(props, "auth-anonymous"); - f = open_memstream(&args, &size); - if (f == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - snprintf(address, sizeof(address), "tcp:%s%s%s", + spa_json_builder_array_push(&b, "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_stringf(&b, "address", "tcp:%s%s%s", listen ? listen : "", listen ? ":" : "", port); - spa_json_encode_string(encoded, sizeof(encoded), address); - - fprintf(f, "[ { "); - fprintf(f, " \"address\": %s ", encoded); if (auth && module_args_parse_bool(auth)) - fprintf(f, " \"client.access\": \"unrestricted\" "); - fprintf(f, "} ]"); - fclose(f); + spa_json_builder_object_string(&b, "client.access", "unrestricted"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_close(&b); pw_properties_set(props, "pulse.tcp", args); free(args); diff --git a/src/modules/module-protocol-pulse/modules/module-pipe-sink.c b/src/modules/module-protocol-pulse/modules/module-pipe-sink.c index 0f910ac6b..c80caecc0 100644 --- a/src/modules/module-protocol-pulse/modules/module-pipe-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-pipe-sink.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -70,22 +71,24 @@ static const struct pw_impl_module_events module_events = { static int module_pipe_sink_load(struct module *module) { struct module_pipesink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " \"stream.props\": {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-pipe-tunnel", diff --git a/src/modules/module-protocol-pulse/modules/module-pipe-source.c b/src/modules/module-protocol-pulse/modules/module-pipe-source.c index ff44f3c38..4e69d1d6b 100644 --- a/src/modules/module-protocol-pulse/modules/module-pipe-source.c +++ b/src/modules/module-protocol-pulse/modules/module-pipe-source.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -69,22 +70,24 @@ static const struct pw_impl_module_events module_events = { static int module_pipe_source_load(struct module *module) { struct module_pipesrc_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " \"stream.props\": {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-pipe-tunnel", diff --git a/src/modules/module-protocol-pulse/modules/module-raop-discover.c b/src/modules/module-protocol-pulse/modules/module-raop-discover.c index 4e1cf8adb..d1c06e183 100644 --- a/src/modules/module-protocol-pulse/modules/module-raop-discover.c +++ b/src/modules/module-protocol-pulse/modules/module-raop-discover.c @@ -3,6 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -54,18 +55,19 @@ static const struct pw_impl_module_events module_events = { static int module_raop_discover_load(struct module *module) { struct module_raop_discover_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); + spa_json_builder_array_push(&b, "{"); if (data->latency_msec > 0) - fprintf(f, " raop.latency.ms = %u ", data->latency_msec); - fprintf(f, "}"); - fclose(f); + spa_json_builder_object_uint(&b, "raop.latency.ms", data->latency_msec); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-raop-discover", diff --git a/src/modules/module-protocol-pulse/modules/module-remap-sink.c b/src/modules/module-protocol-pulse/modules/module-remap-sink.c index 103579fe2..92feb5543 100644 --- a/src/modules/module-protocol-pulse/modules/module-remap-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-remap-sink.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -68,26 +68,29 @@ static const struct pw_impl_module_events module_events = { static int module_remap_sink_load(struct module *module) { struct module_remap_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "remap-sink-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "remap-sink-%u", module->index); pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-loopback", diff --git a/src/modules/module-protocol-pulse/modules/module-remap-source.c b/src/modules/module-protocol-pulse/modules/module-remap-source.c index eaff91b08..d67f150df 100644 --- a/src/modules/module-protocol-pulse/modules/module-remap-source.c +++ b/src/modules/module-protocol-pulse/modules/module-remap-source.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -68,26 +68,29 @@ static const struct pw_impl_module_events module_events = { static int module_remap_source_load(struct module *module) { struct module_remap_source_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "remap-source-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "remap-source-%u", module->index); pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " capture.props = { "); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = { "); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-loopback", diff --git a/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c b/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c index 2200a7bfc..76e6eebba 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-sink-input.c @@ -4,6 +4,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -67,22 +68,24 @@ static const struct pw_impl_module_events module_events = { static int module_roc_sink_input_load(struct module *module) { struct module_roc_sink_input_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->roc_props->dict, 0); - fprintf(f, " source.props = {"); - pw_properties_serialize_dict(f, &data->source_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->roc_props->dict, 0); + spa_json_builder_object_push(&b, "source.props", "{"); + pw_properties_serialize_dict(b.f, &data->source_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-roc-source", diff --git a/src/modules/module-protocol-pulse/modules/module-roc-sink.c b/src/modules/module-protocol-pulse/modules/module-roc-sink.c index 6b03006ab..bdddadda2 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-sink.c @@ -4,6 +4,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -65,22 +66,24 @@ static const struct pw_impl_module_events module_events = { static int module_roc_sink_load(struct module *module) { struct module_roc_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->sink_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->roc_props->dict, 0); - fprintf(f, " sink.props = {"); - pw_properties_serialize_dict(f, &data->sink_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->roc_props->dict, 0); + spa_json_builder_object_push(&b, "sink.props", "{"); + pw_properties_serialize_dict(b.f, &data->sink_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-roc-sink", diff --git a/src/modules/module-protocol-pulse/modules/module-roc-source.c b/src/modules/module-protocol-pulse/modules/module-roc-source.c index 661153d1e..4607ffbd6 100644 --- a/src/modules/module-protocol-pulse/modules/module-roc-source.c +++ b/src/modules/module-protocol-pulse/modules/module-roc-source.c @@ -4,6 +4,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -67,22 +68,24 @@ static const struct pw_impl_module_events module_events = { static int module_roc_source_load(struct module *module) { struct module_roc_source_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->source_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->roc_props->dict, 0); - fprintf(f, " source.props = {"); - pw_properties_serialize_dict(f, &data->source_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->roc_props->dict, 0); + spa_json_builder_object_push(&b, "source.props", "{"); + pw_properties_serialize_dict(b.f, &data->source_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-roc-source", diff --git a/src/modules/module-protocol-pulse/modules/module-rtp-recv.c b/src/modules/module-protocol-pulse/modules/module-rtp-recv.c index f81e19047..fc6e2491c 100644 --- a/src/modules/module-protocol-pulse/modules/module-rtp-recv.c +++ b/src/modules/module-protocol-pulse/modules/module-rtp-recv.c @@ -3,6 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -60,25 +61,35 @@ static const struct pw_impl_module_events module_events = { static int module_rtp_recv_load(struct module *module) { struct module_rtp_recv_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " stream.rules = "); - fprintf(f, "[ { matches = [ { rtp.session = \"~.*\" } ] "), - fprintf(f, " actions = { create-stream = { "); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } } } ] "); - fprintf(f, " }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "stream.rules", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_push(&b, "matches", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "rtp.session", "~.*"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_object_push(&b, "actions", "{"); + spa_json_builder_object_push(&b, "create-stream", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-rtp-sap", diff --git a/src/modules/module-protocol-pulse/modules/module-rtp-send.c b/src/modules/module-protocol-pulse/modules/module-rtp-send.c index aa4015d12..3a6d1d6c6 100644 --- a/src/modules/module-protocol-pulse/modules/module-rtp-send.c +++ b/src/modules/module-protocol-pulse/modules/module-rtp-send.c @@ -3,6 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -87,22 +88,24 @@ static const struct pw_impl_module_events sap_module_events = { static int module_rtp_send_load(struct module *module) { struct module_rtp_send_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " stream.props = {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-rtp-sink", @@ -116,16 +119,26 @@ static int module_rtp_send_load(struct module *module) &data->mod_listener, &module_events, data); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->sap_props->dict, 0); - fprintf(f, " stream.rules = ["); - fprintf(f, " { matches = [ { pulse.module.id = %u } ] ", module->index); - fprintf(f, " actions = { announce-stream = { } } "); - fprintf(f, " } ] }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->sap_props->dict, 0); + spa_json_builder_object_push(&b, "stream.rules", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_push(&b, "matches", "["); + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_uint(&b, "pulse.module.id", module->index); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_object_push(&b, "actions", "{"); + spa_json_builder_object_push(&b, "announce-stream", "{"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "]"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->sap = pw_context_load_module(module->impl->context, "libpipewire-module-rtp-sap", diff --git a/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c b/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c index 852cd87ab..edc52af07 100644 --- a/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c +++ b/src/modules/module-protocol-pulse/modules/module-simple-protocol-tcp.c @@ -2,7 +2,7 @@ /* SPDX-FileCopyrightText: Copyright © 2021 Wim Taymans */ /* SPDX-License-Identifier: MIT */ -#include +#include #include #include @@ -70,17 +70,18 @@ static int module_simple_protocol_tcp_load(struct module *module) { struct module_simple_protocol_tcp_data *data = module->user_data; struct impl *impl = module->impl; + struct spa_json_builder b; char *args; size_t size; - FILE *f; + int res; - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->module_props->dict, 0); - fprintf(f, "}"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->module_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(impl->context, "libpipewire-module-protocol-simple", @@ -171,11 +172,21 @@ static int module_simple_protocol_tcp_prepare(struct module * const module) listen = pw_properties_get(props, "listen"); { - char address[1024], encoded[1024]; - snprintf(address, sizeof(address), "tcp:%s%s%s", + struct spa_json_builder ab; + char *addr; + size_t addr_size; + + if ((res = spa_json_builder_memstream(&ab, &addr, &addr_size, 0)) < 0) + goto out; + + spa_json_builder_array_push(&ab, "["); + spa_json_builder_array_stringf(&ab, "tcp:%s%s%s", listen ? listen : "", listen ? ":" : "", port); - spa_json_encode_string(encoded, sizeof(encoded), address); - pw_properties_setf(module_props, "server.address", "[ %s ]", encoded); + spa_json_builder_pop(&ab, "]"); + spa_json_builder_close(&ab); + + pw_properties_set(module_props, "server.address", addr); + free(addr); } d->module = module; diff --git a/src/modules/module-protocol-pulse/modules/module-stream-restore.c b/src/modules/module-protocol-pulse/modules/module-stream-restore.c index 3304a0dec..a56a8e5f3 100644 --- a/src/modules/module-protocol-pulse/modules/module-stream-restore.c +++ b/src/modules/module-protocol-pulse/modules/module-stream-restore.c @@ -64,7 +64,7 @@ enum { #include #include #include -#include +#include #include #include @@ -262,10 +262,9 @@ static int do_extension_stream_restore_write(struct module *module, struct clien struct volume vol; bool mute = false; uint32_t i; - FILE *f; char *ptr; size_t size; - char key[1024], buf[128]; + char key[1024]; spa_zero(map); spa_zero(vol); @@ -282,35 +281,36 @@ static int do_extension_stream_restore_write(struct module *module, struct clien if (name == NULL || name[0] == '\0') return -EPROTO; - if ((f = open_memstream(&ptr, &size)) == NULL) - return -errno; + { + struct spa_json_builder b; + int bres; - fprintf(f, "{"); - fprintf(f, " \"mute\": %s", mute ? "true" : "false"); + if ((bres = spa_json_builder_memstream(&b, &ptr, &size, 0)) < 0) + return bres; + + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_bool(&b, "mute", mute); if (vol.channels > 0) { - fprintf(f, ", \"volumes\": ["); + spa_json_builder_object_push(&b, "volumes", "["); for (i = 0; i < vol.channels; i++) - fprintf(f, "%s%s", (i == 0 ? " ":", "), - spa_json_format_float(buf, sizeof(buf), vol.values[i])); - fprintf(f, " ]"); + spa_json_builder_array_double(&b, vol.values[i]); + spa_json_builder_pop(&b, "]"); } if (map.channels > 0) { char pos[8]; - fprintf(f, ", \"channels\": ["); + spa_json_builder_object_push(&b, "channels", "["); for (i = 0; i < map.channels; i++) - fprintf(f, "%s\"%s\"", (i == 0 ? " ":", "), + spa_json_builder_array_string(&b, channel_id2name(map.map[i], pos, sizeof(pos))); - fprintf(f, " ]"); + spa_json_builder_pop(&b, "]"); } if (device_name != NULL && device_name[0] && (client->default_source == NULL || !spa_streq(device_name, client->default_source)) && - (client->default_sink == NULL || !spa_streq(device_name, client->default_sink))) { - char target[1024]; - spa_json_encode_string(target, sizeof(target), device_name); - fprintf(f, ", \"target-node\": %s", target); + (client->default_sink == NULL || !spa_streq(device_name, client->default_sink))) + spa_json_builder_object_string(&b, "target-node", device_name); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); } - fprintf(f, " }"); - fclose(f); if (key_from_name(name, key, sizeof(key)) >= 0) { pw_log_debug("%s -> %s: %s", name, key, ptr); if ((res = pw_manager_set_metadata(client->manager, diff --git a/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c b/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c index 55c901d0b..33150d517 100644 --- a/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c +++ b/src/modules/module-protocol-pulse/modules/module-switch-on-connect.c @@ -4,7 +4,7 @@ /* SPDX-License-Identifier: MIT */ #include -#include +#include #include #include @@ -136,13 +136,23 @@ static void manager_added(void *data, struct pw_manager_object *o) pw_log_debug("switching to %s", name); { - char encoded[1024]; - spa_json_encode_string(encoded, sizeof(encoded), name); - pw_manager_set_metadata(d->manager, d->metadata_default, - PW_ID_CORE, - pw_manager_object_is_sink(o) ? METADATA_CONFIG_DEFAULT_SINK - : METADATA_CONFIG_DEFAULT_SOURCE, - "Spa:String:JSON", "{ \"name\" %s }", encoded); + struct spa_json_builder b; + char *val; + size_t val_size; + + if (spa_json_builder_memstream(&b, &val, &val_size, 0) >= 0) { + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "name", name); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); + + pw_manager_set_metadata(d->manager, d->metadata_default, + PW_ID_CORE, + pw_manager_object_is_sink(o) ? METADATA_CONFIG_DEFAULT_SINK + : METADATA_CONFIG_DEFAULT_SOURCE, + "Spa:String:JSON", "%s", val); + free(val); + } } } diff --git a/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c b/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c index 59de3c6c2..7c34b5f6b 100644 --- a/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-tunnel-sink.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -70,22 +70,24 @@ static const struct pw_impl_module_events module_events = { static int module_tunnel_sink_load(struct module *module) { struct module_tunnel_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " stream.props = {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-pulse-tunnel", diff --git a/src/modules/module-protocol-pulse/modules/module-tunnel-source.c b/src/modules/module-protocol-pulse/modules/module-tunnel-source.c index 415f2ddd1..af6a29d87 100644 --- a/src/modules/module-protocol-pulse/modules/module-tunnel-source.c +++ b/src/modules/module-protocol-pulse/modules/module-tunnel-source.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include @@ -70,22 +70,24 @@ static const struct pw_impl_module_events module_events = { static int module_tunnel_source_load(struct module *module) { struct module_tunnel_source_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &module->props->dict, 0); - fprintf(f, " stream.props = {"); - pw_properties_serialize_dict(f, &data->stream_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &module->props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &data->stream_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-pulse-tunnel", diff --git a/src/modules/module-protocol-pulse/modules/module-virtual-sink.c b/src/modules/module-protocol-pulse/modules/module-virtual-sink.c index e3193f2f6..66e196169 100644 --- a/src/modules/module-protocol-pulse/modules/module-virtual-sink.c +++ b/src/modules/module-protocol-pulse/modules/module-virtual-sink.c @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -66,26 +66,29 @@ static const struct pw_impl_module_events module_events = { static int module_virtual_sink_load(struct module *module) { struct module_virtual_sink_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "virtual-sink-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "virtual-sink-%u", module->index); pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-loopback", diff --git a/src/modules/module-protocol-pulse/modules/module-virtual-source.c b/src/modules/module-protocol-pulse/modules/module-virtual-source.c index fe3f10648..da6947e45 100644 --- a/src/modules/module-protocol-pulse/modules/module-virtual-source.c +++ b/src/modules/module-protocol-pulse/modules/module-virtual-source.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include "../defs.h" @@ -68,26 +68,29 @@ static const struct pw_impl_module_events module_events = { static int module_virtual_source_load(struct module *module) { struct module_virtual_source_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; pw_properties_setf(data->capture_props, PW_KEY_NODE_GROUP, "virtual-source-%u", module->index); pw_properties_setf(data->playback_props, PW_KEY_NODE_GROUP, "virtual-source-%u", module->index); pw_properties_setf(data->capture_props, "pulse.module.id", "%u", module->index); pw_properties_setf(data->playback_props, "pulse.module.id", "%u", module->index); - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - pw_properties_serialize_dict(f, &data->global_props->dict, 0); - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &data->global_props->dict, 0); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data->capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data->playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-loopback", diff --git a/src/modules/module-protocol-pulse/modules/module-x11-bell.c b/src/modules/module-protocol-pulse/modules/module-x11-bell.c index b9d7c4908..9ae13d915 100644 --- a/src/modules/module-protocol-pulse/modules/module-x11-bell.c +++ b/src/modules/module-protocol-pulse/modules/module-x11-bell.c @@ -2,7 +2,7 @@ /* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans */ /* SPDX-License-Identifier: MIT */ -#include +#include #include #include "../module.h" @@ -56,34 +56,26 @@ static const struct pw_impl_module_events module_events = { static int module_x11_bell_load(struct module *module) { struct module_x11_bell_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; const char *str; - char encoded[1024]; size_t size; + int res; - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); - if ((str = pw_properties_get(module->props, "sink")) != NULL) { - spa_json_encode_string(encoded, sizeof(encoded), str); - fprintf(f, " sink.name = %s", encoded); - } - if ((str = pw_properties_get(module->props, "sample")) != NULL) { - spa_json_encode_string(encoded, sizeof(encoded), str); - fprintf(f, " sample.name = %s", encoded); - } - if ((str = pw_properties_get(module->props, "display")) != NULL) { - spa_json_encode_string(encoded, sizeof(encoded), str); - fprintf(f, " x11.display = %s", encoded); - } - if ((str = pw_properties_get(module->props, "xauthority")) != NULL) { - spa_json_encode_string(encoded, sizeof(encoded), str); - fprintf(f, " x11.xauthority = %s", encoded); - } - fprintf(f, " }"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + if ((str = pw_properties_get(module->props, "sink")) != NULL) + spa_json_builder_object_string(&b, "sink.name", str); + if ((str = pw_properties_get(module->props, "sample")) != NULL) + spa_json_builder_object_string(&b, "sample.name", str); + if ((str = pw_properties_get(module->props, "display")) != NULL) + spa_json_builder_object_string(&b, "x11.display", str); + if ((str = pw_properties_get(module->props, "xauthority")) != NULL) + spa_json_builder_object_string(&b, "x11.xauthority", str); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-x11-bell", diff --git a/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c b/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c index 5b1763eb8..b2b3acec9 100644 --- a/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c +++ b/src/modules/module-protocol-pulse/modules/module-zeroconf-discover.c @@ -3,6 +3,7 @@ /* SPDX-License-Identifier: MIT */ #include +#include #include #include "../defs.h" @@ -57,18 +58,19 @@ static const struct pw_impl_module_events module_events = { static int module_zeroconf_discover_load(struct module *module) { struct module_zeroconf_discover_data *data = module->user_data; - FILE *f; + struct spa_json_builder b; char *args; size_t size; + int res; - if ((f = open_memstream(&args, &size)) == NULL) - return -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) + return res; - fprintf(f, "{"); + spa_json_builder_array_push(&b, "{"); if (data->latency_msec > 0) - fprintf(f, " pulse.latency = %u ", data->latency_msec); - fprintf(f, "}"); - fclose(f); + spa_json_builder_object_uint(&b, "pulse.latency", data->latency_msec); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); data->mod = pw_context_load_module(module->impl->context, "libpipewire-module-zeroconf-discover", diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 5191dd6b4..eea6693c3 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -4818,12 +4818,23 @@ static int do_set_default(struct client *client, uint32_t command, uint32_t tag, else if (spa_strendswith(name, ".monitor")) name = strndupa(name, strlen(name)-8); - char val[1024]; - spa_json_encode_string(val, sizeof(val), name); + struct spa_json_builder b; + char *val; + size_t val_size; + + if ((res = spa_json_builder_memstream(&b, &val, &val_size, 0)) < 0) + return res; + + spa_json_builder_array_push(&b, "{"); + spa_json_builder_object_string(&b, "name", name); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); + res = pw_manager_set_metadata(manager, client->metadata_default, PW_ID_CORE, sink ? METADATA_CONFIG_DEFAULT_SINK : METADATA_CONFIG_DEFAULT_SOURCE, - "Spa:String:JSON", "{ \"name\": %s }", val); + "Spa:String:JSON", "%s", val); + free(val); } else { res = pw_manager_set_metadata(manager, client->metadata_default, PW_ID_CORE, diff --git a/src/modules/module-protocol-simple.c b/src/modules/module-protocol-simple.c index ffc225b98..3a405684d 100644 --- a/src/modules/module-protocol-simple.c +++ b/src/modules/module-protocol-simple.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -961,7 +961,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) struct pw_properties *props; struct impl *impl; struct server *s; - FILE *f; + struct spa_json_builder b; char *str; size_t size; int res; @@ -994,13 +994,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if ((res = parse_params(impl)) < 0) goto error_free; - if ((f = open_memstream(&str, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &str, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); goto error_free; } - fprintf(f, "["); + spa_json_builder_array_push(&b, "["); spa_list_for_each(s, &impl->server_list, link) { char ip[128]; @@ -1010,10 +1009,11 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) if (pw_net_get_ip(&s->addr, ip, sizeof(ip), &ipv4, &port) < 0) continue; - fprintf(f, " \"%s%s%s:%d\"", ipv4 ? "" : "[", ip, ipv4 ? "" : "]", port); + spa_json_builder_array_stringf(&b, "%s%s%s:%d", + ipv4 ? "" : "[", ip, ipv4 ? "" : "]", port); } - fprintf(f, " ]"); - fclose(f); + spa_json_builder_pop(&b, "]"); + spa_json_builder_close(&b); pw_log_info("listening on %s", str); it[0] = SPA_DICT_ITEM_INIT("server.address", str); diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c index 60bdfa563..33ec653e8 100644 --- a/src/modules/module-raop-discover.c +++ b/src/modules/module-raop-discover.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -232,22 +232,21 @@ struct match_info { static int create_stream(struct impl *impl, struct pw_properties *props, struct tunnel *t) { - FILE *f; + struct spa_json_builder b; char *args; size_t size; int res = 0; struct pw_impl_module *mod; - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); goto done; } - fprintf(f, "{"); - pw_properties_serialize_dict(f, &props->dict, 0); - fprintf(f, "}"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading module args:'%s'", args); mod = pw_context_load_module(impl->context, diff --git a/src/modules/module-rtp-sap.c b/src/modules/module-rtp-sap.c index a8c1fc5e5..c1292ca20 100644 --- a/src/modules/module-rtp-sap.c +++ b/src/modules/module-rtp-sap.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include @@ -1269,37 +1269,34 @@ static int session_load_source(struct session *session, struct pw_properties *pr { struct impl *impl = session->impl; struct pw_context *context = pw_impl_module_get_context(impl->module); - FILE *f = NULL; + struct spa_json_builder b; char *args = NULL; size_t size; const char *str, *media; int res; - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); - goto done; + return res; } - fprintf(f, "{"); + spa_json_builder_array_push(&b, "{"); if ((str = pw_properties_get(props, "rtp.destination.ip")) != NULL) - fprintf(f, "\"source.ip\" = \"%s\", ", str); + spa_json_builder_object_string(&b, "source.ip", str); if ((str = pw_properties_get(props, "rtp.destination.port")) != NULL) - fprintf(f, "\"source.port\" = %s, ", str); + spa_json_builder_object_value(&b, false, "source.port", str); if ((str = pw_properties_get(props, "rtp.session")) != NULL) - fprintf(f, "\"sess.name\" = \"%s\", ", str); + spa_json_builder_object_string(&b, "sess.name", str); /* Use an interface if explicitly specified, else use the SAP interface if that was specified */ - if ((str = pw_properties_get(props, "local.ifname")) != NULL || (str = impl->ifname) != NULL) { - fprintf(f, "\"local.ifname\" = \"%s\", ", str); - } + if ((str = pw_properties_get(props, "local.ifname")) != NULL || (str = impl->ifname) != NULL) + spa_json_builder_object_string(&b, "local.ifname", str); if ((media = pw_properties_get(props, "sess.media")) == NULL) media = "audio"; - if ((str = pw_properties_get(props, "cleanup.sec")) != NULL) { - fprintf(f, "\"cleanup.sec\" = \"%s\", ", str); - } + if ((str = pw_properties_get(props, "cleanup.sec")) != NULL) + spa_json_builder_object_string(&b, "cleanup.sec", str); if (spa_streq(media, "audio")) { const char *mime; @@ -1308,15 +1305,16 @@ static int session_load_source(struct session *session, struct pw_properties *pr if ((mime = pw_properties_get(props, "rtp.mime")) == NULL) { pw_log_error("missing rtp.mime property"); res = -EINVAL; - goto done; + goto error; } format_info = find_audio_format_info(mime); if (format_info == NULL) { pw_log_error("unknown rtp.mime type %s", mime); res = -ENOTSUP; - goto done; + goto error; } - fprintf(f, "\"sess.media\" = \"%s\", ", format_info->media_type); + spa_json_builder_object_string(&b, "sess.media", format_info->media_type); + if (format_info->format_str != NULL) { pw_properties_set(props, "audio.format", format_info->format_str); if ((str = pw_properties_get(props, "rtp.rate")) != NULL) @@ -1325,41 +1323,40 @@ static int session_load_source(struct session *session, struct pw_properties *pr pw_properties_set(props, "audio.channels", str); } if ((str = pw_properties_get(props, "rtp.ssrc")) != NULL) - fprintf(f, "\"rtp.receiver-ssrc\" = \"%s\", ", str); + spa_json_builder_object_string(&b, "rtp.receiver-ssrc", str); } else { pw_log_error("Unhandled media %s", media); res = -EINVAL; - goto done; + goto error; } if ((str = pw_properties_get(props, "rtp.ts-offset")) != NULL) - fprintf(f, "\"sess.ts-offset\" = %s, ", str); + spa_json_builder_object_value(&b, false, "sess.ts-offset", str); - fprintf(f, " stream.props = {"); - pw_properties_serialize_dict(f, &props->dict, 0); - fprintf(f, " }"); - fprintf(f, "}"); - fclose(f); - f = NULL; + spa_json_builder_object_push(&b, "stream.props", "{"); + pw_properties_serialize_dict(b.f, &props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading new RTP source"); session->module = pw_context_load_module(context, "libpipewire-module-rtp-source", args, NULL); + free(args); + if (session->module == NULL) { - res = -errno; pw_log_error("Can't load module: %m"); - goto done; + return -errno; } pw_impl_module_add_listener(session->module, &session->module_listener, &session_module_events, session); - res = 0; -done: - if (f != NULL) - fclose(f); + return 0; +error: + spa_json_builder_close(&b); free(args); return res; } diff --git a/src/modules/module-snapcast-discover.c b/src/modules/module-snapcast-discover.c index 5c7896bcf..677725699 100644 --- a/src/modules/module-snapcast-discover.c +++ b/src/modules/module-snapcast-discover.c @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -523,7 +523,7 @@ static int parse_audio_info(struct pw_properties *props, struct spa_audio_info_r static int create_stream(struct impl *impl, struct pw_properties *props, struct tunnel *t) { - FILE *f; + struct spa_json_builder b; char *args; size_t size; int res = 0; @@ -548,16 +548,15 @@ static int create_stream(struct impl *impl, struct pw_properties *props, goto done; } - if ((f = open_memstream(&args, &size)) == NULL) { - res = -errno; + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { pw_log_error("Can't open memstream: %m"); goto done; } - fprintf(f, "{"); - pw_properties_serialize_dict(f, &props->dict, 0); - fprintf(f, "}"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading module args:'%s'", args); mod = pw_context_load_module(impl->context, diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c index 61108d424..c2dd72188 100644 --- a/src/modules/module-zeroconf-discover.c +++ b/src/modules/module-zeroconf-discover.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -233,7 +233,7 @@ static void on_zeroconf_added(void *data, const void *user_data, const struct sp struct tunnel *t; struct tunnel_info tinfo; const struct spa_dict_item *it; - FILE *f; + struct spa_json_builder b; char *args; size_t size; struct pw_impl_module *mod; @@ -309,17 +309,17 @@ static void on_zeroconf_added(void *data, const void *user_data, const struct sp if ((str = pw_properties_get(impl->properties, "pulse.latency")) != NULL) pw_properties_set(props, "pulse.latency", str); - if ((f = open_memstream(&args, &size)) == NULL) { + if (spa_json_builder_memstream(&b, &args, &size, 0) < 0) { pw_log_error("Can't open memstream: %m"); goto done; } - fprintf(f, "{"); - pw_properties_serialize_dict(f, &props->dict, 0); - fprintf(f, " stream.props = {"); - fprintf(f, " }"); - fprintf(f, "}"); - fclose(f); + spa_json_builder_array_push(&b, "{"); + pw_properties_serialize_dict(b.f, &props->dict, 0); + spa_json_builder_object_push(&b, "stream.props", "{"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading module args:'%s'", args); mod = pw_context_load_module(impl->context, diff --git a/src/tools/pw-loopback.c b/src/tools/pw-loopback.c index 991934a37..3375beae4 100644 --- a/src/tools/pw-loopback.c +++ b/src/tools/pw-loopback.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -92,10 +92,10 @@ int main(int argc, char *argv[]) struct data data = { 0 }; struct pw_loop *l; const char *opt_remote = NULL, *remote_name; - char cname[256], value[256]; + char cname[256]; + struct spa_json_builder b; char *args; size_t size; - FILE *f; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -214,42 +214,44 @@ int main(int argc, char *argv[]) } - if ((f = open_memstream(&args, &size)) == NULL) { + if ((res = spa_json_builder_memstream(&b, &args, &size, 0)) < 0) { fprintf(stderr, "can't open memstream: %m\n"); goto exit; } - fprintf(f, "{"); - remote_name = "[" PW_DEFAULT_REMOTE "-manager," PW_DEFAULT_REMOTE "]"; if (opt_remote) remote_name = opt_remote; - fprintf(f, " remote.name = \"%s\"", remote_name); + spa_json_builder_array_push(&b, "{"); + + spa_json_builder_object_string(&b, "remote.name", remote_name); if (data.latency != 0) - fprintf(f, " node.latency = %u/%u", data.latency, DEFAULT_RATE); + spa_json_builder_object_stringf(&b, "node.latency", "%u/%u", + data.latency, DEFAULT_RATE); if (data.delay != 0.0f) - fprintf(f, " target.delay.sec = %s", - spa_json_format_float(value, sizeof(value), data.delay)); + spa_json_builder_object_double(&b, "target.delay.sec", data.delay); if (data.channels != 0) - fprintf(f, " audio.channels = %u", data.channels); + spa_json_builder_object_uint(&b, "audio.channels", data.channels); if (data.opt_channel_map != NULL) - fprintf(f, " audio.position = %s", data.opt_channel_map); + spa_json_builder_object_value(&b, true, "audio.position", data.opt_channel_map); if (data.opt_node_name != NULL) - fprintf(f, " node.name = %s", data.opt_node_name); + spa_json_builder_object_string(&b, "node.name", data.opt_node_name); if (data.opt_group_name != NULL) { pw_properties_set(data.capture_props, PW_KEY_NODE_GROUP, data.opt_group_name); pw_properties_set(data.playback_props, PW_KEY_NODE_GROUP, data.opt_group_name); } - fprintf(f, " capture.props = {"); - pw_properties_serialize_dict(f, &data.capture_props->dict, 0); - fprintf(f, " } playback.props = {"); - pw_properties_serialize_dict(f, &data.playback_props->dict, 0); - fprintf(f, " } }"); - fclose(f); + spa_json_builder_object_push(&b, "capture.props", "{"); + pw_properties_serialize_dict(b.f, &data.capture_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_object_push(&b, "playback.props", "{"); + pw_properties_serialize_dict(b.f, &data.playback_props->dict, 0); + spa_json_builder_pop(&b, "}"); + spa_json_builder_pop(&b, "}"); + spa_json_builder_close(&b); pw_log_info("loading module with %s", args);