Jack fixed too
[platform/upstream/gst-plugins-good.git] / ext / jack / gstjack.c
1 /* -*- Mode: C; c-basic-offset: 4 -*- */
2 /*
3     Copyright (C) 2002, 2003 Andy Wingo <wingo@pobox.com>
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     General Public License for more details.
14
15     You should have received a copy of the GNU General Public
16     License along with this library; if not, write to the Free
17     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <stdlib.h>
24 #include <string.h>
25 #include "gstjack.h"
26 #include <gst/audio/audio.h>
27
28 /* TODO:
29
30    - work out the src side (caps setting, etc)
31
32    future core TODO:
33    - make a jack clock provider
34    - add GST_ELEMENT_FIXED_DATA_RATE, GST_ELEMENT_QOS,
35      GST_ELEMENT_CHANGES_DATA_RATE element flags, and make the scheduler
36      sensitive to them
37 */
38
39 /* elementfactory information */
40 static GstElementDetails gst_jack_bin_details = {
41     "Jack Bin",
42     "Generic/Bin",
43     "Jack processing bin",
44     "Andy Wingo <wingo@pobox.com>",
45 };
46
47 static GstElementDetails gst_jack_sink_details = {  
48     "Jack Sink",
49     "Sink/Audio",
50     "Output to a Jack processing network",
51     "Andy Wingo <wingo@pobox.com>",
52 };
53
54 static GstElementDetails gst_jack_src_details = {  
55     "Jack Src",
56     "Source/Audio",
57     "Input from a Jack processing network",
58     "Andy Wingo <wingo@pobox.com>",
59 };
60
61
62 static GHashTable *port_name_counts = NULL;
63 static GstElementClass *parent_class = NULL;
64
65 static void             gst_jack_base_init (gpointer g_class);
66 static void             gst_jack_src_base_init (gpointer g_class);
67 static void             gst_jack_sink_base_init (gpointer g_class);
68 static void             gst_jack_init(GstJack *this);
69 static void             gst_jack_class_init(GstJackClass *klass);
70 static void             gst_jack_set_property (GObject *object, guint prop_id,
71                                                const GValue *value, GParamSpec *pspec);
72 static void             gst_jack_get_property (GObject *object, guint prop_id,
73                                                GValue *value, GParamSpec *pspec);
74
75 static GstPadTemplate*  gst_jack_src_request_pad_factory();
76 static GstPadTemplate*  gst_jack_sink_request_pad_factory();
77 static GstPad*          gst_jack_request_new_pad (GstElement *element, GstPadTemplate *templ,
78                                                   const gchar *name);
79 static GstElementStateReturn    gst_jack_change_state (GstElement *element);
80 static GstPadLinkReturn gst_jack_link (GstPad *pad, GstCaps *caps);
81
82 static void             gst_jack_loop (GstElement *element);
83
84
85 enum {
86     ARG_0,
87     ARG_PORT_NAME_PREFIX,
88 };
89
90
91 GType
92 gst_jack_get_type (void) 
93 {
94   static GType jack_type = 0;
95
96   if (!jack_type) {
97     static const GTypeInfo jack_info = {
98       sizeof(GstJackClass),
99       gst_jack_base_init,
100       NULL,
101       NULL,
102       NULL,
103       NULL,
104       sizeof(GstJack),
105       0,
106       NULL,
107     };
108     jack_type = g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0);
109   }
110   return jack_type;
111 }
112
113 GType
114 gst_jack_sink_get_type (void) 
115 {
116   static GType jack_type = 0;
117   
118   if (!jack_type) {
119     static const GTypeInfo jack_info = {
120       sizeof(GstJackClass),
121       gst_jack_sink_base_init,
122       NULL,
123       (GClassInitFunc)gst_jack_class_init,
124       NULL,
125       NULL,
126       sizeof(GstJack),
127       0,
128       (GInstanceInitFunc)gst_jack_init,
129     };
130     jack_type = g_type_register_static (GST_TYPE_JACK, "GstJackSink", &jack_info, 0);
131   }
132   return jack_type;
133 }
134
135 GType
136 gst_jack_src_get_type (void) 
137 {
138   static GType jack_type = 0;
139   
140   if (!jack_type) {
141     static const GTypeInfo jack_info = {
142       sizeof(GstJackClass),
143       gst_jack_src_base_init,
144       NULL,
145       (GClassInitFunc)gst_jack_class_init,
146       NULL,
147       NULL,
148       sizeof(GstJack),
149       0,
150       (GInstanceInitFunc)gst_jack_init,
151     };
152     jack_type = g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0);
153   }
154   return jack_type;
155 }
156
157 static void
158 gst_jack_base_init (gpointer g_class)
159 {
160   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
161
162   gst_element_class_set_details (element_class, &gst_jack_bin_details);
163 }
164
165 static void
166 gst_jack_src_base_init (gpointer g_class)
167 {
168   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
169
170   gst_element_class_add_pad_template (element_class, gst_jack_src_request_pad_factory ());
171   gst_element_class_set_details (element_class, &gst_jack_src_details);
172 }
173
174 static void
175 gst_jack_sink_base_init (gpointer g_class)
176 {
177   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
178
179   gst_element_class_add_pad_template (element_class, gst_jack_sink_request_pad_factory ());
180   gst_element_class_set_details (element_class, &gst_jack_sink_details);
181 }
182
183 static void
184 gst_jack_class_init(GstJackClass *klass)
185 {
186     GObjectClass *object_class;
187     GstElementClass *element_class;
188     GParamSpec *pspec;
189     gchar *prefix;
190     
191     object_class = (GObjectClass *)klass;
192     element_class = (GstElementClass *)klass;
193     
194     if (parent_class == NULL)
195         parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
196     
197     object_class->get_property = gst_jack_get_property;
198     object_class->set_property = gst_jack_set_property;
199     
200     if (GST_IS_JACK_SINK_CLASS (klass))
201         prefix = "gst-out-";
202     else
203         prefix = "gst-in-";
204     
205     pspec = g_param_spec_string ("port-name-prefix", "Port name prefix",
206                                  "String to prepend to jack port names",
207                                  prefix, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
208     g_object_class_install_property (object_class, ARG_PORT_NAME_PREFIX, pspec);
209
210     element_class->change_state = gst_jack_change_state;
211     
212     element_class->request_new_pad = gst_jack_request_new_pad;
213 }
214
215 static void
216 gst_jack_init(GstJack *this)
217 {
218     if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SRC)
219         this->direction = GST_PAD_SRC;
220     else if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SINK)
221         this->direction = GST_PAD_SINK;
222     else
223         g_assert_not_reached ();
224     
225     gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop);
226 }
227
228 static void
229 gst_jack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
230 {
231     GstJack *this = (GstJack*)object;
232
233     switch (prop_id) {
234     case ARG_PORT_NAME_PREFIX:
235         if (this->port_name_prefix)
236             g_free (this->port_name_prefix);
237         this->port_name_prefix = g_strdup (g_value_get_string (value));
238         break;
239     default:
240         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
241         return;
242     }
243 }
244
245 static void
246 gst_jack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
247 {
248     GstJack *this = (GstJack*)object;
249
250     switch (prop_id) {
251     case ARG_PORT_NAME_PREFIX:
252         g_value_set_string (value, this->port_name_prefix);
253         break;
254     default:
255         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
256         break;
257     }
258 }
259
260 static GstPadTemplate*
261 gst_jack_src_request_pad_factory (void)
262 {
263     static GstPadTemplate *template = NULL;
264     
265     if (!template) {
266         GstCaps *caps;
267         caps = gst_caps_new ("src",
268                              "audio/x-raw-float",
269                              GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
270         template = gst_pad_template_new ("%s", GST_PAD_SRC,
271                                          GST_PAD_REQUEST, caps, NULL);
272     }
273     
274     return template;
275 }
276
277 static GstPadTemplate*
278 gst_jack_sink_request_pad_factory (void)
279 {
280     static GstPadTemplate *template = NULL;
281     
282     if (!template) {
283         GstCaps *caps;
284         caps = gst_caps_new ("sink",
285                              "audio/x-raw-float",
286                              GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
287         template = gst_pad_template_new ("%s", GST_PAD_SINK,
288                                          GST_PAD_REQUEST, caps, NULL);
289     }
290     
291     return template;
292 }
293
294 static GstPad*
295 gst_jack_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name) 
296 {
297     GstJack *this;
298     gchar *newname;
299     GList *l, **pad_list;
300     GstJackPad *pad;
301     gint count;
302     
303     g_return_val_if_fail ((this = GST_JACK (element)), NULL);
304     
305     if (!this->bin)
306         pad_list = &this->pads;
307     else if (this->direction == GST_PAD_SRC)
308         pad_list = &this->bin->src_pads;
309     else
310         pad_list = &this->bin->sink_pads;
311     
312     if (name) {
313         l = *pad_list;
314         while (l) {
315             if (strcmp (GST_JACK_PAD(l)->name, name) == 0) {
316                 g_warning("requested port name %s already in use.", name);
317                 return NULL;
318             }
319             l = l->next;
320         }
321         newname = g_strdup (name);
322     } else {
323         if (this->direction == GST_PAD_SINK)
324             newname = g_strdup ("alsa_pcm:playback_1");
325         else
326             newname = g_strdup ("alsa_pcm:capture_1");
327     }
328     
329     pad = g_new0 (GstJackPad, 1);
330     
331     if (!port_name_counts)
332         port_name_counts = g_hash_table_new (g_str_hash, g_str_equal);
333
334     count = GPOINTER_TO_INT (g_hash_table_lookup (port_name_counts, this->port_name_prefix));
335     g_hash_table_insert (port_name_counts, g_strdup (this->port_name_prefix), GINT_TO_POINTER (count+1));
336
337     pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count);
338
339     pad->peer_name = newname;
340     pad->pad = gst_pad_new_from_template (templ, newname);
341     gst_element_add_pad (GST_ELEMENT (this), pad->pad);
342     gst_pad_set_link_function (pad->pad, gst_jack_link);
343     
344     this->pads = g_list_append (this->pads, pad);
345     
346     g_print ("returning from request_new_pad, pad %s created, to connect to %s\n", pad->name, pad->peer_name);
347     return pad->pad;
348 }
349
350 static GstElementStateReturn
351 gst_jack_change_state (GstElement *element)
352 {
353     GstJack *this;
354     GList *l = NULL, **pads;
355     GstJackPad *pad;
356     GstCaps *caps;
357     
358     g_return_val_if_fail (element != NULL, FALSE);
359     this = GST_JACK (element);
360     
361     switch (GST_STATE_PENDING (element)) {
362     case GST_STATE_NULL:
363         JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this)));
364
365         break;
366         
367     case GST_STATE_READY:
368         JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this)));
369
370         if (!this->bin) {
371             if (!(this->bin = (GstJackBin*)gst_element_get_managing_bin (element))
372                 || !GST_IS_JACK_BIN (this->bin)) {
373                 this->bin = NULL;
374                 g_warning ("jack element %s needs to be contained in a jack bin.",
375                            GST_OBJECT_NAME (element));
376                 return GST_STATE_FAILURE;
377             }
378
379             /* fixme: verify that all names are unique */
380             l = this->pads;
381             pads = (this->direction == GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads;
382             while (l) {
383                 pad = GST_JACK_PAD (l);
384                 JACK_DEBUG ("%s: appending pad %s:%s to list", GST_OBJECT_NAME (this), pad->name, pad->peer_name);
385                 *pads = g_list_append (*pads, pad);
386                 l = g_list_next (l);
387             }
388         }
389         break;
390         
391     case GST_STATE_PAUSED:
392         JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this)));
393
394         if (GST_STATE (element) == GST_STATE_READY) {
395             /* we're in READY->PAUSED */
396             l = this->pads;
397             while (l) {
398                 pad = GST_JACK_PAD (l);
399                 caps = gst_pad_get_caps (pad->pad);
400                 gst_caps_set (caps, "rate", GST_PROPS_INT_TYPE,
401                               (gint)this->bin->rate, NULL);
402                 gst_caps_set (caps, "buffer-frames", GST_PROPS_INT_TYPE,
403                               (gint)this->bin->nframes, NULL);
404                 if (gst_pad_try_set_caps (pad->pad, caps) <= 0)
405                     return GST_STATE_FAILURE;
406                 l = g_list_next (l);
407             }
408         }
409         break;
410     case GST_STATE_PLAYING:
411         JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this)));
412         break;
413     }
414     
415     JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this));
416     
417     if (GST_ELEMENT_CLASS (parent_class)->change_state)
418         return GST_ELEMENT_CLASS (parent_class)->change_state (element);
419
420     return GST_STATE_SUCCESS;
421 }
422
423 static GstPadLinkReturn
424 gst_jack_link (GstPad *pad, GstCaps *caps)
425 {
426   GstJack *this;
427   gint rate, buffer_frames;
428   
429   this = GST_JACK (GST_OBJECT_PARENT (pad));
430   
431   if (GST_CAPS_IS_FIXED (caps)) {
432       gst_caps_get_int (caps, "rate", &rate);
433       gst_caps_get_int (caps, "buffer-frames", &buffer_frames);
434       if (this->bin && (rate != this->bin->rate ||
435                         buffer_frames != this->bin->nframes))
436           return GST_PAD_LINK_REFUSED;
437       
438       return GST_PAD_LINK_OK;
439   }
440   
441   return GST_PAD_LINK_DELAYED;
442 }
443
444 static void
445 gst_jack_loop (GstElement *element)
446 {
447     GstJack *this;
448     GList *pads;
449     gint len;
450     GstJackPad *pad;
451     GstBuffer *buffer;
452     
453     this = GST_JACK (element);
454     
455     len = this->bin->nframes * sizeof (sample_t);
456     
457     pads = this->pads;
458     while (pads) {
459         pad = GST_JACK_PAD (pads);
460             
461         if (this->direction == GST_PAD_SINK) {
462             buffer = GST_BUFFER (gst_pad_pull (pad->pad));
463                 
464             if (GST_IS_EVENT (buffer)) {
465                 GstEvent *event = GST_EVENT (buffer);
466                 switch (GST_EVENT_TYPE (buffer)) {
467                 case GST_EVENT_EOS:
468                     gst_element_set_eos (element);
469                     gst_event_unref (event);
470                     return;
471                 default:
472                     gst_pad_event_default (pad->pad, event);
473                     return;
474                 }
475             }
476                 
477             /* if the other plugins only give out buffer-frames or less (as
478                they should), if the length of the GstBuffer is different
479                from nframes then the buffer is short and we will get EOS
480                next */
481             memcpy (pad->data, GST_BUFFER_DATA (buffer),
482                     GST_BUFFER_SIZE (buffer));
483             if (len != GST_BUFFER_SIZE (buffer))
484                 memset (pad->data + GST_BUFFER_SIZE (buffer), 0,
485                         len - GST_BUFFER_SIZE (buffer));
486
487             gst_buffer_unref (buffer);
488         } else {
489             buffer = gst_buffer_new ();
490             gst_buffer_set_data (buffer, pad->data, len);
491             GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE);
492                 
493             gst_pad_push (pad->pad, GST_DATA (buffer));
494         }
495         pads = g_list_next (pads);
496     }
497 }
498
499 static gboolean
500 plugin_init (GstPlugin *plugin)
501 {
502   if (!gst_element_register (plugin, "jackbin", GST_RANK_NONE, GST_TYPE_JACK_BIN))
503     return FALSE;
504
505   if (!gst_element_register (plugin, "jacksrc", GST_RANK_NONE, GST_TYPE_JACK_SRC))
506     return FALSE;
507
508   if (!gst_element_register (plugin, "jacksink", GST_RANK_NONE, GST_TYPE_JACK_SINK))
509     return FALSE;
510     
511   return TRUE;
512 }
513
514 GST_PLUGIN_DEFINE (
515   GST_VERSION_MAJOR,
516   GST_VERSION_MINOR,
517   "jack",
518   "Jack Plugin Library",
519   plugin_init,
520   VERSION,
521   "GPL",
522   GST_COPYRIGHT,
523   GST_PACKAGE,
524   GST_ORIGIN)