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:
Wim Taymans 2016-06-28 12:21:56 +02:00
parent b8f6e99537
commit 3c029cba53
56 changed files with 7055 additions and 1530 deletions

View file

@ -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)

View file

@ -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

View file

@ -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
View 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
View 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__ */

View file

@ -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);

View file

@ -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);

View file

@ -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>

View file

@ -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);

View 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;
}

View 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__ */

View file

@ -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);

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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, &params)) < 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, &params)) < 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, &params)) < 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
View file

@ -0,0 +1 @@
subdir('spa')

View 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
View 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
View 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__ */

View file

@ -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
View 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
View 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
View 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__ */

View 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
View 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
View 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__ */

View file

@ -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
View 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__ */

View 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
View 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
View 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
View 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
View 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
View 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')

View 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;
}

View 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 (&params);
/* 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 (&params);
/* 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
View 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;
}

View 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)

View 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;
}

View 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)

View 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;
}

View 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;
}

View 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)

View 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
View file

@ -0,0 +1,4 @@
subdir('alsa')
subdir('audiomixer')
subdir('audiotestsrc')
subdir('volume')

View 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)

View 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
View 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
View 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
View 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
View 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
View 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;
}