1 /* -*- Mode: C; c-basic-offset: 4 -*- */
3 Copyright (C) 2002, 2003 Andy Wingo <wingo@pobox.com>
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.
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.
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.
26 #include <gst/audio/audio.h>
30 - work out the src side (caps setting, etc)
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
39 /* elementfactory information */
40 static GstElementDetails gst_jack_bin_details = {
43 "Jack processing bin",
44 "Andy Wingo <wingo@pobox.com>",
47 static GstElementDetails gst_jack_sink_details = {
50 "Output to a Jack processing network",
51 "Andy Wingo <wingo@pobox.com>",
54 static GstElementDetails gst_jack_src_details = {
57 "Input from a Jack processing network",
58 "Andy Wingo <wingo@pobox.com>",
62 static GHashTable *port_name_counts = NULL;
63 static GstElementClass *parent_class = NULL;
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);
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,
79 static GstElementStateReturn gst_jack_change_state (GstElement *element);
80 static GstPadLinkReturn gst_jack_link (GstPad *pad, GstCaps *caps);
82 static void gst_jack_loop (GstElement *element);
92 gst_jack_get_type (void)
94 static GType jack_type = 0;
97 static const GTypeInfo jack_info = {
108 jack_type = g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0);
114 gst_jack_sink_get_type (void)
116 static GType jack_type = 0;
119 static const GTypeInfo jack_info = {
120 sizeof(GstJackClass),
121 gst_jack_sink_base_init,
123 (GClassInitFunc)gst_jack_class_init,
128 (GInstanceInitFunc)gst_jack_init,
130 jack_type = g_type_register_static (GST_TYPE_JACK, "GstJackSink", &jack_info, 0);
136 gst_jack_src_get_type (void)
138 static GType jack_type = 0;
141 static const GTypeInfo jack_info = {
142 sizeof(GstJackClass),
143 gst_jack_src_base_init,
145 (GClassInitFunc)gst_jack_class_init,
150 (GInstanceInitFunc)gst_jack_init,
152 jack_type = g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0);
158 gst_jack_base_init (gpointer g_class)
160 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
162 gst_element_class_set_details (element_class, &gst_jack_bin_details);
166 gst_jack_src_base_init (gpointer g_class)
168 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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);
175 gst_jack_sink_base_init (gpointer g_class)
177 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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);
184 gst_jack_class_init(GstJackClass *klass)
186 GObjectClass *object_class;
187 GstElementClass *element_class;
191 object_class = (GObjectClass *)klass;
192 element_class = (GstElementClass *)klass;
194 if (parent_class == NULL)
195 parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
197 object_class->get_property = gst_jack_get_property;
198 object_class->set_property = gst_jack_set_property;
200 if (GST_IS_JACK_SINK_CLASS (klass))
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);
210 element_class->change_state = gst_jack_change_state;
212 element_class->request_new_pad = gst_jack_request_new_pad;
216 gst_jack_init(GstJack *this)
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;
223 g_assert_not_reached ();
225 gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop);
229 gst_jack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
231 GstJack *this = (GstJack*)object;
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));
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 gst_jack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
248 GstJack *this = (GstJack*)object;
251 case ARG_PORT_NAME_PREFIX:
252 g_value_set_string (value, this->port_name_prefix);
255 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260 static GstPadTemplate*
261 gst_jack_src_request_pad_factory (void)
263 static GstPadTemplate *template = NULL;
267 caps = gst_caps_new ("src",
269 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
270 template = gst_pad_template_new ("%s", GST_PAD_SRC,
271 GST_PAD_REQUEST, caps, NULL);
277 static GstPadTemplate*
278 gst_jack_sink_request_pad_factory (void)
280 static GstPadTemplate *template = NULL;
284 caps = gst_caps_new ("sink",
286 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
287 template = gst_pad_template_new ("%s", GST_PAD_SINK,
288 GST_PAD_REQUEST, caps, NULL);
295 gst_jack_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name)
299 GList *l, **pad_list;
303 g_return_val_if_fail ((this = GST_JACK (element)), NULL);
306 pad_list = &this->pads;
307 else if (this->direction == GST_PAD_SRC)
308 pad_list = &this->bin->src_pads;
310 pad_list = &this->bin->sink_pads;
315 if (strcmp (GST_JACK_PAD(l)->name, name) == 0) {
316 g_warning("requested port name %s already in use.", name);
321 newname = g_strdup (name);
323 if (this->direction == GST_PAD_SINK)
324 newname = g_strdup ("alsa_pcm:playback_1");
326 newname = g_strdup ("alsa_pcm:capture_1");
329 pad = g_new0 (GstJackPad, 1);
331 if (!port_name_counts)
332 port_name_counts = g_hash_table_new (g_str_hash, g_str_equal);
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));
337 pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count);
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);
344 this->pads = g_list_append (this->pads, pad);
346 g_print ("returning from request_new_pad, pad %s created, to connect to %s\n", pad->name, pad->peer_name);
350 static GstElementStateReturn
351 gst_jack_change_state (GstElement *element)
354 GList *l = NULL, **pads;
358 g_return_val_if_fail (element != NULL, FALSE);
359 this = GST_JACK (element);
361 switch (GST_STATE_PENDING (element)) {
363 JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this)));
367 case GST_STATE_READY:
368 JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this)));
371 if (!(this->bin = (GstJackBin*)gst_element_get_managing_bin (element))
372 || !GST_IS_JACK_BIN (this->bin)) {
374 g_warning ("jack element %s needs to be contained in a jack bin.",
375 GST_OBJECT_NAME (element));
376 return GST_STATE_FAILURE;
379 /* fixme: verify that all names are unique */
381 pads = (this->direction == GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads;
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);
391 case GST_STATE_PAUSED:
392 JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this)));
394 if (GST_STATE (element) == GST_STATE_READY) {
395 /* we're in READY->PAUSED */
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;
410 case GST_STATE_PLAYING:
411 JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this)));
415 JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this));
417 if (GST_ELEMENT_CLASS (parent_class)->change_state)
418 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
420 return GST_STATE_SUCCESS;
423 static GstPadLinkReturn
424 gst_jack_link (GstPad *pad, GstCaps *caps)
427 gint rate, buffer_frames;
429 this = GST_JACK (GST_OBJECT_PARENT (pad));
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;
438 return GST_PAD_LINK_OK;
441 return GST_PAD_LINK_DELAYED;
445 gst_jack_loop (GstElement *element)
453 this = GST_JACK (element);
455 len = this->bin->nframes * sizeof (sample_t);
459 pad = GST_JACK_PAD (pads);
461 if (this->direction == GST_PAD_SINK) {
462 buffer = GST_BUFFER (gst_pad_pull (pad->pad));
464 if (GST_IS_EVENT (buffer)) {
465 GstEvent *event = GST_EVENT (buffer);
466 switch (GST_EVENT_TYPE (buffer)) {
468 gst_element_set_eos (element);
469 gst_event_unref (event);
472 gst_pad_event_default (pad->pad, event);
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
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));
487 gst_buffer_unref (buffer);
489 buffer = gst_buffer_new ();
490 gst_buffer_set_data (buffer, pad->data, len);
491 GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE);
493 gst_pad_push (pad->pad, GST_DATA (buffer));
495 pads = g_list_next (pads);
500 plugin_init (GstPlugin *plugin)
502 if (!gst_element_register (plugin, "jackbin", GST_RANK_NONE, GST_TYPE_JACK_BIN))
505 if (!gst_element_register (plugin, "jacksrc", GST_RANK_NONE, GST_TYPE_JACK_SRC))
508 if (!gst_element_register (plugin, "jacksink", GST_RANK_NONE, GST_TYPE_JACK_SINK))
518 "Jack Plugin Library",