summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-09-10 13:31:06 +0000
committerLennart Poettering <lennart@poettering.net>2004-09-10 13:31:06 +0000
commitc0b67627b12536efe6bac5a065f1e6b0a5d39c72 (patch)
tree12b9ddde7d468c2dbed85abdc6b3842031811939
parent626941c51c739ae7fcfc2cb46e6edc2336b393e7 (diff)
initial commit
git-svn-id: file:///home/lennart/svn/public/gst-pulse/trunk@3 bb39ca4e-bce3-0310-b5d4-eea78a553289
-rw-r--r--Makefile.am42
-rwxr-xr-xbootstrap.sh47
-rw-r--r--configure.ac90
-rw-r--r--src/Makefile.am16
-rw-r--r--src/nullsink.c608
-rw-r--r--src/plugin.c25
-rw-r--r--src/polypsink.c611
-rw-r--r--src/polypsink.h56
8 files changed, 1495 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..5ab9c3c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,42 @@
+# $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 General Public License as published by
+# the Free Software Foundation; either version 2 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU 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.
+
+EXTRA_DIST = bootstrap.sh README LICENSE
+SUBDIRS=src
+# doc
+
+MAINTAINERCLEANFILES=README
+noinst_DATA = README
+
+README:
+ rm -f README
+# $(MAKE) -C doc README
+# cd $(srcdir) && ln -s doc/README README
+
+homepage: all dist
+ test -d $$HOME/homepage/private
+ mkdir -p $$HOME/homepage/private/projects/gst-polyp
+ cp gst-polyp-@PACKAGE_VERSION@.tar.gz $$HOME/homepage/private/projects/gst-polyp
+ 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
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..b048d59
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+# $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 General Public License as published by
+# the Free Software Foundation; either version 2 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU 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.
+
+run_versioned() {
+ local P
+ type -p "$1-$2" &> /dev/null && P="$1-$2" || local P="$1"
+
+ shift 2
+ "$P" "$@"
+}
+
+if [ "x$1" = "xam" ] ; then
+ set -ex
+ run_versioned automake 1.7 -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
+
+ CFLAGS="-g -O0" ./configure --sysconfdir=/etc "$@"
+
+ make clean
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c3ebd83
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,90 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+# $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 General Public License as published by
+# the Free Software Foundation; either version 2 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU 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.
+
+AC_PREREQ(2.57)
+AC_INIT([gst-polyp],[0.2],[mzkzzfcbylc (at) 0pointer (dot) de])
+AC_CONFIG_SRCDIR([src/plugin.c])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([foreign -Wall])
+
+AC_SUBST(PACKAGE_URL, [http://0pointer.de/lennart/projects/gst-polyp/])
+
+if type -p stow > /dev/null && test -d /usr/local/stow ; then
+ AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***])
+ ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}"
+fi
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_LN_S
+AC_PROG_LIBTOOL
+
+AC_CHECK_FUNCS([gethostname])
+AC_CHECK_HEADERS([limits.h])
+AC_C_CONST
+AC_FUNC_MALLOC
+AC_TYPE_SIZE_T
+
+ACX_PTHREAD
+
+if test -d ../polypaudio ; then
+ POLYP_CFLAGS='-I$(top_srcdir)/../polypaudio'
+ POLYP_LIBS='-L$(top_srcdir)/../polypaudio/polyp -lpolyp -lpolyp-error -lpolyp-mainloop'
+ echo "*** Found polypaudio in ../polypaudio, using that version ***"
+else
+ PKG_CHECK_MODULES(POLYP, [ polyplib >= 0.4 ])
+fi
+
+AC_SUBST(POLYP_LIBS)
+AC_SUBST(POLYP_CFLAGS)
+
+
+PKG_CHECK_MODULES(GST, [ gstreamer-0.8 >= 0.8.4 ])
+
+AC_SUBST(GST_LIBS)
+AC_SUBST(GST_CFLAGS)
+y
+# If using GCC specifiy some additional parameters
+if test "x$GCC" = "xyes" ; then
+ CFLAGS="$CFLAGS -pipe -Wall -W -Wno-unused-parameter"
+fi
+
+# LYNX documentation generation
+AC_ARG_ENABLE(lynx,
+ AS_HELP_STRING(--disable-lynx,Turn off lynx usage for documentation generation),
+[case "${enableval}" in
+ yes) lynx=yes ;;
+ no) lynx=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-lynx) ;;
+esac],[lynx=yes])
+
+if test x$lynx = xyes ; then
+ AC_CHECK_PROG(have_lynx, lynx, yes, no)
+
+ if test x$have_lynx = xno ; then
+ AC_MSG_ERROR([*** Sorry, you have to install lynx or use --disable-lynx ***])
+ fi
+fi
+
+AM_CONDITIONAL([USE_LYNX], [test "x$lynx" = xyes])
+
+AC_CONFIG_FILES([Makefile src/Makefile]) # doc/Makefile doc/README.html])
+AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..1bd1fae
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,16 @@
+plugindir=/tmp/
+
+plugin_LTLIBRARIES = libgstpolyp.la
+
+libgstpolyp_la_SOURCES = plugin.c polypsink.c
+libgstpolyp_la_CFLAGS = $(GST_CFLAGS) $(POLYP_CFLAGS)
+libgstpolyp_la_LIBADD = $(POLYP_LIBS) $(GST_LIBS)
+
+
+reg:
+ gst-register-0.8 --gst-plugin-path=`pwd`
+
+inspect:
+ gst-inspect-0.8 polypsink
+
+.PHONY: reg inspect
diff --git a/src/nullsink.c b/src/nullsink.c
new file mode 100644
index 0000000..743cdbf
--- /dev/null
+++ b/src/nullsink.c
@@ -0,0 +1,608 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <polyp/polyplib-error.h>
+#include <polyp/mainloop.h>
+
+#include "polypsink.h"
+
+enum {
+ ARG_0,
+ ARG_SERVER,
+ ARG_SINK,
+};
+
+static GstElementClass *parent_class = NULL;
+
+/* static void create_stream(GstPolypSink *polypsink); */
+/* static void destroy_stream(GstPolypSink *polypsink); */
+
+/* static void create_context(GstPolypSink *polypsink); */
+/* static void destroy_context(GstPolypSink *polypsink); */
+
+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) [ 1000, 96000 ], "
+ "channels = (int) [1, 16]"
+
+/* "audio/x-raw-float, " */
+/* "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " */
+/* "width = (int) 32, " */
+/* "rate = (int) [ 1, 192000 ], " */
+/* "channels = (int) [ 1, 16 ];" */
+
+/* "audio/x-raw-int, " */
+/* "signed = (boolean) FALSE, " */
+/* "width = (int) 8, " */
+/* "depth = (int) 8, " */
+/* "rate = (int) [ 1, 192000 ], " */
+/* "channels = (int) [ 1, 16 ]" */
+
+ )
+ );
+
+ static const GstElementDetails 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);
+}
+
+/* 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); */
+
+/* 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_message("Fehler!"); */
+/* G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); */
+/* break; */
+/* } */
+/* } */
+
+/* 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); */
+
+/* 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_message("Fehler!"); */
+/* G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); */
+/* break; */
+/* } */
+/* } */
+
+/* static GstElementStateReturn gst_polypsink_change_state(GstElement * element) { */
+/* GstPolypSink *polypsink; */
+
+/* polypsink = GST_POLYPSINK (element); */
+
+/* g_message("state change: %u" , GST_STATE(element)); */
+
+/* /\* 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; *\/ */
+
+/* /\* case GST_STATE_READY_TO_PAUSED: *\/ */
+/* /\* case GST_STATE_PLAYING_TO_PAUSED: *\/ */
+
+/* /\* /\\* create_stream(polypsink); *\\/ *\/ */
+
+/* /\* /\\* g_message("cork on due to pause"); *\\/ *\/ */
+
+/* /\* /\\* 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_PAUSED_TO_PLAYING: *\/ */
+
+/* /\* /\\* create_stream(polypsink); *\\/ *\/ */
+
+/* /\* /\\* g_message("cork off due to pause"); *\\/ *\/ */
+
+/* /\* /\\* if (polypsink->stream && pa_stream_get_state(polypsink->stream) == PA_STREAM_READY) *\\/ *\/ */
+/* /\* /\\* pa_operation_unref(pa_stream_cork(polypsink->stream, 0, NULL, NULL)); *\\/ *\/ */
+
+/* /\* break; *\/ */
+
+/* /\* case GST_STATE_PAUSED_TO_READY: *\/ */
+/* /\* /\\* 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; */
+/* } */
+
+
+/* static void do_write(GstPolypSink *polypsink, size_t length) { */
+/* size_t l; */
+
+/* if (!polypsink->buffer) */
+/* return; */
+
+/* g_assert(polypsink->buffer_index < GST_BUFFER_SIZE(polypsink->buffer)); */
+/* l = GST_BUFFER_SIZE(polypsink->buffer) - polypsink->buffer_index; */
+
+/* if (l > length) */
+/* l = length; */
+
+/* pa_stream_write(polypsink->stream, GST_BUFFER_DATA(polypsink->buffer) + polypsink->buffer_index, l, NULL, 0); */
+/* polypsink->buffer_index += l; */
+
+/* /\* g_message("WROTE: %i/%i", polypsink->buffer_index, GST_BUFFER_SIZE(polypsink->buffer)); *\/ */
+
+/* if (polypsink->buffer_index >= GST_BUFFER_SIZE(polypsink->buffer)) { */
+/* gst_buffer_unref(polypsink->buffer); */
+/* polypsink->buffer = NULL; */
+/* polypsink->buffer_index = 0; */
+/* } */
+/* } */
+
+/* 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); */
+
+/* g_message("stream_state_callback()"); */
+
+/* switch (pa_stream_get_state(s)) { */
+/* case PA_STREAM_DISCONNECTED: */
+/* case PA_STREAM_CREATING: */
+/* break; */
+
+/* case PA_STREAM_READY: */
+/* 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); */
+/* break; */
+/* } */
+/* } */
+
+/* static void context_state_callback(struct pa_context *c, void *userdata) { */
+/* GstPolypSink *polypsink = userdata; */
+/* g_assert(c && polypsink); */
+
+/* g_message("context_state_callback()"); */
+
+/* switch (pa_context_get_state(c)) { */
+/* case PA_CONTEXT_UNCONNECTED: */
+/* case PA_CONTEXT_CONNECTING: */
+/* case PA_CONTEXT_AUTHORIZING: */
+/* case PA_CONTEXT_SETTING_NAME: */
+/* break; */
+
+/* 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); */
+
+/* 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); */
+/* break; */
+/* } */
+/* } */
+
+/* static void create_stream(GstPolypSink *polypsink) { */
+/* char t[256]; */
+/* g_assert(polypsink); */
+
+/* g_message("create_stream()"); */
+
+/* if (polypsink->stream) */
+/* return; */
+
+/* if (!polypsink->context) { */
+/* create_context(polypsink); */
+/* return; */
+/* } */
+
+/* if (!polypsink->negotiated) */
+/* return; */
+
+/* if (pa_context_get_state(polypsink->context) != PA_CONTEXT_READY) */
+/* return; */
+
+/* g_message("creating stream"); */
+
+/* pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec); */
+
+/* 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); */
+
+/* g_message("create_stream()"); */
+/* } */
+
+/* static void create_context(GstPolypSink *polypsink) { */
+/* g_assert(polypsink); */
+
+/* g_message("create_context()"); */
+
+/* if (polypsink->context) */
+/* return; */
+
+/* g_message("creating context"); */
+
+/* polypsink->context = pa_context_new(polypsink->mainloop_api, "gstreamer"); */
+/* g_assert(polypsink->context); */
+
+/* pa_context_set_state_callback(polypsink->context, context_state_callback, polypsink); */
+/* pa_context_connect(polypsink->context, NULL); */
+/* } */
+
+/* static void destroy_stream(GstPolypSink *polypsink) { */
+/* g_message("destroy_stream()"); */
+/* g_assert(polypsink); */
+
+/* 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); */
+
+/* g_message("destroying stream"); */
+/* } */
+/* } */
+
+/* static void destroy_context(GstPolypSink *polypsink) { */
+/* g_message("destroy_context()"); */
+
+/* destroy_stream(polypsink); */
+
+/* if (polypsink->context) { */
+/* struct pa_context *c = polypsink->context; */
+/* polypsink->context = NULL; */
+/* pa_context_set_state_callback(c, NULL, NULL); */
+/* pa_context_unref(c); */
+
+/* g_message("destroying context"); */
+/* } */
+/* } */
+
+static void gst_polypsink_chain(GstPad *pad, GstData *data) {
+ GstPolypSink *polypsink = GST_POLYPSINK(gst_pad_get_parent(pad));
+
+ return;
+
+/* g_assert(!polypsink->buffer); */
+
+/* g_message("chain-entry"); */
+
+/* if (GST_IS_EVENT(data)) { */
+/* GstEvent *event = GST_EVENT(data); */
+
+/* switch (GST_EVENT_TYPE(event)) { */
+/* case GST_EVENT_EOS: */
+/* g_message("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: */
+/* g_message("FLUSH"); */
+/* if (polypsink->stream) */
+/* pa_operation_unref(pa_stream_flush(polypsink->stream, NULL, NULL)); */
+/* break; */
+
+/* default: */
+/* g_message("other event: %i", GST_EVENT_TYPE(event)); */
+/* 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); *\/ */
+
+/* gst_data_unref(data); */
+/* } */
+
+/* /\* while (pa_context_is_pending(polypsink->context) || polypsink->buffer) { *\/ */
+
+/* /\* g_message("iterate start %i %i", !!pa_context_is_pending(polypsink->context), !!polypsink->buffer); *\/ */
+
+/* /\* if (pa_mainloop_iterate(polypsink->mainloop, 1, NULL) < 0) *\/ */
+/* /\* return; *\/ */
+
+/* /\* g_message("iterate stop"); *\/ */
+
+/* /\* } *\/ */
+
+/* g_message("chain-exit"); */
+}
+
+/* static void stream_get_latency_callback(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) { */
+/* GstPolypSink *polypsink = (GstPolypSink*) userdata; */
+
+/* polypsink->latency = i->buffer_usec + i->sink_usec; */
+/* } */
+
+/* static GstClockTime gst_polypsink_get_time(GstClock *clock, gpointer data) { */
+/* struct pa_operation *o; */
+/* GstPolypSink *polypsink = GST_POLYPSINK(data); */
+/* GstClockTime r, l; */
+
+/* if (!polypsink->stream || pa_stream_get_state(polypsink->stream) != PA_STREAM_READY) */
+/* return 0; */
+
+/* polypsink->latency = 0; */
+
+/* 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; */
+/* } */
+
+/* r = ((GstClockTime) polypsink->counter / pa_frame_size(&polypsink->sample_spec))*GST_SECOND/polypsink->sample_spec.rate; */
+/* l = polypsink->latency*GST_USECOND; */
+
+/* return r > l ? r - l : 0; */
+/* } */
+
+/* static GstClock *gst_polypsink_get_clock(GstElement * element) { */
+/* GstPolypSink *polypsink = GST_POLYPSINK (element); */
+/* return GST_CLOCK (polypsink->provided_clock); */
+/* } */
+
+/* static void gst_polypsink_set_clock (GstElement * element, GstClock * clock) { */
+/* GstPolypSink *polypsink = GST_POLYPSINK (element); */
+/* polypsink->clock = clock; */
+/* } */
+
+/* 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; */
+
+/* polypsink = GST_POLYPSINK(gst_pad_get_parent(pad)); */
+
+/* g_message("link: %p", polypsink); */
+
+/* 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 = 0; */
+/* polypsink->sample_spec.channels = 0; */
+
+/* gst_structure_get_int(structure, "channels", (int*) &polypsink->sample_spec.channels); */
+/* gst_structure_get_int(structure, "rate", &polypsink->sample_spec.rate); */
+
+/* if (!pa_sample_spec_valid(&polypsink->sample_spec)) */
+/* return GST_PAD_LINK_REFUSED; */
+
+/* polypsink->negotiated = 1; */
+
+/* /\* destroy_stream(polypsink); *\/ */
+/* /\*create_stream(polypsink);*\/ */
+
+/* return GST_PAD_LINK_OK; */
+/* } */
+
+
+static void gst_polypsink_init(GTypeInstance* instance, gpointer g_class) {
+ GstPolypSink *polypsink = GST_POLYPSINK(instance);
+
+ g_message("created: %p", polypsink);
+
+ 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_FLAG_SET (polypsink, GST_ELEMENT_THREAD_SUGGESTED);*/
+ GST_FLAG_SET(polypsink, GST_ELEMENT_EVENT_AWARE);
+
+/* polypsink->context = NULL; */
+/* polypsink->stream = NULL; */
+
+/* polypsink->mainloop = NULL; */
+/* polypsink->mainloop_api = NULL; */
+
+ /*pa_mainloop_new();
+ g_assert(polypsink->mainloop);
+
+ polypsink->mainloop_api = pa_mainloop_get_api(polypsink->mainloop);*/
+
+/* polypsink->sample_spec.rate = 0; */
+/* polypsink->sample_spec.channels = 0; */
+/* polypsink->sample_spec.format = 0; */
+
+/* polypsink->negotiated = 0; */
+
+/* polypsink->buffer = NULL; */
+/* polypsink->buffer_index = 0; */
+
+/* polypsink->latency = 0; */
+/* polypsink->counter = 0; */
+
+/* polypsink->server = NULL; */
+/* polypsink->sink = NULL; */
+
+}
+
+static void gst_polypsink_dispose(GObject *object) {
+ GstPolypSink *polypsink = GST_POLYPSINK(object);
+
+/* gst_object_unparent(GST_OBJECT(polypsink->provided_clock)); */
+
+
+/* destroy_context(polypsink); */
+
+/* if (polypsink->buffer) */
+/* gst_buffer_unref(polypsink->buffer); */
+
+
+/* g_free(polypsink->server); */
+/* g_free(polypsink->sink); */
+
+
+/* pa_mainloop_free(polypsink->mainloop); */
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+
+ g_message("destroyed: %p", polypsink);
+}
+
+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);
+
+/* 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)); */
+
+/* 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; */
+}
+
+
+gboolean gst_polypsink_factory_init(GstPlugin *plugin) {
+ return gst_element_register(plugin, "polypsink", GST_RANK_NONE, GST_TYPE_POLYPSINK);
+}
+
+GType gst_polypsink_get_type(void) {
+ static GType polypsink_type = 0;
+
+ if (!polypsink_type) {
+
+ static const GTypeInfo polypsink_info = {
+ sizeof (GstPolypSinkClass),
+ gst_polypsink_base_init,
+ NULL,
+ gst_polypsink_class_init,
+ NULL,
+ NULL,
+ sizeof(GstPolypSink),
+ 0,
+ gst_polypsink_init,
+ };
+
+ polypsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPolypSink", &polypsink_info, 0);
+ }
+
+ return polypsink_type;
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 0000000..900fd17
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,25 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "polypsink.h"
+
+GST_DEBUG_CATEGORY(polyp_debug);
+
+static gboolean plugin_init (GstPlugin * plugin) {
+ if (!gst_library_load("gstaudio"))
+ return FALSE;
+
+ if (!(gst_polypsink_factory_init (plugin)))
+ 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/")
diff --git a/src/polypsink.c b/src/polypsink.c
new file mode 100644
index 0000000..bc37252
--- /dev/null
+++ b/src/polypsink.c
@@ -0,0 +1,611 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <polyp/polyplib-error.h>
+#include <polyp/mainloop.h>
+
+#include "polypsink.h"
+
+enum {
+ ARG_0,
+ ARG_SERVER,
+ ARG_SINK,
+};
+
+static GstElementClass *parent_class = NULL;
+
+static void create_stream(GstPolypSink *polypsink);
+static void destroy_stream(GstPolypSink *polypsink);
+
+static void create_context(GstPolypSink *polypsink);
+static void destroy_context(GstPolypSink *polypsink);
+
+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) 1234, " /*{ LITTLE_ENDIAN , BIG_ENDIAN }, "*/
+ "signed = (boolean) TRUE, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) 44100, " /*[ 1000, 192000 ], "*/
+ "channels = (int) [ 1, 16 ]" /*[ 1, 16 ];"*/
+
+/* "audio/x-raw-float, " */
+/* "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " */
+/* "width = (int) 32, " */
+/* "rate = (int) [ 1000, 192000 ], " */
+/* "channels = (int) [ 1, 16 ];" */
+
+/* "audio/x-raw-int, " */
+/* "signed = (boolean) FALSE, " */
+/* "width = (int) 8, " */
+/* "depth = (int) 8, " */
+/* "rate = (int) [ 1000, 192000 ], " */
+ /* "channels = (int) [ 1, 16 ]"*/)
+ );
+
+ static const GstElementDetails 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);
+}
+
+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);
+
+ 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_message("Fehler!");
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+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);
+
+ 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_message("Fehler!");
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstElementStateReturn gst_polypsink_change_state(GstElement * element) {
+ GstPolypSink *polypsink;
+
+ polypsink = GST_POLYPSINK (element);
+
+ g_message("state change: %i -> %i" , GST_STATE(element), GST_STATE_PENDING(element));
+
+ switch(GST_STATE_TRANSITION(element)) {
+
+ case GST_STATE_NULL_TO_READY:
+ g_message("NULL->READY");
+ create_context(polypsink);
+ g_message("NULL->READY DONE");
+ break;
+
+ case GST_STATE_READY_TO_NULL:
+ g_message("READY->NULL");
+ destroy_context(polypsink);
+ g_message("READY->NULL DONE");
+ break;
+
+ case GST_STATE_READY_TO_PAUSED:
+
+ g_message("READY->PAUSED");
+
+ create_stream(polypsink);
+
+ 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:
+
+ g_message("PLAYING->PAUSED");
+
+ 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_PAUSED_TO_PLAYING:
+
+ g_message("PAUSED->PLAYING");
+
+ create_stream(polypsink);
+
+ if (polypsink->stream && pa_stream_get_state(polypsink->stream) == PA_STREAM_READY)
+ pa_operation_unref(pa_stream_cork(polypsink->stream, 0, NULL, NULL));
+
+ break;
+
+ case GST_STATE_PAUSED_TO_READY:
+
+ g_message("PAUSED->READY");
+
+ 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;
+}
+
+
+static void do_write(GstPolypSink *polypsink, size_t length) {
+ size_t l;
+
+ if (!polypsink->buffer)
+ return;
+
+ g_assert(polypsink->buffer_index < GST_BUFFER_SIZE(polypsink->buffer));
+ l = GST_BUFFER_SIZE(polypsink->buffer) - polypsink->buffer_index;
+
+ if (l > length)
+ l = length;
+
+ pa_stream_write(polypsink->stream, GST_BUFFER_DATA(polypsink->buffer) + polypsink->buffer_index, l, NULL, 0);
+ polypsink->buffer_index += l;
+
+/* g_message("WROTE: %i/%i", polypsink->buffer_index, GST_BUFFER_SIZE(polypsink->buffer)); */
+
+ if (polypsink->buffer_index >= GST_BUFFER_SIZE(polypsink->buffer)) {
+ gst_buffer_unref(polypsink->buffer);
+ polypsink->buffer = NULL;
+ polypsink->buffer_index = 0;
+ }
+}
+
+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);
+
+/* g_message("stream_state_callback()"); */
+
+ switch (pa_stream_get_state(s)) {
+ case PA_STREAM_DISCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
+
+ case PA_STREAM_READY:
+ 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:
+ g_message("TERMINATED");
+ polypsink->mainloop_api->quit(polypsink->mainloop_api, 1);
+ destroy_context(polypsink);
+ break;
+ }
+}
+
+static void context_state_callback(struct pa_context *c, void *userdata) {
+ GstPolypSink *polypsink = userdata;
+ g_assert(c && polypsink);
+
+/* g_message("context_state_callback()"); */
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ 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);
+
+ 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:
+ g_message("TERMINATED");
+ polypsink->mainloop_api->quit(polypsink->mainloop_api, 1);
+ destroy_context(polypsink);
+ break;
+ }
+}
+
+static void create_stream(GstPolypSink *polypsink) {
+ char t[256];
+ g_assert(polypsink);
+
+ if (polypsink->stream)
+ return;
+
+ if (!polypsink->context) {
+ create_context(polypsink);
+ return;
+ }
+
+ if (!polypsink->negotiated)
+ return;
+
+ if (pa_context_get_state(polypsink->context) != PA_CONTEXT_READY)
+ return;
+
+ g_message("creating stream");
+
+ pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec);
+
+ 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);
+}
+
+static void create_context(GstPolypSink *polypsink) {
+ g_assert(polypsink);
+
+ if (polypsink->context)
+ return;
+
+ g_message("creating context");
+
+ polypsink->context = pa_context_new(polypsink->mainloop_api, "gstreamer");
+ g_assert(polypsink->context);
+
+ pa_context_set_state_callback(polypsink->context, context_state_callback, polypsink);
+ pa_context_connect(polypsink->context, NULL);
+}
+
+static void destroy_stream(GstPolypSink *polypsink) {
+ g_assert(polypsink);
+
+ 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);
+
+ g_message("destroying stream");
+ }
+}
+
+static void destroy_context(GstPolypSink *polypsink) {
+ destroy_stream(polypsink);
+
+ if (polypsink->context) {
+ struct pa_context *c = polypsink->context;
+ polypsink->context = NULL;
+ pa_context_set_state_callback(c, NULL, NULL);
+ pa_context_unref(c);
+
+ g_message("destroying context");
+ }
+}
+
+static void gst_polypsink_chain(GstPad *pad, GstData *data) {
+ GstPolypSink *polypsink = GST_POLYPSINK(gst_pad_get_parent(pad));
+
+ g_assert(!polypsink->buffer);
+
+/* g_message("chain-entry"); */
+
+ if (GST_IS_EVENT(data)) {
+ GstEvent *event = GST_EVENT(data);
+
+ switch (GST_EVENT_TYPE(event)) {
+ case GST_EVENT_EOS:
+ g_message("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:
+ g_message("FLUSH");
+ if (polypsink->stream)
+ pa_operation_unref(pa_stream_flush(polypsink->stream, NULL, NULL));
+ break;
+
+ default:
+ g_message("other event: %i", GST_EVENT_TYPE(event));
+ 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);
+ }
+
+ while (polypsink->context && (pa_context_is_pending(polypsink->context) || polypsink->buffer)) {
+/* g_message("iterate start %i %i", !!pa_context_is_pending(polypsink->context), !!polypsink->buffer); */
+
+ if (pa_mainloop_iterate(polypsink->mainloop, 1, NULL) < 0)
+ return;
+
+/* g_message("iterate stop"); */
+ }
+
+/* g_message("chain-exit"); */
+}
+
+/* static void stream_get_latency_callback(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) { */
+/* GstPolypSink *polypsink = (GstPolypSink*) userdata; */
+
+/* polypsink->latency = i->buffer_usec + i->sink_usec; */
+/* } */
+
+/* static GstClockTime gst_polypsink_get_time(GstClock *clock, gpointer data) { */
+/* struct pa_operation *o; */
+/* GstPolypSink *polypsink = GST_POLYPSINK(data); */
+/* GstClockTime r, l; */
+
+/* if (!polypsink->stream || pa_stream_get_state(polypsink->stream) != PA_STREAM_READY) */
+/* return 0; */
+
+/* polypsink->latency = 0; */
+
+/* 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; */
+/* } */
+
+/* r = ((GstClockTime) polypsink->counter / pa_frame_size(&polypsink->sample_spec))*GST_SECOND/polypsink->sample_spec.rate; */
+/* l = polypsink->latency*GST_USECOND; */
+
+/* return r > l ? r - l : 0; */
+/* } */
+
+/* static GstClock *gst_polypsink_get_clock(GstElement * element) { */
+/* GstPolypSink *polypsink = GST_POLYPSINK (element); */
+/* return GST_CLOCK (polypsink->provided_clock); */
+/* } */
+
+/* static void gst_polypsink_set_clock (GstElement * element, GstClock * clock) { */
+/* GstPolypSink *polypsink = GST_POLYPSINK (element); */
+/* polypsink->clock = clock; */
+/* } */
+
+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));
+
+ g_message("link: %p", polypsink);
+
+ 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);
+ g_message("using %s", t);
+
+ gst_structure_get_int(structure, "channels", (int*) &polypsink->sample_spec.channels);
+ gst_structure_get_int(structure, "rate", &polypsink->sample_spec.rate);
+
+ pa_sample_spec_snprint(t, sizeof(t), &polypsink->sample_spec);
+ g_message("using %s", t);
+
+ if (!pa_sample_spec_valid(&polypsink->sample_spec))
+ return GST_PAD_LINK_REFUSED;
+
+
+
+ polypsink->negotiated = 1;
+
+ destroy_stream(polypsink);
+
+ state = gst_element_get_state(GST_ELEMENT(polypsink));
+ if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)
+ create_stream(polypsink);
+
+ return GST_PAD_LINK_OK;
+}
+
+
+static void gst_polypsink_init(GTypeInstance* instance, gpointer g_class) {
+ GstPolypSink *polypsink = GST_POLYPSINK(instance);
+
+ g_message("created: %p", polypsink);
+
+ 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_FLAG_SET(polypsink, GST_ELEMENT_THREAD_SUGGESTED); */
+ GST_FLAG_SET(polypsink, GST_ELEMENT_EVENT_AWARE);
+
+ polypsink->context = NULL;
+ polypsink->stream = NULL;
+
+ polypsink->mainloop = pa_mainloop_new();
+ g_assert(polypsink->mainloop);
+
+ polypsink->mainloop_api = pa_mainloop_get_api(polypsink->mainloop);
+
+ polypsink->sample_spec.rate = 0;
+ polypsink->sample_spec.channels = 0;
+ polypsink->sample_spec.format = 0;
+
+ polypsink->negotiated = 0;
+
+ polypsink->buffer = NULL;
+ polypsink->buffer_index = 0;
+
+ polypsink->latency = 0;
+ polypsink->counter = 0;
+}
+
+static void gst_polypsink_dispose(GObject *object) {
+ GstPolypSink *polypsink = GST_POLYPSINK(object);
+
+/* gst_object_unparent(GST_OBJECT(polypsink->provided_clock)); */
+
+
+ destroy_context(polypsink);
+
+ if (polypsink->buffer)
+ gst_buffer_unref(polypsink->buffer);
+
+
+ g_free(polypsink->server);
+ g_free(polypsink->sink);
+
+ pa_mainloop_free(polypsink->mainloop);
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+
+ g_message("destroyed: %p", polypsink);
+}
+
+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);
+
+ 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));
+
+ 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; */
+}
+
+
+gboolean gst_polypsink_factory_init(GstPlugin *plugin) {
+ return gst_element_register(plugin, "polypsink", GST_RANK_NONE, GST_TYPE_POLYPSINK);
+}
+
+GType gst_polypsink_get_type(void) {
+ static GType polypsink_type = 0;
+
+ if (!polypsink_type) {
+
+ static const GTypeInfo polypsink_info = {
+ sizeof (GstPolypSinkClass),
+ gst_polypsink_base_init,
+ NULL,
+ gst_polypsink_class_init,
+ NULL,
+ NULL,
+ sizeof(GstPolypSink),
+ 0,
+ gst_polypsink_init,
+ };
+
+ polypsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPolypSink", &polypsink_info, 0);
+ }
+
+ return polypsink_type;
+}
diff --git a/src/polypsink.h b/src/polypsink.h
new file mode 100644
index 0000000..90b3b7e
--- /dev/null
+++ b/src/polypsink.h
@@ -0,0 +1,56 @@
+#ifndef __GST_POLYPSINK_H__
+#define __GST_POLYPSINK_H__
+
+#include <gst/gst.h>
+
+#include <polyp/polyplib-context.h>
+#include <polyp/polyplib-stream.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_POLYPSINK \
+ (gst_polypsink_get_type())
+#define GST_POLYPSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_POLYPSINK,GstPolypSink))
+#define GST_POLYPSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_POLYPSINK,GstPolypSinkClass))
+#define GST_IS_POLYPSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_POLYPSINK))
+#define GST_IS_POLYPSINK_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_POLYPSINK))
+
+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;
+
+ int negotiated;
+
+ GstBuffer *buffer;
+ size_t buffer_index;
+
+ size_t counter;
+ pa_usec_t latency;
+};
+
+struct _GstPolypSinkClass {
+ GstElementClass parent_class;
+};
+
+GType gst_polypsink_get_type(void);
+gboolean gst_polypsink_factory_init(GstPlugin *plugin);
+
+G_END_DECLS
+
+#endif /* __GST_POLYPSINK_H__ */