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 = {
44 "Jack processing bin",
46 "Andy Wingo <wingo@pobox.com>",
50 static GstElementDetails gst_jack_sink_details = {
54 "Output to a Jack processing network",
56 "Andy Wingo <wingo@pobox.com>",
60 static GstElementDetails gst_jack_src_details = {
64 "Input from a Jack processing network",
66 "Andy Wingo <wingo@pobox.com>",
71 static GHashTable *port_name_counts = NULL;
72 static GstElementClass *parent_class = NULL;
74 static void gst_jack_init(GstJack *this);
75 static void gst_jack_class_init(GstJackClass *klass);
76 static void gst_jack_set_property (GObject *object, guint prop_id,
77 const GValue *value, GParamSpec *pspec);
78 static void gst_jack_get_property (GObject *object, guint prop_id,
79 GValue *value, GParamSpec *pspec);
81 static GstPadTemplate* gst_jack_src_request_pad_factory();
82 static GstPadTemplate* gst_jack_sink_request_pad_factory();
83 static GstPad* gst_jack_request_new_pad (GstElement *element, GstPadTemplate *templ,
85 static GstElementStateReturn gst_jack_change_state (GstElement *element);
86 static GstPadLinkReturn gst_jack_link (GstPad *pad, GstCaps *caps);
88 static void gst_jack_loop (GstElement *element);
98 gst_jack_get_type (void)
100 static GType jack_type = 0;
103 static const GTypeInfo jack_info = {
104 sizeof(GstJackClass),
114 jack_type = g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0);
120 gst_jack_sink_get_type (void)
122 static GType jack_type = 0;
125 static const GTypeInfo jack_info = {
126 sizeof(GstJackClass),
129 (GClassInitFunc)gst_jack_class_init,
134 (GInstanceInitFunc)gst_jack_init,
136 jack_type = 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),
151 (GClassInitFunc)gst_jack_class_init,
156 (GInstanceInitFunc)gst_jack_init,
158 jack_type = g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0);
164 gst_jack_class_init(GstJackClass *klass)
166 GObjectClass *object_class;
167 GstElementClass *element_class;
171 object_class = (GObjectClass *)klass;
172 element_class = (GstElementClass *)klass;
174 if (parent_class == NULL)
175 parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
177 object_class->get_property = gst_jack_get_property;
178 object_class->set_property = gst_jack_set_property;
180 if (GST_IS_JACK_SINK_CLASS (klass))
185 pspec = g_param_spec_string ("port-name-prefix", "Port name prefix",
186 "String to prepend to jack port names",
187 prefix, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
188 g_object_class_install_property (object_class, ARG_PORT_NAME_PREFIX, pspec);
190 element_class->change_state = gst_jack_change_state;
192 element_class->request_new_pad = gst_jack_request_new_pad;
196 gst_jack_init(GstJack *this)
198 if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SRC)
199 this->direction = GST_PAD_SRC;
200 else if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SINK)
201 this->direction = GST_PAD_SINK;
203 g_assert_not_reached ();
205 gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop);
209 gst_jack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
211 GstJack *this = (GstJack*)object;
214 case ARG_PORT_NAME_PREFIX:
215 if (this->port_name_prefix)
216 g_free (this->port_name_prefix);
217 this->port_name_prefix = g_strdup (g_value_get_string (value));
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226 gst_jack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
228 GstJack *this = (GstJack*)object;
231 case ARG_PORT_NAME_PREFIX:
232 g_value_set_string (value, this->port_name_prefix);
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240 static GstPadTemplate*
241 gst_jack_src_request_pad_factory (void)
243 static GstPadTemplate *template = NULL;
247 caps = gst_caps_new ("src",
249 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
250 template = gst_pad_template_new ("%s", GST_PAD_SRC,
251 GST_PAD_REQUEST, caps, NULL);
257 static GstPadTemplate*
258 gst_jack_sink_request_pad_factory (void)
260 static GstPadTemplate *template = NULL;
264 caps = gst_caps_new ("sink",
266 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS);
267 template = gst_pad_template_new ("%s", GST_PAD_SINK,
268 GST_PAD_REQUEST, caps, NULL);
275 gst_jack_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name)
279 GList *l, **pad_list;
283 g_return_val_if_fail ((this = GST_JACK (element)), NULL);
286 pad_list = &this->pads;
287 else if (this->direction == GST_PAD_SRC)
288 pad_list = &this->bin->src_pads;
290 pad_list = &this->bin->sink_pads;
295 if (strcmp (GST_JACK_PAD(l)->name, name) == 0) {
296 g_warning("requested port name %s already in use.", name);
301 newname = g_strdup (name);
303 if (this->direction == GST_PAD_SINK)
304 newname = g_strdup ("alsa_pcm:playback_1");
306 newname = g_strdup ("alsa_pcm:capture_1");
309 pad = g_new0 (GstJackPad, 1);
311 if (!port_name_counts)
312 port_name_counts = g_hash_table_new (g_str_hash, g_str_equal);
314 count = GPOINTER_TO_INT (g_hash_table_lookup (port_name_counts, this->port_name_prefix));
315 g_hash_table_insert (port_name_counts, g_strdup (this->port_name_prefix), GINT_TO_POINTER (count+1));
317 pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count);
319 pad->peer_name = newname;
320 pad->pad = gst_pad_new_from_template (templ, newname);
321 gst_element_add_pad (GST_ELEMENT (this), pad->pad);
322 gst_pad_set_link_function (pad->pad, gst_jack_link);
324 this->pads = g_list_append (this->pads, pad);
326 g_print ("returning from request_new_pad, pad %s created, to connect to %s\n", pad->name, pad->peer_name);
330 static GstElementStateReturn
331 gst_jack_change_state (GstElement *element)
334 GList *l = NULL, **pads;
338 g_return_val_if_fail (element != NULL, FALSE);
339 this = GST_JACK (element);
341 switch (GST_STATE_PENDING (element)) {
343 JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this)));
347 case GST_STATE_READY:
348 JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this)));
351 if (!(this->bin = (GstJackBin*)gst_element_get_managing_bin (element))
352 || !GST_IS_JACK_BIN (this->bin)) {
354 g_warning ("jack element %s needs to be contained in a jack bin.",
355 GST_OBJECT_NAME (element));
356 return GST_STATE_FAILURE;
359 /* fixme: verify that all names are unique */
361 pads = (this->direction == GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads;
363 pad = GST_JACK_PAD (l);
364 JACK_DEBUG ("%s: appending pad %s:%s to list", GST_OBJECT_NAME (this), pad->name, pad->peer_name);
365 *pads = g_list_append (*pads, pad);
371 case GST_STATE_PAUSED:
372 JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this)));
374 if (GST_STATE (element) == GST_STATE_READY) {
375 /* we're in READY->PAUSED */
378 pad = GST_JACK_PAD (l);
379 caps = gst_pad_get_caps (pad->pad);
380 gst_caps_set (caps, "rate", GST_PROPS_INT_TYPE,
381 (gint)this->bin->rate, NULL);
382 gst_caps_set (caps, "buffer-frames", GST_PROPS_INT_TYPE,
383 (gint)this->bin->nframes, NULL);
384 if (gst_pad_try_set_caps (pad->pad, caps) <= 0)
385 return GST_STATE_FAILURE;
390 case GST_STATE_PLAYING:
391 JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this)));
395 JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this));
397 if (GST_ELEMENT_CLASS (parent_class)->change_state)
398 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
400 return GST_STATE_SUCCESS;
403 static GstPadLinkReturn
404 gst_jack_link (GstPad *pad, GstCaps *caps)
407 gint rate, buffer_frames;
409 this = GST_JACK (GST_OBJECT_PARENT (pad));
411 if (GST_CAPS_IS_FIXED (caps)) {
412 gst_caps_get_int (caps, "rate", &rate);
413 gst_caps_get_int (caps, "buffer-frames", &buffer_frames);
414 if (this->bin && (rate != this->bin->rate ||
415 buffer_frames != this->bin->nframes))
416 return GST_PAD_LINK_REFUSED;
418 return GST_PAD_LINK_OK;
421 return GST_PAD_LINK_DELAYED;
425 gst_jack_loop (GstElement *element)
433 this = GST_JACK (element);
435 len = this->bin->nframes * sizeof (sample_t);
439 pad = GST_JACK_PAD (pads);
441 if (this->direction == GST_PAD_SINK) {
442 buffer = GST_BUFFER (gst_pad_pull (pad->pad));
444 if (GST_IS_EVENT (buffer)) {
445 GstEvent *event = GST_EVENT (buffer);
446 switch (GST_EVENT_TYPE (buffer)) {
448 gst_element_set_eos (element);
449 gst_event_unref (event);
452 gst_pad_event_default (pad->pad, event);
457 /* if the other plugins only give out buffer-frames or less (as
458 they should), if the length of the GstBuffer is different
459 from nframes then the buffer is short and we will get EOS
461 memcpy (pad->data, GST_BUFFER_DATA (buffer),
462 GST_BUFFER_SIZE (buffer));
463 if (len != GST_BUFFER_SIZE (buffer))
464 memset (pad->data + GST_BUFFER_SIZE (buffer), 0,
465 len - GST_BUFFER_SIZE (buffer));
467 gst_buffer_unref (buffer);
469 buffer = gst_buffer_new ();
470 gst_buffer_set_data (buffer, pad->data, len);
471 GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE);
473 gst_pad_push (pad->pad, GST_DATA (buffer));
475 pads = g_list_next (pads);
480 plugin_init (GModule *module, GstPlugin *plugin)
482 GstElementFactory *factory;
484 factory = gst_element_factory_new ("jackbin", GST_TYPE_JACK_BIN, &gst_jack_bin_details);
485 g_return_val_if_fail (factory != NULL, FALSE);
486 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
488 factory = gst_element_factory_new ("jacksrc", GST_TYPE_JACK_SRC, &gst_jack_src_details);
489 g_return_val_if_fail (factory != NULL, FALSE);
490 gst_element_factory_add_pad_template (factory, gst_jack_src_request_pad_factory());
491 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
493 factory = gst_element_factory_new ("jacksink", GST_TYPE_JACK_SINK, &gst_jack_sink_details);
494 g_return_val_if_fail (factory != NULL, FALSE);
495 gst_element_factory_add_pad_template (factory, gst_jack_sink_request_pad_factory());
496 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
498 gst_plugin_set_longname (plugin, "JACK plugin library");
503 GstPluginDesc plugin_desc = {