void-packages/srcpkgs/gst-plugins-base1/patches/sndio.patch

1135 lines
35 KiB
Diff

Source: OpenBSD ports, adapted to work with meson
Upstream: No
Reason: sndio support
diff --git a/ext/meson.build b/ext/meson.build
index d984b50..5f0becc 100644
--- a/ext/meson.build
+++ b/ext/meson.build
@@ -5,5 +5,6 @@ subdir('libvisual')
subdir('ogg')
subdir('opus')
subdir('pango')
+subdir('sndio')
subdir('theora')
subdir('vorbis')
diff --git a/ext/sndio/gstsndio.c b/ext/sndio/gstsndio.c
new file mode 100644
index 0000000..2321dc2
--- /dev/null
+++ b/ext/sndio/gstsndio.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include "gstsndio.h"
+
+GST_DEBUG_CATEGORY (gst_sndio_debug);
+#define GST_CAT_DEFAULT gst_sndio_debug
+
+GType gst_sndiosink_get_type (void);
+GType gst_sndiosrc_get_type (void);
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (gst_sndio_debug, "sndio", 0, "sndio plugins");
+
+ /* prefer sndiosrc over pulsesrc (GST_RANK_PRIMARY + 10) */
+ if (!gst_element_register (plugin, "sndiosrc", GST_RANK_PRIMARY + 20,
+ gst_sndiosrc_get_type()))
+ return FALSE;
+ /* prefer sndiosink over pulsesink (GST_RANK_PRIMARY + 10) */
+ if (!gst_element_register (plugin, "sndiosink", GST_RANK_PRIMARY + 20,
+ gst_sndiosink_get_type()))
+ return FALSE;
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ sndio,
+ "sndio plugin library",
+ plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+
+/*
+ * common code to src and sink
+ */
+
+void
+gst_sndio_init (struct gstsndio *sio, GObject *obj)
+{
+ sio->obj = obj;
+ sio->hdl = NULL;
+ sio->device = g_strdup (SIO_DEVANY);
+}
+
+void
+gst_sndio_finalize (struct gstsndio *sio)
+{
+ gst_caps_replace (&sio->cur_caps, NULL);
+ g_free (sio->device);
+}
+
+GstCaps *
+gst_sndio_getcaps (struct gstsndio *sio, GstCaps * filter)
+{
+ if (sio->cur_caps == NULL) {
+ /* XXX */
+ GST_LOG_OBJECT (sio->obj, "getcaps called, returning template caps");
+ return NULL;
+ }
+
+ GST_LOG_OBJECT (sio->obj, "returning %" GST_PTR_FORMAT, sio->cur_caps);
+
+ if (filter) {
+ return gst_caps_intersect_full (filter,
+ sio->cur_caps, GST_CAPS_INTERSECT_FIRST);
+ } else {
+ return gst_caps_ref (sio->cur_caps);
+ }
+}
+
+static void
+gst_sndio_onvol (void *arg, unsigned int vol)
+{
+ struct gstsndio *sio = arg;
+ sio->volume = vol;
+ g_object_notify (G_OBJECT (sio->obj), "mute");
+ g_object_notify (G_OBJECT (sio->obj), "volume");
+}
+
+gboolean
+gst_sndio_open (struct gstsndio *sio, gint mode)
+{
+ GValue list = G_VALUE_INIT, item = G_VALUE_INIT;
+ GstStructure *s;
+ GstCaps *caps;
+ struct sio_enc *enc;
+ struct sio_cap cap;
+ char fmt[16];
+ int i, chan;
+
+ GST_DEBUG_OBJECT (sio->obj, "open");
+
+ sio->hdl = sio_open (sio->device, mode, 0);
+ if (sio->hdl == NULL) {
+ GST_ELEMENT_ERROR (sio->obj, RESOURCE, OPEN_READ_WRITE,
+ ("Couldn't open sndio device"), (NULL));
+ return FALSE;
+ }
+ sio->mode = mode;
+
+ if (!sio_getcap(sio->hdl, &cap)) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_WRITE,
+ ("Couldn't get device capabilities"), (NULL));
+ sio_close(sio->hdl);
+ sio->hdl = NULL;
+ return FALSE;
+ }
+ if (cap.nconf == 0) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_WRITE,
+ ("Device has empty capabilities"), (NULL));
+ sio_close(sio->hdl);
+ sio->hdl = NULL;
+ return FALSE;
+ }
+ sio_onvol (sio->hdl, gst_sndio_onvol, sio);
+
+ caps = gst_caps_new_empty ();
+ s = gst_structure_new ("audio/x-raw", (char *)NULL, (void *)NULL);
+
+ /*
+ * scan supported rates
+ */
+ g_value_init (&list, GST_TYPE_LIST);
+ g_value_init (&item, G_TYPE_INT);
+ for (i = 0; i < SIO_NRATE; i++) {
+ if ((cap.confs[0].rate & (1 << i)) == 0)
+ continue;
+ g_value_set_int(&item, cap.rate[i]);
+ gst_value_list_append_value (&list, &item);
+ }
+ gst_structure_set_value (s, "rate", &list);
+ g_value_unset (&item);
+ g_value_unset (&list);
+
+ /*
+ * scan supported channels
+ */
+ g_value_init (&list, GST_TYPE_LIST);
+ g_value_init (&item, G_TYPE_INT);
+ chan = (mode == SIO_PLAY) ? cap.confs[0].pchan : cap.confs[0].rchan;
+ for (i = 0; i < SIO_NCHAN; i++) {
+ if ((chan & (1 << i)) == 0)
+ continue;
+ g_value_set_int(&item, (mode == SIO_PLAY) ? cap.pchan[i] : cap.rchan[i]);
+ gst_value_list_append_value (&list, &item);
+ }
+ gst_structure_set_value (s, "channels", &list);
+ g_value_unset (&item);
+ g_value_unset (&list);
+
+ /*
+ * scan supported encodings
+ */
+ g_value_init (&list, GST_TYPE_LIST);
+ g_value_init (&item, G_TYPE_STRING);
+ for (i = 0; i < SIO_NENC; i++) {
+ if ((cap.confs[0].enc & (1 << i)) == 0)
+ continue;
+ enc = cap.enc + i;
+ if (enc->bits % 8 != 0)
+ continue;
+ if (enc->bits < enc->bps * 8 && enc->msb)
+ continue;
+ if (enc->bits == enc->bps * 8) {
+ snprintf(fmt, sizeof(fmt), "%s%u%s",
+ enc->sig ? "S" : "U",
+ enc->bits,
+ enc->bps > 1 ? (enc->le ? "LE" : "BE") : "");
+ } else {
+ snprintf(fmt, sizeof(fmt), "%s%u_%u%s",
+ enc->sig ? "S" : "U",
+ enc->bits,
+ enc->bps * 8,
+ enc->bps > 1 ? (enc->le ? "LE" : "BE") : "");
+ }
+ g_value_set_string(&item, fmt);
+ gst_value_list_append_value (&list, &item);
+ }
+ gst_structure_set_value (s, "format", &list);
+ g_value_unset (&item);
+ g_value_unset (&list);
+
+ /*
+ * add the only supported layout: interleaved
+ */
+ g_value_init (&item, G_TYPE_STRING);
+ g_value_set_string(&item, "interleaved");
+ gst_structure_set_value (s, "layout", &item);
+ g_value_unset (&item);
+
+ gst_caps_append_structure (caps, s);
+ sio->cur_caps = caps;
+ GST_DEBUG ("caps are %s", gst_caps_to_string(caps));
+ return TRUE;
+}
+
+gboolean
+gst_sndio_close (struct gstsndio *sio)
+{
+ GST_DEBUG_OBJECT (sio->obj, "close");
+
+ gst_caps_replace (&sio->cur_caps, NULL);
+ sio_close (sio->hdl);
+ sio->hdl = NULL;
+ return TRUE;
+}
+
+static void
+gst_sndio_cb (void *addr, int delta)
+{
+ struct gstsndio *sio = addr;
+
+ delta *= sio->bpf;
+ if (sio->mode == SIO_PLAY)
+ sio->delay -= delta;
+ else
+ sio->delay += delta;
+}
+
+gboolean
+gst_sndio_prepare (struct gstsndio *sio, GstAudioRingBufferSpec *spec)
+{
+ struct sio_par par, retpar;
+ unsigned nchannels;
+
+ GST_DEBUG_OBJECT (sio, "prepare");
+
+ if (spec->type != GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_READ_WRITE,
+ ("Only raw buffer format supported by sndio"), (NULL));
+ return FALSE;
+ }
+ if (!GST_AUDIO_INFO_IS_INTEGER(&spec->info)) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_READ_WRITE,
+ ("Only integer format supported"), (NULL));
+ return FALSE;
+ }
+ if (GST_AUDIO_INFO_DEPTH(&spec->info) % 8) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_READ_WRITE,
+ ("Only depths multiple of 8 are supported"), (NULL));
+ return FALSE;
+ }
+
+ sio_initpar (&par);
+ switch (GST_AUDIO_INFO_FORMAT (&spec->info)) {
+ case GST_AUDIO_FORMAT_S8:
+ case GST_AUDIO_FORMAT_U8:
+ case GST_AUDIO_FORMAT_S16LE:
+ case GST_AUDIO_FORMAT_S16BE:
+ case GST_AUDIO_FORMAT_U16LE:
+ case GST_AUDIO_FORMAT_U16BE:
+ case GST_AUDIO_FORMAT_S32LE:
+ case GST_AUDIO_FORMAT_S32BE:
+ case GST_AUDIO_FORMAT_U32LE:
+ case GST_AUDIO_FORMAT_U32BE:
+ case GST_AUDIO_FORMAT_S24_32LE:
+ case GST_AUDIO_FORMAT_S24_32BE:
+ case GST_AUDIO_FORMAT_U24_32LE:
+ case GST_AUDIO_FORMAT_U24_32BE:
+ case GST_AUDIO_FORMAT_S24LE:
+ case GST_AUDIO_FORMAT_S24BE:
+ case GST_AUDIO_FORMAT_U24LE:
+ case GST_AUDIO_FORMAT_U24BE:
+ break;
+ default:
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_READ_WRITE,
+ ("Unsupported audio format"),
+ ("format = %d", GST_AUDIO_INFO_FORMAT (&spec->info)));
+ return FALSE;
+ }
+ par.sig = GST_AUDIO_INFO_IS_SIGNED(&spec->info);
+ par.bits = GST_AUDIO_INFO_WIDTH(&spec->info);
+ par.bps = GST_AUDIO_INFO_DEPTH(&spec->info) / 8;
+ if (par.bps > 1)
+ par.le = GST_AUDIO_INFO_IS_LITTLE_ENDIAN(&spec->info);
+ if (par.bits < par.bps * 8)
+ par.msb = 0;
+ par.rate = GST_AUDIO_INFO_RATE(&spec->info);
+ if (sio->mode == SIO_PLAY)
+ par.pchan = GST_AUDIO_INFO_CHANNELS(&spec->info);
+ else
+ par.rchan = GST_AUDIO_INFO_CHANNELS(&spec->info);
+ par.round = par.rate / 1000000. * spec->latency_time;
+ par.appbufsz = par.rate / 1000000. * spec->buffer_time;
+
+ if (!sio_setpar (sio->hdl, &par)) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_WRITE,
+ ("Unsupported audio encoding"), (NULL));
+ return FALSE;
+ }
+ if (!sio_getpar (sio->hdl, &retpar)) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_WRITE,
+ ("Couldn't get audio device parameters"), (NULL));
+ return FALSE;
+ }
+#if 0
+ GST_DEBUG ("format = %s, "
+ "requested: sig = %d, bits = %d, bps = %d, le = %d, msb = %d, "
+ "rate = %d, pchan = %d, round = %d, appbufsz = %d; "
+ "returned: sig = %d, bits = %d, bps = %d, le = %d, msb = %d, "
+ "rate = %d, pchan = %d, round = %d, appbufsz = %d, bufsz = %d",
+ GST_AUDIO_INFO_NAME(&spec->info),
+ par.sig, par.bits, par.bps, par.le, par.msb,
+ par.rate, par.pchan, par.round, par.appbufsz,
+ retpar.sig, retpar.bits, retpar.bps, retpar.le, retpar.msb,
+ retpar.rate, retpar.pchan, retpar.round, retpar.appbufsz, retpar.bufsz);
+#endif
+ if (par.bits != retpar.bits ||
+ par.bps != retpar.bps ||
+ par.rate != retpar.rate ||
+ (sio->mode == SIO_PLAY && par.pchan != retpar.pchan) ||
+ (sio->mode == SIO_REC && par.rchan != retpar.rchan) ||
+ (par.bps > 1 && par.le != retpar.le) ||
+ (par.bits < par.bps * 8 && par.msb != retpar.msb)) {
+ GST_ELEMENT_ERROR (sio, RESOURCE, OPEN_WRITE,
+ ("Audio device refused requested parameters"), (NULL));
+ return FALSE;
+ }
+
+ nchannels = (sio->mode == SIO_PLAY) ? retpar.pchan : retpar.rchan;
+ spec->segsize = retpar.round * retpar.bps * nchannels;
+ spec->segtotal = retpar.bufsz / retpar.round;
+ sio->bpf = retpar.bps * nchannels;
+ sio->delay = 0;
+ sio_onmove (sio->hdl, gst_sndio_cb, sio);
+
+ if (!sio_start (sio->hdl)) {
+ GST_ELEMENT_ERROR (sio->obj, RESOURCE, OPEN_READ_WRITE,
+ ("Could not start sndio"), (NULL));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+gst_sndio_unprepare (struct gstsndio *sio)
+{
+ if (sio->hdl)
+ sio_stop (sio->hdl);
+ return TRUE;
+}
+
+void
+gst_sndio_set_property (struct gstsndio *sio, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_free (sio->device);
+ sio->device = g_value_dup_string (value);
+ break;
+ case PROP_VOLUME:
+ sio_setvol (sio->hdl, g_value_get_double (value) * SIO_MAXVOL);
+ break;
+ case PROP_MUTE:
+ if (g_value_get_boolean (value))
+ sio_setvol (sio->hdl, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+gst_sndio_get_property (struct gstsndio *sio, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string (value, sio->device);
+ break;
+ case PROP_VOLUME:
+ g_value_set_double (value, (gdouble)sio->volume / SIO_MAXVOL);
+ break;
+ case PROP_MUTE:
+ g_value_set_boolean (value, (sio->volume == 0));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (sio->obj, prop_id, pspec);
+ }
+}
diff --git a/ext/sndio/gstsndio.h b/ext/sndio/gstsndio.h
new file mode 100644
index 0000000..77ec4e6
--- /dev/null
+++ b/ext/sndio/gstsndio.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __GST_SNDIO_H__
+#define __GST_SNDIO_H__
+
+#include <sndio.h>
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosink.h>
+#include <gst/audio/gstaudiosrc.h>
+#include <gst/audio/streamvolume.h>
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_VOLUME,
+ PROP_MUTE
+};
+
+#define GST_SNDIO_CAPS_STRING \
+ "audio/x-raw, " \
+ "format = (string) { " \
+ "S8, U8, " \
+ "S16LE, S16BE, U16LE, U16BE, " \
+ "S32LE, S32BE, U32LE, U32BE, " \
+ "S24_32LE, S24_32BE, U24_32LE, " \
+ "U24_32BE, S24LE, S24BE, U24LE, U24BE " \
+ "}, " \
+ "layout = (string) interleaved, " \
+ "rate = (int) [ 8000, 192000 ], " \
+ "channels = (int) [1, 16]"
+
+/*
+ * data common to src and sink
+ */
+struct gstsndio {
+ struct sio_hdl *hdl;
+ gchar *device;
+ gint mode;
+ gint bpf; /* bytes per frame */
+ gint delay; /* bytes stored in the audio fifo */
+ guint volume; /* volume level */
+ GstCaps *cur_caps; /* saved capabilities of opened device */
+ GObject *obj; /* for logging */
+};
+
+#define GST_SNDIO_DELAY(s) ((s)->delay / (s)->bpf)
+
+void gst_sndio_init (struct gstsndio *sio, GObject *obj);
+void gst_sndio_finalize (struct gstsndio *sio);
+GstCaps *gst_sndio_getcaps (struct gstsndio *sio, GstCaps * filter);
+gboolean gst_sndio_open (struct gstsndio *sio, gint mode);
+gboolean gst_sndio_close (struct gstsndio *sio);
+gboolean gst_sndio_prepare (struct gstsndio *sio, GstAudioRingBufferSpec *spec);
+gboolean gst_sndio_unprepare (struct gstsndio *sio);
+void gst_sndio_set_property (struct gstsndio *sio, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+void gst_sndio_get_property (struct gstsndio *sio, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+int gst_sndio_setpar(gpointer sio, GstAudioRingBufferSpec * spec,
+ int mode, struct sio_hdl *hdl);
+GstCaps *gst_sndio_caps (gpointer sio, int mode, struct sio_hdl *hdl);
+
+#endif
diff --git a/ext/sndio/meson.build b/ext/sndio/meson.build
new file mode 100644
index 0000000..3f9a9fe
--- /dev/null
+++ b/ext/sndio/meson.build
@@ -0,0 +1,21 @@
+sndio_sources = [
+ 'gstsndio.c',
+ 'sndiosink.c',
+ 'sndiosrc.c',
+]
+
+cc = meson.get_compiler('c')
+
+sndio_dep = cc.find_library('sndio', required : get_option('sndio'))
+
+if sndio_dep.found()
+ gstsndio = library('gstsndio',
+ sndio_sources,
+ c_args : gst_plugins_base_args,
+ include_directories: [configinc, libsinc],
+ dependencies: [gst_base_dep, sndio_dep, audio_dep],
+ install : true,
+ install_dir : plugins_install_dir,
+ )
+ pkgconfig.generate(gstsndio, install_dir : plugins_pkgconfig_install_dir)
+endif
diff --git a/ext/sndio/sndiosink.c b/ext/sndio/sndiosink.c
new file mode 100644
index 0000000..cc68bd2
--- /dev/null
+++ b/ext/sndio/sndiosink.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * SECTION:element-sndiosink
+ * @see_also: #GstAutoAudioSink
+ *
+ * <refsect2>
+ * <para>
+ * This element outputs sound to a sound card using sndio.
+ * </para>
+ * <para>
+ * Simple example pipeline that plays an Ogg/Vorbis file via sndio:
+ * <programlisting>
+ * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! audioresample ! sndiosink
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sndiosink.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_sndio_debug);
+#define GST_CAT_DEFAULT gst_sndio_debug
+
+#define gst_sndiosink_parent_class parent_class
+
+static GstStaticPadTemplate sndiosink_factory =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_SNDIO_CAPS_STRING)
+ );
+
+G_DEFINE_TYPE_WITH_CODE (GstSndioSink, gst_sndiosink, GST_TYPE_AUDIO_SINK,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL));
+
+static void gst_sndiosink_finalize (GObject * object);
+static GstCaps *gst_sndiosink_getcaps (GstBaseSink * bsink,
+ GstCaps * filter);
+static gboolean gst_sndiosink_open (GstAudioSink * asink);
+static gboolean gst_sndiosink_close (GstAudioSink * asink);
+static gboolean gst_sndiosink_prepare (GstAudioSink * asink,
+ GstAudioRingBufferSpec * spec);
+static gboolean gst_sndiosink_unprepare (GstAudioSink * asink);
+static gint gst_sndiosink_write (GstAudioSink * asink, gpointer data,
+ guint length);
+static guint gst_sndiosink_delay (GstAudioSink * asink);
+static void gst_sndiosink_reset (GstAudioSink * asink);
+static void gst_sndiosink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_sndiosink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void
+gst_sndiosink_init (GstSndioSink * sink)
+{
+ gst_sndio_init (&sink->sndio, G_OBJECT(sink));
+}
+
+static void
+gst_sndiosink_finalize (GObject * object)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (object);
+
+ gst_sndio_finalize (&sink->sndio);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstCaps *
+gst_sndiosink_getcaps (GstBaseSink * bsink, GstCaps * filter)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (bsink);
+
+ return gst_sndio_getcaps (&sink->sndio, filter);
+}
+
+static gboolean
+gst_sndiosink_open (GstAudioSink * asink)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+
+ return gst_sndio_open (&sink->sndio, SIO_PLAY);
+}
+
+static gboolean
+gst_sndiosink_close (GstAudioSink * asink)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+
+ return gst_sndio_close (&sink->sndio);
+}
+
+static gboolean
+gst_sndiosink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+
+ return gst_sndio_prepare (&sink->sndio, spec);
+}
+
+static gboolean
+gst_sndiosink_unprepare (GstAudioSink * asink)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+
+ return gst_sndio_unprepare (&sink->sndio);
+}
+
+static gint
+gst_sndiosink_write (GstAudioSink * asink, gpointer data, guint length)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+ guint done;
+
+ if (length == 0)
+ return 0;
+ done = sio_write (sink->sndio.hdl, data, length);
+ if (done == 0) {
+ GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
+ ("Failed to write data to sndio"), (NULL));
+ return 0;
+ }
+ sink->sndio.delay += done;
+ return done;
+}
+
+static guint
+gst_sndiosink_delay (GstAudioSink * asink)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (asink);
+
+ return GST_SNDIO_DELAY(&sink->sndio);
+}
+
+static void
+gst_sndiosink_reset (GstAudioSink * asink)
+{
+}
+
+static void
+gst_sndiosink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (object);
+
+ gst_sndio_set_property (&sink->sndio, prop_id, value, pspec);
+}
+
+static void
+gst_sndiosink_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstSndioSink *sink = GST_SNDIOSINK (object);
+
+ gst_sndio_get_property (&sink->sndio, prop_id, value, pspec);
+}
+
+static void
+gst_sndiosink_class_init (GstSndioSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+ GstAudioBaseSinkClass *gstbaseaudiosink_class;
+ GstAudioSinkClass *gstaudiosink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+ gstbaseaudiosink_class = (GstAudioBaseSinkClass *) klass;
+ gstaudiosink_class = (GstAudioSinkClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gst_sndiosink_finalize;
+ gobject_class->get_property = gst_sndiosink_get_property;
+ gobject_class->set_property = gst_sndiosink_set_property;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "Audio sink (sndio)", "Sink/Audio",
+ "Output to a sound card via sndio",
+ "Jacob Meuser <jakemsr@sdf.lonestar.org>");
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sndiosink_factory));
+
+ gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_sndiosink_getcaps);
+ gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_sndiosink_open);
+ gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_sndiosink_prepare);
+ gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_sndiosink_unprepare);
+ gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_sndiosink_close);
+ gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_sndiosink_write);
+ gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_sndiosink_delay);
+ gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_sndiosink_reset);
+
+ g_object_class_install_property (gobject_class, PROP_DEVICE,
+ g_param_spec_string ("device", "Device",
+ "sndio device as defined in sndio(7)",
+ SIO_DEVANY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_VOLUME,
+ g_param_spec_double ("volume", "Volume",
+ "Linear volume of this stream, 1.0=100%", 0.0, 1.0,
+ 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_MUTE,
+ g_param_spec_boolean ("mute", "Mute",
+ "Mute state of this stream", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
diff --git a/ext/sndio/sndiosink.h b/ext/sndio/sndiosink.h
new file mode 100644
index 0000000..314e622
--- /dev/null
+++ b/ext/sndio/sndiosink.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef __GST_SNDIOSINK_H__
+#define __GST_SNDIOSINK_H__
+
+#include <sndio.h>
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosink.h>
+#include "gstsndio.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SNDIOSINK \
+ (gst_sndiosink_get_type())
+#define GST_SNDIOSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SNDIOSINK,GstSndioSink))
+#define GST_SNDIOSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SNDIOSINK,GstSndioSinkClass))
+#define GST_IS_SNDIOSINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SNDIOSINK))
+#define GST_IS_SNDIOSINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SNDIOSINK))
+
+typedef struct _GstSndioSink GstSndioSink;
+typedef struct _GstSndioSinkClass GstSndioSinkClass;
+
+struct _GstSndioSink {
+ GstAudioSink sink;
+ struct gstsndio sndio;
+};
+
+struct _GstSndioSinkClass {
+ GstAudioSinkClass parent_class;
+};
+
+GType gst_sndiosink_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_SNDIOSINK_H__ */
diff --git a/ext/sndio/sndiosrc.c b/ext/sndio/sndiosrc.c
new file mode 100644
index 0000000..5b6d787
--- /dev/null
+++ b/ext/sndio/sndiosrc.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * SECTION:element-sndiosrc
+ * @see_also: #GstAutoAudioSrc
+ *
+ * <refsect2>
+ * <para>
+ * This element retrieves samples from a sound card using sndio.
+ * </para>
+ * <para>
+ * Simple example pipeline that records an Ogg/Vorbis file via sndio:
+ * <programlisting>
+ * gst-launch -v sndiosrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=foo.ogg
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sndiosrc.h"
+#include "gstsndio.h"
+
+GST_DEBUG_CATEGORY_EXTERN (gst_sndio_debug);
+#define GST_CAT_DEFAULT gst_sndio_debug
+
+#define gst_sndiosrc_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstSndioSrc, gst_sndiosrc, GST_TYPE_AUDIO_SRC,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL));
+
+static void gst_sndiosrc_finalize (GObject * object);
+static GstCaps *gst_sndiosrc_getcaps (GstBaseSrc * bsrc, GstCaps * filter);
+static gboolean gst_sndiosrc_open (GstAudioSrc * asrc);
+static gboolean gst_sndiosrc_close (GstAudioSrc * asrc);
+static gboolean gst_sndiosrc_prepare (GstAudioSrc * asrc,
+ GstAudioRingBufferSpec * spec);
+static gboolean gst_sndiosrc_unprepare (GstAudioSrc * asrc);
+static guint gst_sndiosrc_read (GstAudioSrc * asrc, gpointer data,
+ guint length);
+static guint gst_sndiosrc_delay (GstAudioSrc * asrc);
+static void gst_sndiosrc_reset (GstAudioSrc * asrc);
+static void gst_sndiosrc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_sndiosrc_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstStaticPadTemplate sndiosrc_factory =
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_SNDIO_CAPS_STRING)
+ );
+
+static void
+gst_sndiosrc_init (GstSndioSrc * src)
+{
+ gst_sndio_init (&src->sndio, G_OBJECT(src));
+}
+
+static void
+gst_sndiosrc_finalize (GObject * object)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (object);
+
+ gst_sndio_finalize (&src->sndio);
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstCaps *
+gst_sndiosrc_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (bsrc);
+
+ return gst_sndio_getcaps (&src->sndio, filter);
+}
+
+static gboolean
+gst_sndiosrc_open (GstAudioSrc * asrc)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+
+ return gst_sndio_open (&src->sndio, SIO_REC);
+}
+
+static gboolean
+gst_sndiosrc_close (GstAudioSrc * asrc)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+
+ return gst_sndio_close (&src->sndio);
+}
+
+static gboolean
+gst_sndiosrc_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+
+ return gst_sndio_prepare (&src->sndio, spec);
+}
+
+static gboolean
+gst_sndiosrc_unprepare (GstAudioSrc * asrc)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+
+ return gst_sndio_unprepare (&src->sndio);
+}
+
+static guint
+gst_sndiosrc_read (GstAudioSrc * asrc, gpointer data, guint length)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+ guint done;
+
+ if (length == 0)
+ return 0;
+ done = sio_read (src->sndio.hdl, data, length);
+ if (done == 0) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ,
+ ("Failed to read data from sndio"), (NULL));
+ return 0;
+ }
+ src->sndio.delay -= done;
+ return done;
+}
+
+static guint
+gst_sndiosrc_delay (GstAudioSrc * asrc)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (asrc);
+
+ return GST_SNDIO_DELAY(&src->sndio);
+}
+
+static void
+gst_sndiosrc_reset (GstAudioSrc * asrc)
+{
+}
+
+static void
+gst_sndiosrc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (object);
+
+ gst_sndio_set_property (&src->sndio, prop_id, value, pspec);
+}
+
+static void
+gst_sndiosrc_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstSndioSrc *src = GST_SNDIOSRC (object);
+
+ gst_sndio_get_property (&src->sndio, prop_id, value, pspec);
+}
+
+static void
+gst_sndiosrc_class_init (GstSndioSrcClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSrcClass *gstbasesrc_class;
+ GstAudioBaseSrcClass *gstbaseaudiosrc_class;
+ GstAudioSrcClass *gstaudiosrc_class;
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesrc_class = (GstBaseSrcClass *) klass;
+ gstbaseaudiosrc_class = (GstAudioBaseSrcClass *) klass;
+ gstaudiosrc_class = (GstAudioSrcClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gst_sndiosrc_finalize;
+ gobject_class->get_property = gst_sndiosrc_get_property;
+ gobject_class->set_property = gst_sndiosrc_set_property;
+
+ gst_element_class_set_static_metadata (gstelement_class,
+ "Audio src (sndio)", "Src/Audio",
+ "Input from a sound card via sndio",
+ "Jacob Meuser <jakemsr@sdf.lonestar.org>");
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sndiosrc_factory));
+
+ gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_sndiosrc_getcaps);
+ gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_sndiosrc_open);
+ gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_sndiosrc_prepare);
+ gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_sndiosrc_unprepare);
+ gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_sndiosrc_close);
+ gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_sndiosrc_read);
+ gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_sndiosrc_delay);
+ gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_sndiosrc_reset);
+
+ g_object_class_install_property (gobject_class, PROP_DEVICE,
+ g_param_spec_string ("device", "Device",
+ "sndio device as defined in sndio(7)",
+ SIO_DEVANY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_VOLUME,
+ g_param_spec_double ("volume", "Volume",
+ "Linear volume of this stream, 1.0=100%", 0.0, 1.0,
+ 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_MUTE,
+ g_param_spec_boolean ("mute", "Mute",
+ "Mute state of this stream", FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
diff --git a/ext/sndio/sndiosrc.h b/ext/sndio/sndiosrc.h
new file mode 100644
index 0000000..83fd29c
--- /dev/null
+++ b/ext/sndio/sndiosrc.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
+ * Copyright (C) 2012 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef __GST_SNDIOSRC_H__
+#define __GST_SNDIOSRC_H__
+
+#include <sndio.h>
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosrc.h>
+#include "gstsndio.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SNDIOSRC \
+ (gst_sndiosrc_get_type())
+#define GST_SNDIOSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SNDIOSRC,GstSndioSrc))
+#define GST_SNDIOSRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SNDIOSRC,GstSndioSrcClass))
+#define GST_IS_SNDIOSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SNDIOSRC))
+#define GST_IS_SNDIOSRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SNDIOSRC))
+
+typedef struct _GstSndioSrc GstSndioSrc;
+typedef struct _GstSndioSrcClass GstSndioSrcClass;
+
+struct _GstSndioSrc {
+ GstAudioSrc src;
+ struct gstsndio sndio;
+};
+
+struct _GstSndioSrcClass {
+ GstAudioSrcClass parent_class;
+};
+
+GType gst_sndiosrc_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_SNDIOSRC_H__ */
diff --git a/meson_options.txt b/meson_options.txt
index e7af4dd..827507f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -58,6 +58,7 @@ option('libvisual', type : 'feature', value : 'auto', description : 'libvisual a
option('ogg', type : 'feature', value : 'auto', description : 'ogg parser, muxer, demuxer plugin')
option('opus', type : 'feature', value : 'auto', description : 'OPUS audio codec plugin')
option('pango', type : 'feature', value : 'auto', description : 'Pango text rendering and overlay plugin')
+option('sndio', type : 'feature', value : 'auto', description : 'sndio support')
option('theora', type : 'feature', value : 'auto', description : 'Theora video parser and codec plugin')
option('tremor', type : 'feature', value : 'auto', description : 'Integer Vorbis decoder plugin for devices without floating point')
option('vorbis', type : 'feature', value : 'auto', description : 'Vorbis audio parser, tagger, and codec plugin')