From c0b67627b12536efe6bac5a065f1e6b0a5d39c72 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 10 Sep 2004 13:31:06 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/gst-pulse/trunk@3 bb39ca4e-bce3-0310-b5d4-eea78a553289 --- Makefile.am | 42 ++++ bootstrap.sh | 47 +++++ configure.ac | 90 +++++++++ src/Makefile.am | 16 ++ src/nullsink.c | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/plugin.c | 25 +++ src/polypsink.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/polypsink.h | 56 ++++++ 8 files changed, 1495 insertions(+) create mode 100644 Makefile.am create mode 100755 bootstrap.sh create mode 100644 configure.ac create mode 100644 src/Makefile.am create mode 100644 src/nullsink.c create mode 100644 src/plugin.c create mode 100644 src/polypsink.c create mode 100644 src/polypsink.h 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 +#include +#include + +#include +#include + +#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 +#include +#include + +#include +#include + +#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 + +#include +#include + +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__ */ -- cgit