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 + * Copyright (C) 2012 Alexandre Ratchov + * + * 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 +#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 + * + * 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 +#include +#include +#include +#include + +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 + * Copyright (C) 2012 Alexandre Ratchov + * + * 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 + * + * + * + * This element outputs sound to a sound card using sndio. + * + * + * Simple example pipeline that plays an Ogg/Vorbis file via sndio: + * + * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! audioresample ! sndiosink + * + * + * + */ + +#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 "); + + 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 + * Copyright (C) 2012 Alexandre Ratchov + * + * 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 + +#include +#include +#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 + * Copyright (C) 2012 Alexandre Ratchov + * + * 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 + * + * + * + * This element retrieves samples from a sound card using sndio. + * + * + * Simple example pipeline that records an Ogg/Vorbis file via sndio: + * + * gst-launch -v sndiosrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=foo.ogg + * + * + * + */ + +#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 "); + + 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 + * Copyright (C) 2012 Alexandre Ratchov + * + * 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 + +#include +#include +#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')