mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-16 08:56:45 -05:00
more hacking
Move plugin API to separate directory for now Add ringbuffer and way to get ringbuffer from a port
This commit is contained in:
parent
b8f6e99537
commit
3c029cba53
56 changed files with 7055 additions and 1530 deletions
|
|
@ -31,6 +31,7 @@ dbuspolicydir=$(sysconfdir)/dbus-1/system.d
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = \
|
||||||
-I$(top_srcdir)/ \
|
-I$(top_srcdir)/ \
|
||||||
|
-I$(top_srcdir)/spa/include/ \
|
||||||
-I$(top_srcdir)/pinos/modules \
|
-I$(top_srcdir)/pinos/modules \
|
||||||
-I$(top_builddir)/pinos/modules \
|
-I$(top_builddir)/pinos/modules \
|
||||||
-DPINOS_SRCDIR=\"$(abs_srcdir)\" \
|
-DPINOS_SRCDIR=\"$(abs_srcdir)\" \
|
||||||
|
|
@ -39,8 +40,8 @@ AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS)
|
||||||
AM_CXXFLAGS = $(AM_CFLAGS)
|
AM_CXXFLAGS = $(AM_CFLAGS)
|
||||||
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
|
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
|
||||||
|
|
||||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS)
|
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS)
|
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||||
AM_LDFLAGS = $(NODELETE_LDFLAGS)
|
AM_LDFLAGS = $(NODELETE_LDFLAGS)
|
||||||
|
|
||||||
FOREIGN_CFLAGS = -w
|
FOREIGN_CFLAGS = -w
|
||||||
|
|
@ -69,6 +70,7 @@ xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
|
||||||
|
|
||||||
enumtypesincludes = client/context.h \
|
enumtypesincludes = client/context.h \
|
||||||
client/introspect.h \
|
client/introspect.h \
|
||||||
|
client/ringbuffer.h \
|
||||||
client/stream.h \
|
client/stream.h \
|
||||||
client/subscribe.h
|
client/subscribe.h
|
||||||
|
|
||||||
|
|
@ -131,7 +133,7 @@ noinst_LTLIBRARIES =
|
||||||
|
|
||||||
TESTS_default =
|
TESTS_default =
|
||||||
|
|
||||||
TESTS_norun = test-client test-node
|
TESTS_norun = test-client
|
||||||
|
|
||||||
# These tests need a running pinos daemon
|
# These tests need a running pinos daemon
|
||||||
TESTS_daemon =
|
TESTS_daemon =
|
||||||
|
|
@ -143,11 +145,6 @@ test_client_CFLAGS = $(AM_CFLAGS)
|
||||||
test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||||
test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||||
|
|
||||||
test_node_SOURCES = tests/test-node.c tests/spi-volume.c tests/spi-alsa-sink.c tests/spi-audiotestsrc.c
|
|
||||||
test_node_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS)
|
|
||||||
test_node_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
|
||||||
test_node_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ALSA_LIBS)
|
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
# Tools programs #
|
# Tools programs #
|
||||||
###################################
|
###################################
|
||||||
|
|
@ -169,12 +166,14 @@ pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \
|
||||||
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
|
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
|
||||||
|
|
||||||
pinosinclude_HEADERS = \
|
pinosinclude_HEADERS = \
|
||||||
client/pinos.h \
|
|
||||||
client/buffer.h \
|
client/buffer.h \
|
||||||
|
client/client-node.h \
|
||||||
|
client/client-port.h \
|
||||||
client/context.h \
|
client/context.h \
|
||||||
client/enumtypes.h \
|
client/enumtypes.h \
|
||||||
client/introspect.h \
|
client/introspect.h \
|
||||||
client/mainloop.h \
|
client/mainloop.h \
|
||||||
|
client/pinos.h \
|
||||||
client/properties.h \
|
client/properties.h \
|
||||||
client/stream.h \
|
client/stream.h \
|
||||||
client/subscribe.h
|
client/subscribe.h
|
||||||
|
|
@ -194,6 +193,7 @@ libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \
|
||||||
client/stream.h client/stream.c \
|
client/stream.h client/stream.c \
|
||||||
client/pinos.c client/pinos.h \
|
client/pinos.c client/pinos.h \
|
||||||
client/fdmanager.c client/fdmanager.h \
|
client/fdmanager.c client/fdmanager.h \
|
||||||
|
client/ringbuffer.c client/ringbuffer.h \
|
||||||
client/subscribe.c client/subscribe.h \
|
client/subscribe.c client/subscribe.h \
|
||||||
$(pinosgstsource)
|
$(pinosgstsource)
|
||||||
|
|
||||||
|
|
@ -222,6 +222,7 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \
|
||||||
modules/gst/gst-source.c modules/gst/gst-source.h \
|
modules/gst/gst-source.c modules/gst/gst-source.h \
|
||||||
modules/gst/gst-sink.c modules/gst/gst-sink.h \
|
modules/gst/gst-sink.c modules/gst/gst-sink.h \
|
||||||
modules/gst/gst-node-factory.c modules/gst/gst-node-factory.h \
|
modules/gst/gst-node-factory.c modules/gst/gst-node-factory.h \
|
||||||
|
modules/spa/spa-alsa-sink.c modules/spa/spa-alsa-sink.h \
|
||||||
dbus/org-pinos.c dbus/org-pinos.h
|
dbus/org-pinos.c dbus/org-pinos.h
|
||||||
|
|
||||||
libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
|
libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
#include "pinos/client/pinos.h"
|
#include "pinos/client/pinos.h"
|
||||||
#include "pinos/client/enumtypes.h"
|
#include "pinos/client/enumtypes.h"
|
||||||
|
|
@ -43,6 +44,74 @@ enum
|
||||||
PROP_PROXY,
|
PROP_PROXY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_ringbuffer (GObject *source_object,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GTask *task = user_data;
|
||||||
|
PinosClientPort *port = g_task_get_source_object (task);
|
||||||
|
PinosClientPortPrivate *priv = port->priv;
|
||||||
|
GVariant *ret;
|
||||||
|
GError *error = NULL;
|
||||||
|
GUnixFDList *fdlist;
|
||||||
|
gint fd, semfd, fd_idx, sem_idx;
|
||||||
|
guint fdsize;
|
||||||
|
PinosRingbuffer *rbuf;
|
||||||
|
|
||||||
|
g_assert (priv->proxy == G_DBUS_PROXY (source_object));
|
||||||
|
|
||||||
|
ret = g_dbus_proxy_call_with_unix_fd_list_finish (priv->proxy, &fdlist, res, &error);
|
||||||
|
if (ret == NULL)
|
||||||
|
goto create_failed;
|
||||||
|
|
||||||
|
g_variant_get (ret, "(huh)", &fd_idx, &fdsize, &sem_idx);
|
||||||
|
g_variant_unref (ret);
|
||||||
|
|
||||||
|
fd = g_unix_fd_list_get (fdlist, fd_idx, &error);
|
||||||
|
semfd = g_unix_fd_list_get (fdlist, sem_idx, &error);
|
||||||
|
g_object_unref (fdlist);
|
||||||
|
|
||||||
|
if (fd == -1 || semfd == -1)
|
||||||
|
goto create_failed;
|
||||||
|
|
||||||
|
rbuf = pinos_ringbuffer_new_import (PINOS_RINGBUFFER_MODE_WRITE,
|
||||||
|
fd, fdsize, semfd);
|
||||||
|
|
||||||
|
g_task_return_pointer (task, rbuf, (GDestroyNotify) g_object_unref);
|
||||||
|
g_object_unref (task);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
create_failed:
|
||||||
|
{
|
||||||
|
g_warning ("failed to get ringbuffer: %s", error->message);
|
||||||
|
g_task_return_error (task, error);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_client_port_get_ringbuffer (PinosPort *port,
|
||||||
|
PinosProperties *props,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
PinosClientPortPrivate *priv = PINOS_CLIENT_PORT (port)->priv;
|
||||||
|
|
||||||
|
g_dbus_proxy_call (priv->proxy,
|
||||||
|
"GetRingbuffer",
|
||||||
|
g_variant_new ("(@a{sv})",
|
||||||
|
pinos_properties_to_variant (props)),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
-1,
|
||||||
|
NULL, /* GCancellable *cancellable */
|
||||||
|
on_ringbuffer,
|
||||||
|
task);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pinos_client_port_get_property (GObject *_object,
|
pinos_client_port_get_property (GObject *_object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
|
|
@ -205,6 +274,7 @@ static void
|
||||||
pinos_client_port_class_init (PinosClientPortClass * klass)
|
pinos_client_port_class_init (PinosClientPortClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
PinosPortClass *port_class = PINOS_PORT_CLASS (klass);
|
||||||
|
|
||||||
g_type_class_add_private (klass, sizeof (PinosClientPortPrivate));
|
g_type_class_add_private (klass, sizeof (PinosClientPortPrivate));
|
||||||
|
|
||||||
|
|
@ -223,6 +293,8 @@ pinos_client_port_class_init (PinosClientPortClass * klass)
|
||||||
G_PARAM_READWRITE |
|
G_PARAM_READWRITE |
|
||||||
G_PARAM_CONSTRUCT_ONLY |
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
port_class->get_ringbuffer = pinos_client_port_get_ringbuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <pinos/client/introspect.h>
|
#include <pinos/client/introspect.h>
|
||||||
#include <pinos/client/mainloop.h>
|
#include <pinos/client/mainloop.h>
|
||||||
#include <pinos/client/properties.h>
|
#include <pinos/client/properties.h>
|
||||||
|
#include <pinos/client/ringbuffer.h>
|
||||||
#include <pinos/client/stream.h>
|
#include <pinos/client/stream.h>
|
||||||
#include <pinos/client/subscribe.h>
|
#include <pinos/client/subscribe.h>
|
||||||
|
|
||||||
|
|
|
||||||
393
pinos/client/ringbuffer.c
Normal file
393
pinos/client/ringbuffer.c
Normal file
|
|
@ -0,0 +1,393 @@
|
||||||
|
/* Pinos
|
||||||
|
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include <pinos/client/enumtypes.h>
|
||||||
|
#include <pinos/client/ringbuffer.h>
|
||||||
|
|
||||||
|
#include <spa/include/spa/ringbuffer.h>
|
||||||
|
#include <spa/lib/ringbuffer.c>
|
||||||
|
|
||||||
|
#define PINOS_RINGBUFFER_GET_PRIVATE(rb) \
|
||||||
|
(G_TYPE_INSTANCE_GET_PRIVATE ((rb), PINOS_TYPE_RINGBUFFER, PinosRingbufferPrivate))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaRingbuffer rbuf;
|
||||||
|
/* ringbuffer starts here */
|
||||||
|
} PinosRingbufferData;
|
||||||
|
|
||||||
|
struct _PinosRingbufferPrivate
|
||||||
|
{
|
||||||
|
PinosRingbufferMode mode;
|
||||||
|
guint size;
|
||||||
|
guint fdsize;
|
||||||
|
int fd;
|
||||||
|
int semaphore;
|
||||||
|
|
||||||
|
PinosRingbufferData *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (PinosRingbuffer, pinos_ringbuffer, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
PROP_MODE,
|
||||||
|
PROP_SIZE,
|
||||||
|
PROP_FD,
|
||||||
|
PROP_FDSIZE,
|
||||||
|
PROP_SEMAPHORE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SIGNAL_NONE,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_get_property (GObject *_object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb = PINOS_RINGBUFFER (_object);
|
||||||
|
PinosRingbufferPrivate *priv = rb->priv;
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
g_value_set_enum (value, priv->mode);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SIZE:
|
||||||
|
g_value_set_uint (value, priv->size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FD:
|
||||||
|
g_value_set_int (value, priv->fd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FDSIZE:
|
||||||
|
g_value_set_uint (value, priv->fdsize);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SEMAPHORE:
|
||||||
|
g_value_set_int (value, priv->semaphore);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (rb, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_set_property (GObject *_object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb = PINOS_RINGBUFFER (_object);
|
||||||
|
PinosRingbufferPrivate *priv = rb->priv;
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_MODE:
|
||||||
|
priv->mode = g_value_get_enum (value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SIZE:
|
||||||
|
priv->size = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FD:
|
||||||
|
priv->fd = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_FDSIZE:
|
||||||
|
priv->fdsize = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROP_SEMAPHORE:
|
||||||
|
priv->semaphore = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (rb, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tmpfile_create (gsize size)
|
||||||
|
{
|
||||||
|
char filename[] = "/dev/shm/tmpfilepay.XXXXXX";
|
||||||
|
int fd, result;
|
||||||
|
|
||||||
|
fd = mkostemp (filename, O_CLOEXEC);
|
||||||
|
if (fd == -1)
|
||||||
|
return -1;
|
||||||
|
unlink (filename);
|
||||||
|
|
||||||
|
result = ftruncate (fd, size);
|
||||||
|
if (result == -1) {
|
||||||
|
close (fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_constructed (GObject * obj)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||||
|
PinosRingbufferPrivate *priv = rb->priv;
|
||||||
|
|
||||||
|
g_debug ("ringbuffer %p: constructed", rb);
|
||||||
|
|
||||||
|
if (priv->fd == -1) {
|
||||||
|
priv->fdsize = priv->size + sizeof (PinosRingbufferData);
|
||||||
|
priv->fd = tmpfile_create (priv->fdsize);
|
||||||
|
priv->semaphore = eventfd (0, EFD_CLOEXEC);
|
||||||
|
}
|
||||||
|
priv->data = mmap (NULL, priv->fdsize, PROT_READ | PROT_WRITE, MAP_SHARED, priv->fd, 0);
|
||||||
|
|
||||||
|
spa_ringbuffer_init (&priv->data->rbuf, (guint8 *)priv->data + sizeof (PinosRingbufferData), priv->size);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->constructed (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_dispose (GObject * obj)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||||
|
|
||||||
|
g_debug ("ringbuffer %p: dispose", rb);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->dispose (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_finalize (GObject * obj)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||||
|
|
||||||
|
g_debug ("ringbuffer %p: finalize", rb);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->finalize (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_class_init (PinosRingbufferClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (klass, sizeof (PinosRingbufferPrivate));
|
||||||
|
|
||||||
|
gobject_class->constructed = pinos_ringbuffer_constructed;
|
||||||
|
gobject_class->dispose = pinos_ringbuffer_dispose;
|
||||||
|
gobject_class->finalize = pinos_ringbuffer_finalize;
|
||||||
|
gobject_class->set_property = pinos_ringbuffer_set_property;
|
||||||
|
gobject_class->get_property = pinos_ringbuffer_get_property;
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_MODE,
|
||||||
|
g_param_spec_enum ("mode",
|
||||||
|
"Mode",
|
||||||
|
"The mode of the ringbuffer",
|
||||||
|
PINOS_TYPE_RINGBUFFER_MODE,
|
||||||
|
PINOS_RINGBUFFER_MODE_READ,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_SIZE,
|
||||||
|
g_param_spec_uint ("size",
|
||||||
|
"Size",
|
||||||
|
"The size of the ringbuffer",
|
||||||
|
1,
|
||||||
|
G_MAXUINT,
|
||||||
|
64 * 1024,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_FD,
|
||||||
|
g_param_spec_int ("fd",
|
||||||
|
"Fd",
|
||||||
|
"The file descriptor with memory",
|
||||||
|
-1,
|
||||||
|
G_MAXINT,
|
||||||
|
-1,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_FDSIZE,
|
||||||
|
g_param_spec_uint ("fdsize",
|
||||||
|
"Fd Size",
|
||||||
|
"Size of the memory",
|
||||||
|
1,
|
||||||
|
G_MAXUINT,
|
||||||
|
-1,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class,
|
||||||
|
PROP_SEMAPHORE,
|
||||||
|
g_param_spec_int ("semaphore",
|
||||||
|
"Semaphore",
|
||||||
|
"Semaphore file desciptor",
|
||||||
|
-1,
|
||||||
|
G_MAXINT,
|
||||||
|
-1,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_ringbuffer_init (PinosRingbuffer * rb)
|
||||||
|
{
|
||||||
|
PinosRingbufferPrivate *priv = rb->priv = PINOS_RINGBUFFER_GET_PRIVATE (rb);
|
||||||
|
|
||||||
|
g_debug ("ringbuffer %p: new %u", rb, priv->size);
|
||||||
|
|
||||||
|
priv->mode = PINOS_RINGBUFFER_MODE_READ;
|
||||||
|
priv->size = 0;
|
||||||
|
priv->fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PinosRingbuffer *
|
||||||
|
pinos_ringbuffer_new (PinosRingbufferMode mode,
|
||||||
|
gsize size)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb;
|
||||||
|
|
||||||
|
g_return_val_if_fail (size > 0, NULL);
|
||||||
|
|
||||||
|
rb = g_object_new (PINOS_TYPE_RINGBUFFER,
|
||||||
|
"size", size,
|
||||||
|
"mode", mode,
|
||||||
|
NULL);
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
PinosRingbuffer *
|
||||||
|
pinos_ringbuffer_new_import (PinosRingbufferMode mode,
|
||||||
|
guint fdsize,
|
||||||
|
int fd,
|
||||||
|
int semaphore)
|
||||||
|
{
|
||||||
|
PinosRingbuffer *rb;
|
||||||
|
|
||||||
|
g_return_val_if_fail (fdsize > 0, NULL);
|
||||||
|
g_return_val_if_fail (fd >= 0, NULL);
|
||||||
|
|
||||||
|
rb = g_object_new (PINOS_TYPE_RINGBUFFER,
|
||||||
|
"mode", mode,
|
||||||
|
"fd", fd,
|
||||||
|
"fdsize", fdsize,
|
||||||
|
"semaphore", semaphore,
|
||||||
|
NULL);
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
pinos_ringbuffer_get_read_areas (PinosRingbuffer *rbuf,
|
||||||
|
PinosRingbufferArea areas[2])
|
||||||
|
{
|
||||||
|
PinosRingbufferPrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||||
|
priv = rbuf->priv;
|
||||||
|
|
||||||
|
spa_ringbuffer_get_read_areas (&priv->data->rbuf, (SpaRingbufferArea *)areas);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
pinos_ringbuffer_get_write_areas (PinosRingbuffer *rbuf,
|
||||||
|
PinosRingbufferArea areas[2])
|
||||||
|
{
|
||||||
|
PinosRingbufferPrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||||
|
priv = rbuf->priv;
|
||||||
|
|
||||||
|
spa_ringbuffer_get_write_areas (&priv->data->rbuf, (SpaRingbufferArea *)areas);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
pinos_ringbuffer_read_advance (PinosRingbuffer *rbuf,
|
||||||
|
gssize len)
|
||||||
|
{
|
||||||
|
PinosRingbufferPrivate *priv;
|
||||||
|
guint64 val;
|
||||||
|
|
||||||
|
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||||
|
priv = rbuf->priv;
|
||||||
|
|
||||||
|
spa_ringbuffer_read_advance (&priv->data->rbuf, len);
|
||||||
|
|
||||||
|
if (priv->mode == PINOS_RINGBUFFER_MODE_READ) {
|
||||||
|
val = 1;
|
||||||
|
write (priv->semaphore, &val, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
pinos_ringbuffer_write_advance (PinosRingbuffer *rbuf,
|
||||||
|
gssize len)
|
||||||
|
{
|
||||||
|
PinosRingbufferPrivate *priv;
|
||||||
|
guint64 val;
|
||||||
|
|
||||||
|
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||||
|
priv = rbuf->priv;
|
||||||
|
|
||||||
|
spa_ringbuffer_write_advance (&priv->data->rbuf, len);
|
||||||
|
|
||||||
|
if (priv->mode == PINOS_RINGBUFFER_MODE_WRITE) {
|
||||||
|
val = 1;
|
||||||
|
write (priv->semaphore, &val, 8);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
97
pinos/client/ringbuffer.h
Normal file
97
pinos/client/ringbuffer.h
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
/* Pinos
|
||||||
|
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINOS_RINGBUFFER_H__
|
||||||
|
#define __PINOS_RINGBUFFER_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _PinosRingbuffer PinosRingbuffer;
|
||||||
|
typedef struct _PinosRingbufferClass PinosRingbufferClass;
|
||||||
|
typedef struct _PinosRingbufferPrivate PinosRingbufferPrivate;
|
||||||
|
|
||||||
|
#include <pinos/client/introspect.h>
|
||||||
|
#include <pinos/client/buffer.h>
|
||||||
|
|
||||||
|
#define PINOS_TYPE_RINGBUFFER (pinos_ringbuffer_get_type ())
|
||||||
|
#define PINOS_IS_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_RINGBUFFER))
|
||||||
|
#define PINOS_IS_RINGBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_RINGBUFFER))
|
||||||
|
#define PINOS_RINGBUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_RINGBUFFER, PinosRingbufferClass))
|
||||||
|
#define PINOS_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_RINGBUFFER, PinosRingbuffer))
|
||||||
|
#define PINOS_RINGBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_RINGBUFFER, PinosRingbufferClass))
|
||||||
|
#define PINOS_RINGBUFFER_CAST(obj) ((PinosRingbuffer*)(obj))
|
||||||
|
#define PINOS_RINGBUFFER_CLASS_CAST(klass) ((PinosRingbufferClass*)(klass))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PinosRingbuffer:
|
||||||
|
*
|
||||||
|
* Pinos ringbuffer object class.
|
||||||
|
*/
|
||||||
|
struct _PinosRingbuffer {
|
||||||
|
GObject object;
|
||||||
|
|
||||||
|
PinosRingbufferPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PinosRingbufferClass:
|
||||||
|
*
|
||||||
|
* Pinos ringbuffer object class.
|
||||||
|
*/
|
||||||
|
struct _PinosRingbufferClass {
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*PinosRingbufferCallback) (PinosRingbuffer *rb, gpointer user_data);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PINOS_RINGBUFFER_MODE_READ,
|
||||||
|
PINOS_RINGBUFFER_MODE_WRITE,
|
||||||
|
} PinosRingbufferMode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gpointer data;
|
||||||
|
gsize len;
|
||||||
|
} PinosRingbufferArea;
|
||||||
|
|
||||||
|
/* normal GObject stuff */
|
||||||
|
GType pinos_ringbuffer_get_type (void);
|
||||||
|
|
||||||
|
PinosRingbuffer * pinos_ringbuffer_new (PinosRingbufferMode mode,
|
||||||
|
gsize size);
|
||||||
|
PinosRingbuffer * pinos_ringbuffer_new_import (PinosRingbufferMode mode,
|
||||||
|
guint fdsize,
|
||||||
|
int fd,
|
||||||
|
int semaphore);
|
||||||
|
|
||||||
|
gboolean pinos_ringbuffer_get_read_areas (PinosRingbuffer *rbuf,
|
||||||
|
PinosRingbufferArea areas[2]);
|
||||||
|
gboolean pinos_ringbuffer_read_advance (PinosRingbuffer *rbuf,
|
||||||
|
gssize len);
|
||||||
|
|
||||||
|
gboolean pinos_ringbuffer_get_write_areas (PinosRingbuffer *rbuf,
|
||||||
|
PinosRingbufferArea areas[2]);
|
||||||
|
gboolean pinos_ringbuffer_write_advance (PinosRingbuffer *rbuf,
|
||||||
|
gssize len);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __PINOS_RINGBUFFER_H__ */
|
||||||
|
|
@ -851,6 +851,38 @@ pinos_stream_connect (PinosStream *stream,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_ringbuffer (GObject *source_object,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PinosStream *stream = user_data;
|
||||||
|
PinosStreamPrivate *priv = stream->priv;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
g_assert (priv->port == PINOS_PORT (source_object));
|
||||||
|
|
||||||
|
priv->ringbuffer = pinos_port_get_ringbuffer_finish (priv->port,
|
||||||
|
res,
|
||||||
|
&error);
|
||||||
|
if (priv->ringbuffer == NULL)
|
||||||
|
goto no_ringbuffer;
|
||||||
|
|
||||||
|
stream_set_state (stream, PINOS_STREAM_STATE_STREAMING, NULL);
|
||||||
|
g_object_unref (stream);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_ringbuffer:
|
||||||
|
{
|
||||||
|
g_warning ("failed to get ringbuffer: %s", error->message);
|
||||||
|
stream_set_state (stream, PINOS_STREAM_STATE_ERROR, error);
|
||||||
|
g_object_unref (stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
do_start (PinosStream *stream)
|
do_start (PinosStream *stream)
|
||||||
{
|
{
|
||||||
|
|
@ -1110,7 +1142,6 @@ pinos_stream_send_buffer (PinosStream *stream,
|
||||||
g_return_val_if_fail (buffer != NULL, FALSE);
|
g_return_val_if_fail (buffer != NULL, FALSE);
|
||||||
|
|
||||||
priv = stream->priv;
|
priv = stream->priv;
|
||||||
g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
|
|
||||||
|
|
||||||
if (!pinos_io_write_buffer (priv->fd, buffer, &error)) {
|
if (!pinos_io_write_buffer (priv->fd, buffer, &error)) {
|
||||||
g_warning ("stream %p: failed to read buffer: %s", stream, error->message);
|
g_warning ("stream %p: failed to read buffer: %s", stream, error->message);
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ main (gint argc, gchar *argv[])
|
||||||
factory = pinos_gst_node_factory_new ("gst-node-factory");
|
factory = pinos_gst_node_factory_new ("gst-node-factory");
|
||||||
pinos_daemon_add_node_factory (daemon, factory);
|
pinos_daemon_add_node_factory (daemon, factory);
|
||||||
|
|
||||||
|
pinos_spa_alsa_sink_new (daemon, "alsa-sink", NULL);
|
||||||
pinos_daemon_start (daemon);
|
pinos_daemon_start (daemon);
|
||||||
|
|
||||||
g_main_loop_run (loop);
|
g_main_loop_run (loop);
|
||||||
|
|
|
||||||
|
|
@ -143,5 +143,12 @@
|
||||||
|
|
||||||
<method name='Remove'/>
|
<method name='Remove'/>
|
||||||
|
|
||||||
|
<method name='GetRingbuffer'>
|
||||||
|
<arg type='a{sv}' name='properties' direction='in'/>
|
||||||
|
<arg type='h' name='buffermem' direction='out'/>
|
||||||
|
<arg type='u' name='buffersize' direction='out'/>
|
||||||
|
<arg type='h' name='fd' direction='out'/>
|
||||||
|
</method>
|
||||||
|
|
||||||
</interface>
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
|
|
|
||||||
|
|
@ -467,23 +467,6 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
||||||
pinos_main_loop_wait (pinossink->loop);
|
pinos_main_loop_wait (pinossink->loop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
|
||||||
PinosBufferBuilder builder;
|
|
||||||
PinosPacketFormatChange change;
|
|
||||||
PinosBuffer pbuf;
|
|
||||||
|
|
||||||
pinos_stream_buffer_builder_init (pinossink->stream, &builder);
|
|
||||||
|
|
||||||
change.id = 1;
|
|
||||||
change.format = g_bytes_get_data (format, NULL);
|
|
||||||
pinos_buffer_builder_add_format_change (&builder, &change);
|
|
||||||
pinos_buffer_builder_end (&builder, &pbuf);
|
|
||||||
|
|
||||||
g_debug ("sending format");
|
|
||||||
res = pinos_stream_send_buffer (pinossink->stream, &pbuf);
|
|
||||||
pinos_buffer_unref (&pbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pinos_main_loop_unlock (pinossink->loop);
|
pinos_main_loop_unlock (pinossink->loop);
|
||||||
g_bytes_unref (format);
|
g_bytes_unref (format);
|
||||||
|
|
||||||
|
|
|
||||||
531
pinos/modules/spa/spa-alsa-sink.c
Normal file
531
pinos/modules/spa/spa-alsa-sink.c
Normal file
|
|
@ -0,0 +1,531 @@
|
||||||
|
/* Pinos
|
||||||
|
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include <spa/include/spa/node.h>
|
||||||
|
#include <spa/include/spa/audio/raw.h>
|
||||||
|
|
||||||
|
#include "spa-alsa-sink.h"
|
||||||
|
|
||||||
|
#define PINOS_SPA_ALSA_SINK_GET_PRIVATE(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkPrivate))
|
||||||
|
|
||||||
|
struct _PinosSpaAlsaSinkPrivate
|
||||||
|
{
|
||||||
|
PinosPort *input;
|
||||||
|
|
||||||
|
PinosProperties *props;
|
||||||
|
PinosRingbuffer *ringbuffer;
|
||||||
|
|
||||||
|
SpaHandle *sink;
|
||||||
|
const SpaNode *sink_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_POSSIBLE_FORMATS
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (PinosSpaAlsaSink, pinos_spa_alsa_sink, PINOS_TYPE_SERVER_NODE);
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char *name)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
void *hnd;
|
||||||
|
SpaEnumHandleFactoryFunc enum_func;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) {
|
||||||
|
g_error ("can't load %s: %s", lib, dlerror());
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) {
|
||||||
|
g_error ("can't find enum function");
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; ;i++) {
|
||||||
|
const SpaHandleFactory *factory;
|
||||||
|
const void *iface;
|
||||||
|
|
||||||
|
if ((res = enum_func (i, &factory)) < 0) {
|
||||||
|
if (res != SPA_RESULT_ENUM_END)
|
||||||
|
g_error ("can't enumerate factories: %d", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strcmp (factory->name, name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((res = factory->instantiate (factory, handle)) < 0) {
|
||||||
|
g_error ("can't make factory instance: %d", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if ((res = (*handle)->get_interface (*handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) {
|
||||||
|
g_error ("can't get interface %d", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
*node = iface;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_sink_event (SpaHandle *handle, SpaEvent *event, void *user_data)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSink *this = user_data;
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case SPA_EVENT_TYPE_PULL_INPUT:
|
||||||
|
{
|
||||||
|
SpaBuffer *buf;
|
||||||
|
SpaInputInfo iinfo;
|
||||||
|
SpaOutputInfo oinfo;
|
||||||
|
SpaResult res;
|
||||||
|
PinosRingbufferArea areas[2];
|
||||||
|
uint8_t *data;
|
||||||
|
size_t size, towrite, total;
|
||||||
|
|
||||||
|
buf = event->data;
|
||||||
|
|
||||||
|
oinfo.port_id = 0;
|
||||||
|
oinfo.flags = SPA_OUTPUT_FLAG_PULL;
|
||||||
|
oinfo.buffer = buf;
|
||||||
|
oinfo.event = NULL;
|
||||||
|
|
||||||
|
g_debug ("pull ringbuffer %p", buf);
|
||||||
|
|
||||||
|
size = buf->size;
|
||||||
|
data = buf->datas[0].data;
|
||||||
|
|
||||||
|
pinos_ringbuffer_get_read_areas (priv->ringbuffer, areas);
|
||||||
|
|
||||||
|
total = MIN (size, areas[0].len + areas[1].len);
|
||||||
|
g_debug ("total read %zd %zd", total, areas[0].len + areas[1].len);
|
||||||
|
if (total < size) {
|
||||||
|
g_warning ("underrun");
|
||||||
|
}
|
||||||
|
towrite = MIN (size, areas[0].len);
|
||||||
|
memcpy (data, areas[0].data, towrite);
|
||||||
|
size -= towrite;
|
||||||
|
data += towrite;
|
||||||
|
towrite = MIN (size, areas[1].len);
|
||||||
|
memcpy (data, areas[1].data, towrite);
|
||||||
|
|
||||||
|
pinos_ringbuffer_read_advance (priv->ringbuffer, total);
|
||||||
|
|
||||||
|
buf->size = total;
|
||||||
|
|
||||||
|
iinfo.port_id = event->port_id;
|
||||||
|
iinfo.flags = SPA_INPUT_FLAG_NONE;
|
||||||
|
iinfo.buffer = oinfo.buffer;
|
||||||
|
iinfo.event = oinfo.event;
|
||||||
|
|
||||||
|
g_debug ("push sink %p", iinfo.buffer);
|
||||||
|
if ((res = priv->sink_node->push_port_input (priv->sink, 1, &iinfo)) < 0)
|
||||||
|
g_debug ("got error %d", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
g_debug ("got event %d", event->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_pipeline (PinosSpaAlsaSink *this)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||||
|
SpaResult res;
|
||||||
|
SpaProps *props;
|
||||||
|
SpaPropValue value;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
if ((res = make_node (&priv->sink, &priv->sink_node, "spa/build/plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) {
|
||||||
|
g_error ("can't create alsa-sink: %d", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
priv->sink_node->set_event_callback (priv->sink, on_sink_event, this);
|
||||||
|
|
||||||
|
if ((res = priv->sink_node->get_props (priv->sink, &props)) < 0)
|
||||||
|
g_debug ("got get_props error %d", res);
|
||||||
|
|
||||||
|
value.type = SPA_PROP_TYPE_STRING;
|
||||||
|
value.size = strlen ("hw:1")+1;
|
||||||
|
value.value = "hw:1";
|
||||||
|
props->set_prop (props, spa_props_index_for_name (props, "device"), &value);
|
||||||
|
|
||||||
|
if ((res = priv->sink_node->set_props (priv->sink, props)) < 0)
|
||||||
|
g_debug ("got set_props error %d", res);
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_ACTIVATE;
|
||||||
|
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||||
|
g_debug ("got activate error %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_pipeline (PinosSpaAlsaSink *sink)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
g_debug ("spa-alsa-sink %p: starting pipeline", sink);
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_START;
|
||||||
|
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||||
|
g_debug ("got error %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stop_pipeline (PinosSpaAlsaSink *sink)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
g_debug ("spa-alsa-sink %p: stopping pipeline", sink);
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_STOP;
|
||||||
|
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||||
|
g_debug ("got error %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
destroy_pipeline (PinosSpaAlsaSink *sink)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
g_debug ("spa-alsa-sink %p: destroy pipeline", sink);
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_DEACTIVATE;
|
||||||
|
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||||
|
g_debug ("got error %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
set_state (PinosNode *node,
|
||||||
|
PinosNodeState state)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSink *this = PINOS_SPA_ALSA_SINK (node);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||||
|
|
||||||
|
g_debug ("spa-alsa-sink %p: set state %s", node, pinos_node_state_as_string (state));
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case PINOS_NODE_STATE_SUSPENDED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PINOS_NODE_STATE_INITIALIZING:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PINOS_NODE_STATE_IDLE:
|
||||||
|
stop_pipeline (this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PINOS_NODE_STATE_RUNNING:
|
||||||
|
//start_pipeline (this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PINOS_NODE_STATE_ERROR:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pinos_node_update_state (node, state);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_linked (PinosPort *port, PinosPort *peer, gpointer user_data)
|
||||||
|
{
|
||||||
|
PinosNode *node = user_data;
|
||||||
|
gint n_peers;
|
||||||
|
|
||||||
|
g_debug ("port %p: linked", port);
|
||||||
|
|
||||||
|
n_peers = pinos_port_get_n_links (port);
|
||||||
|
if (n_peers == 1)
|
||||||
|
pinos_node_report_busy (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_unlinked (PinosPort *port, PinosPort *peer, gpointer user_data)
|
||||||
|
{
|
||||||
|
PinosNode *node = user_data;
|
||||||
|
gint n_peers;
|
||||||
|
|
||||||
|
g_debug ("port %p: unlinked", port);
|
||||||
|
n_peers = pinos_port_get_n_links (port);
|
||||||
|
if (n_peers == 0)
|
||||||
|
pinos_node_report_idle (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
negotiate_formats (PinosSpaAlsaSink *this)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||||
|
SpaResult res;
|
||||||
|
SpaFormat *format;
|
||||||
|
SpaProps *props;
|
||||||
|
uint32_t val;
|
||||||
|
SpaPropValue value;
|
||||||
|
|
||||||
|
if ((res = priv->sink_node->enum_port_formats (priv->sink, 0, 0, &format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
props = &format->props;
|
||||||
|
|
||||||
|
value.type = SPA_PROP_TYPE_UINT32;
|
||||||
|
value.size = sizeof (uint32_t);
|
||||||
|
value.value = &val;
|
||||||
|
|
||||||
|
val = SPA_AUDIO_FORMAT_S16LE;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_FORMAT), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = SPA_AUDIO_LAYOUT_INTERLEAVED;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_LAYOUT), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = 44100;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_RATE), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = 2;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = priv->sink_node->set_port_format (priv->sink, 0, 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
priv->ringbuffer = pinos_ringbuffer_new (PINOS_RINGBUFFER_MODE_READ, 64 * 1024);
|
||||||
|
|
||||||
|
g_object_set (priv->input, "ringbuffer", priv->ringbuffer, NULL);
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_received_buffer (PinosPort *port,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSink *this = user_data;
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||||
|
PinosBuffer *pbuf;
|
||||||
|
PinosBufferIter it;
|
||||||
|
|
||||||
|
pbuf = pinos_port_peek_buffer (port);
|
||||||
|
|
||||||
|
pinos_buffer_iter_init (&it, pbuf);
|
||||||
|
while (pinos_buffer_iter_next (&it)) {
|
||||||
|
switch (pinos_buffer_iter_get_type (&it)) {
|
||||||
|
case PINOS_PACKET_TYPE_HEADER:
|
||||||
|
{
|
||||||
|
PinosPacketHeader hdr;
|
||||||
|
|
||||||
|
if (!pinos_buffer_iter_parse_header (&it, &hdr))
|
||||||
|
break;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PINOS_PACKET_TYPE_FD_PAYLOAD:
|
||||||
|
{
|
||||||
|
PinosPacketFDPayload p;
|
||||||
|
int fd;
|
||||||
|
PinosRingbufferArea areas[2];
|
||||||
|
uint8_t *data, *d;
|
||||||
|
size_t size, towrite, total;
|
||||||
|
|
||||||
|
if (!pinos_buffer_iter_parse_fd_payload (&it, &p))
|
||||||
|
break;
|
||||||
|
|
||||||
|
g_debug ("got fd payload id %d", p.id);
|
||||||
|
fd = pinos_buffer_get_fd (pbuf, p.fd_index);
|
||||||
|
if (fd == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
d = data = mmap (NULL, p.size, PROT_READ, MAP_PRIVATE, fd, p.offset);
|
||||||
|
size = p.size;
|
||||||
|
|
||||||
|
pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas);
|
||||||
|
|
||||||
|
total = MIN (size, areas[0].len + areas[1].len);
|
||||||
|
g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len);
|
||||||
|
towrite = MIN (size, areas[0].len);
|
||||||
|
memcpy (areas[0].data, data, towrite);
|
||||||
|
size -= towrite;
|
||||||
|
data += towrite;
|
||||||
|
towrite = MIN (size, areas[1].len);
|
||||||
|
memcpy (areas[1].data, data, towrite);
|
||||||
|
|
||||||
|
pinos_ringbuffer_write_advance (priv->ringbuffer, total);
|
||||||
|
|
||||||
|
munmap (d, p.size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PINOS_PACKET_TYPE_FORMAT_CHANGE:
|
||||||
|
{
|
||||||
|
PinosPacketFormatChange change;
|
||||||
|
|
||||||
|
if (!pinos_buffer_iter_parse_format_change (&it, &change))
|
||||||
|
break;
|
||||||
|
g_debug ("got format change %d %s", change.id, change.format);
|
||||||
|
|
||||||
|
negotiate_formats (this);
|
||||||
|
start_pipeline (this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pinos_buffer_iter_end (&it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_input_port_created (GObject *source_object,
|
||||||
|
GAsyncResult *res,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
PinosNode *node = PINOS_NODE (source_object);
|
||||||
|
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
|
||||||
|
priv->input = pinos_node_create_port_finish (node, res, NULL);
|
||||||
|
|
||||||
|
pinos_port_set_received_buffer_cb (priv->input, on_received_buffer, sink, NULL);
|
||||||
|
|
||||||
|
g_signal_connect (priv->input, "linked", (GCallback) on_linked, node);
|
||||||
|
g_signal_connect (priv->input, "unlinked", (GCallback) on_unlinked, node);
|
||||||
|
|
||||||
|
create_pipeline (sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sink_constructed (GObject * object)
|
||||||
|
{
|
||||||
|
PinosServerNode *node = PINOS_SERVER_NODE (object);
|
||||||
|
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->constructed (object);
|
||||||
|
|
||||||
|
pinos_node_create_port (PINOS_NODE (node),
|
||||||
|
PINOS_DIRECTION_INPUT,
|
||||||
|
"input",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
on_input_port_created,
|
||||||
|
node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sink_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
PinosServerNode *node = PINOS_SERVER_NODE (object);
|
||||||
|
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||||
|
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||||
|
|
||||||
|
destroy_pipeline (sink);
|
||||||
|
pinos_node_remove_port (PINOS_NODE (node), priv->input);
|
||||||
|
pinos_properties_free (priv->props);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_spa_alsa_sink_class_init (PinosSpaAlsaSinkClass * klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
PinosNodeClass *node_class = PINOS_NODE_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (klass, sizeof (PinosSpaAlsaSinkPrivate));
|
||||||
|
|
||||||
|
gobject_class->constructed = sink_constructed;
|
||||||
|
gobject_class->finalize = sink_finalize;
|
||||||
|
gobject_class->get_property = get_property;
|
||||||
|
gobject_class->set_property = set_property;
|
||||||
|
|
||||||
|
node_class->set_state = set_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinos_spa_alsa_sink_init (PinosSpaAlsaSink * sink)
|
||||||
|
{
|
||||||
|
PinosSpaAlsaSinkPrivate *priv;
|
||||||
|
|
||||||
|
priv = sink->priv = PINOS_SPA_ALSA_SINK_GET_PRIVATE (sink);
|
||||||
|
priv->props = pinos_properties_new (NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
PinosServerNode *
|
||||||
|
pinos_spa_alsa_sink_new (PinosDaemon *daemon,
|
||||||
|
const gchar *name,
|
||||||
|
PinosProperties *properties)
|
||||||
|
{
|
||||||
|
PinosServerNode *node;
|
||||||
|
|
||||||
|
node = g_object_new (PINOS_TYPE_SPA_ALSA_SINK,
|
||||||
|
"daemon", daemon,
|
||||||
|
"name", name,
|
||||||
|
"properties", properties,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
61
pinos/modules/spa/spa-alsa-sink.h
Normal file
61
pinos/modules/spa/spa-alsa-sink.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* Pinos
|
||||||
|
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PINOS_SPA_ALSA_SINK_H__
|
||||||
|
#define __PINOS_SPA_ALSA_SINK_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include <client/pinos.h>
|
||||||
|
#include <server/server-node.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define PINOS_TYPE_SPA_ALSA_SINK (pinos_spa_alsa_sink_get_type ())
|
||||||
|
#define PINOS_IS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_ALSA_SINK))
|
||||||
|
#define PINOS_IS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_ALSA_SINK))
|
||||||
|
#define PINOS_SPA_ALSA_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass))
|
||||||
|
#define PINOS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSink))
|
||||||
|
#define PINOS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass))
|
||||||
|
#define PINOS_SPA_ALSA_SINK_CAST(obj) ((PinosSpaAlsaSink*)(obj))
|
||||||
|
#define PINOS_SPA_ALSA_SINK_CLASS_CAST(klass) ((PinosSpaAlsaSinkClass*)(klass))
|
||||||
|
|
||||||
|
typedef struct _PinosSpaAlsaSink PinosSpaAlsaSink;
|
||||||
|
typedef struct _PinosSpaAlsaSinkClass PinosSpaAlsaSinkClass;
|
||||||
|
typedef struct _PinosSpaAlsaSinkPrivate PinosSpaAlsaSinkPrivate;
|
||||||
|
|
||||||
|
struct _PinosSpaAlsaSink {
|
||||||
|
PinosServerNode object;
|
||||||
|
|
||||||
|
PinosSpaAlsaSinkPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _PinosSpaAlsaSinkClass {
|
||||||
|
PinosServerNodeClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType pinos_spa_alsa_sink_get_type (void);
|
||||||
|
|
||||||
|
PinosServerNode * pinos_spa_alsa_sink_new (PinosDaemon *daemon,
|
||||||
|
const gchar *name,
|
||||||
|
PinosProperties *properties);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __PINOS_SPA_ALSA_SINK_H__ */
|
||||||
|
|
@ -59,6 +59,10 @@ struct _PinosPort {
|
||||||
*/
|
*/
|
||||||
struct _PinosPortClass {
|
struct _PinosPortClass {
|
||||||
GObjectClass parent_class;
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (*get_ringbuffer) (PinosPort *port,
|
||||||
|
PinosProperties *props,
|
||||||
|
GTask *task);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef gboolean (*PinosBufferCallback) (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data);
|
typedef gboolean (*PinosBufferCallback) (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data);
|
||||||
|
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_BUFFER_H__
|
|
||||||
#define __SPI_BUFFER_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _SpiBuffer SpiBuffer;
|
|
||||||
|
|
||||||
#include <spi/defs.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SPI_META_TYPE_INVALID = 0,
|
|
||||||
SPI_META_TYPE_HEADER,
|
|
||||||
} SpiMetaType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t flags;
|
|
||||||
uint32_t seq;
|
|
||||||
int64_t pts;
|
|
||||||
int64_t dts_offset;
|
|
||||||
} SpiMetaHeader;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SpiMetaType type;
|
|
||||||
void *data;
|
|
||||||
size_t size;
|
|
||||||
} SpiMeta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiDataType:
|
|
||||||
* @SPI_DATA_TYPE_INVALID: invalid data type, is ignored
|
|
||||||
* @SPI_DATA_TYPE_MEMPTR: data and size point to memory
|
|
||||||
* @SPI_DATA_TYPE_FD: data points to SpiDataFd
|
|
||||||
* @SPI_DATA_TYPE_FD: data points to SpiDataFd
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_DATA_TYPE_INVALID = 0,
|
|
||||||
SPI_DATA_TYPE_MEMPTR,
|
|
||||||
SPI_DATA_TYPE_FD,
|
|
||||||
} SpiDataType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiDataFd
|
|
||||||
* fd: a file descriptor
|
|
||||||
* offset: offset in the data referenced by @fd
|
|
||||||
* @size: size of data referenced by fd
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
int fd;
|
|
||||||
unsigned int offset;
|
|
||||||
size_t size;
|
|
||||||
} SpiDataFD;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiData:
|
|
||||||
* @id: user id
|
|
||||||
* @type: the type of data
|
|
||||||
* @data: pointer to data
|
|
||||||
* @size: size of data
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
SpiDataType type;
|
|
||||||
void *data;
|
|
||||||
size_t size;
|
|
||||||
} SpiData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiBuffer:
|
|
||||||
* @refcount: reference counter
|
|
||||||
* @notify: called when the refcount reaches 0
|
|
||||||
* @size: total size of the buffer
|
|
||||||
* @n_metas: number of metadata
|
|
||||||
* @metas: array of @n_metas metadata
|
|
||||||
* @n_datas: number of data pointers
|
|
||||||
* @datas: array of @n_datas data pointers
|
|
||||||
*/
|
|
||||||
struct _SpiBuffer {
|
|
||||||
volatile int refcount;
|
|
||||||
SpiNotify notify;
|
|
||||||
size_t size;
|
|
||||||
unsigned int n_metas;
|
|
||||||
SpiMeta *metas;
|
|
||||||
unsigned int n_datas;
|
|
||||||
SpiData *datas;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline SpiBuffer *
|
|
||||||
spi_buffer_ref (SpiBuffer *buffer)
|
|
||||||
{
|
|
||||||
if (buffer != NULL)
|
|
||||||
buffer->refcount++;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline SpiBuffer *
|
|
||||||
spi_buffer_unref (SpiBuffer *buffer)
|
|
||||||
{
|
|
||||||
if (buffer != NULL) {
|
|
||||||
if (--buffer->refcount == 0) {
|
|
||||||
if (buffer->notify)
|
|
||||||
buffer->notify (buffer);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPI_BUFFER_H__ */
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_DEFS_H__
|
|
||||||
#define __SPI_DEFS_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#else
|
|
||||||
#include <stdbool.h>
|
|
||||||
#endif
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SPI_RESULT_OK = 0,
|
|
||||||
SPI_RESULT_ERROR = -1,
|
|
||||||
SPI_RESULT_INACTIVE = -2,
|
|
||||||
SPI_RESULT_NO_FORMAT = -3,
|
|
||||||
SPI_RESULT_INVALID_COMMAND = -4,
|
|
||||||
SPI_RESULT_INVALID_PORT = -5,
|
|
||||||
SPI_RESULT_HAVE_ENOUGH_INPUT = -6,
|
|
||||||
SPI_RESULT_NEED_MORE_INPUT = -7,
|
|
||||||
SPI_RESULT_PORTS_CHANGED = -9,
|
|
||||||
SPI_RESULT_FORMAT_CHANGED = -10,
|
|
||||||
SPI_RESULT_PROPERTIES_CHANGED = -11,
|
|
||||||
SPI_RESULT_NOT_IMPLEMENTED = -12,
|
|
||||||
SPI_RESULT_INVALID_PARAM_ID = -13,
|
|
||||||
SPI_RESULT_PARAM_UNSET = -14,
|
|
||||||
SPI_RESULT_ENUM_END = -15,
|
|
||||||
SPI_RESULT_WRONG_PARAM_TYPE = -16,
|
|
||||||
SPI_RESULT_WRONG_PARAM_SIZE = -17,
|
|
||||||
SPI_RESULT_INVALID_MEDIA_TYPE = -18,
|
|
||||||
SPI_RESULT_INVALID_FORMAT_PARAMS = -19,
|
|
||||||
SPI_RESULT_FORMAT_INCOMPLETE = -20,
|
|
||||||
SPI_RESULT_INVALID_ARGUMENTS = -21,
|
|
||||||
SPI_RESULT_UNKNOWN_INTERFACE = -22,
|
|
||||||
} SpiResult;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SPI_DIRECTION_INVALID = 0,
|
|
||||||
SPI_DIRECTION_INPUT,
|
|
||||||
SPI_DIRECTION_OUTPUT
|
|
||||||
} SpiDirection;
|
|
||||||
|
|
||||||
typedef void (*SpiNotify) (void *data);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* __SPI_DEFS_H__ */
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_EVENT_H__
|
|
||||||
#define __SPI_EVENT_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _SpiEvent SpiEvent;
|
|
||||||
|
|
||||||
#include <spi/defs.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiEventType:
|
|
||||||
* @SPI_EVENT_TYPE_INVALID: invalid event, should be ignored
|
|
||||||
* @SPI_EVENT_TYPE_ACTIVATED: emited when the ACTIVATE command completes
|
|
||||||
* @SPI_EVENT_TYPE_DEACTIVATED: emited when the DEACTIVATE command completes
|
|
||||||
* @SPI_EVENT_TYPE_CAN_PULL_OUTPUT: emited when an async node has output that can be pulled
|
|
||||||
* @SPI_EVENT_TYPE_CAN_PUSH_INTPUT: emited when more data can be pushed to an async node
|
|
||||||
* @SPI_EVENT_TYPE_PULL_INPUT: emited when data needs to be provided on an input
|
|
||||||
* @SPI_EVENT_TYPE_ADD_POLL: emited when a pollfd should be added
|
|
||||||
* @SPI_EVENT_TYPE_REMOVE_POLL: emited when a pollfd should be removed
|
|
||||||
* @SPI_EVENT_TYPE_DRAINED: emited when DRAIN command completed
|
|
||||||
* @SPI_EVENT_TYPE_MARKER: emited when MARK command completed
|
|
||||||
* @SPI_EVENT_TYPE_ERROR: emited when error occured
|
|
||||||
* @SPI_EVENT_TYPE_BUFFERING: emited when buffering is in progress
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_EVENT_TYPE_INVALID = 0,
|
|
||||||
SPI_EVENT_TYPE_ACTIVATED,
|
|
||||||
SPI_EVENT_TYPE_DEACTIVATED,
|
|
||||||
SPI_EVENT_TYPE_CAN_PULL_OUTPUT,
|
|
||||||
SPI_EVENT_TYPE_CAN_PUSH_INTPUT,
|
|
||||||
SPI_EVENT_TYPE_PULL_INPUT,
|
|
||||||
SPI_EVENT_TYPE_ADD_POLL,
|
|
||||||
SPI_EVENT_TYPE_REMOVE_POLL,
|
|
||||||
SPI_EVENT_TYPE_DRAINED,
|
|
||||||
SPI_EVENT_TYPE_MARKER,
|
|
||||||
SPI_EVENT_TYPE_ERROR,
|
|
||||||
SPI_EVENT_TYPE_BUFFERING,
|
|
||||||
} SpiEventType;
|
|
||||||
|
|
||||||
struct _SpiEvent {
|
|
||||||
volatile int refcount;
|
|
||||||
SpiNotify notify;
|
|
||||||
SpiEventType type;
|
|
||||||
uint32_t port_id;
|
|
||||||
void *data;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPI_EVENT_H__ */
|
|
||||||
367
pinos/spi/node.h
367
pinos/spi/node.h
|
|
@ -1,367 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_NODE_H__
|
|
||||||
#define __SPI_NODE_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _SpiNode SpiNode;
|
|
||||||
|
|
||||||
#include <spi/defs.h>
|
|
||||||
#include <spi/plugin.h>
|
|
||||||
#include <spi/params.h>
|
|
||||||
#include <spi/port.h>
|
|
||||||
#include <spi/event.h>
|
|
||||||
#include <spi/buffer.h>
|
|
||||||
#include <spi/command.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiInputFlags:
|
|
||||||
* @SPI_INPUT_FLAG_NONE: no flag
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_INPUT_FLAG_NONE = 0,
|
|
||||||
} SpiInputFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiInputInfo:
|
|
||||||
* @port_id: the port id
|
|
||||||
* @flags: extra flags
|
|
||||||
* @buffer: a buffer
|
|
||||||
*
|
|
||||||
* Input information for a node.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t port_id;
|
|
||||||
SpiInputFlags flags;
|
|
||||||
SpiBuffer *buffer;
|
|
||||||
SpiEvent *event;
|
|
||||||
SpiResult status;
|
|
||||||
} SpiInputInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiOutputFlags:
|
|
||||||
* @SPI_OUTPUT_FLAG_NONE: no flag
|
|
||||||
* @SPI_OUTPUT_FLAG_PULL: force a #SPI_EVENT_NEED_INPUT event on the
|
|
||||||
* peer input ports when no data is available.
|
|
||||||
* @SPI_OUTPUT_FLAG_DISCARD: discard the buffer data
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_OUTPUT_FLAG_NONE = 0,
|
|
||||||
SPI_OUTPUT_FLAG_PULL = (1 << 0),
|
|
||||||
SPI_OUTPUT_FLAG_DISCARD = (1 << 1),
|
|
||||||
} SpiOutputFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiOutputInfo:
|
|
||||||
* @port_id: the port id
|
|
||||||
* @flags: extra flags
|
|
||||||
* @buffer: a buffer
|
|
||||||
* @event: an event
|
|
||||||
*
|
|
||||||
* Output information for a node.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t port_id;
|
|
||||||
SpiOutputFlags flags;
|
|
||||||
SpiBuffer *buffer;
|
|
||||||
SpiEvent *event;
|
|
||||||
SpiResult status;
|
|
||||||
} SpiOutputInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiEventCallback:
|
|
||||||
* @node: a #SpiHandle emiting the event
|
|
||||||
* @event: the event that was emited
|
|
||||||
* @user_data: user data provided when registering the callback
|
|
||||||
*
|
|
||||||
* This will be called when an out-of-bound event is notified
|
|
||||||
* on @node.
|
|
||||||
*/
|
|
||||||
typedef void (*SpiEventCallback) (SpiHandle *handle,
|
|
||||||
SpiEvent *event,
|
|
||||||
void *user_data);
|
|
||||||
|
|
||||||
#define SPI_INTERFACE_ID_NODE 0
|
|
||||||
#define SPI_INTERFACE_ID_NODE_NAME "Node interface"
|
|
||||||
#define SPI_INTERFACE_ID_NODE_DESCRIPTION "Main processing node interface"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiNode:
|
|
||||||
*
|
|
||||||
* The main processing nodes.
|
|
||||||
*/
|
|
||||||
struct _SpiNode {
|
|
||||||
/* the total size of this node. This can be used to expand this
|
|
||||||
* structure in the future */
|
|
||||||
size_t size;
|
|
||||||
/**
|
|
||||||
* SpiNode::get_params:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @props: a location for a #SpiParams pointer
|
|
||||||
*
|
|
||||||
* Get the configurable parameters of @node.
|
|
||||||
*
|
|
||||||
* The returned @props is a snapshot of the current configuration and
|
|
||||||
* can be modified. The modifications will take effect after a call
|
|
||||||
* to SpiNode::set_params.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or props are %NULL
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no properties
|
|
||||||
* implemented on @node
|
|
||||||
*/
|
|
||||||
SpiResult (*get_params) (SpiHandle *handle,
|
|
||||||
SpiParams **props);
|
|
||||||
/**
|
|
||||||
* SpiNode::set_params:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @props: a #SpiParams
|
|
||||||
*
|
|
||||||
* Set the configurable parameters in @node.
|
|
||||||
*
|
|
||||||
* Usually, @props will be obtained from SpiNode::get_params and then
|
|
||||||
* modified but it is also possible to set another #SpiParams object
|
|
||||||
* as long as its keys and types match those of SpiParams::get_params.
|
|
||||||
*
|
|
||||||
* Properties with keys that are not known are ignored.
|
|
||||||
*
|
|
||||||
* If @props is NULL, all the parameters are reset to their defaults.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when no properties can be
|
|
||||||
* modified on @node.
|
|
||||||
* #SPI_RESULT_WRONG_PARAM_TYPE when a property has the wrong
|
|
||||||
* type.
|
|
||||||
*/
|
|
||||||
SpiResult (*set_params) (SpiHandle *handle,
|
|
||||||
const SpiParams *props);
|
|
||||||
/**
|
|
||||||
* SpiNode::send_command:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @command: a #SpiCommand
|
|
||||||
*
|
|
||||||
* Send a command to @node.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or command is %NULL
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when this node can't process commands
|
|
||||||
* #SPI_RESULT_INVALID_COMMAND @command is an invalid command
|
|
||||||
*/
|
|
||||||
SpiResult (*send_command) (SpiHandle *handle,
|
|
||||||
SpiCommand *command);
|
|
||||||
/**
|
|
||||||
* SpiNode::set_event_callback:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @callback: a callback
|
|
||||||
* @user_data: user data passed in the callback
|
|
||||||
*
|
|
||||||
* Set a callback to receive events from @node. if @callback is %NULL, the
|
|
||||||
* current callback is removed.
|
|
||||||
*
|
|
||||||
* The callback can be emited from any thread. The caller should take
|
|
||||||
* appropriate actions to handle the event in other threads when needed.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
|
||||||
*/
|
|
||||||
SpiResult (*set_event_callback) (SpiHandle *handle,
|
|
||||||
SpiEventCallback callback,
|
|
||||||
void *user_data);
|
|
||||||
/**
|
|
||||||
* SpiNode::get_n_ports:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @n_input_ports: location to hold the number of input ports or %NULL
|
|
||||||
* @max_input_ports: location to hold the maximum number of input ports or %NULL
|
|
||||||
* @n_output_ports: location to hold the number of output ports or %NULL
|
|
||||||
* @max_output_ports: location to hold the maximum number of output ports or %NULL
|
|
||||||
*
|
|
||||||
* Get the current number of input and output ports and also the maximum
|
|
||||||
* number of ports.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
|
||||||
*/
|
|
||||||
SpiResult (*get_n_ports) (SpiHandle *handle,
|
|
||||||
unsigned int *n_input_ports,
|
|
||||||
unsigned int *max_input_ports,
|
|
||||||
unsigned int *n_output_ports,
|
|
||||||
unsigned int *max_output_ports);
|
|
||||||
/**
|
|
||||||
* SpiNode::get_port_ids:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @n_input_ports: size of the @input_ids array
|
|
||||||
* @input_ids: array to store the input stream ids
|
|
||||||
* @n_output_ports: size of the @output_ids array
|
|
||||||
* @output_ids: array to store the output stream ids
|
|
||||||
*
|
|
||||||
* Get the current number of input and output ports and also the maximum
|
|
||||||
* number of ports.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
|
||||||
*/
|
|
||||||
SpiResult (*get_port_ids) (SpiHandle *handle,
|
|
||||||
unsigned int n_input_ports,
|
|
||||||
uint32_t *input_ids,
|
|
||||||
unsigned int n_output_ports,
|
|
||||||
uint32_t *output_ids);
|
|
||||||
|
|
||||||
SpiResult (*add_port) (SpiHandle *handle,
|
|
||||||
SpiDirection direction,
|
|
||||||
uint32_t *port_id);
|
|
||||||
SpiResult (*remove_port) (SpiHandle *handle,
|
|
||||||
uint32_t port_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiNode::enum_port_formats:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @port_id: the port to query
|
|
||||||
* @index: the format index to retrieve
|
|
||||||
* @format: pointer to a format
|
|
||||||
*
|
|
||||||
* Enumerate all possible formats on @port_id of @node.
|
|
||||||
*
|
|
||||||
* Use the index to retrieve the formats one by one until the function
|
|
||||||
* returns #SPI_RESULT_ENUM_END.
|
|
||||||
*
|
|
||||||
* The result format can be queried and modified and ultimately be used
|
|
||||||
* to call SpiNode::set_port_format.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or format is %NULL
|
|
||||||
* #SPI_RESULT_INVALID_PORT when port_id is not valid
|
|
||||||
* #SPI_RESULT_ENUM_END when no format exists for @index
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
SpiResult (*enum_port_formats) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
unsigned int index,
|
|
||||||
SpiParams **format);
|
|
||||||
/**
|
|
||||||
* SpiNode::set_port_format:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @port_id: the port to configure
|
|
||||||
* @format: a #SpiParam with the format
|
|
||||||
*
|
|
||||||
* Set a format on @port_id of @node.
|
|
||||||
*
|
|
||||||
* When @format is %NULL, the current format will be removed.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
|
||||||
* #SPI_RESULT_INVALID_PORT when port_id is not valid
|
|
||||||
* #SPI_RESULT_INVALID_MEDIA_TYPE when the media type is not valid
|
|
||||||
* #SPI_RESULT_INVALID_FORMAT_PARAMS when one of the mandatory format
|
|
||||||
* parameters is not specified.
|
|
||||||
* #SPI_RESULT_WRONG_PARAM_TYPE when the type of size of a parameter
|
|
||||||
* is not correct.
|
|
||||||
*/
|
|
||||||
SpiResult (*set_port_format) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
int test_only,
|
|
||||||
const SpiParams *format);
|
|
||||||
/**
|
|
||||||
* SpiNode::get_port_format:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @port_id: the port to query
|
|
||||||
* @format: a pointer to a location to hold the #SpiParam
|
|
||||||
*
|
|
||||||
* Get the format on @port_id of @node. The result #SpiParam can
|
|
||||||
* not be modified.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when @node or @format is %NULL
|
|
||||||
* #SPI_RESULT_INVALID_PORT when @port_id is not valid
|
|
||||||
* #SPI_RESULT_INVALID_NO_FORMAT when no format was set
|
|
||||||
*/
|
|
||||||
SpiResult (*get_port_format) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
const SpiParams **format);
|
|
||||||
|
|
||||||
SpiResult (*get_port_info) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
SpiPortInfo *info);
|
|
||||||
|
|
||||||
SpiResult (*get_port_params) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
SpiParams **params);
|
|
||||||
SpiResult (*set_port_params) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
const SpiParams *params);
|
|
||||||
|
|
||||||
SpiResult (*get_port_status) (SpiHandle *handle,
|
|
||||||
uint32_t port_id,
|
|
||||||
SpiPortStatus *status);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiNode::push_port_input:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @n_info: number of #SpiInputInfo in @info
|
|
||||||
* @info: array of #SpiInputInfo
|
|
||||||
*
|
|
||||||
* Push a buffer and/or an event into one or more input ports of
|
|
||||||
* @node.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
|
||||||
* #SPI_RESULT_ERROR when one or more of the @info has an
|
|
||||||
* error result. Check the status of all the
|
|
||||||
* @info.
|
|
||||||
* #SPI_RESULT_HAVE_ENOUGH_INPUT when output can be produced.
|
|
||||||
*/
|
|
||||||
SpiResult (*push_port_input) (SpiHandle *handle,
|
|
||||||
unsigned int n_info,
|
|
||||||
SpiInputInfo *info);
|
|
||||||
/**
|
|
||||||
* SpiNode::pull_port_output:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @n_info: number of #SpiOutputInfo in @info
|
|
||||||
* @info: array of #SpiOutputInfo
|
|
||||||
*
|
|
||||||
* Pull a buffer and/or an event from one or more output ports of
|
|
||||||
* @node.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
|
||||||
* #SPI_RESULT_PORTS_CHANGED the number of ports has changed. None
|
|
||||||
* of the @info fields are modified
|
|
||||||
* #SPI_RESULT_FORMAT_CHANGED a format changed on some port.
|
|
||||||
* the ports that changed are marked in the status.
|
|
||||||
* #SPI_RESULT_PROPERTIES_CHANGED port properties changed. The
|
|
||||||
* changed ports are marked in the status.
|
|
||||||
* #SPI_RESULT_ERROR when one or more of the @info has an
|
|
||||||
* error result. Check the status of all the @info.
|
|
||||||
* #SPI_RESULT_NEED_MORE_INPUT when no output can be produced
|
|
||||||
* because more input is needed.
|
|
||||||
*/
|
|
||||||
SpiResult (*pull_port_output) (SpiHandle *handle,
|
|
||||||
unsigned int n_info,
|
|
||||||
SpiOutputInfo *info);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPI_NODE_H__ */
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_PARAMS_H__
|
|
||||||
#define __SPI_PARAMS_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _SpiParams SpiParams;
|
|
||||||
|
|
||||||
#include <spi/defs.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiParamType:
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_PARAM_TYPE_INVALID = 0,
|
|
||||||
SPI_PARAM_TYPE_BOOL,
|
|
||||||
SPI_PARAM_TYPE_INT8,
|
|
||||||
SPI_PARAM_TYPE_UINT8,
|
|
||||||
SPI_PARAM_TYPE_INT16,
|
|
||||||
SPI_PARAM_TYPE_UINT16,
|
|
||||||
SPI_PARAM_TYPE_INT32,
|
|
||||||
SPI_PARAM_TYPE_UINT32,
|
|
||||||
SPI_PARAM_TYPE_INT64,
|
|
||||||
SPI_PARAM_TYPE_UINT64,
|
|
||||||
SPI_PARAM_TYPE_FLOAT,
|
|
||||||
SPI_PARAM_TYPE_DOUBLE,
|
|
||||||
SPI_PARAM_TYPE_STRING,
|
|
||||||
SPI_PARAM_TYPE_POINTER,
|
|
||||||
SPI_PARAM_TYPE_FRACTION,
|
|
||||||
SPI_PARAM_TYPE_BITMASK,
|
|
||||||
SPI_PARAM_TYPE_BYTES,
|
|
||||||
} SpiParamType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiParamFlags:
|
|
||||||
* @SPI_PARAM_FLAG_NONE: no flags
|
|
||||||
* @SPI_PARAM_FLAG_OPTIONAL: the value can be left unset
|
|
||||||
* @SPI_PARAM_FLAG_READABLE: param is readable
|
|
||||||
* @SPI_PARAM_FLAG_WRITABLE: param is writable
|
|
||||||
* @SPI_PARAM_FLAG_READWRITE: param is readable and writable
|
|
||||||
* @SPI_PARAM_FLAG_DEPRECATED: param is deprecated and should not be used
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_PARAM_FLAG_NONE = 0,
|
|
||||||
SPI_PARAM_FLAG_OPTIONAL = (1 << 0),
|
|
||||||
SPI_PARAM_FLAG_READABLE = (1 << 1),
|
|
||||||
SPI_PARAM_FLAG_WRITABLE = (1 << 2),
|
|
||||||
SPI_PARAM_FLAG_READWRITE = SPI_PARAM_FLAG_READABLE | SPI_PARAM_FLAG_WRITABLE,
|
|
||||||
SPI_PARAM_FLAG_DEPRECATED = (1 << 3),
|
|
||||||
} SpiParamFlags;
|
|
||||||
|
|
||||||
/* SpiParamRangeType:
|
|
||||||
* @SPI_PARAM_RANGE_TYPE_NONE: no range specified, full range of type applies
|
|
||||||
* @SPI_PARAM_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max
|
|
||||||
* @SPI_PARAM_RANGE_TYPE_ENUM: range contains enum of possible values with
|
|
||||||
* NULL-terminated name
|
|
||||||
* @SPI_PARAM_RANGE_TYPE_FLAGS: range contains flags of possible values with
|
|
||||||
* NULL-terminated name
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
SPI_PARAM_RANGE_TYPE_NONE = 0,
|
|
||||||
SPI_PARAM_RANGE_TYPE_MIN_MAX,
|
|
||||||
SPI_PARAM_RANGE_TYPE_ENUM,
|
|
||||||
SPI_PARAM_RANGE_TYPE_FLAGS,
|
|
||||||
} SpiParamRangeType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiParamRangeInfo:
|
|
||||||
* @name: name of this value
|
|
||||||
* @description: user visible description of this value
|
|
||||||
* @size: the size of value
|
|
||||||
* @value: a possible value
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
const char *name;
|
|
||||||
const char *description;
|
|
||||||
size_t size;
|
|
||||||
const void *value;
|
|
||||||
} SpiParamRangeInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiParamInfo:
|
|
||||||
* @id: unique id
|
|
||||||
* @name: human readable name
|
|
||||||
* @description: description of the param
|
|
||||||
* @flags: param flags
|
|
||||||
* @type: param type
|
|
||||||
* @max_size: maximum size of param value
|
|
||||||
* @default_size: size of default value
|
|
||||||
* @default_value: default value of param
|
|
||||||
* @range_type: type of the range values
|
|
||||||
* @range_values: array of possible values
|
|
||||||
* @tags: extra tags, NULL terminated
|
|
||||||
* @priv: extra private data
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t id;
|
|
||||||
const char *name;
|
|
||||||
const char *description;
|
|
||||||
SpiParamFlags flags;
|
|
||||||
SpiParamType type;
|
|
||||||
size_t maxsize;
|
|
||||||
size_t default_size;
|
|
||||||
const void *default_value;
|
|
||||||
SpiParamRangeType range_type;
|
|
||||||
const SpiParamRangeInfo *range_values;
|
|
||||||
const char **tags;
|
|
||||||
const void *priv;
|
|
||||||
} SpiParamInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiParams:
|
|
||||||
*
|
|
||||||
* Generic parameters.
|
|
||||||
*/
|
|
||||||
struct _SpiParams {
|
|
||||||
/**
|
|
||||||
* SpiParams::get_param_info:
|
|
||||||
* @params: a #SpiParams
|
|
||||||
* @idx: the param index
|
|
||||||
* @info: pointer to a result #SpiParamInfo
|
|
||||||
*
|
|
||||||
* Gets the information about the parameter at @idx in @params.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success.
|
|
||||||
* #SPI_RESULT_ENM_END when there is no param info
|
|
||||||
* at @idx. This can be used to iterate the @params.
|
|
||||||
*/
|
|
||||||
SpiResult (*enum_param_info) (const SpiParams *params,
|
|
||||||
unsigned int idx,
|
|
||||||
const SpiParamInfo **infos);
|
|
||||||
/**
|
|
||||||
* SpiParams::set_param
|
|
||||||
* @params: a #SpiParams
|
|
||||||
* @id: the param id
|
|
||||||
* @type: the value type to set
|
|
||||||
* @size: the value size
|
|
||||||
* @value: the value to set
|
|
||||||
*
|
|
||||||
* Sets @value in @param. @type should match the type specified
|
|
||||||
* in the #SpiParamInfo for @id or else #SPI_RESULT_WRONG_PARAM_TYPE
|
|
||||||
* is returned.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success.
|
|
||||||
* #SPI_RESULT_INVALID_PARAM_ID when @id is not valid
|
|
||||||
* #SPI_RESULT_WRONG_PARAM_TYPE when @type is not correct
|
|
||||||
*/
|
|
||||||
SpiResult (*set_param) (SpiParams *params,
|
|
||||||
uint32_t id,
|
|
||||||
SpiParamType type,
|
|
||||||
size_t size,
|
|
||||||
const void *value);
|
|
||||||
/**
|
|
||||||
* SpiParams::get_param
|
|
||||||
* @params: a #SpiParams
|
|
||||||
* @id: the param id
|
|
||||||
* @type: a location for the value type
|
|
||||||
* @size: a location for the value size
|
|
||||||
* @value: a location for the value pointer
|
|
||||||
*
|
|
||||||
* Get the type, size and value of the parameter with @id.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success.
|
|
||||||
* #SPI_RESULT_INVALID_PARAM_ID when @id is not valid
|
|
||||||
* #SPI_RESULT_PARAM_UNSET when no value has been set yet
|
|
||||||
*/
|
|
||||||
SpiResult (*get_param) (const SpiParams *params,
|
|
||||||
uint32_t id,
|
|
||||||
SpiParamType *type,
|
|
||||||
size_t *size,
|
|
||||||
const void **value);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPI_PARAMS_H__ */
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
/* Simple Plugin Interface
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPI_PLUGIN_H__
|
|
||||||
#define __SPI_PLUGIN_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spi/defs.h>
|
|
||||||
#include <spi/params.h>
|
|
||||||
|
|
||||||
typedef struct _SpiHandle SpiHandle;
|
|
||||||
typedef struct _SpiHandleFactory SpiHandleFactory;
|
|
||||||
|
|
||||||
struct _SpiHandle {
|
|
||||||
/* user_data that can be set by the application */
|
|
||||||
void * user_data;
|
|
||||||
/**
|
|
||||||
* SpiHandle::get_interface:
|
|
||||||
* @handle: a #SpiHandle
|
|
||||||
* @interface_id: the interface id
|
|
||||||
* @interface: result to hold the interface.
|
|
||||||
*
|
|
||||||
* Get the interface provided by @handle with @interface_id.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no extensions
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
|
||||||
*/
|
|
||||||
SpiResult (*get_interface) (SpiHandle *handle,
|
|
||||||
uint32_t interface_id,
|
|
||||||
void **interface);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiInterfaceInfo:
|
|
||||||
* @interface_id: the id of the interface, can be used to get the interface
|
|
||||||
* @name: name of the interface
|
|
||||||
* @description: Human readable description of the interface.
|
|
||||||
*
|
|
||||||
* This structure lists the information about available interfaces on
|
|
||||||
* handles.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t interface_id;
|
|
||||||
const char *name;
|
|
||||||
const char *description;
|
|
||||||
} SpiInterfaceInfo;
|
|
||||||
|
|
||||||
struct _SpiHandleFactory {
|
|
||||||
/**
|
|
||||||
* SpiHandleFactory::name
|
|
||||||
*
|
|
||||||
* The name
|
|
||||||
*/
|
|
||||||
const char * name;
|
|
||||||
/**
|
|
||||||
* SpiHandleFactory::info
|
|
||||||
*
|
|
||||||
* Extra information about the handles of this factory.
|
|
||||||
*/
|
|
||||||
const SpiParams * info;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpiHandleFactory::instantiate
|
|
||||||
* @factory: a #SpiHandleFactory
|
|
||||||
* @handle: a pointer to hold the result
|
|
||||||
*
|
|
||||||
* Make an instance of this factory.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when an instance can't be made
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when factory or handle are %NULL
|
|
||||||
*/
|
|
||||||
SpiResult (*instantiate) (SpiHandleFactory *factory,
|
|
||||||
SpiHandle **handle);
|
|
||||||
/**
|
|
||||||
* SpiHandle::enum_interface_info:
|
|
||||||
* @factory: a #SpiHandleFactory
|
|
||||||
* @index: the interface index
|
|
||||||
* @info: result to hold SpiInterfaceInfo.
|
|
||||||
*
|
|
||||||
* Get the interface information at @index.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no interfaces
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
|
||||||
* #SPI_RESULT_ENUM_END when there are no more infos
|
|
||||||
*/
|
|
||||||
SpiResult (*enum_interface_info) (SpiHandleFactory *factory,
|
|
||||||
unsigned int index,
|
|
||||||
const SpiInterfaceInfo **info);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spi_enum_handle_factory:
|
|
||||||
* @index: the index to enumerate
|
|
||||||
* @factory: a location to hold the factory result
|
|
||||||
*
|
|
||||||
* The main entry point in a plugin.
|
|
||||||
*
|
|
||||||
* Returns: #SPI_RESULT_OK on success
|
|
||||||
* #SPI_RESULT_INVALID_ARGUMENTS when factory is %NULL
|
|
||||||
* #SPI_RESULT_ENUM_END when there are no more factories
|
|
||||||
*/
|
|
||||||
SpiResult spi_enum_handle_factory (unsigned int index,
|
|
||||||
const SpiHandleFactory **factory);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __SPI_PLUGIN_H__ */
|
|
||||||
|
|
@ -1,479 +0,0 @@
|
||||||
/* Pinos
|
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public
|
|
||||||
* License along with this library; if not, write to the
|
|
||||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <spi/node.h>
|
|
||||||
#include "spi-plugins.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
SpiNode *src_node;
|
|
||||||
SpiNode *sink_node;
|
|
||||||
SpiHandle *src;
|
|
||||||
SpiHandle *sink;
|
|
||||||
} AppData;
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_value (const char *prefix, SpiParamType type, int size, const void *value)
|
|
||||||
{
|
|
||||||
printf ("%s", prefix);
|
|
||||||
switch (type) {
|
|
||||||
case SPI_PARAM_TYPE_INVALID:
|
|
||||||
printf ("invalid");
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_BOOL:
|
|
||||||
printf ("%s", *(bool *)value ? "true" : "false");
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_INT8:
|
|
||||||
printf ("%" PRIi8, *(int8_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_UINT8:
|
|
||||||
printf ("%" PRIu8, *(uint8_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_INT16:
|
|
||||||
printf ("%" PRIi16, *(int16_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_UINT16:
|
|
||||||
printf ("%" PRIu16, *(uint16_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_INT32:
|
|
||||||
printf ("%" PRIi32, *(int32_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_UINT32:
|
|
||||||
printf ("%" PRIu32, *(uint32_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_INT64:
|
|
||||||
printf ("%" PRIi64 "\n", *(int64_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_UINT64:
|
|
||||||
printf ("%" PRIu64 "\n", *(uint64_t *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_FLOAT:
|
|
||||||
printf ("%f", *(float *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_DOUBLE:
|
|
||||||
printf ("%g", *(double *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_STRING:
|
|
||||||
printf ("%s", (char *)value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_POINTER:
|
|
||||||
printf ("%p", value);
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_FRACTION:
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_BITMASK:
|
|
||||||
break;
|
|
||||||
case SPI_PARAM_TYPE_BYTES:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf ("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_params (const SpiParams *params, int print_ranges)
|
|
||||||
{
|
|
||||||
SpiResult res;
|
|
||||||
const SpiParamInfo *info;
|
|
||||||
int i, j;
|
|
||||||
SpiParamType type;
|
|
||||||
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
const void *value;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
if ((res = params->enum_param_info (params, i, &info)) < 0) {
|
|
||||||
if (res != SPI_RESULT_ENUM_END)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf ("id:\t\t%d\n", info->id);
|
|
||||||
printf ("name:\t\t%s\n", info->name);
|
|
||||||
printf ("description:\t%s\n", info->description);
|
|
||||||
printf ("flags:\t\t%d\n", info->flags);
|
|
||||||
printf ("type:\t\t%d\n", info->type);
|
|
||||||
printf ("maxsize:\t%zu\n", info->maxsize);
|
|
||||||
|
|
||||||
res = params->get_param (params, info->id, &type, &size, &value);
|
|
||||||
if (res == SPI_RESULT_PARAM_UNSET)
|
|
||||||
printf ("value:\t\tunset\n");
|
|
||||||
else
|
|
||||||
print_value ("value:\t\t", type, size, value);
|
|
||||||
|
|
||||||
if (print_ranges) {
|
|
||||||
if (info->default_value)
|
|
||||||
print_value ("default:\t", info->type, info->default_size, info->default_value);
|
|
||||||
else
|
|
||||||
printf ("default:\tunset\n");
|
|
||||||
|
|
||||||
printf ("range_type:\t%d\n", info->range_type);
|
|
||||||
if (info->range_values) {
|
|
||||||
for (j = 0; info->range_values[j].name; j++) {
|
|
||||||
const SpiParamRangeInfo *rinfo = &info->range_values[j];
|
|
||||||
printf (" name:\t%s\n", rinfo->name);
|
|
||||||
printf (" description:\t%s\n", rinfo->description);
|
|
||||||
print_value (" value:\t", info->type, rinfo->size, rinfo->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (info->tags) {
|
|
||||||
for (j = 0; info->tags[j]; j++) {
|
|
||||||
printf ("tag:\t%s\n", info->tags[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
inspect_node (SpiNode *node, SpiHandle *handle)
|
|
||||||
{
|
|
||||||
SpiResult res;
|
|
||||||
SpiParams *params;
|
|
||||||
unsigned int n_input, max_input, n_output, max_output, i;
|
|
||||||
SpiParams *format;
|
|
||||||
|
|
||||||
if ((res = node->get_params (handle, ¶ms)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
else
|
|
||||||
print_params (params, 1);
|
|
||||||
|
|
||||||
if ((res = node->get_n_ports (handle, &n_input, &max_input, &n_output, &max_output)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
else
|
|
||||||
printf ("supported ports %d %d %d %d\n", n_input, max_input, n_output, max_output);
|
|
||||||
|
|
||||||
for (i = 0; ; i++) {
|
|
||||||
if ((res = node->enum_port_formats (handle, 0, i, &format)) < 0) {
|
|
||||||
if (res != SPI_RESULT_ENUM_END)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
print_params (format, 1);
|
|
||||||
}
|
|
||||||
if ((res = node->get_port_params (handle, 0, ¶ms)) < 0)
|
|
||||||
printf ("get_port_params error: %d\n", res);
|
|
||||||
else
|
|
||||||
printf ("got params %p\n", params);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_format (AppData *data)
|
|
||||||
{
|
|
||||||
SpiParams *format;
|
|
||||||
SpiResult res;
|
|
||||||
uint32_t val;
|
|
||||||
|
|
||||||
if ((res = data->src_node->enum_port_formats (data->src, 0, 0, &format)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
|
|
||||||
printf ("setting format\n");
|
|
||||||
if ((res = format->set_param (format, 1, SPI_PARAM_TYPE_STRING, 5, "S16LE")) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
val = 1;
|
|
||||||
if ((res = format->set_param (format, 2, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
val = 44100;
|
|
||||||
if ((res = format->set_param (format, 3, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
val = 2;
|
|
||||||
if ((res = format->set_param (format, 4, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
|
|
||||||
if ((res = data->src_node->set_port_format (data->src, 0, 0, format)) < 0)
|
|
||||||
printf ("set format failed: %d\n", res);
|
|
||||||
if ((res = data->sink_node->set_port_format (data->sink, 0, 0, format)) < 0)
|
|
||||||
printf ("set format failed: %d\n", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _MyBuffer MyBuffer;
|
|
||||||
|
|
||||||
struct _MyBuffer {
|
|
||||||
SpiBuffer buffer;
|
|
||||||
SpiMeta meta[1];
|
|
||||||
SpiMetaHeader header;
|
|
||||||
SpiData data[1];
|
|
||||||
MyBuffer *next;
|
|
||||||
uint16_t samples[4096];
|
|
||||||
};
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static MyBuffer my_buffers[4];
|
|
||||||
static MyBuffer *free_list = NULL;
|
|
||||||
|
|
||||||
static void
|
|
||||||
my_buffer_notify (MyBuffer *buffer)
|
|
||||||
{
|
|
||||||
printf ("free buffer %p\n", buffer);
|
|
||||||
buffer->next = free_list;
|
|
||||||
free_list = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpiResult
|
|
||||||
setup_buffers (SpiNode *node)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
my_buffers[i].buffer.refcount = 0;
|
|
||||||
my_buffers[i].buffer.notify = (SpiNotify) my_buffer_notify;
|
|
||||||
my_buffers[i].buffer.size = sizeof (MyBuffer);
|
|
||||||
my_buffers[i].buffer.n_metas = 1;
|
|
||||||
my_buffers[i].buffer.metas = my_buffers[i].meta;
|
|
||||||
my_buffers[i].buffer.n_datas = 1;
|
|
||||||
my_buffers[i].buffer.datas = my_buffers[i].data;
|
|
||||||
|
|
||||||
my_buffers[i].header.flags = 0;
|
|
||||||
my_buffers[i].header.seq = 0;
|
|
||||||
my_buffers[i].header.pts = 0;
|
|
||||||
my_buffers[i].header.dts_offset = 0;
|
|
||||||
|
|
||||||
my_buffers[i].meta[0].type = SPI_META_TYPE_HEADER;
|
|
||||||
my_buffers[i].meta[0].data = &my_buffers[i].header;
|
|
||||||
my_buffers[i].meta[0].size = sizeof (my_buffers[i].header);
|
|
||||||
|
|
||||||
my_buffers[i].data[0].type = SPI_DATA_TYPE_MEMPTR;
|
|
||||||
my_buffers[i].data[0].data = my_buffers[i].samples;
|
|
||||||
my_buffers[i].data[0].size = sizeof (my_buffers[i].samples);
|
|
||||||
|
|
||||||
my_buffers[i].next = free_list;
|
|
||||||
free_list = &my_buffers[i];
|
|
||||||
}
|
|
||||||
return SPI_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpiResult
|
|
||||||
push_input (SpiNode *node)
|
|
||||||
{
|
|
||||||
SpiResult res;
|
|
||||||
SpiDataInfo info;
|
|
||||||
MyBuffer *mybuf;
|
|
||||||
|
|
||||||
mybuf = free_list;
|
|
||||||
free_list = mybuf->next;
|
|
||||||
|
|
||||||
printf ("alloc input buffer %p\n", mybuf);
|
|
||||||
mybuf->buffer.refcount = 1;
|
|
||||||
|
|
||||||
info.port_id = 0;
|
|
||||||
info.flags = SPI_DATA_FLAG_NONE;
|
|
||||||
info.buffer = &mybuf->buffer;
|
|
||||||
info.event = NULL;
|
|
||||||
|
|
||||||
res = node->send_port_data (node, &info);
|
|
||||||
|
|
||||||
spi_buffer_unref (&mybuf->buffer);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpiResult
|
|
||||||
pull_output (SpiNode *node)
|
|
||||||
{
|
|
||||||
SpiDataInfo info[1] = { { 0, }, };
|
|
||||||
SpiResult res;
|
|
||||||
MyBuffer *mybuf;
|
|
||||||
SpiBuffer *buf;
|
|
||||||
|
|
||||||
mybuf = free_list;
|
|
||||||
free_list = mybuf->next;
|
|
||||||
|
|
||||||
printf ("alloc output buffer %p\n", mybuf);
|
|
||||||
mybuf->buffer.refcount = 1;
|
|
||||||
|
|
||||||
info[0].port_id = 1;
|
|
||||||
info[0].buffer = &mybuf->buffer;
|
|
||||||
info[0].event = NULL;
|
|
||||||
|
|
||||||
res = node->receive_port_data (node, 1, info);
|
|
||||||
|
|
||||||
buf = info[0].buffer;
|
|
||||||
spi_buffer_unref (buf);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
run_volume (SpiNode *node)
|
|
||||||
{
|
|
||||||
int state;
|
|
||||||
SpiResult res;
|
|
||||||
|
|
||||||
set_params (node);
|
|
||||||
set_format (node);
|
|
||||||
|
|
||||||
state = 0;
|
|
||||||
while (TRUE) {
|
|
||||||
SpiPortStatus status;
|
|
||||||
|
|
||||||
if (state == 0) {
|
|
||||||
if ((res = push_input (node)) < 0) {
|
|
||||||
if (res == SPI_RESULT_HAVE_ENOUGH_INPUT)
|
|
||||||
state = 1;
|
|
||||||
else {
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((res = node->get_port_status (node, 1, &status)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
else if (status.flags & SPI_PORT_STATUS_FLAG_HAVE_OUTPUT)
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
if (state == 1) {
|
|
||||||
if ((res = pull_output (node)) < 0) {
|
|
||||||
if (res == SPI_RESULT_NEED_MORE_INPUT)
|
|
||||||
state = 0;
|
|
||||||
else {
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((res = node->get_port_status (node, 0, &status)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
else if (status.flags & SPI_PORT_STATUS_FLAG_NEED_INPUT)
|
|
||||||
state = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_event (SpiHandle *handle, SpiEvent *event, void *user_data)
|
|
||||||
{
|
|
||||||
AppData *data = user_data;
|
|
||||||
|
|
||||||
switch (event->type) {
|
|
||||||
case SPI_EVENT_TYPE_PULL_INPUT:
|
|
||||||
{
|
|
||||||
SpiBuffer *buf;
|
|
||||||
SpiInputInfo iinfo;
|
|
||||||
SpiOutputInfo oinfo;
|
|
||||||
SpiResult res;
|
|
||||||
|
|
||||||
buf = event->data;
|
|
||||||
|
|
||||||
oinfo.port_id = event->port_id;
|
|
||||||
oinfo.flags = SPI_OUTPUT_FLAG_NONE;
|
|
||||||
oinfo.buffer = buf;
|
|
||||||
oinfo.event = NULL;
|
|
||||||
|
|
||||||
if ((res = data->src_node->pull_port_output (data->src, 1, &oinfo)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
|
|
||||||
iinfo.port_id = 0;
|
|
||||||
iinfo.flags = SPI_INPUT_FLAG_NONE;
|
|
||||||
iinfo.buffer = oinfo.buffer;
|
|
||||||
iinfo.event = oinfo.event;
|
|
||||||
|
|
||||||
if ((res = data->sink_node->push_port_input (data->sink, 1, &iinfo)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
printf ("got event %d\n", event->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
run_async_sink (AppData *data)
|
|
||||||
{
|
|
||||||
SpiResult res;
|
|
||||||
SpiCommand cmd;
|
|
||||||
|
|
||||||
set_format (data);
|
|
||||||
|
|
||||||
cmd.type = SPI_COMMAND_START;
|
|
||||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
|
|
||||||
printf ("sleeping for 10 seconds\n");
|
|
||||||
sleep (10);
|
|
||||||
|
|
||||||
cmd.type = SPI_COMMAND_STOP;
|
|
||||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
setup_source (AppData *data)
|
|
||||||
{
|
|
||||||
SpiCommand cmd;
|
|
||||||
SpiResult res;
|
|
||||||
|
|
||||||
data->src = spi_audiotestsrc_new ();
|
|
||||||
data->src->get_interface (data->src, SPI_INTERFACE_ID_NODE, (void **)&data->src_node);
|
|
||||||
|
|
||||||
cmd.type = SPI_COMMAND_ACTIVATE;
|
|
||||||
if ((res = data->src_node->send_command (data->src, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
setup_sink (AppData *data)
|
|
||||||
{
|
|
||||||
SpiCommand cmd;
|
|
||||||
SpiResult res;
|
|
||||||
SpiParams *params;
|
|
||||||
|
|
||||||
data->sink = spi_alsa_sink_new ();
|
|
||||||
data->sink->get_interface (data->sink, SPI_INTERFACE_ID_NODE, (void **)&data->sink_node);
|
|
||||||
|
|
||||||
data->sink_node->set_event_callback (data->sink, on_event, data);
|
|
||||||
|
|
||||||
if ((res = data->sink_node->get_params (data->sink, ¶ms)) < 0)
|
|
||||||
printf ("got get_params error %d\n", res);
|
|
||||||
|
|
||||||
params->set_param (params, 0, SPI_PARAM_TYPE_STRING, strlen ("hw:1")+1, "hw:1");
|
|
||||||
|
|
||||||
if ((res = data->sink_node->set_params (data->sink, params)) < 0)
|
|
||||||
printf ("got set_params error %d\n", res);
|
|
||||||
|
|
||||||
cmd.type = SPI_COMMAND_ACTIVATE;
|
|
||||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
SpiResult res;
|
|
||||||
SpiCommand cmd;
|
|
||||||
AppData data;
|
|
||||||
|
|
||||||
setup_source (&data);
|
|
||||||
setup_sink (&data);
|
|
||||||
|
|
||||||
run_async_sink (&data);
|
|
||||||
|
|
||||||
cmd.type = SPI_COMMAND_DEACTIVATE;
|
|
||||||
if ((res = data.sink_node->send_command (data.sink, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
cmd.type = SPI_COMMAND_DEACTIVATE;
|
|
||||||
if ((res = data.src_node->send_command (data.src, &cmd)) < 0)
|
|
||||||
printf ("got error %d\n", res);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
1
spa/include/meson.build
Normal file
1
spa/include/meson.build
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
subdir('spa')
|
||||||
44
spa/include/spa/audio/format.h
Normal file
44
spa/include/spa/audio/format.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_AUDIO_FORMAT_H__
|
||||||
|
#define __SPA_AUDIO_FORMAT_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spa/format.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_PROP_ID_AUDIO_FORMAT = SPA_PROP_ID_MEDIA_CUSTOM_START,
|
||||||
|
SPA_PROP_ID_AUDIO_FLAGS,
|
||||||
|
SPA_PROP_ID_AUDIO_LAYOUT,
|
||||||
|
SPA_PROP_ID_AUDIO_RATE,
|
||||||
|
SPA_PROP_ID_AUDIO_CHANNELS,
|
||||||
|
SPA_PROP_ID_AUDIO_CHANNEL_MASK,
|
||||||
|
SPA_PROP_ID_AUDIO_RAW_INFO,
|
||||||
|
} SpaPropIdAudio;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_AUDIO_FORMAT */
|
||||||
158
spa/include/spa/audio/raw.h
Normal file
158
spa/include/spa/audio/raw.h
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_AUDIO_RAW_H__
|
||||||
|
#define __SPA_AUDIO_RAW_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaAudioRawInfo SpaAudioRawInfo;
|
||||||
|
typedef struct _SpaAudioRawFormat SpaAudioRawFormat;
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include <spa/audio/format.h>
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
#define _SPA_AUDIO_FORMAT_NE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## BE
|
||||||
|
#define _SPA_AUDIO_FORMAT_OE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## LE
|
||||||
|
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
#define _SPA_AUDIO_FORMAT_NE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## LE
|
||||||
|
#define _SPA_AUDIO_FORMAT_OE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## BE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_AUDIO_FORMAT_UNKNOWN,
|
||||||
|
SPA_AUDIO_FORMAT_ENCODED,
|
||||||
|
/* 8 bit */
|
||||||
|
SPA_AUDIO_FORMAT_S8,
|
||||||
|
SPA_AUDIO_FORMAT_U8,
|
||||||
|
/* 16 bit */
|
||||||
|
SPA_AUDIO_FORMAT_S16LE,
|
||||||
|
SPA_AUDIO_FORMAT_S16BE,
|
||||||
|
SPA_AUDIO_FORMAT_U16LE,
|
||||||
|
SPA_AUDIO_FORMAT_U16BE,
|
||||||
|
/* 24 bit in low 3 bytes of 32 bits*/
|
||||||
|
SPA_AUDIO_FORMAT_S24_32LE,
|
||||||
|
SPA_AUDIO_FORMAT_S24_32BE,
|
||||||
|
SPA_AUDIO_FORMAT_U24_32LE,
|
||||||
|
SPA_AUDIO_FORMAT_U24_32BE,
|
||||||
|
/* 32 bit */
|
||||||
|
SPA_AUDIO_FORMAT_S32LE,
|
||||||
|
SPA_AUDIO_FORMAT_S32BE,
|
||||||
|
SPA_AUDIO_FORMAT_U32LE,
|
||||||
|
SPA_AUDIO_FORMAT_U32BE,
|
||||||
|
/* 24 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S24LE,
|
||||||
|
SPA_AUDIO_FORMAT_S24BE,
|
||||||
|
SPA_AUDIO_FORMAT_U24LE,
|
||||||
|
SPA_AUDIO_FORMAT_U24BE,
|
||||||
|
/* 20 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S20LE,
|
||||||
|
SPA_AUDIO_FORMAT_S20BE,
|
||||||
|
SPA_AUDIO_FORMAT_U20LE,
|
||||||
|
SPA_AUDIO_FORMAT_U20BE,
|
||||||
|
/* 18 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S18LE,
|
||||||
|
SPA_AUDIO_FORMAT_S18BE,
|
||||||
|
SPA_AUDIO_FORMAT_U18LE,
|
||||||
|
SPA_AUDIO_FORMAT_U18BE,
|
||||||
|
/* float */
|
||||||
|
SPA_AUDIO_FORMAT_F32LE,
|
||||||
|
SPA_AUDIO_FORMAT_F32BE,
|
||||||
|
SPA_AUDIO_FORMAT_F64LE,
|
||||||
|
SPA_AUDIO_FORMAT_F64BE,
|
||||||
|
/* native endianness equivalents */
|
||||||
|
SPA_AUDIO_FORMAT_S16 = _SPA_AUDIO_FORMAT_NE(S16),
|
||||||
|
SPA_AUDIO_FORMAT_U16 = _SPA_AUDIO_FORMAT_NE(U16),
|
||||||
|
SPA_AUDIO_FORMAT_S24_32 = _SPA_AUDIO_FORMAT_NE(S24_32),
|
||||||
|
SPA_AUDIO_FORMAT_U24_32 = _SPA_AUDIO_FORMAT_NE(U24_32),
|
||||||
|
SPA_AUDIO_FORMAT_S32 = _SPA_AUDIO_FORMAT_NE(S32),
|
||||||
|
SPA_AUDIO_FORMAT_U32 = _SPA_AUDIO_FORMAT_NE(U32),
|
||||||
|
SPA_AUDIO_FORMAT_S24 = _SPA_AUDIO_FORMAT_NE(S24),
|
||||||
|
SPA_AUDIO_FORMAT_U24 = _SPA_AUDIO_FORMAT_NE(U24),
|
||||||
|
SPA_AUDIO_FORMAT_S20 = _SPA_AUDIO_FORMAT_NE(S20),
|
||||||
|
SPA_AUDIO_FORMAT_U20 = _SPA_AUDIO_FORMAT_NE(U20),
|
||||||
|
SPA_AUDIO_FORMAT_S18 = _SPA_AUDIO_FORMAT_NE(S18),
|
||||||
|
SPA_AUDIO_FORMAT_U18 = _SPA_AUDIO_FORMAT_NE(U18),
|
||||||
|
SPA_AUDIO_FORMAT_F32 = _SPA_AUDIO_FORMAT_NE(F32),
|
||||||
|
SPA_AUDIO_FORMAT_F64 = _SPA_AUDIO_FORMAT_NE(F64)
|
||||||
|
} SpaAudioFormat;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaAudioFlags:
|
||||||
|
* @SPA_AUDIO_FLAG_NONE: no valid flag
|
||||||
|
* @SPA_AUDIO_FLAG_UNPOSITIONED: the position array explicitly
|
||||||
|
* contains unpositioned channels.
|
||||||
|
*
|
||||||
|
* Extra audio flags
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_AUDIO_FLAG_NONE = 0,
|
||||||
|
SPA_AUDIO_FLAG_UNPOSITIONED = (1 << 0)
|
||||||
|
} SpaAudioFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaAudioLayout:
|
||||||
|
* @SPA_AUDIO_LAYOUT_INTERLEAVED: interleaved audio
|
||||||
|
* @SPA_AUDIO_LAYOUT_NON_INTERLEAVED: non-interleaved audio
|
||||||
|
*
|
||||||
|
* Layout of the audio samples for the different channels.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_AUDIO_LAYOUT_INTERLEAVED = 0,
|
||||||
|
SPA_AUDIO_LAYOUT_NON_INTERLEAVED
|
||||||
|
} SpaAudioLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaAudioRawInfo:
|
||||||
|
* @format: the format
|
||||||
|
* @flags: extra flags
|
||||||
|
* @layout: the sample layout
|
||||||
|
* @rate: the sample rate
|
||||||
|
* @channels: the number of channels
|
||||||
|
* @channel_mask: the channel mask
|
||||||
|
*/
|
||||||
|
struct _SpaAudioRawInfo {
|
||||||
|
SpaAudioFormat format;
|
||||||
|
SpaAudioFlags flags;
|
||||||
|
SpaAudioLayout layout;
|
||||||
|
uint32_t rate;
|
||||||
|
uint32_t channels;
|
||||||
|
uint32_t channel_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _SpaAudioRawFormat {
|
||||||
|
SpaFormat format;
|
||||||
|
uint32_t unset_mask;
|
||||||
|
SpaAudioRawInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpaResult spa_audio_raw_format_init (SpaAudioRawFormat *format);
|
||||||
|
SpaResult spa_audio_raw_format_parse (const SpaFormat *format,
|
||||||
|
SpaAudioRawFormat *rawformat);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_AUDIO_RAW_H__ */
|
||||||
180
spa/include/spa/buffer.h
Normal file
180
spa/include/spa/buffer.h
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_BUFFER_H__
|
||||||
|
#define __SPA_BUFFER_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaBuffer SpaBuffer;
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaMetaType:
|
||||||
|
* @SPA_META_TYPE_INVALID: invalid metadata, should be ignored
|
||||||
|
* @SPA_META_TYPE_HEADER: header metadata
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_META_TYPE_INVALID = 0,
|
||||||
|
SPA_META_TYPE_HEADER,
|
||||||
|
} SpaMetaType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaBufferFlags:
|
||||||
|
* @SPA_BUFFER_FLAG_NONE: no flag
|
||||||
|
* @SPA_BUFFER_FLAG_DISCONT: the buffer marks a data discontinuity
|
||||||
|
* @SPA_BUFFER_FLAG_CORRUPTED: the buffer data might be corrupted
|
||||||
|
* @SPA_BUFFER_FLAG_MARKER: the buffer contains a media specific marker
|
||||||
|
* @SPA_BUFFER_FLAG_HEADER: the buffer contains a header
|
||||||
|
* @SPA_BUFFER_FLAG_GAP: the buffer has been constructed to fill a gap
|
||||||
|
* and contains media neutral data
|
||||||
|
* @SPA_BUFFER_FLAG_DELTA_UNIT: the media cannot be decoded independently
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_BUFFER_FLAG_NONE = 0,
|
||||||
|
SPA_BUFFER_FLAG_DISCONT = (1 << 0),
|
||||||
|
SPA_BUFFER_FLAG_CORRUPTED = (1 << 1),
|
||||||
|
SPA_BUFFER_FLAG_MARKER = (1 << 2),
|
||||||
|
SPA_BUFFER_FLAG_HEADER = (1 << 3),
|
||||||
|
SPA_BUFFER_FLAG_GAP = (1 << 4),
|
||||||
|
SPA_BUFFER_FLAG_DELTA_UNIT = (1 << 5),
|
||||||
|
} SpaBufferFlags;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaBufferFlags flags;
|
||||||
|
uint32_t seq;
|
||||||
|
int64_t pts;
|
||||||
|
int64_t dts_offset;
|
||||||
|
} SpaMetaHeader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaMeta:
|
||||||
|
* @type: metadata type
|
||||||
|
* @data: pointer to metadata
|
||||||
|
* @size: size of metadata
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
SpaMetaType type;
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
} SpaMeta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaDataType:
|
||||||
|
* @SPA_DATA_TYPE_INVALID: invalid data type, is ignored
|
||||||
|
* @SPA_DATA_TYPE_MEMPTR: data and size point to memory
|
||||||
|
* @SPA_DATA_TYPE_FD: data points to SpaDataFd
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_DATA_TYPE_INVALID = 0,
|
||||||
|
SPA_DATA_TYPE_MEMPTR,
|
||||||
|
SPA_DATA_TYPE_FD,
|
||||||
|
} SpaDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaDataFd
|
||||||
|
* fd: a file descriptor
|
||||||
|
* offset: offset in the data referenced by @fd
|
||||||
|
* @size: size of data referenced by fd
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int fd;
|
||||||
|
unsigned int offset;
|
||||||
|
size_t size;
|
||||||
|
} SpaDataFD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaData:
|
||||||
|
* @id: user id
|
||||||
|
* @type: the type of data
|
||||||
|
* @data: pointer to data
|
||||||
|
* @size: size of data
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
SpaDataType type;
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
} SpaData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaBuffer:
|
||||||
|
* @refcount: reference counter
|
||||||
|
* @notify: called when the refcount reaches 0
|
||||||
|
* @size: total size of the buffer data
|
||||||
|
* @n_metas: number of metadata
|
||||||
|
* @metas: array of @n_metas metadata
|
||||||
|
* @n_datas: number of data pointers
|
||||||
|
* @datas: array of @n_datas data pointers
|
||||||
|
*/
|
||||||
|
struct _SpaBuffer {
|
||||||
|
volatile int refcount;
|
||||||
|
SpaNotify notify;
|
||||||
|
size_t size;
|
||||||
|
unsigned int n_metas;
|
||||||
|
SpaMeta *metas;
|
||||||
|
unsigned int n_datas;
|
||||||
|
SpaData *datas;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_buffer_ref:
|
||||||
|
* @buffer: a #SpaBuffer
|
||||||
|
*
|
||||||
|
* Increase the refcount on @buffer
|
||||||
|
*
|
||||||
|
* Returns: @buffer
|
||||||
|
*/
|
||||||
|
static inline SpaBuffer *
|
||||||
|
spa_buffer_ref (SpaBuffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer != NULL)
|
||||||
|
buffer->refcount++;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_buffer_unref:
|
||||||
|
* @buffer: a #SpaBuffer
|
||||||
|
*
|
||||||
|
* Decrease the refcount on buffer. when the refcount is 0, the notify,
|
||||||
|
* if any, of the buffer will be called.
|
||||||
|
*
|
||||||
|
* Returns: @buffer or %NULL when the refcount is 0
|
||||||
|
*/
|
||||||
|
static inline SpaBuffer *
|
||||||
|
spa_buffer_unref (SpaBuffer *buffer)
|
||||||
|
{
|
||||||
|
if (buffer != NULL) {
|
||||||
|
if (--buffer->refcount == 0) {
|
||||||
|
if (buffer->notify)
|
||||||
|
buffer->notify (buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_BUFFER_H__ */
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Simple Plugin Interface
|
/* Simple Plugin API
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
|
|
@ -17,32 +17,32 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SPI_COMMAND_H__
|
#ifndef __SPA_COMMAND_H__
|
||||||
#define __SPI_COMMAND_H__
|
#define __SPA_COMMAND_H__
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct _SpiCommand SpiCommand;
|
typedef struct _SpaCommand SpaCommand;
|
||||||
|
|
||||||
#include <spi/defs.h>
|
#include <spa/defs.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPI_COMMAND_INVALID = 0,
|
SPA_COMMAND_INVALID = 0,
|
||||||
SPI_COMMAND_ACTIVATE,
|
SPA_COMMAND_ACTIVATE,
|
||||||
SPI_COMMAND_DEACTIVATE,
|
SPA_COMMAND_DEACTIVATE,
|
||||||
SPI_COMMAND_START,
|
SPA_COMMAND_START,
|
||||||
SPI_COMMAND_STOP,
|
SPA_COMMAND_STOP,
|
||||||
SPI_COMMAND_FLUSH,
|
SPA_COMMAND_FLUSH,
|
||||||
SPI_COMMAND_DRAIN,
|
SPA_COMMAND_DRAIN,
|
||||||
SPI_COMMAND_MARKER,
|
SPA_COMMAND_MARKER,
|
||||||
} SpiCommandType;
|
} SpaCommandType;
|
||||||
|
|
||||||
struct _SpiCommand {
|
struct _SpaCommand {
|
||||||
volatile int refcount;
|
volatile int refcount;
|
||||||
SpiNotify notify;
|
SpaNotify notify;
|
||||||
SpiCommandType type;
|
SpaCommandType type;
|
||||||
uint32_t port_id;
|
uint32_t port_id;
|
||||||
void *data;
|
void *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
@ -52,4 +52,4 @@ struct _SpiCommand {
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __SPI_COMMAND_H__ */
|
#endif /* __SPA_COMMAND_H__ */
|
||||||
74
spa/include/spa/defs.h
Normal file
74
spa/include/spa/defs.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_DEFS_H__
|
||||||
|
#define __SPA_DEFS_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include <stdbool.h>
|
||||||
|
#endif
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_RESULT_OK = 0,
|
||||||
|
SPA_RESULT_ERROR = -1,
|
||||||
|
SPA_RESULT_INACTIVE = -2,
|
||||||
|
SPA_RESULT_NO_FORMAT = -3,
|
||||||
|
SPA_RESULT_INVALID_COMMAND = -4,
|
||||||
|
SPA_RESULT_INVALID_PORT = -5,
|
||||||
|
SPA_RESULT_HAVE_ENOUGH_INPUT = -6,
|
||||||
|
SPA_RESULT_NEED_MORE_INPUT = -7,
|
||||||
|
SPA_RESULT_PORTS_CHANGED = -9,
|
||||||
|
SPA_RESULT_FORMAT_CHANGED = -10,
|
||||||
|
SPA_RESULT_PROPERTIES_CHANGED = -11,
|
||||||
|
SPA_RESULT_NOT_IMPLEMENTED = -12,
|
||||||
|
SPA_RESULT_INVALID_PROPERTY_INDEX = -13,
|
||||||
|
SPA_RESULT_PROPERTY_UNSET = -14,
|
||||||
|
SPA_RESULT_ENUM_END = -15,
|
||||||
|
SPA_RESULT_WRONG_PROPERTY_TYPE = -16,
|
||||||
|
SPA_RESULT_WRONG_PROPERTY_SIZE = -17,
|
||||||
|
SPA_RESULT_INVALID_MEDIA_TYPE = -18,
|
||||||
|
SPA_RESULT_INVALID_FORMAT_PROPERTIES = -19,
|
||||||
|
SPA_RESULT_FORMAT_INCOMPLETE = -20,
|
||||||
|
SPA_RESULT_INVALID_ARGUMENTS = -21,
|
||||||
|
SPA_RESULT_UNKNOWN_INTERFACE = -22,
|
||||||
|
SPA_RESULT_INVALID_DIRECTION = -23,
|
||||||
|
SPA_RESULT_TOO_MANY_PORTS = -24,
|
||||||
|
SPA_RESULT_INVALID_PROPERTY_ACCESS = -25,
|
||||||
|
} SpaResult;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_DIRECTION_INVALID = 0,
|
||||||
|
SPA_DIRECTION_INPUT,
|
||||||
|
SPA_DIRECTION_OUTPUT
|
||||||
|
} SpaDirection;
|
||||||
|
|
||||||
|
typedef void (*SpaNotify) (void *data);
|
||||||
|
|
||||||
|
#define SPA_N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __SPA_DEFS_H__ */
|
||||||
76
spa/include/spa/event.h
Normal file
76
spa/include/spa/event.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_EVENT_H__
|
||||||
|
#define __SPA_EVENT_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaEvent SpaEvent;
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaEventType:
|
||||||
|
* @SPA_EVENT_TYPE_INVALID: invalid event, should be ignored
|
||||||
|
* @SPA_EVENT_TYPE_ACTIVATED: emited when the ACTIVATE command completes
|
||||||
|
* @SPA_EVENT_TYPE_DEACTIVATED: emited when the DEACTIVATE command completes
|
||||||
|
* @SPA_EVENT_TYPE_CAN_PULL_OUTPUT: emited when an async node has output that can be pulled
|
||||||
|
* @SPA_EVENT_TYPE_CAN_PUSH_INTPUT: emited when more data can be pushed to an async node
|
||||||
|
* @SPA_EVENT_TYPE_PULL_INPUT: emited when data needs to be provided on an input
|
||||||
|
* @SPA_EVENT_TYPE_ALLOC_OUTPUT: emited when an output buffer needs to be allocated
|
||||||
|
* @SPA_EVENT_TYPE_ADD_POLL: emited when a pollfd should be added
|
||||||
|
* @SPA_EVENT_TYPE_REMOVE_POLL: emited when a pollfd should be removed
|
||||||
|
* @SPA_EVENT_TYPE_DRAINED: emited when DRAIN command completed
|
||||||
|
* @SPA_EVENT_TYPE_MARKER: emited when MARK command completed
|
||||||
|
* @SPA_EVENT_TYPE_ERROR: emited when error occured
|
||||||
|
* @SPA_EVENT_TYPE_BUFFERING: emited when buffering is in progress
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_EVENT_TYPE_INVALID = 0,
|
||||||
|
SPA_EVENT_TYPE_ACTIVATED,
|
||||||
|
SPA_EVENT_TYPE_DEACTIVATED,
|
||||||
|
SPA_EVENT_TYPE_CAN_PULL_OUTPUT,
|
||||||
|
SPA_EVENT_TYPE_CAN_PUSH_INTPUT,
|
||||||
|
SPA_EVENT_TYPE_PULL_INPUT,
|
||||||
|
SPA_EVENT_TYPE_ALLOC_OUTPUT,
|
||||||
|
SPA_EVENT_TYPE_ADD_POLL,
|
||||||
|
SPA_EVENT_TYPE_REMOVE_POLL,
|
||||||
|
SPA_EVENT_TYPE_DRAINED,
|
||||||
|
SPA_EVENT_TYPE_MARKER,
|
||||||
|
SPA_EVENT_TYPE_ERROR,
|
||||||
|
SPA_EVENT_TYPE_BUFFERING,
|
||||||
|
} SpaEventType;
|
||||||
|
|
||||||
|
struct _SpaEvent {
|
||||||
|
volatile int refcount;
|
||||||
|
SpaNotify notify;
|
||||||
|
SpaEventType type;
|
||||||
|
uint32_t port_id;
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_EVENT_H__ */
|
||||||
57
spa/include/spa/format.h
Normal file
57
spa/include/spa/format.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_FORMAT_H__
|
||||||
|
#define __SPA_FORMAT_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaFormat SpaFormat;
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
#include <spa/props.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_MEDIA_TYPE_INVALID = 0,
|
||||||
|
SPA_MEDIA_TYPE_AUDIO = 1,
|
||||||
|
} SpaMediaType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_MEDIA_SUBTYPE_INVALID = 0,
|
||||||
|
SPA_MEDIA_SUBTYPE_RAW = 1,
|
||||||
|
} SpaMediaSubType;
|
||||||
|
|
||||||
|
struct _SpaFormat {
|
||||||
|
SpaProps props;
|
||||||
|
SpaMediaType media_type;
|
||||||
|
SpaMediaSubType media_subtype;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SPA_PROP_ID_INVALID = 0,
|
||||||
|
SPA_PROP_ID_MEDIA_CUSTOM_START = 200,
|
||||||
|
} SpaFormatProps;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_FORMAT_H__ */
|
||||||
8
spa/include/spa/meson.build
Normal file
8
spa/include/spa/meson.build
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
install_headers('buffer.h',
|
||||||
|
'command.h',
|
||||||
|
'defs.h',
|
||||||
|
'event.h',
|
||||||
|
'node.h',
|
||||||
|
'plugin.h',
|
||||||
|
'port.h',
|
||||||
|
'props.h')
|
||||||
370
spa/include/spa/node.h
Normal file
370
spa/include/spa/node.h
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_NODE_H__
|
||||||
|
#define __SPA_NODE_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaNode SpaNode;
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
#include <spa/plugin.h>
|
||||||
|
#include <spa/props.h>
|
||||||
|
#include <spa/port.h>
|
||||||
|
#include <spa/event.h>
|
||||||
|
#include <spa/buffer.h>
|
||||||
|
#include <spa/command.h>
|
||||||
|
#include <spa/format.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaInputFlags:
|
||||||
|
* @SPA_INPUT_FLAG_NONE: no flag
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_INPUT_FLAG_NONE = 0,
|
||||||
|
} SpaInputFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaInputInfo:
|
||||||
|
* @port_id: the port id
|
||||||
|
* @flags: extra flags
|
||||||
|
* @buffer: a buffer
|
||||||
|
*
|
||||||
|
* Input information for a node.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t port_id;
|
||||||
|
SpaInputFlags flags;
|
||||||
|
SpaBuffer *buffer;
|
||||||
|
SpaEvent *event;
|
||||||
|
SpaResult status;
|
||||||
|
} SpaInputInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaOutputFlags:
|
||||||
|
* @SPA_OUTPUT_FLAG_NONE: no flag
|
||||||
|
* @SPA_OUTPUT_FLAG_PULL: force a #SPA_EVENT_NEED_INPUT event on the
|
||||||
|
* peer input ports when no data is available.
|
||||||
|
* @SPA_OUTPUT_FLAG_DISCARD: discard the buffer data
|
||||||
|
* @SPA_OUTPUT_FLAG_NO_BUFFER: no buffer was produced on the port
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_OUTPUT_FLAG_NONE = 0,
|
||||||
|
SPA_OUTPUT_FLAG_PULL = (1 << 0),
|
||||||
|
SPA_OUTPUT_FLAG_DISCARD = (1 << 1),
|
||||||
|
SPA_OUTPUT_FLAG_NO_BUFFER = (1 << 2),
|
||||||
|
} SpaOutputFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaOutputInfo:
|
||||||
|
* @port_id: the port id
|
||||||
|
* @flags: extra flags
|
||||||
|
* @buffer: a buffer
|
||||||
|
* @event: an event
|
||||||
|
*
|
||||||
|
* Output information for a node.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t port_id;
|
||||||
|
SpaOutputFlags flags;
|
||||||
|
SpaBuffer *buffer;
|
||||||
|
SpaEvent *event;
|
||||||
|
SpaResult status;
|
||||||
|
} SpaOutputInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaEventCallback:
|
||||||
|
* @node: a #SpaHandle emiting the event
|
||||||
|
* @event: the event that was emited
|
||||||
|
* @user_data: user data provided when registering the callback
|
||||||
|
*
|
||||||
|
* This will be called when an out-of-bound event is notified
|
||||||
|
* on @node.
|
||||||
|
*/
|
||||||
|
typedef void (*SpaEventCallback) (SpaHandle *handle,
|
||||||
|
SpaEvent *event,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
#define SPA_INTERFACE_ID_NODE 0
|
||||||
|
#define SPA_INTERFACE_ID_NODE_NAME "Node interface"
|
||||||
|
#define SPA_INTERFACE_ID_NODE_DESCRIPTION "Main processing node interface"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaNode:
|
||||||
|
*
|
||||||
|
* The main processing nodes.
|
||||||
|
*/
|
||||||
|
struct _SpaNode {
|
||||||
|
/* the total size of this node. This can be used to expand this
|
||||||
|
* structure in the future */
|
||||||
|
size_t size;
|
||||||
|
/**
|
||||||
|
* SpaNode::get_props:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @props: a location for a #SpaProps pointer
|
||||||
|
*
|
||||||
|
* Get the configurable properties of @node.
|
||||||
|
*
|
||||||
|
* The returned @props is a snapshot of the current configuration and
|
||||||
|
* can be modified. The modifications will take effect after a call
|
||||||
|
* to SpaNode::set_props.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node or props are %NULL
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when there are no properties
|
||||||
|
* implemented on @node
|
||||||
|
*/
|
||||||
|
SpaResult (*get_props) (SpaHandle *handle,
|
||||||
|
SpaProps **props);
|
||||||
|
/**
|
||||||
|
* SpaNode::set_props:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @props: a #SpaProps
|
||||||
|
*
|
||||||
|
* Set the configurable properties in @node.
|
||||||
|
*
|
||||||
|
* Usually, @props will be obtained from SpaNode::get_props and then
|
||||||
|
* modified but it is also possible to set another #SpaProps object
|
||||||
|
* as long as its keys and types match those of SpaProps::get_props.
|
||||||
|
*
|
||||||
|
* Properties with keys that are not known are ignored.
|
||||||
|
*
|
||||||
|
* If @props is NULL, all the properties are reset to their defaults.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when no properties can be
|
||||||
|
* modified on @node.
|
||||||
|
* #SPA_RESULT_WRONG_PROPERTY_TYPE when a property has the wrong
|
||||||
|
* type.
|
||||||
|
*/
|
||||||
|
SpaResult (*set_props) (SpaHandle *handle,
|
||||||
|
const SpaProps *props);
|
||||||
|
/**
|
||||||
|
* SpaNode::send_command:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @command: a #SpaCommand
|
||||||
|
*
|
||||||
|
* Send a command to @node.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node or command is %NULL
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when this node can't process commands
|
||||||
|
* #SPA_RESULT_INVALID_COMMAND @command is an invalid command
|
||||||
|
*/
|
||||||
|
SpaResult (*send_command) (SpaHandle *handle,
|
||||||
|
SpaCommand *command);
|
||||||
|
/**
|
||||||
|
* SpaNode::set_event_callback:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @callback: a callback
|
||||||
|
* @user_data: user data passed in the callback
|
||||||
|
*
|
||||||
|
* Set a callback to receive events from @node. if @callback is %NULL, the
|
||||||
|
* current callback is removed.
|
||||||
|
*
|
||||||
|
* The callback can be emited from any thread. The caller should take
|
||||||
|
* appropriate actions to handle the event in other threads when needed.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||||
|
*/
|
||||||
|
SpaResult (*set_event_callback) (SpaHandle *handle,
|
||||||
|
SpaEventCallback callback,
|
||||||
|
void *user_data);
|
||||||
|
/**
|
||||||
|
* SpaNode::get_n_ports:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @n_input_ports: location to hold the number of input ports or %NULL
|
||||||
|
* @max_input_ports: location to hold the maximum number of input ports or %NULL
|
||||||
|
* @n_output_ports: location to hold the number of output ports or %NULL
|
||||||
|
* @max_output_ports: location to hold the maximum number of output ports or %NULL
|
||||||
|
*
|
||||||
|
* Get the current number of input and output ports and also the maximum
|
||||||
|
* number of ports.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||||
|
*/
|
||||||
|
SpaResult (*get_n_ports) (SpaHandle *handle,
|
||||||
|
unsigned int *n_input_ports,
|
||||||
|
unsigned int *max_input_ports,
|
||||||
|
unsigned int *n_output_ports,
|
||||||
|
unsigned int *max_output_ports);
|
||||||
|
/**
|
||||||
|
* SpaNode::get_port_ids:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @n_input_ports: size of the @input_ids array
|
||||||
|
* @input_ids: array to store the input stream ids
|
||||||
|
* @n_output_ports: size of the @output_ids array
|
||||||
|
* @output_ids: array to store the output stream ids
|
||||||
|
*
|
||||||
|
* Get the current number of input and output ports and also the maximum
|
||||||
|
* number of ports.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||||
|
*/
|
||||||
|
SpaResult (*get_port_ids) (SpaHandle *handle,
|
||||||
|
unsigned int n_input_ports,
|
||||||
|
uint32_t *input_ids,
|
||||||
|
unsigned int n_output_ports,
|
||||||
|
uint32_t *output_ids);
|
||||||
|
|
||||||
|
SpaResult (*add_port) (SpaHandle *handle,
|
||||||
|
SpaDirection direction,
|
||||||
|
uint32_t *port_id);
|
||||||
|
SpaResult (*remove_port) (SpaHandle *handle,
|
||||||
|
uint32_t port_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaNode::enum_port_formats:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @port_id: the port to query
|
||||||
|
* @index: the format index to retrieve
|
||||||
|
* @format: pointer to a format
|
||||||
|
*
|
||||||
|
* Enumerate all possible formats on @port_id of @node.
|
||||||
|
*
|
||||||
|
* Use the index to retrieve the formats one by one until the function
|
||||||
|
* returns #SPA_RESULT_ENUM_END.
|
||||||
|
*
|
||||||
|
* The result format can be queried and modified and ultimately be used
|
||||||
|
* to call SpaNode::set_port_format.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node or format is %NULL
|
||||||
|
* #SPA_RESULT_INVALID_PORT when port_id is not valid
|
||||||
|
* #SPA_RESULT_ENUM_END when no format exists for @index
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
SpaResult (*enum_port_formats) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
unsigned int index,
|
||||||
|
SpaFormat **format);
|
||||||
|
/**
|
||||||
|
* SpaNode::set_port_format:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @port_id: the port to configure
|
||||||
|
* @format: a #SpaFormat with the format
|
||||||
|
*
|
||||||
|
* Set a format on @port_id of @node.
|
||||||
|
*
|
||||||
|
* When @format is %NULL, the current format will be removed.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||||
|
* #SPA_RESULT_INVALID_PORT when port_id is not valid
|
||||||
|
* #SPA_RESULT_INVALID_MEDIA_TYPE when the media type is not valid
|
||||||
|
* #SPA_RESULT_INVALID_FORMAT_PROPERTIES when one of the mandatory format
|
||||||
|
* properties is not specified.
|
||||||
|
* #SPA_RESULT_WRONG_PROPERTY_TYPE when the type or size of a property
|
||||||
|
* is not correct.
|
||||||
|
*/
|
||||||
|
SpaResult (*set_port_format) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
int test_only,
|
||||||
|
const SpaFormat *format);
|
||||||
|
/**
|
||||||
|
* SpaNode::get_port_format:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @port_id: the port to query
|
||||||
|
* @format: a pointer to a location to hold the #SpaFormat
|
||||||
|
*
|
||||||
|
* Get the format on @port_id of @node. The result #SpaFormat can
|
||||||
|
* not be modified.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when @node or @format is %NULL
|
||||||
|
* #SPA_RESULT_INVALID_PORT when @port_id is not valid
|
||||||
|
* #SPA_RESULT_INVALID_NO_FORMAT when no format was set
|
||||||
|
*/
|
||||||
|
SpaResult (*get_port_format) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaFormat **format);
|
||||||
|
|
||||||
|
SpaResult (*get_port_info) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortInfo **info);
|
||||||
|
|
||||||
|
SpaResult (*get_port_props) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
SpaProps **props);
|
||||||
|
SpaResult (*set_port_props) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaProps *props);
|
||||||
|
|
||||||
|
SpaResult (*get_port_status) (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortStatus **status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaNode::push_port_input:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @n_info: number of #SpaInputInfo in @info
|
||||||
|
* @info: array of #SpaInputInfo
|
||||||
|
*
|
||||||
|
* Push a buffer and/or an event into one or more input ports of
|
||||||
|
* @node.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
||||||
|
* #SPA_RESULT_ERROR when one or more of the @info has an
|
||||||
|
* error result. Check the status of all the
|
||||||
|
* @info.
|
||||||
|
* #SPA_RESULT_HAVE_ENOUGH_INPUT when output can be produced.
|
||||||
|
*/
|
||||||
|
SpaResult (*push_port_input) (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaInputInfo *info);
|
||||||
|
/**
|
||||||
|
* SpaNode::pull_port_output:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @n_info: number of #SpaOutputInfo in @info
|
||||||
|
* @info: array of #SpaOutputInfo
|
||||||
|
*
|
||||||
|
* Pull a buffer and/or an event from one or more output ports of
|
||||||
|
* @node.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
||||||
|
* #SPA_RESULT_PORTS_CHANGED the number of ports has changed. None
|
||||||
|
* of the @info fields are modified
|
||||||
|
* #SPA_RESULT_FORMAT_CHANGED a format changed on some port.
|
||||||
|
* the ports that changed are marked in the status.
|
||||||
|
* #SPA_RESULT_PROPERTIES_CHANGED port properties changed. The
|
||||||
|
* changed ports are marked in the status.
|
||||||
|
* #SPA_RESULT_ERROR when one or more of the @info has an
|
||||||
|
* error result. Check the status of all the @info.
|
||||||
|
* #SPA_RESULT_NEED_MORE_INPUT when no output can be produced
|
||||||
|
* because more input is needed.
|
||||||
|
*/
|
||||||
|
SpaResult (*pull_port_output) (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaOutputInfo *info);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_NODE_H__ */
|
||||||
145
spa/include/spa/plugin.h
Normal file
145
spa/include/spa/plugin.h
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_PLUGIN_H__
|
||||||
|
#define __SPA_PLUGIN_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
#include <spa/props.h>
|
||||||
|
|
||||||
|
typedef struct _SpaHandle SpaHandle;
|
||||||
|
typedef struct _SpaHandleFactory SpaHandleFactory;
|
||||||
|
|
||||||
|
struct _SpaHandle {
|
||||||
|
/* user_data that can be set by the application */
|
||||||
|
void * user_data;
|
||||||
|
/**
|
||||||
|
* SpaHandle::get_interface:
|
||||||
|
* @handle: a #SpaHandle
|
||||||
|
* @interface_id: the interface id
|
||||||
|
* @interface: result to hold the interface.
|
||||||
|
*
|
||||||
|
* Get the interface provided by @handle with @interface_id.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when there are no extensions
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
||||||
|
*/
|
||||||
|
SpaResult (*get_interface) (SpaHandle *handle,
|
||||||
|
uint32_t interface_id,
|
||||||
|
const void **interface);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaInterfaceInfo:
|
||||||
|
* @interface_id: the id of the interface, can be used to get the interface
|
||||||
|
* @name: name of the interface
|
||||||
|
* @description: Human readable description of the interface.
|
||||||
|
*
|
||||||
|
* This structure lists the information about available interfaces on
|
||||||
|
* handles.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t interface_id;
|
||||||
|
const char *name;
|
||||||
|
const char *description;
|
||||||
|
} SpaInterfaceInfo;
|
||||||
|
|
||||||
|
struct _SpaHandleFactory {
|
||||||
|
/**
|
||||||
|
* SpaHandleFactory::name
|
||||||
|
*
|
||||||
|
* The name
|
||||||
|
*/
|
||||||
|
const char * name;
|
||||||
|
/**
|
||||||
|
* SpaHandleFactory::info
|
||||||
|
*
|
||||||
|
* Extra information about the handles of this factory.
|
||||||
|
*/
|
||||||
|
const SpaProps * info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaHandleFactory::instantiate
|
||||||
|
* @factory: a #SpaHandleFactory
|
||||||
|
* @handle: a pointer to hold the result
|
||||||
|
*
|
||||||
|
* Make an instance of this factory.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when an instance can't be made
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when factory or handle are %NULL
|
||||||
|
*/
|
||||||
|
SpaResult (*instantiate) (const SpaHandleFactory *factory,
|
||||||
|
SpaHandle **handle);
|
||||||
|
/**
|
||||||
|
* SpaHandle::enum_interface_info:
|
||||||
|
* @factory: a #SpaHandleFactory
|
||||||
|
* @index: the interface index
|
||||||
|
* @info: result to hold SpaInterfaceInfo.
|
||||||
|
*
|
||||||
|
* Get the interface information at @index.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_NOT_IMPLEMENTED when there are no interfaces
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
||||||
|
* #SPA_RESULT_ENUM_END when there are no more infos
|
||||||
|
*/
|
||||||
|
SpaResult (*enum_interface_info) (const SpaHandleFactory *factory,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaInterfaceInfo **info);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaEnumHandleFactoryFunc:
|
||||||
|
* @index: the index to enumerate
|
||||||
|
* @factory: a location to hold the factory result
|
||||||
|
*
|
||||||
|
* The function signature of the entry point in a plugin.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when factory is %NULL
|
||||||
|
* #SPA_RESULT_ENUM_END when there are no more factories
|
||||||
|
*/
|
||||||
|
typedef SpaResult (*SpaEnumHandleFactoryFunc) (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_enum_handle_factory:
|
||||||
|
* @index: the index to enumerate
|
||||||
|
* @factory: a location to hold the factory result
|
||||||
|
*
|
||||||
|
* The entry point in a plugin.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when factory is %NULL
|
||||||
|
* #SPA_RESULT_ENUM_END when there are no more factories
|
||||||
|
*/
|
||||||
|
SpaResult spa_enum_handle_factory (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_PLUGIN_H__ */
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Simple Plugin Interface
|
/* Simple Plugin API
|
||||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
|
|
@ -17,38 +17,38 @@
|
||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SPI_PORT_H__
|
#ifndef __SPA_PORT_H__
|
||||||
#define __SPI_PORT_H__
|
#define __SPA_PORT_H__
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <spi/defs.h>
|
#include <spa/defs.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpiPortInfoFlags:
|
* SpaPortInfoFlags:
|
||||||
* @SPI_PORT_INFO_FLAG_NONE: no flags
|
* @SPA_PORT_INFO_FLAG_NONE: no flags
|
||||||
* @SPI_PORT_INFO_FLAG_REMOVABLE: port can be removed
|
* @SPA_PORT_INFO_FLAG_REMOVABLE: port can be removed
|
||||||
* @SPI_PORT_INFO_FLAG_OPTIONAL: processing on port is optional
|
* @SPA_PORT_INFO_FLAG_OPTIONAL: processing on port is optional
|
||||||
* @SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER: the port can give a buffer
|
* @SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER: the port can give a buffer
|
||||||
* @SPI_PORT_INFO_FLAG_CAN_USE_BUFFER: the port can use a provided buffer
|
* @SPA_PORT_INFO_FLAG_CAN_USE_BUFFER: the port can use a provided buffer
|
||||||
* @SPI_PORT_INFO_FLAG_IN_PLACE: the port can process data in-place and will need
|
* @SPA_PORT_INFO_FLAG_IN_PLACE: the port can process data in-place and will need
|
||||||
* a writable input buffer when no output buffer is specified.
|
* a writable input buffer when no output buffer is specified.
|
||||||
* @SPI_PORT_INFO_FLAG_NO_REF: the port does not keep a ref on the buffer
|
* @SPA_PORT_INFO_FLAG_NO_REF: the port does not keep a ref on the buffer
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPI_PORT_INFO_FLAG_NONE = 0,
|
SPA_PORT_INFO_FLAG_NONE = 0,
|
||||||
SPI_PORT_INFO_FLAG_REMOVABLE = 1 << 0,
|
SPA_PORT_INFO_FLAG_REMOVABLE = 1 << 0,
|
||||||
SPI_PORT_INFO_FLAG_OPTIONAL = 1 << 1,
|
SPA_PORT_INFO_FLAG_OPTIONAL = 1 << 1,
|
||||||
SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2,
|
SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2,
|
||||||
SPI_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3,
|
SPA_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3,
|
||||||
SPI_PORT_INFO_FLAG_IN_PLACE = 1 << 4,
|
SPA_PORT_INFO_FLAG_IN_PLACE = 1 << 4,
|
||||||
SPI_PORT_INFO_FLAG_NO_REF = 1 << 5,
|
SPA_PORT_INFO_FLAG_NO_REF = 1 << 5,
|
||||||
} SpiPortInfoFlags;
|
} SpaPortInfoFlags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpiPortInfo
|
* SpaPortInfo
|
||||||
* @flags: extra port flags
|
* @flags: extra port flags
|
||||||
* @size: minimum size of the buffers or 0 when not specified
|
* @size: minimum size of the buffers or 0 when not specified
|
||||||
* @align: required alignment of the data
|
* @align: required alignment of the data
|
||||||
|
|
@ -59,33 +59,37 @@ typedef enum {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SpiPortInfoFlags flags;
|
SpaPortInfoFlags flags;
|
||||||
size_t minsize;
|
size_t minsize;
|
||||||
uint32_t align;
|
uint32_t align;
|
||||||
unsigned int maxbuffering;
|
unsigned int maxbuffering;
|
||||||
uint64_t latency;
|
uint64_t latency;
|
||||||
const char **features;
|
const char **features;
|
||||||
} SpiPortInfo;
|
} SpaPortInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpiPortStatusFlags:
|
* SpaPortStatusFlags:
|
||||||
* @SPI_PORT_STATUS_FLAG_NONE: no status flags
|
* @SPA_PORT_STATUS_FLAG_NONE: no status flags
|
||||||
* @SPI_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output
|
* @SPA_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output
|
||||||
* @SPI_PORT_STATUS_FLAG_NEED_INPUT: port needs input
|
* @SPA_PORT_STATUS_FLAG_NEED_INPUT: port needs input
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPI_PORT_STATUS_FLAG_NONE = 0,
|
SPA_PORT_STATUS_FLAG_NONE = 0,
|
||||||
SPI_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0,
|
SPA_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0,
|
||||||
SPI_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1,
|
SPA_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1,
|
||||||
} SpiPortStatusFlags;
|
} SpaPortStatusFlags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaPortStatus:
|
||||||
|
* @flags: port status flags
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SpiPortStatusFlags flags;
|
SpaPortStatusFlags flags;
|
||||||
} SpiPortStatus;
|
} SpaPortStatus;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif /* __SPI_PORT_H__ */
|
#endif /* __SPA_PORT_H__ */
|
||||||
242
spa/include/spa/props.h
Normal file
242
spa/include/spa/props.h
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_PROPS_H__
|
||||||
|
#define __SPA_PROPS_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaProps SpaProps;
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <spa/defs.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaPropType:
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_PROP_TYPE_INVALID = 0,
|
||||||
|
SPA_PROP_TYPE_BOOL,
|
||||||
|
SPA_PROP_TYPE_INT8,
|
||||||
|
SPA_PROP_TYPE_UINT8,
|
||||||
|
SPA_PROP_TYPE_INT16,
|
||||||
|
SPA_PROP_TYPE_UINT16,
|
||||||
|
SPA_PROP_TYPE_INT32,
|
||||||
|
SPA_PROP_TYPE_UINT32,
|
||||||
|
SPA_PROP_TYPE_INT64,
|
||||||
|
SPA_PROP_TYPE_UINT64,
|
||||||
|
SPA_PROP_TYPE_FLOAT,
|
||||||
|
SPA_PROP_TYPE_DOUBLE,
|
||||||
|
SPA_PROP_TYPE_STRING,
|
||||||
|
SPA_PROP_TYPE_POINTER,
|
||||||
|
SPA_PROP_TYPE_FRACTION,
|
||||||
|
SPA_PROP_TYPE_BITMASK,
|
||||||
|
SPA_PROP_TYPE_BYTES,
|
||||||
|
SPA_PROP_TYPE_STRUCT,
|
||||||
|
} SpaPropType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaPropFlags:
|
||||||
|
* @SPA_PROP_FLAG_NONE: no flags
|
||||||
|
* @SPA_PROP_FLAG_OPTIONAL: the value can be left unset
|
||||||
|
* @SPA_PROP_FLAG_READABLE: property is readable
|
||||||
|
* @SPA_PROP_FLAG_WRITABLE: property is writable
|
||||||
|
* @SPA_PROP_FLAG_READWRITE: property is readable and writable
|
||||||
|
* @SPA_PROP_FLAG_DEPRECATED: property is deprecated and should not be used
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_PROP_FLAG_NONE = 0,
|
||||||
|
SPA_PROP_FLAG_OPTIONAL = (1 << 0),
|
||||||
|
SPA_PROP_FLAG_READABLE = (1 << 1),
|
||||||
|
SPA_PROP_FLAG_WRITABLE = (1 << 2),
|
||||||
|
SPA_PROP_FLAG_READWRITE = SPA_PROP_FLAG_READABLE | SPA_PROP_FLAG_WRITABLE,
|
||||||
|
SPA_PROP_FLAG_DEPRECATED = (1 << 3),
|
||||||
|
} SpaPropFlags;
|
||||||
|
|
||||||
|
/* SpaPropRangeType:
|
||||||
|
* @SPA_PROP_RANGE_TYPE_NONE: no range specified, full range of type applies
|
||||||
|
* @SPA_PROP_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max
|
||||||
|
* @SPA_PROP_RANGE_TYPE_ENUM: range contains enum of possible values with
|
||||||
|
* NULL-terminated name
|
||||||
|
* @SPA_PROP_RANGE_TYPE_FLAGS: range contains flags of possible values with
|
||||||
|
* NULL-terminated name
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE = 0,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX,
|
||||||
|
SPA_PROP_RANGE_TYPE_ENUM,
|
||||||
|
SPA_PROP_RANGE_TYPE_FLAGS,
|
||||||
|
} SpaPropRangeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaPropRangeInfo:
|
||||||
|
* @name: name of this value
|
||||||
|
* @description: user visible description of this value
|
||||||
|
* @size: the size of value
|
||||||
|
* @value: a possible value
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
const char *description;
|
||||||
|
size_t size;
|
||||||
|
const void *value;
|
||||||
|
} SpaPropRangeInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaPropInfo:
|
||||||
|
* @id: unique id
|
||||||
|
* @name: human readable name
|
||||||
|
* @description: description of the property
|
||||||
|
* @flags: property flags
|
||||||
|
* @type: property type
|
||||||
|
* @max_size: maximum size of property value
|
||||||
|
* @default_size: size of default value
|
||||||
|
* @default_value: default value of property
|
||||||
|
* @range_type: type of the range values
|
||||||
|
* @n_range_values: number of elements in @range_values
|
||||||
|
* @range_values: array of possible values
|
||||||
|
* @tags: extra tags, NULL terminated
|
||||||
|
* @offset: offset in structure with data
|
||||||
|
* @unset_mask: mask to clear when value is set
|
||||||
|
* @priv: extra private data
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t id;
|
||||||
|
const char *name;
|
||||||
|
const char *description;
|
||||||
|
SpaPropFlags flags;
|
||||||
|
SpaPropType type;
|
||||||
|
size_t maxsize;
|
||||||
|
size_t default_size;
|
||||||
|
const void *default_value;
|
||||||
|
SpaPropRangeType range_type;
|
||||||
|
unsigned int n_range_values;
|
||||||
|
const SpaPropRangeInfo *range_values;
|
||||||
|
const char **tags;
|
||||||
|
size_t offset;
|
||||||
|
size_t mask_offset;
|
||||||
|
uint32_t unset_mask;
|
||||||
|
const void *priv;
|
||||||
|
} SpaPropInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaPropType type;
|
||||||
|
size_t size;
|
||||||
|
const void *value;
|
||||||
|
} SpaPropValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaProps:
|
||||||
|
*
|
||||||
|
* Generic propertiers.
|
||||||
|
*/
|
||||||
|
struct _SpaProps {
|
||||||
|
/**
|
||||||
|
* SpaProps::n_prop_info:
|
||||||
|
*
|
||||||
|
* The number of items in @prop_info.
|
||||||
|
*/
|
||||||
|
unsigned int n_prop_info;
|
||||||
|
/**
|
||||||
|
* SpaProps::prop_info:
|
||||||
|
*
|
||||||
|
* Info about the properties. Can be %NULL when unspecified.
|
||||||
|
*/
|
||||||
|
const SpaPropInfo *prop_info;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaProps::set_prop
|
||||||
|
* @props: a #SpaProps
|
||||||
|
* @index: the index of the property in the prop_info array
|
||||||
|
* @value: the value to set
|
||||||
|
*
|
||||||
|
* Sets @value in @prop. type should match the type specified
|
||||||
|
* in the #SpaPropInfo at @index or else #SPA_RESULT_WRONG_PROPERTY_TYPE
|
||||||
|
* is returned.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success.
|
||||||
|
* #SPA_RESULT_INVALID_PROPERTY_INDEX when @index is not valid
|
||||||
|
* #SPA_RESULT_WRONG_PROPERTY_TYPE when type is not correct
|
||||||
|
*/
|
||||||
|
SpaResult (*set_prop) (SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaPropValue *value);
|
||||||
|
/**
|
||||||
|
* SpaProps::get_prop
|
||||||
|
* @props: a #SpaProps
|
||||||
|
* @index: the property index in the prop_info array
|
||||||
|
* @value: a location for the type, size and value
|
||||||
|
*
|
||||||
|
* Get the type, size and value of the property at @index.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success.
|
||||||
|
* #SPA_RESULT_INVALID_PROPERTY_INDEX when @index is not valid
|
||||||
|
* #SPA_RESULT_PROPERTY_UNSET when no value has been set yet
|
||||||
|
*/
|
||||||
|
SpaResult (*get_prop) (const SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
SpaPropValue *value);
|
||||||
|
|
||||||
|
void *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
spa_props_index_for_id (const SpaProps *props, uint32_t id)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < props->n_prop_info; i++) {
|
||||||
|
if (props->prop_info[i].id == id)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
spa_props_index_for_name (const SpaProps *props, const char *name)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < props->n_prop_info; i++) {
|
||||||
|
if (strcmp (props->prop_info[i].name, name) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult spa_props_generic_set_prop (SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaPropValue *value);
|
||||||
|
SpaResult spa_props_generic_get_prop (const SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
SpaPropValue *value);
|
||||||
|
|
||||||
|
SpaResult spa_props_copy (const SpaProps *src,
|
||||||
|
SpaProps *dest);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_PROPS_H__ */
|
||||||
71
spa/include/spa/ringbuffer.h
Normal file
71
spa/include/spa/ringbuffer.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SPA_RINGBUFFER_H__
|
||||||
|
#define __SPA_RINGBUFFER_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _SpaRingbuffer SpaRingbuffer;
|
||||||
|
|
||||||
|
#include <spa/defs.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *data;
|
||||||
|
size_t len;
|
||||||
|
} SpaRingbufferArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpaRingbuffer:
|
||||||
|
* @data: pointer to data
|
||||||
|
* @readindex: the current read index
|
||||||
|
* @writeindex: the current write index
|
||||||
|
* @size: the size of the ringbuffer
|
||||||
|
* @size_mask: mask if @size is power of 2
|
||||||
|
*/
|
||||||
|
struct _SpaRingbuffer {
|
||||||
|
uint8_t *data;
|
||||||
|
volatile size_t readindex;
|
||||||
|
volatile size_t writeindex;
|
||||||
|
size_t size;
|
||||||
|
size_t size_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpaResult spa_ringbuffer_init (SpaRingbuffer *rbuf,
|
||||||
|
uint8_t *data, size_t size);
|
||||||
|
|
||||||
|
SpaResult spa_ringbuffer_clear (SpaRingbuffer *rbuf);
|
||||||
|
|
||||||
|
SpaResult spa_ringbuffer_get_read_areas (SpaRingbuffer *rbuf,
|
||||||
|
SpaRingbufferArea areas[2]);
|
||||||
|
SpaResult spa_ringbuffer_read_advance (SpaRingbuffer *rbuf,
|
||||||
|
ssize_t len);
|
||||||
|
|
||||||
|
SpaResult spa_ringbuffer_get_write_areas (SpaRingbuffer *rbuf,
|
||||||
|
SpaRingbufferArea areas[2]);
|
||||||
|
SpaResult spa_ringbuffer_write_advance (SpaRingbuffer *rbuf,
|
||||||
|
ssize_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SPA_RINGBUFFER_H__ */
|
||||||
254
spa/lib/audio-raw.c
Normal file
254
spa/lib/audio-raw.c
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
static const uint32_t format_values[] = {
|
||||||
|
SPA_AUDIO_FORMAT_S8,
|
||||||
|
SPA_AUDIO_FORMAT_U8,
|
||||||
|
/* 16 bit */
|
||||||
|
SPA_AUDIO_FORMAT_S16LE,
|
||||||
|
SPA_AUDIO_FORMAT_S16BE,
|
||||||
|
SPA_AUDIO_FORMAT_U16LE,
|
||||||
|
SPA_AUDIO_FORMAT_U16BE,
|
||||||
|
/* 24 bit in low 3 bytes of 32 bits*/
|
||||||
|
SPA_AUDIO_FORMAT_S24_32LE,
|
||||||
|
SPA_AUDIO_FORMAT_S24_32BE,
|
||||||
|
SPA_AUDIO_FORMAT_U24_32LE,
|
||||||
|
SPA_AUDIO_FORMAT_U24_32BE,
|
||||||
|
/* 32 bit */
|
||||||
|
SPA_AUDIO_FORMAT_S32LE,
|
||||||
|
SPA_AUDIO_FORMAT_S32BE,
|
||||||
|
SPA_AUDIO_FORMAT_U32LE,
|
||||||
|
SPA_AUDIO_FORMAT_U32BE,
|
||||||
|
/* 24 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S24LE,
|
||||||
|
SPA_AUDIO_FORMAT_S24BE,
|
||||||
|
SPA_AUDIO_FORMAT_U24LE,
|
||||||
|
SPA_AUDIO_FORMAT_U24BE,
|
||||||
|
/* 20 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S20LE,
|
||||||
|
SPA_AUDIO_FORMAT_S20BE,
|
||||||
|
SPA_AUDIO_FORMAT_U20LE,
|
||||||
|
SPA_AUDIO_FORMAT_U20BE,
|
||||||
|
/* 18 bit in 3 bytes*/
|
||||||
|
SPA_AUDIO_FORMAT_S18LE,
|
||||||
|
SPA_AUDIO_FORMAT_S18BE,
|
||||||
|
SPA_AUDIO_FORMAT_U18LE,
|
||||||
|
SPA_AUDIO_FORMAT_U18BE,
|
||||||
|
/* float */
|
||||||
|
SPA_AUDIO_FORMAT_F32LE,
|
||||||
|
SPA_AUDIO_FORMAT_F32BE,
|
||||||
|
SPA_AUDIO_FORMAT_F64LE,
|
||||||
|
SPA_AUDIO_FORMAT_F64BE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo format_format_range[] = {
|
||||||
|
{ "S8", "S8", sizeof (uint32_t), &format_values[0] },
|
||||||
|
{ "U8", "U8", sizeof (uint32_t), &format_values[1] },
|
||||||
|
{ "S16LE", "S16LE", sizeof (uint32_t), &format_values[2] },
|
||||||
|
{ "S16BE", "S16BE", sizeof (uint32_t), &format_values[3] },
|
||||||
|
{ "U16LE", "U16LE", sizeof (uint32_t), &format_values[4] },
|
||||||
|
{ "U16BE", "U16BE", sizeof (uint32_t), &format_values[5] },
|
||||||
|
{ "S24_32LE", "S24_32LE", sizeof (uint32_t), &format_values[6] },
|
||||||
|
{ "S24_32BE", "S24_32BE", sizeof (uint32_t), &format_values[7] },
|
||||||
|
{ "U24_32LE", "U24_32LE", sizeof (uint32_t), &format_values[8] },
|
||||||
|
{ "U24_32BE", "U24_32BE", sizeof (uint32_t), &format_values[9] },
|
||||||
|
{ "S32LE", "S32LE", sizeof (uint32_t), &format_values[10] },
|
||||||
|
{ "S32BE", "S32BE", sizeof (uint32_t), &format_values[11] },
|
||||||
|
{ "U32LE", "U32LE", sizeof (uint32_t), &format_values[12] },
|
||||||
|
{ "U32BE", "U32BE", sizeof (uint32_t), &format_values[13] },
|
||||||
|
{ "S24LE", "S24LE", sizeof (uint32_t), &format_values[14] },
|
||||||
|
{ "S24BE", "S24BE", sizeof (uint32_t), &format_values[15] },
|
||||||
|
{ "U24LE", "U24LE", sizeof (uint32_t), &format_values[16] },
|
||||||
|
{ "U24BE", "U24BE", sizeof (uint32_t), &format_values[17] },
|
||||||
|
{ "S20LE", "S20LE", sizeof (uint32_t), &format_values[18] },
|
||||||
|
{ "S20BE", "S20BE", sizeof (uint32_t), &format_values[19] },
|
||||||
|
{ "U20LE", "U20LE", sizeof (uint32_t), &format_values[20] },
|
||||||
|
{ "U20BE", "U20BE", sizeof (uint32_t), &format_values[21] },
|
||||||
|
{ "S18LE", "S18LE", sizeof (uint32_t), &format_values[22] },
|
||||||
|
{ "S18BE", "S18BE", sizeof (uint32_t), &format_values[23] },
|
||||||
|
{ "U18LE", "U18LE", sizeof (uint32_t), &format_values[24] },
|
||||||
|
{ "U18BE", "U18BE", sizeof (uint32_t), &format_values[25] },
|
||||||
|
{ "F32LE", "F32LE", sizeof (uint32_t), &format_values[26] },
|
||||||
|
{ "F32BE", "F32BE", sizeof (uint32_t), &format_values[27] },
|
||||||
|
{ "F64LE", "F64LE", sizeof (uint32_t), &format_values[28] },
|
||||||
|
{ "F64BE", "F64BE", sizeof (uint32_t), &format_values[29] },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t default_format = SPA_AUDIO_FORMAT_S16;
|
||||||
|
static const uint32_t default_flags = SPA_AUDIO_FLAG_NONE;
|
||||||
|
static const uint32_t default_layout = SPA_AUDIO_LAYOUT_INTERLEAVED;
|
||||||
|
static const uint32_t default_rate = 44100;
|
||||||
|
static const uint32_t default_channels = 2;
|
||||||
|
static const uint32_t default_channel_mask = 0;
|
||||||
|
|
||||||
|
static const uint32_t format_layouts[] = {
|
||||||
|
SPA_AUDIO_LAYOUT_INTERLEAVED,
|
||||||
|
SPA_AUDIO_LAYOUT_NON_INTERLEAVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo layouts_range[] = {
|
||||||
|
{ "interleaved", "Interleaved samples", sizeof (uint32_t), &format_layouts[0] },
|
||||||
|
{ "non-interleaved", "Non-interleaved samples", sizeof (uint32_t), &format_layouts[1] },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t format_flags[] = {
|
||||||
|
SPA_AUDIO_FLAG_NONE,
|
||||||
|
SPA_AUDIO_FLAG_UNPOSITIONED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo flags_range[] = {
|
||||||
|
{ "none", "No flags", sizeof (uint32_t), &format_flags[0] },
|
||||||
|
{ "unpositioned", "Unpositioned channels", sizeof (uint32_t), &format_flags[1] },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t min_uint32 = 1;
|
||||||
|
static const uint32_t max_uint32 = UINT32_MAX;
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo uint32_range[] = {
|
||||||
|
{ "min", "Minimum value", 4, &min_uint32 },
|
||||||
|
{ "max", "Maximum value", 4, &max_uint32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropInfo raw_format_prop_info[] =
|
||||||
|
{
|
||||||
|
{ SPA_PROP_ID_AUDIO_FORMAT, "format", "The media format",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_format,
|
||||||
|
SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (format_format_range), format_format_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.format),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 0,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_FLAGS, "flags", "Sample Flags",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_flags,
|
||||||
|
SPA_PROP_RANGE_TYPE_FLAGS, SPA_N_ELEMENTS (flags_range), flags_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.flags),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 1,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_LAYOUT, "layout", "Sample Layout",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_layout,
|
||||||
|
SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (layouts_range), layouts_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.layout),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 2,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_RATE, "rate", "Audio sample rate",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_rate,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.rate),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 3,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_CHANNELS, "channels", "Audio channels",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_channels,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.channels),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 4,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_CHANNEL_MASK, "channel-mask", "Audio channel mask",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_BITMASK, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_channel_mask,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info.channel_mask),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), 1 << 5,
|
||||||
|
NULL },
|
||||||
|
{ SPA_PROP_ID_AUDIO_RAW_INFO, "info", "the SpaAudioRawInfo structure",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_STRUCT, sizeof (SpaAudioRawInfo),
|
||||||
|
0, NULL,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioRawFormat, info),
|
||||||
|
offsetof (SpaAudioRawFormat, unset_mask), ~0,
|
||||||
|
NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_audio_raw_format_init (SpaAudioRawFormat *format)
|
||||||
|
{
|
||||||
|
format->format.media_type = SPA_MEDIA_TYPE_AUDIO;
|
||||||
|
format->format.media_subtype = SPA_MEDIA_SUBTYPE_RAW;
|
||||||
|
format->format.props.n_prop_info = SPA_N_ELEMENTS (raw_format_prop_info);
|
||||||
|
format->format.props.prop_info = raw_format_prop_info;
|
||||||
|
format->format.props.set_prop = spa_props_generic_set_prop;
|
||||||
|
format->format.props.get_prop = spa_props_generic_get_prop;
|
||||||
|
format->unset_mask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 4);
|
||||||
|
format->info.format = default_format;
|
||||||
|
format->info.flags = default_flags;
|
||||||
|
format->info.layout = default_layout;
|
||||||
|
format->info.rate = default_rate;
|
||||||
|
format->info.channels = default_channels;
|
||||||
|
format->info.channel_mask = default_channel_mask;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_audio_raw_format_parse (const SpaFormat *format,
|
||||||
|
SpaAudioRawFormat *rawformat)
|
||||||
|
{
|
||||||
|
SpaPropValue value;
|
||||||
|
const SpaProps *props;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if ((void *)format == (void *)rawformat)
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
|
||||||
|
if (format->media_type != SPA_MEDIA_TYPE_AUDIO ||
|
||||||
|
format->media_subtype != SPA_MEDIA_SUBTYPE_RAW)
|
||||||
|
return SPA_RESULT_INVALID_MEDIA_TYPE;
|
||||||
|
|
||||||
|
spa_audio_raw_format_init (rawformat);
|
||||||
|
|
||||||
|
props = &format->props;
|
||||||
|
if ((res = props->get_prop (props, SPA_PROP_ID_AUDIO_RAW_INFO, &value)) < 0)
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
if (value.type != SPA_PROP_TYPE_STRUCT || value.size != sizeof (SpaAudioRawInfo))
|
||||||
|
goto fallback;
|
||||||
|
|
||||||
|
memcpy (&rawformat->info, value.value, sizeof (SpaAudioRawInfo));
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
res = spa_props_copy (props, &rawformat->format.props);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
8
spa/lib/meson.build
Normal file
8
spa/lib/meson.build
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
spalib_sources = ['audio-raw.c',
|
||||||
|
'props.c',
|
||||||
|
'ringbuffer.c']
|
||||||
|
|
||||||
|
spalib = shared_library('spa-lib',
|
||||||
|
spalib_sources,
|
||||||
|
include_directories : inc,
|
||||||
|
install : true)
|
||||||
118
spa/lib/props.c
Normal file
118
spa/lib/props.c
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <spa/props.h>
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_props_generic_set_prop (SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaPropValue *value)
|
||||||
|
{
|
||||||
|
const SpaPropInfo *info;
|
||||||
|
|
||||||
|
if (props == NULL || value == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
if (index >= props->n_prop_info)
|
||||||
|
return SPA_RESULT_INVALID_PROPERTY_INDEX;
|
||||||
|
|
||||||
|
info = &props->prop_info[index];
|
||||||
|
if ((info->flags & SPA_PROP_FLAG_WRITABLE) == 0)
|
||||||
|
return SPA_RESULT_INVALID_PROPERTY_ACCESS;
|
||||||
|
if (info->type != value->type)
|
||||||
|
return SPA_RESULT_WRONG_PROPERTY_TYPE;
|
||||||
|
if (info->maxsize < value->size)
|
||||||
|
return SPA_RESULT_WRONG_PROPERTY_SIZE;
|
||||||
|
|
||||||
|
if (info->offset != 0)
|
||||||
|
memcpy ((uint8_t*)props + info->offset, value->value, value->size);
|
||||||
|
|
||||||
|
if (info->mask_offset != 0) {
|
||||||
|
uint32_t *mask = (uint32_t *)((uint8_t *)props + info->mask_offset);
|
||||||
|
*mask &= ~info->unset_mask;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_props_generic_get_prop (const SpaProps *props,
|
||||||
|
unsigned int index,
|
||||||
|
SpaPropValue *value)
|
||||||
|
{
|
||||||
|
const SpaPropInfo *info;
|
||||||
|
|
||||||
|
if (props == NULL || value == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
if (index >= props->n_prop_info)
|
||||||
|
return SPA_RESULT_INVALID_PROPERTY_INDEX;
|
||||||
|
|
||||||
|
info = &props->prop_info[index];
|
||||||
|
if ((info->flags & SPA_PROP_FLAG_READABLE) == 0)
|
||||||
|
return SPA_RESULT_INVALID_PROPERTY_ACCESS;
|
||||||
|
|
||||||
|
if (info->mask_offset != 0) {
|
||||||
|
uint32_t *mask = (uint32_t *)((uint8_t *)props + info->mask_offset);
|
||||||
|
if ((*mask & info->unset_mask))
|
||||||
|
return SPA_RESULT_PROPERTY_UNSET;
|
||||||
|
}
|
||||||
|
value->type = info->type;
|
||||||
|
value->size = info->maxsize;
|
||||||
|
if (info->offset != 0)
|
||||||
|
value->value = (uint8_t*)props + info->offset;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_props_copy (const SpaProps *src,
|
||||||
|
SpaProps *dest)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (src == dest)
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
|
||||||
|
for (i = 0; i < dest->n_prop_info; i++) {
|
||||||
|
const SpaPropInfo *info = &dest->prop_info[i];
|
||||||
|
SpaPropValue value;
|
||||||
|
|
||||||
|
if (!(info->flags & SPA_PROP_FLAG_WRITABLE))
|
||||||
|
continue;
|
||||||
|
if ((res = src->get_prop (src, spa_props_index_for_id (src, info->id), &value)) < 0)
|
||||||
|
continue;
|
||||||
|
if (value.type != info->type)
|
||||||
|
return SPA_RESULT_WRONG_PROPERTY_TYPE;
|
||||||
|
if (value.size > info->maxsize)
|
||||||
|
return SPA_RESULT_WRONG_PROPERTY_SIZE;
|
||||||
|
|
||||||
|
if (info->offset)
|
||||||
|
memcpy ((uint8_t*)dest + info->offset, value.value, value.size);
|
||||||
|
if (info->mask_offset != 0) {
|
||||||
|
uint32_t *mask = (uint32_t *)((uint8_t *)dest + info->mask_offset);
|
||||||
|
*mask &= ~info->unset_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
185
spa/lib/ringbuffer.c
Normal file
185
spa/lib/ringbuffer.c
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
/* Simple Plugin API
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <spa/ringbuffer.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_init:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
* @data: pointer to data
|
||||||
|
* @size: size of @data
|
||||||
|
*
|
||||||
|
* Initialize a #SpaRingbuffer with @data and @size.
|
||||||
|
* When size is a power of 2, size_mask will be set with the mask to
|
||||||
|
* efficiently wrap around the indexes.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK on success
|
||||||
|
* #SPA_RESULT_INVALID_ARGUMENTS when data or rbuf is %NULL
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_init (SpaRingbuffer *rbuf,
|
||||||
|
uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
if (rbuf == NULL || data == NULL || size == 0)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
rbuf->data = data;
|
||||||
|
rbuf->size = size;
|
||||||
|
rbuf->readindex = 0;
|
||||||
|
rbuf->writeindex = 0;
|
||||||
|
if ((size & (size - 1)) == 0)
|
||||||
|
rbuf->size_mask = size - 1;
|
||||||
|
else
|
||||||
|
rbuf->size_mask = 0;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_clear:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
*
|
||||||
|
* Clear @rbuf
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_clear (SpaRingbuffer *rbuf)
|
||||||
|
{
|
||||||
|
rbuf->readindex = 0;
|
||||||
|
rbuf->writeindex = 0;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_get_read_areas:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
* @areas: an array of #SpaRingbufferArea
|
||||||
|
*
|
||||||
|
* Fill @areas with pointers to read from. The total amount of
|
||||||
|
* bytes that can be read can be obtained by summing the areas len fields.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_get_read_areas (SpaRingbuffer *rbuf,
|
||||||
|
SpaRingbufferArea areas[2])
|
||||||
|
{
|
||||||
|
size_t avail, end, w, r;
|
||||||
|
|
||||||
|
w = rbuf->writeindex;
|
||||||
|
r = rbuf->readindex;
|
||||||
|
|
||||||
|
if (w > r) {
|
||||||
|
avail = w - r;
|
||||||
|
} else {
|
||||||
|
avail = (w - r + rbuf->size);
|
||||||
|
avail = (rbuf->size_mask ? avail & rbuf->size_mask : avail % rbuf->size);
|
||||||
|
}
|
||||||
|
end = r + avail;
|
||||||
|
if (end > rbuf->size) {
|
||||||
|
areas[0].data = &rbuf->data[r];
|
||||||
|
areas[0].len = rbuf->size - r;
|
||||||
|
areas[1].data = rbuf->data;
|
||||||
|
areas[1].len = end - rbuf->size;
|
||||||
|
} else {
|
||||||
|
areas[0].data = &rbuf->data[r];
|
||||||
|
areas[0].len = avail;
|
||||||
|
areas[1].len = 0;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_read_advance:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
* @len: number of bytes to advance
|
||||||
|
*
|
||||||
|
* Advance the read pointer by @len
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_read_advance (SpaRingbuffer *rbuf,
|
||||||
|
ssize_t len)
|
||||||
|
{
|
||||||
|
size_t tmp = rbuf->readindex + len;
|
||||||
|
rbuf->readindex = (rbuf->size_mask ? tmp & rbuf->size_mask : tmp % rbuf->size);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_get_write_areas:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
* @areas: an array of #SpaRingbufferArea
|
||||||
|
*
|
||||||
|
* Fill @areas with pointers to write to. The total amount of
|
||||||
|
* bytes that can be written can be obtained by summing the areas len fields.
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_get_write_areas (SpaRingbuffer *rbuf,
|
||||||
|
SpaRingbufferArea areas[2])
|
||||||
|
{
|
||||||
|
size_t avail, end, w, r;
|
||||||
|
|
||||||
|
w = rbuf->writeindex;
|
||||||
|
r = rbuf->readindex;
|
||||||
|
|
||||||
|
if (w > r) {
|
||||||
|
avail = (r - w + rbuf->size);
|
||||||
|
avail = (rbuf->size_mask ? avail & rbuf->size_mask : avail % rbuf->size);
|
||||||
|
avail -= 1;
|
||||||
|
} else if (w < r) {
|
||||||
|
avail = r - w - 1;
|
||||||
|
} else {
|
||||||
|
avail = rbuf->size - 1;
|
||||||
|
}
|
||||||
|
end = w + avail;
|
||||||
|
if (end > rbuf->size) {
|
||||||
|
areas[0].data = &rbuf->data[w];
|
||||||
|
areas[0].len = rbuf->size - w;
|
||||||
|
areas[1].data = rbuf->data;
|
||||||
|
areas[1].len = end - rbuf->size;
|
||||||
|
} else {
|
||||||
|
areas[0].data = &rbuf->data[w];
|
||||||
|
areas[0].len = avail;
|
||||||
|
areas[1].len = 0;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spa_ringbuffer_write_advance:
|
||||||
|
* @rbuf: a #SpaRingbuffer
|
||||||
|
* @len: number of bytes to advance
|
||||||
|
*
|
||||||
|
* Advance the write pointer by @len
|
||||||
|
*
|
||||||
|
* Returns: #SPA_RESULT_OK
|
||||||
|
*/
|
||||||
|
SpaResult
|
||||||
|
spa_ringbuffer_write_advance (SpaRingbuffer *rbuf,
|
||||||
|
ssize_t len)
|
||||||
|
{
|
||||||
|
size_t tmp = rbuf->writeindex + len;
|
||||||
|
rbuf->writeindex = (rbuf->size_mask ? tmp & rbuf->size_mask : tmp % rbuf->size);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
12
spa/meson.build
Normal file
12
spa/meson.build
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
project('spa', 'c')
|
||||||
|
|
||||||
|
alsa_dep = dependency('alsa')
|
||||||
|
dl_lib = find_library('dl', required : true)
|
||||||
|
|
||||||
|
inc = include_directories('include')
|
||||||
|
|
||||||
|
subdir('include')
|
||||||
|
subdir('lib')
|
||||||
|
subdir('plugins')
|
||||||
|
subdir('tools')
|
||||||
|
subdir('tests')
|
||||||
587
spa/plugins/alsa/alsa-sink.c
Normal file
587
spa/plugins/alsa/alsa-sink.c
Normal file
|
|
@ -0,0 +1,587 @@
|
||||||
|
/* Spa ALSA Sink
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <asoundlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
typedef struct _SpaALSASink SpaALSASink;
|
||||||
|
|
||||||
|
static const char default_device[] = "default";
|
||||||
|
static const uint32_t default_buffer_time = 10000;
|
||||||
|
static const uint32_t default_period_time = 5000;
|
||||||
|
static const bool default_period_event = 0;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaProps props;
|
||||||
|
char device[64];
|
||||||
|
char device_name[128];
|
||||||
|
char card_name[128];
|
||||||
|
uint32_t buffer_time;
|
||||||
|
uint32_t period_time;
|
||||||
|
bool period_event;
|
||||||
|
} SpaALSASinkProps;
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_alsa_sink_props (SpaALSASinkProps *props)
|
||||||
|
{
|
||||||
|
strncpy (props->device, default_device, 64);
|
||||||
|
props->buffer_time = default_buffer_time;
|
||||||
|
props->period_time = default_period_time;
|
||||||
|
props->period_event = default_period_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
snd_pcm_t *handle;
|
||||||
|
snd_output_t *output;
|
||||||
|
snd_pcm_sframes_t buffer_size;
|
||||||
|
snd_pcm_sframes_t period_size;
|
||||||
|
snd_pcm_channel_area_t areas[16];
|
||||||
|
pthread_t thread;
|
||||||
|
bool running;
|
||||||
|
} SpaALSAState;
|
||||||
|
|
||||||
|
typedef struct _ALSABuffer ALSABuffer;
|
||||||
|
|
||||||
|
struct _ALSABuffer {
|
||||||
|
SpaBuffer buffer;
|
||||||
|
SpaMeta meta[1];
|
||||||
|
SpaMetaHeader header;
|
||||||
|
SpaData data[1];
|
||||||
|
ALSABuffer *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _SpaALSASink {
|
||||||
|
SpaHandle handle;
|
||||||
|
|
||||||
|
SpaALSASinkProps tmp_props;
|
||||||
|
SpaALSASinkProps props;
|
||||||
|
|
||||||
|
bool activated;
|
||||||
|
|
||||||
|
SpaEventCallback event_cb;
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
bool have_format;
|
||||||
|
SpaAudioRawFormat query_format;
|
||||||
|
SpaAudioRawFormat current_format;
|
||||||
|
|
||||||
|
SpaALSAState state;
|
||||||
|
|
||||||
|
SpaPortInfo info;
|
||||||
|
SpaPortStatus status;
|
||||||
|
|
||||||
|
SpaBuffer *input_buffer;
|
||||||
|
|
||||||
|
ALSABuffer buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "alsa-utils.c"
|
||||||
|
|
||||||
|
static const uint32_t min_uint32 = 1;
|
||||||
|
static const uint32_t max_uint32 = UINT32_MAX;
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo uint32_range[] = {
|
||||||
|
{ "min", "Minimum value", 4, &min_uint32 },
|
||||||
|
{ "max", "Maximum value", 4, &max_uint32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_ID_DEVICE,
|
||||||
|
PROP_ID_DEVICE_NAME,
|
||||||
|
PROP_ID_CARD_NAME,
|
||||||
|
PROP_ID_BUFFER_TIME,
|
||||||
|
PROP_ID_PERIOD_TIME,
|
||||||
|
PROP_ID_PERIOD_EVENT,
|
||||||
|
PROP_ID_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropInfo prop_info[] =
|
||||||
|
{
|
||||||
|
{ PROP_ID_DEVICE, "device", "ALSA device, as defined in an asound configuration file",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_STRING, 63,
|
||||||
|
strlen (default_device)+1, default_device,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, device),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_DEVICE_NAME, "device-name", "Human-readable name of the sound device",
|
||||||
|
SPA_PROP_FLAG_READABLE,
|
||||||
|
SPA_PROP_TYPE_STRING, 127,
|
||||||
|
0, NULL,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, device_name),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_CARD_NAME, "card-name", "Human-readable name of the sound card",
|
||||||
|
SPA_PROP_FLAG_READABLE,
|
||||||
|
SPA_PROP_TYPE_STRING, 127,
|
||||||
|
0, NULL,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, card_name),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_BUFFER_TIME, "buffer-time", "The total size of the buffer in time",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_buffer_time,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, buffer_time),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_PERIOD_TIME, "period-time", "The size of a period in time",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_period_time,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, period_time),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_PERIOD_EVENT, "period-event", "Generate an event each period",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_BOOL, sizeof (bool),
|
||||||
|
sizeof (bool), &default_period_event,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaALSASinkProps, period_event),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_props (SpaHandle *handle,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || props == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
memcpy (&this->tmp_props, &this->props, sizeof (this->props));
|
||||||
|
*props = &this->tmp_props.props;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_set_props (SpaHandle *handle,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
SpaALSASinkProps *p = &this->props;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (props == NULL) {
|
||||||
|
reset_alsa_sink_props (p);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = spa_props_copy (props, &p->props);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_send_command (SpaHandle *handle,
|
||||||
|
SpaCommand *command)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || command == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (command->type) {
|
||||||
|
case SPA_COMMAND_INVALID:
|
||||||
|
return SPA_RESULT_INVALID_COMMAND;
|
||||||
|
|
||||||
|
case SPA_COMMAND_ACTIVATE:
|
||||||
|
if (!this->activated) {
|
||||||
|
spa_alsa_open (this);
|
||||||
|
this->activated = true;
|
||||||
|
}
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_ACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPA_COMMAND_DEACTIVATE:
|
||||||
|
if (this->activated) {
|
||||||
|
spa_alsa_close (this);
|
||||||
|
this->activated = false;
|
||||||
|
}
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_DEACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPA_COMMAND_START:
|
||||||
|
spa_alsa_start (this);
|
||||||
|
break;
|
||||||
|
case SPA_COMMAND_STOP:
|
||||||
|
spa_alsa_stop (this);
|
||||||
|
break;
|
||||||
|
case SPA_COMMAND_FLUSH:
|
||||||
|
case SPA_COMMAND_DRAIN:
|
||||||
|
case SPA_COMMAND_MARKER:
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_set_event_callback (SpaHandle *handle,
|
||||||
|
SpaEventCallback event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
this->event_cb = event;
|
||||||
|
this->user_data = user_data;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_n_ports (SpaHandle *handle,
|
||||||
|
unsigned int *n_input_ports,
|
||||||
|
unsigned int *max_input_ports,
|
||||||
|
unsigned int *n_output_ports,
|
||||||
|
unsigned int *max_output_ports)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports)
|
||||||
|
*n_input_ports = 1;
|
||||||
|
if (n_output_ports)
|
||||||
|
*n_output_ports = 0;
|
||||||
|
if (max_input_ports)
|
||||||
|
*max_input_ports = 1;
|
||||||
|
if (max_output_ports)
|
||||||
|
*max_output_ports = 0;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_port_ids (SpaHandle *handle,
|
||||||
|
unsigned int n_input_ports,
|
||||||
|
uint32_t *input_ids,
|
||||||
|
unsigned int n_output_ports,
|
||||||
|
uint32_t *output_ids)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports > 0)
|
||||||
|
input_ids[0] = 0;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_add_port (SpaHandle *handle,
|
||||||
|
SpaDirection direction,
|
||||||
|
uint32_t *port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_remove_port (SpaHandle *handle,
|
||||||
|
uint32_t port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_enum_port_formats (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
unsigned int index,
|
||||||
|
SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
spa_audio_raw_format_init (&this->query_format);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
spa_audio_raw_format_init (&this->query_format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
}
|
||||||
|
*format = &this->query_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_set_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
int test_only,
|
||||||
|
const SpaFormat *format)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (format == NULL) {
|
||||||
|
this->have_format = false;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
printf ("format %d\n", this->current_format.info.rate);
|
||||||
|
|
||||||
|
this->have_format = true;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*format = &this->current_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_port_info (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortInfo **info)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
*info = &this->info;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_set_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_get_port_status (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortStatus **status)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || status == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
*status = &this->status;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_push_port_input (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaInputInfo *info)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = (SpaALSASink *) handle;
|
||||||
|
unsigned int i;
|
||||||
|
bool have_error = false, have_enough = false;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
for (i = 0; i < n_info; i++) {
|
||||||
|
if (info[i].port_id != 0) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_PORT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info[i].buffer != NULL) {
|
||||||
|
if (!this->have_format) {
|
||||||
|
info[i].status = SPA_RESULT_NO_FORMAT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->input_buffer != NULL) {
|
||||||
|
info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
have_enough = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this->input_buffer = spa_buffer_ref (info[i].buffer);
|
||||||
|
}
|
||||||
|
info[i].status = SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
if (have_error)
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
if (have_enough)
|
||||||
|
return SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_node_pull_port_output (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaOutputInfo *info)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaNode alsasink_node = {
|
||||||
|
sizeof (SpaNode),
|
||||||
|
spa_alsa_sink_node_get_props,
|
||||||
|
spa_alsa_sink_node_set_props,
|
||||||
|
spa_alsa_sink_node_send_command,
|
||||||
|
spa_alsa_sink_node_set_event_callback,
|
||||||
|
spa_alsa_sink_node_get_n_ports,
|
||||||
|
spa_alsa_sink_node_get_port_ids,
|
||||||
|
spa_alsa_sink_node_add_port,
|
||||||
|
spa_alsa_sink_node_remove_port,
|
||||||
|
spa_alsa_sink_node_enum_port_formats,
|
||||||
|
spa_alsa_sink_node_set_port_format,
|
||||||
|
spa_alsa_sink_node_get_port_format,
|
||||||
|
spa_alsa_sink_node_get_port_info,
|
||||||
|
spa_alsa_sink_node_get_port_props,
|
||||||
|
spa_alsa_sink_node_set_port_props,
|
||||||
|
spa_alsa_sink_node_get_port_status,
|
||||||
|
spa_alsa_sink_node_push_port_input,
|
||||||
|
spa_alsa_sink_node_pull_port_output,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_alsa_sink_get_interface (SpaHandle *handle,
|
||||||
|
uint32_t interface_id,
|
||||||
|
const void **interface)
|
||||||
|
{
|
||||||
|
if (handle == NULL || interface == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (interface_id) {
|
||||||
|
case SPA_INTERFACE_ID_NODE:
|
||||||
|
*interface = &alsasink_node;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_UNKNOWN_INTERFACE;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaHandle *
|
||||||
|
spa_alsa_sink_new (void)
|
||||||
|
{
|
||||||
|
SpaHandle *handle;
|
||||||
|
SpaALSASink *this;
|
||||||
|
|
||||||
|
handle = calloc (1, sizeof (SpaALSASink));
|
||||||
|
handle->get_interface = spa_alsa_sink_get_interface;
|
||||||
|
|
||||||
|
this = (SpaALSASink *) handle;
|
||||||
|
this->props.props.n_prop_info = PROP_ID_LAST;
|
||||||
|
this->props.props.prop_info = prop_info;
|
||||||
|
this->props.props.set_prop = spa_props_generic_set_prop;
|
||||||
|
this->props.props.get_prop = spa_props_generic_get_prop;
|
||||||
|
reset_alsa_sink_props (&this->props);
|
||||||
|
|
||||||
|
this->info.flags = SPA_PORT_INFO_FLAG_NONE;
|
||||||
|
this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
367
spa/plugins/alsa/alsa-utils.c
Normal file
367
spa/plugins/alsa/alsa-utils.c
Normal file
|
|
@ -0,0 +1,367 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static int verbose = 0; /* verbose flag */
|
||||||
|
|
||||||
|
#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; }
|
||||||
|
|
||||||
|
static snd_pcm_format_t
|
||||||
|
spa_alsa_format_to_alsa (SpaAudioFormat format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case SPA_AUDIO_FORMAT_S8:
|
||||||
|
return SND_PCM_FORMAT_S8;
|
||||||
|
case SPA_AUDIO_FORMAT_U8:
|
||||||
|
return SND_PCM_FORMAT_U8;
|
||||||
|
/* 16 bit */
|
||||||
|
case SPA_AUDIO_FORMAT_S16LE:
|
||||||
|
return SND_PCM_FORMAT_S16_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_S16BE:
|
||||||
|
return SND_PCM_FORMAT_S16_BE;
|
||||||
|
case SPA_AUDIO_FORMAT_U16LE:
|
||||||
|
return SND_PCM_FORMAT_U16_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_U16BE:
|
||||||
|
return SND_PCM_FORMAT_U16_BE;
|
||||||
|
/* 24 bit in low 3 bytes of 32 bits */
|
||||||
|
case SPA_AUDIO_FORMAT_S24_32LE:
|
||||||
|
return SND_PCM_FORMAT_S24_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_S24_32BE:
|
||||||
|
return SND_PCM_FORMAT_S24_BE;
|
||||||
|
case SPA_AUDIO_FORMAT_U24_32LE:
|
||||||
|
return SND_PCM_FORMAT_U24_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_U24_32BE:
|
||||||
|
return SND_PCM_FORMAT_U24_BE;
|
||||||
|
/* 24 bit in 3 bytes */
|
||||||
|
case SPA_AUDIO_FORMAT_S24LE:
|
||||||
|
return SND_PCM_FORMAT_S24_3LE;
|
||||||
|
case SPA_AUDIO_FORMAT_S24BE:
|
||||||
|
return SND_PCM_FORMAT_S24_3BE;
|
||||||
|
case SPA_AUDIO_FORMAT_U24LE:
|
||||||
|
return SND_PCM_FORMAT_U24_3LE;
|
||||||
|
case SPA_AUDIO_FORMAT_U24BE:
|
||||||
|
return SND_PCM_FORMAT_U24_3BE;
|
||||||
|
/* 32 bit */
|
||||||
|
case SPA_AUDIO_FORMAT_S32LE:
|
||||||
|
return SND_PCM_FORMAT_S32_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_S32BE:
|
||||||
|
return SND_PCM_FORMAT_S32_BE;
|
||||||
|
case SPA_AUDIO_FORMAT_U32LE:
|
||||||
|
return SND_PCM_FORMAT_U32_LE;
|
||||||
|
case SPA_AUDIO_FORMAT_U32BE:
|
||||||
|
return SND_PCM_FORMAT_U32_BE;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SND_PCM_FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_hwparams (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
unsigned int rrate;
|
||||||
|
snd_pcm_uframes_t size;
|
||||||
|
int err, dir;
|
||||||
|
snd_pcm_hw_params_t *params;
|
||||||
|
snd_pcm_format_t format;
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
SpaAudioRawFormat *fmt = &this->current_format;
|
||||||
|
SpaAudioRawInfo *info = &fmt->info;
|
||||||
|
snd_pcm_t *handle = state->handle;
|
||||||
|
unsigned int buffer_time;
|
||||||
|
unsigned int period_time;
|
||||||
|
|
||||||
|
snd_pcm_hw_params_alloca (¶ms);
|
||||||
|
/* choose all parameters */
|
||||||
|
CHECK (snd_pcm_hw_params_any (handle, params), "Broken configuration for playback: no configurations available");
|
||||||
|
/* set hardware resampling */
|
||||||
|
CHECK (snd_pcm_hw_params_set_rate_resample (handle, params, 0), "set_rate_resample");
|
||||||
|
/* set the interleaved read/write format */
|
||||||
|
CHECK (snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access");
|
||||||
|
|
||||||
|
/* set the sample format */
|
||||||
|
format = spa_alsa_format_to_alsa (info->format);
|
||||||
|
printf ("Stream parameters are %iHz, %s, %i channels\n", info->rate, snd_pcm_format_name(format), info->channels);
|
||||||
|
CHECK (snd_pcm_hw_params_set_format (handle, params, format), "set_format");
|
||||||
|
/* set the count of channels */
|
||||||
|
CHECK (snd_pcm_hw_params_set_channels (handle, params, info->channels), "set_channels");
|
||||||
|
/* set the stream rate */
|
||||||
|
rrate = info->rate;
|
||||||
|
CHECK (snd_pcm_hw_params_set_rate_near (handle, params, &rrate, 0), "set_rate_near");
|
||||||
|
if (rrate != info->rate) {
|
||||||
|
printf("Rate doesn't match (requested %iHz, get %iHz)\n", info->rate, rrate);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/* set the buffer time */
|
||||||
|
buffer_time = this->props.buffer_time;
|
||||||
|
CHECK (snd_pcm_hw_params_set_buffer_time_near (handle, params, &buffer_time, &dir), "set_buffer_time_near");
|
||||||
|
CHECK (snd_pcm_hw_params_get_buffer_size (params, &size), "get_buffer_size");
|
||||||
|
state->buffer_size = size;
|
||||||
|
|
||||||
|
/* set the period time */
|
||||||
|
period_time = this->props.period_time;
|
||||||
|
CHECK (snd_pcm_hw_params_set_period_time_near (handle, params, &period_time, &dir), "set_period_time_near");
|
||||||
|
CHECK (snd_pcm_hw_params_get_period_size (params, &size, &dir), "get_period_size");
|
||||||
|
state->period_size = size;
|
||||||
|
|
||||||
|
/* write the parameters to device */
|
||||||
|
CHECK (snd_pcm_hw_params (handle, params), "set_hw_params");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_swparams (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
snd_pcm_t *handle = state->handle;
|
||||||
|
int err = 0;
|
||||||
|
snd_pcm_sw_params_t *params;
|
||||||
|
|
||||||
|
snd_pcm_sw_params_alloca (¶ms);
|
||||||
|
|
||||||
|
/* get the current params */
|
||||||
|
CHECK (snd_pcm_sw_params_current (handle, params), "sw_params_current");
|
||||||
|
/* start the transfer when the buffer is almost full: */
|
||||||
|
/* (buffer_size / avail_min) * avail_min */
|
||||||
|
CHECK (snd_pcm_sw_params_set_start_threshold (handle, params,
|
||||||
|
(state->buffer_size / state->period_size) * state->period_size), "set_start_threshold");
|
||||||
|
|
||||||
|
/* allow the transfer when at least period_size samples can be processed */
|
||||||
|
/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
|
||||||
|
CHECK (snd_pcm_sw_params_set_avail_min (handle, params,
|
||||||
|
this->props.period_event ? state->buffer_size : state->period_size), "set_avail_min");
|
||||||
|
/* enable period events when requested */
|
||||||
|
if (this->props.period_event) {
|
||||||
|
CHECK (snd_pcm_sw_params_set_period_event (handle, params, 1), "set_period_event");
|
||||||
|
}
|
||||||
|
/* write the parameters to the playback device */
|
||||||
|
CHECK (snd_pcm_sw_params (handle, params), "sw_params");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Underrun and suspend recovery
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xrun_recovery (snd_pcm_t *handle, int err)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
printf("stream recovery\n");
|
||||||
|
if (err == -EPIPE) { /* under-run */
|
||||||
|
err = snd_pcm_prepare(handle);
|
||||||
|
if (err < 0)
|
||||||
|
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
|
||||||
|
return 0;
|
||||||
|
} else if (err == -ESTRPIPE) {
|
||||||
|
while ((err = snd_pcm_resume(handle)) == -EAGAIN)
|
||||||
|
sleep(1); /* wait until the suspend flag is released */
|
||||||
|
if (err < 0) {
|
||||||
|
err = snd_pcm_prepare(handle);
|
||||||
|
if (err < 0)
|
||||||
|
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transfer method - direct write only
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
direct_loop (void *user_data)
|
||||||
|
{
|
||||||
|
SpaALSASink *this = user_data;
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
snd_pcm_t *handle = state->handle;
|
||||||
|
const snd_pcm_channel_area_t *my_areas;
|
||||||
|
snd_pcm_uframes_t offset, frames, size;
|
||||||
|
snd_pcm_sframes_t avail, commitres;
|
||||||
|
snd_pcm_state_t st;
|
||||||
|
int err, first = 1;
|
||||||
|
|
||||||
|
while (state->running) {
|
||||||
|
st = snd_pcm_state(handle);
|
||||||
|
if (st == SND_PCM_STATE_XRUN) {
|
||||||
|
err = xrun_recovery(handle, -EPIPE);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("XRUN recovery failed: %s\n", snd_strerror(err));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
first = 1;
|
||||||
|
} else if (st == SND_PCM_STATE_SUSPENDED) {
|
||||||
|
err = xrun_recovery(handle, -ESTRPIPE);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avail = snd_pcm_avail_update(handle);
|
||||||
|
if (avail < 0) {
|
||||||
|
err = xrun_recovery(handle, avail);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("avail update failed: %s\n", snd_strerror(err));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
first = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (avail < state->period_size) {
|
||||||
|
if (first) {
|
||||||
|
first = 0;
|
||||||
|
err = snd_pcm_start(handle);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("Start error: %s\n", snd_strerror(err));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = snd_pcm_wait(handle, -1);
|
||||||
|
if (err < 0) {
|
||||||
|
if ((err = xrun_recovery(handle, err)) < 0) {
|
||||||
|
printf("snd_pcm_wait error: %s\n", snd_strerror(err));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
first = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size = state->period_size;
|
||||||
|
while (size > 0) {
|
||||||
|
frames = size;
|
||||||
|
err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
|
||||||
|
if (err < 0) {
|
||||||
|
if ((err = xrun_recovery(handle, err)) < 0) {
|
||||||
|
printf("MMAP begin avail error: %s\n", snd_strerror(err));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
first = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SpaEvent event;
|
||||||
|
ALSABuffer *buffer = &this->buffer;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_PULL_INPUT;
|
||||||
|
event.port_id = 0;
|
||||||
|
event.size = frames * sizeof (uint16_t) * 2;
|
||||||
|
event.data = buffer;
|
||||||
|
|
||||||
|
buffer->buffer.refcount = 1;
|
||||||
|
buffer->buffer.notify = NULL;
|
||||||
|
buffer->buffer.size = frames * sizeof (uint16_t) * 2;
|
||||||
|
buffer->buffer.n_metas = 1;
|
||||||
|
buffer->buffer.metas = buffer->meta;
|
||||||
|
buffer->buffer.n_datas = 1;
|
||||||
|
buffer->buffer.datas = buffer->data;
|
||||||
|
|
||||||
|
buffer->header.flags = 0;
|
||||||
|
buffer->header.seq = 0;
|
||||||
|
buffer->header.pts = 0;
|
||||||
|
buffer->header.dts_offset = 0;
|
||||||
|
|
||||||
|
buffer->meta[0].type = SPA_META_TYPE_HEADER;
|
||||||
|
buffer->meta[0].data = &buffer->header;
|
||||||
|
buffer->meta[0].size = sizeof (buffer->header);
|
||||||
|
|
||||||
|
buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
|
||||||
|
buffer->data[0].data = (uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2);
|
||||||
|
buffer->data[0].size = frames * sizeof (uint16_t) * 2;
|
||||||
|
|
||||||
|
this->event_cb (&this->handle, &event,this->user_data);
|
||||||
|
|
||||||
|
spa_buffer_unref ((SpaBuffer *)event.data);
|
||||||
|
}
|
||||||
|
if (this->input_buffer) {
|
||||||
|
if (this->input_buffer != &this->buffer.buffer) {
|
||||||
|
/* FIXME, copy input */
|
||||||
|
}
|
||||||
|
spa_buffer_unref (this->input_buffer);
|
||||||
|
this->input_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
commitres = snd_pcm_mmap_commit(handle, offset, frames);
|
||||||
|
if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
|
||||||
|
if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
|
||||||
|
printf("MMAP commit error: %s\n", snd_strerror(err));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
first = 1;
|
||||||
|
}
|
||||||
|
size -= frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_alsa_open (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed");
|
||||||
|
|
||||||
|
printf ("Playback device is '%s'\n", this->props.device);
|
||||||
|
CHECK (snd_pcm_open (&state->handle,
|
||||||
|
this->props.device,
|
||||||
|
SND_PCM_STREAM_PLAYBACK,
|
||||||
|
SND_PCM_NONBLOCK |
|
||||||
|
SND_PCM_NO_AUTO_RESAMPLE |
|
||||||
|
SND_PCM_NO_AUTO_CHANNELS |
|
||||||
|
SND_PCM_NO_AUTO_FORMAT), "open failed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_alsa_start (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
CHECK (set_hwparams (this), "hwparams");
|
||||||
|
CHECK (set_swparams (this), "swparams");
|
||||||
|
|
||||||
|
snd_pcm_dump (this->state.handle, this->state.output);
|
||||||
|
|
||||||
|
state->running = true;
|
||||||
|
if ((err = pthread_create (&state->thread, NULL, direct_loop, this)) != 0) {
|
||||||
|
printf ("can't create thread: %d", err);
|
||||||
|
state->running = false;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_alsa_stop (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
|
||||||
|
if (state->running) {
|
||||||
|
state->running = false;
|
||||||
|
pthread_join (state->thread, NULL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_alsa_close (SpaALSASink *this)
|
||||||
|
{
|
||||||
|
SpaALSAState *state = &this->state;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
CHECK (snd_pcm_close (state->handle), "close failed");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
78
spa/plugins/alsa/alsa.c
Normal file
78
spa/plugins/alsa/alsa.c
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Spa ALSA support
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <spa/plugin.h>
|
||||||
|
#include <spa/node.h>
|
||||||
|
|
||||||
|
SpaHandle * spa_alsa_sink_new (void);
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
alsa_sink_instantiate (const SpaHandleFactory *factory,
|
||||||
|
SpaHandle **handle)
|
||||||
|
{
|
||||||
|
if (factory == NULL || handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
*handle = spa_alsa_sink_new ();
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaInterfaceInfo alsa_sink_interfaces[] =
|
||||||
|
{
|
||||||
|
{ SPA_INTERFACE_ID_NODE,
|
||||||
|
SPA_INTERFACE_ID_NODE_NAME,
|
||||||
|
SPA_INTERFACE_ID_NODE_DESCRIPTION,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
alsa_sink_enum_interface_info (const SpaHandleFactory *factory,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaInterfaceInfo **info)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*info = &alsa_sink_interfaces[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaHandleFactory factories[] =
|
||||||
|
{
|
||||||
|
{ "alsa-sink",
|
||||||
|
NULL,
|
||||||
|
alsa_sink_instantiate,
|
||||||
|
alsa_sink_enum_interface_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_enum_handle_factory (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*factory = &factories[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
9
spa/plugins/alsa/meson.build
Normal file
9
spa/plugins/alsa/meson.build
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
alsa_sources = ['alsa.c',
|
||||||
|
'alsa-sink.c']
|
||||||
|
|
||||||
|
alsalib = shared_library('spa-alsa',
|
||||||
|
alsa_sources,
|
||||||
|
include_directories : inc,
|
||||||
|
dependencies : alsa_dep,
|
||||||
|
link_with : spalib,
|
||||||
|
install : true)
|
||||||
740
spa/plugins/audiomixer/audiomixer.c
Normal file
740
spa/plugins/audiomixer/audiomixer.c
Normal file
|
|
@ -0,0 +1,740 @@
|
||||||
|
/* Spa
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
#define MAX_PORTS 128
|
||||||
|
|
||||||
|
typedef struct _SpaAudioMixer SpaAudioMixer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaProps props;
|
||||||
|
} SpaAudioMixerProps;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaProps prop;
|
||||||
|
} SpaAudioMixerPortProps;
|
||||||
|
|
||||||
|
typedef struct _MixerBuffer MixerBuffer;
|
||||||
|
|
||||||
|
struct _MixerBuffer {
|
||||||
|
SpaBuffer buffer;
|
||||||
|
SpaMeta meta[1];
|
||||||
|
SpaMetaHeader header;
|
||||||
|
SpaData data[1];
|
||||||
|
uint16_t samples[4096];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool valid;
|
||||||
|
SpaAudioMixerPortProps props;
|
||||||
|
SpaPortInfo info;
|
||||||
|
SpaPortStatus status;
|
||||||
|
SpaBuffer *buffer;
|
||||||
|
size_t buffer_index;
|
||||||
|
size_t buffer_offset;
|
||||||
|
size_t buffer_queued;
|
||||||
|
MixerBuffer mix;
|
||||||
|
} SpaAudioMixerPort;
|
||||||
|
|
||||||
|
struct _SpaAudioMixer {
|
||||||
|
SpaHandle handle;
|
||||||
|
|
||||||
|
SpaAudioMixerProps props;
|
||||||
|
SpaAudioMixerProps tmp_props;
|
||||||
|
|
||||||
|
SpaEventCallback event_cb;
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
bool have_format;
|
||||||
|
SpaAudioRawFormat query_format;
|
||||||
|
SpaAudioRawFormat current_format;
|
||||||
|
|
||||||
|
int port_count;
|
||||||
|
int port_queued;
|
||||||
|
SpaAudioMixerPort ports[MAX_PORTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_ID_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropInfo prop_info[] =
|
||||||
|
{
|
||||||
|
{ PROP_ID_LAST, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_audiomixer_props (SpaAudioMixerProps *props)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_props (SpaHandle *handle,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || props == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
|
||||||
|
*props = &this->tmp_props.props;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_set_props (SpaHandle *handle,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
SpaAudioMixerProps *p = &this->props;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (props == NULL) {
|
||||||
|
reset_audiomixer_props (p);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
res = spa_props_copy (props, &p->props);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_send_command (SpaHandle *handle,
|
||||||
|
SpaCommand *command)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || command == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (command->type) {
|
||||||
|
case SPA_COMMAND_INVALID:
|
||||||
|
return SPA_RESULT_INVALID_COMMAND;
|
||||||
|
|
||||||
|
case SPA_COMMAND_ACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_ACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_DEACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_DEACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_START:
|
||||||
|
case SPA_COMMAND_STOP:
|
||||||
|
case SPA_COMMAND_FLUSH:
|
||||||
|
case SPA_COMMAND_DRAIN:
|
||||||
|
case SPA_COMMAND_MARKER:
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_set_event_callback (SpaHandle *handle,
|
||||||
|
SpaEventCallback event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
this->event_cb = event;
|
||||||
|
this->user_data = user_data;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_n_ports (SpaHandle *handle,
|
||||||
|
unsigned int *n_input_ports,
|
||||||
|
unsigned int *max_input_ports,
|
||||||
|
unsigned int *n_output_ports,
|
||||||
|
unsigned int *max_output_ports)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports)
|
||||||
|
*n_input_ports = 0;
|
||||||
|
if (n_output_ports)
|
||||||
|
*n_output_ports = 1;
|
||||||
|
if (max_input_ports)
|
||||||
|
*max_input_ports = MAX_PORTS;
|
||||||
|
if (max_output_ports)
|
||||||
|
*max_output_ports = 1;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_port_ids (SpaHandle *handle,
|
||||||
|
unsigned int n_input_ports,
|
||||||
|
uint32_t *input_ids,
|
||||||
|
unsigned int n_output_ports,
|
||||||
|
uint32_t *output_ids)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
int i, idx;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (input_ids) {
|
||||||
|
for (i = 1, idx = 0; i < MAX_PORTS && idx < n_input_ports; i++) {
|
||||||
|
if (this->ports[i].valid)
|
||||||
|
input_ids[idx++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n_output_ports > 0 && output_ids)
|
||||||
|
output_ids[0] = 0;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_add_port (SpaHandle *handle,
|
||||||
|
SpaDirection direction,
|
||||||
|
uint32_t *port_id)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (handle == NULL || port_id == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (direction != SPA_DIRECTION_INPUT)
|
||||||
|
return SPA_RESULT_INVALID_DIRECTION;
|
||||||
|
|
||||||
|
for (i = 1; i < MAX_PORTS; i++)
|
||||||
|
if (!this->ports[i].valid)
|
||||||
|
break;
|
||||||
|
if (i == MAX_PORTS)
|
||||||
|
return SPA_RESULT_TOO_MANY_PORTS;
|
||||||
|
|
||||||
|
this->ports[i].valid = true;
|
||||||
|
*port_id = i;
|
||||||
|
this->port_count++;
|
||||||
|
|
||||||
|
this->ports[i].info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_REMOVABLE |
|
||||||
|
SPA_PORT_INFO_FLAG_OPTIONAL |
|
||||||
|
SPA_PORT_INFO_FLAG_IN_PLACE;
|
||||||
|
this->ports[i].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
|
||||||
|
this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_remove_port (SpaHandle *handle,
|
||||||
|
uint32_t port_id)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id == 0 || port_id >= MAX_PORTS || !this->ports[port_id].valid)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
this->ports[port_id].valid = false;
|
||||||
|
this->port_count--;
|
||||||
|
if (this->ports[port_id].buffer) {
|
||||||
|
spa_buffer_unref (this->ports[port_id].buffer);
|
||||||
|
this->ports[port_id].buffer = NULL;
|
||||||
|
this->port_queued--;
|
||||||
|
}
|
||||||
|
if (this->port_count == this->port_queued)
|
||||||
|
this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_enum_port_formats (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
unsigned int index,
|
||||||
|
SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id > MAX_PORTS)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
spa_audio_raw_format_init (&this->query_format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
}
|
||||||
|
*format = &this->query_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_set_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
int test_only,
|
||||||
|
const SpaFormat *format)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id > MAX_PORTS)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (format == NULL) {
|
||||||
|
this->have_format = false;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
this->have_format = true;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*format = &this->current_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_port_info (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortInfo **info)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
*info = &this->ports[port_id].info;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_set_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_get_port_status (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortStatus **status)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || status == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*status = &this->ports[port_id].status;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_push_port_input (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaInputInfo *info)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
SpaBuffer *buffer;
|
||||||
|
SpaEvent *event;
|
||||||
|
unsigned int i;
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (this->ports[0].status.flags & SPA_PORT_STATUS_FLAG_HAVE_OUTPUT)
|
||||||
|
return SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
|
||||||
|
for (i = 0; i < n_info; i++) {
|
||||||
|
int idx = info[i].port_id;
|
||||||
|
|
||||||
|
if (idx >= MAX_PORTS || !this->ports[idx].valid) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_PORT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = info[i].event;
|
||||||
|
buffer = info[i].buffer;
|
||||||
|
|
||||||
|
if (buffer == NULL && event == NULL) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
if (!this->have_format) {
|
||||||
|
info[i].status = SPA_RESULT_NO_FORMAT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->ports[idx].buffer != NULL) {
|
||||||
|
info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this->ports[idx].buffer = spa_buffer_ref (buffer);
|
||||||
|
this->ports[idx].buffer_queued = buffer->size;
|
||||||
|
this->ports[idx].buffer_index = 0;
|
||||||
|
this->ports[idx].buffer_offset = 0;
|
||||||
|
this->port_queued++;
|
||||||
|
|
||||||
|
if (this->port_queued == this->port_count)
|
||||||
|
this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
}
|
||||||
|
if (event) {
|
||||||
|
info[i].status = SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info[i].status = SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
if (have_error)
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
pull_port (SpaAudioMixer *this, uint32_t port_id, SpaOutputInfo *info, size_t pull_size)
|
||||||
|
{
|
||||||
|
SpaEvent event;
|
||||||
|
MixerBuffer *buffer = &this->ports[port_id].mix;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_PULL_INPUT;
|
||||||
|
event.port_id = port_id;
|
||||||
|
event.data = buffer;
|
||||||
|
|
||||||
|
buffer->buffer.refcount = 1;
|
||||||
|
buffer->buffer.notify = NULL;
|
||||||
|
buffer->buffer.size = pull_size;
|
||||||
|
buffer->buffer.n_metas = 1;
|
||||||
|
buffer->buffer.metas = buffer->meta;
|
||||||
|
buffer->buffer.n_datas = 1;
|
||||||
|
buffer->buffer.datas = buffer->data;
|
||||||
|
|
||||||
|
buffer->header.flags = 0;
|
||||||
|
buffer->header.seq = 0;
|
||||||
|
buffer->header.pts = 0;
|
||||||
|
buffer->header.dts_offset = 0;
|
||||||
|
|
||||||
|
buffer->meta[0].type = SPA_META_TYPE_HEADER;
|
||||||
|
buffer->meta[0].data = &buffer->header;
|
||||||
|
buffer->meta[0].size = sizeof (buffer->header);
|
||||||
|
|
||||||
|
buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
|
||||||
|
buffer->data[0].data = buffer->samples;
|
||||||
|
buffer->data[0].size = pull_size;
|
||||||
|
|
||||||
|
this->event_cb (&this->handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_port_data (SpaAudioMixer *this, SpaBuffer *out, SpaAudioMixerPort *port)
|
||||||
|
{
|
||||||
|
int i, oi = 0;
|
||||||
|
uint8_t *op, *ip;
|
||||||
|
size_t os, is, chunk;
|
||||||
|
|
||||||
|
op = ip = NULL;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (op == NULL) {
|
||||||
|
op = out->datas[oi].data;
|
||||||
|
os = out->datas[oi].size;
|
||||||
|
}
|
||||||
|
if (ip == NULL) {
|
||||||
|
ip = port->buffer->datas[port->buffer_index].data;
|
||||||
|
is = port->buffer->datas[port->buffer_index].size;
|
||||||
|
ip += port->buffer_offset;
|
||||||
|
is -= port->buffer_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = os < is ? os : is;
|
||||||
|
|
||||||
|
for (i = 0; i < chunk; i++)
|
||||||
|
op[i] += ip[i];
|
||||||
|
|
||||||
|
if ((is -= chunk) == 0) {
|
||||||
|
if (++port->buffer_index == port->buffer->n_datas) {
|
||||||
|
spa_buffer_unref (port->buffer);
|
||||||
|
port->buffer = NULL;
|
||||||
|
port->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
port->buffer_offset = 0;
|
||||||
|
ip = NULL;
|
||||||
|
} else {
|
||||||
|
port->buffer_offset += chunk;
|
||||||
|
}
|
||||||
|
port->buffer_queued -= chunk;
|
||||||
|
|
||||||
|
if ((os -= chunk) == 0) {
|
||||||
|
if (++oi == out->n_datas)
|
||||||
|
break;
|
||||||
|
op = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
mix_data (SpaAudioMixer *this, SpaOutputInfo *info)
|
||||||
|
{
|
||||||
|
int i, min_size, min_port, pull_size;
|
||||||
|
|
||||||
|
if (info->port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (info->buffer) {
|
||||||
|
pull_size = info->buffer->size;
|
||||||
|
} else {
|
||||||
|
pull_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
min_size = 0;
|
||||||
|
min_port = 0;
|
||||||
|
for (i = 1; i < MAX_PORTS; i++) {
|
||||||
|
if (!this->ports[i].valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this->ports[i].buffer == NULL) {
|
||||||
|
if (pull_size && info->flags & SPA_OUTPUT_FLAG_PULL) {
|
||||||
|
pull_port (this, i, info, pull_size);
|
||||||
|
}
|
||||||
|
if (this->ports[i].buffer == NULL)
|
||||||
|
return SPA_RESULT_NEED_MORE_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_size == 0 || this->ports[i].buffer_queued < min_size) {
|
||||||
|
min_size = this->ports[i].buffer_queued;
|
||||||
|
min_port = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min_port == 0)
|
||||||
|
return SPA_RESULT_NEED_MORE_INPUT;
|
||||||
|
|
||||||
|
if (info->buffer) {
|
||||||
|
if (info->buffer->size < min_size)
|
||||||
|
min_size = info->buffer->size;
|
||||||
|
else
|
||||||
|
info->buffer->size = min_size;
|
||||||
|
} else {
|
||||||
|
info->buffer = this->ports[min_port].buffer;
|
||||||
|
this->ports[min_port].buffer = NULL;
|
||||||
|
this->ports[min_port].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < MAX_PORTS; i++) {
|
||||||
|
if (!this->ports[i].valid || this->ports[i].buffer == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
add_port_data (this, info->buffer, &this->ports[i]);
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_node_pull_port_output (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaOutputInfo *info)
|
||||||
|
{
|
||||||
|
SpaAudioMixer *this = (SpaAudioMixer *) handle;
|
||||||
|
int i;
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (info->port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
// if (!(this->ports[0].status.flags & SPA_PORT_STATUS_FLAG_HAVE_OUTPUT))
|
||||||
|
// return SPA_RESULT_NEED_MORE_INPUT;
|
||||||
|
|
||||||
|
for (i = 0; i < n_info; i++) {
|
||||||
|
if ((info[i].status = mix_data (this, &info[i])) < 0) {
|
||||||
|
printf ("error mixing: %d\n", info[i].status);
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (have_error)
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaNode audiomixer_node = {
|
||||||
|
sizeof (SpaNode),
|
||||||
|
spa_audiomixer_node_get_props,
|
||||||
|
spa_audiomixer_node_set_props,
|
||||||
|
spa_audiomixer_node_send_command,
|
||||||
|
spa_audiomixer_node_set_event_callback,
|
||||||
|
spa_audiomixer_node_get_n_ports,
|
||||||
|
spa_audiomixer_node_get_port_ids,
|
||||||
|
spa_audiomixer_node_add_port,
|
||||||
|
spa_audiomixer_node_remove_port,
|
||||||
|
spa_audiomixer_node_enum_port_formats,
|
||||||
|
spa_audiomixer_node_set_port_format,
|
||||||
|
spa_audiomixer_node_get_port_format,
|
||||||
|
spa_audiomixer_node_get_port_info,
|
||||||
|
spa_audiomixer_node_get_port_props,
|
||||||
|
spa_audiomixer_node_set_port_props,
|
||||||
|
spa_audiomixer_node_get_port_status,
|
||||||
|
spa_audiomixer_node_push_port_input,
|
||||||
|
spa_audiomixer_node_pull_port_output,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiomixer_get_interface (SpaHandle *handle,
|
||||||
|
uint32_t interface_id,
|
||||||
|
const void **interface)
|
||||||
|
{
|
||||||
|
if (handle == NULL || interface == 0)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (interface_id) {
|
||||||
|
case SPA_INTERFACE_ID_NODE:
|
||||||
|
*interface = &audiomixer_node;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_UNKNOWN_INTERFACE;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaHandle *
|
||||||
|
spa_audiomixer_new (void)
|
||||||
|
{
|
||||||
|
SpaHandle *handle;
|
||||||
|
SpaAudioMixer *this;
|
||||||
|
|
||||||
|
handle = calloc (1, sizeof (SpaAudioMixer));
|
||||||
|
handle->get_interface = spa_audiomixer_get_interface;
|
||||||
|
|
||||||
|
this = (SpaAudioMixer *) handle;
|
||||||
|
this->props.props.n_prop_info = PROP_ID_LAST;
|
||||||
|
this->props.props.prop_info = prop_info;
|
||||||
|
this->props.props.set_prop = spa_props_generic_set_prop;
|
||||||
|
this->props.props.get_prop = spa_props_generic_get_prop;
|
||||||
|
reset_audiomixer_props (&this->props);
|
||||||
|
|
||||||
|
this->ports[0].valid = true;
|
||||||
|
this->ports[0].info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_NO_REF;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
7
spa/plugins/audiomixer/meson.build
Normal file
7
spa/plugins/audiomixer/meson.build
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
audiomixer_sources = ['audiomixer.c', 'plugin.c']
|
||||||
|
|
||||||
|
audiomixerlib = shared_library('spa-audiomixer',
|
||||||
|
audiomixer_sources,
|
||||||
|
include_directories : inc,
|
||||||
|
link_with : spalib,
|
||||||
|
install : true)
|
||||||
78
spa/plugins/audiomixer/plugin.c
Normal file
78
spa/plugins/audiomixer/plugin.c
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Spa Audiomixer plugin
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <spa/plugin.h>
|
||||||
|
#include <spa/node.h>
|
||||||
|
|
||||||
|
SpaHandle * spa_audiomixer_new (void);
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
audiomixer_instantiate (const SpaHandleFactory *factory,
|
||||||
|
SpaHandle **handle)
|
||||||
|
{
|
||||||
|
if (factory == NULL || handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
*handle = spa_audiomixer_new ();
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaInterfaceInfo audiomixer_interfaces[] =
|
||||||
|
{
|
||||||
|
{ SPA_INTERFACE_ID_NODE,
|
||||||
|
SPA_INTERFACE_ID_NODE_NAME,
|
||||||
|
SPA_INTERFACE_ID_NODE_DESCRIPTION,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
audiomixer_enum_interface_info (const SpaHandleFactory *factory,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaInterfaceInfo **info)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*info = &audiomixer_interfaces[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaHandleFactory factories[] =
|
||||||
|
{
|
||||||
|
{ "audiomixer",
|
||||||
|
NULL,
|
||||||
|
audiomixer_instantiate,
|
||||||
|
audiomixer_enum_interface_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_enum_handle_factory (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*factory = &factories[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
526
spa/plugins/audiotestsrc/audiotestsrc.c
Normal file
526
spa/plugins/audiotestsrc/audiotestsrc.c
Normal file
|
|
@ -0,0 +1,526 @@
|
||||||
|
/* Spa
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
typedef struct _SpaAudioTestSrc SpaAudioTestSrc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaProps props;
|
||||||
|
uint32_t wave;
|
||||||
|
double freq;
|
||||||
|
double volume;
|
||||||
|
} SpaAudioTestSrcProps;
|
||||||
|
|
||||||
|
struct _SpaAudioTestSrc {
|
||||||
|
SpaHandle handle;
|
||||||
|
|
||||||
|
SpaAudioTestSrcProps props;
|
||||||
|
SpaAudioTestSrcProps tmp_props;
|
||||||
|
|
||||||
|
SpaEventCallback event_cb;
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
SpaPortInfo info;
|
||||||
|
SpaPortStatus status;
|
||||||
|
|
||||||
|
bool have_format;
|
||||||
|
SpaAudioRawFormat query_format;
|
||||||
|
SpaAudioRawFormat current_format;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t default_wave = 1.0;
|
||||||
|
static const double default_volume = 1.0;
|
||||||
|
static const double min_volume = 0.0;
|
||||||
|
static const double max_volume = 10.0;
|
||||||
|
static const double default_freq = 440.0;
|
||||||
|
static const double min_freq = 0.0;
|
||||||
|
static const double max_freq = 50000000.0;
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo volume_range[] = {
|
||||||
|
{ "min", "Minimum value", sizeof (double), &min_volume },
|
||||||
|
{ "max", "Maximum value", sizeof (double), &max_volume },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t wave_val_sine = 0;
|
||||||
|
static const uint32_t wave_val_square = 1;
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo wave_range[] = {
|
||||||
|
{ "sine", "Sine", sizeof (uint32_t), &wave_val_sine },
|
||||||
|
{ "square", "Square", sizeof (uint32_t), &wave_val_square },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo freq_range[] = {
|
||||||
|
{ "min", "Minimum value", sizeof (double), &min_freq },
|
||||||
|
{ "max", "Maximum value", sizeof (double), &max_freq },
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_ID_WAVE,
|
||||||
|
PROP_ID_FREQ,
|
||||||
|
PROP_ID_VOLUME,
|
||||||
|
PROP_ID_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropInfo prop_info[] =
|
||||||
|
{
|
||||||
|
{ PROP_ID_WAVE, "wave", "Oscillator waveform",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
|
||||||
|
sizeof (uint32_t), &default_wave,
|
||||||
|
SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (wave_range), wave_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioTestSrcProps, wave),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_FREQ, "freq", "Frequency of test signal. The sample rate needs to be at least 4 times higher",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_DOUBLE, sizeof (double),
|
||||||
|
sizeof (double), &default_freq,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, freq_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioTestSrcProps, freq),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_VOLUME, "volume", "The Volume factor",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_DOUBLE, sizeof (double),
|
||||||
|
sizeof (double), &default_volume,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, volume_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaAudioTestSrcProps, volume),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_audiotestsrc_props (SpaAudioTestSrcProps *props)
|
||||||
|
{
|
||||||
|
props->wave = default_wave;
|
||||||
|
props->freq = default_freq;
|
||||||
|
props->volume = default_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_props (SpaHandle *handle,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || props == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
|
||||||
|
*props = &this->tmp_props.props;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_set_props (SpaHandle *handle,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
SpaAudioTestSrcProps *p = &this->props;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (props == NULL) {
|
||||||
|
reset_audiotestsrc_props (p);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
res = spa_props_copy (props, &p->props);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_send_command (SpaHandle *handle,
|
||||||
|
SpaCommand *command)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || command == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (command->type) {
|
||||||
|
case SPA_COMMAND_INVALID:
|
||||||
|
return SPA_RESULT_INVALID_COMMAND;
|
||||||
|
|
||||||
|
case SPA_COMMAND_ACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_ACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_DEACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_DEACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_START:
|
||||||
|
case SPA_COMMAND_STOP:
|
||||||
|
case SPA_COMMAND_FLUSH:
|
||||||
|
case SPA_COMMAND_DRAIN:
|
||||||
|
case SPA_COMMAND_MARKER:
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_set_event_callback (SpaHandle *handle,
|
||||||
|
SpaEventCallback event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
this->event_cb = event;
|
||||||
|
this->user_data = user_data;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_n_ports (SpaHandle *handle,
|
||||||
|
unsigned int *n_input_ports,
|
||||||
|
unsigned int *max_input_ports,
|
||||||
|
unsigned int *n_output_ports,
|
||||||
|
unsigned int *max_output_ports)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports)
|
||||||
|
*n_input_ports = 0;
|
||||||
|
if (n_output_ports)
|
||||||
|
*n_output_ports = 1;
|
||||||
|
if (max_input_ports)
|
||||||
|
*max_input_ports = 0;
|
||||||
|
if (max_output_ports)
|
||||||
|
*max_output_ports = 1;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_port_ids (SpaHandle *handle,
|
||||||
|
unsigned int n_input_ports,
|
||||||
|
uint32_t *input_ids,
|
||||||
|
unsigned int n_output_ports,
|
||||||
|
uint32_t *output_ids)
|
||||||
|
{
|
||||||
|
if (handle == NULL || input_ids == NULL || output_ids == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_output_ports > 0)
|
||||||
|
output_ids[0] = 0;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_add_port (SpaHandle *handle,
|
||||||
|
SpaDirection direction,
|
||||||
|
uint32_t *port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_remove_port (SpaHandle *handle,
|
||||||
|
uint32_t port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_enum_port_formats (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
unsigned int index,
|
||||||
|
SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
spa_audio_raw_format_init (&this->query_format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
}
|
||||||
|
*format = &this->query_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_set_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
int test_only,
|
||||||
|
const SpaFormat *format)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (format == NULL) {
|
||||||
|
this->have_format = false;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
this->have_format = true;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*format = &this->current_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_port_info (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortInfo **info)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
*info = &this->info;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_set_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_get_port_status (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortStatus **status)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || status == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*status = &this->status;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_push_port_input (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaInputInfo *info)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_node_pull_port_output (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaOutputInfo *info)
|
||||||
|
{
|
||||||
|
SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
|
||||||
|
size_t j, size;
|
||||||
|
uint8_t *ptr;
|
||||||
|
unsigned int i;
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
for (i = 0; i < n_info; i++) {
|
||||||
|
if (info[i].port_id != 0) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_PORT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->have_format) {
|
||||||
|
info[i].status = SPA_RESULT_NO_FORMAT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info[i].buffer == NULL || info[i].buffer->n_datas == 0) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = info[i].buffer->datas[0].data;
|
||||||
|
size = info[i].buffer->datas[0].size;
|
||||||
|
|
||||||
|
for (j = 0; j < size; j++)
|
||||||
|
ptr[j] = rand();
|
||||||
|
|
||||||
|
info[i].status = SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
if (have_error)
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaNode audiotestsrc_node = {
|
||||||
|
sizeof (SpaNode),
|
||||||
|
spa_audiotestsrc_node_get_props,
|
||||||
|
spa_audiotestsrc_node_set_props,
|
||||||
|
spa_audiotestsrc_node_send_command,
|
||||||
|
spa_audiotestsrc_node_set_event_callback,
|
||||||
|
spa_audiotestsrc_node_get_n_ports,
|
||||||
|
spa_audiotestsrc_node_get_port_ids,
|
||||||
|
spa_audiotestsrc_node_add_port,
|
||||||
|
spa_audiotestsrc_node_remove_port,
|
||||||
|
spa_audiotestsrc_node_enum_port_formats,
|
||||||
|
spa_audiotestsrc_node_set_port_format,
|
||||||
|
spa_audiotestsrc_node_get_port_format,
|
||||||
|
spa_audiotestsrc_node_get_port_info,
|
||||||
|
spa_audiotestsrc_node_get_port_props,
|
||||||
|
spa_audiotestsrc_node_set_port_props,
|
||||||
|
spa_audiotestsrc_node_get_port_status,
|
||||||
|
spa_audiotestsrc_node_push_port_input,
|
||||||
|
spa_audiotestsrc_node_pull_port_output,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_audiotestsrc_get_interface (SpaHandle *handle,
|
||||||
|
uint32_t interface_id,
|
||||||
|
const void **interface)
|
||||||
|
{
|
||||||
|
if (handle == NULL || interface == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (interface_id) {
|
||||||
|
case SPA_INTERFACE_ID_NODE:
|
||||||
|
*interface = &audiotestsrc_node;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_UNKNOWN_INTERFACE;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaHandle *
|
||||||
|
spa_audiotestsrc_new (void)
|
||||||
|
{
|
||||||
|
SpaHandle *handle;
|
||||||
|
SpaAudioTestSrc *this;
|
||||||
|
|
||||||
|
handle = calloc (1, sizeof (SpaAudioTestSrc));
|
||||||
|
handle->get_interface = spa_audiotestsrc_get_interface;
|
||||||
|
|
||||||
|
this = (SpaAudioTestSrc *) handle;
|
||||||
|
this->props.props.n_prop_info = PROP_ID_LAST;
|
||||||
|
this->props.props.prop_info = prop_info;
|
||||||
|
this->props.props.set_prop = spa_props_generic_set_prop;
|
||||||
|
this->props.props.get_prop = spa_props_generic_get_prop;
|
||||||
|
reset_audiotestsrc_props (&this->props);
|
||||||
|
|
||||||
|
this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_NO_REF;
|
||||||
|
this->status.flags = SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
7
spa/plugins/audiotestsrc/meson.build
Normal file
7
spa/plugins/audiotestsrc/meson.build
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
audiotestsrc_sources = ['audiotestsrc.c', 'plugin.c']
|
||||||
|
|
||||||
|
audiotestsrclib = shared_library('spa-audiotestsrc',
|
||||||
|
audiotestsrc_sources,
|
||||||
|
include_directories : inc,
|
||||||
|
link_with : spalib,
|
||||||
|
install : true)
|
||||||
78
spa/plugins/audiotestsrc/plugin.c
Normal file
78
spa/plugins/audiotestsrc/plugin.c
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Spa Volume plugin
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <spa/plugin.h>
|
||||||
|
#include <spa/node.h>
|
||||||
|
|
||||||
|
SpaHandle * spa_audiotestsrc_new (void);
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
audiotestsrc_instantiate (const SpaHandleFactory *factory,
|
||||||
|
SpaHandle **handle)
|
||||||
|
{
|
||||||
|
if (factory == NULL || handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
*handle = spa_audiotestsrc_new ();
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaInterfaceInfo audiotestsrc_interfaces[] =
|
||||||
|
{
|
||||||
|
{ SPA_INTERFACE_ID_NODE,
|
||||||
|
SPA_INTERFACE_ID_NODE_NAME,
|
||||||
|
SPA_INTERFACE_ID_NODE_DESCRIPTION,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
audiotestsrc_enum_interface_info (const SpaHandleFactory *factory,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaInterfaceInfo **info)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*info = &audiotestsrc_interfaces[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaHandleFactory factories[] =
|
||||||
|
{
|
||||||
|
{ "audiotestsrc",
|
||||||
|
NULL,
|
||||||
|
audiotestsrc_instantiate,
|
||||||
|
audiotestsrc_enum_interface_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_enum_handle_factory (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*factory = &factories[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
4
spa/plugins/meson.build
Normal file
4
spa/plugins/meson.build
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
subdir('alsa')
|
||||||
|
subdir('audiomixer')
|
||||||
|
subdir('audiotestsrc')
|
||||||
|
subdir('volume')
|
||||||
7
spa/plugins/volume/meson.build
Normal file
7
spa/plugins/volume/meson.build
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
volume_sources = ['volume.c', 'plugin.c']
|
||||||
|
|
||||||
|
volumelib = shared_library('spa-volume',
|
||||||
|
volume_sources,
|
||||||
|
include_directories : inc,
|
||||||
|
link_with : spalib,
|
||||||
|
install : true)
|
||||||
78
spa/plugins/volume/plugin.c
Normal file
78
spa/plugins/volume/plugin.c
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Spa Volume plugin
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <spa/plugin.h>
|
||||||
|
#include <spa/node.h>
|
||||||
|
|
||||||
|
SpaHandle * spa_volume_new (void);
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
volume_instantiate (const SpaHandleFactory *factory,
|
||||||
|
SpaHandle **handle)
|
||||||
|
{
|
||||||
|
if (factory == NULL || handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
*handle = spa_volume_new ();
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaInterfaceInfo volume_interfaces[] =
|
||||||
|
{
|
||||||
|
{ SPA_INTERFACE_ID_NODE,
|
||||||
|
SPA_INTERFACE_ID_NODE_NAME,
|
||||||
|
SPA_INTERFACE_ID_NODE_DESCRIPTION,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
volume_enum_interface_info (const SpaHandleFactory *factory,
|
||||||
|
unsigned int index,
|
||||||
|
const SpaInterfaceInfo **info)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*info = &volume_interfaces[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaHandleFactory factories[] =
|
||||||
|
{
|
||||||
|
{ "volume",
|
||||||
|
NULL,
|
||||||
|
volume_instantiate,
|
||||||
|
volume_enum_interface_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SpaResult
|
||||||
|
spa_enum_handle_factory (unsigned int index,
|
||||||
|
const SpaHandleFactory **factory)
|
||||||
|
{
|
||||||
|
if (index >= 1)
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
|
||||||
|
*factory = &factories[index];
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
605
spa/plugins/volume/volume.c
Normal file
605
spa/plugins/volume/volume.c
Normal file
|
|
@ -0,0 +1,605 @@
|
||||||
|
/* Spa
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
typedef struct _SpaVolume SpaVolume;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaProps props;
|
||||||
|
double volume;
|
||||||
|
bool mute;
|
||||||
|
} SpaVolumeProps;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool have_format;
|
||||||
|
SpaPortInfo info;
|
||||||
|
SpaPortStatus status;
|
||||||
|
} SpaVolumePort;
|
||||||
|
|
||||||
|
struct _SpaVolume {
|
||||||
|
SpaHandle handle;
|
||||||
|
|
||||||
|
SpaVolumeProps props;
|
||||||
|
SpaVolumeProps tmp_props;
|
||||||
|
|
||||||
|
SpaEventCallback event_cb;
|
||||||
|
void *user_data;
|
||||||
|
|
||||||
|
bool have_format;
|
||||||
|
SpaAudioRawFormat query_format;
|
||||||
|
SpaAudioRawFormat current_format;
|
||||||
|
|
||||||
|
SpaVolumePort ports[2];
|
||||||
|
SpaBuffer *input_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const double default_volume = 1.0;
|
||||||
|
static const double min_volume = 0.0;
|
||||||
|
static const double max_volume = 10.0;
|
||||||
|
static const bool default_mute = false;
|
||||||
|
|
||||||
|
static const SpaPropRangeInfo volume_range[] = {
|
||||||
|
{ "min", "Minimum value", sizeof (double), &min_volume },
|
||||||
|
{ "max", "Maximum value", sizeof (double), &max_volume },
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_ID_VOLUME,
|
||||||
|
PROP_ID_MUTE,
|
||||||
|
PROP_ID_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const SpaPropInfo prop_info[] =
|
||||||
|
{
|
||||||
|
{ PROP_ID_VOLUME, "volume", "The Volume factor",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_DOUBLE, sizeof (double),
|
||||||
|
sizeof (double), &default_volume,
|
||||||
|
SPA_PROP_RANGE_TYPE_MIN_MAX, 2, volume_range,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaVolumeProps, volume),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
{ PROP_ID_MUTE, "mute", "Mute",
|
||||||
|
SPA_PROP_FLAG_READWRITE,
|
||||||
|
SPA_PROP_TYPE_BOOL, sizeof (bool),
|
||||||
|
sizeof (bool), &default_mute,
|
||||||
|
SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
|
||||||
|
NULL,
|
||||||
|
offsetof (SpaVolumeProps, mute),
|
||||||
|
0, 0,
|
||||||
|
NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_volume_props (SpaVolumeProps *props)
|
||||||
|
{
|
||||||
|
props->volume = default_volume;
|
||||||
|
props->mute = default_mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_props (SpaHandle *handle,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || props == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
|
||||||
|
*props = &this->tmp_props.props;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_set_props (SpaHandle *handle,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
SpaVolumeProps *p = &this->props;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (props == NULL) {
|
||||||
|
reset_volume_props (p);
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
res = spa_props_copy (props, &p->props);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_send_command (SpaHandle *handle,
|
||||||
|
SpaCommand *command)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || command == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (command->type) {
|
||||||
|
case SPA_COMMAND_INVALID:
|
||||||
|
return SPA_RESULT_INVALID_COMMAND;
|
||||||
|
|
||||||
|
case SPA_COMMAND_ACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_ACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_DEACTIVATE:
|
||||||
|
if (this->event_cb) {
|
||||||
|
SpaEvent event;
|
||||||
|
|
||||||
|
event.refcount = 1;
|
||||||
|
event.notify = NULL;
|
||||||
|
event.type = SPA_EVENT_TYPE_DEACTIVATED;
|
||||||
|
event.port_id = -1;
|
||||||
|
event.data = NULL;
|
||||||
|
event.size = 0;
|
||||||
|
|
||||||
|
this->event_cb (handle, &event, this->user_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SPA_COMMAND_START:
|
||||||
|
case SPA_COMMAND_STOP:
|
||||||
|
case SPA_COMMAND_FLUSH:
|
||||||
|
case SPA_COMMAND_DRAIN:
|
||||||
|
case SPA_COMMAND_MARKER:
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_set_event_callback (SpaHandle *handle,
|
||||||
|
SpaEventCallback event,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
this->event_cb = event;
|
||||||
|
this->user_data = user_data;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_n_ports (SpaHandle *handle,
|
||||||
|
unsigned int *n_input_ports,
|
||||||
|
unsigned int *max_input_ports,
|
||||||
|
unsigned int *n_output_ports,
|
||||||
|
unsigned int *max_output_ports)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports)
|
||||||
|
*n_input_ports = 1;
|
||||||
|
if (n_output_ports)
|
||||||
|
*n_output_ports = 1;
|
||||||
|
if (max_input_ports)
|
||||||
|
*max_input_ports = 1;
|
||||||
|
if (max_output_ports)
|
||||||
|
*max_output_ports = 1;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_port_ids (SpaHandle *handle,
|
||||||
|
unsigned int n_input_ports,
|
||||||
|
uint32_t *input_ids,
|
||||||
|
unsigned int n_output_ports,
|
||||||
|
uint32_t *output_ids)
|
||||||
|
{
|
||||||
|
if (handle == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (n_input_ports > 0 && input_ids)
|
||||||
|
input_ids[0] = 0;
|
||||||
|
if (n_output_ports > 0 && output_ids)
|
||||||
|
output_ids[0] = 1;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_add_port (SpaHandle *handle,
|
||||||
|
SpaDirection direction,
|
||||||
|
uint32_t *port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_remove_port (SpaHandle *handle,
|
||||||
|
uint32_t port_id)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_enum_port_formats (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
unsigned int index,
|
||||||
|
SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id != 0)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
spa_audio_raw_format_init (&this->query_format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_ENUM_END;
|
||||||
|
}
|
||||||
|
*format = &this->query_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_set_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
int test_only,
|
||||||
|
const SpaFormat *format)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= 2)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (format == NULL) {
|
||||||
|
this->ports[port_id].have_format = false;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
this->ports[port_id].have_format = true;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_port_format (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaFormat **format)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || format == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= 2)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->ports[port_id].have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*format = &this->current_format.format;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_port_info (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortInfo **info)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= 2)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
*info = &this->ports[port_id].info;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
SpaProps **props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_set_port_props (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaProps *props)
|
||||||
|
{
|
||||||
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_get_port_status (SpaHandle *handle,
|
||||||
|
uint32_t port_id,
|
||||||
|
const SpaPortStatus **status)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
|
||||||
|
if (handle == NULL || status == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (port_id >= 2)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->ports[port_id].have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
*status = &this->ports[port_id].status;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_push_port_input (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaInputInfo *info)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
SpaBuffer *buffer;
|
||||||
|
SpaEvent *event;
|
||||||
|
unsigned int i;
|
||||||
|
bool have_error = false;
|
||||||
|
bool have_enough = false;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
for (i = 0; i < n_info; i++) {
|
||||||
|
if (info[i].port_id != 0) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_PORT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = info[i].event;
|
||||||
|
buffer = info[i].buffer;
|
||||||
|
|
||||||
|
if (buffer == NULL && event == NULL) {
|
||||||
|
info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer) {
|
||||||
|
if (!this->ports[0].have_format) {
|
||||||
|
info[i].status = SPA_RESULT_NO_FORMAT;
|
||||||
|
have_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->input_buffer != NULL) {
|
||||||
|
info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
have_enough = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this->input_buffer = spa_buffer_ref (buffer);
|
||||||
|
|
||||||
|
this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
this->ports[1].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
}
|
||||||
|
if (event) {
|
||||||
|
switch (event->type) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info[i].status = SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
if (have_error)
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
if (have_enough)
|
||||||
|
return SPA_RESULT_HAVE_ENOUGH_INPUT;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_node_pull_port_output (SpaHandle *handle,
|
||||||
|
unsigned int n_info,
|
||||||
|
SpaOutputInfo *info)
|
||||||
|
{
|
||||||
|
SpaVolume *this = (SpaVolume *) handle;
|
||||||
|
unsigned int si, di, i, n_samples, n_bytes, soff, doff ;
|
||||||
|
SpaBuffer *sbuf, *dbuf;
|
||||||
|
SpaData *sd, *dd;
|
||||||
|
uint16_t *src, *dst;
|
||||||
|
double volume;
|
||||||
|
|
||||||
|
if (handle == NULL || n_info == 0 || info == NULL)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
if (info->port_id != 1)
|
||||||
|
return SPA_RESULT_INVALID_PORT;
|
||||||
|
|
||||||
|
if (!this->ports[1].have_format)
|
||||||
|
return SPA_RESULT_NO_FORMAT;
|
||||||
|
|
||||||
|
if (this->input_buffer == NULL)
|
||||||
|
return SPA_RESULT_NEED_MORE_INPUT;
|
||||||
|
|
||||||
|
volume = this->props.volume;
|
||||||
|
|
||||||
|
sbuf = this->input_buffer;
|
||||||
|
dbuf = info->buffer ? info->buffer : this->input_buffer;
|
||||||
|
|
||||||
|
si = di = 0;
|
||||||
|
soff = doff = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (si == sbuf->n_datas || di == dbuf->n_datas)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sd = &sbuf->datas[si];
|
||||||
|
dd = &dbuf->datas[di];
|
||||||
|
|
||||||
|
if (sd->type != SPA_DATA_TYPE_MEMPTR) {
|
||||||
|
si++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dd->type != SPA_DATA_TYPE_MEMPTR) {
|
||||||
|
di++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
src = (uint16_t*) ((uint8_t*)sd->data + soff);
|
||||||
|
dst = (uint16_t*) ((uint8_t*)dd->data + doff);
|
||||||
|
|
||||||
|
n_bytes = MIN (sd->size - soff, dd->size - doff);
|
||||||
|
n_samples = n_bytes / sizeof (uint16_t);
|
||||||
|
|
||||||
|
for (i = 0; i < n_samples; i++)
|
||||||
|
*src++ = *dst++ * volume;
|
||||||
|
|
||||||
|
soff += n_bytes;
|
||||||
|
doff += n_bytes;
|
||||||
|
|
||||||
|
if (soff >= sd->size) {
|
||||||
|
si++;
|
||||||
|
soff = 0;
|
||||||
|
}
|
||||||
|
if (doff >= dd->size) {
|
||||||
|
di++;
|
||||||
|
doff = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sbuf != dbuf)
|
||||||
|
spa_buffer_unref (sbuf);
|
||||||
|
|
||||||
|
this->input_buffer = NULL;
|
||||||
|
info->buffer = dbuf;
|
||||||
|
|
||||||
|
this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
this->ports[1].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SpaNode volume_node = {
|
||||||
|
sizeof (SpaNode),
|
||||||
|
spa_volume_node_get_props,
|
||||||
|
spa_volume_node_set_props,
|
||||||
|
spa_volume_node_send_command,
|
||||||
|
spa_volume_node_set_event_callback,
|
||||||
|
spa_volume_node_get_n_ports,
|
||||||
|
spa_volume_node_get_port_ids,
|
||||||
|
spa_volume_node_add_port,
|
||||||
|
spa_volume_node_remove_port,
|
||||||
|
spa_volume_node_enum_port_formats,
|
||||||
|
spa_volume_node_set_port_format,
|
||||||
|
spa_volume_node_get_port_format,
|
||||||
|
spa_volume_node_get_port_info,
|
||||||
|
spa_volume_node_get_port_props,
|
||||||
|
spa_volume_node_set_port_props,
|
||||||
|
spa_volume_node_get_port_status,
|
||||||
|
spa_volume_node_push_port_input,
|
||||||
|
spa_volume_node_pull_port_output,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
spa_volume_get_interface (SpaHandle *handle,
|
||||||
|
uint32_t interface_id,
|
||||||
|
const void **interface)
|
||||||
|
{
|
||||||
|
if (handle == NULL || interface == 0)
|
||||||
|
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||||
|
|
||||||
|
switch (interface_id) {
|
||||||
|
case SPA_INTERFACE_ID_NODE:
|
||||||
|
*interface = &volume_node;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return SPA_RESULT_UNKNOWN_INTERFACE;
|
||||||
|
|
||||||
|
}
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaHandle *
|
||||||
|
spa_volume_new (void)
|
||||||
|
{
|
||||||
|
SpaHandle *handle;
|
||||||
|
SpaVolume *this;
|
||||||
|
|
||||||
|
handle = calloc (1, sizeof (SpaVolume));
|
||||||
|
handle->get_interface = spa_volume_get_interface;
|
||||||
|
|
||||||
|
this = (SpaVolume *) handle;
|
||||||
|
this->props.props.n_prop_info = PROP_ID_LAST;
|
||||||
|
this->props.props.prop_info = prop_info;
|
||||||
|
this->props.props.set_prop = spa_props_generic_set_prop;
|
||||||
|
this->props.props.get_prop = spa_props_generic_get_prop;
|
||||||
|
reset_volume_props (&this->props);
|
||||||
|
|
||||||
|
this->ports[0].info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_IN_PLACE;
|
||||||
|
this->ports[1].info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
|
||||||
|
SPA_PORT_INFO_FLAG_NO_REF;
|
||||||
|
|
||||||
|
this->ports[0].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
|
||||||
|
this->ports[1].status.flags = SPA_PORT_STATUS_FLAG_NONE;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
4
spa/tests/meson.build
Normal file
4
spa/tests/meson.build
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
executable('test-mixer', 'test-mixer.c',
|
||||||
|
include_directories : inc,
|
||||||
|
dependencies : [dl_lib],
|
||||||
|
install : false)
|
||||||
353
spa/tests/test-mixer.c
Normal file
353
spa/tests/test-mixer.c
Normal file
|
|
@ -0,0 +1,353 @@
|
||||||
|
/* Spa
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
#include <spa/audio/raw.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SpaHandle *sink;
|
||||||
|
const SpaNode *sink_node;
|
||||||
|
SpaHandle *mix;
|
||||||
|
const SpaNode *mix_node;
|
||||||
|
uint32_t mix_ports[2];
|
||||||
|
SpaHandle *source1;
|
||||||
|
const SpaNode *source1_node;
|
||||||
|
SpaHandle *source2;
|
||||||
|
const SpaNode *source2_node;
|
||||||
|
} AppData;
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char *name)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
void *hnd;
|
||||||
|
SpaEnumHandleFactoryFunc enum_func;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) {
|
||||||
|
printf ("can't load %s: %s\n", lib, dlerror());
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) {
|
||||||
|
printf ("can't find enum function\n");
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; ;i++) {
|
||||||
|
const SpaHandleFactory *factory;
|
||||||
|
const void *iface;
|
||||||
|
|
||||||
|
if ((res = enum_func (i, &factory)) < 0) {
|
||||||
|
if (res != SPA_RESULT_ENUM_END)
|
||||||
|
printf ("can't enumerate factories: %d\n", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strcmp (factory->name, name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((res = factory->instantiate (factory, handle)) < 0) {
|
||||||
|
printf ("can't make factory instance: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if ((res = (*handle)->get_interface (*handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) {
|
||||||
|
printf ("can't get interface %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
*node = iface;
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
return SPA_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_mix_event (SpaHandle *handle, SpaEvent *event, void *user_data)
|
||||||
|
{
|
||||||
|
AppData *data = user_data;
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case SPA_EVENT_TYPE_PULL_INPUT:
|
||||||
|
{
|
||||||
|
SpaBuffer *buf;
|
||||||
|
SpaInputInfo iinfo;
|
||||||
|
SpaOutputInfo oinfo;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
buf = event->data;
|
||||||
|
|
||||||
|
oinfo.port_id = 0;
|
||||||
|
oinfo.flags = SPA_OUTPUT_FLAG_NONE;
|
||||||
|
oinfo.buffer = buf;
|
||||||
|
oinfo.event = NULL;
|
||||||
|
|
||||||
|
printf ("pull source %p\n", buf);
|
||||||
|
if (event->port_id == data->mix_ports[0]) {
|
||||||
|
if ((res = data->source1_node->pull_port_output (data->source1, 1, &oinfo)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
} else {
|
||||||
|
if ((res = data->source2_node->pull_port_output (data->source2, 1, &oinfo)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
iinfo.port_id = event->port_id;
|
||||||
|
iinfo.flags = SPA_INPUT_FLAG_NONE;
|
||||||
|
iinfo.buffer = oinfo.buffer;
|
||||||
|
iinfo.event = oinfo.event;
|
||||||
|
|
||||||
|
printf ("push mixer %p\n", iinfo.buffer);
|
||||||
|
if ((res = data->mix_node->push_port_input (data->mix, 1, &iinfo)) < 0)
|
||||||
|
printf ("got error from mixer %d\n", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
printf ("got event %d\n", event->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_sink_event (SpaHandle *handle, SpaEvent *event, void *user_data)
|
||||||
|
{
|
||||||
|
AppData *data = user_data;
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case SPA_EVENT_TYPE_PULL_INPUT:
|
||||||
|
{
|
||||||
|
SpaBuffer *buf;
|
||||||
|
SpaInputInfo iinfo;
|
||||||
|
SpaOutputInfo oinfo;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
buf = event->data;
|
||||||
|
|
||||||
|
oinfo.port_id = 0;
|
||||||
|
oinfo.flags = SPA_OUTPUT_FLAG_PULL;
|
||||||
|
oinfo.buffer = buf;
|
||||||
|
oinfo.event = NULL;
|
||||||
|
|
||||||
|
printf ("pull mixer %p\n", buf);
|
||||||
|
if ((res = data->mix_node->pull_port_output (data->mix, 1, &oinfo)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
|
||||||
|
iinfo.port_id = event->port_id;
|
||||||
|
iinfo.flags = SPA_INPUT_FLAG_NONE;
|
||||||
|
iinfo.buffer = oinfo.buffer;
|
||||||
|
iinfo.event = oinfo.event;
|
||||||
|
|
||||||
|
printf ("push sink %p\n", iinfo.buffer);
|
||||||
|
if ((res = data->sink_node->push_port_input (data->sink, 1, &iinfo)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
printf ("got event %d\n", event->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
make_nodes (AppData *data)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaProps *props;
|
||||||
|
SpaPropValue value;
|
||||||
|
|
||||||
|
if ((res = make_node (&data->sink, &data->sink_node, "plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) {
|
||||||
|
printf ("can't create alsa-sink: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
data->sink_node->set_event_callback (data->sink, on_sink_event, data);
|
||||||
|
|
||||||
|
if ((res = data->sink_node->get_props (data->sink, &props)) < 0)
|
||||||
|
printf ("got get_props error %d\n", res);
|
||||||
|
|
||||||
|
value.type = SPA_PROP_TYPE_STRING;
|
||||||
|
value.size = strlen ("hw:1")+1;
|
||||||
|
value.value = "hw:1";
|
||||||
|
props->set_prop (props, spa_props_index_for_name (props, "device"), &value);
|
||||||
|
|
||||||
|
if ((res = data->sink_node->set_props (data->sink, props)) < 0)
|
||||||
|
printf ("got set_props error %d\n", res);
|
||||||
|
|
||||||
|
|
||||||
|
if ((res = make_node (&data->mix, &data->mix_node, "plugins/audiomixer/libspa-audiomixer.so", "audiomixer")) < 0) {
|
||||||
|
printf ("can't create audiomixer: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
data->mix_node->set_event_callback (data->mix, on_mix_event, data);
|
||||||
|
|
||||||
|
if ((res = make_node (&data->source1, &data->source1_node, "plugins/audiotestsrc/libspa-audiotestsrc.so", "audiotestsrc")) < 0) {
|
||||||
|
printf ("can't create audiotestsrc: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if ((res = make_node (&data->source2, &data->source2_node, "plugins/audiotestsrc/libspa-audiotestsrc.so", "audiotestsrc")) < 0) {
|
||||||
|
printf ("can't create audiotestsrc: %d\n", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
negotiate_formats (AppData *data)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaFormat *format;
|
||||||
|
SpaProps *props;
|
||||||
|
uint32_t val;
|
||||||
|
SpaPropValue value;
|
||||||
|
|
||||||
|
if ((res = data->sink_node->enum_port_formats (data->sink, 0, 0, &format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
props = &format->props;
|
||||||
|
|
||||||
|
value.type = SPA_PROP_TYPE_UINT32;
|
||||||
|
value.size = sizeof (uint32_t);
|
||||||
|
value.value = &val;
|
||||||
|
|
||||||
|
val = SPA_AUDIO_FORMAT_S16LE;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_FORMAT), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = 1;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_LAYOUT), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = 44100;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_RATE), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
val = 2;
|
||||||
|
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->sink_node->set_port_format (data->sink, 0, 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->mix_node->set_port_format (data->mix, 0, 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->mix_node->add_port (data->mix, SPA_DIRECTION_INPUT, &data->mix_ports[0])) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[0], 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->source1_node->set_port_format (data->source1, 0, 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->mix_node->add_port (data->mix, SPA_DIRECTION_INPUT, &data->mix_ports[1])) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->mix_node->set_port_format (data->mix, data->mix_ports[1], 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if ((res = data->source2_node->set_port_format (data->source2, 0, 0, format)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
|
||||||
|
return SPA_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
start_nodes (AppData *data)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_ACTIVATE;
|
||||||
|
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->mix_node->send_command (data->mix, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->source1_node->send_command (data->source1, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->source2_node->send_command (data->source1, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SpaResult
|
||||||
|
stop_nodes (AppData *data)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_DEACTIVATE;
|
||||||
|
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->mix_node->send_command (data->mix, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->source1_node->send_command (data->source1, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
if ((res = data->source2_node->send_command (data->source1, &cmd)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_async_sink (AppData *data)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaCommand cmd;
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_START;
|
||||||
|
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
|
||||||
|
printf ("sleeping for 10 seconds\n");
|
||||||
|
sleep (10);
|
||||||
|
|
||||||
|
cmd.type = SPA_COMMAND_STOP;
|
||||||
|
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
AppData data;
|
||||||
|
SpaResult res;
|
||||||
|
|
||||||
|
if ((res = make_nodes (&data)) < 0) {
|
||||||
|
printf ("can't make nodes: %d\n", res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((res = negotiate_formats (&data)) < 0) {
|
||||||
|
printf ("can't negotiate nodes: %d\n", res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((res = start_nodes (&data)) < 0) {
|
||||||
|
printf ("can't start nodes: %d\n", res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_async_sink (&data);
|
||||||
|
|
||||||
|
if ((res = stop_nodes (&data)) < 0) {
|
||||||
|
printf ("can't stop nodes: %d\n", res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
4
spa/tools/meson.build
Normal file
4
spa/tools/meson.build
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
executable('spa-inspect', 'spa-inspect.c',
|
||||||
|
include_directories : inc,
|
||||||
|
dependencies : [dl_lib],
|
||||||
|
install : true)
|
||||||
255
spa/tools/spa-inspect.c
Normal file
255
spa/tools/spa-inspect.c
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
/* Pinos
|
||||||
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include <spa/node.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_value (const char *prefix, SpaPropType type, int size, const void *value)
|
||||||
|
{
|
||||||
|
printf ("%s", prefix);
|
||||||
|
switch (type) {
|
||||||
|
case SPA_PROP_TYPE_INVALID:
|
||||||
|
printf ("invalid");
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_BOOL:
|
||||||
|
printf ("%s", *(bool *)value ? "true" : "false");
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_INT8:
|
||||||
|
printf ("%" PRIi8, *(int8_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_UINT8:
|
||||||
|
printf ("%" PRIu8, *(uint8_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_INT16:
|
||||||
|
printf ("%" PRIi16, *(int16_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_UINT16:
|
||||||
|
printf ("%" PRIu16, *(uint16_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_INT32:
|
||||||
|
printf ("%" PRIi32, *(int32_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_UINT32:
|
||||||
|
printf ("%" PRIu32, *(uint32_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_INT64:
|
||||||
|
printf ("%" PRIi64 "\n", *(int64_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_UINT64:
|
||||||
|
printf ("%" PRIu64 "\n", *(uint64_t *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_FLOAT:
|
||||||
|
printf ("%f", *(float *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_DOUBLE:
|
||||||
|
printf ("%g", *(double *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_STRING:
|
||||||
|
printf ("%s", (char *)value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_POINTER:
|
||||||
|
printf ("%p", value);
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_FRACTION:
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_BITMASK:
|
||||||
|
break;
|
||||||
|
case SPA_PROP_TYPE_BYTES:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_props (const SpaProps *props, int print_ranges)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
const SpaPropInfo *info;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < props->n_prop_info; i++) {
|
||||||
|
SpaPropValue value;
|
||||||
|
|
||||||
|
info = &props->prop_info[i];
|
||||||
|
|
||||||
|
printf ("id:\t\t%d\n", info->id);
|
||||||
|
printf ("name:\t\t%s\n", info->name);
|
||||||
|
printf ("description:\t%s\n", info->description);
|
||||||
|
printf ("flags:\t\t%d\n", info->flags);
|
||||||
|
printf ("type:\t\t%d\n", info->type);
|
||||||
|
printf ("maxsize:\t%zu\n", info->maxsize);
|
||||||
|
|
||||||
|
res = props->get_prop (props, info->id, &value);
|
||||||
|
if (res == SPA_RESULT_PROPERTY_UNSET)
|
||||||
|
printf ("value:\t\tunset\n");
|
||||||
|
else
|
||||||
|
print_value ("value:\t\t", value.type, value.size, value.value);
|
||||||
|
|
||||||
|
if (print_ranges) {
|
||||||
|
if (info->default_value)
|
||||||
|
print_value ("default:\t", info->type, info->default_size, info->default_value);
|
||||||
|
else
|
||||||
|
printf ("default:\tunset\n");
|
||||||
|
|
||||||
|
printf ("range_type:\t%d\n", info->range_type);
|
||||||
|
if (info->range_values) {
|
||||||
|
for (j = 0; j < info->n_range_values; j++) {
|
||||||
|
const SpaPropRangeInfo *rinfo = &info->range_values[j];
|
||||||
|
printf (" name:\t%s\n", rinfo->name);
|
||||||
|
printf (" description:\t%s\n", rinfo->description);
|
||||||
|
print_value (" value:\t", info->type, rinfo->size, rinfo->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (info->tags) {
|
||||||
|
for (j = 0; info->tags[j]; j++) {
|
||||||
|
printf ("tag:\t%s\n", info->tags[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_format (const SpaFormat *format, int print_ranges)
|
||||||
|
{
|
||||||
|
printf ("media-type:\t\t%d\n", format->media_type);
|
||||||
|
printf ("media-subtype:\t\t%d\n", format->media_subtype);
|
||||||
|
print_props (&format->props, print_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inspect_node (const SpaNode *node, SpaHandle *handle)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
SpaProps *props;
|
||||||
|
unsigned int n_input, max_input, n_output, max_output, i;
|
||||||
|
SpaFormat *format;
|
||||||
|
|
||||||
|
if ((res = node->get_props (handle, &props)) < 0)
|
||||||
|
printf ("can't get properties: %d\n", res);
|
||||||
|
else
|
||||||
|
print_props (props, 1);
|
||||||
|
|
||||||
|
if ((res = node->get_n_ports (handle, &n_input, &max_input, &n_output, &max_output)) < 0)
|
||||||
|
printf ("can't get n_ports: %d\n", res);
|
||||||
|
else
|
||||||
|
printf ("supported ports %d %d %d %d\n", n_input, max_input, n_output, max_output);
|
||||||
|
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
if ((res = node->enum_port_formats (handle, 0, i, &format)) < 0) {
|
||||||
|
if (res != SPA_RESULT_ENUM_END)
|
||||||
|
printf ("got error %d\n", res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_format (format, 1);
|
||||||
|
}
|
||||||
|
if ((res = node->get_port_props (handle, 0, &props)) < 0)
|
||||||
|
printf ("get_port_props error: %d\n", res);
|
||||||
|
else
|
||||||
|
print_props (props, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
inspect_factory (const SpaHandleFactory *factory)
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
unsigned int i;
|
||||||
|
SpaHandle *handle;
|
||||||
|
const void *interface;
|
||||||
|
|
||||||
|
printf ("factory name:\t\t'%s'\n", factory->name);
|
||||||
|
printf ("factory info:\n");
|
||||||
|
if (factory->info)
|
||||||
|
print_props (factory->info, 1);
|
||||||
|
else
|
||||||
|
printf (" none\n");
|
||||||
|
|
||||||
|
if ((res = factory->instantiate (factory, &handle)) < 0) {
|
||||||
|
printf ("can't make factory instance: %d\n", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf ("factory interfaces:\n");
|
||||||
|
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
const SpaInterfaceInfo *info;
|
||||||
|
|
||||||
|
if ((res = factory->enum_interface_info (factory, i, &info)) < 0) {
|
||||||
|
if (res == SPA_RESULT_ENUM_END)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
printf ("can't enumerate interfaces: %d\n", res);
|
||||||
|
}
|
||||||
|
printf (" interface: %d, (%d) '%s' : '%s'\n", i, info->interface_id, info->name, info->description);
|
||||||
|
|
||||||
|
if ((res = handle->get_interface (handle, info->interface_id, &interface)) < 0) {
|
||||||
|
printf ("can't get interface: %d\n", res);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (info->interface_id) {
|
||||||
|
case SPA_INTERFACE_ID_NODE:
|
||||||
|
inspect_node (interface, handle);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf ("skipping unknown interface\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
SpaResult res;
|
||||||
|
void *handle;
|
||||||
|
SpaEnumHandleFactoryFunc enum_func;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if ((handle = dlopen (argv[1], RTLD_NOW)) == NULL) {
|
||||||
|
printf ("can't load %s\n", argv[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((enum_func = dlsym (handle, "spa_enum_handle_factory")) == NULL) {
|
||||||
|
printf ("can't find function\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; ;i++) {
|
||||||
|
const SpaHandleFactory *factory;
|
||||||
|
|
||||||
|
if ((res = enum_func (i, &factory)) < 0) {
|
||||||
|
if (res == SPA_RESULT_ENUM_END)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
printf ("can't enumerate factories\n");
|
||||||
|
}
|
||||||
|
inspect_factory (factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue