Fix #337365 (g_type_class_ref <-> g_type_class_peek_parent)
[platform/upstream/gstreamer.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,
78     GstPadTemplate * templ, const gchar * name);
79 static GstStateChangeReturn gst_jack_change_state (GstElement * element,
80     GstStateChange transition);
81 static GstPadLinkReturn gst_jack_link (GstPad * pad, const GstCaps * caps);
82
83 static void gst_jack_loop (GstElement * element);
84
85
86 enum
87 {
88   ARG_0,
89   ARG_PORT_NAME_PREFIX
90 };
91
92
93 GType
94 gst_jack_get_type (void)
95 {
96   static GType jack_type = 0;
97
98   if (!jack_type) {
99     static const GTypeInfo jack_info = {
100       sizeof (GstJackClass),
101       gst_jack_base_init,
102       NULL,
103       NULL,
104       NULL,
105       NULL,
106       sizeof (GstJack),
107       0,
108       NULL,
109     };
110
111     jack_type =
112         g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0);
113   }
114   return jack_type;
115 }
116
117 GType
118 gst_jack_sink_get_type (void)
119 {
120   static GType jack_type = 0;
121
122   if (!jack_type) {
123     static const GTypeInfo jack_info = {
124       sizeof (GstJackClass),
125       gst_jack_sink_base_init,
126       NULL,
127       (GClassInitFunc) gst_jack_class_init,
128       NULL,
129       NULL,
130       sizeof (GstJack),
131       0,
132       (GInstanceInitFunc) gst_jack_init,
133     };
134
135     jack_type =
136         g_type_register_static (GST_TYPE_JACK, "GstJackSink", &jack_info, 0);
137   }
138   return jack_type;
139 }
140
141 GType
142 gst_jack_src_get_type (void)
143 {
144   static GType jack_type = 0;
145
146   if (!jack_type) {
147     static const GTypeInfo jack_info = {
148       sizeof (GstJackClass),
149       gst_jack_src_base_init,
150       NULL,
151       (GClassInitFunc) gst_jack_class_init,
152       NULL,
153       NULL,
154       sizeof (GstJack),
155       0,
156       (GInstanceInitFunc) gst_jack_init,
157     };
158
159     jack_type =
160         g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0);
161   }
162   return jack_type;
163 }
164
165 static void
166 gst_jack_base_init (gpointer g_class)
167 {
168   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
169
170   gst_element_class_set_details (element_class, &gst_jack_bin_details);
171 }
172
173 static void
174 gst_jack_src_base_init (gpointer g_class)
175 {
176   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
177
178   gst_element_class_add_pad_template (element_class,
179       gst_jack_src_request_pad_factory ());
180   gst_element_class_set_details (element_class, &gst_jack_src_details);
181 }
182
183 static void
184 gst_jack_sink_base_init (gpointer g_class)
185 {
186   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
187
188   gst_element_class_add_pad_template (element_class,
189       gst_jack_sink_request_pad_factory ());
190   gst_element_class_set_details (element_class, &gst_jack_sink_details);
191 }
192
193 static void
194 gst_jack_class_init (GstJackClass * klass)
195 {
196   GObjectClass *object_class;
197   GstElementClass *element_class;
198   GParamSpec *pspec;
199   gchar *prefix;
200
201   object_class = (GObjectClass *) klass;
202   element_class = (GstElementClass *) klass;
203
204   if (parent_class == NULL)
205     parent_class = g_type_class_peek_parent (klass);
206
207   object_class->get_property = gst_jack_get_property;
208   object_class->set_property = gst_jack_set_property;
209
210   if (GST_IS_JACK_SINK_CLASS (klass))
211     prefix = "gst-out-";
212   else
213     prefix = "gst-in-";
214
215   pspec = g_param_spec_string ("port-name-prefix", "Port name prefix",
216       "String to prepend to jack port names",
217       prefix, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
218   g_object_class_install_property (object_class, ARG_PORT_NAME_PREFIX, pspec);
219
220   element_class->change_state = gst_jack_change_state;
221
222   element_class->request_new_pad = gst_jack_request_new_pad;
223 }
224
225 static void
226 gst_jack_init (GstJack * this)
227 {
228   if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SRC)
229     this->direction = GST_PAD_SRC;
230   else if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SINK)
231     this->direction = GST_PAD_SINK;
232   else
233     g_assert_not_reached ();
234
235   gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop);
236 }
237
238 static void
239 gst_jack_set_property (GObject * object, guint prop_id, const GValue * value,
240     GParamSpec * pspec)
241 {
242   GstJack *this = (GstJack *) object;
243
244   switch (prop_id) {
245     case ARG_PORT_NAME_PREFIX:
246       if (this->port_name_prefix)
247         g_free (this->port_name_prefix);
248       this->port_name_prefix = g_strdup (g_value_get_string (value));
249       break;
250     default:
251       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252       return;
253   }
254 }
255
256 static void
257 gst_jack_get_property (GObject * object, guint prop_id, GValue * value,
258     GParamSpec * pspec)
259 {
260   GstJack *this = (GstJack *) object;
261
262   switch (prop_id) {
263     case ARG_PORT_NAME_PREFIX:
264       g_value_set_string (value, this->port_name_prefix);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269   }
270 }
271
272 static GstPadTemplate *
273 gst_jack_src_request_pad_factory (void)
274 {
275   static GstPadTemplate *template = NULL;
276
277   if (!template) {
278     GstCaps *caps;
279
280     caps = gst_caps_from_string (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS);
281     template = gst_pad_template_new ("%s", GST_PAD_SRC, GST_PAD_REQUEST, caps);
282   }
283
284   return template;
285 }
286
287 static GstPadTemplate *
288 gst_jack_sink_request_pad_factory (void)
289 {
290   static GstPadTemplate *template = NULL;
291
292   if (!template) {
293     GstCaps *caps;
294
295     caps = gst_caps_from_string (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS);
296     template = gst_pad_template_new ("%s", GST_PAD_SINK, GST_PAD_REQUEST, caps);
297   }
298
299   return template;
300 }
301
302 static GstPad *
303 gst_jack_request_new_pad (GstElement * element, GstPadTemplate * templ,
304     const gchar * name)
305 {
306   GstJack *this;
307   gchar *newname;
308   GList *l, **pad_list;
309   GstJackPad *pad;
310   gint count;
311
312   g_return_val_if_fail (GST_IS_JACK (element), NULL);
313   this = GST_JACK (element);
314
315   if (!this->bin)
316     pad_list = &this->pads;
317   else if (this->direction == GST_PAD_SRC)
318     pad_list = &this->bin->src_pads;
319   else
320     pad_list = &this->bin->sink_pads;
321
322   if (name) {
323     l = *pad_list;
324     while (l) {
325       if (strcmp (GST_JACK_PAD (l)->name, name) == 0) {
326         g_warning ("requested port name %s already in use.", name);
327         return NULL;
328       }
329       l = l->next;
330     }
331     newname = g_strdup (name);
332   } else {
333     if (this->direction == GST_PAD_SINK)
334       newname = g_strdup ("alsa_pcm:playback_1");
335     else
336       newname = g_strdup ("alsa_pcm:capture_1");
337   }
338
339   pad = g_new0 (GstJackPad, 1);
340
341   if (!port_name_counts)
342     port_name_counts = g_hash_table_new (g_str_hash, g_str_equal);
343
344   count =
345       GPOINTER_TO_INT (g_hash_table_lookup (port_name_counts,
346           this->port_name_prefix));
347   g_hash_table_insert (port_name_counts, g_strdup (this->port_name_prefix),
348       GINT_TO_POINTER (count + 1));
349
350   pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count);
351
352   pad->peer_name = newname;
353   pad->pad = gst_pad_new_from_template (templ, newname);
354   gst_element_add_pad (GST_ELEMENT (this), pad->pad);
355   gst_pad_set_link_function (pad->pad, gst_jack_link);
356
357   this->pads = g_list_append (this->pads, pad);
358
359   g_print ("returning from request_new_pad, pad %s created, to connect to %s\n",
360       pad->name, pad->peer_name);
361   return pad->pad;
362 }
363
364 static GstStateChangeReturn
365 gst_jack_change_state (GstElement * element, GstStateChange transition)
366 {
367   GstJack *this;
368   GList *l = NULL, **pads;
369   GstJackPad *pad;
370   GstCaps *caps;
371
372   g_return_val_if_fail (element != NULL, FALSE);
373   this = GST_JACK (element);
374
375   switch (GST_STATE_PENDING (element)) {
376     case GST_STATE_NULL:
377       JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this)));
378
379       break;
380
381     case GST_STATE_READY:
382       JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this)));
383
384       if (!this->bin) {
385         if (!(this->bin = (GstJackBin *) gst_element_get_managing_bin (element))
386             || !GST_IS_JACK_BIN (this->bin)) {
387           this->bin = NULL;
388           g_warning ("jack element %s needs to be contained in a jack bin.",
389               GST_OBJECT_NAME (element));
390           return GST_STATE_CHANGE_FAILURE;
391         }
392
393         /* fixme: verify that all names are unique */
394         l = this->pads;
395         pads =
396             (this->direction ==
397             GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads;
398         while (l) {
399           pad = GST_JACK_PAD (l);
400           JACK_DEBUG ("%s: appending pad %s:%s to list", GST_OBJECT_NAME (this),
401               pad->name, pad->peer_name);
402           *pads = g_list_append (*pads, pad);
403           l = g_list_next (l);
404         }
405       }
406       break;
407
408     case GST_STATE_PAUSED:
409       JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this)));
410
411       if (GST_STATE (element) == GST_STATE_READY) {
412         /* we're in READY->PAUSED */
413         l = this->pads;
414         while (l) {
415           pad = GST_JACK_PAD (l);
416           caps = gst_caps_copy (gst_pad_get_negotiated_caps (pad->pad));
417           gst_caps_set_simple (caps,
418               "rate", G_TYPE_INT, (int) this->bin->rate,
419               "buffer-frames", G_TYPE_INT, (gint) this->bin->nframes, NULL);
420           if (gst_pad_try_set_caps (pad->pad, caps) <= 0)
421             return GST_STATE_CHANGE_FAILURE;
422           l = g_list_next (l);
423         }
424       }
425       break;
426     case GST_STATE_PLAYING:
427       JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this)));
428       break;
429   }
430
431   JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this));
432
433   if (GST_ELEMENT_CLASS (parent_class)->change_state)
434     return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
435
436   return GST_STATE_CHANGE_SUCCESS;
437 }
438
439 static GstPadLinkReturn
440 gst_jack_link (GstPad * pad, const GstCaps * caps)
441 {
442   GstJack *this;
443   gint rate, buffer_frames;
444   GstStructure *structure;
445
446   this = GST_JACK (GST_OBJECT_PARENT (pad));
447
448   structure = gst_caps_get_structure (caps, 0);
449   gst_structure_get_int (structure, "rate", &rate);
450   gst_structure_get_int (structure, "buffer-frames", &buffer_frames);
451   if (this->bin && (rate != this->bin->rate ||
452           buffer_frames != this->bin->nframes))
453     return GST_PAD_LINK_REFUSED;
454
455   return GST_PAD_LINK_OK;
456 }
457
458 static void
459 gst_jack_loop (GstElement * element)
460 {
461   GstJack *this;
462   GList *pads;
463   gint len;
464   GstJackPad *pad;
465   GstBuffer *buffer;
466
467   this = GST_JACK (element);
468
469   len = this->bin->nframes * sizeof (sample_t);
470
471   pads = this->pads;
472   while (pads) {
473     pad = GST_JACK_PAD (pads);
474
475     if (this->direction == GST_PAD_SINK) {
476       buffer = GST_BUFFER (gst_pad_pull (pad->pad));
477
478       if (GST_IS_EVENT (buffer)) {
479         GstEvent *event = GST_EVENT (buffer);
480
481         switch (GST_EVENT_TYPE (buffer)) {
482           case GST_EVENT_EOS:
483             gst_element_set_eos (element);
484             gst_event_unref (event);
485             return;
486           default:
487             gst_pad_event_default (pad->pad, event);
488             return;
489         }
490       }
491
492       /* if the other plugins only give out buffer-frames or less (as
493          they should), if the length of the GstBuffer is different
494          from nframes then the buffer is short and we will get EOS
495          next */
496       memcpy (pad->data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
497       if (len != GST_BUFFER_SIZE (buffer))
498         memset (pad->data + GST_BUFFER_SIZE (buffer), 0,
499             len - GST_BUFFER_SIZE (buffer));
500
501       gst_buffer_unref (buffer);
502     } else {
503       buffer = gst_buffer_new ();
504       gst_buffer_set_data (buffer, pad->data, len);
505       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTFREE);
506
507       gst_pad_push (pad->pad, GST_DATA (buffer));
508     }
509     pads = g_list_next (pads);
510   }
511 }
512
513 static gboolean
514 plugin_init (GstPlugin * plugin)
515 {
516   if (!gst_element_register (plugin, "jackbin", GST_RANK_NONE,
517           GST_TYPE_JACK_BIN))
518     return FALSE;
519
520   if (!gst_element_register (plugin, "jacksrc", GST_RANK_NONE,
521           GST_TYPE_JACK_SRC))
522     return FALSE;
523
524   if (!gst_element_register (plugin, "jacksink", GST_RANK_NONE,
525           GST_TYPE_JACK_SINK))
526     return FALSE;
527
528   return TRUE;
529 }
530
531 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
532     GST_VERSION_MINOR,
533     "jack",
534     "Jack Plugin Library", plugin_init, VERSION, "GPL", GST_PACKAGE_NAME,
535     GST_PACKAGE_ORIGIN)