From 76c1316131f03bc89de8dee086199d3c86ff2e4e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 30 Nov 2006 11:49:36 +0000 Subject: Added fully functional jackaudiosink. Original commit message from CVS: * configure.ac: * ext/Makefile.am: * ext/jack/Makefile.am: * ext/jack/gstjack.c: (plugin_init): * ext/jack/gstjack.h: * ext/jack/gstjackaudiosink.c: (gst_jack_ring_buffer_get_type), (gst_jack_ring_buffer_class_init), (jack_process_cb), (jack_sample_rate_cb), (jack_buffer_size_cb), (jack_shutdown_cb), (gst_jack_ring_buffer_init), (gst_jack_ring_buffer_dispose), (gst_jack_ring_buffer_finalize), (gst_jack_ring_buffer_open_device), (gst_jack_ring_buffer_close_device), (gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release), (gst_jack_ring_buffer_start), (gst_jack_ring_buffer_pause), (gst_jack_ring_buffer_stop), (gst_jack_ring_buffer_delay), (gst_jack_connect_get_type), (gst_jack_audio_sink_base_init), (gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init), (gst_jack_audio_sink_set_property), (gst_jack_audio_sink_get_property), (gst_jack_audio_sink_getcaps), (gst_jack_audio_sink_create_ringbuffer): * ext/jack/gstjackaudiosink.h: Added fully functional jackaudiosink. --- ChangeLog | 25 ++ configure.ac | 9 + ext/Makefile.am | 8 +- ext/jack/Makefile.am | 8 +- ext/jack/gstjack.c | 542 ++-------------------------- ext/jack/gstjack.h | 129 ------- ext/jack/gstjackaudiosink.c | 846 ++++++++++++++++++++++++++++++++++++++++++++ ext/jack/gstjackaudiosink.h | 80 +++++ 8 files changed, 992 insertions(+), 655 deletions(-) delete mode 100644 ext/jack/gstjack.h create mode 100644 ext/jack/gstjackaudiosink.c create mode 100644 ext/jack/gstjackaudiosink.h diff --git a/ChangeLog b/ChangeLog index 197d44c0..d0504afc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2006-11-30 Wim Taymans + + * configure.ac: + * ext/Makefile.am: + * ext/jack/Makefile.am: + * ext/jack/gstjack.c: (plugin_init): + * ext/jack/gstjack.h: + * ext/jack/gstjackaudiosink.c: (gst_jack_ring_buffer_get_type), + (gst_jack_ring_buffer_class_init), (jack_process_cb), + (jack_sample_rate_cb), (jack_buffer_size_cb), (jack_shutdown_cb), + (gst_jack_ring_buffer_init), (gst_jack_ring_buffer_dispose), + (gst_jack_ring_buffer_finalize), + (gst_jack_ring_buffer_open_device), + (gst_jack_ring_buffer_close_device), + (gst_jack_ring_buffer_acquire), (gst_jack_ring_buffer_release), + (gst_jack_ring_buffer_start), (gst_jack_ring_buffer_pause), + (gst_jack_ring_buffer_stop), (gst_jack_ring_buffer_delay), + (gst_jack_connect_get_type), (gst_jack_audio_sink_base_init), + (gst_jack_audio_sink_class_init), (gst_jack_audio_sink_init), + (gst_jack_audio_sink_set_property), + (gst_jack_audio_sink_get_property), (gst_jack_audio_sink_getcaps), + (gst_jack_audio_sink_create_ringbuffer): + * ext/jack/gstjackaudiosink.h: + Added fully functional jackaudiosink. + 2006-11-27 Wim Taymans * gst/qtdemux/qtdemux.c: (gst_qtdemux_get_duration), diff --git a/configure.ac b/configure.ac index 3a1b63da..71105b96 100644 --- a/configure.ac +++ b/configure.ac @@ -490,6 +490,14 @@ GST_CHECK_FEATURE(IVORBIS, [integer vorbis plug-in], ivorbisdec, [ AC_SUBST(IVORBIS_CFLAGS) ]) +dnl *** Jack *** +translit(dnm, m, l) AM_CONDITIONAL(USE_JACK, true) +GST_CHECK_FEATURE(JACK, Jack, jack, [ + PKG_CHECK_MODULES(JACK, jack >= 0.29.0, HAVE_JACK="yes", HAVE_JACK="no") + AC_SUBST(JACK_CFLAGS) + AC_SUBST(JACK_LIBS) +]) + dnl *** libmms *** translit(dnm, m, l) AM_CONDITIONAL(USE_LIBMMS, true) GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [ @@ -842,6 +850,7 @@ ext/faac/Makefile ext/faad/Makefile ext/gsm/Makefile ext/ivorbis/Makefile +ext/jack/Makefile ext/libmms/Makefile ext/Makefile ext/mpeg2enc/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 8dce1a47..0511c9ad 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -106,11 +106,11 @@ else IVORBIS_DIR= endif -# if USE_JACK -# JACK_DIR=jack -# else +if USE_JACK +JACK_DIR=jack +else JACK_DIR= -# endif +endif # if USE_LCS # LCS_DIR=lcs diff --git a/ext/jack/Makefile.am b/ext/jack/Makefile.am index a97a2ad7..0a237dd6 100644 --- a/ext/jack/Makefile.am +++ b/ext/jack/Makefile.am @@ -1,11 +1,11 @@ plugin_LTLIBRARIES = libgstjack.la -libgstjack_la_SOURCES = gstjack.c gstjackbin.c -libgstjack_la_CFLAGS = $(GST_CFLAGS) $(JACK_CFLAGS) -libgstjack_la_LIBADD = $(JACK_LIBS) +libgstjack_la_SOURCES = gstjack.c gstjackaudiosink.c +libgstjack_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(JACK_CFLAGS) +libgstjack_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(JACK_LIBS) libgstjack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstjack.h +noinst_HEADERS = gstjackaudiosink.h EXTRA_DIST = README diff --git a/ext/jack/gstjack.c b/ext/jack/gstjack.c index 2f6affcc..52b4b245 100644 --- a/ext/jack/gstjack.c +++ b/ext/jack/gstjack.c @@ -1,528 +1,33 @@ -/* -*- Mode: C; c-basic-offset: 4 -*- */ -/* - Copyright (C) 2002, 2003 Andy Wingo - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +/* GStreamer Jack plugins + * Copyright (C) 2006 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include -#include "gstjack.h" -#include - -/* TODO: - - - work out the src side (caps setting, etc) - - future core TODO: - - make a jack clock provider - - add GST_ELEMENT_FIXED_DATA_RATE, GST_ELEMENT_QOS, - GST_ELEMENT_CHANGES_DATA_RATE element flags, and make the scheduler - sensitive to them -*/ - -/* elementfactory information */ -static GstElementDetails gst_jack_bin_details = { - "Jack Bin", - "Generic/Bin", - "Jack processing bin", - "Andy Wingo ", -}; - -static GstElementDetails gst_jack_sink_details = { - "Jack Sink", - "Sink/Audio", - "Output to a Jack processing network", - "Andy Wingo ", -}; - -static GstElementDetails gst_jack_src_details = { - "Jack Src", - "Source/Audio", - "Input from a Jack processing network", - "Andy Wingo ", -}; - - -static GHashTable *port_name_counts = NULL; -static GstElementClass *parent_class = NULL; - -static void gst_jack_base_init (gpointer g_class); -static void gst_jack_src_base_init (gpointer g_class); -static void gst_jack_sink_base_init (gpointer g_class); -static void gst_jack_init (GstJack * this); -static void gst_jack_class_init (GstJackClass * klass); -static void gst_jack_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_jack_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static GstPadTemplate *gst_jack_src_request_pad_factory (); -static GstPadTemplate *gst_jack_sink_request_pad_factory (); -static GstPad *gst_jack_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static GstStateChangeReturn gst_jack_change_state (GstElement * element, - GstStateChange transition); -static GstPadLinkReturn gst_jack_link (GstPad * pad, const GstCaps * caps); - -static void gst_jack_loop (GstElement * element); - - -enum -{ - ARG_0, - ARG_PORT_NAME_PREFIX -}; - - -GType -gst_jack_get_type (void) -{ - static GType jack_type = 0; - - if (!jack_type) { - static const GTypeInfo jack_info = { - sizeof (GstJackClass), - gst_jack_base_init, - NULL, - NULL, - NULL, - NULL, - sizeof (GstJack), - 0, - NULL, - }; - - jack_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstJack", &jack_info, 0); - } - return jack_type; -} - -GType -gst_jack_sink_get_type (void) -{ - static GType jack_type = 0; - - if (!jack_type) { - static const GTypeInfo jack_info = { - sizeof (GstJackClass), - gst_jack_sink_base_init, - NULL, - (GClassInitFunc) gst_jack_class_init, - NULL, - NULL, - sizeof (GstJack), - 0, - (GInstanceInitFunc) gst_jack_init, - }; - - jack_type = - g_type_register_static (GST_TYPE_JACK, "GstJackSink", &jack_info, 0); - } - return jack_type; -} - -GType -gst_jack_src_get_type (void) -{ - static GType jack_type = 0; - - if (!jack_type) { - static const GTypeInfo jack_info = { - sizeof (GstJackClass), - gst_jack_src_base_init, - NULL, - (GClassInitFunc) gst_jack_class_init, - NULL, - NULL, - sizeof (GstJack), - 0, - (GInstanceInitFunc) gst_jack_init, - }; - - jack_type = - g_type_register_static (GST_TYPE_JACK, "GstJackSrc", &jack_info, 0); - } - return jack_type; -} - -static void -gst_jack_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_jack_bin_details); -} - -static void -gst_jack_src_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_jack_src_request_pad_factory ()); - gst_element_class_set_details (element_class, &gst_jack_src_details); -} - -static void -gst_jack_sink_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_add_pad_template (element_class, - gst_jack_sink_request_pad_factory ()); - gst_element_class_set_details (element_class, &gst_jack_sink_details); -} - -static void -gst_jack_class_init (GstJackClass * klass) -{ - GObjectClass *object_class; - GstElementClass *element_class; - GParamSpec *pspec; - gchar *prefix; - - object_class = (GObjectClass *) klass; - element_class = (GstElementClass *) klass; - if (parent_class == NULL) - parent_class = g_type_class_peek_parent (klass); - - object_class->get_property = gst_jack_get_property; - object_class->set_property = gst_jack_set_property; - - if (GST_IS_JACK_SINK_CLASS (klass)) - prefix = "gst-out-"; - else - prefix = "gst-in-"; - - pspec = g_param_spec_string ("port-name-prefix", "Port name prefix", - "String to prepend to jack port names", - prefix, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); - g_object_class_install_property (object_class, ARG_PORT_NAME_PREFIX, pspec); - - element_class->change_state = gst_jack_change_state; - - element_class->request_new_pad = gst_jack_request_new_pad; -} - -static void -gst_jack_init (GstJack * this) -{ - if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SRC) - this->direction = GST_PAD_SRC; - else if (G_OBJECT_TYPE (this) == GST_TYPE_JACK_SINK) - this->direction = GST_PAD_SINK; - else - g_assert_not_reached (); - - gst_element_set_loop_function (GST_ELEMENT (this), gst_jack_loop); -} - -static void -gst_jack_set_property (GObject * object, guint prop_id, const GValue * value, - GParamSpec * pspec) -{ - GstJack *this = (GstJack *) object; - - switch (prop_id) { - case ARG_PORT_NAME_PREFIX: - if (this->port_name_prefix) - g_free (this->port_name_prefix); - this->port_name_prefix = g_strdup (g_value_get_string (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - return; - } -} - -static void -gst_jack_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstJack *this = (GstJack *) object; - - switch (prop_id) { - case ARG_PORT_NAME_PREFIX: - g_value_set_string (value, this->port_name_prefix); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstPadTemplate * -gst_jack_src_request_pad_factory (void) -{ - static GstPadTemplate *template = NULL; - - if (!template) { - GstCaps *caps; - - caps = gst_caps_from_string (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); - template = gst_pad_template_new ("%s", GST_PAD_SRC, GST_PAD_REQUEST, caps); - } - - return template; -} - -static GstPadTemplate * -gst_jack_sink_request_pad_factory (void) -{ - static GstPadTemplate *template = NULL; - - if (!template) { - GstCaps *caps; - - caps = gst_caps_from_string (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); - template = gst_pad_template_new ("%s", GST_PAD_SINK, GST_PAD_REQUEST, caps); - } - - return template; -} - -static GstPad * -gst_jack_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name) -{ - GstJack *this; - gchar *newname; - GList *l, **pad_list; - GstJackPad *pad; - gint count; - - g_return_val_if_fail (GST_IS_JACK (element), NULL); - this = GST_JACK (element); - - if (!this->bin) - pad_list = &this->pads; - else if (this->direction == GST_PAD_SRC) - pad_list = &this->bin->src_pads; - else - pad_list = &this->bin->sink_pads; - - if (name) { - l = *pad_list; - while (l) { - if (strcmp (GST_JACK_PAD (l)->name, name) == 0) { - g_warning ("requested port name %s already in use.", name); - return NULL; - } - l = l->next; - } - newname = g_strdup (name); - } else { - if (this->direction == GST_PAD_SINK) - newname = g_strdup ("alsa_pcm:playback_1"); - else - newname = g_strdup ("alsa_pcm:capture_1"); - } - - pad = g_new0 (GstJackPad, 1); - - if (!port_name_counts) - port_name_counts = g_hash_table_new (g_str_hash, g_str_equal); - - count = - GPOINTER_TO_INT (g_hash_table_lookup (port_name_counts, - this->port_name_prefix)); - g_hash_table_insert (port_name_counts, g_strdup (this->port_name_prefix), - GINT_TO_POINTER (count + 1)); - - pad->name = g_strdup_printf ("%s%d", this->port_name_prefix, count); - - pad->peer_name = newname; - pad->pad = gst_pad_new_from_template (templ, newname); - gst_element_add_pad (GST_ELEMENT (this), pad->pad); - gst_pad_set_link_function (pad->pad, gst_jack_link); - - this->pads = g_list_append (this->pads, pad); - - g_print ("returning from request_new_pad, pad %s created, to connect to %s\n", - pad->name, pad->peer_name); - return pad->pad; -} - -static GstStateChangeReturn -gst_jack_change_state (GstElement * element, GstStateChange transition) -{ - GstJack *this; - GList *l = NULL, **pads; - GstJackPad *pad; - GstCaps *caps; - - g_return_val_if_fail (element != NULL, FALSE); - this = GST_JACK (element); - - switch (GST_STATE_PENDING (element)) { - case GST_STATE_NULL: - JACK_DEBUG ("%s: NULL", GST_OBJECT_NAME (GST_OBJECT (this))); - - break; - - case GST_STATE_READY: - JACK_DEBUG ("%s: READY", GST_OBJECT_NAME (GST_OBJECT (this))); - - if (!this->bin) { - if (!(this->bin = (GstJackBin *) gst_element_get_managing_bin (element)) - || !GST_IS_JACK_BIN (this->bin)) { - this->bin = NULL; - g_warning ("jack element %s needs to be contained in a jack bin.", - GST_OBJECT_NAME (element)); - return GST_STATE_CHANGE_FAILURE; - } - - /* fixme: verify that all names are unique */ - l = this->pads; - pads = - (this->direction == - GST_PAD_SRC) ? &this->bin->src_pads : &this->bin->sink_pads; - while (l) { - pad = GST_JACK_PAD (l); - JACK_DEBUG ("%s: appending pad %s:%s to list", GST_OBJECT_NAME (this), - pad->name, pad->peer_name); - *pads = g_list_append (*pads, pad); - l = g_list_next (l); - } - } - break; - - case GST_STATE_PAUSED: - JACK_DEBUG ("%s: PAUSED", GST_OBJECT_NAME (GST_OBJECT (this))); - - if (GST_STATE (element) == GST_STATE_READY) { - /* we're in READY->PAUSED */ - l = this->pads; - while (l) { - pad = GST_JACK_PAD (l); - caps = gst_caps_copy (gst_pad_get_negotiated_caps (pad->pad)); - gst_caps_set_simple (caps, - "rate", G_TYPE_INT, (int) this->bin->rate, - "buffer-frames", G_TYPE_INT, (gint) this->bin->nframes, NULL); - if (gst_pad_try_set_caps (pad->pad, caps) <= 0) - return GST_STATE_CHANGE_FAILURE; - l = g_list_next (l); - } - } - break; - case GST_STATE_PLAYING: - JACK_DEBUG ("%s: PLAYING", GST_OBJECT_NAME (GST_OBJECT (this))); - break; - } - - JACK_DEBUG ("%s: state change finished", GST_OBJECT_NAME (this)); - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - return GST_STATE_CHANGE_SUCCESS; -} - -static GstPadLinkReturn -gst_jack_link (GstPad * pad, const GstCaps * caps) -{ - GstJack *this; - gint rate, buffer_frames; - GstStructure *structure; - - this = GST_JACK (GST_OBJECT_PARENT (pad)); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "rate", &rate); - gst_structure_get_int (structure, "buffer-frames", &buffer_frames); - if (this->bin && (rate != this->bin->rate || - buffer_frames != this->bin->nframes)) - return GST_PAD_LINK_REFUSED; - - return GST_PAD_LINK_OK; -} - -static void -gst_jack_loop (GstElement * element) -{ - GstJack *this; - GList *pads; - gint len; - GstJackPad *pad; - GstBuffer *buffer; - - this = GST_JACK (element); - - len = this->bin->nframes * sizeof (sample_t); - - pads = this->pads; - while (pads) { - pad = GST_JACK_PAD (pads); - - if (this->direction == GST_PAD_SINK) { - buffer = GST_BUFFER (gst_pad_pull (pad->pad)); - - if (GST_IS_EVENT (buffer)) { - GstEvent *event = GST_EVENT (buffer); - - switch (GST_EVENT_TYPE (buffer)) { - case GST_EVENT_EOS: - gst_element_set_eos (element); - gst_event_unref (event); - return; - default: - gst_pad_event_default (pad->pad, event); - return; - } - } - - /* if the other plugins only give out buffer-frames or less (as - they should), if the length of the GstBuffer is different - from nframes then the buffer is short and we will get EOS - next */ - memcpy (pad->data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer)); - if (len != GST_BUFFER_SIZE (buffer)) - memset (pad->data + GST_BUFFER_SIZE (buffer), 0, - len - GST_BUFFER_SIZE (buffer)); - - gst_buffer_unref (buffer); - } else { - buffer = gst_buffer_new (); - gst_buffer_set_data (buffer, pad->data, len); - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTFREE); - - gst_pad_push (pad->pad, GST_DATA (buffer)); - } - pads = g_list_next (pads); - } -} +#include "gstjackaudiosink.h" static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_element_register (plugin, "jackbin", GST_RANK_NONE, - GST_TYPE_JACK_BIN)) - return FALSE; - - if (!gst_element_register (plugin, "jacksrc", GST_RANK_NONE, - GST_TYPE_JACK_SRC)) - return FALSE; - - if (!gst_element_register (plugin, "jacksink", GST_RANK_NONE, - GST_TYPE_JACK_SINK)) + if (!gst_element_register (plugin, "jackaudiosink", GST_RANK_PRIMARY, + GST_TYPE_JACK_AUDIO_SINK)) return FALSE; return TRUE; @@ -531,5 +36,6 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "jack", - "Jack Plugin Library", plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN) + "Jack elements", + plugin_init, + VERSION, "LGPL", "Gstreamer", "http://gstreamer.freedesktop.org") diff --git a/ext/jack/gstjack.h b/ext/jack/gstjack.h deleted file mode 100644 index 738168ca..00000000 --- a/ext/jack/gstjack.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- Mode: C; c-basic-offset: 4 -*- */ -/* - Copyright (C) 2002 Andy Wingo - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __GST_JACK_H__ -#define __GST_JACK_H__ - -#include -#include -#include - -//#define JACK_DEBUG(str, a...) g_message (str, ##a) -#define JACK_DEBUG(str, a...) - -#define GST_JACK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_JACK, GstJack) -#define GST_JACK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_JACK, GstJackClass) -#define GST_IS_JACK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_JACK) -#define GST_IS_JACK_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_JACK) -#define GST_TYPE_JACK gst_jack_get_type() - -#define GST_JACK_SINK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_JACK_SINK, GstJack) -#define GST_JACK_SINK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_JACK_SINK, GstJackClass) -#define GST_IS_JACK_SINK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_JACK_SINK) -#define GST_IS_JACK_SINK_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_JACK_SINK) -#define GST_TYPE_JACK_SINK gst_jack_sink_get_type() - -#define GST_JACK_SRC(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_JACK_SRC, GstJack) -#define GST_JACK_SRC_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_JACK_SRC, GstJackClass) -#define GST_IS_JACK_SRC(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_JACK_SRC) -#define GST_IS_JACK_SRC_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_JACK_SRC) -#define GST_TYPE_JACK_SRC gst_jack_src_get_type() - -#define GST_JACK_BIN(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, GST_TYPE_JACK_BIN, GstJackBin) -#define GST_JACK_BIN_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, GST_TYPE_JACK_BIN, GstJackClass) -#define GST_IS_JACK_BIN(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, GST_TYPE_JACK_BIN) -#define GST_IS_JACK_BIN_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass, GST_TYPE_JACK_BIN) -#define GST_TYPE_JACK_BIN gst_jack_bin_get_type() - -#define GST_JACK_PAD(l) ((GstJackPad*)l->data) /* l is a GList */ - - -typedef struct _GstJack GstJack; -typedef struct _GstJackClass GstJackClass; -typedef struct _GstJackBin GstJackBin; -typedef struct _GstJackBinClass GstJackBinClass; -typedef GstJack GstJackSink; -typedef GstJackClass GstJackSinkClass; -typedef GstJack GstJackSrc; -typedef GstJackClass GstJackSrcClass; - - -enum { - GST_JACK_OPEN = (GST_BIN_FLAG_LAST << 0), - GST_JACK_ACTIVE = (GST_BIN_FLAG_LAST << 1), - GST_JACK_FLAG_LAST = (GST_BIN_FLAG_LAST << 3) -}; - - -typedef jack_default_audio_sample_t sample_t; - -typedef struct { - GstPad *pad; - void *data; - const gchar *name; - const gchar *peer_name; - jack_port_t *port; -} GstJackPad; - -struct _GstJack { - GstElement element; - - /* list of GstJackPads */ - GList *pads; - - /* for convenience */ - GstPadDirection direction; - - gchar *port_name_prefix; - - GstJackBin *bin; -}; - -struct _GstJackClass { - GstElementClass parent_class; -}; - -struct _GstJackBin { - GstBin bin; - - jack_client_t *client; - gint default_new_port_number; - - /* lists of GstJackPads */ - GList *sink_pads; - GList *src_pads; - - gchar *client_name; - - guint rate; - jack_nframes_t nframes; -}; - -struct _GstJackBinClass { - GstBinClass parent_class; -}; - - -GType gst_jack_get_type (void); -GType gst_jack_bin_get_type (void); -GType gst_jack_sink_get_type (void); -GType gst_jack_src_get_type (void); - - -#endif /* __GST_JACK_H__ */ diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c new file mode 100644 index 00000000..5a0cadf8 --- /dev/null +++ b/ext/jack/gstjackaudiosink.c @@ -0,0 +1,846 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjackaudiosink.c: jack audio sink implementation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:gstjacksink + * @short_description: JACK audio sink + * @see_also: #GstBaseAudioSink, #GstRingBuffer + * + * A Sink that outputs data to Jack ports. + * + * It will create N Jack ports named out_ where is starting from 1. + * Each port corresponds to a gstreamer channel. + * + * The samplerate as exposed on the caps is always the same as the samplerate of + * the jack server. + * + * When the ::connect property is set to auto, this element will try to connect + * each output port to a random physical jack input pin. In this mode, the sink + * will expose the number of physical channels on its pad caps. + * + * When the ::connect property is set to none, the element will accept any + * number of input channels and will create (but not connect) an output port for + * each channel. + * + * The element will generate an error when the Jack server is shut down when it + * was PAUSED or PLAYING. This element does not support dynamic rate and buffer + * size changes at runtime. + * + * Last reviewed on 2006-11-30 (0.10.4) + */ +#include + +#include "gstjackaudiosink.h" + +GST_DEBUG_CATEGORY_STATIC (gst_jack_audio_sink_debug); +#define GST_CAT_DEFAULT gst_jack_audio_sink_debug + +typedef jack_default_audio_sample_t sample_t; + +#define GST_TYPE_JACK_RING_BUFFER \ + (gst_jack_ring_buffer_get_type()) +#define GST_JACK_RING_BUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JACK_RING_BUFFER,GstJackRingBuffer)) +#define GST_JACK_RING_BUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JACK_RING_BUFFER,GstJackRingBufferClass)) +#define GST_JACK_RING_BUFFER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_JACK_RING_BUFFER, GstJackRingBufferClass)) +#define GST_JACK_RING_BUFFER_CAST(obj) \ + ((GstJackRingBuffer *)obj) +#define GST_IS_JACK_RING_BUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JACK_RING_BUFFER)) +#define GST_IS_JACK_RING_BUFFER_CLASS(klass)\ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JACK_RING_BUFFER)) + +typedef struct _GstJackRingBuffer GstJackRingBuffer; +typedef struct _GstJackRingBufferClass GstJackRingBufferClass; + +struct _GstJackRingBuffer +{ + GstRingBuffer object; + + gint sample_rate; + gint buffer_size; + gint channels; + + jack_port_t **outport; +}; + +struct _GstJackRingBufferClass +{ + GstRingBufferClass parent_class; +}; + +static void gst_jack_ring_buffer_class_init (GstJackRingBufferClass * klass); +static void gst_jack_ring_buffer_init (GstJackRingBuffer * ringbuffer, + GstJackRingBufferClass * klass); +static void gst_jack_ring_buffer_dispose (GObject * object); +static void gst_jack_ring_buffer_finalize (GObject * object); + +static GstRingBufferClass *ring_parent_class = NULL; + +static gboolean gst_jack_ring_buffer_open_device (GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_close_device (GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_acquire (GstRingBuffer * buf, + GstRingBufferSpec * spec); +static gboolean gst_jack_ring_buffer_release (GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_start (GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_pause (GstRingBuffer * buf); +static gboolean gst_jack_ring_buffer_stop (GstRingBuffer * buf); +static guint gst_jack_ring_buffer_delay (GstRingBuffer * buf); + +/* ringbuffer abstract base class */ +static GType +gst_jack_ring_buffer_get_type (void) +{ + static GType ringbuffer_type = 0; + + if (!ringbuffer_type) { + static const GTypeInfo ringbuffer_info = { + sizeof (GstJackRingBufferClass), + NULL, + NULL, + (GClassInitFunc) gst_jack_ring_buffer_class_init, + NULL, + NULL, + sizeof (GstJackRingBuffer), + 0, + (GInstanceInitFunc) gst_jack_ring_buffer_init, + NULL + }; + + ringbuffer_type = + g_type_register_static (GST_TYPE_RING_BUFFER, + "GstJackAudioSinkRingBuffer", &ringbuffer_info, 0); + } + return ringbuffer_type; +} + +static void +gst_jack_ring_buffer_class_init (GstJackRingBufferClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + GstRingBufferClass *gstringbuffer_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + gstringbuffer_class = (GstRingBufferClass *) klass; + + ring_parent_class = g_type_class_peek_parent (klass); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_finalize); + + gstringbuffer_class->open_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_open_device); + gstringbuffer_class->close_device = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_close_device); + gstringbuffer_class->acquire = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_acquire); + gstringbuffer_class->release = + GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_release); + gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->pause = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_pause); + gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_start); + gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_stop); + + gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_jack_ring_buffer_delay); +} + +/* this is the callback of jack. This should RT-safe. + */ +static int +jack_process_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstRingBuffer *buf; + GstJackRingBuffer *abuf; + gint readseg, len; + guint8 *readptr; + gint i, j, flen, channels; + sample_t **buffers, *data; + + buf = GST_RING_BUFFER_CAST (arg); + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + channels = buf->spec.channels; + + /* alloc pointers to samples */ + buffers = g_alloca (sizeof (sample_t *) * channels); + + /* get target buffers */ + for (i = 0; i < channels; i++) { + buffers[i] = (sample_t *) jack_port_get_buffer (abuf->outport[i], nframes); + } + + if (gst_ring_buffer_prepare_read (buf, &readseg, &readptr, &len)) { + flen = len / channels; + + if (nframes * sizeof (sample_t) != flen) + goto wrong_size; + + /* copy samples */ + GST_DEBUG ("copy %d frames: %p, %d bytes, %d channels", nframes, readptr, + flen, channels); + data = (sample_t *) readptr; + + /* copy and interleave into target buffers */ + for (i = 0; i < nframes; i++) { + for (j = 0; j < channels; j++) { + buffers[j][i] = *data++; + } + } + + /* clear written samples */ + gst_ring_buffer_clear (buf, readseg); + + /* we wrote one segment */ + gst_ring_buffer_advance (buf, 1); + } else { + /* write silence to all buffers */ + for (i = 0; i < channels; i++) { + memset (buffers[i], 0, nframes * sizeof (sample_t)); + } + } + return 0; + + /* ERRORS */ +wrong_size: + { + GST_ERROR_OBJECT (sink, "nbytes (%d) != flen (%d)", + nframes * sizeof (sample_t), flen); + return 1; + } +} + +/* we error out */ +static int +jack_sample_rate_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + if (abuf->sample_rate != -1 && abuf->sample_rate != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + (NULL), ("Jack changed the sample rate, which is not supported")); + return 1; + } +} + +/* we error out */ +static int +jack_buffer_size_cb (jack_nframes_t nframes, void *arg) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + + abuf = GST_JACK_RING_BUFFER_CAST (arg); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + if (abuf->buffer_size != -1 && abuf->buffer_size != nframes) + goto not_supported; + + return 0; + + /* ERRORS */ +not_supported: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + (NULL), ("Jack changed the buffer size, which is not supported")); + return 1; + } +} + +static void +jack_shutdown_cb (void *arg) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (arg)); + + GST_DEBUG_OBJECT (sink, "shutdown"); + + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + (NULL), ("Jack server shutdown")); +} + +static void +gst_jack_ring_buffer_init (GstJackRingBuffer * buf, + GstJackRingBufferClass * g_class) +{ + buf->channels = -1; + buf->buffer_size = -1; + buf->sample_rate = -1; +} + +static void +gst_jack_ring_buffer_dispose (GObject * object) +{ + G_OBJECT_CLASS (ring_parent_class)->dispose (object); +} + +static void +gst_jack_ring_buffer_finalize (GObject * object) +{ + GstJackRingBuffer *ringbuffer; + + ringbuffer = GST_JACK_RING_BUFFER_CAST (object); + + G_OBJECT_CLASS (ring_parent_class)->finalize (object); +} + +/* the _open_device method should make a connection with the server + */ +static gboolean +gst_jack_ring_buffer_open_device (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + jack_options_t options; + jack_status_t status = 0; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "open"); + + /* never start a server */ + options = JackNoStartServer; + /* if we have a servername, use it */ + if (sink->server != NULL) + options |= JackServerName; + /* open the client */ + sink->client = jack_client_open ("GStreamer", options, &status, sink->server); + if (sink->client == NULL) + goto could_not_open; + + /* set our callbacks */ + jack_set_process_callback (sink->client, jack_process_cb, buf); + /* these callbacks cause us to error */ + jack_set_buffer_size_callback (sink->client, jack_buffer_size_cb, buf); + jack_set_sample_rate_callback (sink->client, jack_sample_rate_cb, buf); + jack_on_shutdown (sink->client, jack_shutdown_cb, buf); + + GST_DEBUG_OBJECT (sink, "opened"); + + return TRUE; + + /* ERRORS */ +could_not_open: + { + if (status & JackServerFailed) { + GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, + (NULL), ("Cannot connect to the Jack server (status %d)", status)); + } else { + GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, + (NULL), ("Jack client open error (status %d)", status)); + } + return FALSE; + } +} + +/* close the connection with the server + */ +static gboolean +gst_jack_ring_buffer_close_device (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + gint res; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "close"); + + if ((res = jack_client_close (sink->client))) { + /* just a warning, we assume the client is gone. */ + GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, + (NULL), ("Jack client close error (%d)", res)); + } + sink->client = NULL; + + return TRUE; +} + +/* allocate a buffer and setup resources to process the audio samples of + * the format as specified in @spec. + * + * We allocate N jack ports for each channel. If we are asked to automatically + * make a connection with physical ports, we connect as many ports as there are + * physical ports, leaving leftover ports unconnected. + * + * It is assumed that samplerate and number of channels are acceptable since our + * getcaps method will always provide correct values. If unacceptable caps are + * received for some reason, we fail here. + */ +static gboolean +gst_jack_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + const char **ports; + gint sample_rate, buffer_size; + gint i, channels, res; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + abuf = GST_JACK_RING_BUFFER_CAST (buf); + + GST_DEBUG_OBJECT (sink, "acquire"); + + /* sample rate must be that of the server */ + sample_rate = jack_get_sample_rate (sink->client); + if (sample_rate != spec->rate) + goto wrong_samplerate; + + channels = spec->channels; + + /* alloc enough output ports */ + abuf->outport = g_new (jack_port_t *, channels); + + /* create an output port for each channel */ + for (i = 0; i < channels; i++) { + gchar *name; + + /* port names start from 1 */ + name = g_strdup_printf ("out_%d", i + 1); + abuf->outport[i] = jack_port_register (sink->client, name, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if (abuf->outport[i] == NULL) + goto out_of_ports; + + g_free (name); + } + + buffer_size = jack_get_buffer_size (sink->client); + + /* the segment size in bytes, this is large enough to hold a buffer of 32bit floats + * for all channels */ + spec->segsize = buffer_size * sizeof (gfloat) * channels; + spec->latency_time = gst_util_uint64_scale (spec->segsize, + (GST_SECOND / GST_USECOND), spec->rate * spec->bytes_per_sample); + /* segtotal based on buffer-time latency */ + spec->segtotal = spec->buffer_time / spec->latency_time; + + GST_DEBUG_OBJECT (sink, "segsize %d, segtotal %d", spec->segsize, + spec->segtotal); + + /* allocate the ringbuffer memory now */ + buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize); + memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data)); + + if ((res = jack_activate (sink->client))) + goto could_not_activate; + + /* if we need to automatically connect the ports, do so now. We must do this + * after activating the client. */ + if (sink->connect == GST_JACK_CONNECT_AUTO) { + /* find all the physical input ports. A physical input port is a port + * associated with a hardware device. Someone needs connect to a physical + * port in order to hear something. */ + ports = jack_get_ports (sink->client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); + if (ports == NULL) { + /* no ports? fine then we don't do anything except for posting a warning + * message. */ + GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL), + ("No physical input ports found, leaving ports unconnected")); + goto done; + } + + for (i = 0; i < channels; i++) { + /* stop when all input ports are exhausted */ + if (ports[i] == NULL) { + /* post a warning that we could not connect all ports */ + GST_ELEMENT_WARNING (sink, RESOURCE, NOT_FOUND, (NULL), + ("No more physical ports, leaving some ports unconnected")); + break; + } + /* connect the port to a physical port */ + if ((res = jack_connect (sink->client, jack_port_name (abuf->outport[i]), + ports[i]))) + goto cannot_connect; + } + free (ports); + } +done: + + abuf->sample_rate = sample_rate; + abuf->buffer_size = buffer_size; + abuf->channels = spec->channels; + + return TRUE; + + /* ERRORS */ +wrong_samplerate: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Wrong samplerate, server is running at %d and we received %d", + sample_rate, spec->rate)); + return FALSE; + } +out_of_ports: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Cannot allocate more Jack ports")); + return FALSE; + } +could_not_activate: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not activate client (%d)", res)); + return FALSE; + } +cannot_connect: + { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL), + ("Could not connect output ports to physical ports (%d)", res)); + free (ports); + return FALSE; + } +} + +/* function is called with LOCK */ +static gboolean +gst_jack_ring_buffer_release (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + GstJackRingBuffer *abuf; + gint i, res; + + abuf = GST_JACK_RING_BUFFER_CAST (buf); + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "release"); + + if ((res = jack_deactivate (sink->client))) { + /* we only warn, this means the server is probably shut down and the client + * is gone anyway. */ + GST_ELEMENT_WARNING (sink, RESOURCE, CLOSE, (NULL), + ("Could not deactivate Jack client (%d)", res)); + } + + /* remove all ports */ + for (i = 0; i < abuf->channels; i++) { + GST_LOG_OBJECT (sink, "unregister port %d", i); + if ((res = jack_port_unregister (sink->client, abuf->outport[i]))) { + GST_DEBUG_OBJECT (sink, "unregister of port failed (%d)", res); + } + abuf->outport[i] = NULL; + } + g_free (abuf->outport); + abuf->outport = NULL; + abuf->channels = -1; + abuf->buffer_size = -1; + abuf->sample_rate = -1; + + /* free the buffer */ + gst_buffer_unref (buf->data); + buf->data = NULL; + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_start (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "start"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_pause (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "pause"); + + return TRUE; +} + +static gboolean +gst_jack_ring_buffer_stop (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "stop"); + + return TRUE; +} + +static guint +gst_jack_ring_buffer_delay (GstRingBuffer * buf) +{ + GstJackAudioSink *sink; + guint res = 0; + + sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); + + GST_DEBUG_OBJECT (sink, "delay %u", res); + + return res; +} + +/* elementfactory information */ +static const GstElementDetails gst_jack_audio_sink_details = +GST_ELEMENT_DETAILS ("Audio Sink (Jack)", + "Sink/Audio", + "Output to Jack", + "Wim Taymans "); + +static GstStaticPadTemplate jackaudiosink_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, " + "width = (int) 32, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +/* AudioSink signals and args */ +enum +{ + /* FILL ME */ + SIGNAL_LAST +}; + +#define DEFAULT_PROP_CONNECT GST_JACK_CONNECT_AUTO +#define DEFAULT_PROP_SERVER NULL + +enum +{ + PROP_0, + PROP_CONNECT, + PROP_SERVER, + PROP_LAST +}; + +#define GST_TYPE_JACK_CONNECT (gst_jack_connect_get_type()) +static GType +gst_jack_connect_get_type (void) +{ + static GType jack_connect_type = 0; + static const GEnumValue jack_connect[] = { + {GST_JACK_CONNECT_NONE, + "Don't automatically connect ports to physical ports", "none"}, + {GST_JACK_CONNECT_AUTO, + "Automatically connect ports to physical ports", "auto"}, + {0, NULL, NULL}, + }; + + if (!jack_connect_type) { + jack_connect_type = g_enum_register_static ("GstJackConnect", jack_connect); + } + return jack_connect_type; +} + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_jack_audio_sink_debug, "jacksink", 0, "jacksink element"); + +GST_BOILERPLATE_FULL (GstJackAudioSink, gst_jack_audio_sink, GstBaseAudioSink, + GST_TYPE_BASE_AUDIO_SINK, _do_init); + +static void gst_jack_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_jack_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstCaps *gst_jack_audio_sink_getcaps (GstBaseSink * bsink); +static GstRingBuffer *gst_jack_audio_sink_create_ringbuffer (GstBaseAudioSink * + sink); + +static void +gst_jack_audio_sink_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_jack_audio_sink_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&jackaudiosink_sink_factory)); +} + +static void +gst_jack_audio_sink_class_init (GstJackAudioSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstBaseAudioSinkClass *gstbaseaudiosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; + + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_jack_audio_sink_get_property); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_jack_audio_sink_set_property); + + g_object_class_install_property (gobject_class, PROP_CONNECT, + g_param_spec_enum ("connect", "Connect", + "Specify how the output ports will be connected", + GST_TYPE_JACK_CONNECT, DEFAULT_PROP_CONNECT, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_SERVER, + g_param_spec_string ("server", "Server", + "The Jack server to connect to (NULL = default)", + DEFAULT_PROP_SERVER, G_PARAM_READWRITE)); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_jack_audio_sink_getcaps); + + gstbaseaudiosink_class->create_ringbuffer = + GST_DEBUG_FUNCPTR (gst_jack_audio_sink_create_ringbuffer); +} + +static void +gst_jack_audio_sink_init (GstJackAudioSink * sink, + GstJackAudioSinkClass * g_class) +{ + sink->connect = DEFAULT_PROP_CONNECT; + sink->server = g_strdup (DEFAULT_PROP_SERVER); +} + +static void +gst_jack_audio_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_CONNECT: + sink->connect = g_value_get_enum (value); + break; + case PROP_SERVER: + g_free (sink->server); + sink->server = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_jack_audio_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstJackAudioSink *sink; + + sink = GST_JACK_AUDIO_SINK (object); + + switch (prop_id) { + case PROP_CONNECT: + g_value_set_enum (value, sink->connect); + break; + case PROP_SERVER: + g_value_set_string (value, sink->server); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_jack_audio_sink_getcaps (GstBaseSink * bsink) +{ + GstJackAudioSink *sink = GST_JACK_AUDIO_SINK (bsink); + const char **ports; + gint min, max; + gint rate; + + if (sink->client == NULL) + goto no_client; + + if (sink->connect == GST_JACK_CONNECT_AUTO) { + /* get a port count, this is the number of channels we can automatically + * connect. */ + ports = jack_get_ports (sink->client, NULL, NULL, + JackPortIsPhysical | JackPortIsInput); + max = 0; + if (ports != NULL) { + for (; ports[max]; max++); + free (ports); + } else + max = 0; + } else { + /* we allow any number of pads, somoething else is going to connect the + * pads. */ + max = G_MAXINT; + } + min = MIN (1, max); + + rate = jack_get_sample_rate (sink->client); + + GST_DEBUG_OBJECT (sink, "got %d-%d ports, samplerate: %d", min, max, rate); + + if (!sink->caps) { + sink->caps = gst_caps_new_simple ("audio/x-raw-float", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 32, + "rate", G_TYPE_INT, rate, + "channels", GST_TYPE_INT_RANGE, min, max, NULL); + } + GST_INFO_OBJECT (sink, "returning caps %" GST_PTR_FORMAT, sink->caps); + + return gst_caps_ref (sink->caps); + + /* ERRORS */ +no_client: + { + GST_DEBUG_OBJECT (sink, "device not open, using template caps"); + /* base class will get template caps for us when we return NULL */ + return NULL; + } +} + +static GstRingBuffer * +gst_jack_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) +{ + GstRingBuffer *buffer; + + buffer = g_object_new (GST_TYPE_JACK_RING_BUFFER, NULL); + GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); + + return buffer; +} diff --git a/ext/jack/gstjackaudiosink.h b/ext/jack/gstjackaudiosink.h new file mode 100644 index 00000000..218e9de0 --- /dev/null +++ b/ext/jack/gstjackaudiosink.h @@ -0,0 +1,80 @@ +/* GStreamer + * Copyright (C) 2006 Wim Taymans + * + * gstjacksink.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_JACK_AUDIO_SINK_H__ +#define __GST_JACK_AUDIO_SINK_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_JACK_AUDIO_SINK (gst_jack_audio_sink_get_type()) +#define GST_JACK_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSink)) +#define GST_JACK_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSinkClass)) +#define GST_JACK_AUDIO_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_JACK_AUDIO_SINK,GstJackAudioSinkClass)) +#define GST_IS_JACK_AUDIO_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_JACK_AUDIO_SINK)) +#define GST_IS_JACK_AUDIO_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_JACK_AUDIO_SINK)) + +typedef struct _GstJackAudioSink GstJackAudioSink; +typedef struct _GstJackAudioSinkClass GstJackAudioSinkClass; + +typedef enum { + GST_JACK_CONNECT_NONE, + GST_JACK_CONNECT_AUTO +} GstJackConnect; + +/** + * GstJackAudioSink: + * + * Opaque #GstJackAudioSink. + */ +struct _GstJackAudioSink { + GstBaseAudioSink element; + + /* cached caps */ + GstCaps *caps; + + /* properties */ + GstJackConnect connect; + gchar *server; + + /* our client */ + jack_client_t *client; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstJackAudioSinkClass { + GstBaseAudioSinkClass parent_class; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_jack_audio_sink_get_type (void); + +G_END_DECLS + +#endif /* __GST_JACK_AUDIO_SINK_H__ */ -- cgit v1.2.1