1 /* GStreamer
2  * Copyright (C) 2006 Wim Taymans <wim@fluendo.com>
3  *
4  * gstjackaudioclient.c: jack audio client implementation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include <string.h>
23 
24 #include "gstjackaudioclient.h"
25 #include "gstjack.h"
26 
27 #include <gst/glib-compat-private.h>
28 
29 GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug);
30 #define GST_CAT_DEFAULT gst_jack_audio_client_debug
31 
32 static void
jack_log_error(const gchar * msg)33 jack_log_error (const gchar * msg)
34 {
35   GST_ERROR ("%s", msg);
36 }
37 
38 static void
jack_info_error(const gchar * msg)39 jack_info_error (const gchar * msg)
40 {
41   GST_INFO ("%s", msg);
42 }
43 
44 void
gst_jack_audio_client_init(void)45 gst_jack_audio_client_init (void)
46 {
47   GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0,
48       "jackclient helpers");
49 
50   jack_set_error_function (jack_log_error);
51   jack_set_info_function (jack_info_error);
52 }
53 
54 /* a list of global connections indexed by id and server. */
55 G_LOCK_DEFINE_STATIC (connections_lock);
56 static GList *connections;
57 
58 /* the connection to a server  */
59 typedef struct
60 {
61   gint refcount;
62   GMutex lock;
63   GCond flush_cond;
64 
65   /* id/server pair and the connection */
66   gchar *id;
67   gchar *server;
68   jack_client_t *client;
69 
70   /* lists of GstJackAudioClients */
71   gint n_clients;
72   GList *src_clients;
73   GList *sink_clients;
74 
75   /* transport state handling */
76   gint cur_ts;
77   GstState transport_state;
78 } GstJackAudioConnection;
79 
80 /* an object sharing a jack_client_t connection. */
81 struct _GstJackAudioClient
82 {
83   GstJackAudioConnection *conn;
84 
85   GstJackClientType type;
86   gboolean active;
87   gboolean deactivate;
88   gboolean server_down;
89 
90   JackShutdownCallback shutdown;
91   JackProcessCallback process;
92   JackBufferSizeCallback buffer_size;
93   JackSampleRateCallback sample_rate;
94   gpointer user_data;
95 };
96 
97 typedef struct
98 {
99   jack_nframes_t nframes;
100   gpointer user_data;
101 } JackCB;
102 
103 static gboolean
jack_handle_transport_change(GstJackAudioClient * client,GstState state)104 jack_handle_transport_change (GstJackAudioClient * client, GstState state)
105 {
106   GstObject *obj = GST_OBJECT_PARENT (client->user_data);
107   guint mode;
108 
109   g_object_get (obj, "transport", &mode, NULL);
110   if ((mode & GST_JACK_TRANSPORT_SLAVE) && (GST_STATE (obj) != state)) {
111     GST_INFO_OBJECT (obj, "requesting state change: %s",
112         gst_element_state_get_name (state));
113     gst_element_post_message (GST_ELEMENT (obj),
114         gst_message_new_request_state (obj, state));
115     return TRUE;
116   }
117   return FALSE;
118 }
119 
120 static int
jack_process_cb(jack_nframes_t nframes,void * arg)121 jack_process_cb (jack_nframes_t nframes, void *arg)
122 {
123   GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
124   GList *walk;
125   int res = 0;
126   jack_transport_state_t ts = jack_transport_query (conn->client, NULL);
127 
128   if (ts != conn->cur_ts) {
129     conn->cur_ts = ts;
130     switch (ts) {
131       case JackTransportStopped:
132         GST_DEBUG ("transport state is 'stopped'");
133         conn->transport_state = GST_STATE_PAUSED;
134         break;
135       case JackTransportStarting:
136         GST_DEBUG ("transport state is 'starting'");
137         conn->transport_state = GST_STATE_READY;
138         break;
139       case JackTransportRolling:
140         GST_DEBUG ("transport state is 'rolling'");
141         conn->transport_state = GST_STATE_PLAYING;
142         break;
143       default:
144         break;
145     }
146     GST_DEBUG ("num of clients: src=%d, sink=%d",
147         g_list_length (conn->src_clients), g_list_length (conn->sink_clients));
148   }
149 
150   g_mutex_lock (&conn->lock);
151   /* call sources first, then sinks. Sources will either push data into the
152    * ringbuffer of the sinks, which will then pull the data out of it, or
153    * sinks will pull the data from the sources. */
154   for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
155     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
156 
157     /* only call active clients */
158     if ((client->active || client->deactivate) && client->process) {
159       res = client->process (nframes, client->user_data);
160       if (client->deactivate) {
161         client->deactivate = FALSE;
162         g_cond_signal (&conn->flush_cond);
163       }
164     }
165   }
166   for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
167     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
168 
169     /* only call active clients */
170     if ((client->active || client->deactivate) && client->process) {
171       res = client->process (nframes, client->user_data);
172       if (client->deactivate) {
173         client->deactivate = FALSE;
174         g_cond_signal (&conn->flush_cond);
175       }
176     }
177   }
178 
179   /* handle transport state requisition, do sinks first, stop after the first
180    * element that handled it */
181   if (conn->transport_state != GST_STATE_VOID_PENDING) {
182     for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
183       if (jack_handle_transport_change ((GstJackAudioClient *) walk->data,
184               conn->transport_state)) {
185         conn->transport_state = GST_STATE_VOID_PENDING;
186         break;
187       }
188     }
189   }
190   if (conn->transport_state != GST_STATE_VOID_PENDING) {
191     for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
192       if (jack_handle_transport_change ((GstJackAudioClient *) walk->data,
193               conn->transport_state)) {
194         conn->transport_state = GST_STATE_VOID_PENDING;
195         break;
196       }
197     }
198   }
199   g_mutex_unlock (&conn->lock);
200 
201   return res;
202 }
203 
204 static void
jack_shutdown_cb(void * arg)205 jack_shutdown_cb (void *arg)
206 {
207   GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
208   GList *walk;
209 
210   GST_DEBUG ("disconnect client %s from server %s", conn->id,
211       GST_STR_NULL (conn->server));
212 
213   g_mutex_lock (&conn->lock);
214   for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
215     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
216 
217     client->server_down = TRUE;
218     g_cond_signal (&conn->flush_cond);
219     if (client->shutdown)
220       client->shutdown (client->user_data);
221   }
222   for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
223     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
224 
225     client->server_down = TRUE;
226     g_cond_signal (&conn->flush_cond);
227     if (client->shutdown)
228       client->shutdown (client->user_data);
229   }
230   g_mutex_unlock (&conn->lock);
231 }
232 
233 /* we error out */
234 static int
jack_sample_rate_cb(jack_nframes_t nframes,void * arg)235 jack_sample_rate_cb (jack_nframes_t nframes, void *arg)
236 {
237   jack_shutdown_cb (arg);
238   return 0;
239 }
240 
241 /* we error out */
242 static int
jack_buffer_size_cb(jack_nframes_t nframes,void * arg)243 jack_buffer_size_cb (jack_nframes_t nframes, void *arg)
244 {
245   jack_shutdown_cb (arg);
246   return 0;
247 }
248 
249 typedef struct
250 {
251   const gchar *id;
252   const gchar *server;
253 } FindData;
254 
255 static gint
connection_find(GstJackAudioConnection * conn,FindData * data)256 connection_find (GstJackAudioConnection * conn, FindData * data)
257 {
258   /* id's must match */
259   if (strcmp (conn->id, data->id))
260     return 1;
261 
262   /* both the same or NULL */
263   if (conn->server == data->server)
264     return 0;
265 
266   /* we cannot compare NULL */
267   if (conn->server == NULL || data->server == NULL)
268     return 1;
269 
270   if (strcmp (conn->server, data->server))
271     return 1;
272 
273   return 0;
274 }
275 
276 /* make a connection with @id and @server. Returns NULL on failure with the
277  * status set. */
278 static GstJackAudioConnection *
gst_jack_audio_make_connection(const gchar * id,const gchar * server,jack_client_t * jclient,jack_status_t * status)279 gst_jack_audio_make_connection (const gchar * id, const gchar * server,
280     jack_client_t * jclient, jack_status_t * status)
281 {
282   GstJackAudioConnection *conn;
283   jack_options_t options;
284   gint res;
285 
286   *status = 0;
287 
288   GST_DEBUG ("new client %s, connecting to server %s", id,
289       GST_STR_NULL (server));
290 
291   /* never start a server */
292   options = JackNoStartServer;
293   /* if we have a servername, use it */
294   if (server != NULL)
295     options |= JackServerName;
296   /* open the client */
297   if (jclient == NULL)
298     jclient = jack_client_open (id, options, status, server);
299   if (jclient == NULL)
300     goto could_not_open;
301 
302   /* now create object */
303   conn = g_new (GstJackAudioConnection, 1);
304   conn->refcount = 1;
305   g_mutex_init (&conn->lock);
306   g_cond_init (&conn->flush_cond);
307   conn->id = g_strdup (id);
308   conn->server = g_strdup (server);
309   conn->client = jclient;
310   conn->n_clients = 0;
311   conn->src_clients = NULL;
312   conn->sink_clients = NULL;
313   conn->cur_ts = -1;
314   conn->transport_state = GST_STATE_VOID_PENDING;
315 
316   /* set our callbacks  */
317   jack_set_process_callback (jclient, jack_process_cb, conn);
318   /* these callbacks cause us to error */
319   jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn);
320   jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn);
321   jack_on_shutdown (jclient, jack_shutdown_cb, conn);
322 
323   /* all callbacks are set, activate the client */
324   GST_INFO ("activate jack_client %p", jclient);
325   if ((res = jack_activate (jclient)))
326     goto could_not_activate;
327 
328   GST_DEBUG ("opened connection %p", conn);
329 
330   return conn;
331 
332   /* ERRORS */
333 could_not_open:
334   {
335     GST_DEBUG ("failed to open jack client, %d", *status);
336     return NULL;
337   }
338 could_not_activate:
339   {
340     GST_ERROR ("Could not activate client (%d)", res);
341     *status = JackFailure;
342     g_mutex_clear (&conn->lock);
343     g_free (conn->id);
344     g_free (conn->server);
345     g_free (conn);
346     return NULL;
347   }
348 }
349 
350 static GstJackAudioConnection *
gst_jack_audio_get_connection(const gchar * id,const gchar * server,jack_client_t * jclient,jack_status_t * status)351 gst_jack_audio_get_connection (const gchar * id, const gchar * server,
352     jack_client_t * jclient, jack_status_t * status)
353 {
354   GstJackAudioConnection *conn;
355   GList *found;
356   FindData data;
357 
358   GST_DEBUG ("getting connection for id %s, server %s", id,
359       GST_STR_NULL (server));
360 
361   data.id = id;
362   data.server = server;
363 
364   G_LOCK (connections_lock);
365   found =
366       g_list_find_custom (connections, &data, (GCompareFunc) connection_find);
367   if (found != NULL && jclient != NULL) {
368     /* we found it, increase refcount and return it */
369     conn = (GstJackAudioConnection *) found->data;
370     conn->refcount++;
371 
372     GST_DEBUG ("found connection %p", conn);
373   } else {
374     /* make new connection */
375     conn = gst_jack_audio_make_connection (id, server, jclient, status);
376     if (conn != NULL) {
377       GST_DEBUG ("created connection %p", conn);
378       /* add to list on success */
379       connections = g_list_prepend (connections, conn);
380     } else {
381       GST_WARNING ("could not create connection");
382     }
383   }
384   G_UNLOCK (connections_lock);
385 
386   return conn;
387 }
388 
389 static void
gst_jack_audio_unref_connection(GstJackAudioConnection * conn)390 gst_jack_audio_unref_connection (GstJackAudioConnection * conn)
391 {
392   gint res;
393   gboolean zero;
394 
395   GST_DEBUG ("unref connection %p refcnt %d", conn, conn->refcount);
396 
397   G_LOCK (connections_lock);
398   conn->refcount--;
399   if ((zero = (conn->refcount == 0))) {
400     GST_DEBUG ("closing connection %p", conn);
401     /* remove from list, we can release the mutex after removing the connection
402      * from the list because after that, nobody can access the connection anymore. */
403     connections = g_list_remove (connections, conn);
404   }
405   G_UNLOCK (connections_lock);
406 
407   /* if we are zero, close and cleanup the connection */
408   if (zero) {
409     /* don't use conn->lock here. two reasons:
410      *
411      *  1) its not necessary: jack_deactivate() will not return until the JACK thread
412      *      associated with this connection is cleaned up by a thread join, hence
413      *      no more callbacks can occur or be in progress.
414      *
415      * 2) it would deadlock anyway, because jack_deactivate() will sleep
416      *      waiting for the JACK thread, and can thus cause deadlock in
417      *      jack_process_cb()
418      */
419     GST_INFO ("deactivate jack_client %p", conn->client);
420     if ((res = jack_deactivate (conn->client))) {
421       /* we only warn, this means the server is probably shut down and the client
422        * is gone anyway. */
423       GST_WARNING ("Could not deactivate Jack client (%d)", res);
424     }
425     /* close connection */
426     if ((res = jack_client_close (conn->client))) {
427       /* we assume the client is gone. */
428       GST_WARNING ("close failed (%d)", res);
429     }
430 
431     /* free resources */
432     g_mutex_clear (&conn->lock);
433     g_cond_clear (&conn->flush_cond);
434     g_free (conn->id);
435     g_free (conn->server);
436     g_free (conn);
437   }
438 }
439 
440 static void
gst_jack_audio_connection_add_client(GstJackAudioConnection * conn,GstJackAudioClient * client)441 gst_jack_audio_connection_add_client (GstJackAudioConnection * conn,
442     GstJackAudioClient * client)
443 {
444   g_mutex_lock (&conn->lock);
445   switch (client->type) {
446     case GST_JACK_CLIENT_SOURCE:
447       conn->src_clients = g_list_append (conn->src_clients, client);
448       conn->n_clients++;
449       break;
450     case GST_JACK_CLIENT_SINK:
451       conn->sink_clients = g_list_append (conn->sink_clients, client);
452       conn->n_clients++;
453       break;
454     default:
455       g_warning ("trying to add unknown client type");
456       break;
457   }
458   g_mutex_unlock (&conn->lock);
459 }
460 
461 static void
gst_jack_audio_connection_remove_client(GstJackAudioConnection * conn,GstJackAudioClient * client)462 gst_jack_audio_connection_remove_client (GstJackAudioConnection * conn,
463     GstJackAudioClient * client)
464 {
465   g_mutex_lock (&conn->lock);
466   switch (client->type) {
467     case GST_JACK_CLIENT_SOURCE:
468       conn->src_clients = g_list_remove (conn->src_clients, client);
469       conn->n_clients--;
470       break;
471     case GST_JACK_CLIENT_SINK:
472       conn->sink_clients = g_list_remove (conn->sink_clients, client);
473       conn->n_clients--;
474       break;
475     default:
476       g_warning ("trying to remove unknown client type");
477       break;
478   }
479   g_mutex_unlock (&conn->lock);
480 }
481 
482 /**
483  * gst_jack_audio_client_get:
484  * @id: the client id
485  * @server: the server to connect to or NULL for the default server
486  * @type: the client type
487  * @shutdown: a callback when the jack server shuts down
488  * @process: a callback when samples are available
489  * @buffer_size: a callback when the buffer_size changes
490  * @sample_rate: a callback when the sample_rate changes
491  * @user_data: user data passed to the callbacks
492  * @status: pointer to hold the jack status code in case of errors
493  *
494  * Get the jack client connection for @id and @server. Connections to the same
495  * @id and @server will receive the same physical Jack client connection and
496  * will therefore be scheduled in the same process callback.
497  *
498  * Returns: a #GstJackAudioClient.
499  */
500 GstJackAudioClient *
gst_jack_audio_client_new(const gchar * id,const gchar * server,jack_client_t * jclient,GstJackClientType type,void (* shutdown)(void * arg),JackProcessCallback process,JackBufferSizeCallback buffer_size,JackSampleRateCallback sample_rate,gpointer user_data,jack_status_t * status)501 gst_jack_audio_client_new (const gchar * id, const gchar * server,
502     jack_client_t * jclient, GstJackClientType type,
503     void (*shutdown) (void *arg), JackProcessCallback process,
504     JackBufferSizeCallback buffer_size, JackSampleRateCallback sample_rate,
505     gpointer user_data, jack_status_t * status)
506 {
507   GstJackAudioClient *client;
508   GstJackAudioConnection *conn;
509 
510   g_return_val_if_fail (id != NULL, NULL);
511   g_return_val_if_fail (status != NULL, NULL);
512 
513   /* first get a connection for the id/server pair */
514   conn = gst_jack_audio_get_connection (id, server, jclient, status);
515   if (conn == NULL)
516     goto no_connection;
517 
518   GST_INFO ("new client %s", id);
519 
520   /* make new client using the connection */
521   client = g_new (GstJackAudioClient, 1);
522   client->active = client->deactivate = FALSE;
523   client->conn = conn;
524   client->type = type;
525   client->shutdown = shutdown;
526   client->process = process;
527   client->buffer_size = buffer_size;
528   client->sample_rate = sample_rate;
529   client->user_data = user_data;
530   client->server_down = FALSE;
531 
532   /* add the client to the connection */
533   gst_jack_audio_connection_add_client (conn, client);
534 
535   return client;
536 
537   /* ERRORS */
538 no_connection:
539   {
540     GST_DEBUG ("Could not get server connection (%d)", *status);
541     return NULL;
542   }
543 }
544 
545 /**
546  * gst_jack_audio_client_free:
547  * @client: a #GstJackAudioClient
548  *
549  * Free the resources used by @client.
550  */
551 void
gst_jack_audio_client_free(GstJackAudioClient * client)552 gst_jack_audio_client_free (GstJackAudioClient * client)
553 {
554   GstJackAudioConnection *conn;
555 
556   g_return_if_fail (client != NULL);
557 
558   GST_INFO ("free client");
559 
560   conn = client->conn;
561 
562   /* remove from connection first so that it's not scheduled anymore after this
563    * call */
564   gst_jack_audio_connection_remove_client (conn, client);
565   gst_jack_audio_unref_connection (conn);
566 
567   g_free (client);
568 }
569 
570 /**
571  * gst_jack_audio_client_get_client:
572  * @client: a #GstJackAudioClient
573  *
574  * Get the jack audio client for @client. This function is used to perform
575  * operations on the jack server from this client.
576  *
577  * Returns: The jack audio client.
578  */
579 jack_client_t *
gst_jack_audio_client_get_client(GstJackAudioClient * client)580 gst_jack_audio_client_get_client (GstJackAudioClient * client)
581 {
582   g_return_val_if_fail (client != NULL, NULL);
583 
584   /* no lock needed, the connection and the client does not change
585    * once the client is created. */
586   return client->conn->client;
587 }
588 
589 /**
590  * gst_jack_audio_client_set_active:
591  * @client: a #GstJackAudioClient
592  * @active: new mode for the client
593  *
594  * Activate or deactive @client. When a client is activated it will receive
595  * callbacks when data should be processed.
596  *
597  * Returns: 0 if all ok.
598  */
599 gint
gst_jack_audio_client_set_active(GstJackAudioClient * client,gboolean active)600 gst_jack_audio_client_set_active (GstJackAudioClient * client, gboolean active)
601 {
602   g_return_val_if_fail (client != NULL, -1);
603 
604   /* make sure that we are not dispatching the client */
605   g_mutex_lock (&client->conn->lock);
606   if (client->active && !active) {
607     /* we need to process once more to flush the port */
608     client->deactivate = TRUE;
609 
610     /* need to wait for process_cb run once more */
611     while (client->deactivate && !client->server_down)
612       g_cond_wait (&client->conn->flush_cond, &client->conn->lock);
613   }
614   client->active = active;
615   g_mutex_unlock (&client->conn->lock);
616 
617   return 0;
618 }
619 
620 /**
621  * gst_jack_audio_client_get_transport_state:
622  * @client: a #GstJackAudioClient
623  *
624  * Check the current transport state. The client can use this to request a state
625  * change from the application.
626  *
627  * Returns: the state, %GST_STATE_VOID_PENDING for no change in the transport
628  * state
629  */
630 GstState
gst_jack_audio_client_get_transport_state(GstJackAudioClient * client)631 gst_jack_audio_client_get_transport_state (GstJackAudioClient * client)
632 {
633   GstState state = client->conn->transport_state;
634 
635   client->conn->transport_state = GST_STATE_VOID_PENDING;
636   return state;
637 }
638