module-jack: load libjack.so.0 with dlopen

This way we can search for the real jack library instead of using our
custom libjack.so. Add an option to override the search path with
LIBJACK_PATH and an option to select the library name to load.
This commit is contained in:
Wim Taymans 2023-05-05 15:42:14 +02:00
parent 7da4eb13f7
commit 0629647cb5
3 changed files with 228 additions and 35 deletions

View file

@ -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

View file

@ -29,7 +29,7 @@
#include <pipewire/i18n.h>
#include <pipewire/private.h>
#include <jack/jack.h>
#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,
&current_frames, &current_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) {

View file

@ -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 <dlfcn.h>
#include <jack/jack.h>
#include <jack/transport.h>
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 */