mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
ext/jack/: Make an object to manage client connections to the jack server which we will use in the future to run sele...
Original commit message from CVS: Includes patch by: Paul Davis <paul at linuxaudiosystems dot com> * ext/jack/Makefile.am: * ext/jack/gstjackaudioclient.c: (gst_jack_audio_client_init), (jack_process_cb), (jack_sample_rate_cb), (jack_buffer_size_cb), (jack_shutdown_cb), (connection_find), (gst_jack_audio_make_connection), (gst_jack_audio_get_connection), (gst_jack_audio_unref_connection), (gst_jack_audio_connection_add_client), (gst_jack_audio_connection_remove_client), (gst_jack_audio_client_new), (gst_jack_audio_client_free), (gst_jack_audio_client_get_client), (gst_jack_audio_client_set_active): * ext/jack/gstjackaudioclient.h: Make an object to manage client connections to the jack server which we will use in the future to run selected jack elements with the same jack connection. Make some stuff a bit more threadsafe. Activate the jack client ASAP. * ext/jack/gstjackaudiosink.c: (gst_jack_audio_sink_allocate_channels), (gst_jack_audio_sink_free_channels), (jack_process_cb), (gst_jack_ring_buffer_open_device), (gst_jack_ring_buffer_close_device), (gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release), (gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init), (gst_jack_audio_sink_getcaps): * ext/jack/gstjackaudiosink.h: Use new client object to manage connections. Don't remove and recreate all ports, try to reuse them.
This commit is contained in:
parent
49e29a2716
commit
f1b91e369d
7 changed files with 669 additions and 69 deletions
34
ChangeLog
34
ChangeLog
|
@ -1,3 +1,37 @@
|
|||
2007-03-08 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
Includes patch by: Paul Davis <paul at linuxaudiosystems dot com>
|
||||
|
||||
* ext/jack/Makefile.am:
|
||||
* ext/jack/gstjackaudioclient.c: (gst_jack_audio_client_init),
|
||||
(jack_process_cb), (jack_sample_rate_cb), (jack_buffer_size_cb),
|
||||
(jack_shutdown_cb), (connection_find),
|
||||
(gst_jack_audio_make_connection), (gst_jack_audio_get_connection),
|
||||
(gst_jack_audio_unref_connection),
|
||||
(gst_jack_audio_connection_add_client),
|
||||
(gst_jack_audio_connection_remove_client),
|
||||
(gst_jack_audio_client_new), (gst_jack_audio_client_free),
|
||||
(gst_jack_audio_client_get_client),
|
||||
(gst_jack_audio_client_set_active):
|
||||
* ext/jack/gstjackaudioclient.h:
|
||||
Make an object to manage client connections to the jack server which we
|
||||
will use in the future to run selected jack elements with the same jack
|
||||
connection.
|
||||
Make some stuff a bit more threadsafe.
|
||||
Activate the jack client ASAP.
|
||||
|
||||
* ext/jack/gstjackaudiosink.c:
|
||||
(gst_jack_audio_sink_allocate_channels),
|
||||
(gst_jack_audio_sink_free_channels), (jack_process_cb),
|
||||
(gst_jack_ring_buffer_open_device),
|
||||
(gst_jack_ring_buffer_close_device),
|
||||
(gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release),
|
||||
(gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init),
|
||||
(gst_jack_audio_sink_getcaps):
|
||||
* ext/jack/gstjackaudiosink.h:
|
||||
Use new client object to manage connections.
|
||||
Don't remove and recreate all ports, try to reuse them.
|
||||
|
||||
2007-03-07 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
|
||||
* ext/wavpack/gstwavpack.c: (plugin_init):
|
||||
|
|
2
common
2
common
|
@ -1 +1 @@
|
|||
Subproject commit c4f56a657d79aee0e3fc25ef2bcf876f9f3c1593
|
||||
Subproject commit 7c5a0ab68de1fed4e5a1fd473160debc2c4c7b89
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
plugin_LTLIBRARIES = libgstjack.la
|
||||
|
||||
libgstjack_la_SOURCES = gstjack.c gstjackaudiosink.c
|
||||
libgstjack_la_SOURCES = gstjack.c gstjackaudiosink.c gstjackaudioclient.c
|
||||
libgstjack_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(JACK_CFLAGS)
|
||||
libgstjack_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(JACK_LIBS)
|
||||
libgstjack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
||||
noinst_HEADERS = gstjackaudiosink.h
|
||||
noinst_HEADERS = gstjackaudiosink.h gstjackaudioclient.h
|
||||
|
||||
EXTRA_DIST = README
|
||||
|
|
484
ext/jack/gstjackaudioclient.c
Normal file
484
ext/jack/gstjackaudioclient.c
Normal file
|
@ -0,0 +1,484 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2006 Wim Taymans <wim@fluendo.com>
|
||||
*
|
||||
* gstjackaudioclient.c: jack audio client implementation
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gstjackaudioclient.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug);
|
||||
#define GST_CAT_DEFAULT gst_jack_audio_client_debug
|
||||
|
||||
void
|
||||
gst_jack_audio_client_init (void)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0,
|
||||
"jackclient helpers");
|
||||
}
|
||||
|
||||
/* a list of global connections indexed by id and server. */
|
||||
G_LOCK_DEFINE_STATIC (connections_lock);
|
||||
static GList *connections;
|
||||
|
||||
/* the connection to a server */
|
||||
typedef struct
|
||||
{
|
||||
gint refcount;
|
||||
GMutex *lock;
|
||||
|
||||
/* id/server pair and the connection */
|
||||
gchar *id;
|
||||
gchar *server;
|
||||
jack_client_t *client;
|
||||
|
||||
/* lists of GstJackAudioClients */
|
||||
gint n_clients;
|
||||
GList *src_clients;
|
||||
GList *sink_clients;
|
||||
} GstJackAudioConnection;
|
||||
|
||||
/* an object sharing a jack_client_t connection. */
|
||||
struct _GstJackAudioClient
|
||||
{
|
||||
GstJackAudioConnection *conn;
|
||||
|
||||
GstJackClientType type;
|
||||
gboolean active;
|
||||
|
||||
void (*shutdown) (void *arg);
|
||||
JackProcessCallback process;
|
||||
JackBufferSizeCallback buffer_size;
|
||||
JackSampleRateCallback sample_rate;
|
||||
gpointer user_data;
|
||||
};
|
||||
|
||||
typedef jack_default_audio_sample_t sample_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
jack_nframes_t nframes;
|
||||
gpointer user_data;
|
||||
} JackCB;
|
||||
|
||||
static int
|
||||
jack_process_cb (jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
|
||||
GList *walk;
|
||||
int res = 0;
|
||||
|
||||
g_mutex_lock (conn->lock);
|
||||
/* call sources first, then sinks. Sources will either push data into the
|
||||
* ringbuffer of the sinks, which will then pull the data out of it, or
|
||||
* sinks will pull the data from the sources. */
|
||||
for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
|
||||
GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
|
||||
|
||||
/* only call active clients */
|
||||
if (client->active && client->process)
|
||||
res = client->process (nframes, client->user_data);
|
||||
}
|
||||
for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
|
||||
GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
|
||||
|
||||
/* only call active clients */
|
||||
if (client->active && client->process)
|
||||
res = client->process (nframes, client->user_data);
|
||||
}
|
||||
g_mutex_unlock (conn->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* we error out */
|
||||
static int
|
||||
jack_sample_rate_cb (jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we error out */
|
||||
static int
|
||||
jack_buffer_size_cb (jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
jack_shutdown_cb (void *arg)
|
||||
{
|
||||
GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
|
||||
GList *walk;
|
||||
|
||||
g_mutex_lock (conn->lock);
|
||||
for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
|
||||
GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
|
||||
|
||||
if (client->shutdown)
|
||||
client->shutdown (client->user_data);
|
||||
}
|
||||
for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
|
||||
GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
|
||||
|
||||
if (client->shutdown)
|
||||
client->shutdown (client->user_data);
|
||||
}
|
||||
g_mutex_unlock (conn->lock);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *id;
|
||||
const gchar *server;
|
||||
} FindData;
|
||||
|
||||
static gint
|
||||
connection_find (GstJackAudioConnection * conn, FindData * data)
|
||||
{
|
||||
/* id's must match */
|
||||
if (strcmp (conn->id, data->id))
|
||||
return 1;
|
||||
|
||||
/* both the same or NULL */
|
||||
if (conn->server == data->server)
|
||||
return 0;
|
||||
|
||||
/* we cannot compare NULL */
|
||||
if (conn->server == NULL || data->server == NULL)
|
||||
return 1;
|
||||
|
||||
if (strcmp (conn->server, data->server))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* make a connection with @id and @server. Returns NULL on failure with the
|
||||
* status set. */
|
||||
static GstJackAudioConnection *
|
||||
gst_jack_audio_make_connection (const gchar * id, const gchar * server,
|
||||
jack_status_t * status)
|
||||
{
|
||||
GstJackAudioConnection *conn;
|
||||
jack_options_t options;
|
||||
jack_client_t *jclient;
|
||||
gint res;
|
||||
|
||||
*status = 0;
|
||||
|
||||
GST_DEBUG ("new client %s, connecting to server %s", id,
|
||||
GST_STR_NULL (server));
|
||||
|
||||
/* never start a server */
|
||||
options = JackNoStartServer;
|
||||
/* if we have a servername, use it */
|
||||
if (server != NULL)
|
||||
options |= JackServerName;
|
||||
/* open the client */
|
||||
jclient = jack_client_open (id, options, status, server);
|
||||
if (jclient == NULL)
|
||||
goto could_not_open;
|
||||
|
||||
/* now create object */
|
||||
conn = g_new (GstJackAudioConnection, 1);
|
||||
conn->refcount = 1;
|
||||
conn->lock = g_mutex_new ();
|
||||
conn->id = g_strdup (id);
|
||||
conn->server = g_strdup (server);
|
||||
conn->client = jclient;
|
||||
conn->n_clients = 0;
|
||||
conn->src_clients = NULL;
|
||||
conn->sink_clients = NULL;
|
||||
|
||||
/* set our callbacks */
|
||||
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);
|
||||
|
||||
/* all callbacks are set, activate the client */
|
||||
if ((res = jack_activate (jclient)))
|
||||
goto could_not_activate;
|
||||
|
||||
GST_DEBUG ("opened connection %p", conn);
|
||||
|
||||
return conn;
|
||||
|
||||
/* ERRORS */
|
||||
could_not_open:
|
||||
{
|
||||
GST_DEBUG ("failed to open jack client, %d", *status);
|
||||
return NULL;
|
||||
}
|
||||
could_not_activate:
|
||||
{
|
||||
GST_ERROR ("Could not activate client (%d)", res);
|
||||
*status = JackFailure;
|
||||
g_mutex_free (conn->lock);
|
||||
g_free (conn->id);
|
||||
g_free (conn->server);
|
||||
g_free (conn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static GstJackAudioConnection *
|
||||
gst_jack_audio_get_connection (const gchar * id, const gchar * server,
|
||||
jack_status_t * status)
|
||||
{
|
||||
GstJackAudioConnection *conn;
|
||||
GList *found;
|
||||
FindData data;
|
||||
|
||||
GST_DEBUG ("getting connection for id %s, server %s", id,
|
||||
GST_STR_NULL (server));
|
||||
|
||||
data.id = id;
|
||||
data.server = server;
|
||||
|
||||
G_LOCK (connections_lock);
|
||||
found =
|
||||
g_list_find_custom (connections, &data, (GCompareFunc) connection_find);
|
||||
if (found != NULL) {
|
||||
/* we found it, increase refcount and return it */
|
||||
conn = (GstJackAudioConnection *) found->data;
|
||||
conn->refcount++;
|
||||
|
||||
GST_DEBUG ("found connection %p", conn);
|
||||
} else {
|
||||
/* make new connection */
|
||||
conn = gst_jack_audio_make_connection (id, server, status);
|
||||
if (conn != NULL) {
|
||||
GST_DEBUG ("created connection %p", conn);
|
||||
/* add to list on success */
|
||||
connections = g_list_prepend (connections, conn);
|
||||
} else {
|
||||
GST_WARNING ("could not create connection");
|
||||
}
|
||||
}
|
||||
G_UNLOCK (connections_lock);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jack_audio_unref_connection (GstJackAudioConnection * conn)
|
||||
{
|
||||
gint res;
|
||||
|
||||
GST_DEBUG ("unref connection %p", conn);
|
||||
|
||||
G_LOCK (connections_lock);
|
||||
conn->refcount--;
|
||||
if (conn->refcount == 0) {
|
||||
GST_DEBUG ("closing connection %p", conn);
|
||||
/* remove from list */
|
||||
connections = g_list_remove (connections, conn);
|
||||
|
||||
/* grab lock to be sure that we are not in one of the callbacks */
|
||||
g_mutex_lock (conn->lock);
|
||||
if ((res = 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))) {
|
||||
/* we assume the client is gone. */
|
||||
GST_WARNING ("close failed (%d)", res);
|
||||
}
|
||||
g_mutex_unlock (conn->lock);
|
||||
|
||||
/* free resources */
|
||||
g_mutex_free (conn->lock);
|
||||
g_free (conn->id);
|
||||
g_free (conn->server);
|
||||
g_free (conn);
|
||||
}
|
||||
G_UNLOCK (connections_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jack_audio_connection_add_client (GstJackAudioConnection * conn,
|
||||
GstJackAudioClient * client)
|
||||
{
|
||||
g_mutex_lock (conn->lock);
|
||||
switch (client->type) {
|
||||
case GST_JACK_CLIENT_SOURCE:
|
||||
conn->src_clients = g_list_append (conn->src_clients, client);
|
||||
conn->n_clients++;
|
||||
break;
|
||||
case GST_JACK_CLIENT_SINK:
|
||||
conn->sink_clients = g_list_append (conn->sink_clients, client);
|
||||
conn->n_clients++;
|
||||
break;
|
||||
default:
|
||||
g_warning ("trying to add unknown client type");
|
||||
break;
|
||||
}
|
||||
g_mutex_unlock (conn->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jack_audio_connection_remove_client (GstJackAudioConnection * conn,
|
||||
GstJackAudioClient * client)
|
||||
{
|
||||
g_mutex_lock (conn->lock);
|
||||
switch (client->type) {
|
||||
case GST_JACK_CLIENT_SOURCE:
|
||||
conn->src_clients = g_list_remove (conn->src_clients, client);
|
||||
conn->n_clients--;
|
||||
break;
|
||||
case GST_JACK_CLIENT_SINK:
|
||||
conn->sink_clients = g_list_remove (conn->sink_clients, client);
|
||||
conn->n_clients--;
|
||||
break;
|
||||
default:
|
||||
g_warning ("trying to remove unknown client type");
|
||||
break;
|
||||
}
|
||||
g_mutex_unlock (conn->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_jack_audio_client_get:
|
||||
* @id: the client id
|
||||
* @server: the server to connect to or NULL for the default server
|
||||
* @type: the client type
|
||||
* @shutdown: a callback when the jack server shuts down
|
||||
* @process: a callback when samples are available
|
||||
* @buffer_size: a callback when the buffer_size changes
|
||||
* @sample_rate: a callback when the sample_rate changes
|
||||
* @user_data: user data passed to the callbacks
|
||||
* @status: pointer to hold the jack status code in case of errors
|
||||
*
|
||||
* Get the jack client connection for @id and @server. Connections to the same
|
||||
* @id and @server will receive the same physical Jack client connection and
|
||||
* will therefore be scheduled in the same process callback.
|
||||
*
|
||||
* Returns: a #GstJackAudioClient.
|
||||
*/
|
||||
GstJackAudioClient *
|
||||
gst_jack_audio_client_new (const gchar * id, const gchar * server,
|
||||
GstJackClientType type, void (*shutdown) (void *arg),
|
||||
JackProcessCallback process, JackBufferSizeCallback buffer_size,
|
||||
JackSampleRateCallback sample_rate, gpointer user_data,
|
||||
jack_status_t * status)
|
||||
{
|
||||
GstJackAudioClient *client;
|
||||
GstJackAudioConnection *conn;
|
||||
|
||||
g_return_val_if_fail (id != NULL, NULL);
|
||||
g_return_val_if_fail (status != NULL, NULL);
|
||||
|
||||
/* first get a connection for the id/server pair */
|
||||
conn = gst_jack_audio_get_connection (id, server, status);
|
||||
if (conn == NULL)
|
||||
goto no_connection;
|
||||
|
||||
/* make new client using the connection */
|
||||
client = g_new (GstJackAudioClient, 1);
|
||||
client->active = FALSE;
|
||||
client->conn = conn;
|
||||
client->type = type;
|
||||
client->shutdown = shutdown;
|
||||
client->process = process;
|
||||
client->buffer_size = buffer_size;
|
||||
client->sample_rate = sample_rate;
|
||||
client->user_data = user_data;
|
||||
|
||||
/* add the client to the connection */
|
||||
gst_jack_audio_connection_add_client (conn, client);
|
||||
|
||||
return client;
|
||||
|
||||
/* ERRORS */
|
||||
no_connection:
|
||||
{
|
||||
GST_DEBUG ("Could not get server connection (%d)", *status);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_jack_audio_client_free:
|
||||
* @client: a #GstJackAudioClient
|
||||
*
|
||||
* Free the resources used by @client.
|
||||
*/
|
||||
void
|
||||
gst_jack_audio_client_free (GstJackAudioClient * client)
|
||||
{
|
||||
GstJackAudioConnection *conn;
|
||||
|
||||
g_return_if_fail (client != NULL);
|
||||
|
||||
conn = client->conn;
|
||||
|
||||
/* remove from connection first so that it's not scheduled anymore after this
|
||||
* call */
|
||||
gst_jack_audio_connection_remove_client (conn, client);
|
||||
gst_jack_audio_unref_connection (conn);
|
||||
|
||||
g_free (client);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_jack_audio_client_get_client:
|
||||
* @client: a #GstJackAudioClient
|
||||
*
|
||||
* Get the jack audio client for @client. This function is used to perform
|
||||
* operations on the jack server from this client.
|
||||
*
|
||||
* Returns: The jack audio client.
|
||||
*/
|
||||
jack_client_t *
|
||||
gst_jack_audio_client_get_client (GstJackAudioClient * client)
|
||||
{
|
||||
g_return_val_if_fail (client != NULL, NULL);
|
||||
|
||||
/* no lock needed, the connection and the client does not change
|
||||
* once the client is created. */
|
||||
return client->conn->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_jack_audio_client_set_active:
|
||||
* @client: a #GstJackAudioClient
|
||||
* @active: new mode for the client
|
||||
*
|
||||
* Activate or deactive @client. When a client is activated it will receive
|
||||
* callbacks when data should be processed.
|
||||
*
|
||||
* Returns: 0 if all ok.
|
||||
*/
|
||||
gint
|
||||
gst_jack_audio_client_set_active (GstJackAudioClient * client, gboolean active)
|
||||
{
|
||||
g_return_val_if_fail (client != NULL, -1);
|
||||
|
||||
/* make sure that we are not dispatching the client */
|
||||
g_mutex_lock (client->conn->lock);
|
||||
client->active = active;
|
||||
g_mutex_unlock (client->conn->lock);
|
||||
|
||||
return 0;
|
||||
}
|
58
ext/jack/gstjackaudioclient.h
Normal file
58
ext/jack/gstjackaudioclient.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2006 Wim Taymans <wim@fluendo.com>
|
||||
*
|
||||
* gstjackaudioclient.h:
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_JACK_AUDIO_CLIENT_H__
|
||||
#define __GST_JACK_AUDIO_CLIENT_H__
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GST_JACK_CLIENT_SOURCE,
|
||||
GST_JACK_CLIENT_SINK
|
||||
} GstJackClientType;
|
||||
|
||||
typedef struct _GstJackAudioClient GstJackAudioClient;
|
||||
|
||||
void gst_jack_audio_client_init (void);
|
||||
|
||||
|
||||
GstJackAudioClient * gst_jack_audio_client_new (const gchar *id, const gchar *server,
|
||||
GstJackClientType type,
|
||||
void (*shutdown) (void *arg),
|
||||
JackProcessCallback process,
|
||||
JackBufferSizeCallback buffer_size,
|
||||
JackSampleRateCallback sample_rate,
|
||||
gpointer user_data,
|
||||
jack_status_t *status);
|
||||
void gst_jack_audio_client_free (GstJackAudioClient *client);
|
||||
|
||||
jack_client_t * gst_jack_audio_client_get_client (GstJackAudioClient *client);
|
||||
|
||||
gboolean gst_jack_audio_client_set_active (GstJackAudioClient *client, gboolean active);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_JACK_AUDIO_CLIENT_H__ */
|
|
@ -96,8 +96,6 @@ struct _GstJackRingBuffer
|
|||
gint sample_rate;
|
||||
gint buffer_size;
|
||||
gint channels;
|
||||
|
||||
jack_port_t **outport;
|
||||
};
|
||||
|
||||
struct _GstJackRingBufferClass
|
||||
|
@ -123,6 +121,60 @@ static gboolean gst_jack_ring_buffer_pause (GstRingBuffer * buf);
|
|||
static gboolean gst_jack_ring_buffer_stop (GstRingBuffer * buf);
|
||||
static guint gst_jack_ring_buffer_delay (GstRingBuffer * buf);
|
||||
|
||||
static gboolean
|
||||
gst_jack_audio_sink_allocate_channels (GstJackAudioSink * sink, gint channels)
|
||||
{
|
||||
jack_client_t *client;
|
||||
|
||||
client = gst_jack_audio_client_get_client (sink->client);
|
||||
|
||||
/* remove ports we don't need */
|
||||
while (sink->port_count > channels) {
|
||||
jack_port_unregister (client, sink->ports[--sink->port_count]);
|
||||
}
|
||||
|
||||
/* alloc enough output ports */
|
||||
sink->ports = g_realloc (sink->ports, sizeof (jack_port_t *) * channels);
|
||||
|
||||
/* create an output port for each channel */
|
||||
while (sink->port_count < channels) {
|
||||
gchar *name;
|
||||
|
||||
/* port names start from 1 */
|
||||
name = g_strdup_printf ("out_%d", sink->port_count + 1);
|
||||
sink->ports[sink->port_count] =
|
||||
jack_port_register (client, name,
|
||||
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
if (sink->ports[sink->port_count] == NULL)
|
||||
return FALSE;
|
||||
|
||||
sink->port_count++;
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_jack_audio_sink_free_channels (GstJackAudioSink * sink)
|
||||
{
|
||||
gint res, i = 0;
|
||||
jack_client_t *client;
|
||||
|
||||
client = gst_jack_audio_client_get_client (sink->client);
|
||||
|
||||
/* 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++]))) {
|
||||
GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res);
|
||||
}
|
||||
sink->port_count--;
|
||||
}
|
||||
g_free (sink->ports);
|
||||
sink->ports = NULL;
|
||||
}
|
||||
|
||||
/* ringbuffer abstract base class */
|
||||
static GType
|
||||
gst_jack_ring_buffer_get_type (void)
|
||||
|
@ -206,7 +258,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg)
|
|||
|
||||
/* get target buffers */
|
||||
for (i = 0; i < channels; i++) {
|
||||
buffers[i] = (sample_t *) jack_port_get_buffer (abuf->outport[i], nframes);
|
||||
buffers[i] = (sample_t *) jack_port_get_buffer (sink->ports[i], nframes);
|
||||
}
|
||||
|
||||
if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) {
|
||||
|
@ -343,30 +395,19 @@ static gboolean
|
|||
gst_jack_ring_buffer_open_device (GstRingBuffer * buf)
|
||||
{
|
||||
GstJackAudioSink *sink;
|
||||
jack_options_t options;
|
||||
jack_status_t status = 0;
|
||||
|
||||
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "open");
|
||||
|
||||
/* never start a server */
|
||||
options = JackNoStartServer;
|
||||
/* if we have a servername, use it */
|
||||
if (sink->server != NULL)
|
||||
options |= JackServerName;
|
||||
/* open the client */
|
||||
sink->client = jack_client_open ("GStreamer", options, &status, sink->server);
|
||||
sink->client = gst_jack_audio_client_new ("GStreamer", sink->server,
|
||||
GST_JACK_CLIENT_SINK,
|
||||
jack_shutdown_cb,
|
||||
jack_process_cb, jack_buffer_size_cb, jack_sample_rate_cb, buf, &status);
|
||||
if (sink->client == NULL)
|
||||
goto could_not_open;
|
||||
|
||||
/* set our callbacks */
|
||||
jack_set_process_callback (sink->client, jack_process_cb, buf);
|
||||
/* these callbacks cause us to error */
|
||||
jack_set_buffer_size_callback (sink->client, jack_buffer_size_cb, buf);
|
||||
jack_set_sample_rate_callback (sink->client, jack_sample_rate_cb, buf);
|
||||
jack_on_shutdown (sink->client, jack_shutdown_cb, buf);
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "opened");
|
||||
|
||||
return TRUE;
|
||||
|
@ -391,17 +432,13 @@ static gboolean
|
|||
gst_jack_ring_buffer_close_device (GstRingBuffer * buf)
|
||||
{
|
||||
GstJackAudioSink *sink;
|
||||
gint res;
|
||||
|
||||
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "close");
|
||||
|
||||
if ((res = jack_client_close (sink->client))) {
|
||||
/* just a warning, we assume the client is gone. */
|
||||
GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE,
|
||||
(NULL), ("Jack client close error (%d)", res));
|
||||
}
|
||||
gst_jack_audio_sink_free_channels (sink);
|
||||
gst_jack_audio_client_free (sink->client);
|
||||
sink->client = NULL;
|
||||
|
||||
return TRUE;
|
||||
|
@ -426,37 +463,26 @@ gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
const char **ports;
|
||||
gint sample_rate, buffer_size;
|
||||
gint i, channels, res;
|
||||
jack_client_t *client;
|
||||
|
||||
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
|
||||
abuf = GST_JACK_RING_BUFFER_CAST (buf);
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "acquire");
|
||||
|
||||
client = gst_jack_audio_client_get_client (sink->client);
|
||||
|
||||
/* sample rate must be that of the server */
|
||||
sample_rate = jack_get_sample_rate (sink->client);
|
||||
sample_rate = jack_get_sample_rate (client);
|
||||
if (sample_rate != spec->rate)
|
||||
goto wrong_samplerate;
|
||||
|
||||
channels = spec->channels;
|
||||
|
||||
/* alloc enough output ports */
|
||||
abuf->outport = g_new (jack_port_t *, channels);
|
||||
if (!gst_jack_audio_sink_allocate_channels (sink, channels))
|
||||
goto out_of_ports;
|
||||
|
||||
/* create an output port for each channel */
|
||||
for (i = 0; i < channels; i++) {
|
||||
gchar *name;
|
||||
|
||||
/* port names start from 1 */
|
||||
name = g_strdup_printf ("out_%d", i + 1);
|
||||
abuf->outport[i] = jack_port_register (sink->client, name,
|
||||
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
if (abuf->outport[i] == NULL)
|
||||
goto out_of_ports;
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
buffer_size = jack_get_buffer_size (sink->client);
|
||||
buffer_size = jack_get_buffer_size (client);
|
||||
|
||||
/* the segment size in bytes, this is large enough to hold a buffer of 32bit floats
|
||||
* for all channels */
|
||||
|
@ -473,7 +499,7 @@ gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
|
||||
memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
|
||||
|
||||
if ((res = jack_activate (sink->client)))
|
||||
if ((res = gst_jack_audio_client_set_active (sink->client, TRUE)))
|
||||
goto could_not_activate;
|
||||
|
||||
/* if we need to automatically connect the ports, do so now. We must do this
|
||||
|
@ -482,7 +508,7 @@ gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
/* find all the physical input ports. A physical input port is a port
|
||||
* associated with a hardware device. Someone needs connect to a physical
|
||||
* port in order to hear something. */
|
||||
ports = jack_get_ports (sink->client, NULL, NULL,
|
||||
ports = jack_get_ports (client, NULL, NULL,
|
||||
JackPortIsPhysical | JackPortIsInput);
|
||||
if (ports == NULL) {
|
||||
/* no ports? fine then we don't do anything except for posting a warning
|
||||
|
@ -501,7 +527,7 @@ gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
|
|||
break;
|
||||
}
|
||||
/* connect the port to a physical port */
|
||||
if ((res = jack_connect (sink->client, jack_port_name (abuf->outport[i]),
|
||||
if ((res = jack_connect (client, jack_port_name (sink->ports[i]),
|
||||
ports[i])))
|
||||
goto cannot_connect;
|
||||
}
|
||||
|
@ -550,30 +576,20 @@ gst_jack_ring_buffer_release (GstRingBuffer * buf)
|
|||
{
|
||||
GstJackAudioSink *sink;
|
||||
GstJackRingBuffer *abuf;
|
||||
gint i, res;
|
||||
gint res;
|
||||
|
||||
abuf = GST_JACK_RING_BUFFER_CAST (buf);
|
||||
sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf));
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "release");
|
||||
|
||||
if ((res = jack_deactivate (sink->client))) {
|
||||
if ((res = gst_jack_audio_client_set_active (sink->client, FALSE))) {
|
||||
/* we only warn, this means the server is probably shut down and the client
|
||||
* is gone anyway. */
|
||||
GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL),
|
||||
("Could not deactivate Jack client (%d)", res));
|
||||
}
|
||||
|
||||
/* remove all ports */
|
||||
for (i = 0; i < abuf->channels; i++) {
|
||||
GST_LOG_OBJECT (sink, "unregister port %d", i);
|
||||
if ((res = jack_port_unregister (sink->client, abuf->outport[i]))) {
|
||||
GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res);
|
||||
}
|
||||
abuf->outport[i] = NULL;
|
||||
}
|
||||
g_free (abuf->outport);
|
||||
abuf->outport = NULL;
|
||||
abuf->channels = -1;
|
||||
abuf->buffer_size = -1;
|
||||
abuf->sample_rate = -1;
|
||||
|
@ -746,6 +762,8 @@ gst_jack_audio_sink_class_init (GstJackAudioSinkClass * klass)
|
|||
|
||||
gstbaseaudiosink_class->create_ringbuffer =
|
||||
GST_DEBUG_FUNCPTR (gst_jack_audio_sink_create_ringbuffer);
|
||||
|
||||
gst_jack_audio_client_init ();
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -754,6 +772,8 @@ gst_jack_audio_sink_init (GstJackAudioSink * sink,
|
|||
{
|
||||
sink->connect = DEFAULT_PROP_CONNECT;
|
||||
sink->server = g_strdup (DEFAULT_PROP_SERVER);
|
||||
sink->ports = NULL;
|
||||
sink->port_count = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -806,14 +826,17 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink)
|
|||
const char **ports;
|
||||
gint min, max;
|
||||
gint rate;
|
||||
jack_client_t *client;
|
||||
|
||||
if (sink->client == NULL)
|
||||
goto no_client;
|
||||
|
||||
client = gst_jack_audio_client_get_client (sink->client);
|
||||
|
||||
if (sink->connect == GST_JACK_CONNECT_AUTO) {
|
||||
/* get a port count, this is the number of channels we can automatically
|
||||
* connect. */
|
||||
ports = jack_get_ports (sink->client, NULL, NULL,
|
||||
ports = jack_get_ports (client, NULL, NULL,
|
||||
JackPortIsPhysical | JackPortIsInput);
|
||||
max = 0;
|
||||
if (ports != NULL) {
|
||||
|
@ -822,13 +845,13 @@ gst_jack_audio_sink_getcaps (GstBaseSink * bsink)
|
|||
} else
|
||||
max = 0;
|
||||
} else {
|
||||
/* we allow any number of pads, somoething else is going to connect the
|
||||
/* we allow any number of pads, something else is going to connect the
|
||||
* pads. */
|
||||
max = G_MAXINT;
|
||||
}
|
||||
min = MIN (1, max);
|
||||
|
||||
rate = jack_get_sample_rate (sink->client);
|
||||
rate = jack_get_sample_rate (client);
|
||||
|
||||
GST_DEBUG_OBJECT (sink, "got %d-%d ports, samplerate: %d", min, max, rate);
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/audio/gstbaseaudiosink.h>
|
||||
|
||||
#include "gstjackaudioclient.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_JACK_AUDIO_SINK (gst_jack_audio_sink_get_type())
|
||||
|
@ -63,6 +65,7 @@ typedef enum {
|
|||
struct _GstJackAudioSink {
|
||||
GstBaseAudioSink element;
|
||||
|
||||
/*< private >*/
|
||||
/* cached caps */
|
||||
GstCaps *caps;
|
||||
|
||||
|
@ -71,17 +74,15 @@ struct _GstJackAudioSink {
|
|||
gchar *server;
|
||||
|
||||
/* our client */
|
||||
jack_client_t *client;
|
||||
GstJackAudioClient *client;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
/* our ports */
|
||||
jack_port_t **ports;
|
||||
int port_count;
|
||||
};
|
||||
|
||||
struct _GstJackAudioSinkClass {
|
||||
GstBaseAudioSinkClass parent_class;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
GType gst_jack_audio_sink_get_type (void);
|
||||
|
|
Loading…
Reference in a new issue