Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / ext / jack / gstjackaudioclient.c
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <string.h>
23
24 #include "gstjackaudioclient.h"
25
26 #include <gst/glib-compat-private.h>
27
28 GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_client_debug);
29 #define GST_CAT_DEFAULT gst_jack_audio_client_debug
30
31 void
32 gst_jack_audio_client_init (void)
33 {
34   GST_DEBUG_CATEGORY_INIT (gst_jack_audio_client_debug, "jackclient", 0,
35       "jackclient helpers");
36 }
37
38 /* a list of global connections indexed by id and server. */
39 G_LOCK_DEFINE_STATIC (connections_lock);
40 static GList *connections;
41
42 /* the connection to a server  */
43 typedef struct
44 {
45   gint refcount;
46   GMutex *lock;
47   GCond *flush_cond;
48
49   /* id/server pair and the connection */
50   gchar *id;
51   gchar *server;
52   jack_client_t *client;
53
54   /* lists of GstJackAudioClients */
55   gint n_clients;
56   GList *src_clients;
57   GList *sink_clients;
58 } GstJackAudioConnection;
59
60 /* an object sharing a jack_client_t connection. */
61 struct _GstJackAudioClient
62 {
63   GstJackAudioConnection *conn;
64
65   GstJackClientType type;
66   gboolean active;
67   gboolean deactivate;
68
69   void (*shutdown) (void *arg);
70   JackProcessCallback process;
71   JackBufferSizeCallback buffer_size;
72   JackSampleRateCallback sample_rate;
73   gpointer user_data;
74 };
75
76 typedef jack_default_audio_sample_t sample_t;
77
78 typedef struct
79 {
80   jack_nframes_t nframes;
81   gpointer user_data;
82 } JackCB;
83
84 static int
85 jack_process_cb (jack_nframes_t nframes, void *arg)
86 {
87   GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
88   GList *walk;
89   int res = 0;
90
91   g_mutex_lock (conn->lock);
92   /* call sources first, then sinks. Sources will either push data into the
93    * ringbuffer of the sinks, which will then pull the data out of it, or
94    * sinks will pull the data from the sources. */
95   for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
96     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
97
98     /* only call active clients */
99     if ((client->active || client->deactivate) && client->process) {
100       res = client->process (nframes, client->user_data);
101       if (client->deactivate) {
102         client->deactivate = FALSE;
103         g_cond_signal (conn->flush_cond);
104       }
105     }
106   }
107   for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
108     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
109
110     /* only call active clients */
111     if ((client->active || client->deactivate) && client->process) {
112       res = client->process (nframes, client->user_data);
113       if (client->deactivate) {
114         client->deactivate = FALSE;
115         g_cond_signal (conn->flush_cond);
116       }
117     }
118   }
119   g_mutex_unlock (conn->lock);
120
121   return res;
122 }
123
124 /* we error out */
125 static int
126 jack_sample_rate_cb (jack_nframes_t nframes, void *arg)
127 {
128   return 0;
129 }
130
131 /* we error out */
132 static int
133 jack_buffer_size_cb (jack_nframes_t nframes, void *arg)
134 {
135   return 0;
136 }
137
138 static void
139 jack_shutdown_cb (void *arg)
140 {
141   GstJackAudioConnection *conn = (GstJackAudioConnection *) arg;
142   GList *walk;
143
144   GST_DEBUG ("disconnect client %s from server %s", conn->id,
145       GST_STR_NULL (conn->server));
146
147   g_mutex_lock (conn->lock);
148   for (walk = conn->src_clients; walk; walk = g_list_next (walk)) {
149     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
150
151     if (client->shutdown)
152       client->shutdown (client->user_data);
153   }
154   for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) {
155     GstJackAudioClient *client = (GstJackAudioClient *) walk->data;
156
157     if (client->shutdown)
158       client->shutdown (client->user_data);
159   }
160   g_mutex_unlock (conn->lock);
161 }
162
163 typedef struct
164 {
165   const gchar *id;
166   const gchar *server;
167 } FindData;
168
169 static gint
170 connection_find (GstJackAudioConnection * conn, FindData * data)
171 {
172   /* id's must match */
173   if (strcmp (conn->id, data->id))
174     return 1;
175
176   /* both the same or NULL */
177   if (conn->server == data->server)
178     return 0;
179
180   /* we cannot compare NULL */
181   if (conn->server == NULL || data->server == NULL)
182     return 1;
183
184   if (strcmp (conn->server, data->server))
185     return 1;
186
187   return 0;
188 }
189
190 /* make a connection with @id and @server. Returns NULL on failure with the
191  * status set. */
192 static GstJackAudioConnection *
193 gst_jack_audio_make_connection (const gchar * id, const gchar * server,
194     jack_client_t * jclient, jack_status_t * status)
195 {
196   GstJackAudioConnection *conn;
197   jack_options_t options;
198   gint res;
199
200   *status = 0;
201
202   GST_DEBUG ("new client %s, connecting to server %s", id,
203       GST_STR_NULL (server));
204
205   /* never start a server */
206   options = JackNoStartServer;
207   /* if we have a servername, use it */
208   if (server != NULL)
209     options |= JackServerName;
210   /* open the client */
211   if (jclient == NULL)
212     jclient = jack_client_open (id, options, status, server);
213   if (jclient == NULL)
214     goto could_not_open;
215
216   /* now create object */
217   conn = g_new (GstJackAudioConnection, 1);
218   conn->refcount = 1;
219   conn->lock = g_mutex_new ();
220   conn->flush_cond = g_cond_new ();
221   conn->id = g_strdup (id);
222   conn->server = g_strdup (server);
223   conn->client = jclient;
224   conn->n_clients = 0;
225   conn->src_clients = NULL;
226   conn->sink_clients = NULL;
227
228   /* set our callbacks  */
229   jack_set_process_callback (jclient, jack_process_cb, conn);
230   /* these callbacks cause us to error */
231   jack_set_buffer_size_callback (jclient, jack_buffer_size_cb, conn);
232   jack_set_sample_rate_callback (jclient, jack_sample_rate_cb, conn);
233   jack_on_shutdown (jclient, jack_shutdown_cb, conn);
234
235   /* all callbacks are set, activate the client */
236   if ((res = jack_activate (jclient)))
237     goto could_not_activate;
238
239   GST_DEBUG ("opened connection %p", conn);
240
241   return conn;
242
243   /* ERRORS */
244 could_not_open:
245   {
246     GST_DEBUG ("failed to open jack client, %d", *status);
247     return NULL;
248   }
249 could_not_activate:
250   {
251     GST_ERROR ("Could not activate client (%d)", res);
252     *status = JackFailure;
253     g_mutex_free (conn->lock);
254     g_free (conn->id);
255     g_free (conn->server);
256     g_free (conn);
257     return NULL;
258   }
259 }
260
261 static GstJackAudioConnection *
262 gst_jack_audio_get_connection (const gchar * id, const gchar * server,
263     jack_client_t * jclient, jack_status_t * status)
264 {
265   GstJackAudioConnection *conn;
266   GList *found;
267   FindData data;
268
269   GST_DEBUG ("getting connection for id %s, server %s", id,
270       GST_STR_NULL (server));
271
272   data.id = id;
273   data.server = server;
274
275   G_LOCK (connections_lock);
276   found =
277       g_list_find_custom (connections, &data, (GCompareFunc) connection_find);
278   if (found != NULL && jclient != NULL) {
279     /* we found it, increase refcount and return it */
280     conn = (GstJackAudioConnection *) found->data;
281     conn->refcount++;
282
283     GST_DEBUG ("found connection %p", conn);
284   } else {
285     /* make new connection */
286     conn = gst_jack_audio_make_connection (id, server, jclient, status);
287     if (conn != NULL) {
288       GST_DEBUG ("created connection %p", conn);
289       /* add to list on success */
290       connections = g_list_prepend (connections, conn);
291     } else {
292       GST_WARNING ("could not create connection");
293     }
294   }
295   G_UNLOCK (connections_lock);
296
297   return conn;
298 }
299
300 static void
301 gst_jack_audio_unref_connection (GstJackAudioConnection * conn)
302 {
303   gint res;
304   gboolean zero;
305
306   GST_DEBUG ("unref connection %p refcnt %d", conn, conn->refcount);
307
308   G_LOCK (connections_lock);
309   conn->refcount--;
310   if ((zero = (conn->refcount == 0))) {
311     GST_DEBUG ("closing connection %p", conn);
312     /* remove from list, we can release the mutex after removing the connection
313      * from the list because after that, nobody can access the connection anymore. */
314     connections = g_list_remove (connections, conn);
315   }
316   G_UNLOCK (connections_lock);
317
318   /* if we are zero, close and cleanup the connection */
319   if (zero) {
320     /* don't use conn->lock here. two reasons:
321      *
322      *  1) its not necessary: jack_deactivate() will not return until the JACK thread
323      *      associated with this connection is cleaned up by a thread join, hence 
324      *      no more callbacks can occur or be in progress.
325      *
326      * 2) it would deadlock anyway, because jack_deactivate() will sleep
327      *      waiting for the JACK thread, and can thus cause deadlock in 
328      *      jack_process_cb()
329      */
330     if ((res = jack_deactivate (conn->client))) {
331       /* we only warn, this means the server is probably shut down and the client
332        * is gone anyway. */
333       GST_WARNING ("Could not deactivate Jack client (%d)", res);
334     }
335     /* close connection */
336     if ((res = jack_client_close (conn->client))) {
337       /* we assume the client is gone. */
338       GST_WARNING ("close failed (%d)", res);
339     }
340
341     /* free resources */
342     g_mutex_free (conn->lock);
343     g_cond_free (conn->flush_cond);
344     g_free (conn->id);
345     g_free (conn->server);
346     g_free (conn);
347   }
348 }
349
350 static void
351 gst_jack_audio_connection_add_client (GstJackAudioConnection * conn,
352     GstJackAudioClient * client)
353 {
354   g_mutex_lock (conn->lock);
355   switch (client->type) {
356     case GST_JACK_CLIENT_SOURCE:
357       conn->src_clients = g_list_append (conn->src_clients, client);
358       conn->n_clients++;
359       break;
360     case GST_JACK_CLIENT_SINK:
361       conn->sink_clients = g_list_append (conn->sink_clients, client);
362       conn->n_clients++;
363       break;
364     default:
365       g_warning ("trying to add unknown client type");
366       break;
367   }
368   g_mutex_unlock (conn->lock);
369 }
370
371 static void
372 gst_jack_audio_connection_remove_client (GstJackAudioConnection * conn,
373     GstJackAudioClient * client)
374 {
375   g_mutex_lock (conn->lock);
376   switch (client->type) {
377     case GST_JACK_CLIENT_SOURCE:
378       conn->src_clients = g_list_remove (conn->src_clients, client);
379       conn->n_clients--;
380       break;
381     case GST_JACK_CLIENT_SINK:
382       conn->sink_clients = g_list_remove (conn->sink_clients, client);
383       conn->n_clients--;
384       break;
385     default:
386       g_warning ("trying to remove unknown client type");
387       break;
388   }
389   g_mutex_unlock (conn->lock);
390 }
391
392 /**
393  * gst_jack_audio_client_get:
394  * @id: the client id
395  * @server: the server to connect to or NULL for the default server
396  * @type: the client type
397  * @shutdown: a callback when the jack server shuts down
398  * @process: a callback when samples are available
399  * @buffer_size: a callback when the buffer_size changes
400  * @sample_rate: a callback when the sample_rate changes
401  * @user_data: user data passed to the callbacks
402  * @status: pointer to hold the jack status code in case of errors
403  *
404  * Get the jack client connection for @id and @server. Connections to the same
405  * @id and @server will receive the same physical Jack client connection and
406  * will therefore be scheduled in the same process callback.
407  * 
408  * Returns: a #GstJackAudioClient.
409  */
410 GstJackAudioClient *
411 gst_jack_audio_client_new (const gchar * id, const gchar * server,
412     jack_client_t * jclient, GstJackClientType type,
413     void (*shutdown) (void *arg), JackProcessCallback process,
414     JackBufferSizeCallback buffer_size, JackSampleRateCallback sample_rate,
415     gpointer user_data, jack_status_t * status)
416 {
417   GstJackAudioClient *client;
418   GstJackAudioConnection *conn;
419
420   g_return_val_if_fail (id != NULL, NULL);
421   g_return_val_if_fail (status != NULL, NULL);
422
423   /* first get a connection for the id/server pair */
424   conn = gst_jack_audio_get_connection (id, server, jclient, status);
425   if (conn == NULL)
426     goto no_connection;
427
428   GST_INFO ("new client %s", id);
429
430   /* make new client using the connection */
431   client = g_new (GstJackAudioClient, 1);
432   client->active = client->deactivate = FALSE;
433   client->conn = conn;
434   client->type = type;
435   client->shutdown = shutdown;
436   client->process = process;
437   client->buffer_size = buffer_size;
438   client->sample_rate = sample_rate;
439   client->user_data = user_data;
440
441   /* add the client to the connection */
442   gst_jack_audio_connection_add_client (conn, client);
443
444   return client;
445
446   /* ERRORS */
447 no_connection:
448   {
449     GST_DEBUG ("Could not get server connection (%d)", *status);
450     return NULL;
451   }
452 }
453
454 /**
455  * gst_jack_audio_client_free:
456  * @client: a #GstJackAudioClient
457  *
458  * Free the resources used by @client.
459  */
460 void
461 gst_jack_audio_client_free (GstJackAudioClient * client)
462 {
463   GstJackAudioConnection *conn;
464
465   g_return_if_fail (client != NULL);
466
467   GST_INFO ("free client");
468
469   conn = client->conn;
470
471   /* remove from connection first so that it's not scheduled anymore after this
472    * call */
473   gst_jack_audio_connection_remove_client (conn, client);
474   gst_jack_audio_unref_connection (conn);
475
476   g_free (client);
477 }
478
479 /**
480  * gst_jack_audio_client_get_client:
481  * @client: a #GstJackAudioClient
482  *
483  * Get the jack audio client for @client. This function is used to perform
484  * operations on the jack server from this client.
485  *
486  * Returns: The jack audio client.
487  */
488 jack_client_t *
489 gst_jack_audio_client_get_client (GstJackAudioClient * client)
490 {
491   g_return_val_if_fail (client != NULL, NULL);
492
493   /* no lock needed, the connection and the client does not change 
494    * once the client is created. */
495   return client->conn->client;
496 }
497
498 /**
499  * gst_jack_audio_client_set_active:
500  * @client: a #GstJackAudioClient
501  * @active: new mode for the client
502  *
503  * Activate or deactive @client. When a client is activated it will receive
504  * callbacks when data should be processed.
505  *
506  * Returns: 0 if all ok.
507  */
508 gint
509 gst_jack_audio_client_set_active (GstJackAudioClient * client, gboolean active)
510 {
511   g_return_val_if_fail (client != NULL, -1);
512
513   /* make sure that we are not dispatching the client */
514   g_mutex_lock (client->conn->lock);
515   if (client->active && !active) {
516     /* we need to process once more to flush the port */
517     client->deactivate = TRUE;
518
519     /* need to wait for process_cb run once more */
520     while (client->deactivate)
521       g_cond_wait (client->conn->flush_cond, client->conn->lock);
522   }
523   client->active = active;
524   g_mutex_unlock (client->conn->lock);
525
526   return 0;
527 }