mirror of
https://git.freebsd.org/ports.git
synced 2025-05-02 03:26:41 -04:00
OpenJFX is an open source, next generation client application platform for desktop and embedded systems based on JavaSE. It is a collaborative effort by many individuals and companies with the goal of producing a modern, efficient, and fully featured toolkit for developing rich client applications. PR: 248248
396 lines
11 KiB
C
396 lines
11 KiB
C
/*
|
|
* 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);
|
|
|
|
gboolean
|
|
plugin_init_alsa (GstPlugin * plugin)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (gst_sndio_debug, "sndio", 0, "sndio plugins");
|
|
|
|
/* prefer sndiosink over pulsesink (GST_RANK_PRIMARY + 10) */
|
|
if (!gst_element_register (plugin, "bsdaudiosink", 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_alsa, 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);
|
|
}
|
|
}
|