| /* 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, µ) == 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); | |
| }; |