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,55 +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_COMMAND_H__
#define __SPI_COMMAND_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _SpiCommand SpiCommand;
#include <spi/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;
struct _SpiCommand {
volatile int refcount;
SpiNotify notify;
SpiCommandType type;
uint32_t port_id;
void *data;
size_t size;
};
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __SPI_COMMAND_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,91 +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_PORT_H__
#define __SPI_PORT_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <spi/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
* 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
*/
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;
/**
* SpiPortInfo
* @flags: extra port flags
* @size: minimum size of the buffers or 0 when not specified
* @align: required alignment of the data
* @maxbuffering: the maximum amount of bytes that the element will keep
* around internally
* @latency: latency on this port in nanoseconds
* @features: NULL terminated array of extra port features
*
*/
typedef struct {
SpiPortInfoFlags flags;
size_t minsize;
uint32_t align;
unsigned int maxbuffering;
uint64_t latency;
const char **features;
} SpiPortInfo;
/**
* 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
*/
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;
typedef struct {
SpiPortStatusFlags flags;
} SpiPortStatus;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __SPI_PORT_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;
}