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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4350>
This commit is contained in:
Jordan Petridis 2023-03-29 19:50:19 +03:00
parent e709e2d97c
commit 689dbd1fbe
12 changed files with 834 additions and 149 deletions

View file

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

View file

@ -22,8 +22,8 @@
#ifndef _GST_JACK_H_
#define _GST_JACK_H_
#include <jack/jack.h>
#include <gst/audio/audio.h>
#include "gstjackloader.h"
GST_ELEMENT_REGISTER_DECLARE (jackaudiosrc);
GST_ELEMENT_REGISTER_DECLARE (jackaudiosink);

View file

@ -24,8 +24,6 @@
#include "gstjackaudioclient.h"
#include "gstjack.h"
#include <gst/glib-compat-private.h>
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);

View file

@ -22,9 +22,8 @@
#ifndef __GST_JACK_AUDIO_CLIENT_H__
#define __GST_JACK_AUDIO_CLIENT_H__
#include <jack/jack.h>
#include <gst/gst.h>
#include "gstjackloader.h"
G_BEGIN_DECLS

View file

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

View file

@ -22,13 +22,12 @@
#ifndef __GST_JACK_AUDIO_SINK_H__
#define __GST_JACK_AUDIO_SINK_H__
#include <jack/jack.h>
#include <gst/gst.h>
#include <gst/audio/gstaudiobasesink.h>
#include "gstjack.h"
#include "gstjackaudioclient.h"
#include "gstjackloader.h"
G_BEGIN_DECLS

View file

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

View file

@ -43,13 +43,12 @@
#ifndef __GST_JACK_AUDIO_SRC_H__
#define __GST_JACK_AUDIO_SRC_H__
#include <jack/jack.h>
#include <gst/gst.h>
#include <gst/audio/gstaudiosrc.h>
#include "gstjackaudioclient.h"
#include "gstjack.h"
#include "gstjackloader.h"
G_BEGIN_DECLS

View file

@ -0,0 +1,472 @@
/* GStreamer
* Copyright (C) 2023 Jordan Petridis <jordan@centricular.com>
*
* 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 <stdio.h>
#include <gmodule.h>
#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, &micro) == 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);
};

View file

@ -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 <stdint.h>
#include <gst/gst.h>
#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_

View file

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

View file

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