mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-13 08:56:39 -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 = \
|
||||
-I$(top_srcdir)/ \
|
||||
-I$(top_srcdir)/spa/include/ \
|
||||
-I$(top_srcdir)/pinos/modules \
|
||||
-I$(top_builddir)/pinos/modules \
|
||||
-DPINOS_SRCDIR=\"$(abs_srcdir)\" \
|
||||
|
|
@ -39,8 +40,8 @@ AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS)
|
|||
AM_CXXFLAGS = $(AM_CFLAGS)
|
||||
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
|
||||
|
||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS)
|
||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS)
|
||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||
AM_LDFLAGS = $(NODELETE_LDFLAGS)
|
||||
|
||||
FOREIGN_CFLAGS = -w
|
||||
|
|
@ -69,6 +70,7 @@ xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
|
|||
|
||||
enumtypesincludes = client/context.h \
|
||||
client/introspect.h \
|
||||
client/ringbuffer.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
||||
|
|
@ -131,7 +133,7 @@ noinst_LTLIBRARIES =
|
|||
|
||||
TESTS_default =
|
||||
|
||||
TESTS_norun = test-client test-node
|
||||
TESTS_norun = test-client
|
||||
|
||||
# These tests need a running pinos daemon
|
||||
TESTS_daemon =
|
||||
|
|
@ -143,11 +145,6 @@ test_client_CFLAGS = $(AM_CFLAGS)
|
|||
test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||
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 #
|
||||
###################################
|
||||
|
|
@ -169,12 +166,14 @@ pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \
|
|||
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
|
||||
|
||||
pinosinclude_HEADERS = \
|
||||
client/pinos.h \
|
||||
client/buffer.h \
|
||||
client/client-node.h \
|
||||
client/client-port.h \
|
||||
client/context.h \
|
||||
client/enumtypes.h \
|
||||
client/introspect.h \
|
||||
client/mainloop.h \
|
||||
client/pinos.h \
|
||||
client/properties.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
|
@ -194,6 +193,7 @@ libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \
|
|||
client/stream.h client/stream.c \
|
||||
client/pinos.c client/pinos.h \
|
||||
client/fdmanager.c client/fdmanager.h \
|
||||
client/ringbuffer.c client/ringbuffer.h \
|
||||
client/subscribe.c client/subscribe.h \
|
||||
$(pinosgstsource)
|
||||
|
||||
|
|
@ -222,6 +222,7 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \
|
|||
modules/gst/gst-source.c modules/gst/gst-source.h \
|
||||
modules/gst/gst-sink.c modules/gst/gst-sink.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
|
||||
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "pinos/client/pinos.h"
|
||||
#include "pinos/client/enumtypes.h"
|
||||
|
|
@ -43,6 +44,74 @@ enum
|
|||
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
|
||||
pinos_client_port_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
|
|
@ -205,6 +274,7 @@ static void
|
|||
pinos_client_port_class_init (PinosClientPortClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
PinosPortClass *port_class = PINOS_PORT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosClientPortPrivate));
|
||||
|
||||
|
|
@ -223,6 +293,8 @@ pinos_client_port_class_init (PinosClientPortClass * klass)
|
|||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
port_class->get_ringbuffer = pinos_client_port_get_ringbuffer;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <pinos/client/introspect.h>
|
||||
#include <pinos/client/mainloop.h>
|
||||
#include <pinos/client/properties.h>
|
||||
#include <pinos/client/ringbuffer.h>
|
||||
#include <pinos/client/stream.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;
|
||||
}
|
||||
|
||||
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
|
||||
do_start (PinosStream *stream)
|
||||
{
|
||||
|
|
@ -1110,7 +1142,6 @@ pinos_stream_send_buffer (PinosStream *stream,
|
|||
g_return_val_if_fail (buffer != NULL, FALSE);
|
||||
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
|
||||
|
||||
if (!pinos_io_write_buffer (priv->fd, buffer, &error)) {
|
||||
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");
|
||||
pinos_daemon_add_node_factory (daemon, factory);
|
||||
|
||||
pinos_spa_alsa_sink_new (daemon, "alsa-sink", NULL);
|
||||
pinos_daemon_start (daemon);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
|
|
|||
|
|
@ -143,5 +143,12 @@
|
|||
|
||||
<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>
|
||||
</node>
|
||||
|
|
|
|||
|
|
@ -467,23 +467,6 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|||
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);
|
||||
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 {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*get_ringbuffer) (PinosPort *port,
|
||||
PinosProperties *props,
|
||||
GTask *task);
|
||||
};
|
||||
|
||||
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>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
|
@ -17,32 +17,32 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_COMMAND_H__
|
||||
#define __SPI_COMMAND_H__
|
||||
#ifndef __SPA_COMMAND_H__
|
||||
#define __SPA_COMMAND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _SpiCommand SpiCommand;
|
||||
typedef struct _SpaCommand SpaCommand;
|
||||
|
||||
#include <spi/defs.h>
|
||||
#include <spa/defs.h>
|
||||
|
||||
typedef enum {
|
||||
SPI_COMMAND_INVALID = 0,
|
||||
SPI_COMMAND_ACTIVATE,
|
||||
SPI_COMMAND_DEACTIVATE,
|
||||
SPI_COMMAND_START,
|
||||
SPI_COMMAND_STOP,
|
||||
SPI_COMMAND_FLUSH,
|
||||
SPI_COMMAND_DRAIN,
|
||||
SPI_COMMAND_MARKER,
|
||||
} SpiCommandType;
|
||||
SPA_COMMAND_INVALID = 0,
|
||||
SPA_COMMAND_ACTIVATE,
|
||||
SPA_COMMAND_DEACTIVATE,
|
||||
SPA_COMMAND_START,
|
||||
SPA_COMMAND_STOP,
|
||||
SPA_COMMAND_FLUSH,
|
||||
SPA_COMMAND_DRAIN,
|
||||
SPA_COMMAND_MARKER,
|
||||
} SpaCommandType;
|
||||
|
||||
struct _SpiCommand {
|
||||
struct _SpaCommand {
|
||||
volatile int refcount;
|
||||
SpiNotify notify;
|
||||
SpiCommandType type;
|
||||
SpaNotify notify;
|
||||
SpaCommandType type;
|
||||
uint32_t port_id;
|
||||
void *data;
|
||||
size_t size;
|
||||
|
|
@ -52,4 +52,4 @@ struct _SpiCommand {
|
|||
} /* extern "C" */
|
||||
#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>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
|
@ -17,38 +17,38 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_PORT_H__
|
||||
#define __SPI_PORT_H__
|
||||
#ifndef __SPA_PORT_H__
|
||||
#define __SPA_PORT_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <spi/defs.h>
|
||||
#include <spa/defs.h>
|
||||
|
||||
/**
|
||||
* SpiPortInfoFlags:
|
||||
* @SPI_PORT_INFO_FLAG_NONE: no flags
|
||||
* @SPI_PORT_INFO_FLAG_REMOVABLE: port can be removed
|
||||
* @SPI_PORT_INFO_FLAG_OPTIONAL: processing on port is optional
|
||||
* @SPI_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
|
||||
* @SPI_PORT_INFO_FLAG_IN_PLACE: the port can process data in-place and will need
|
||||
* SpaPortInfoFlags:
|
||||
* @SPA_PORT_INFO_FLAG_NONE: no flags
|
||||
* @SPA_PORT_INFO_FLAG_REMOVABLE: port can be removed
|
||||
* @SPA_PORT_INFO_FLAG_OPTIONAL: processing on port is optional
|
||||
* @SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER: the port can give a buffer
|
||||
* @SPA_PORT_INFO_FLAG_CAN_USE_BUFFER: the port can use a provided buffer
|
||||
* @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.
|
||||
* @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 {
|
||||
SPI_PORT_INFO_FLAG_NONE = 0,
|
||||
SPI_PORT_INFO_FLAG_REMOVABLE = 1 << 0,
|
||||
SPI_PORT_INFO_FLAG_OPTIONAL = 1 << 1,
|
||||
SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2,
|
||||
SPI_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3,
|
||||
SPI_PORT_INFO_FLAG_IN_PLACE = 1 << 4,
|
||||
SPI_PORT_INFO_FLAG_NO_REF = 1 << 5,
|
||||
} SpiPortInfoFlags;
|
||||
SPA_PORT_INFO_FLAG_NONE = 0,
|
||||
SPA_PORT_INFO_FLAG_REMOVABLE = 1 << 0,
|
||||
SPA_PORT_INFO_FLAG_OPTIONAL = 1 << 1,
|
||||
SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2,
|
||||
SPA_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3,
|
||||
SPA_PORT_INFO_FLAG_IN_PLACE = 1 << 4,
|
||||
SPA_PORT_INFO_FLAG_NO_REF = 1 << 5,
|
||||
} SpaPortInfoFlags;
|
||||
|
||||
/**
|
||||
* SpiPortInfo
|
||||
* SpaPortInfo
|
||||
* @flags: extra port flags
|
||||
* @size: minimum size of the buffers or 0 when not specified
|
||||
* @align: required alignment of the data
|
||||
|
|
@ -59,33 +59,37 @@ typedef enum {
|
|||
*
|
||||
*/
|
||||
typedef struct {
|
||||
SpiPortInfoFlags flags;
|
||||
SpaPortInfoFlags flags;
|
||||
size_t minsize;
|
||||
uint32_t align;
|
||||
unsigned int maxbuffering;
|
||||
uint64_t latency;
|
||||
const char **features;
|
||||
} SpiPortInfo;
|
||||
} SpaPortInfo;
|
||||
|
||||
/**
|
||||
* SpiPortStatusFlags:
|
||||
* @SPI_PORT_STATUS_FLAG_NONE: no status flags
|
||||
* @SPI_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output
|
||||
* @SPI_PORT_STATUS_FLAG_NEED_INPUT: port needs input
|
||||
* SpaPortStatusFlags:
|
||||
* @SPA_PORT_STATUS_FLAG_NONE: no status flags
|
||||
* @SPA_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output
|
||||
* @SPA_PORT_STATUS_FLAG_NEED_INPUT: port needs input
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_PORT_STATUS_FLAG_NONE = 0,
|
||||
SPI_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0,
|
||||
SPI_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1,
|
||||
} SpiPortStatusFlags;
|
||||
SPA_PORT_STATUS_FLAG_NONE = 0,
|
||||
SPA_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0,
|
||||
SPA_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1,
|
||||
} SpaPortStatusFlags;
|
||||
|
||||
/**
|
||||
* SpaPortStatus:
|
||||
* @flags: port status flags
|
||||
*/
|
||||
typedef struct {
|
||||
SpiPortStatusFlags flags;
|
||||
} SpiPortStatus;
|
||||
SpaPortStatusFlags flags;
|
||||
} SpaPortStatus;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#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