diff --git a/src/modules/meson.build b/src/modules/meson.build index 10db01869..5d2dc9984 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -168,7 +168,7 @@ if build_module_jack_tunnel install : true, install_dir : modules_install_dir, install_rpath: modules_install_dir, - dependencies : [mathlib, dl_lib, pipewire_dep, jack_dep], + dependencies : [mathlib, dl_lib, pipewire_dep], ) build_module_jackdbus_detect = dbus_dep.found() if build_module_jackdbus_detect diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index f1a70eb95..30a2f3cef 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -29,7 +29,7 @@ #include #include -#include +#include "module-jack-tunnel/weakjack.h" /** \page page_module_jack_tunnel PipeWire Module: JACK Tunnel * @@ -42,6 +42,9 @@ * * ## Module Options * + * - `jack.library`: the libjack to load, by default libjack.so.0 is searched in + * JACK_PATH directories and then some standard library paths. + * Can be an absolute path. * - `jack.server`: the name of the JACK server to tunnel to. * - `jack.client-name`: the name of the JACK client. * - `jack.connect`: if jack ports should be connected automatically can also be @@ -70,6 +73,7 @@ * context.modules = [ * { name = libpipewire-module-jack-tunnel * args = { + * #jack.library = libjack.so.0 * #jack.server = null * #jack.client-name = PipeWire * #jack.connect = true @@ -115,6 +119,8 @@ static const struct spa_dict_item module_props[] = { { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; +static struct weakjack jack; + struct port { jack_port_t *jack_port; @@ -221,13 +227,13 @@ static void sink_process(void *d, struct spa_io_position *position) if (src == NULL || p->jack_port == NULL) continue; - dst = jack_port_get_buffer(p->jack_port, n_samples); + dst = jack.port_get_buffer(p->jack_port, n_samples); memcpy(dst, src, n_samples * sizeof(float)); } pw_log_trace_fp("done %u", impl->frame_time); if (impl->mode & MODE_SINK) { impl->done = true; - jack_cycle_signal(impl->client, 0); + jack.cycle_signal(impl->client, 0); } } @@ -247,13 +253,13 @@ static void source_process(void *d, struct spa_io_position *position) if (dst == NULL || p->jack_port == NULL) continue; - src = jack_port_get_buffer (p->jack_port, n_samples); + src = jack.port_get_buffer (p->jack_port, n_samples); memcpy(dst, src, n_samples * sizeof(float)); } pw_log_trace_fp("done %u", impl->frame_time); if (impl->mode == MODE_SOURCE) { impl->done = true; - jack_cycle_signal(impl->client, 0); + jack.cycle_signal(impl->client, 0); } } @@ -305,7 +311,7 @@ static void param_latency_changed(struct impl *impl, const struct spa_pod *param port->latency_changed[direction] = update = true; } if (update) - jack_recompute_total_latencies(impl->client); + jack.recompute_total_latencies(impl->client); } static void make_sink_ports(struct impl *impl) @@ -317,7 +323,7 @@ static void make_sink_ports(struct impl *impl) const char **ports = NULL; if (impl->sink_connect) - ports = jack_get_ports(impl->client, NULL, NULL, + ports = jack.get_ports(impl->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); for (i = 0; i < impl->sink_info.channels; i++) { @@ -325,7 +331,7 @@ static void make_sink_ports(struct impl *impl) if (port != NULL) { impl->sink_ports[i] = NULL; if (port->jack_port) - jack_port_unregister(impl->client, port->jack_port); + jack.port_unregister(impl->client, port->jack_port); pw_filter_remove_port(port); } @@ -347,18 +353,18 @@ static void make_sink_ports(struct impl *impl) else snprintf(name, sizeof(name), "output_%d", i); - port->jack_port = jack_port_register (impl->client, name, + port->jack_port = jack.port_register (impl->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); impl->sink_ports[i] = port; if (ports != NULL && ports[i] != NULL) { - if (jack_connect(impl->client, jack_port_name(port->jack_port), ports[i])) + if (jack.connect(impl->client, jack.port_name(port->jack_port), ports[i])) pw_log_warn("cannot connect output ports"); } } if (ports) - jack_free(ports); + jack.free(ports); } static void sink_param_changed(void *data, void *port_data, uint32_t id, @@ -389,7 +395,7 @@ static void make_source_ports(struct impl *impl) const char **ports = NULL; if (impl->source_connect) - ports = jack_get_ports(impl->client, NULL, NULL, + ports = jack.get_ports(impl->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); for (i = 0; i < impl->source_info.channels; i++) { @@ -397,7 +403,7 @@ static void make_source_ports(struct impl *impl) if (port != NULL) { impl->source_ports[i] = NULL; if (port->jack_port) - jack_port_unregister(impl->client, port->jack_port); + jack.port_unregister(impl->client, port->jack_port); pw_filter_remove_port(port); } @@ -419,18 +425,18 @@ static void make_source_ports(struct impl *impl) else snprintf(name, sizeof(name), "input_%d", i); - port->jack_port = jack_port_register (impl->client, name, + port->jack_port = jack.port_register (impl->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); impl->source_ports[i] = port; if (ports != NULL && ports[i] != NULL) { - if (jack_connect(impl->client, ports[i], jack_port_name(port->jack_port))) + if (jack.connect(impl->client, ports[i], jack.port_name(port->jack_port))) pw_log_warn("cannot connect input ports"); } } if (ports) - jack_free(ports); + jack.free(ports); } static void source_param_changed(void *data, void *port_data, uint32_t id, @@ -545,12 +551,12 @@ static void *jack_process_thread(void *arg) jack_nframes_t nframes; while (true) { - nframes = jack_cycle_wait (impl->client); + nframes = jack.cycle_wait (impl->client); source_running = impl->source_running; sink_running = impl->sink_running; - impl->frame_time = jack_frame_time(impl->client); + impl->frame_time = jack.frame_time(impl->client); pw_log_trace_fp("process %d %u %u %p %d", nframes, source_running, sink_running, impl->position, impl->frame_time); @@ -568,7 +574,7 @@ static void *jack_process_thread(void *arg) float period_usecs; jack_position_t pos; - jack_get_cycle_times(impl->client, + jack.get_cycle_times(impl->client, ¤t_frames, ¤t_usecs, &next_usecs, &period_usecs); @@ -583,7 +589,7 @@ static void *jack_process_thread(void *arg) c->target_rate = c->rate; c->target_duration = c->duration; - jack_transport_query (impl->client, &pos); + jack.transport_query (impl->client, &pos); } if (impl->mode & MODE_SINK && sink_running) { impl->done = false; @@ -592,7 +598,7 @@ static void *jack_process_thread(void *arg) impl->done = false; pw_filter_trigger_process(impl->source); } else { - jack_cycle_signal(impl->client, 0); + jack.cycle_signal(impl->client, 0); } } return NULL; @@ -692,7 +698,7 @@ static void jack_latency(jack_latency_callback_mode_t mode, void *arg) if (port == NULL || port->jack_port == NULL) continue; - jack_port_get_latency_range(port->jack_port, mode, &range); + jack.port_get_latency_range(port->jack_port, mode, &range); latency.direction = PW_DIRECTION_INPUT; latency.min_rate = range.min; @@ -712,7 +718,7 @@ static void jack_latency(jack_latency_callback_mode_t mode, void *arg) if (port->latency_changed[PW_DIRECTION_OUTPUT]) { range.min = port->latency[PW_DIRECTION_OUTPUT].min_rate; range.max = port->latency[PW_DIRECTION_OUTPUT].max_rate; - jack_port_set_latency_range(port->jack_port, mode, &range); + jack.port_set_latency_range(port->jack_port, mode, &range); port->latency_changed[PW_DIRECTION_OUTPUT] = false; } } @@ -724,7 +730,7 @@ static void jack_latency(jack_latency_callback_mode_t mode, void *arg) struct port *port = impl->source_ports[i]; if (port == NULL || port->jack_port == NULL) continue; - jack_port_get_latency_range(port->jack_port, mode, &range); + jack.port_get_latency_range(port->jack_port, mode, &range); latency.direction = PW_DIRECTION_OUTPUT; latency.min_rate = range.min; @@ -744,7 +750,7 @@ static void jack_latency(jack_latency_callback_mode_t mode, void *arg) if (port->latency_changed[PW_DIRECTION_INPUT]) { range.min = port->latency[PW_DIRECTION_INPUT].min_rate; range.max = port->latency[PW_DIRECTION_INPUT].max_rate; - jack_port_set_latency_range(port->jack_port, mode, &range); + jack.port_set_latency_range(port->jack_port, mode, &range); port->latency_changed[PW_DIRECTION_INPUT] = false; } } @@ -768,17 +774,17 @@ static int create_jack_client(struct impl *impl) if (client_name == NULL) client_name = DEFAULT_CLIENT_NAME; - impl->client = jack_client_open(client_name, options, &status, server_name); + impl->client = jack.client_open(client_name, options, &status, server_name); if (impl->client == NULL) { pw_log_error ("jack_client_open() failed 0x%2.0x\n", status); return -EIO; } - jack_on_info_shutdown(impl->client, jack_info_shutdown, impl); - jack_set_process_thread(impl->client, jack_process_thread, impl); - jack_set_xrun_callback(impl->client, jack_xrun, impl); - jack_set_latency_callback(impl->client, jack_latency, impl); + jack.on_info_shutdown(impl->client, jack_info_shutdown, impl); + jack.set_process_thread(impl->client, jack_process_thread, impl); + jack.set_xrun_callback(impl->client, jack_xrun, impl); + jack.set_latency_callback(impl->client, jack_latency, impl); - impl->samplerate = jack_get_sample_rate(impl->client); + impl->samplerate = jack.get_sample_rate(impl->client); impl->source_info.rate = impl->samplerate; impl->sink_info.rate = impl->samplerate; @@ -787,7 +793,7 @@ static int create_jack_client(struct impl *impl) static int start_jack_clients(struct impl *impl) { - jack_activate(impl->client); + jack.activate(impl->client); return 0; } @@ -822,8 +828,8 @@ static const struct pw_proxy_events core_proxy_events = { static void impl_destroy(struct impl *impl) { if (impl->client) { - jack_deactivate(impl->client); - jack_client_close(impl->client); + jack.deactivate(impl->client); + jack.client_close(impl->client); } if (impl->source) pw_filter_destroy(impl->source); @@ -931,6 +937,14 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) } impl->props = props; + if ((str = pw_properties_get(props, "jack.library")) == NULL) + str = "libjack.so.0"; + + if ((res = weakjack_load(&jack, str)) < 0) { + pw_log_error( "can't load '%s': %s", str, spa_strerror(res)); + goto error; + } + impl->sink_props = pw_properties_new(NULL, NULL); impl->source_props = pw_properties_new(NULL, NULL); if (impl->source_props == NULL || impl->sink_props == NULL) { diff --git a/src/modules/module-jack-tunnel/weakjack.h b/src/modules/module-jack-tunnel/weakjack.h new file mode 100644 index 000000000..460445e2a --- /dev/null +++ b/src/modules/module-jack-tunnel/weakjack.h @@ -0,0 +1,179 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef PIPEWIRE_WEAK_JACK_H +#define PIPEWIRE_WEAK_JACK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "config.h" + +#include + +#include +#include + +struct weakjack { + jack_nframes_t (*cycle_wait) (jack_client_t* client); + void (*cycle_signal) (jack_client_t* client, int status); + + jack_nframes_t (*frame_time) (const jack_client_t *); + int (*get_cycle_times) (const jack_client_t *client, + jack_nframes_t *current_frames, + jack_time_t *current_usecs, + jack_time_t *next_usecs, + float *period_usecs); + jack_transport_state_t (*transport_query) (const jack_client_t *client, + jack_position_t *pos); + + jack_client_t * (*client_open) (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + int (*client_close) (jack_client_t *client); + + + int (*activate) (jack_client_t *client); + int (*deactivate) (jack_client_t *client); + + jack_nframes_t (*get_sample_rate) (jack_client_t *); + + int (*recompute_total_latencies) (jack_client_t *client); + + jack_port_t * (*port_register) (jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + int (*port_unregister) (jack_client_t *client, jack_port_t *port); + void * (*port_get_buffer) (jack_port_t *port, jack_nframes_t); + const char * (*port_name) (const jack_port_t *port); + + void (*port_get_latency_range) (jack_port_t *port, + jack_latency_callback_mode_t mode, + jack_latency_range_t *range); + void (*port_set_latency_range) (jack_port_t *port, + jack_latency_callback_mode_t mode, + jack_latency_range_t *range); + + + int (*connect) (jack_client_t *client, + const char *source_port, + const char *destination_port); + int (*disconnect) (jack_client_t *client, + const char *source_port, + const char *destination_port); + + const char ** (*get_ports) (jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + void (*free) (void* ptr); + + int (*set_process_thread) (jack_client_t* client, + JackThreadCallback thread_callback, void *arg); + int (*set_xrun_callback) (jack_client_t *client, + JackXRunCallback xrun_callback, void *arg); + void (*on_info_shutdown) (jack_client_t *client, + JackInfoShutdownCallback shutdown_callback, void *arg); + int (*set_latency_callback) (jack_client_t *client, + JackLatencyCallback latency_callback, void *arg); +}; + + +static inline int weakjack_load_by_path(struct weakjack *jack, const char *path) +{ + void *hnd; + + hnd = dlopen(path, RTLD_NOW); + if (hnd == NULL) + return -errno; + + pw_log_info("opened libjack: %s", path); + +#define LOAD_SYM(name) ({ \ + if ((jack->name = dlsym(hnd, "jack_"#name )) == NULL) \ + return -ENOSYS; \ +}) + spa_zero(*jack); + LOAD_SYM(cycle_wait); + LOAD_SYM(cycle_signal); + LOAD_SYM(frame_time); + LOAD_SYM(get_cycle_times); + LOAD_SYM(transport_query); + + LOAD_SYM(client_open); + LOAD_SYM(client_close); + + LOAD_SYM(activate); + LOAD_SYM(deactivate); + + LOAD_SYM(get_sample_rate); + + LOAD_SYM(recompute_total_latencies); + + LOAD_SYM(port_register); + LOAD_SYM(port_unregister); + LOAD_SYM(port_get_buffer); + LOAD_SYM(port_name); + + LOAD_SYM(port_get_latency_range); + LOAD_SYM(port_set_latency_range); + + LOAD_SYM(connect); + LOAD_SYM(disconnect); + + LOAD_SYM(get_ports); + LOAD_SYM(free); + + LOAD_SYM(set_process_thread); + LOAD_SYM(set_xrun_callback); + LOAD_SYM(on_info_shutdown); + LOAD_SYM(set_latency_callback); +#undef LOAD_SYM + + return 0; +} + +static inline int weakjack_load(struct weakjack *jack, const char *lib) +{ + int res = -ENOENT; + + if (lib[0] != '/') { + const char *search_dirs, *p, *state = NULL; + char path[PATH_MAX]; + size_t len; + + search_dirs = getenv("LIBJACK_PATH"); + if (!search_dirs) + search_dirs = "/usr/lib64:/usr/lib/:" LIBDIR; + + + while ((p = pw_split_walk(search_dirs, ":", &len, &state))) { + int pathlen; + + if (len >= sizeof(path)) { + res = -ENAMETOOLONG; + continue; + } + pathlen = snprintf(path, sizeof(path), "%.*s/%s", (int) len, p, lib); + if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) { + res = -ENAMETOOLONG; + continue; + } + if ((res = weakjack_load_by_path(jack, path)) == 0) + break; + } + } else { + res = weakjack_load_by_path(jack, lib); + } + return res; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PIPEWIRE_WEAK_JACK_H */