summaryrefslogtreecommitdiffstats
path: root/ext/polyp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/polyp')
-rw-r--r--ext/polyp/Makefile.am9
-rw-r--r--ext/polyp/plugin.c26
-rw-r--r--ext/polyp/polypsink.c694
-rw-r--r--ext/polyp/polypsink.h59
4 files changed, 788 insertions, 0 deletions
diff --git a/ext/polyp/Makefile.am b/ext/polyp/Makefile.am
new file mode 100644
index 00000000..e8a5ae51
--- /dev/null
+++ b/ext/polyp/Makefile.am
@@ -0,0 +1,9 @@
+
+plugin_LTLIBRARIES = libpolypaudio.la
+
+libpolypaudio_la_SOURCES = plugin.c polypsink.c
+libpolypaudio_la_CFLAGS = $(GST_CFLAGS)
+libpolypaudio_la_LIBADD = $(GST_LIBS) $(POLYP_LIBS)
+libpolypaudio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = polypsink.h
diff --git a/ext/polyp/plugin.c b/ext/polyp/plugin.c
new file mode 100644
index 00000000..aea78e24
--- /dev/null
+++ b/ext/polyp/plugin.c
@@ -0,0 +1,26 @@
+#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/ext/polyp/polypsink.c b/ext/polyp/polypsink.c
new file mode 100644
index 00000000..9719a23e
--- /dev/null
+++ b/ext/polyp/polypsink.c
@@ -0,0 +1,694 @@
+/*
+ * 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
+ */
+
+#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) [ 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",
+ };
+
+ 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_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_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstElementStateReturn
+gst_polypsink_change_state (GstElement * element)
+{
+ GstPolypSink *polypsink;
+
+ polypsink = GST_POLYPSINK (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:
+
+ 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:
+
+ 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);
+
+ 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;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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;
+
+ 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,
+ PA_STREAM_INTERPOLATE_LATENCY, PA_VOLUME_NORM);
+
+ while (polypsink->context && pa_context_is_pending (polypsink->context)) {
+ if (pa_mainloop_iterate (polypsink->mainloop, 1, NULL) < 0)
+ return;
+ }
+}
+
+static void
+create_context (GstPolypSink * polypsink)
+{
+ g_assert (polypsink);
+
+ if (polypsink->context)
+ return;
+
+ 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, 1, 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);
+ }
+}
+
+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);
+ }
+}
+
+static void
+gst_polypsink_chain (GstPad * pad, GstData * data)
+{
+ GstPolypSink *polypsink = GST_POLYPSINK (gst_pad_get_parent (pad));
+
+ g_assert (!polypsink->buffer);
+
+ 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);
+ }
+
+ while (polypsink->context && (pa_context_is_pending (polypsink->context)
+ || polypsink->buffer)) {
+ if (pa_mainloop_iterate (polypsink->mainloop, 1, NULL) < 0)
+ return;
+ }
+
+}
+
+#if 0
+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;
+}
+
+#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);
+
+ 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);
+
+ 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 GstCaps *
+gst_polypsink_sink_fixate (GstPad * pad, const GstCaps * caps)
+{
+ GstCaps *newcaps;
+ GstStructure *structure;
+
+ newcaps =
+ gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (caps, 0)),
+ NULL);
+ structure = gst_caps_get_structure (newcaps, 0);
+
+ 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;
+}
+
+static void
+gst_polypsink_init (GTypeInstance * instance, gpointer g_class)
+{
+ GstPolypSink *polypsink = GST_POLYPSINK (instance);
+
+ 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;
+
+ 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);
+}
+
+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/ext/polyp/polypsink.h b/ext/polyp/polypsink.h
new file mode 100644
index 00000000..3bcb454d
--- /dev/null
+++ b/ext/polyp/polypsink.h
@@ -0,0 +1,59 @@
+#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;
+
+ gboolean caching;
+ char *cache_id;
+};
+
+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__ */