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,
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);
83 static void gst_jack_loop (GstElement * element);
94 gst_jack_get_type (void)
96 static GType jack_type = 0;
99 static const GTypeInfo jack_info = {
100 sizeof (GstJackClass),
112 g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0);
118 gst_jack_sink_get_type (void)
120 static GType jack_type = 0;
123 static const GTypeInfo jack_info = {
124 sizeof (GstJackClass),
125 gst_jack_sink_base_init,
127 (GClassInitFunc) gst_jack_class_init,
132 (GInstanceInitFunc) gst_jack_init,
136 g_type_register_static (GST_TYPE_JACK, "GstJackSink", &jack_info, 0);
142 gst_jack_src_get_type (void)
144 static GType jack_type = 0;
147 static const GTypeInfo jack_info = {
148 sizeof (GstJackClass),
149 gst_jack_src_base_init,
151 (GClassInitFunc) gst_jack_class_init,
156 (GInstanceInitFunc) gst_jack_init,
160 g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0);
166 gst_jack_base_init (gpointer g_class)
168 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
170 gst_element_class_set_details (element_class, &gst_jack_bin_details);
174 gst_jack_src_base_init (gpointer g_class)
176 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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);
184 gst_jack_sink_base_init (gpointer g_class)
186 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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);
194 gst_jack_class_init (GstJackClass * klass)
196 GObjectClass *object_class;
197 GstElementClass *element_class;
201 object_class = (GObjectClass *) klass;
202 element_class = (GstElementClass *) klass;
204 if (parent_class == NULL)
205 parent_class = g_type_class_peek_parent (klass);
207 object_class->get_property = gst_jack_get_property;
208 object_class->set_property = gst_jack_set_property;
210 if (GST_IS_JACK_SINK_CLASS (klass))
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);
220 element_class->change_state = gst_jack_change_state;
222 element_class->request_new_pad = gst_jack_request_new_pad;
226 gst_jack_init (GstJack * this)
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;
233 g_assert_not_reached ();
235 gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop);
239 gst_jack_set_property (GObject * object, guint prop_id, const GValue * value,
242 GstJack *this = (GstJack *) object;
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));
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257 gst_jack_get_property (GObject * object, guint prop_id, GValue * value,
260 GstJack *this = (GstJack *) object;
263 case ARG_PORT_NAME_PREFIX:
264 g_value_set_string (value, this->port_name_prefix);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272 static GstPadTemplate *
273 gst_jack_src_request_pad_factory (void)
275 static GstPadTemplate *template = NULL;
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);
287 static GstPadTemplate *
288 gst_jack_sink_request_pad_factory (void)
290 static GstPadTemplate *template = NULL;
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);
303 gst_jack_request_new_pad (GstElement * element, GstPadTemplate * templ,
308 GList *l, **pad_list;
312 g_return_val_if_fail (GST_IS_JACK (element), NULL);
313 this = GST_JACK (element);
316 pad_list = &this->pads;
317 else if (this->direction == GST_PAD_SRC)
318 pad_list = &this->bin->src_pads;
320 pad_list = &this->bin->sink_pads;
325 if (strcmp (GST_JACK_PAD (l)->name, name) == 0) {
326 g_warning ("requested port name %s already in use.", name);
331 newname = g_strdup (name);
333 if (this->direction == GST_PAD_SINK)
334 newname = g_strdup ("alsa_pcm:playback_1");
336 newname = g_strdup ("alsa_pcm:capture_1");
339 pad = g_new0 (GstJackPad, 1);
341 if (!port_name_counts)
342 port_name_counts = g_hash_table_new (g_str_hash, g_str_equal);
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));
350 pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count);
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);
357 this->pads = g_list_append (this->pads, pad);
359 g_print ("returning from request_new_pad, pad %s created, to connect to %s\n",
360 pad->name, pad->peer_name);
364 static GstStateChangeReturn
365 gst_jack_change_state (GstElement * element, GstStateChange transition)
368 GList *l = NULL, **pads;
372 g_return_val_if_fail (element != NULL, FALSE);
373 this = GST_JACK (element);
375 switch (GST_STATE_PENDING (element)) {
377 JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this)));
381 case GST_STATE_READY:
382 JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this)));
385 if (!(this->bin = (GstJackBin *) gst_element_get_managing_bin (element))
386 || !GST_IS_JACK_BIN (this->bin)) {
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;
393 /* fixme: verify that all names are unique */
397 GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads;
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);
408 case GST_STATE_PAUSED:
409 JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this)));
411 if (GST_STATE (element) == GST_STATE_READY) {
412 /* we're in READY->PAUSED */
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;
426 case GST_STATE_PLAYING:
427 JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this)));
431 JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this));
433 if (GST_ELEMENT_CLASS (parent_class)->change_state)
434 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
436 return GST_STATE_CHANGE_SUCCESS;
439 static GstPadLinkReturn
440 gst_jack_link (GstPad * pad, const GstCaps * caps)
443 gint rate, buffer_frames;
444 GstStructure *structure;
446 this = GST_JACK (GST_OBJECT_PARENT (pad));
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;
455 return GST_PAD_LINK_OK;
459 gst_jack_loop (GstElement * element)
467 this = GST_JACK (element);
469 len = this->bin->nframes * sizeof (sample_t);
473 pad = GST_JACK_PAD (pads);
475 if (this->direction == GST_PAD_SINK) {
476 buffer = GST_BUFFER (gst_pad_pull (pad->pad));
478 if (GST_IS_EVENT (buffer)) {
479 GstEvent *event = GST_EVENT (buffer);
481 switch (GST_EVENT_TYPE (buffer)) {
483 gst_element_set_eos (element);
484 gst_event_unref (event);
487 gst_pad_event_default (pad->pad, event);
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
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));
501 gst_buffer_unref (buffer);
503 buffer = gst_buffer_new ();
504 gst_buffer_set_data (buffer, pad->data, len);
505 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTFREE);
507 gst_pad_push (pad->pad, GST_DATA (buffer));
509 pads = g_list_next (pads);
514 plugin_init (GstPlugin * plugin)
516 if (!gst_element_register (plugin, "jackbin", GST_RANK_NONE,
520 if (!gst_element_register (plugin, "jacksrc", GST_RANK_NONE,
524 if (!gst_element_register (plugin, "jacksink", GST_RANK_NONE,
531 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
534 "Jack Plugin Library", plugin_init, VERSION, "GPL", GST_PACKAGE_NAME,