From 689dbd1fbe43f293d7a46d5fa0fe1ddcfbea50d6 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 29 Mar 2023 19:50:19 +0300 Subject: [PATCH] jack: Dynamically load libjack at runtime instead of linking In order to provide build and provide the jack plugin with the prebuilt binaries of gstreamer we distribute with releases, we can not depend on an external dependency nor can we ship plugins linking to libraries we don't provide. We can also not provide jack ourselves, as it would likely cause a mismatch with the jack daemon on the host. Part-of: --- .../gst-plugins-good/ext/jack/gstjack.c | 6 + .../gst-plugins-good/ext/jack/gstjack.h | 2 +- .../ext/jack/gstjackaudioclient.c | 28 +- .../ext/jack/gstjackaudioclient.h | 3 +- .../ext/jack/gstjackaudiosink.c | 42 +- .../ext/jack/gstjackaudiosink.h | 3 +- .../ext/jack/gstjackaudiosrc.c | 41 +- .../ext/jack/gstjackaudiosrc.h | 3 +- .../gst-plugins-good/ext/jack/gstjackloader.c | 472 ++++++++++++++++++ .../gst-plugins-good/ext/jack/gstjackloader.h | 207 ++++++++ .../gst-plugins-good/ext/jack/meson.build | 84 +--- .../tests/examples/jack/meson.build | 92 +++- 12 files changed, 834 insertions(+), 149 deletions(-) create mode 100644 subprojects/gst-plugins-good/ext/jack/gstjackloader.c create mode 100644 subprojects/gst-plugins-good/ext/jack/gstjackloader.h diff --git a/subprojects/gst-plugins-good/ext/jack/gstjack.c b/subprojects/gst-plugins-good/ext/jack/gstjack.c index 59682bb7b6..1227d8d9aa 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjack.c +++ b/subprojects/gst-plugins-good/ext/jack/gstjack.c @@ -22,6 +22,7 @@ #endif #include "gstjack.h" +#include "gstjackloader.h" GType gst_jack_connect_get_type (void) @@ -103,6 +104,11 @@ plugin_init (GstPlugin * plugin) { gboolean ret = FALSE; + if (!gst_jack_load_library ()) { + GST_WARNING ("Failed to load jack library"); + return FALSE; + } + ret |= GST_ELEMENT_REGISTER (jackaudiosrc, plugin); ret |= GST_ELEMENT_REGISTER (jackaudiosink, plugin); diff --git a/subprojects/gst-plugins-good/ext/jack/gstjack.h b/subprojects/gst-plugins-good/ext/jack/gstjack.h index 84693f6140..5506ba17c5 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjack.h +++ b/subprojects/gst-plugins-good/ext/jack/gstjack.h @@ -22,8 +22,8 @@ #ifndef _GST_JACK_H_ #define _GST_JACK_H_ -#include #include +#include "gstjackloader.h" GST_ELEMENT_REGISTER_DECLARE (jackaudiosrc); GST_ELEMENT_REGISTER_DECLARE (jackaudiosink); diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.c b/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.c index 5b483a810a..ca3ff3d1ca 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.c +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.c @@ -24,8 +24,6 @@ #include "gstjackaudioclient.h" #include "gstjack.h" -#include - GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug); #define GST_CAT_DEFAULT gst_jack_audio_client_debug @@ -47,8 +45,8 @@ gst_jack_audio_client_init (void) GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0, "jackclient helpers"); - jack_set_error_function (jack_log_error); - jack_set_info_function (jack_info_error); + gst_jack_set_error_function (jack_log_error); + gst_jack_set_info_function (jack_info_error); } /* a list of global connections indexed by id and server. */ @@ -123,7 +121,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) GstJackAudioConnection *conn = (GstJackAudioConnection *) arg; GList *walk; int res = 0; - jack_transport_state_t ts = jack_transport_query (conn->client, NULL); + jack_transport_state_t ts = gst_jack_transport_query (conn->client, NULL); if (ts != conn->cur_ts) { conn->cur_ts = ts; @@ -295,7 +293,7 @@ gst_jack_audio_make_connection (const gchar * id, const gchar * server, options |= JackServerName; /* open the client */ if (jclient == NULL) - jclient = jack_client_open (id, options, status, server); + jclient = gst_jack_client_open (id, options, status, server); if (jclient == NULL) goto could_not_open; @@ -314,15 +312,15 @@ gst_jack_audio_make_connection (const gchar * id, const gchar * server, conn->transport_state = GST_STATE_VOID_PENDING; /* set our callbacks */ - jack_set_process_callback (jclient, jack_process_cb, conn); + gst_jack_set_process_callback (jclient, jack_process_cb, conn); /* these callbacks cause us to error */ - jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn); - jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn); - jack_on_shutdown (jclient, jack_shutdown_cb, conn); + gst_jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn); + gst_jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn); + gst_jack_on_shutdown (jclient, jack_shutdown_cb, conn); /* all callbacks are set, activate the client */ GST_INFO ("activate jack_client %p", jclient); - if ((res = jack_activate (jclient))) + if ((res = gst_jack_activate (jclient))) goto could_not_activate; GST_DEBUG ("opened connection %p", conn); @@ -417,13 +415,13 @@ gst_jack_audio_unref_connection (GstJackAudioConnection * conn) * jack_process_cb() */ GST_INFO ("deactivate jack_client %p", conn->client); - if ((res = jack_deactivate (conn->client))) { + if ((res = gst_jack_deactivate (conn->client))) { /* we only warn, this means the server is probably shut down and the client * is gone anyway. */ GST_WARNING ("Could not deactivate Jack client (%d)", res); } /* close connection */ - if ((res = jack_client_close (conn->client))) { + if ((res = gst_jack_client_close (conn->client))) { /* we assume the client is gone. */ GST_WARNING ("close failed (%d)", res); } @@ -664,7 +662,7 @@ gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient, goto invalid; for (i = 0; i < len; i++) { - jack_port_t *port = jack_port_by_name (jclient, p[i]); + jack_port_t *port = gst_jack_port_by_name (jclient, p[i]); int flags; if (!port) { @@ -672,7 +670,7 @@ gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient, goto invalid; } - flags = jack_port_flags (port); + flags = gst_jack_port_flags (port); if ((flags & port_flags) != port_flags) { GST_WARNING ("Port flags 0x%x doesn't match expected flags 0x%x", flags, port_flags); diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.h b/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.h index 455edd2e78..c3f3e3868f 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.h +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudioclient.h @@ -22,9 +22,8 @@ #ifndef __GST_JACK_AUDIO_CLIENT_H__ #define __GST_JACK_AUDIO_CLIENT_H__ -#include - #include +#include "gstjackloader.h" G_BEGIN_DECLS diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.c b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.c index ee433f2e19..c69c6a6e32 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.c +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.c @@ -64,6 +64,7 @@ #include "gstjackaudiosink.h" #include "gstjackringbuffer.h" +#include "gstjackloader.h" GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_sink_debug); #define GST_CAT_DEFAULT gst_jack_audio_sink_debug @@ -77,7 +78,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels) /* remove ports we don't need */ while (sink->port_count > channels) { - jack_port_unregister (client, sink->ports[--sink->port_count]); + gst_jack_port_unregister (client, sink->ports[--sink->port_count]); } /* alloc enough output ports */ @@ -93,7 +94,7 @@ gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels) g_strdup_printf ("out_%s_%d", GST_ELEMENT_NAME (sink), sink->port_count + 1); sink->ports[sink->port_count] = - jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, + gst_jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (sink->ports[sink->port_count] == NULL) return FALSE; @@ -116,7 +117,7 @@ gst_jack_audio_sink_free_channels (GstJackAudioSink * sink) /* get rid of all ports */ while (sink->port_count) { GST_LOG_OBJECT (sink, "unregister port %d", i); - if ((res = jack_port_unregister (client, sink->ports[i++]))) { + if ((res = gst_jack_port_unregister (client, sink->ports[i++]))) { GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res); } sink->port_count--; @@ -202,7 +203,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) /* get target buffers */ for (i = 0; i < channels; i++) { sink->buffers[i] = - (sample_t *) jack_port_get_buffer (sink->ports[i], nframes); + (sample_t *) gst_jack_port_get_buffer (sink->ports[i], nframes); } if (gst_audio_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { @@ -415,7 +416,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, rate = GST_AUDIO_INFO_RATE (&spec->info); /* sample rate must be that of the server */ - sample_rate = jack_get_sample_rate (client); + sample_rate = gst_jack_get_sample_rate (client); if (sample_rate != rate) goto wrong_samplerate; @@ -425,7 +426,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, if (!gst_jack_audio_sink_allocate_channels (sink, channels)) goto out_of_ports; - buffer_size = jack_get_buffer_size (client); + buffer_size = gst_jack_get_buffer_size (client); /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats * for all channels */ @@ -480,10 +481,10 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, if (!available_ports) { if (!sink->port_pattern) { - jack_ports = jack_get_ports (client, NULL, NULL, + jack_ports = gst_jack_get_ports (client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); } else { - jack_ports = jack_get_ports (client, sink->port_pattern, NULL, + jack_ports = gst_jack_get_ports (client, sink->port_pattern, NULL, JackPortIsInput); } @@ -507,19 +508,19 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, break; } GST_DEBUG_OBJECT (sink, "try connecting to %s", - jack_port_name (sink->ports[i])); + gst_jack_port_name (sink->ports[i])); /* connect the port to a physical port */ - res = jack_connect (client, - jack_port_name (sink->ports[i]), available_ports[i]); + res = gst_jack_connect (client, + gst_jack_port_name (sink->ports[i]), available_ports[i]); if (res != 0 && res != EEXIST) { - jack_free (jack_ports); + gst_jack_free (jack_ports); g_strfreev (user_ports); goto cannot_connect; } } - jack_free (jack_ports); + gst_jack_free (jack_ports); g_strfreev (user_ports); } done: @@ -609,7 +610,7 @@ gst_jack_ring_buffer_start (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (sink->client); - jack_transport_start (client); + gst_jack_transport_start (client); } return TRUE; @@ -628,7 +629,7 @@ gst_jack_ring_buffer_pause (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (sink->client); - jack_transport_stop (client); + gst_jack_transport_stop (client); } return TRUE; @@ -647,7 +648,7 @@ gst_jack_ring_buffer_stop (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (sink->client); - jack_transport_stop (client); + gst_jack_transport_stop (client); } return TRUE; @@ -663,7 +664,8 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf) sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); for (i = 0; i < sink->port_count; i++) { - jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency, &range); + gst_jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency, + &range); if (range.max > res) res = range.max; } @@ -1015,11 +1017,11 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink, GstCaps * filter) /* get a port count, this is the number of channels we can automatically * connect. */ - ports = jack_get_ports (client, NULL, NULL, + ports = gst_jack_get_ports (client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if (ports != NULL) { for (; ports[max]; max++); - jack_free (ports); + gst_jack_free (ports); } else max = 0; } else { @@ -1035,7 +1037,7 @@ found: min = MIN (1, max); } - rate = jack_get_sample_rate (client); + rate = gst_jack_get_sample_rate (client); GST_DEBUG_OBJECT (sink, "got %d-%d ports, samplerate: %d", min, max, rate); diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.h b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.h index 088289de46..4169a2b236 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.h +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosink.h @@ -22,13 +22,12 @@ #ifndef __GST_JACK_AUDIO_SINK_H__ #define __GST_JACK_AUDIO_SINK_H__ -#include - #include #include #include "gstjack.h" #include "gstjackaudioclient.h" +#include "gstjackloader.h" G_BEGIN_DECLS diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.c b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.c index 3fcd0655a9..87c2ce4aec 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.c +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.c @@ -85,6 +85,7 @@ #include "gstjackaudiosrc.h" #include "gstjackringbuffer.h" #include "gstjackutil.h" +#include "gstjackloader.h" GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_src_debug); #define GST_CAT_DEFAULT gst_jack_audio_src_debug @@ -98,7 +99,7 @@ gst_jack_audio_src_allocate_channels (GstJackAudioSrc * src, gint channels) /* remove ports we don't need */ while (src->port_count > channels) - jack_port_unregister (client, src->ports[--src->port_count]); + gst_jack_port_unregister (client, src->ports[--src->port_count]); /* alloc enough input ports */ src->ports = g_realloc (src->ports, sizeof (jack_port_t *) * channels); @@ -113,7 +114,7 @@ gst_jack_audio_src_allocate_channels (GstJackAudioSrc * src, gint channels) g_strdup_printf ("in_%s_%d", GST_ELEMENT_NAME (src), src->port_count + 1); src->ports[src->port_count] = - jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, + gst_jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (src->ports[src->port_count] == NULL) return FALSE; @@ -136,7 +137,7 @@ gst_jack_audio_src_free_channels (GstJackAudioSrc * src) /* get rid of all ports */ while (src->port_count) { GST_LOG_OBJECT (src, "unregister port %d", i); - if ((res = jack_port_unregister (client, src->ports[i++]))) + if ((res = gst_jack_port_unregister (client, src->ports[i++]))) GST_DEBUG_OBJECT (src, "unregister of port failed (%d)", res); src->port_count--; @@ -220,7 +221,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) /* get input buffers */ for (i = 0; i < channels; i++) src->buffers[i] = - (sample_t *) jack_port_get_buffer (src->ports[i], nframes); + (sample_t *) gst_jack_port_get_buffer (src->ports[i], nframes); if (gst_audio_ring_buffer_prepare_read (buf, &writeseg, &writeptr, &len)) { flen = len / channels; @@ -421,7 +422,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, rate = GST_AUDIO_INFO_RATE (&spec->info); /* sample rate must be that of the server */ - sample_rate = jack_get_sample_rate (client); + sample_rate = gst_jack_get_sample_rate (client); if (sample_rate != rate) goto wrong_samplerate; @@ -433,7 +434,7 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, gst_jack_set_layout (buf, spec); - buffer_size = jack_get_buffer_size (client); + buffer_size = gst_jack_get_buffer_size (client); /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats * for all channels */ @@ -489,10 +490,10 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, if (!available_ports) { if (!src->port_pattern) { - jack_ports = jack_get_ports (client, NULL, NULL, + jack_ports = gst_jack_get_ports (client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); } else { - jack_ports = jack_get_ports (client, src->port_pattern, NULL, + jack_ports = gst_jack_get_ports (client, src->port_pattern, NULL, JackPortIsOutput); } } @@ -514,20 +515,20 @@ gst_jack_ring_buffer_acquire (GstAudioRingBuffer * buf, break; } GST_DEBUG_OBJECT (src, "try connecting to %s", - jack_port_name (src->ports[i])); + gst_jack_port_name (src->ports[i])); /* connect the physical port to a port */ - res = jack_connect (client, - available_ports[i], jack_port_name (src->ports[i])); + res = gst_jack_connect (client, + available_ports[i], gst_jack_port_name (src->ports[i])); if (res != 0 && res != EEXIST) { - jack_free (jack_ports); + gst_jack_free (jack_ports); g_strfreev (user_ports); goto cannot_connect; } } - jack_free (jack_ports); + gst_jack_free (jack_ports); g_strfreev (user_ports); } done: @@ -617,7 +618,7 @@ gst_jack_ring_buffer_start (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (src->client); - jack_transport_start (client); + gst_jack_transport_start (client); } return TRUE; @@ -636,7 +637,7 @@ gst_jack_ring_buffer_pause (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (src->client); - jack_transport_stop (client); + gst_jack_transport_stop (client); } return TRUE; @@ -655,7 +656,7 @@ gst_jack_ring_buffer_stop (GstAudioRingBuffer * buf) jack_client_t *client; client = gst_jack_audio_client_get_client (src->client); - jack_transport_stop (client); + gst_jack_transport_stop (client); } return TRUE; @@ -671,7 +672,7 @@ gst_jack_ring_buffer_delay (GstAudioRingBuffer * buf) src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); for (i = 0; i < src->port_count; i++) { - jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range); + gst_jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range); if (range.max > res) res = range.max; } @@ -1025,12 +1026,12 @@ gst_jack_audio_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter) /* get a port count, this is the number of channels we can automatically * connect. */ - ports = jack_get_ports (client, NULL, NULL, + ports = gst_jack_get_ports (client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); if (ports != NULL) { for (; ports[max]; max++); - jack_free (ports); + gst_jack_free (ports); } else max = 0; } else { @@ -1046,7 +1047,7 @@ found: min = MIN (1, max); } - rate = jack_get_sample_rate (client); + rate = gst_jack_get_sample_rate (client); GST_DEBUG_OBJECT (src, "got %d-%d ports, samplerate: %d", min, max, rate); diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.h b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.h index 3657c60cea..98c31e095f 100644 --- a/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.h +++ b/subprojects/gst-plugins-good/ext/jack/gstjackaudiosrc.h @@ -43,13 +43,12 @@ #ifndef __GST_JACK_AUDIO_SRC_H__ #define __GST_JACK_AUDIO_SRC_H__ -#include - #include #include #include "gstjackaudioclient.h" #include "gstjack.h" +#include "gstjackloader.h" G_BEGIN_DECLS diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackloader.c b/subprojects/gst-plugins-good/ext/jack/gstjackloader.c new file mode 100644 index 0000000000..cbbf716251 --- /dev/null +++ b/subprojects/gst-plugins-good/ext/jack/gstjackloader.c @@ -0,0 +1,472 @@ +/* GStreamer + * Copyright (C) 2023 Jordan Petridis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstjackloader.h" +#include +#include + +#ifdef __APPLE__ +#define JACK_LIBNAME "libjack.0.dylib" +#elif defined(G_OS_WIN32) +#ifdef _WIN64 +#define JACK_LIBNAME "libjack64.dll" +#else +#define JACK_LIBNAME "libjack.dll" +#endif /* End ifdef _WIN64 */ +#else /* End ifdef G_OS_WIN32 */ +#define JACK_LIBNAME "libjack.so.0" +#endif + +#define LOAD_SYMBOL(name,func,mandatory) G_STMT_START { \ + if (!g_module_symbol (module, G_STRINGIFY (name), (gpointer *) &vtable->func)) { \ + if (mandatory) { \ + GST_ERROR ("Failed to load '%s' from %s, %s", G_STRINGIFY (name), filename, g_module_error()); \ + goto error; \ + } \ + GST_WARNING ("Failed to load '%s' from %s, %s", G_STRINGIFY (name), filename, g_module_error()); \ + } \ +} G_STMT_END; + +typedef struct _GstJackVTable +{ + gboolean loaded; + + gint major_version; + gint minor_version; + gint micro_version; + + const char *(*GstJackGetVersionString) (void); + + jack_client_t *(*GstJackClientOpen) (const char *client_name, + jack_options_t options, jack_status_t * status, ...); + + jack_client_t *(*GstJackClientNew) (const char *client_name); + + int (*GstJackClientClose) (jack_client_t * client); + + int (*GstJackActivate) (jack_client_t * client); + + int (*GstJackDeactivate) (jack_client_t * client); + + void (*GstJackOnShutdown) (jack_client_t * client, + JackShutdownCallback shutdown_callback, void *arg); + + int (*GstJackSetProcessCallback) (jack_client_t * client, + JackProcessCallback process_callback, void *arg); + + + int (*GstJackSetBufferSizeCallback) (jack_client_t * client, + JackBufferSizeCallback bufsize_callback, void *arg); + + int (*GstJackSetSampleRateCallback) (jack_client_t * client, + JackSampleRateCallback srate_callback, void *arg); + + int (*GstJackSetBufferSize) (jack_client_t * client, jack_nframes_t nframes); + + jack_nframes_t (*GstJackGetSampleRate) (jack_client_t *); + + jack_nframes_t (*GstJackGetBufferSize) (jack_client_t *); + + jack_port_t *(*GstJackPortRegister) (jack_client_t * client, + const char *port_name, + const char *port_type, unsigned long flags, unsigned long buffer_size); + + int (*GstJackPortUnregister) (jack_client_t * client, jack_port_t * port); + + void *(*GstJackPortGetBuffer) (jack_port_t * port, jack_nframes_t nframes); + + const char *(*GstJackPortName) (const jack_port_t * port); + + int (*GstJackPortFlags) (const jack_port_t * port); + + int (*GstJackConnect) (jack_client_t * client, + const char *source_port, const char *destination_port); + + void (*GstJackPortGetLatencyRange) (jack_port_t * port, + jack_latency_callback_mode_t mode, jack_latency_range_t * range); + + const char **(*GstJackGetPorts) (jack_client_t * client, + const char *port_name_pattern, + const char *type_name_pattern, unsigned long flags); + + jack_port_t *(*GstJackPortByName) (jack_client_t * client, + const char *port_name); + + void (*GstJackSetErrorFunction) (void (*func) (const char *)); + + void (*GstJackSetInfoFunction) (void (*func) (const char *)); + + void (*GstJackFree) (void *ptr); + + void (*GstJackTransportStart) (jack_client_t * client); + + void (*GstJackTransportStop) (jack_client_t * client); + + jack_transport_state_t (*GstJackTransportQuery) (const jack_client_t * + client, jack_position_t * pos); + +} GstJackVTable; + +static GstJackVTable gst_jack_vtable = { 0, }; + +static const char * +gst_jack_get_version_string (void) +{ + g_assert (gst_jack_vtable.GstJackGetVersionString != NULL); + + const char *ret = gst_jack_vtable.GstJackGetVersionString (); + g_assert (ret != NULL); + return ret; +} + +static gboolean +gst_jack_check_api_version (void) +{ + /* hardcoded minimum supported version */ + gint supported_major_ver = 1; + gint minimum_minor_ver = 9; + gint minimum_micro_ver = 7; + + const char *jack_version = gst_jack_get_version_string (); + + if (jack_version == NULL || *jack_version == '\0') { + GST_ERROR ("No JACK version string"); + return FALSE; + } + + GST_INFO ("Checking JACK client library version: %s", jack_version); + + if (strstr (jack_version, "PipeWire")) { + GST_INFO ("Using Pipewire as the Jack server: %s", jack_version); + } else { + int major, minor, micro; + if (sscanf (jack_version, "%u.%u.%u", &major, &minor, µ) == 3 && + major == supported_major_ver && ((minor == minimum_minor_ver + && micro >= minimum_micro_ver) || minor > minimum_minor_ver)) { + GST_INFO ("Compatible Jack Server version: %s", jack_version); + } else { + GST_ERROR ("Unsupported Jack version: %s", jack_version); + } + } + + return TRUE; +} + +gboolean +gst_jack_load_library (void) +{ + GModule *module; + const gchar *filename = JACK_LIBNAME; + GstJackVTable *vtable; + + if (gst_jack_vtable.loaded) + return TRUE; + + module = g_module_open (filename, G_MODULE_BIND_LAZY); + if (module == NULL) { + GST_WARNING ("Could not open library %s, %s", filename, g_module_error ()); + return FALSE; + } + + vtable = &gst_jack_vtable; + LOAD_SYMBOL (jack_get_version_string, GstJackGetVersionString, TRUE); + + if (!gst_jack_check_api_version ()) + goto error; + + LOAD_SYMBOL (jack_client_open, GstJackClientOpen, TRUE); + LOAD_SYMBOL (jack_client_new, GstJackClientNew, TRUE); + LOAD_SYMBOL (jack_client_close, GstJackClientClose, TRUE); + LOAD_SYMBOL (jack_activate, GstJackActivate, TRUE); + LOAD_SYMBOL (jack_deactivate, GstJackDeactivate, TRUE); + LOAD_SYMBOL (jack_on_shutdown, GstJackOnShutdown, TRUE); + LOAD_SYMBOL (jack_set_process_callback, GstJackSetProcessCallback, TRUE); + LOAD_SYMBOL (jack_set_buffer_size_callback, GstJackSetBufferSizeCallback, + TRUE); + LOAD_SYMBOL (jack_set_sample_rate_callback, GstJackSetSampleRateCallback, + TRUE); + LOAD_SYMBOL (jack_set_buffer_size, GstJackSetBufferSize, TRUE); + LOAD_SYMBOL (jack_get_sample_rate, GstJackGetSampleRate, TRUE); + LOAD_SYMBOL (jack_get_buffer_size, GstJackGetBufferSize, TRUE); + LOAD_SYMBOL (jack_port_register, GstJackPortRegister, TRUE); + LOAD_SYMBOL (jack_port_unregister, GstJackPortUnregister, TRUE); + LOAD_SYMBOL (jack_port_get_buffer, GstJackPortGetBuffer, TRUE); + LOAD_SYMBOL (jack_port_name, GstJackPortName, TRUE); + LOAD_SYMBOL (jack_port_flags, GstJackPortFlags, TRUE); + LOAD_SYMBOL (jack_connect, GstJackConnect, TRUE); + LOAD_SYMBOL (jack_port_get_latency_range, GstJackPortGetLatencyRange, TRUE); + LOAD_SYMBOL (jack_get_ports, GstJackGetPorts, TRUE); + LOAD_SYMBOL (jack_port_by_name, GstJackPortByName, TRUE); + LOAD_SYMBOL (jack_set_error_function, GstJackSetErrorFunction, TRUE); + LOAD_SYMBOL (jack_set_info_function, GstJackSetInfoFunction, TRUE); + LOAD_SYMBOL (jack_free, GstJackFree, TRUE); + LOAD_SYMBOL (jack_transport_start, GstJackTransportStart, TRUE); + LOAD_SYMBOL (jack_transport_stop, GstJackTransportStop, TRUE); + LOAD_SYMBOL (jack_transport_query, GstJackTransportQuery, TRUE); + + vtable->loaded = TRUE; + + return TRUE; + +error: + g_module_close (module); + + return FALSE; +} + +#define _gst_jack_client_open(client_name,options,status,...) (gst_jack_vtable.GstJackClientOpen(client_name, options, status, ##__VA_ARGS__)) + +jack_client_t * +gst_jack_client_open (const char *client_name, + jack_options_t options, jack_status_t * status, ...) +{ + g_assert (gst_jack_vtable.GstJackClientOpen != NULL); + + return _gst_jack_client_open (client_name, options, status); +}; + +jack_client_t * +gst_jack_client_new (const char *client_name) +{ + g_assert (gst_jack_vtable.GstJackClientNew != NULL); + + return gst_jack_vtable.GstJackClientNew (client_name); +} + +int +gst_jack_client_close (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackClientClose != NULL); + + return gst_jack_vtable.GstJackClientClose (client); +} + +int +gst_jack_activate (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackActivate != NULL); + + return gst_jack_vtable.GstJackActivate (client); +}; + +int +gst_jack_deactivate (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackDeactivate != NULL); + + return gst_jack_vtable.GstJackDeactivate (client); +}; + + +void +gst_jack_on_shutdown (jack_client_t * client, + JackShutdownCallback shutdown_callback, void *arg) +{ + g_assert (gst_jack_vtable.GstJackOnShutdown != NULL); + + gst_jack_vtable.GstJackOnShutdown (client, shutdown_callback, arg); +}; + +int +gst_jack_set_process_callback (jack_client_t * client, + JackProcessCallback process_callback, void *arg) +{ + g_assert (gst_jack_vtable.GstJackSetProcessCallback != NULL); + + return gst_jack_vtable.GstJackSetProcessCallback (client, process_callback, + arg); +}; + + +int +gst_jack_set_buffer_size_callback (jack_client_t * client, + JackBufferSizeCallback bufsize_callback, void *arg) +{ + g_assert (gst_jack_vtable.GstJackSetBufferSizeCallback != NULL); + + return gst_jack_vtable.GstJackSetBufferSizeCallback (client, bufsize_callback, + arg); +}; + +int +gst_jack_set_sample_rate_callback (jack_client_t * client, + JackSampleRateCallback srate_callback, void *arg) +{ + g_assert (gst_jack_vtable.GstJackSetSampleRateCallback != NULL); + + return gst_jack_vtable.GstJackSetSampleRateCallback (client, srate_callback, + arg); +}; + +int +gst_jack_set_buffer_size (jack_client_t * client, jack_nframes_t nframes) +{ + g_assert (gst_jack_vtable.GstJackSetBufferSize != NULL); + + return gst_jack_vtable.GstJackSetBufferSize (client, nframes); +}; + +jack_nframes_t +gst_jack_get_sample_rate (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackGetSampleRate != NULL); + + return gst_jack_vtable.GstJackGetSampleRate (client); +}; + +jack_nframes_t +gst_jack_get_buffer_size (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackGetBufferSize != NULL); + + return gst_jack_vtable.GstJackGetBufferSize (client); +}; + +jack_port_t * +gst_jack_port_register (jack_client_t * client, + const char *port_name, + const char *port_type, unsigned long flags, unsigned long buffer_size) +{ + g_assert (gst_jack_vtable.GstJackPortRegister != NULL); + + return gst_jack_vtable.GstJackPortRegister (client, port_name, port_type, + flags, buffer_size); + +}; + +int +gst_jack_port_unregister (jack_client_t * client, jack_port_t * port) +{ + g_assert (gst_jack_vtable.GstJackPortUnregister != NULL); + + return gst_jack_vtable.GstJackPortUnregister (client, port); +}; + +void * +gst_jack_port_get_buffer (jack_port_t * port, jack_nframes_t nframes) +{ + g_assert (gst_jack_vtable.GstJackPortGetBuffer != NULL); + + return gst_jack_vtable.GstJackPortGetBuffer (port, nframes); +}; + +const char * +gst_jack_port_name (const jack_port_t * port) +{ + g_assert (gst_jack_vtable.GstJackPortName != NULL); + + return gst_jack_vtable.GstJackPortName (port); +}; + +int +gst_jack_port_flags (const jack_port_t * port) +{ + g_assert (gst_jack_vtable.GstJackPortFlags != NULL); + + return gst_jack_vtable.GstJackPortFlags (port); +}; + +int +gst_jack_connect (jack_client_t * client, + const char *source_port, const char *destination_port) +{ + g_assert (gst_jack_vtable.GstJackConnect != NULL); + + return gst_jack_vtable.GstJackConnect (client, source_port, destination_port); +}; + +void +gst_jack_port_get_latency_range (jack_port_t * port, + jack_latency_callback_mode_t mode, jack_latency_range_t * range) +{ + g_assert (gst_jack_vtable.GstJackPortGetLatencyRange != NULL); + + gst_jack_vtable.GstJackPortGetLatencyRange (port, mode, range); +}; + +const char ** +gst_jack_get_ports (jack_client_t * client, + const char *port_name_pattern, + const char *type_name_pattern, unsigned long flags) +{ + g_assert (gst_jack_vtable.GstJackGetPorts != NULL); + + return gst_jack_vtable.GstJackGetPorts (client, port_name_pattern, + type_name_pattern, flags); +}; + +jack_port_t * +gst_jack_port_by_name (jack_client_t * client, const char *port_name) +{ + g_assert (gst_jack_vtable.GstJackPortByName != NULL); + + return gst_jack_vtable.GstJackPortByName (client, port_name); +}; + +void +gst_jack_set_error_function (void (*func) (const char *)) +{ + g_assert (gst_jack_vtable.GstJackSetErrorFunction != NULL); + + gst_jack_vtable.GstJackSetErrorFunction (func); +}; + +void +gst_jack_set_info_function (void (*func) (const char *)) +{ + g_assert (gst_jack_vtable.GstJackSetInfoFunction != NULL); + + gst_jack_vtable.GstJackSetInfoFunction (func); +}; + +void +gst_jack_free (void *ptr) +{ + g_assert (gst_jack_vtable.GstJackFree != NULL); + + gst_jack_vtable.GstJackFree (ptr); +} + +void +gst_jack_transport_start (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackTransportStart != NULL); + + gst_jack_vtable.GstJackTransportStart (client); +}; + +void +gst_jack_transport_stop (jack_client_t * client) +{ + g_assert (gst_jack_vtable.GstJackTransportStop != NULL); + + gst_jack_vtable.GstJackTransportStop (client); +}; + +jack_transport_state_t +gst_jack_transport_query (const jack_client_t * client, jack_position_t * pos) +{ + g_assert (gst_jack_vtable.GstJackTransportQuery != NULL); + + return gst_jack_vtable.GstJackTransportQuery (client, pos); +}; diff --git a/subprojects/gst-plugins-good/ext/jack/gstjackloader.h b/subprojects/gst-plugins-good/ext/jack/gstjackloader.h new file mode 100644 index 0000000000..41cf8bce07 --- /dev/null +++ b/subprojects/gst-plugins-good/ext/jack/gstjackloader.h @@ -0,0 +1,207 @@ +/* Jack + + Jack definitions copied from: + - jack/jack.h + - jack/types.h + - jack/transport.h + + Copyright (C) 2001 Paul Davis + Copyright (C) 2003 Jack O'Quin + Copyright (C) 2004 Jack O'Quin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _GST_JACK_WRAPPER_H_ +#define _GST_JACK_WRAPPER_H_ + +#include +#include +#include "gstjackloader.h" + +G_BEGIN_DECLS + +typedef uint32_t jack_nframes_t; +typedef struct _jack_client jack_client_t; +typedef struct _jack_port jack_port_t; +typedef struct _jack_position jack_position_t; +typedef struct _jack_latency_range jack_latency_range_t; + +typedef void (* JackShutdownCallback) (void *arg); +typedef int (* JackProcessCallback) (jack_nframes_t nframes, void *arg); +typedef int (* JackBufferSizeCallback) (jack_nframes_t nframes, void *arg); +typedef int (* JackSampleRateCallback) (jack_nframes_t nframes, void *arg); + +#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" + +struct _jack_latency_range + { + jack_nframes_t min; + jack_nframes_t max; + }; + +enum JackLatencyCallbackMode { + JackCaptureLatency, + JackPlaybackLatency +}; + +typedef enum JackLatencyCallbackMode jack_latency_callback_mode_t; + +enum JackOptions { + JackNullOption = 0x00, + JackNoStartServer = 0x01, + JackUseExactName = 0x02, + JackServerName = 0x04, + JackLoadName = 0x08, + JackLoadInit = 0x10, + JackSessionID = 0x20 +}; + +typedef enum JackOptions jack_options_t; + +enum JackStatus { + JackFailure = 0x01, + JackInvalidOption = 0x02, + JackNameNotUnique = 0x04, + JackServerStarted = 0x08, + JackServerFailed = 0x10, + JackServerError = 0x20, + JackNoSuchClient = 0x40, + JackLoadFailure = 0x80, + JackInitFailure = 0x100, + JackShmFailure = 0x200, + JackVersionError = 0x400, + /* + * BackendError + */ + JackBackendError = 0x800, + /* + * Client is being shutdown against its will + */ + JackClientZombie = 0x1000 +}; + +typedef enum JackStatus jack_status_t; + +typedef float jack_default_audio_sample_t; + + enum JackPortFlags { + JackPortIsInput = 0x1, + JackPortIsOutput = 0x2, + JackPortIsPhysical = 0x4, + JackPortCanMonitor = 0x8, + JackPortIsTerminal = 0x10 + }; + +typedef enum { + /* the order matters for binary compatibility */ + JackTransportStopped = 0, /* Transport halted */ + JackTransportRolling = 1, /* Transport playing */ + JackTransportLooping = 2, /* For OLD_TRANSPORT, now ignored */ + JackTransportStarting = 3, /* Waiting for sync ready */ + JackTransportNetStarting = 4, /* Waiting for sync ready on the network*/ +} jack_transport_state_t; + + +gboolean gst_jack_load_library (void); + +// +// jack/jack.h +// + +jack_client_t * gst_jack_client_open (const char *client_name, + jack_options_t options, + jack_status_t *status, ...); + +jack_client_t * gst_jack_client_new (const char *client_name) ; + +int gst_jack_client_close (jack_client_t *client); + +int gst_jack_activate (jack_client_t *client); + +int gst_jack_deactivate (jack_client_t *client); + +void gst_jack_on_shutdown (jack_client_t * client, + JackShutdownCallback shutdown_callback, + void *arg); + +int gst_jack_set_process_callback (jack_client_t * client, + JackProcessCallback process_callback, + void *arg); + + +int gst_jack_set_buffer_size_callback (jack_client_t *client, + JackBufferSizeCallback bufsize_callback, + void *arg); + +int gst_jack_set_sample_rate_callback (jack_client_t * client, + JackSampleRateCallback srate_callback, void *arg); + +int gst_jack_set_buffer_size (jack_client_t * client, jack_nframes_t nframes); + +jack_nframes_t gst_jack_get_sample_rate (jack_client_t * client); + +jack_nframes_t gst_jack_get_buffer_size (jack_client_t * client); + +jack_port_t * gst_jack_port_register (jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size); + +int gst_jack_port_unregister (jack_client_t *client, jack_port_t *port); + + +void * gst_jack_port_get_buffer (jack_port_t* port, jack_nframes_t nframes); + +const char * gst_jack_port_name (const jack_port_t* port); + +int gst_jack_port_flags (const jack_port_t*port); + +int gst_jack_connect (jack_client_t * client, + const char *source_port, + const char *destination_port); + +void gst_jack_port_get_latency_range (jack_port_t* port, + jack_latency_callback_mode_t mode, + jack_latency_range_t * range); + +const char ** gst_jack_get_ports (jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags); + +jack_port_t * gst_jack_port_by_name (jack_client_t * client, + const char *port_name); + +void gst_jack_set_error_function (void (*func)(const char *)); + +void gst_jack_set_info_function (void (*func)(const char *)); + +void gst_jack_free (void* ptr); + +// +// jack/transport.h +// + +void gst_jack_transport_start (jack_client_t *client); + +void gst_jack_transport_stop (jack_client_t *client); + +jack_transport_state_t gst_jack_transport_query (const jack_client_t *client, + jack_position_t *pos); + +G_END_DECLS +#endif // _GST_JACK_UTIL_H_ diff --git a/subprojects/gst-plugins-good/ext/jack/meson.build b/subprojects/gst-plugins-good/ext/jack/meson.build index fe67e74458..f5780e266b 100644 --- a/subprojects/gst-plugins-good/ext/jack/meson.build +++ b/subprojects/gst-plugins-good/ext/jack/meson.build @@ -4,6 +4,7 @@ jack_sources = [ 'gstjackaudiosrc.c', 'gstjack.c', 'gstjackutil.c', + 'gstjackloader.c', ] jack_option = get_option('jack') @@ -11,90 +12,11 @@ if jack_option.disabled() subdir_done() endif -jack_incdirs = [configinc, libsinc] - -libjack_dep = dependency('jack', version : '>= 1.9.7', required : false) - -if not libjack_dep.found() - fs = import('fs') - host_cpu = host_machine.cpu_family() - jack_maybe_installed = false - error_msg = '"jack" option is enabled but ' - if (host_system == 'windows' and build_machine.system() == 'windows') - # Need to detect whether we're running on 64-bit Windows or not. - # If `C:/Program Files (x86)/` exists, we're running on 64-bit Windows, and - # C:/Program Files/ contains 64-bit programs. Else, we're on 32-bit Windows - # and C:/Program Files/ contains 32-bit programs. - # - # The user could either have a 32-bit JACK installation or a 64-bit one. - # When building for 32-bit x86, we need to check for both. - if fs.is_dir('C:/Program Files (x86)') - jack64_install_dir = 'C:/Program Files/JACK2' - jack32_install_dir = 'C:/Program Files (x86)/JACK2' - else - jack64_install_dir = '' - jack32_install_dir = 'C:/Program Files/JACK2' - endif - - if host_cpu == 'x86' - jack_install_dir = jack32_install_dir - jack_maybe_installed = fs.is_dir(jack32_install_dir / 'include') - if not jack_maybe_installed and jack64_install_dir != '' - jack_maybe_installed = fs.is_dir(jack64_install_dir / 'include') - jack_install_dir = jack64_install_dir - endif - elif jack64_install_dir != '' - jack_maybe_installed = import('fs').is_dir(jack64_install_dir / 'include') - jack_install_dir = jack64_install_dir - endif - - error_msg += 'JACK2 installation could not be found' - else - error_msg += 'JACK dependency could not be found' - endif - - if not jack_maybe_installed - if jack_option.enabled() - error(error_msg) - endif - subdir_done() - endif - - if not host_cpu.startswith('x86') - if jack_option.enabled() - error('On Windows, JACK only supports x86 32-bit and 64-bit') - endif - subdir_done() - endif - - if host_cpu == 'x86' - jack_libname = 'libjack' - if jack_install_dir == jack64_install_dir - jack_libdir = jack_install_dir / 'lib32' - else - jack_libdir = jack_install_dir / 'lib' - endif - else - jack_libname = 'libjack64' - jack_libdir = jack_install_dir / 'lib' - endif - - inc = include_directories(jack_install_dir / 'include') - libjack_dep = cc.find_library(jack_libname, - dirs: jack_libdir, - has_headers: 'jack/jack.h', - header_include_directories: inc, - required: jack_option) - # This won't be needed once we require a meson version that includes this: - # https://github.com/mesonbuild/meson/pull/10428 - jack_incdirs += inc -endif - gstjack = library('gstjack', jack_sources, c_args : gst_plugins_good_args, - include_directories : jack_incdirs, - dependencies : [gst_dep, gstbase_dep, gstaudio_dep, libjack_dep], + include_directories : [configinc], + dependencies : [gst_dep, gstbase_dep, gstaudio_dep, gmodule_dep], install : true, install_dir : plugins_install_dir, ) diff --git a/subprojects/gst-plugins-good/tests/examples/jack/meson.build b/subprojects/gst-plugins-good/tests/examples/jack/meson.build index f964216896..fa87fb455d 100644 --- a/subprojects/gst-plugins-good/tests/examples/jack/meson.build +++ b/subprojects/gst-plugins-good/tests/examples/jack/meson.build @@ -2,10 +2,90 @@ if get_option('jack').disabled() subdir_done() endif -if libjack_dep.found() - executable('jack_client', 'jack_client.c', - dependencies: [gst_dep, gtk_dep, libjack_dep], - c_args: gst_plugins_good_args, - include_directories: [configinc], - install: false) +jack_incdirs = [configinc, libsinc] + +# While we are dlopening jack for the gstjack plugins, or the example +# we should link against jack and use its api directly as that's the +# usage we expect for applications. +libjack_dep = dependency('jack', version : '>= 1.9.7', required : false) + +if not libjack_dep.found() + fs = import('fs') + host_cpu = host_machine.cpu_family() + jack_maybe_installed = false + error_msg = '"jack" option is enabled but ' + if (host_system == 'windows' and build_machine.system() == 'windows') + # Need to detect whether we're running on 64-bit Windows or not. + # If `C:/Program Files (x86)/` exists, we're running on 64-bit Windows, and + # C:/Program Files/ contains 64-bit programs. Else, we're on 32-bit Windows + # and C:/Program Files/ contains 32-bit programs. + # + # The user could either have a 32-bit JACK installation or a 64-bit one. + # When building for 32-bit x86, we need to check for both. + if fs.is_dir('C:/Program Files (x86)') + jack64_install_dir = 'C:/Program Files/JACK2' + jack32_install_dir = 'C:/Program Files (x86)/JACK2' + else + jack64_install_dir = '' + jack32_install_dir = 'C:/Program Files/JACK2' + endif + + if host_cpu == 'x86' + jack_install_dir = jack32_install_dir + jack_maybe_installed = fs.is_dir(jack32_install_dir / 'include') + if not jack_maybe_installed and jack64_install_dir != '' + jack_maybe_installed = fs.is_dir(jack64_install_dir / 'include') + jack_install_dir = jack64_install_dir + endif + elif jack64_install_dir != '' + jack_maybe_installed = import('fs').is_dir(jack64_install_dir / 'include') + jack_install_dir = jack64_install_dir + endif + + error_msg += 'JACK2 installation could not be found' + else + error_msg += 'JACK dependency could not be found' + endif + + if not jack_maybe_installed + if jack_option.enabled() + error(error_msg) + endif + subdir_done() + endif + + if not host_cpu.startswith('x86') + if jack_option.enabled() + error('On Windows, JACK only supports x86 32-bit and 64-bit') + endif + subdir_done() + endif + + if host_cpu == 'x86' + jack_libname = 'libjack' + if jack_install_dir == jack64_install_dir + jack_libdir = jack_install_dir / 'lib32' + else + jack_libdir = jack_install_dir / 'lib' + endif + else + jack_libname = 'libjack64' + jack_libdir = jack_install_dir / 'lib' + endif + + inc = include_directories(jack_install_dir / 'include') + libjack_dep = cc.find_library(jack_libname, + dirs: jack_libdir, + has_headers: 'jack/jack.h', + header_include_directories: inc, + required: jack_option) + # This won't be needed once we require a meson version that includes this: + # https://github.com/mesonbuild/meson/pull/10428 + jack_incdirs += inc endif + +executable('jack_client', 'jack_client.c', + dependencies: [gst_dep, gtk_dep, libjack_dep], + c_args: gst_plugins_good_args, + include_directories: jack_incdirs, + install: false)