mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
sndio: add sndio plugin for OpenBSD
This commit is contained in:
parent
4d1bd12ff8
commit
18b0bfc1f1
8 changed files with 1282 additions and 0 deletions
15
configure.ac
15
configure.ac
|
@ -1839,6 +1839,19 @@ AG_GST_CHECK_FEATURE(GSETTINGS, [GSettings plugin], gsettings, [
|
||||||
fi
|
fi
|
||||||
])
|
])
|
||||||
|
|
||||||
|
dnl *** sndio ***
|
||||||
|
translit(dnm, m, l) AM_CONDITIONAL(USE_SNDIO, true)
|
||||||
|
AG_GST_CHECK_FEATURE(SNDIO, [sndio audio], sndio, [
|
||||||
|
AC_CHECK_HEADER(sndio.h, HAVE_SNDIO="yes", HAVE_SNDIO="no")
|
||||||
|
if test "x$HAVE_SNDIO" = "xyes"; then
|
||||||
|
AC_CHECK_LIB(sndio, sio_open, HAVE_SNDIO="yes", HAVE_SNDIO="no")
|
||||||
|
if test "x$HAVE_SNDIO" = "xyes"; then
|
||||||
|
SNDIO_LIBS=-lsndio
|
||||||
|
AC_SUBST(SNDIO_LIBS)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
])
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
dnl not building plugins with external dependencies,
|
dnl not building plugins with external dependencies,
|
||||||
|
@ -1910,6 +1923,7 @@ AM_CONDITIONAL(USE_ZBAR, false)
|
||||||
AM_CONDITIONAL(USE_VP8, false)
|
AM_CONDITIONAL(USE_VP8, false)
|
||||||
AM_CONDITIONAL(USE_RTMP, false)
|
AM_CONDITIONAL(USE_RTMP, false)
|
||||||
AM_CONDITIONAL(USE_TELETEXTDEC, false)
|
AM_CONDITIONAL(USE_TELETEXTDEC, false)
|
||||||
|
AM_CONDITIONAL(USE_SNDIO, false)
|
||||||
|
|
||||||
fi dnl of EXT plugins
|
fi dnl of EXT plugins
|
||||||
|
|
||||||
|
@ -2147,6 +2161,7 @@ ext/sdl/Makefile
|
||||||
ext/sndfile/Makefile
|
ext/sndfile/Makefile
|
||||||
ext/soundtouch/Makefile
|
ext/soundtouch/Makefile
|
||||||
ext/spandsp/Makefile
|
ext/spandsp/Makefile
|
||||||
|
ext/sndio/Makefile
|
||||||
ext/teletextdec/Makefile
|
ext/teletextdec/Makefile
|
||||||
ext/gme/Makefile
|
ext/gme/Makefile
|
||||||
ext/gsettings/Makefile
|
ext/gsettings/Makefile
|
||||||
|
|
|
@ -330,6 +330,12 @@ else
|
||||||
SNDFILE_DIR=
|
SNDFILE_DIR=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if USE_SNDIO
|
||||||
|
SNDIO_DIR = sndio
|
||||||
|
else
|
||||||
|
SNDIO_DIR =
|
||||||
|
endif
|
||||||
|
|
||||||
if USE_SOUNDTOUCH
|
if USE_SOUNDTOUCH
|
||||||
SOUNDTOUCH_DIR=soundtouch
|
SOUNDTOUCH_DIR=soundtouch
|
||||||
else
|
else
|
||||||
|
@ -452,6 +458,7 @@ SUBDIRS=\
|
||||||
$(SHOUT_DIR) \
|
$(SHOUT_DIR) \
|
||||||
$(SMOOTHWAVE_DIR) \
|
$(SMOOTHWAVE_DIR) \
|
||||||
$(SNDFILE_DIR) \
|
$(SNDFILE_DIR) \
|
||||||
|
$(SNDIO_DIR) \
|
||||||
$(SOUNDTOUCH_DIR) \
|
$(SOUNDTOUCH_DIR) \
|
||||||
$(SPANDSP_DIR) \
|
$(SPANDSP_DIR) \
|
||||||
$(GME_DIR) \
|
$(GME_DIR) \
|
||||||
|
@ -507,6 +514,7 @@ DIST_SUBDIRS = \
|
||||||
schroedinger \
|
schroedinger \
|
||||||
sdl \
|
sdl \
|
||||||
sndfile \
|
sndfile \
|
||||||
|
sndio \
|
||||||
soundtouch \
|
soundtouch \
|
||||||
spandsp \
|
spandsp \
|
||||||
spc \
|
spc \
|
||||||
|
|
11
ext/sndio/Makefile.am
Normal file
11
ext/sndio/Makefile.am
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
plugin_LTLIBRARIES = libgstsndio.la
|
||||||
|
|
||||||
|
libgstsndio_la_SOURCES = gstsndio.c sndiosink.c sndiosrc.c
|
||||||
|
libgstsndio_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||||
|
libgstsndio_la_LIBADD = \
|
||||||
|
$(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
|
||||||
|
$(SNDIO_LIBS)
|
||||||
|
libgstsndio_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
||||||
|
noinst_HEADERS = sndiosink.h sndiosrc.h
|
||||||
|
EXTRA_DIST =
|
51
ext/sndio/gstsndio.c
Normal file
51
ext/sndio/gstsndio.c
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) <2008> Jacob Meuser <jakemsr@sdf.lonestar.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 "sndiosink.h"
|
||||||
|
#include "sndiosrc.h"
|
||||||
|
|
||||||
|
#include "gst/gst-i18n-plugin.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY (gst_sndio_debug);
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
plugin_init (GstPlugin * plugin)
|
||||||
|
{
|
||||||
|
if (!gst_element_register (plugin, "sndiosrc", GST_RANK_PRIMARY,
|
||||||
|
GST_TYPE_SNDIOSRC) ||
|
||||||
|
!gst_element_register (plugin, "sndiosink", GST_RANK_PRIMARY,
|
||||||
|
GST_TYPE_SNDIOSINK)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (gst_sndio_debug, "sndio", 0, "sndio elements");
|
||||||
|
|
||||||
|
#ifdef ENABLE_NLS
|
||||||
|
setlocale (LC_ALL, "");
|
||||||
|
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
#endif /* ENABLE_NLS */
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||||
|
GST_VERSION_MINOR,
|
||||||
|
"sndio",
|
||||||
|
"sndio support for GStreamer",
|
||||||
|
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
532
ext/sndio/sndiosink.c
Normal file
532
ext/sndio/sndiosink.c
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) <2008> Jacob Meuser <jakemsr@sdf.lonestar.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"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <gst/gst-i18n-plugin.h>
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (gst_sndio_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_sndio_debug
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_HOST
|
||||||
|
};
|
||||||
|
|
||||||
|
static GstStaticPadTemplate sndio_sink_factory =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
GST_PAD_SINK,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS ("audio/x-raw-int, "
|
||||||
|
"endianness = (int) { 1234, 4321 }, "
|
||||||
|
"signed = (boolean) { TRUE, FALSE }, "
|
||||||
|
"width = (int) { 8, 16, 24, 32 }, "
|
||||||
|
"depth = (int) { 8, 16, 24, 32 }, "
|
||||||
|
"rate = (int) [ 8000, 192000 ], "
|
||||||
|
"channels = (int) [ 1, 16 ] ")
|
||||||
|
);
|
||||||
|
|
||||||
|
static void gst_sndiosink_finalize (GObject * object);
|
||||||
|
|
||||||
|
static GstCaps *gst_sndiosink_getcaps (GstBaseSink * bsink);
|
||||||
|
|
||||||
|
static gboolean gst_sndiosink_open (GstAudioSink * asink);
|
||||||
|
static gboolean gst_sndiosink_close (GstAudioSink * asink);
|
||||||
|
static gboolean gst_sndiosink_prepare (GstAudioSink * asink,
|
||||||
|
GstRingBufferSpec * spec);
|
||||||
|
static gboolean gst_sndiosink_unprepare (GstAudioSink * asink);
|
||||||
|
static guint 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_cb (void *addr, int delta);
|
||||||
|
|
||||||
|
GST_BOILERPLATE (GstSndioSink, gst_sndiosink, GstAudioSink,
|
||||||
|
GST_TYPE_AUDIO_SINK);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_base_init (gpointer g_class)
|
||||||
|
{
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||||
|
|
||||||
|
gst_element_class_set_details_simple (element_class,
|
||||||
|
"Sndio audio sink",
|
||||||
|
"Sink/Audio",
|
||||||
|
"Plays audio through sndio",
|
||||||
|
"Jacob Meuser <jakemsr@sdf.lonestar.org>");
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_static_pad_template_get (&sndio_sink_factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_class_init (GstSndioSinkClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstBaseSinkClass *gstbasesink_class;
|
||||||
|
GstBaseAudioSinkClass *gstbaseaudiosink_class;
|
||||||
|
GstAudioSinkClass *gstaudiosink_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||||
|
gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
|
||||||
|
gstaudiosink_class = (GstAudioSinkClass *) klass;
|
||||||
|
|
||||||
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_sndiosink_finalize;
|
||||||
|
|
||||||
|
gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_sndiosink_getcaps);
|
||||||
|
|
||||||
|
gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_sndiosink_open);
|
||||||
|
gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_sndiosink_close);
|
||||||
|
gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_sndiosink_prepare);
|
||||||
|
gstaudiosink_class->unprepare =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_sndiosink_unprepare);
|
||||||
|
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);
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_sndiosink_set_property;
|
||||||
|
gobject_class->get_property = gst_sndiosink_get_property;
|
||||||
|
|
||||||
|
/* default value is filled in the _init method */
|
||||||
|
g_object_class_install_property (gobject_class, PROP_HOST,
|
||||||
|
g_param_spec_string ("host", "Host",
|
||||||
|
"Device or socket sndio will access", NULL, G_PARAM_READWRITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_init (GstSndioSink * sndiosink,
|
||||||
|
GstSndioSinkClass * klass)
|
||||||
|
{
|
||||||
|
sndiosink->hdl = NULL;
|
||||||
|
sndiosink->host = g_strdup (g_getenv ("AUDIODEVICE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (object);
|
||||||
|
|
||||||
|
gst_caps_replace (&sndiosink->cur_caps, NULL);
|
||||||
|
g_free (sndiosink->host);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_sndiosink_getcaps (GstBaseSink * bsink)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink;
|
||||||
|
|
||||||
|
sndiosink = GST_SNDIOSINK (bsink);
|
||||||
|
|
||||||
|
/* no hdl, we're done with the template caps */
|
||||||
|
if (sndiosink->cur_caps == NULL) {
|
||||||
|
GST_LOG_OBJECT (sndiosink, "getcaps called, returning template caps");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (sndiosink, "returning %" GST_PTR_FORMAT,
|
||||||
|
sndiosink->cur_caps);
|
||||||
|
|
||||||
|
return gst_caps_ref (sndiosink->cur_caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosink_open (GstAudioSink * asink)
|
||||||
|
{
|
||||||
|
GstPadTemplate *pad_template;
|
||||||
|
GstSndioSink *sndiosink;
|
||||||
|
struct sio_par par;
|
||||||
|
struct sio_cap cap;
|
||||||
|
GArray *rates, *chans;
|
||||||
|
GValue rates_v = { 0 };
|
||||||
|
GValue chans_v = { 0 };
|
||||||
|
GValue value = { 0 };
|
||||||
|
struct sio_enc enc;
|
||||||
|
struct sio_conf conf;
|
||||||
|
int confs[SIO_NCONF];
|
||||||
|
int rate, chan;
|
||||||
|
int i, j, k;
|
||||||
|
int nconfs;
|
||||||
|
|
||||||
|
sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosink, "open");
|
||||||
|
|
||||||
|
/* conect */
|
||||||
|
sndiosink->hdl = sio_open (sndiosink->host, SIO_PLAY, 0);
|
||||||
|
|
||||||
|
if (sndiosink->hdl == NULL)
|
||||||
|
goto couldnt_connect;
|
||||||
|
|
||||||
|
/* Use sndio defaults as the only encodings, but get the supported
|
||||||
|
* sample rates and number of channels.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!sio_getpar (sndiosink->hdl, &par))
|
||||||
|
goto no_server_info;
|
||||||
|
|
||||||
|
if (!sio_getcap (sndiosink->hdl, &cap))
|
||||||
|
goto no_server_info;
|
||||||
|
|
||||||
|
rates = g_array_new (FALSE, FALSE, sizeof (int));
|
||||||
|
chans = g_array_new (FALSE, FALSE, sizeof (int));
|
||||||
|
|
||||||
|
/* find confs that have the default encoding */
|
||||||
|
nconfs = 0;
|
||||||
|
for (i = 0; i < cap.nconf; i++) {
|
||||||
|
for (j = 0; j < SIO_NENC; j++) {
|
||||||
|
if (cap.confs[i].enc & (1 << j)) {
|
||||||
|
enc = cap.enc[j];
|
||||||
|
if (enc.bits == par.bits && enc.sig == par.sig && enc.le == par.le) {
|
||||||
|
confs[nconfs] = i;
|
||||||
|
nconfs++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the rates and channels of the confs that have the default encoding */
|
||||||
|
for (i = 0; i < nconfs; i++) {
|
||||||
|
conf = cap.confs[confs[i]];
|
||||||
|
/* rates */
|
||||||
|
for (j = 0; j < SIO_NRATE; j++) {
|
||||||
|
if (conf.rate & (1 << j)) {
|
||||||
|
rate = cap.rate[j];
|
||||||
|
for (k = 0; k < rates->len && rate; k++) {
|
||||||
|
if (rate == g_array_index (rates, int, k))
|
||||||
|
rate = 0;
|
||||||
|
}
|
||||||
|
/* add in ascending order */
|
||||||
|
if (rate) {
|
||||||
|
for (k = 0; k < rates->len; k++) {
|
||||||
|
if (rate < g_array_index (rates, int, k))
|
||||||
|
{
|
||||||
|
g_array_insert_val (rates, k, rate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == rates->len)
|
||||||
|
g_array_append_val (rates, rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* channels */
|
||||||
|
for (j = 0; j < SIO_NCHAN; j++) {
|
||||||
|
if (conf.pchan & (1 << j)) {
|
||||||
|
chan = cap.pchan[j];
|
||||||
|
for (k = 0; k < chans->len && chan; k++) {
|
||||||
|
if (chan == g_array_index (chans, int, k))
|
||||||
|
chan = 0;
|
||||||
|
}
|
||||||
|
/* add in ascending order */
|
||||||
|
if (chan) {
|
||||||
|
for (k = 0; k < chans->len; k++) {
|
||||||
|
if (chan < g_array_index (chans, int, k))
|
||||||
|
{
|
||||||
|
g_array_insert_val (chans, k, chan);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == chans->len)
|
||||||
|
g_array_append_val (chans, chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* not sure how this can happen, but it might */
|
||||||
|
if (cap.nconf == 0) {
|
||||||
|
g_array_append_val (rates, par.rate);
|
||||||
|
g_array_append_val (chans, par.pchan);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_init (&rates_v, GST_TYPE_LIST);
|
||||||
|
g_value_init (&chans_v, GST_TYPE_LIST);
|
||||||
|
g_value_init (&value, G_TYPE_INT);
|
||||||
|
|
||||||
|
for (i = 0; i < rates->len; i++) {
|
||||||
|
g_value_set_int (&value, g_array_index (rates, int, i));
|
||||||
|
gst_value_list_append_value (&rates_v, &value);
|
||||||
|
}
|
||||||
|
for (i = 0; i < chans->len; i++) {
|
||||||
|
g_value_set_int (&value, g_array_index (chans, int, i));
|
||||||
|
gst_value_list_append_value (&chans_v, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_free (rates, TRUE);
|
||||||
|
g_array_free (chans, TRUE);
|
||||||
|
|
||||||
|
pad_template = gst_static_pad_template_get (&sndio_sink_factory);
|
||||||
|
sndiosink->cur_caps =
|
||||||
|
gst_caps_copy (gst_pad_template_get_caps (pad_template));
|
||||||
|
gst_object_unref (pad_template);
|
||||||
|
|
||||||
|
for (i = 0; i < sndiosink->cur_caps->structs->len; i++) {
|
||||||
|
GstStructure *s;
|
||||||
|
|
||||||
|
s = gst_caps_get_structure (sndiosink->cur_caps, i);
|
||||||
|
gst_structure_set (s, "endianness", G_TYPE_INT, par.le ? 1234 : 4321, NULL);
|
||||||
|
gst_structure_set (s, "signed", G_TYPE_BOOLEAN, par.sig ? TRUE : FALSE,
|
||||||
|
NULL);
|
||||||
|
gst_structure_set (s, "width", G_TYPE_INT, par.bits, NULL);
|
||||||
|
// gst_structure_set (s, "depth", G_TYPE_INT, par.bps * 8, NULL); /* XXX */
|
||||||
|
gst_structure_set_value (s, "rate", &rates_v);
|
||||||
|
gst_structure_set_value (s, "channels", &chans_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
couldnt_connect:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosink, RESOURCE, OPEN_WRITE,
|
||||||
|
(_("Could not establish connection to sndio")),
|
||||||
|
("can't open connection to sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
no_server_info:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosink, RESOURCE, OPEN_WRITE,
|
||||||
|
(_("Failed to query sndio capabilities")),
|
||||||
|
("couldn't get sndio info!"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosink_close (GstAudioSink * asink)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosink, "close");
|
||||||
|
|
||||||
|
gst_caps_replace (&sndiosink->cur_caps, NULL);
|
||||||
|
sio_close (sndiosink->hdl);
|
||||||
|
sndiosink->hdl = NULL;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_cb (void *addr, int delta)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK ((GstAudioSink *) addr);
|
||||||
|
|
||||||
|
sndiosink->realpos += delta;
|
||||||
|
|
||||||
|
if (sndiosink->realpos >= sndiosink->playpos)
|
||||||
|
sndiosink->latency = 0;
|
||||||
|
else
|
||||||
|
sndiosink->latency = sndiosink->playpos - sndiosink->realpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
struct sio_par par;
|
||||||
|
int spec_bpf;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosink, "prepare");
|
||||||
|
|
||||||
|
sndiosink->playpos = sndiosink->realpos = sndiosink->latency = 0;
|
||||||
|
|
||||||
|
sio_initpar (&par);
|
||||||
|
par.sig = spec->sign;
|
||||||
|
par.le = !spec->bigend;
|
||||||
|
par.bits = spec->width;
|
||||||
|
// par.bps = spec->depth / 8; /* XXX */
|
||||||
|
par.rate = spec->rate;
|
||||||
|
par.pchan = spec->channels;
|
||||||
|
|
||||||
|
spec_bpf = ((spec->width / 8) * spec->channels);
|
||||||
|
|
||||||
|
par.appbufsz = (spec->segsize * spec->segtotal) / spec_bpf;
|
||||||
|
|
||||||
|
if (!sio_setpar (sndiosink->hdl, &par))
|
||||||
|
goto cannot_configure;
|
||||||
|
|
||||||
|
sio_getpar (sndiosink->hdl, &par);
|
||||||
|
|
||||||
|
spec->sign = par.sig;
|
||||||
|
spec->bigend = !par.le;
|
||||||
|
spec->width = par.bits;
|
||||||
|
// spec->depth = par.bps * 8; /* XXX */
|
||||||
|
spec->rate = par.rate;
|
||||||
|
spec->channels = par.pchan;
|
||||||
|
|
||||||
|
sndiosink->bpf = par.bps * par.pchan;
|
||||||
|
|
||||||
|
spec->segsize = par.round * par.pchan * par.bps;
|
||||||
|
spec->segtotal = par.bufsz / par.round;
|
||||||
|
|
||||||
|
/* FIXME: this is wrong for signed ints (and the
|
||||||
|
* audioringbuffers should do it for us anyway) */
|
||||||
|
spec->silence_sample[0] = 0;
|
||||||
|
spec->silence_sample[1] = 0;
|
||||||
|
spec->silence_sample[2] = 0;
|
||||||
|
spec->silence_sample[3] = 0;
|
||||||
|
|
||||||
|
sio_onmove (sndiosink->hdl, gst_sndiosink_cb, sndiosink);
|
||||||
|
|
||||||
|
if (!sio_start (sndiosink->hdl))
|
||||||
|
goto cannot_start;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (sndiosink, "successfully opened connection to sndio");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
cannot_configure:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosink, RESOURCE, OPEN_WRITE,
|
||||||
|
(_("Could not configure sndio")), ("can't configure sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cannot_start:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosink, RESOURCE, OPEN_WRITE,
|
||||||
|
(_("Could not start sndio")), ("can't start sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosink_unprepare (GstAudioSink * asink)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
|
||||||
|
if (sndiosink->hdl == NULL)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
sio_stop (sndiosink->hdl);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
gst_sndiosink_write (GstAudioSink * asink, gpointer data, guint length)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
guint done;
|
||||||
|
|
||||||
|
done = sio_write (sndiosink->hdl, data, length);
|
||||||
|
|
||||||
|
if (done == 0)
|
||||||
|
goto write_error;
|
||||||
|
|
||||||
|
sndiosink->playpos += (done / sndiosink->bpf);
|
||||||
|
|
||||||
|
data = (char *) data + done;
|
||||||
|
|
||||||
|
return done;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
write_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosink, RESOURCE, WRITE,
|
||||||
|
("Failed to write data to sndio"), GST_ERROR_SYSTEM);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
gst_sndiosink_delay (GstAudioSink * asink)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (asink);
|
||||||
|
|
||||||
|
if (sndiosink->latency == (guint) - 1) {
|
||||||
|
GST_WARNING_OBJECT (asink, "couldn't get latency");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (asink, "got latency: %u", sndiosink->latency);
|
||||||
|
|
||||||
|
return sndiosink->latency;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_reset (GstAudioSink * asink)
|
||||||
|
{
|
||||||
|
/* no way to flush the buffers with sndio ? */
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (asink, "reset called");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_HOST:
|
||||||
|
g_free (sndiosink->host);
|
||||||
|
sndiosink->host = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosink_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
|
GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstSndioSink *sndiosink = GST_SNDIOSINK (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_HOST:
|
||||||
|
g_value_set_string (value, sndiosink->host);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
67
ext/sndio/sndiosink.h
Normal file
67
ext/sndio/sndiosink.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) <2008> Jacob Meuser <jakemsr@sdf.lonestar.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>
|
||||||
|
|
||||||
|
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 sio_hdl *hdl;
|
||||||
|
gchar *host;
|
||||||
|
|
||||||
|
/* bytes per frame */
|
||||||
|
int bpf;
|
||||||
|
|
||||||
|
/* frames counts */
|
||||||
|
volatile long long realpos;
|
||||||
|
volatile long long playpos;
|
||||||
|
volatile guint latency;
|
||||||
|
|
||||||
|
GstCaps *cur_caps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstSndioSinkClass {
|
||||||
|
GstAudioSinkClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_sndiosink_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_SNDIOSINK_H__ */
|
531
ext/sndio/sndiosrc.c
Normal file
531
ext/sndio/sndiosrc.c
Normal file
|
@ -0,0 +1,531 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) <2008> Jacob Meuser <jakemsr@sdf.lonestar.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 <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <gst/gst-i18n-plugin.h>
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (gst_sndio_debug);
|
||||||
|
#define GST_CAT_DEFAULT gst_sndio_debug
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_HOST
|
||||||
|
};
|
||||||
|
|
||||||
|
static GstStaticPadTemplate sndio_src_factory =
|
||||||
|
GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
|
GST_PAD_SRC,
|
||||||
|
GST_PAD_ALWAYS,
|
||||||
|
GST_STATIC_CAPS ("audio/x-raw-int, "
|
||||||
|
"endianness = (int) { 1234, 4321 }, "
|
||||||
|
"signed = (boolean) { TRUE, FALSE }, "
|
||||||
|
"width = (int) { 8, 16, 24, 32 }, "
|
||||||
|
"depth = (int) { 8, 16, 24, 32 }, "
|
||||||
|
"rate = (int) [ 8000, 192000 ], "
|
||||||
|
"channels = (int) [ 1, 16 ] ")
|
||||||
|
);
|
||||||
|
|
||||||
|
static void gst_sndiosrc_finalize (GObject * object);
|
||||||
|
|
||||||
|
static GstCaps *gst_sndiosrc_getcaps (GstBaseSrc * bsrc);
|
||||||
|
|
||||||
|
static gboolean gst_sndiosrc_open (GstAudioSrc * asrc);
|
||||||
|
static gboolean gst_sndiosrc_close (GstAudioSrc * asrc);
|
||||||
|
static gboolean gst_sndiosrc_prepare (GstAudioSrc * asrc,
|
||||||
|
GstRingBufferSpec * 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 void gst_sndiosrc_cb (void *addr, int delta);
|
||||||
|
|
||||||
|
GST_BOILERPLATE (GstSndioSrc, gst_sndiosrc, GstAudioSrc,
|
||||||
|
GST_TYPE_AUDIO_SRC);
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_base_init (gpointer g_class)
|
||||||
|
{
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||||
|
|
||||||
|
gst_element_class_set_details_simple (element_class,
|
||||||
|
"Sndio audio source",
|
||||||
|
"Source/Audio",
|
||||||
|
"Records audio through sndio",
|
||||||
|
"Jacob Meuser <jakemsr@sdf.lonestar.org>");
|
||||||
|
|
||||||
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
gst_static_pad_template_get (&sndio_src_factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_class_init (GstSndioSrcClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class;
|
||||||
|
GstBaseSrcClass *gstbasesrc_class;
|
||||||
|
GstBaseAudioSrcClass *gstbaseaudiosrc_class;
|
||||||
|
GstAudioSrcClass *gstaudiosrc_class;
|
||||||
|
|
||||||
|
gobject_class = (GObjectClass *) klass;
|
||||||
|
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
||||||
|
gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
|
||||||
|
gstaudiosrc_class = (GstAudioSrcClass *) klass;
|
||||||
|
|
||||||
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_sndiosrc_finalize;
|
||||||
|
|
||||||
|
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_sndiosrc_getcaps);
|
||||||
|
|
||||||
|
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_sndiosrc_open);
|
||||||
|
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_sndiosrc_close);
|
||||||
|
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_sndiosrc_prepare);
|
||||||
|
gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_sndiosrc_unprepare);
|
||||||
|
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);
|
||||||
|
|
||||||
|
gobject_class->set_property = gst_sndiosrc_set_property;
|
||||||
|
gobject_class->get_property = gst_sndiosrc_get_property;
|
||||||
|
|
||||||
|
/* default value is filled in the _init method */
|
||||||
|
g_object_class_install_property (gobject_class, PROP_HOST,
|
||||||
|
g_param_spec_string ("host", "Host",
|
||||||
|
"Device or socket sndio will access", NULL, G_PARAM_READWRITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_init (GstSndioSrc * sndiosrc, GstSndioSrcClass * klass)
|
||||||
|
{
|
||||||
|
sndiosrc->hdl = NULL;
|
||||||
|
sndiosrc->host = g_strdup (g_getenv ("AUDIODEVICE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (object);
|
||||||
|
|
||||||
|
gst_caps_replace (&sndiosrc->cur_caps, NULL);
|
||||||
|
g_free (sndiosrc->host);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstCaps *
|
||||||
|
gst_sndiosrc_getcaps (GstBaseSrc * bsrc)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc;
|
||||||
|
|
||||||
|
sndiosrc = GST_SNDIOSRC (bsrc);
|
||||||
|
|
||||||
|
/* no hdl, we're done with the template caps */
|
||||||
|
if (sndiosrc->cur_caps == NULL) {
|
||||||
|
GST_LOG_OBJECT (sndiosrc, "getcaps called, returning template caps");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (sndiosrc, "returning %" GST_PTR_FORMAT,
|
||||||
|
sndiosrc->cur_caps);
|
||||||
|
|
||||||
|
return gst_caps_ref (sndiosrc->cur_caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosrc_open (GstAudioSrc * asrc)
|
||||||
|
{
|
||||||
|
GstPadTemplate *pad_template;
|
||||||
|
GstSndioSrc *sndiosrc;
|
||||||
|
struct sio_par par;
|
||||||
|
struct sio_cap cap;
|
||||||
|
GArray *rates, *chans;
|
||||||
|
GValue rates_v = { 0 };
|
||||||
|
GValue chans_v = { 0 };
|
||||||
|
GValue value = { 0 };
|
||||||
|
struct sio_enc enc;
|
||||||
|
struct sio_conf conf;
|
||||||
|
int confs[SIO_NCONF];
|
||||||
|
int rate, chan;
|
||||||
|
int i, j, k;
|
||||||
|
int nconfs;
|
||||||
|
|
||||||
|
sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosrc, "open");
|
||||||
|
|
||||||
|
/* connect */
|
||||||
|
sndiosrc->hdl = sio_open (sndiosrc->host, SIO_REC, 0);
|
||||||
|
|
||||||
|
if (sndiosrc->hdl == NULL)
|
||||||
|
goto couldnt_connect;
|
||||||
|
|
||||||
|
/* Use sndio defaults as the only encodings, but get the supported
|
||||||
|
* sample rates and number of channels.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!sio_getpar (sndiosrc->hdl, &par))
|
||||||
|
goto no_server_info;
|
||||||
|
|
||||||
|
if (!sio_getcap (sndiosrc->hdl, &cap))
|
||||||
|
goto no_server_info;
|
||||||
|
|
||||||
|
rates = g_array_new (FALSE, FALSE, sizeof (int));
|
||||||
|
chans = g_array_new (FALSE, FALSE, sizeof (int));
|
||||||
|
|
||||||
|
/* find confs that have the default encoding */
|
||||||
|
nconfs = 0;
|
||||||
|
for (i = 0; i < cap.nconf; i++) {
|
||||||
|
for (j = 0; j < SIO_NENC; j++) {
|
||||||
|
if (cap.confs[i].enc & (1 << j)) {
|
||||||
|
enc = cap.enc[j];
|
||||||
|
if (enc.bits == par.bits && enc.sig == par.sig && enc.le == par.le) {
|
||||||
|
confs[nconfs] = i;
|
||||||
|
nconfs++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the rates and channels of the confs that have the default encoding */
|
||||||
|
for (i = 0; i < nconfs; i++) {
|
||||||
|
conf = cap.confs[confs[i]];
|
||||||
|
/* rates */
|
||||||
|
for (j = 0; j < SIO_NRATE; j++) {
|
||||||
|
if (conf.rate & (1 << j)) {
|
||||||
|
rate = cap.rate[j];
|
||||||
|
for (k = 0; k < rates->len && rate; k++) {
|
||||||
|
if (rate == g_array_index (rates, int, k))
|
||||||
|
rate = 0;
|
||||||
|
}
|
||||||
|
/* add in ascending order */
|
||||||
|
if (rate) {
|
||||||
|
for (k = 0; k < rates->len; k++) {
|
||||||
|
if (rate < g_array_index (rates, int, k))
|
||||||
|
{
|
||||||
|
g_array_insert_val (rates, k, rate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == rates->len)
|
||||||
|
g_array_append_val (rates, rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* channels */
|
||||||
|
for (j = 0; j < SIO_NCHAN; j++) {
|
||||||
|
if (conf.rchan & (1 << j)) {
|
||||||
|
chan = cap.rchan[j];
|
||||||
|
for (k = 0; k < chans->len && chan; k++) {
|
||||||
|
if (chan == g_array_index (chans, int, k))
|
||||||
|
chan = 0;
|
||||||
|
}
|
||||||
|
/* add in ascending order */
|
||||||
|
if (chan) {
|
||||||
|
for (k = 0; k < chans->len; k++) {
|
||||||
|
if (chan < g_array_index (chans, int, k))
|
||||||
|
{
|
||||||
|
g_array_insert_val (chans, k, chan);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == chans->len)
|
||||||
|
g_array_append_val (chans, chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* not sure how this can happen, but it might */
|
||||||
|
if (cap.nconf == 0) {
|
||||||
|
g_array_append_val (rates, par.rate);
|
||||||
|
g_array_append_val (chans, par.rchan);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_value_init (&rates_v, GST_TYPE_LIST);
|
||||||
|
g_value_init (&chans_v, GST_TYPE_LIST);
|
||||||
|
g_value_init (&value, G_TYPE_INT);
|
||||||
|
|
||||||
|
for (i = 0; i < rates->len; i++) {
|
||||||
|
g_value_set_int (&value, g_array_index (rates, int, i));
|
||||||
|
gst_value_list_append_value (&rates_v, &value);
|
||||||
|
}
|
||||||
|
for (i = 0; i < chans->len; i++) {
|
||||||
|
g_value_set_int (&value, g_array_index (chans, int, i));
|
||||||
|
gst_value_list_append_value (&chans_v, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_array_free (rates, TRUE);
|
||||||
|
g_array_free (chans, TRUE);
|
||||||
|
|
||||||
|
pad_template = gst_static_pad_template_get (&sndio_src_factory);
|
||||||
|
sndiosrc->cur_caps =
|
||||||
|
gst_caps_copy (gst_pad_template_get_caps (pad_template));
|
||||||
|
gst_object_unref (pad_template);
|
||||||
|
|
||||||
|
for (i = 0; i < sndiosrc->cur_caps->structs->len; i++) {
|
||||||
|
GstStructure *s;
|
||||||
|
|
||||||
|
s = gst_caps_get_structure (sndiosrc->cur_caps, i);
|
||||||
|
gst_structure_set (s, "endianness", G_TYPE_INT, par.le ? 1234 : 4321, NULL);
|
||||||
|
gst_structure_set (s, "signed", G_TYPE_BOOLEAN, par.sig ? TRUE : FALSE,
|
||||||
|
NULL);
|
||||||
|
gst_structure_set (s, "width", G_TYPE_INT, par.bits, NULL);
|
||||||
|
// gst_structure_set (s, "depth", G_TYPE_INT, par.bps * 8, NULL); /* XXX */
|
||||||
|
gst_structure_set_value (s, "rate", &rates_v);
|
||||||
|
gst_structure_set_value (s, "channels", &chans_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
couldnt_connect:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosrc, RESOURCE, OPEN_READ,
|
||||||
|
(_("Could not establish connection to sndio")),
|
||||||
|
("can't open connection to sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
no_server_info:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosrc, RESOURCE, OPEN_READ,
|
||||||
|
(_("Failed to query sndio capabilities")),
|
||||||
|
("couldn't get sndio info!"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosrc_close (GstAudioSrc * asrc)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosrc, "close");
|
||||||
|
|
||||||
|
gst_caps_replace (&sndiosrc->cur_caps, NULL);
|
||||||
|
sio_close (sndiosrc->hdl);
|
||||||
|
sndiosrc->hdl = NULL;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_cb (void *addr, int delta)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC ((GstAudioSrc *) addr);
|
||||||
|
|
||||||
|
sndiosrc->realpos += delta;
|
||||||
|
|
||||||
|
if (sndiosrc->readpos >= sndiosrc->realpos)
|
||||||
|
sndiosrc->latency = 0;
|
||||||
|
else
|
||||||
|
sndiosrc->latency = sndiosrc->realpos - sndiosrc->readpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosrc_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
struct sio_par par;
|
||||||
|
int spec_bpf;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sndiosrc, "prepare");
|
||||||
|
|
||||||
|
sndiosrc->readpos = sndiosrc->realpos = sndiosrc->latency = 0;
|
||||||
|
|
||||||
|
sio_initpar (&par);
|
||||||
|
par.sig = spec->sign;
|
||||||
|
par.le = !spec->bigend;
|
||||||
|
par.bits = spec->width;
|
||||||
|
// par.bps = spec->depth / 8; /* XXX */
|
||||||
|
par.rate = spec->rate;
|
||||||
|
par.rchan = spec->channels;
|
||||||
|
|
||||||
|
spec_bpf = ((spec->width / 8) * spec->channels);
|
||||||
|
|
||||||
|
par.round = spec->segsize / spec_bpf;
|
||||||
|
par.appbufsz = (spec->segsize * spec->segtotal) / spec_bpf;
|
||||||
|
|
||||||
|
if (!sio_setpar (sndiosrc->hdl, &par))
|
||||||
|
goto cannot_configure;
|
||||||
|
|
||||||
|
sio_getpar (sndiosrc->hdl, &par);
|
||||||
|
|
||||||
|
spec->sign = par.sig;
|
||||||
|
spec->bigend = !par.le;
|
||||||
|
spec->width = par.bits;
|
||||||
|
// spec->depth = par.bps * 8; /* XXX */
|
||||||
|
spec->rate = par.rate;
|
||||||
|
spec->channels = par.rchan;
|
||||||
|
|
||||||
|
sndiosrc->bpf = par.bps * par.rchan;
|
||||||
|
|
||||||
|
spec->segsize = par.round * par.rchan * par.bps;
|
||||||
|
spec->segtotal = par.bufsz / par.round;
|
||||||
|
|
||||||
|
/* FIXME: this is wrong for signed ints (and the
|
||||||
|
* audioringbuffers should do it for us anyway) */
|
||||||
|
spec->silence_sample[0] = 0;
|
||||||
|
spec->silence_sample[1] = 0;
|
||||||
|
spec->silence_sample[2] = 0;
|
||||||
|
spec->silence_sample[3] = 0;
|
||||||
|
|
||||||
|
sio_onmove (sndiosrc->hdl, gst_sndiosrc_cb, sndiosrc);
|
||||||
|
|
||||||
|
if (!sio_start (sndiosrc->hdl))
|
||||||
|
goto cannot_start;
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (sndiosrc, "successfully opened connection to sndio");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
cannot_configure:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosrc, RESOURCE, OPEN_READ,
|
||||||
|
(_("Could not configure sndio")), ("can't configure sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cannot_start:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosrc, RESOURCE, OPEN_READ,
|
||||||
|
(_("Could not start sndio")), ("can't start sndio"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_sndiosrc_unprepare (GstAudioSrc * asrc)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
|
||||||
|
if (sndiosrc->hdl == NULL)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
sio_stop (sndiosrc->hdl);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
gst_sndiosrc_read (GstAudioSrc * asrc, gpointer data, guint length)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
guint done;
|
||||||
|
|
||||||
|
done = sio_read (sndiosrc->hdl, data, length);
|
||||||
|
|
||||||
|
if (done == 0)
|
||||||
|
goto read_error;
|
||||||
|
|
||||||
|
sndiosrc->readpos += (done / sndiosrc->bpf);
|
||||||
|
|
||||||
|
data = (char *) data + done;
|
||||||
|
|
||||||
|
return done;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
read_error:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (sndiosrc, RESOURCE, READ,
|
||||||
|
("Failed to read data from sndio"), GST_ERROR_SYSTEM);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
gst_sndiosrc_delay (GstAudioSrc * asrc)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (asrc);
|
||||||
|
|
||||||
|
if (sndiosrc->latency == (guint) - 1) {
|
||||||
|
GST_WARNING_OBJECT (asrc, "couldn't get latency");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (asrc, "got latency: %u", sndiosrc->latency);
|
||||||
|
|
||||||
|
return sndiosrc->latency;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_reset (GstAudioSrc * asrc)
|
||||||
|
{
|
||||||
|
/* no way to flush the buffers with sndio ? */
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (asrc, "reset called");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_set_property (GObject * object, guint prop_id,
|
||||||
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_HOST:
|
||||||
|
g_free (sndiosrc->host);
|
||||||
|
sndiosrc->host = g_value_dup_string (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_sndiosrc_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
|
GParamSpec * pspec)
|
||||||
|
{
|
||||||
|
GstSndioSrc *sndiosrc = GST_SNDIOSRC (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_HOST:
|
||||||
|
g_value_set_string (value, sndiosrc->host);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
67
ext/sndio/sndiosrc.h
Normal file
67
ext/sndio/sndiosrc.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) <2008> Jacob Meuser <jakemsr@sdf.lonestar.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>
|
||||||
|
|
||||||
|
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 sio_hdl *hdl;
|
||||||
|
gchar *host;
|
||||||
|
|
||||||
|
/* bytes per frame */
|
||||||
|
int bpf;
|
||||||
|
|
||||||
|
/* frames counts */
|
||||||
|
volatile long long realpos;
|
||||||
|
volatile long long readpos;
|
||||||
|
volatile guint latency;
|
||||||
|
|
||||||
|
GstCaps *cur_caps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GstSndioSrcClass {
|
||||||
|
GstAudioSrcClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType gst_sndiosrc_get_type (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GST_SNDIOSRC_H__ */
|
Loading…
Reference in a new issue