From f8f641889aafafa817c9c03def299945cd23c018 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 May 2006 22:08:33 +0000 Subject: update for gstreamer 0.10 and polypaudio 0.8.1 git-svn-id: file:///home/lennart/svn/public/gst-pulse/trunk@14 bb39ca4e-bce3-0310-b5d4-eea78a553289 --- Makefile.am | 5 +- bootstrap.sh | 40 ++- configure.ac | 14 +- src/Makefile.am | 11 +- src/plugin.c | 46 ++- src/polypsink.c | 850 +++++++++++++++++++++++++++----------------------------- src/polypsink.h | 54 ++-- 7 files changed, 525 insertions(+), 495 deletions(-) diff --git a/Makefile.am b/Makefile.am index 6fafbc8..11e53bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,7 +35,4 @@ homepage: all dist cp doc/README.html doc/style.css $$HOME/homepage/private/projects/gst-polyp cp $$HOME/homepage/private/projects/gst-polyp/README.html $$HOME/homepage/private/projects/gst-polyp/index.html -distcleancheck: - @: - -.PHONY: homepage distcleancheck +.PHONY: homepage diff --git a/bootstrap.sh b/bootstrap.sh index b048d59..52ba247 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -17,31 +17,47 @@ # along with gst-polyp; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +VERSION=1.9 + run_versioned() { local P - type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1" + local V + + V=$(echo "$2" | sed -e 's,\.,,g') + + if [ -e "`which $1$V`" ] ; then + P="$1$V" + else + if [ -e "`which $1-$2`" ] ; then + P="$1-$2" + else + P="$1" + fi + fi shift 2 "$P" "$@" } +set -ex + if [ "x$1" = "xam" ] ; then - set -ex - run_versioned automake 1.7 -a -c --foreign + run_versioned automake "$VERSION" -a -c --foreign ./config.status else - set -ex - rm -rf autom4te.cache rm -f config.cache - run_versioned aclocal 1.7 - libtoolize -c --force - autoheader - run_versioned automake 1.7 -a -c --foreign - autoconf -Wall + test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize - CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@" + "$LIBTOOLIZE" -c --force + run_versioned aclocal "$VERSION" + run_versioned autoconf 2.59 -Wall + run_versioned autoheader 2.59 + run_versioned automake "$VERSION" -a -c --foreign - make clean + if test "x$NOCONFIGURE" = "x"; then + CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@" + make clean + fi fi diff --git a/configure.ac b/configure.ac index e3eff6f..3d8fecb 100644 --- a/configure.ac +++ b/configure.ac @@ -20,10 +20,10 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. AC_PREREQ(2.57) -AC_INIT([gst-polyp],[0.1.1],[mzkzzfcbylc (at) 0pointer (dot) de]) +AC_INIT([gst-polyp],[0.8.1],[mzkzzfcbylc (at) 0pointer (dot) de]) AC_CONFIG_SRCDIR([src/plugin.c]) AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE([foreign -Wall]) +AM_INIT_AUTOMAKE([foreign 1.9 -Wall]) AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/gst-polyp/]) @@ -43,20 +43,20 @@ AC_C_CONST AC_FUNC_MALLOC AC_TYPE_SIZE_T -ACX_PTHREAD +PKG_PROG_PKG_CONFIG if test -d ../polypaudio ; then - POLYP_CFLAGS='-I$(top_srcdir)/../polypaudio' - POLYP_LIBS='-L$(top_srcdir)/../polypaudio/polyp -lpolyp-0.7 -lpolyp-error-0.7 -lpolyp-mainloop-0.7' + POLYP_CFLAGS='-I$(top_srcdir)/../polypaudio/src' + POLYP_LIBS='-L$(top_srcdir)/../polypaudio/src/.libs -lpolyp-0.8.1' echo "*** Found polypaudio in ../polypaudio, using that version ***" else - PKG_CHECK_MODULES(POLYP, [ polyplib >= 0.7 ]) + PKG_CHECK_MODULES(POLYP, [ polyplib >= 0.8.1 ]) fi AC_SUBST(POLYP_LIBS) AC_SUBST(POLYP_CFLAGS) -PKG_CHECK_MODULES(GST, [ gstreamer-0.8 >= 0.8.4 ]) +PKG_CHECK_MODULES(GST, [ gstreamer-0.10 >= 0.10 gstreamer-plugins-base-0.10 >= 0.10 ]) AC_SUBST(GST_LIBS) AC_SUBST(GST_CFLAGS) diff --git a/src/Makefile.am b/src/Makefile.am index 8423f27..26fc8b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,14 +23,9 @@ pkglib_LTLIBRARIES = libgstpolyp.la libgstpolyp_la_SOURCES = plugin.c polypsink.c polypsink.h libgstpolyp_la_CFLAGS = $(GST_CFLAGS) $(POLYP_CFLAGS) -libgstpolyp_la_LIBADD = $(POLYP_LIBS) $(GST_LIBS) - -install-exec-hook: reg - -reg: - gst-register-0.8 --gst-plugin-path=$(pkglibdir) +libgstpolyp_la_LIBADD = $(POLYP_LIBS) $(GST_LIBS) -lgstaudio-0.10 inspect: - gst-inspect-0.8 polypsink + gst-inspect polypsink -.PHONY: reg inspect +.PHONY: inspect diff --git a/src/plugin.c b/src/plugin.c index 900fd17..9f99aea 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,3 +1,24 @@ +/* $Id$ */ + +/*** + This file is part of gst-polyp. + + gst-polyp is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + gst-polyp 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with gst-polyp; 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 @@ -6,20 +27,23 @@ GST_DEBUG_CATEGORY(polyp_debug); -static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_library_load("gstaudio")) - return FALSE; +static gboolean plugin_init(GstPlugin* plugin) { - if (!(gst_polypsink_factory_init (plugin))) + if (!gst_element_register(plugin, "polypsink", GST_RANK_NONE, GST_TYPE_POLYPSINK)) return FALSE; - + GST_DEBUG_CATEGORY_INIT(polyp_debug, "polyp", 0, "Polypaudio elements"); return TRUE; } -GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, - "polypsink", "Polypaudio Element Plugins", - plugin_init, - VERSION, - "LGPL", - "polypaudio", "http://0pointer.de/lennart/projects/gst-polyp/") +GST_PLUGIN_DEFINE( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "polypsink", + "Polypaudio Element Plugins", + plugin_init, + VERSION, + "LGPL", + "polypaudio", + "http://0pointer.de/lennart/projects/gst-polyp/" +) diff --git a/src/polypsink.c b/src/polypsink.c index 835b63c..96c0c9f 100644 --- a/src/polypsink.c +++ b/src/polypsink.c @@ -1,579 +1,559 @@ -/* - * This sink plugin works, but has some room for improvements: - * - * - Export polypaudio's stream clock through gstreamer's API - * - Add support for querying latency information - * - Add a source for polypaudio - * - * Lennart Poettering, 2004 - */ +/* $Id$ */ + +/*** + This file is part of gst-polyp. + + gst-polyp is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + gst-polyp 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with gst-polyp; 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 -#include -#include - #include "polypsink.h" +GST_DEBUG_CATEGORY_EXTERN(polyp_debug); +#define GST_CAT_DEFAULT polyp_debug + enum { - ARG_0, - ARG_SERVER, - ARG_SINK, + ARG_0, + ARG_SERVER, + ARG_SINK, }; -static GstElementClass *parent_class = NULL; +static GstAudioSinkClass *parent_class = NULL; + +static void gst_polypsink_destroy_stream(GstPolypSink *polypsink); +static void gst_polypsink_destroy_context(GstPolypSink *polypsink); -static void create_stream(GstPolypSink *polypsink); -static void destroy_stream(GstPolypSink *polypsink); +static void gst_polypsink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_polypsink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gst_polypsink_finalize(GObject *object); -static void create_context(GstPolypSink *polypsink); -static void destroy_context(GstPolypSink *polypsink); +static gboolean gst_polypsink_open(GstAudioSink *asink); +static gboolean gst_polypsink_close(GstAudioSink *asink); + +static gboolean gst_polypsink_prepare(GstAudioSink *asink, GstRingBufferSpec *spec); +static gboolean gst_polypsink_unprepare(GstAudioSink *asink); + +static guint gst_polypsink_write(GstAudioSink *asink, gpointer data, guint length); +static guint gst_polypsink_delay(GstAudioSink *asink); +static void gst_polypsink_reset(GstAudioSink *asink); + +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) +# define ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" +#else +# define ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" +#endif static void gst_polypsink_base_init(gpointer g_class) { static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE( - "sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS( - "audio/x-raw-int, " - "endianness = (int) { LITTLE_ENDIAN , BIG_ENDIAN }, " - "signed = (boolean) TRUE, " - "width = (int) 16, " - "depth = (int) 16, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 16 ];" - - "audio/x-raw-float, " - "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " - "width = (int) 32, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 16 ];" - - "audio/x-raw-int, " - "signed = (boolean) FALSE, " - "width = (int) 8, " - "depth = (int) 8, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, 16 ]") - ); - - static const GstElementDetails details = { - "Polypaudio Audio Sink", - "Sink/Audio", - "Plays audio to a Polypaudio server", - "Lennart Poettering", - }; + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS( + "audio/x-raw-int, " + "endianness = (int) { " ENDIANNESS " }, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 16 ];" + + "audio/x-raw-int, " + "signed = (boolean) FALSE, " + "width = (int) 8, " + "depth = (int) 8, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 16 ];" + + "audio/x-raw-float, " + "endianness = (int) { " ENDIANNESS " }, " + "width = (int) 32, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 16 ];" + + "audio/x-alaw, " + "rate = (int) [ 1, MAX], " + "channels = (int) [ 1, 16 ];" + + "audio/x-mulaw, " + "rate = (int) [ 1, MAX], " + "channels = (int) [ 1, 16 ]" + ) + ); + static const GstElementDetails details = + GST_ELEMENT_DETAILS( + "Polypaudio audio sink", + "Sink/Audio", + "Plays audio to a Polypaudio server", + "Lennart Poettering"); + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&pad_template)); + gst_element_class_set_details(element_class, &details); + gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&pad_template)); } -static void gst_polypsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GstPolypSink *polypsink; - g_return_if_fail(GST_IS_POLYPSINK(object)); - polypsink = GST_POLYPSINK(object); +static void gst_polypsink_class_init( + gpointer g_class, + gpointer class_data) { - switch (prop_id) { - case ARG_SERVER: - g_free(polypsink->server); - polypsink->server = g_strdup(g_value_get_string(value)); - break; - - case ARG_SINK: - g_free(polypsink->sink); - polypsink->sink = g_strdup(g_value_get_string(value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); +// GstElementClass *gstelement_class = GST_ELEMENT_CLASS(g_class); + GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS(g_class); + parent_class = g_type_class_peek_parent(g_class); + + gobject_class->set_property = gst_polypsink_set_property; + gobject_class->get_property = gst_polypsink_get_property; + gobject_class->finalize = gst_polypsink_finalize; + + gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_polypsink_open); + gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_polypsink_close); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_polypsink_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_polypsink_unprepare); + gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_polypsink_write); + gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_polypsink_delay); + gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_polypsink_reset); + + /* Overwrite GObject fields */ + g_object_class_install_property( + gobject_class, + ARG_SERVER, + g_param_spec_string("server", "Server", "The Polypaudio server to connect to", NULL, G_PARAM_READWRITE)); + g_object_class_install_property( + gobject_class, + ARG_SINK, + g_param_spec_string("sink", "Sink", "The Polypaudio sink device to connect to", NULL, G_PARAM_READWRITE)); } -static void gst_polypsink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec * pspec) { - GstPolypSink *polypsink; - g_return_if_fail(GST_IS_POLYPSINK(object)); - polypsink = GST_POLYPSINK(object); +static void gst_polypsink_init( + GTypeInstance * instance, + gpointer g_class) { - switch (prop_id) { - case ARG_SERVER: - g_value_set_string(value, polypsink->server); - break; - - case ARG_SINK: - g_value_set_string(value, polypsink->sink); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} + GstPolypSink *polypsink = GST_POLYPSINK(instance); + int e; -static GstElementStateReturn gst_polypsink_change_state(GstElement * element) { - GstPolypSink *polypsink; + polypsink->server = polypsink->device = NULL; - polypsink = GST_POLYPSINK (element); + polypsink->context = NULL; + polypsink->stream = NULL; - switch(GST_STATE_TRANSITION(element)) { - - case GST_STATE_NULL_TO_READY: - create_context(polypsink); - break; - - case GST_STATE_READY_TO_NULL: - destroy_context(polypsink); - break; + polypsink->mainloop = pa_threaded_mainloop_new(); + g_assert(polypsink->mainloop); - case GST_STATE_READY_TO_PAUSED: + e = pa_threaded_mainloop_start(polypsink->mainloop); + g_assert(e == 0); +} - create_stream(polypsink); +static void gst_polypsink_destroy_stream(GstPolypSink* polypsink) { + if (polypsink->stream) { + pa_stream_disconnect(polypsink->stream); + pa_stream_unref(polypsink->stream); + polypsink->stream = NULL; + } +} - if (polypsink->stream && pa_stream_get_state(polypsink->stream) == PA_STREAM_READY) - pa_operation_unref(pa_stream_cork(polypsink->stream, 1, NULL, NULL)); - break; - - case GST_STATE_PLAYING_TO_PAUSED: - - if (polypsink->stream && pa_stream_get_state(polypsink->stream) == PA_STREAM_READY) - pa_operation_unref(pa_stream_cork(polypsink->stream, 1, NULL, NULL)); +static void gst_polypsink_destroy_context(GstPolypSink* polypsink) { - break; + gst_polypsink_destroy_stream(polypsink); - case GST_STATE_PAUSED_TO_PLAYING: + if (polypsink->context) { + pa_context_disconnect(polypsink->context); + pa_context_unref(polypsink->context); + polypsink->context = NULL; + } +} - create_stream(polypsink); +static void gst_polypsink_finalize(GObject * object) { + GstPolypSink *polypsink = GST_POLYPSINK(object); - if (polypsink->stream && pa_stream_get_state(polypsink->stream) == PA_STREAM_READY) - pa_operation_unref(pa_stream_cork(polypsink->stream, 0, NULL, NULL)); + pa_threaded_mainloop_stop(polypsink->mainloop); - break; + gst_polypsink_destroy_context(polypsink); - case GST_STATE_PAUSED_TO_READY: + g_free(polypsink->server); + g_free(polypsink->device); - destroy_stream(polypsink); - break; - } - - if (GST_ELEMENT_CLASS(parent_class)->change_state) - return GST_ELEMENT_CLASS(parent_class)->change_state (element); - - return GST_STATE_SUCCESS; -} + pa_threaded_mainloop_free(polypsink->mainloop); + G_OBJECT_CLASS(parent_class)->finalize(object); +} -static void do_write(GstPolypSink *polypsink, size_t length) { - size_t l; +static void gst_polypsink_set_property( + GObject * object, + guint prop_id, + const GValue * value, + GParamSpec * pspec) { - if (!polypsink->buffer) - return; - - g_assert(polypsink->buffer_index < GST_BUFFER_SIZE(polypsink->buffer)); - l = GST_BUFFER_SIZE(polypsink->buffer) - polypsink->buffer_index; + GstPolypSink *polypsink = GST_POLYPSINK(object); - if (l > length) - l = length; + switch (prop_id) { + case ARG_SERVER: + g_free(polypsink->server); + polypsink->server = g_value_dup_string(value); + break; - pa_stream_write(polypsink->stream, GST_BUFFER_DATA(polypsink->buffer) + polypsink->buffer_index, l, NULL, 0); - polypsink->buffer_index += l; + case ARG_SINK: + g_free(polypsink->device); + polypsink->device = g_value_dup_string(value); + break; - if (polypsink->buffer_index >= GST_BUFFER_SIZE(polypsink->buffer)) { - gst_buffer_unref(polypsink->buffer); - polypsink->buffer = NULL; - polypsink->buffer_index = 0; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; } } -static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) { - GstPolypSink *polypsink = userdata; - g_assert(s && length && polypsink); - - do_write(polypsink, length); -} - -static void stream_state_callback(struct pa_stream *s, void *userdata) { - GstPolypSink *polypsink = userdata; - g_assert(s && polypsink); +static void gst_polypsink_get_property( + GObject * object, + guint prop_id, + GValue * value, + GParamSpec * pspec) { + + GstPolypSink *polypsink = GST_POLYPSINK(object); - switch (pa_stream_get_state(s)) { - case PA_STREAM_DISCONNECTED: - case PA_STREAM_CREATING: + switch(prop_id) { + case ARG_SERVER: + g_value_set_string(value, polypsink->server); break; - case PA_STREAM_READY: + case ARG_SINK: + g_value_set_string(value, polypsink->device); break; - case PA_STREAM_FAILED: - GST_ELEMENT_ERROR(GST_ELEMENT(polypsink), RESOURCE, BUSY, ("Stream creation failed: %s", pa_strerror(pa_context_errno(pa_stream_get_context(s)))), (NULL)); - - /* Pass over */ - case PA_STREAM_TERMINATED: default: - polypsink->mainloop_api->quit(polypsink->mainloop_api, 1); - destroy_context(polypsink); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } -static void context_state_callback(struct pa_context *c, void *userdata) { - GstPolypSink *polypsink = userdata; - g_assert(c && polypsink); +static void gst_polypsink_context_state_cb(pa_context *c, void *userdata) { + GstPolypSink *polypsink = GST_POLYPSINK(userdata); switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(polypsink->mainloop, 0); + break; + case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; + } +} + +static void gst_polypsink_stream_state_cb(pa_stream *s, void * userdata) { + GstPolypSink *polypsink = GST_POLYPSINK(userdata); - case PA_CONTEXT_READY: { - GstElementState state; - g_assert(!polypsink->stream); - - state = gst_element_get_state(GST_ELEMENT(polypsink)); - if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) - create_stream(polypsink); - + switch (pa_stream_get_state(s)) { + + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal(polypsink->mainloop, 0); break; - } - - case PA_CONTEXT_FAILED: - GST_ELEMENT_ERROR(GST_ELEMENT(polypsink), RESOURCE, BUSY, ("Connection failed: %s", pa_strerror(pa_context_errno(c))), (NULL)); - /* Pass over */ - case PA_CONTEXT_TERMINATED: - default: - polypsink->mainloop_api->quit(polypsink->mainloop_api, 1); - destroy_context(polypsink); + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: break; } } -static void create_stream(GstPolypSink *polypsink) { - char t[256]; - g_assert(polypsink); +static void gst_polypsink_stream_request_cb(pa_stream *s, size_t length, void *userdata) { + GstPolypSink *polypsink = GST_POLYPSINK(userdata); - if (polypsink->stream) - return; + pa_threaded_mainloop_signal(polypsink->mainloop, 0); +} - if (!polypsink->context) { - create_context(polypsink); - return; - } +static gboolean gst_polypsink_open(GstAudioSink *asink) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); - if (!polypsink->negotiated) - return; + pa_threaded_mainloop_lock(polypsink->mainloop); - if (pa_context_get_state(polypsink->context) != PA_CONTEXT_READY) - return; + if (!(polypsink->context = pa_context_new(pa_threaded_mainloop_get_api(polypsink->mainloop), "gstreamer"))) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to create context"), (NULL)); + goto unlock_and_fail; + } - pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec); + pa_context_set_state_callback(polypsink->context, gst_polypsink_context_state_cb, polypsink); - polypsink->stream = pa_stream_new(polypsink->context, "gstreamer output", &polypsink->sample_spec); - g_assert(polypsink->stream); - - pa_stream_set_state_callback(polypsink->stream, stream_state_callback, polypsink); - pa_stream_set_write_callback(polypsink->stream, stream_write_callback, polypsink); - pa_stream_connect_playback(polypsink->stream, NULL, NULL, PA_STREAM_INTERPOLATE_LATENCY, PA_VOLUME_NORM); -} + if (pa_context_connect(polypsink->context, polypsink->server, 0, NULL) < 0) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } -static void create_context(GstPolypSink *polypsink) { - g_assert(polypsink); + /* Wait until the context is ready */ + pa_threaded_mainloop_wait(polypsink->mainloop); - if (polypsink->context) - return; + if (pa_context_get_state(polypsink->context) != PA_CONTEXT_READY) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to connect: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - polypsink->context = pa_context_new(polypsink->mainloop_api, "gstreamer"); - g_assert(polypsink->context); + pa_threaded_mainloop_unlock(polypsink->mainloop); + return TRUE; - pa_context_set_state_callback(polypsink->context, context_state_callback, polypsink); - pa_context_connect(polypsink->context, NULL, 1, NULL); +unlock_and_fail: + + pa_threaded_mainloop_unlock(polypsink->mainloop); + return FALSE; } -static void destroy_stream(GstPolypSink *polypsink) { - g_assert(polypsink); +static gboolean gst_polypsink_close(GstAudioSink *asink) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); - if (polypsink->stream) { - struct pa_stream *s = polypsink->stream; - polypsink->stream = NULL; - pa_stream_set_state_callback(s, NULL, NULL); - pa_stream_set_write_callback(s, NULL, NULL); - pa_stream_unref(s); - } + pa_threaded_mainloop_lock(polypsink->mainloop); + gst_polypsink_destroy_context(polypsink); + pa_threaded_mainloop_unlock(polypsink->mainloop); + + return TRUE; } -static void destroy_context(GstPolypSink *polypsink) { - destroy_stream(polypsink); +static gboolean gst_polypsink_fill_sample_spec(GstRingBufferSpec *spec, pa_sample_spec *ss) { + + if (spec->format == GST_MU_LAW && spec->width == 8) + ss->format = PA_SAMPLE_ULAW; + else if (spec->format == GST_A_LAW && spec->width == 8) + ss->format = PA_SAMPLE_ALAW; + else if (spec->format == GST_U8 && spec->width == 8) + ss->format = PA_SAMPLE_U8; + else if (spec->format == GST_S16_LE && spec->width == 16) + ss->format = PA_SAMPLE_S16LE; + else if (spec->format == GST_S16_BE && spec->width == 16) + ss->format = PA_SAMPLE_S16BE; + else if (spec->format == GST_FLOAT32_LE && spec->width == 32) + ss->format = PA_SAMPLE_FLOAT32LE; + else if (spec->format == GST_FLOAT32_BE && spec->width == 32) + ss->format = PA_SAMPLE_FLOAT32BE; + else + return FALSE; - if (polypsink->context) { - struct pa_context *c = polypsink->context; - polypsink->context = NULL; - pa_context_set_state_callback(c, NULL, NULL); - pa_context_unref(c); - } + ss->channels = spec->channels; + ss->rate = spec->rate; + + if (!pa_sample_spec_valid(ss)) + return FALSE; + + return TRUE; } -static void gst_polypsink_chain(GstPad *pad, GstData *data) { - GstPolypSink *polypsink = GST_POLYPSINK(gst_pad_get_parent(pad)); +static gboolean gst_polypsink_prepare(GstAudioSink *asink, GstRingBufferSpec *spec) { + pa_buffer_attr buf_attr; + + GstPolypSink *polypsink = GST_POLYPSINK(asink); - g_assert(!polypsink->buffer); + if (!gst_polypsink_fill_sample_spec(spec, &polypsink->sample_spec)) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, SETTINGS, ("Invalid sample specification."), (NULL)); + goto unlock_and_fail; + } - if (GST_IS_EVENT(data)) { - GstEvent *event = GST_EVENT(data); - - switch (GST_EVENT_TYPE(event)) { - case GST_EVENT_EOS: - if (polypsink->stream) { - struct pa_operation *o; - - pa_operation_unref(pa_stream_cork(polypsink->stream, 0, NULL, NULL)); - o = pa_stream_drain(polypsink->stream, NULL, NULL); - - /* drain now */ - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (pa_mainloop_iterate(polypsink->mainloop, 1, NULL) < 0) - return; - } - - pa_operation_unref(o); - } - - break; - case GST_EVENT_FLUSH: - if (polypsink->stream) - pa_operation_unref(pa_stream_flush(polypsink->stream, NULL, NULL)); - break; - - default: - break; - } - - gst_pad_event_default(polypsink->sinkpad, event); - } else { - size_t l; - polypsink->buffer = GST_BUFFER(data); - polypsink->buffer_index = 0; - polypsink->counter += GST_BUFFER_SIZE(polypsink->buffer); - - if (polypsink->stream && (l = pa_stream_writable_size(polypsink->stream)) > 0) - do_write(polypsink, l); + pa_threaded_mainloop_lock(polypsink->mainloop); + + if (!polypsink->context || pa_context_get_state(polypsink->context) != PA_CONTEXT_READY) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Bad context state: %s", polypsink->context ? pa_strerror(pa_context_errno(polypsink->context)) : NULL), (NULL)); + goto unlock_and_fail; + } + + if (!(polypsink->stream = pa_stream_new(polypsink->context, "the stream", &polypsink->sample_spec, NULL))) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to create stream: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; } - while (polypsink->context && (pa_context_is_pending(polypsink->context) || polypsink->buffer)) { - if (pa_mainloop_iterate(polypsink->mainloop, 1, NULL) < 0) - return; + pa_stream_set_state_callback(polypsink->stream, gst_polypsink_stream_state_cb, polypsink); + pa_stream_set_write_callback(polypsink->stream, gst_polypsink_stream_request_cb, polypsink); + + memset(&buf_attr, 0, sizeof(buf_attr)); + buf_attr.tlength = spec->segtotal*spec->segsize; + buf_attr.maxlength = buf_attr.tlength*2; + buf_attr.prebuf = buf_attr.minreq = spec->segsize; + + if (pa_stream_connect_playback(polypsink->stream, polypsink->device, &buf_attr, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_NOT_MONOTONOUS, NULL, NULL) < 0) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to connext stream: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; } -} + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait(polypsink->mainloop); -#if 0 -static void stream_get_latency_callback(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) { - GstPolypSink *polypsink = (GstPolypSink*) userdata; + if (pa_stream_get_state(polypsink->stream) != PA_STREAM_READY) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Failed to connext stream: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - polypsink->latency = i->buffer_usec + i->sink_usec; -} + pa_threaded_mainloop_unlock(polypsink->mainloop); -static GstClockTime gst_polypsink_get_time(GstClock *clock, gpointer data) { - struct pa_operation *o; - GstPolypSink *polypsink = GST_POLYPSINK(data); - GstClockTime r, l; + spec->bytes_per_sample = pa_frame_size(&polypsink->sample_spec); + memset(spec->silence_sample, 0, spec->bytes_per_sample); - if (!polypsink->stream || pa_stream_get_state(polypsink->stream) != PA_STREAM_READY) - return 0; + return TRUE; - polypsink->latency = 0; +unlock_and_fail: - o = pa_stream_get_latency(polypsink_>stream, latency_func, polypsink); - g_assert(o); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (pa_mainloop_iterate(polypsink->mainloop, 1, NULL) < 0) - return; - } + pa_threaded_mainloop_unlock(polypsink->mainloop); + return FALSE; +} - r = ((GstClockTime) polypsink->counter / pa_frame_size(&polypsink->sample_spec))*GST_SECOND/polypsink->sample_spec.rate; - l = polypsink->latency*GST_USECOND; +static gboolean gst_polypsink_unprepare(GstAudioSink * asink) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); - return r > l ? r - l : 0; -} + pa_threaded_mainloop_lock(polypsink->mainloop); + gst_polypsink_destroy_stream(polypsink); + pa_threaded_mainloop_unlock(polypsink->mainloop); -static GstClock *gst_polypsink_get_clock(GstElement * element) { - GstPolypSink *polypsink = GST_POLYPSINK (element); - return GST_CLOCK (polypsink->provided_clock); + return TRUE; } -static void gst_polypsink_set_clock (GstElement * element, GstClock * clock) { - GstPolypSink *polypsink = GST_POLYPSINK (element); - polypsink->clock = clock; +#define CHECK_DEAD_GOTO(polypsink, label) \ +if (!(polypsink)->context || pa_context_get_state((polypsink)->context) != PA_CONTEXT_READY || \ + !(polypsink)->stream || pa_stream_get_state((polypsink)->stream) != PA_STREAM_READY) { \ + GST_ELEMENT_ERROR((polypsink), RESOURCE, FAILED, ("Disconnected: %s", (polypsink)->context ? pa_strerror(pa_context_errno((polypsink)->context)) : NULL), (NULL)); \ + goto label; \ } -#endif - -static GstPadLinkReturn gst_polypsink_link(GstPad *pad, const GstCaps *caps) { - int depth = 16, endianness = 1234; - gboolean sign = TRUE; - GstPolypSink *polypsink; - GstStructure *structure; - const char *n; - char t[256]; - GstElementState state; - - polypsink = GST_POLYPSINK(gst_pad_get_parent(pad)); - - structure = gst_caps_get_structure(caps, 0); - - if (!(gst_structure_get_int(structure, "depth", &depth))) - gst_structure_get_int(structure, "width", &depth); - - gst_structure_get_int(structure, "endianness", &endianness); - gst_structure_get_boolean(structure, "signed", &sign); - - n = gst_structure_get_name(structure); - - if (depth == 16 && endianness == 1234 && sign && !strcmp(n, "audio/x-raw-int")) - polypsink->sample_spec.format = PA_SAMPLE_S16LE; - else if (depth == 16 && endianness == 4321 && sign && !strcmp(n, "audio/x-raw-int")) - polypsink->sample_spec.format = PA_SAMPLE_S16BE; - else if (depth == 8 && !sign && !strcmp(n, "audio/x-raw-int")) - polypsink->sample_spec.format = PA_SAMPLE_U8; - else if (depth == 32 && endianness == 1234 && !strcmp(n, "audio/x-raw-float")) - polypsink->sample_spec.format = PA_SAMPLE_FLOAT32LE; - else if (depth == 32 && endianness == 4321 && !strcmp(n, "audio/x-raw-float")) - polypsink->sample_spec.format = PA_SAMPLE_FLOAT32LE; - else - return GST_PAD_LINK_REFUSED; - - polypsink->sample_spec.rate = 44100; - polypsink->sample_spec.channels = 2; - - pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec); +static guint gst_polypsink_write(GstAudioSink *asink, gpointer data, guint length) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); + size_t l; - gst_structure_get_int(structure, "channels", (int*) &polypsink->sample_spec.channels); - gst_structure_get_int(structure, "rate", &polypsink->sample_spec.rate); + pa_threaded_mainloop_lock(polypsink->mainloop); - pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec); - - if (!pa_sample_spec_valid(&polypsink->sample_spec)) - return GST_PAD_LINK_REFUSED; + for (;;) { + CHECK_DEAD_GOTO(polypsink, unlock_and_fail); - + if ((l = pa_stream_writable_size(polypsink->stream))) + break; + + pa_threaded_mainloop_wait(polypsink->mainloop); + } - polypsink->negotiated = 1; + if (l == (size_t) -1) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - destroy_stream(polypsink); + if (l > length) + l = length; - state = gst_element_get_state(GST_ELEMENT(polypsink)); - if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) - create_stream(polypsink); + if (pa_stream_write(polypsink->stream, data, l, NULL, 0, PA_SEEK_RELATIVE) < 0) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - return GST_PAD_LINK_OK; -} + pa_threaded_mainloop_unlock(polypsink->mainloop); -static GstCaps* gst_polypsink_sink_fixate(GstPad *pad, const GstCaps *caps) { - GstCaps *newcaps; - GstStructure *structure; + GST_WARNING("Write"); - newcaps = gst_caps_new_full(gst_structure_copy(gst_caps_get_structure(caps, 0)), NULL); - structure = gst_caps_get_structure(newcaps, 0); + return l; +unlock_and_fail: - if (gst_caps_structure_fixate_field_nearest_int (structure, "rate", 44100) || - gst_caps_structure_fixate_field_nearest_int (structure, "depth", 16) || - gst_caps_structure_fixate_field_nearest_int (structure, "width", 16) || - gst_caps_structure_fixate_field_nearest_int (structure, "channels", 2)) - return newcaps; - - gst_caps_free (newcaps); - return NULL; + pa_threaded_mainloop_unlock(polypsink->mainloop); + return 0; } -static void gst_polypsink_init(GTypeInstance* instance, gpointer g_class) { - GstPolypSink *polypsink = GST_POLYPSINK(instance); +static guint gst_polypsink_delay(GstAudioSink *asink) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); + pa_usec_t t; - polypsink->sinkpad = gst_pad_new_from_template(gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS (instance), "sink"), "sink"); - - gst_element_add_pad(GST_ELEMENT(polypsink), polypsink->sinkpad); - gst_pad_set_chain_function(polypsink->sinkpad, gst_polypsink_chain); - gst_pad_set_link_function(polypsink->sinkpad, gst_polypsink_link); - gst_pad_set_fixate_function (polypsink->sinkpad, gst_polypsink_sink_fixate); - -/* GST_FLAG_SET(polypsink, GST_ELEMENT_THREAD_SUGGESTED); */ - GST_FLAG_SET(polypsink, GST_ELEMENT_EVENT_AWARE); - - polypsink->context = NULL; - polypsink->stream = NULL; + pa_threaded_mainloop_lock(polypsink->mainloop); - polypsink->mainloop = pa_mainloop_new(); - g_assert(polypsink->mainloop); + CHECK_DEAD_GOTO(polypsink, unlock_and_fail); - polypsink->mainloop_api = pa_mainloop_get_api(polypsink->mainloop); + if (pa_stream_get_latency(polypsink->stream, &t, NULL) < 0) { - polypsink->sample_spec.rate = 0; - polypsink->sample_spec.channels = 0; - polypsink->sample_spec.format = 0; + if (pa_context_errno(polypsink->context) != PA_ERR_NODATA) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - polypsink->negotiated = 0; + GST_WARNING("Not data while querying latency"); + t = 0; + } - polypsink->buffer = NULL; - polypsink->buffer_index = 0; + GST_WARNING("Latency %0.3f ms", (double) t / 1000); + + pa_threaded_mainloop_unlock(polypsink->mainloop); + + return (guint) ((t * polypsink->sample_spec.rate) / 1000000LL); - polypsink->latency = 0; - polypsink->counter = 0; +unlock_and_fail: + + pa_threaded_mainloop_unlock(polypsink->mainloop); + return 0; } -static void gst_polypsink_dispose(GObject *object) { - GstPolypSink *polypsink = GST_POLYPSINK(object); +static void gst_polypsink_success_cb(pa_stream *s, int success, void *userdata) { + GstPolypSink *polypsink = GST_POLYPSINK(userdata); -/* gst_object_unparent(GST_OBJECT(polypsink->provided_clock)); */ + polypsink->operation_success = success; + pa_threaded_mainloop_signal(polypsink->mainloop, 0); +} +static void gst_polypsink_reset(GstAudioSink *asink) { + GstPolypSink *polypsink = GST_POLYPSINK(asink); + pa_operation *o = NULL; - destroy_context(polypsink); + pa_threaded_mainloop_lock(polypsink->mainloop); - if (polypsink->buffer) - gst_buffer_unref(polypsink->buffer); + CHECK_DEAD_GOTO(polypsink, unlock_and_fail); - - g_free(polypsink->server); - g_free(polypsink->sink); + if (!(o = pa_stream_flush(polypsink->stream, gst_polypsink_success_cb, polypsink))) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } - pa_mainloop_free(polypsink->mainloop); - - G_OBJECT_CLASS(parent_class)->dispose(object); -} - -static void gst_polypsink_class_init(gpointer g_class, gpointer class_data) { - GObjectClass *gobject_class = G_OBJECT_CLASS(g_class); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS(g_class); - - parent_class = g_type_class_peek_parent(g_class); + polypsink->operation_success = 0; + while (pa_operation_get_state(o) != PA_OPERATION_DONE) { + CHECK_DEAD_GOTO(polypsink, unlock_and_fail); + + pa_threaded_mainloop_wait(polypsink->mainloop); + } - g_object_class_install_property(gobject_class, ARG_SERVER, g_param_spec_string("server", "server", "server", NULL, G_PARAM_READWRITE)); - g_object_class_install_property(gobject_class, ARG_SINK, g_param_spec_string("sink", "sink", "sink", NULL, G_PARAM_READWRITE)); + GST_WARNING("Reset"); - gobject_class->set_property = gst_polypsink_set_property; - gobject_class->get_property = gst_polypsink_get_property; - gobject_class->dispose = gst_polypsink_dispose; - - gstelement_class->change_state = gst_polypsink_change_state; -/* gstelement_class->set_clock = gst_polypsink_set_clock; */ -/* gstelement_class->get_clock = gst_polypsink_get_clock; */ -} + if (!polypsink->operation_success) { + GST_ELEMENT_ERROR(polypsink, RESOURCE, FAILED, ("Flush failed: %s", pa_strerror(pa_context_errno(polypsink->context))), (NULL)); + goto unlock_and_fail; + } +unlock_and_fail: -gboolean gst_polypsink_factory_init(GstPlugin *plugin) { - return gst_element_register(plugin, "polypsink", GST_RANK_NONE, GST_TYPE_POLYPSINK); + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + pa_threaded_mainloop_unlock(polypsink->mainloop); } GType gst_polypsink_get_type(void) { static GType polypsink_type = 0; if (!polypsink_type) { - + static const GTypeInfo polypsink_info = { - sizeof (GstPolypSinkClass), + sizeof(GstPolypSinkClass), gst_polypsink_base_init, NULL, gst_polypsink_class_init, @@ -583,9 +563,13 @@ GType gst_polypsink_get_type(void) { 0, gst_polypsink_init, }; - - polypsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPolypSink", &polypsink_info, 0); + + polypsink_type = g_type_register_static( + GST_TYPE_AUDIO_SINK, + "GstPolypSink", + &polypsink_info, + 0); } - + return polypsink_type; } diff --git a/src/polypsink.h b/src/polypsink.h index 90b3b7e..4b9a76b 100644 --- a/src/polypsink.h +++ b/src/polypsink.h @@ -1,10 +1,32 @@ #ifndef __GST_POLYPSINK_H__ #define __GST_POLYPSINK_H__ +/* $Id$ */ + +/*** + This file is part of gst-polyp. + + gst-polyp is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + gst-polyp 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with gst-polyp; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + #include +#include -#include -#include +#include +#include G_BEGIN_DECLS @@ -23,33 +45,25 @@ typedef struct _GstPolypSink GstPolypSink; typedef struct _GstPolypSinkClass GstPolypSinkClass; struct _GstPolypSink { - GstElement element; - - GstPad *sinkpad; - - char *server, *sink; - - struct pa_mainloop *mainloop; - struct pa_mainloop_api *mainloop_api; - struct pa_context *context; - struct pa_stream *stream; - struct pa_sample_spec sample_spec; + GstAudioSink sink; - int negotiated; + gchar *server, *device; - GstBuffer *buffer; - size_t buffer_index; + pa_threaded_mainloop *mainloop; + + pa_context *context; + pa_stream *stream; + + pa_sample_spec sample_spec; - size_t counter; - pa_usec_t latency; + int operation_success; }; struct _GstPolypSinkClass { - GstElementClass parent_class; + GstAudioSinkClass parent_class; }; GType gst_polypsink_get_type(void); -gboolean gst_polypsink_factory_init(GstPlugin *plugin); G_END_DECLS -- cgit