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 deactivate @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
639 /**
640 * gst_jack_audio_client_get_port_names_from_string:
641 * @jclient: a jack_client_t handle
642 * @port_names: comma-separated jack port name(s)
643 * @port_flags: JackPortFlags
644 *
645 * Returns: a newly-allocated %NULL-terminated array of strings or %NULL
646 * if @port_names contains invalid port name. Use g_strfreev() to free it.
647 */
648 gchar **
gst_jack_audio_client_get_port_names_from_string(jack_client_t * jclient,const gchar * port_names,gint port_flags)649 gst_jack_audio_client_get_port_names_from_string (jack_client_t * jclient,
650 const gchar * port_names, gint port_flags)
651 {
652 gchar **p = NULL;
653 guint i, len;
654
655 g_return_val_if_fail (jclient != NULL, NULL);
656
657 if (!port_names)
658 return NULL;
659
660 p = g_strsplit (port_names, ",", 0);
661 len = g_strv_length (p);
662
663 if (len < 1)
664 goto invalid;
665
666 for (i = 0; i < len; i++) {
667 jack_port_t *port = jack_port_by_name (jclient, p[i]);
668 int flags;
669
670 if (!port) {
671 GST_WARNING ("Couldn't get jack port by name %s", p[i]);
672 goto invalid;
673 }
674
675 flags = jack_port_flags (port);
676 if ((flags & port_flags) != port_flags) {
677 GST_WARNING ("Port flags 0x%x doesn't match expected flags 0x%x",
678 flags, port_flags);
679 goto invalid;
680 }
681 }
682
683 return p;
684
685 invalid:
686 g_strfreev (p);
687 return NULL;
688 }
689