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)