mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-19 07:00:10 -05:00
more hacking
Move plugin API to separate directory for now Add ringbuffer and way to get ringbuffer from a port
This commit is contained in:
parent
b8f6e99537
commit
3c029cba53
56 changed files with 7055 additions and 1530 deletions
|
|
@ -31,6 +31,7 @@ dbuspolicydir=$(sysconfdir)/dbus-1/system.d
|
|||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/ \
|
||||
-I$(top_srcdir)/spa/include/ \
|
||||
-I$(top_srcdir)/pinos/modules \
|
||||
-I$(top_builddir)/pinos/modules \
|
||||
-DPINOS_SRCDIR=\"$(abs_srcdir)\" \
|
||||
|
|
@ -39,8 +40,8 @@ AM_CFLAGS = $(GLIB_CFLAGS) $(GST_CFLAGS)
|
|||
AM_CXXFLAGS = $(AM_CFLAGS)
|
||||
SERVER_CFLAGS = -D__INCLUDED_FROM_PINOS
|
||||
|
||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS)
|
||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS)
|
||||
AM_LIBADD = $(GLIB_LIBS) $(INTLLIBS) $(GST_LIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||
AM_LDADD = $(GLIB_LIBS) $(GST_LIBS) $(INTLLIBS) #$(top_srcdir)/spa/build/lib/libspa-lib.so
|
||||
AM_LDFLAGS = $(NODELETE_LDFLAGS)
|
||||
|
||||
FOREIGN_CFLAGS = -w
|
||||
|
|
@ -69,6 +70,7 @@ xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
|
|||
|
||||
enumtypesincludes = client/context.h \
|
||||
client/introspect.h \
|
||||
client/ringbuffer.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
||||
|
|
@ -131,7 +133,7 @@ noinst_LTLIBRARIES =
|
|||
|
||||
TESTS_default =
|
||||
|
||||
TESTS_norun = test-client test-node
|
||||
TESTS_norun = test-client
|
||||
|
||||
# These tests need a running pinos daemon
|
||||
TESTS_daemon =
|
||||
|
|
@ -143,11 +145,6 @@ test_client_CFLAGS = $(AM_CFLAGS)
|
|||
test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||
test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
|
||||
|
||||
test_node_SOURCES = tests/test-node.c tests/spi-volume.c tests/spi-alsa-sink.c tests/spi-audiotestsrc.c
|
||||
test_node_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS)
|
||||
test_node_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la
|
||||
test_node_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ALSA_LIBS)
|
||||
|
||||
###################################
|
||||
# Tools programs #
|
||||
###################################
|
||||
|
|
@ -169,12 +166,14 @@ pinosgstsource = gst/gstpinospay.h gst/gstpinospay.c \
|
|||
gst/gsttmpfileallocator.h gst/gsttmpfileallocator.c
|
||||
|
||||
pinosinclude_HEADERS = \
|
||||
client/pinos.h \
|
||||
client/buffer.h \
|
||||
client/client-node.h \
|
||||
client/client-port.h \
|
||||
client/context.h \
|
||||
client/enumtypes.h \
|
||||
client/introspect.h \
|
||||
client/mainloop.h \
|
||||
client/pinos.h \
|
||||
client/properties.h \
|
||||
client/stream.h \
|
||||
client/subscribe.h
|
||||
|
|
@ -194,6 +193,7 @@ libpinos_@PINOS_MAJORMINOR@_la_SOURCES = \
|
|||
client/stream.h client/stream.c \
|
||||
client/pinos.c client/pinos.h \
|
||||
client/fdmanager.c client/fdmanager.h \
|
||||
client/ringbuffer.c client/ringbuffer.h \
|
||||
client/subscribe.c client/subscribe.h \
|
||||
$(pinosgstsource)
|
||||
|
||||
|
|
@ -222,6 +222,7 @@ libpinoscore_@PINOS_MAJORMINOR@_la_SOURCES = \
|
|||
modules/gst/gst-source.c modules/gst/gst-source.h \
|
||||
modules/gst/gst-sink.c modules/gst/gst-sink.h \
|
||||
modules/gst/gst-node-factory.c modules/gst/gst-node-factory.h \
|
||||
modules/spa/spa-alsa-sink.c modules/spa/spa-alsa-sink.h \
|
||||
dbus/org-pinos.c dbus/org-pinos.h
|
||||
|
||||
libpinoscore_@PINOS_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <gst/gst.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "pinos/client/pinos.h"
|
||||
#include "pinos/client/enumtypes.h"
|
||||
|
|
@ -43,6 +44,74 @@ enum
|
|||
PROP_PROXY,
|
||||
};
|
||||
|
||||
static void
|
||||
on_ringbuffer (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task = user_data;
|
||||
PinosClientPort *port = g_task_get_source_object (task);
|
||||
PinosClientPortPrivate *priv = port->priv;
|
||||
GVariant *ret;
|
||||
GError *error = NULL;
|
||||
GUnixFDList *fdlist;
|
||||
gint fd, semfd, fd_idx, sem_idx;
|
||||
guint fdsize;
|
||||
PinosRingbuffer *rbuf;
|
||||
|
||||
g_assert (priv->proxy == G_DBUS_PROXY (source_object));
|
||||
|
||||
ret = g_dbus_proxy_call_with_unix_fd_list_finish (priv->proxy, &fdlist, res, &error);
|
||||
if (ret == NULL)
|
||||
goto create_failed;
|
||||
|
||||
g_variant_get (ret, "(huh)", &fd_idx, &fdsize, &sem_idx);
|
||||
g_variant_unref (ret);
|
||||
|
||||
fd = g_unix_fd_list_get (fdlist, fd_idx, &error);
|
||||
semfd = g_unix_fd_list_get (fdlist, sem_idx, &error);
|
||||
g_object_unref (fdlist);
|
||||
|
||||
if (fd == -1 || semfd == -1)
|
||||
goto create_failed;
|
||||
|
||||
rbuf = pinos_ringbuffer_new_import (PINOS_RINGBUFFER_MODE_WRITE,
|
||||
fd, fdsize, semfd);
|
||||
|
||||
g_task_return_pointer (task, rbuf, (GDestroyNotify) g_object_unref);
|
||||
g_object_unref (task);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
create_failed:
|
||||
{
|
||||
g_warning ("failed to get ringbuffer: %s", error->message);
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pinos_client_port_get_ringbuffer (PinosPort *port,
|
||||
PinosProperties *props,
|
||||
GTask *task)
|
||||
{
|
||||
PinosClientPortPrivate *priv = PINOS_CLIENT_PORT (port)->priv;
|
||||
|
||||
g_dbus_proxy_call (priv->proxy,
|
||||
"GetRingbuffer",
|
||||
g_variant_new ("(@a{sv})",
|
||||
pinos_properties_to_variant (props)),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL, /* GCancellable *cancellable */
|
||||
on_ringbuffer,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_client_port_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
|
|
@ -205,6 +274,7 @@ static void
|
|||
pinos_client_port_class_init (PinosClientPortClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
PinosPortClass *port_class = PINOS_PORT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosClientPortPrivate));
|
||||
|
||||
|
|
@ -223,6 +293,8 @@ pinos_client_port_class_init (PinosClientPortClass * klass)
|
|||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
port_class->get_ringbuffer = pinos_client_port_get_ringbuffer;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <pinos/client/introspect.h>
|
||||
#include <pinos/client/mainloop.h>
|
||||
#include <pinos/client/properties.h>
|
||||
#include <pinos/client/ringbuffer.h>
|
||||
#include <pinos/client/stream.h>
|
||||
#include <pinos/client/subscribe.h>
|
||||
|
||||
|
|
|
|||
393
pinos/client/ringbuffer.c
Normal file
393
pinos/client/ringbuffer.c
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <pinos/client/enumtypes.h>
|
||||
#include <pinos/client/ringbuffer.h>
|
||||
|
||||
#include <spa/include/spa/ringbuffer.h>
|
||||
#include <spa/lib/ringbuffer.c>
|
||||
|
||||
#define PINOS_RINGBUFFER_GET_PRIVATE(rb) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((rb), PINOS_TYPE_RINGBUFFER, PinosRingbufferPrivate))
|
||||
|
||||
typedef struct {
|
||||
SpaRingbuffer rbuf;
|
||||
/* ringbuffer starts here */
|
||||
} PinosRingbufferData;
|
||||
|
||||
struct _PinosRingbufferPrivate
|
||||
{
|
||||
PinosRingbufferMode mode;
|
||||
guint size;
|
||||
guint fdsize;
|
||||
int fd;
|
||||
int semaphore;
|
||||
|
||||
PinosRingbufferData *data;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PinosRingbuffer, pinos_ringbuffer, G_TYPE_OBJECT);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_MODE,
|
||||
PROP_SIZE,
|
||||
PROP_FD,
|
||||
PROP_FDSIZE,
|
||||
PROP_SEMAPHORE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_NONE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_get_property (GObject *_object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosRingbuffer *rb = PINOS_RINGBUFFER (_object);
|
||||
PinosRingbufferPrivate *priv = rb->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:
|
||||
g_value_set_enum (value, priv->mode);
|
||||
break;
|
||||
|
||||
case PROP_SIZE:
|
||||
g_value_set_uint (value, priv->size);
|
||||
break;
|
||||
|
||||
case PROP_FD:
|
||||
g_value_set_int (value, priv->fd);
|
||||
break;
|
||||
|
||||
case PROP_FDSIZE:
|
||||
g_value_set_uint (value, priv->fdsize);
|
||||
break;
|
||||
|
||||
case PROP_SEMAPHORE:
|
||||
g_value_set_int (value, priv->semaphore);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (rb, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_set_property (GObject *_object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosRingbuffer *rb = PINOS_RINGBUFFER (_object);
|
||||
PinosRingbufferPrivate *priv = rb->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:
|
||||
priv->mode = g_value_get_enum (value);
|
||||
break;
|
||||
|
||||
case PROP_SIZE:
|
||||
priv->size = g_value_get_uint (value);
|
||||
break;
|
||||
|
||||
case PROP_FD:
|
||||
priv->fd = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
case PROP_FDSIZE:
|
||||
priv->fdsize = g_value_get_uint (value);
|
||||
break;
|
||||
|
||||
case PROP_SEMAPHORE:
|
||||
priv->semaphore = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (rb, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
tmpfile_create (gsize size)
|
||||
{
|
||||
char filename[] = "/dev/shm/tmpfilepay.XXXXXX";
|
||||
int fd, result;
|
||||
|
||||
fd = mkostemp (filename, O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
unlink (filename);
|
||||
|
||||
result = ftruncate (fd, size);
|
||||
if (result == -1) {
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_constructed (GObject * obj)
|
||||
{
|
||||
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||
PinosRingbufferPrivate *priv = rb->priv;
|
||||
|
||||
g_debug ("ringbuffer %p: constructed", rb);
|
||||
|
||||
if (priv->fd == -1) {
|
||||
priv->fdsize = priv->size + sizeof (PinosRingbufferData);
|
||||
priv->fd = tmpfile_create (priv->fdsize);
|
||||
priv->semaphore = eventfd (0, EFD_CLOEXEC);
|
||||
}
|
||||
priv->data = mmap (NULL, priv->fdsize, PROT_READ | PROT_WRITE, MAP_SHARED, priv->fd, 0);
|
||||
|
||||
spa_ringbuffer_init (&priv->data->rbuf, (guint8 *)priv->data + sizeof (PinosRingbufferData), priv->size);
|
||||
|
||||
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->constructed (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_dispose (GObject * obj)
|
||||
{
|
||||
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||
|
||||
g_debug ("ringbuffer %p: dispose", rb);
|
||||
|
||||
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->dispose (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_finalize (GObject * obj)
|
||||
{
|
||||
PinosRingbuffer *rb = PINOS_RINGBUFFER (obj);
|
||||
|
||||
g_debug ("ringbuffer %p: finalize", rb);
|
||||
|
||||
G_OBJECT_CLASS (pinos_ringbuffer_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_class_init (PinosRingbufferClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosRingbufferPrivate));
|
||||
|
||||
gobject_class->constructed = pinos_ringbuffer_constructed;
|
||||
gobject_class->dispose = pinos_ringbuffer_dispose;
|
||||
gobject_class->finalize = pinos_ringbuffer_finalize;
|
||||
gobject_class->set_property = pinos_ringbuffer_set_property;
|
||||
gobject_class->get_property = pinos_ringbuffer_get_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MODE,
|
||||
g_param_spec_enum ("mode",
|
||||
"Mode",
|
||||
"The mode of the ringbuffer",
|
||||
PINOS_TYPE_RINGBUFFER_MODE,
|
||||
PINOS_RINGBUFFER_MODE_READ,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SIZE,
|
||||
g_param_spec_uint ("size",
|
||||
"Size",
|
||||
"The size of the ringbuffer",
|
||||
1,
|
||||
G_MAXUINT,
|
||||
64 * 1024,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_FD,
|
||||
g_param_spec_int ("fd",
|
||||
"Fd",
|
||||
"The file descriptor with memory",
|
||||
-1,
|
||||
G_MAXINT,
|
||||
-1,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_FDSIZE,
|
||||
g_param_spec_uint ("fdsize",
|
||||
"Fd Size",
|
||||
"Size of the memory",
|
||||
1,
|
||||
G_MAXUINT,
|
||||
-1,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_SEMAPHORE,
|
||||
g_param_spec_int ("semaphore",
|
||||
"Semaphore",
|
||||
"Semaphore file desciptor",
|
||||
-1,
|
||||
G_MAXINT,
|
||||
-1,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_ringbuffer_init (PinosRingbuffer * rb)
|
||||
{
|
||||
PinosRingbufferPrivate *priv = rb->priv = PINOS_RINGBUFFER_GET_PRIVATE (rb);
|
||||
|
||||
g_debug ("ringbuffer %p: new %u", rb, priv->size);
|
||||
|
||||
priv->mode = PINOS_RINGBUFFER_MODE_READ;
|
||||
priv->size = 0;
|
||||
priv->fd = -1;
|
||||
}
|
||||
|
||||
PinosRingbuffer *
|
||||
pinos_ringbuffer_new (PinosRingbufferMode mode,
|
||||
gsize size)
|
||||
{
|
||||
PinosRingbuffer *rb;
|
||||
|
||||
g_return_val_if_fail (size > 0, NULL);
|
||||
|
||||
rb = g_object_new (PINOS_TYPE_RINGBUFFER,
|
||||
"size", size,
|
||||
"mode", mode,
|
||||
NULL);
|
||||
return rb;
|
||||
}
|
||||
|
||||
PinosRingbuffer *
|
||||
pinos_ringbuffer_new_import (PinosRingbufferMode mode,
|
||||
guint fdsize,
|
||||
int fd,
|
||||
int semaphore)
|
||||
{
|
||||
PinosRingbuffer *rb;
|
||||
|
||||
g_return_val_if_fail (fdsize > 0, NULL);
|
||||
g_return_val_if_fail (fd >= 0, NULL);
|
||||
|
||||
rb = g_object_new (PINOS_TYPE_RINGBUFFER,
|
||||
"mode", mode,
|
||||
"fd", fd,
|
||||
"fdsize", fdsize,
|
||||
"semaphore", semaphore,
|
||||
NULL);
|
||||
return rb;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
pinos_ringbuffer_get_read_areas (PinosRingbuffer *rbuf,
|
||||
PinosRingbufferArea areas[2])
|
||||
{
|
||||
PinosRingbufferPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||
priv = rbuf->priv;
|
||||
|
||||
spa_ringbuffer_get_read_areas (&priv->data->rbuf, (SpaRingbufferArea *)areas);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_ringbuffer_get_write_areas (PinosRingbuffer *rbuf,
|
||||
PinosRingbufferArea areas[2])
|
||||
{
|
||||
PinosRingbufferPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||
priv = rbuf->priv;
|
||||
|
||||
spa_ringbuffer_get_write_areas (&priv->data->rbuf, (SpaRingbufferArea *)areas);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_ringbuffer_read_advance (PinosRingbuffer *rbuf,
|
||||
gssize len)
|
||||
{
|
||||
PinosRingbufferPrivate *priv;
|
||||
guint64 val;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||
priv = rbuf->priv;
|
||||
|
||||
spa_ringbuffer_read_advance (&priv->data->rbuf, len);
|
||||
|
||||
if (priv->mode == PINOS_RINGBUFFER_MODE_READ) {
|
||||
val = 1;
|
||||
write (priv->semaphore, &val, 8);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pinos_ringbuffer_write_advance (PinosRingbuffer *rbuf,
|
||||
gssize len)
|
||||
{
|
||||
PinosRingbufferPrivate *priv;
|
||||
guint64 val;
|
||||
|
||||
g_return_val_if_fail (PINOS_IS_RINGBUFFER (rbuf), FALSE);
|
||||
priv = rbuf->priv;
|
||||
|
||||
spa_ringbuffer_write_advance (&priv->data->rbuf, len);
|
||||
|
||||
if (priv->mode == PINOS_RINGBUFFER_MODE_WRITE) {
|
||||
val = 1;
|
||||
write (priv->semaphore, &val, 8);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
97
pinos/client/ringbuffer.h
Normal file
97
pinos/client/ringbuffer.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_RINGBUFFER_H__
|
||||
#define __PINOS_RINGBUFFER_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _PinosRingbuffer PinosRingbuffer;
|
||||
typedef struct _PinosRingbufferClass PinosRingbufferClass;
|
||||
typedef struct _PinosRingbufferPrivate PinosRingbufferPrivate;
|
||||
|
||||
#include <pinos/client/introspect.h>
|
||||
#include <pinos/client/buffer.h>
|
||||
|
||||
#define PINOS_TYPE_RINGBUFFER (pinos_ringbuffer_get_type ())
|
||||
#define PINOS_IS_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_RINGBUFFER))
|
||||
#define PINOS_IS_RINGBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_RINGBUFFER))
|
||||
#define PINOS_RINGBUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_RINGBUFFER, PinosRingbufferClass))
|
||||
#define PINOS_RINGBUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_RINGBUFFER, PinosRingbuffer))
|
||||
#define PINOS_RINGBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_RINGBUFFER, PinosRingbufferClass))
|
||||
#define PINOS_RINGBUFFER_CAST(obj) ((PinosRingbuffer*)(obj))
|
||||
#define PINOS_RINGBUFFER_CLASS_CAST(klass) ((PinosRingbufferClass*)(klass))
|
||||
|
||||
/**
|
||||
* PinosRingbuffer:
|
||||
*
|
||||
* Pinos ringbuffer object class.
|
||||
*/
|
||||
struct _PinosRingbuffer {
|
||||
GObject object;
|
||||
|
||||
PinosRingbufferPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* PinosRingbufferClass:
|
||||
*
|
||||
* Pinos ringbuffer object class.
|
||||
*/
|
||||
struct _PinosRingbufferClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
typedef void (*PinosRingbufferCallback) (PinosRingbuffer *rb, gpointer user_data);
|
||||
|
||||
typedef enum {
|
||||
PINOS_RINGBUFFER_MODE_READ,
|
||||
PINOS_RINGBUFFER_MODE_WRITE,
|
||||
} PinosRingbufferMode;
|
||||
|
||||
typedef struct {
|
||||
gpointer data;
|
||||
gsize len;
|
||||
} PinosRingbufferArea;
|
||||
|
||||
/* normal GObject stuff */
|
||||
GType pinos_ringbuffer_get_type (void);
|
||||
|
||||
PinosRingbuffer * pinos_ringbuffer_new (PinosRingbufferMode mode,
|
||||
gsize size);
|
||||
PinosRingbuffer * pinos_ringbuffer_new_import (PinosRingbufferMode mode,
|
||||
guint fdsize,
|
||||
int fd,
|
||||
int semaphore);
|
||||
|
||||
gboolean pinos_ringbuffer_get_read_areas (PinosRingbuffer *rbuf,
|
||||
PinosRingbufferArea areas[2]);
|
||||
gboolean pinos_ringbuffer_read_advance (PinosRingbuffer *rbuf,
|
||||
gssize len);
|
||||
|
||||
gboolean pinos_ringbuffer_get_write_areas (PinosRingbuffer *rbuf,
|
||||
PinosRingbufferArea areas[2]);
|
||||
gboolean pinos_ringbuffer_write_advance (PinosRingbuffer *rbuf,
|
||||
gssize len);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_RINGBUFFER_H__ */
|
||||
|
|
@ -851,6 +851,38 @@ pinos_stream_connect (PinosStream *stream,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_ringbuffer (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosStream *stream = user_data;
|
||||
PinosStreamPrivate *priv = stream->priv;
|
||||
GError *error = NULL;
|
||||
|
||||
g_assert (priv->port == PINOS_PORT (source_object));
|
||||
|
||||
priv->ringbuffer = pinos_port_get_ringbuffer_finish (priv->port,
|
||||
res,
|
||||
&error);
|
||||
if (priv->ringbuffer == NULL)
|
||||
goto no_ringbuffer;
|
||||
|
||||
stream_set_state (stream, PINOS_STREAM_STATE_STREAMING, NULL);
|
||||
g_object_unref (stream);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
no_ringbuffer:
|
||||
{
|
||||
g_warning ("failed to get ringbuffer: %s", error->message);
|
||||
stream_set_state (stream, PINOS_STREAM_STATE_ERROR, error);
|
||||
g_object_unref (stream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_start (PinosStream *stream)
|
||||
{
|
||||
|
|
@ -1110,7 +1142,6 @@ pinos_stream_send_buffer (PinosStream *stream,
|
|||
g_return_val_if_fail (buffer != NULL, FALSE);
|
||||
|
||||
priv = stream->priv;
|
||||
g_return_val_if_fail (priv->state == PINOS_STREAM_STATE_STREAMING, FALSE);
|
||||
|
||||
if (!pinos_io_write_buffer (priv->fd, buffer, &error)) {
|
||||
g_warning ("stream %p: failed to read buffer: %s", stream, error->message);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ main (gint argc, gchar *argv[])
|
|||
factory = pinos_gst_node_factory_new ("gst-node-factory");
|
||||
pinos_daemon_add_node_factory (daemon, factory);
|
||||
|
||||
pinos_spa_alsa_sink_new (daemon, "alsa-sink", NULL);
|
||||
pinos_daemon_start (daemon);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
|
|
|||
|
|
@ -143,5 +143,12 @@
|
|||
|
||||
<method name='Remove'/>
|
||||
|
||||
<method name='GetRingbuffer'>
|
||||
<arg type='a{sv}' name='properties' direction='in'/>
|
||||
<arg type='h' name='buffermem' direction='out'/>
|
||||
<arg type='u' name='buffersize' direction='out'/>
|
||||
<arg type='h' name='fd' direction='out'/>
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
||||
|
|
|
|||
|
|
@ -467,23 +467,6 @@ gst_pinos_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
|
|||
pinos_main_loop_wait (pinossink->loop);
|
||||
}
|
||||
}
|
||||
{
|
||||
PinosBufferBuilder builder;
|
||||
PinosPacketFormatChange change;
|
||||
PinosBuffer pbuf;
|
||||
|
||||
pinos_stream_buffer_builder_init (pinossink->stream, &builder);
|
||||
|
||||
change.id = 1;
|
||||
change.format = g_bytes_get_data (format, NULL);
|
||||
pinos_buffer_builder_add_format_change (&builder, &change);
|
||||
pinos_buffer_builder_end (&builder, &pbuf);
|
||||
|
||||
g_debug ("sending format");
|
||||
res = pinos_stream_send_buffer (pinossink->stream, &pbuf);
|
||||
pinos_buffer_unref (&pbuf);
|
||||
}
|
||||
|
||||
pinos_main_loop_unlock (pinossink->loop);
|
||||
g_bytes_unref (format);
|
||||
|
||||
|
|
|
|||
531
pinos/modules/spa/spa-alsa-sink.c
Normal file
531
pinos/modules/spa/spa-alsa-sink.c
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <spa/include/spa/node.h>
|
||||
#include <spa/include/spa/audio/raw.h>
|
||||
|
||||
#include "spa-alsa-sink.h"
|
||||
|
||||
#define PINOS_SPA_ALSA_SINK_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkPrivate))
|
||||
|
||||
struct _PinosSpaAlsaSinkPrivate
|
||||
{
|
||||
PinosPort *input;
|
||||
|
||||
PinosProperties *props;
|
||||
PinosRingbuffer *ringbuffer;
|
||||
|
||||
SpaHandle *sink;
|
||||
const SpaNode *sink_node;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_POSSIBLE_FORMATS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PinosSpaAlsaSink, pinos_spa_alsa_sink, PINOS_TYPE_SERVER_NODE);
|
||||
|
||||
static SpaResult
|
||||
make_node (SpaHandle **handle, const SpaNode **node, const char *lib, const char *name)
|
||||
{
|
||||
SpaResult res;
|
||||
void *hnd;
|
||||
SpaEnumHandleFactoryFunc enum_func;
|
||||
unsigned int i;
|
||||
|
||||
if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) {
|
||||
g_error ("can't load %s: %s", lib, dlerror());
|
||||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) {
|
||||
g_error ("can't find enum function");
|
||||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; ;i++) {
|
||||
const SpaHandleFactory *factory;
|
||||
const void *iface;
|
||||
|
||||
if ((res = enum_func (i, &factory)) < 0) {
|
||||
if (res != SPA_RESULT_ENUM_END)
|
||||
g_error ("can't enumerate factories: %d", res);
|
||||
break;
|
||||
}
|
||||
if (strcmp (factory->name, name))
|
||||
continue;
|
||||
|
||||
if ((res = factory->instantiate (factory, handle)) < 0) {
|
||||
g_error ("can't make factory instance: %d", res);
|
||||
return res;
|
||||
}
|
||||
if ((res = (*handle)->get_interface (*handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) {
|
||||
g_error ("can't get interface %d", res);
|
||||
return res;
|
||||
}
|
||||
*node = iface;
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
|
||||
static void
|
||||
on_sink_event (SpaHandle *handle, SpaEvent *event, void *user_data)
|
||||
{
|
||||
PinosSpaAlsaSink *this = user_data;
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
|
||||
switch (event->type) {
|
||||
case SPA_EVENT_TYPE_PULL_INPUT:
|
||||
{
|
||||
SpaBuffer *buf;
|
||||
SpaInputInfo iinfo;
|
||||
SpaOutputInfo oinfo;
|
||||
SpaResult res;
|
||||
PinosRingbufferArea areas[2];
|
||||
uint8_t *data;
|
||||
size_t size, towrite, total;
|
||||
|
||||
buf = event->data;
|
||||
|
||||
oinfo.port_id = 0;
|
||||
oinfo.flags = SPA_OUTPUT_FLAG_PULL;
|
||||
oinfo.buffer = buf;
|
||||
oinfo.event = NULL;
|
||||
|
||||
g_debug ("pull ringbuffer %p", buf);
|
||||
|
||||
size = buf->size;
|
||||
data = buf->datas[0].data;
|
||||
|
||||
pinos_ringbuffer_get_read_areas (priv->ringbuffer, areas);
|
||||
|
||||
total = MIN (size, areas[0].len + areas[1].len);
|
||||
g_debug ("total read %zd %zd", total, areas[0].len + areas[1].len);
|
||||
if (total < size) {
|
||||
g_warning ("underrun");
|
||||
}
|
||||
towrite = MIN (size, areas[0].len);
|
||||
memcpy (data, areas[0].data, towrite);
|
||||
size -= towrite;
|
||||
data += towrite;
|
||||
towrite = MIN (size, areas[1].len);
|
||||
memcpy (data, areas[1].data, towrite);
|
||||
|
||||
pinos_ringbuffer_read_advance (priv->ringbuffer, total);
|
||||
|
||||
buf->size = total;
|
||||
|
||||
iinfo.port_id = event->port_id;
|
||||
iinfo.flags = SPA_INPUT_FLAG_NONE;
|
||||
iinfo.buffer = oinfo.buffer;
|
||||
iinfo.event = oinfo.event;
|
||||
|
||||
g_debug ("push sink %p", iinfo.buffer);
|
||||
if ((res = priv->sink_node->push_port_input (priv->sink, 1, &iinfo)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_debug ("got event %d", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_pipeline (PinosSpaAlsaSink *this)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaProps *props;
|
||||
SpaPropValue value;
|
||||
SpaCommand cmd;
|
||||
|
||||
if ((res = make_node (&priv->sink, &priv->sink_node, "spa/build/plugins/alsa/libspa-alsa.so", "alsa-sink")) < 0) {
|
||||
g_error ("can't create alsa-sink: %d", res);
|
||||
return;
|
||||
}
|
||||
priv->sink_node->set_event_callback (priv->sink, on_sink_event, this);
|
||||
|
||||
if ((res = priv->sink_node->get_props (priv->sink, &props)) < 0)
|
||||
g_debug ("got get_props error %d", res);
|
||||
|
||||
value.type = SPA_PROP_TYPE_STRING;
|
||||
value.size = strlen ("hw:1")+1;
|
||||
value.value = "hw:1";
|
||||
props->set_prop (props, spa_props_index_for_name (props, "device"), &value);
|
||||
|
||||
if ((res = priv->sink_node->set_props (priv->sink, props)) < 0)
|
||||
g_debug ("got set_props error %d", res);
|
||||
|
||||
cmd.type = SPA_COMMAND_ACTIVATE;
|
||||
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||
g_debug ("got activate error %d", res);
|
||||
}
|
||||
|
||||
static void
|
||||
start_pipeline (PinosSpaAlsaSink *sink)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
|
||||
g_debug ("spa-alsa-sink %p: starting pipeline", sink);
|
||||
|
||||
cmd.type = SPA_COMMAND_START;
|
||||
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_pipeline (PinosSpaAlsaSink *sink)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
|
||||
g_debug ("spa-alsa-sink %p: stopping pipeline", sink);
|
||||
|
||||
cmd.type = SPA_COMMAND_STOP;
|
||||
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_pipeline (PinosSpaAlsaSink *sink)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
|
||||
g_debug ("spa-alsa-sink %p: destroy pipeline", sink);
|
||||
|
||||
cmd.type = SPA_COMMAND_DEACTIVATE;
|
||||
if ((res = priv->sink_node->send_command (priv->sink, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_state (PinosNode *node,
|
||||
PinosNodeState state)
|
||||
{
|
||||
PinosSpaAlsaSink *this = PINOS_SPA_ALSA_SINK (node);
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
|
||||
g_debug ("spa-alsa-sink %p: set state %s", node, pinos_node_state_as_string (state));
|
||||
|
||||
switch (state) {
|
||||
case PINOS_NODE_STATE_SUSPENDED:
|
||||
break;
|
||||
|
||||
case PINOS_NODE_STATE_INITIALIZING:
|
||||
break;
|
||||
|
||||
case PINOS_NODE_STATE_IDLE:
|
||||
stop_pipeline (this);
|
||||
break;
|
||||
|
||||
case PINOS_NODE_STATE_RUNNING:
|
||||
//start_pipeline (this);
|
||||
break;
|
||||
|
||||
case PINOS_NODE_STATE_ERROR:
|
||||
break;
|
||||
}
|
||||
pinos_node_update_state (node, state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_linked (PinosPort *port, PinosPort *peer, gpointer user_data)
|
||||
{
|
||||
PinosNode *node = user_data;
|
||||
gint n_peers;
|
||||
|
||||
g_debug ("port %p: linked", port);
|
||||
|
||||
n_peers = pinos_port_get_n_links (port);
|
||||
if (n_peers == 1)
|
||||
pinos_node_report_busy (node);
|
||||
}
|
||||
|
||||
static void
|
||||
on_unlinked (PinosPort *port, PinosPort *peer, gpointer user_data)
|
||||
{
|
||||
PinosNode *node = user_data;
|
||||
gint n_peers;
|
||||
|
||||
g_debug ("port %p: unlinked", port);
|
||||
n_peers = pinos_port_get_n_links (port);
|
||||
if (n_peers == 0)
|
||||
pinos_node_report_idle (node);
|
||||
}
|
||||
|
||||
static SpaResult
|
||||
negotiate_formats (PinosSpaAlsaSink *this)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaFormat *format;
|
||||
SpaProps *props;
|
||||
uint32_t val;
|
||||
SpaPropValue value;
|
||||
|
||||
if ((res = priv->sink_node->enum_port_formats (priv->sink, 0, 0, &format)) < 0)
|
||||
return res;
|
||||
|
||||
props = &format->props;
|
||||
|
||||
value.type = SPA_PROP_TYPE_UINT32;
|
||||
value.size = sizeof (uint32_t);
|
||||
value.value = &val;
|
||||
|
||||
val = SPA_AUDIO_FORMAT_S16LE;
|
||||
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_FORMAT), &value)) < 0)
|
||||
return res;
|
||||
val = SPA_AUDIO_LAYOUT_INTERLEAVED;
|
||||
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_LAYOUT), &value)) < 0)
|
||||
return res;
|
||||
val = 44100;
|
||||
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_RATE), &value)) < 0)
|
||||
return res;
|
||||
val = 2;
|
||||
if ((res = props->set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_AUDIO_CHANNELS), &value)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = priv->sink_node->set_port_format (priv->sink, 0, 0, format)) < 0)
|
||||
return res;
|
||||
|
||||
priv->ringbuffer = pinos_ringbuffer_new (PINOS_RINGBUFFER_MODE_READ, 64 * 1024);
|
||||
|
||||
g_object_set (priv->input, "ringbuffer", priv->ringbuffer, NULL);
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
on_received_buffer (PinosPort *port,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSpaAlsaSink *this = user_data;
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
PinosBuffer *pbuf;
|
||||
PinosBufferIter it;
|
||||
|
||||
pbuf = pinos_port_peek_buffer (port);
|
||||
|
||||
pinos_buffer_iter_init (&it, pbuf);
|
||||
while (pinos_buffer_iter_next (&it)) {
|
||||
switch (pinos_buffer_iter_get_type (&it)) {
|
||||
case PINOS_PACKET_TYPE_HEADER:
|
||||
{
|
||||
PinosPacketHeader hdr;
|
||||
|
||||
if (!pinos_buffer_iter_parse_header (&it, &hdr))
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PINOS_PACKET_TYPE_FD_PAYLOAD:
|
||||
{
|
||||
PinosPacketFDPayload p;
|
||||
int fd;
|
||||
PinosRingbufferArea areas[2];
|
||||
uint8_t *data, *d;
|
||||
size_t size, towrite, total;
|
||||
|
||||
if (!pinos_buffer_iter_parse_fd_payload (&it, &p))
|
||||
break;
|
||||
|
||||
g_debug ("got fd payload id %d", p.id);
|
||||
fd = pinos_buffer_get_fd (pbuf, p.fd_index);
|
||||
if (fd == -1)
|
||||
break;
|
||||
|
||||
d = data = mmap (NULL, p.size, PROT_READ, MAP_PRIVATE, fd, p.offset);
|
||||
size = p.size;
|
||||
|
||||
pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas);
|
||||
|
||||
total = MIN (size, areas[0].len + areas[1].len);
|
||||
g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len);
|
||||
towrite = MIN (size, areas[0].len);
|
||||
memcpy (areas[0].data, data, towrite);
|
||||
size -= towrite;
|
||||
data += towrite;
|
||||
towrite = MIN (size, areas[1].len);
|
||||
memcpy (areas[1].data, data, towrite);
|
||||
|
||||
pinos_ringbuffer_write_advance (priv->ringbuffer, total);
|
||||
|
||||
munmap (d, p.size);
|
||||
break;
|
||||
}
|
||||
case PINOS_PACKET_TYPE_FORMAT_CHANGE:
|
||||
{
|
||||
PinosPacketFormatChange change;
|
||||
|
||||
if (!pinos_buffer_iter_parse_format_change (&it, &change))
|
||||
break;
|
||||
g_debug ("got format change %d %s", change.id, change.format);
|
||||
|
||||
negotiate_formats (this);
|
||||
start_pipeline (this);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pinos_buffer_iter_end (&it);
|
||||
}
|
||||
|
||||
static void
|
||||
on_input_port_created (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosNode *node = PINOS_NODE (source_object);
|
||||
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (node);
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
|
||||
priv->input = pinos_node_create_port_finish (node, res, NULL);
|
||||
|
||||
pinos_port_set_received_buffer_cb (priv->input, on_received_buffer, sink, NULL);
|
||||
|
||||
g_signal_connect (priv->input, "linked", (GCallback) on_linked, node);
|
||||
g_signal_connect (priv->input, "unlinked", (GCallback) on_unlinked, node);
|
||||
|
||||
create_pipeline (sink);
|
||||
}
|
||||
|
||||
static void
|
||||
sink_constructed (GObject * object)
|
||||
{
|
||||
PinosServerNode *node = PINOS_SERVER_NODE (object);
|
||||
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
|
||||
G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->constructed (object);
|
||||
|
||||
pinos_node_create_port (PINOS_NODE (node),
|
||||
PINOS_DIRECTION_INPUT,
|
||||
"input",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
on_input_port_created,
|
||||
node);
|
||||
}
|
||||
|
||||
static void
|
||||
sink_finalize (GObject * object)
|
||||
{
|
||||
PinosServerNode *node = PINOS_SERVER_NODE (object);
|
||||
PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object);
|
||||
PinosSpaAlsaSinkPrivate *priv = sink->priv;
|
||||
|
||||
destroy_pipeline (sink);
|
||||
pinos_node_remove_port (PINOS_NODE (node), priv->input);
|
||||
pinos_properties_free (priv->props);
|
||||
|
||||
G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_spa_alsa_sink_class_init (PinosSpaAlsaSinkClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
PinosNodeClass *node_class = PINOS_NODE_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (PinosSpaAlsaSinkPrivate));
|
||||
|
||||
gobject_class->constructed = sink_constructed;
|
||||
gobject_class->finalize = sink_finalize;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->set_property = set_property;
|
||||
|
||||
node_class->set_state = set_state;
|
||||
}
|
||||
|
||||
static void
|
||||
pinos_spa_alsa_sink_init (PinosSpaAlsaSink * sink)
|
||||
{
|
||||
PinosSpaAlsaSinkPrivate *priv;
|
||||
|
||||
priv = sink->priv = PINOS_SPA_ALSA_SINK_GET_PRIVATE (sink);
|
||||
priv->props = pinos_properties_new (NULL, NULL);
|
||||
}
|
||||
|
||||
PinosServerNode *
|
||||
pinos_spa_alsa_sink_new (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *properties)
|
||||
{
|
||||
PinosServerNode *node;
|
||||
|
||||
node = g_object_new (PINOS_TYPE_SPA_ALSA_SINK,
|
||||
"daemon", daemon,
|
||||
"name", name,
|
||||
"properties", properties,
|
||||
NULL);
|
||||
|
||||
return node;
|
||||
}
|
||||
61
pinos/modules/spa/spa-alsa-sink.h
Normal file
61
pinos/modules/spa/spa-alsa-sink.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PINOS_SPA_ALSA_SINK_H__
|
||||
#define __PINOS_SPA_ALSA_SINK_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <client/pinos.h>
|
||||
#include <server/server-node.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PINOS_TYPE_SPA_ALSA_SINK (pinos_spa_alsa_sink_get_type ())
|
||||
#define PINOS_IS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_ALSA_SINK))
|
||||
#define PINOS_IS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_ALSA_SINK))
|
||||
#define PINOS_SPA_ALSA_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass))
|
||||
#define PINOS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSink))
|
||||
#define PINOS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass))
|
||||
#define PINOS_SPA_ALSA_SINK_CAST(obj) ((PinosSpaAlsaSink*)(obj))
|
||||
#define PINOS_SPA_ALSA_SINK_CLASS_CAST(klass) ((PinosSpaAlsaSinkClass*)(klass))
|
||||
|
||||
typedef struct _PinosSpaAlsaSink PinosSpaAlsaSink;
|
||||
typedef struct _PinosSpaAlsaSinkClass PinosSpaAlsaSinkClass;
|
||||
typedef struct _PinosSpaAlsaSinkPrivate PinosSpaAlsaSinkPrivate;
|
||||
|
||||
struct _PinosSpaAlsaSink {
|
||||
PinosServerNode object;
|
||||
|
||||
PinosSpaAlsaSinkPrivate *priv;
|
||||
};
|
||||
|
||||
struct _PinosSpaAlsaSinkClass {
|
||||
PinosServerNodeClass parent_class;
|
||||
};
|
||||
|
||||
GType pinos_spa_alsa_sink_get_type (void);
|
||||
|
||||
PinosServerNode * pinos_spa_alsa_sink_new (PinosDaemon *daemon,
|
||||
const gchar *name,
|
||||
PinosProperties *properties);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PINOS_SPA_ALSA_SINK_H__ */
|
||||
|
|
@ -59,6 +59,10 @@ struct _PinosPort {
|
|||
*/
|
||||
struct _PinosPortClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*get_ringbuffer) (PinosPort *port,
|
||||
PinosProperties *props,
|
||||
GTask *task);
|
||||
};
|
||||
|
||||
typedef gboolean (*PinosBufferCallback) (PinosPort *port, PinosBuffer *buffer, GError **error, gpointer user_data);
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_BUFFER_H__
|
||||
#define __SPI_BUFFER_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _SpiBuffer SpiBuffer;
|
||||
|
||||
#include <spi/defs.h>
|
||||
|
||||
typedef enum {
|
||||
SPI_META_TYPE_INVALID = 0,
|
||||
SPI_META_TYPE_HEADER,
|
||||
} SpiMetaType;
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t seq;
|
||||
int64_t pts;
|
||||
int64_t dts_offset;
|
||||
} SpiMetaHeader;
|
||||
|
||||
typedef struct {
|
||||
SpiMetaType type;
|
||||
void *data;
|
||||
size_t size;
|
||||
} SpiMeta;
|
||||
|
||||
/**
|
||||
* SpiDataType:
|
||||
* @SPI_DATA_TYPE_INVALID: invalid data type, is ignored
|
||||
* @SPI_DATA_TYPE_MEMPTR: data and size point to memory
|
||||
* @SPI_DATA_TYPE_FD: data points to SpiDataFd
|
||||
* @SPI_DATA_TYPE_FD: data points to SpiDataFd
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_DATA_TYPE_INVALID = 0,
|
||||
SPI_DATA_TYPE_MEMPTR,
|
||||
SPI_DATA_TYPE_FD,
|
||||
} SpiDataType;
|
||||
|
||||
/**
|
||||
* SpiDataFd
|
||||
* fd: a file descriptor
|
||||
* offset: offset in the data referenced by @fd
|
||||
* @size: size of data referenced by fd
|
||||
*/
|
||||
typedef struct {
|
||||
int fd;
|
||||
unsigned int offset;
|
||||
size_t size;
|
||||
} SpiDataFD;
|
||||
|
||||
/**
|
||||
* SpiData:
|
||||
* @id: user id
|
||||
* @type: the type of data
|
||||
* @data: pointer to data
|
||||
* @size: size of data
|
||||
*/
|
||||
typedef struct {
|
||||
SpiDataType type;
|
||||
void *data;
|
||||
size_t size;
|
||||
} SpiData;
|
||||
|
||||
/**
|
||||
* SpiBuffer:
|
||||
* @refcount: reference counter
|
||||
* @notify: called when the refcount reaches 0
|
||||
* @size: total size of the buffer
|
||||
* @n_metas: number of metadata
|
||||
* @metas: array of @n_metas metadata
|
||||
* @n_datas: number of data pointers
|
||||
* @datas: array of @n_datas data pointers
|
||||
*/
|
||||
struct _SpiBuffer {
|
||||
volatile int refcount;
|
||||
SpiNotify notify;
|
||||
size_t size;
|
||||
unsigned int n_metas;
|
||||
SpiMeta *metas;
|
||||
unsigned int n_datas;
|
||||
SpiData *datas;
|
||||
};
|
||||
|
||||
static inline SpiBuffer *
|
||||
spi_buffer_ref (SpiBuffer *buffer)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
buffer->refcount++;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static inline SpiBuffer *
|
||||
spi_buffer_unref (SpiBuffer *buffer)
|
||||
{
|
||||
if (buffer != NULL) {
|
||||
if (--buffer->refcount == 0) {
|
||||
if (buffer->notify)
|
||||
buffer->notify (buffer);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_BUFFER_H__ */
|
||||
|
|
@ -1,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__ */
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_DEFS_H__
|
||||
#define __SPI_DEFS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum {
|
||||
SPI_RESULT_OK = 0,
|
||||
SPI_RESULT_ERROR = -1,
|
||||
SPI_RESULT_INACTIVE = -2,
|
||||
SPI_RESULT_NO_FORMAT = -3,
|
||||
SPI_RESULT_INVALID_COMMAND = -4,
|
||||
SPI_RESULT_INVALID_PORT = -5,
|
||||
SPI_RESULT_HAVE_ENOUGH_INPUT = -6,
|
||||
SPI_RESULT_NEED_MORE_INPUT = -7,
|
||||
SPI_RESULT_PORTS_CHANGED = -9,
|
||||
SPI_RESULT_FORMAT_CHANGED = -10,
|
||||
SPI_RESULT_PROPERTIES_CHANGED = -11,
|
||||
SPI_RESULT_NOT_IMPLEMENTED = -12,
|
||||
SPI_RESULT_INVALID_PARAM_ID = -13,
|
||||
SPI_RESULT_PARAM_UNSET = -14,
|
||||
SPI_RESULT_ENUM_END = -15,
|
||||
SPI_RESULT_WRONG_PARAM_TYPE = -16,
|
||||
SPI_RESULT_WRONG_PARAM_SIZE = -17,
|
||||
SPI_RESULT_INVALID_MEDIA_TYPE = -18,
|
||||
SPI_RESULT_INVALID_FORMAT_PARAMS = -19,
|
||||
SPI_RESULT_FORMAT_INCOMPLETE = -20,
|
||||
SPI_RESULT_INVALID_ARGUMENTS = -21,
|
||||
SPI_RESULT_UNKNOWN_INTERFACE = -22,
|
||||
} SpiResult;
|
||||
|
||||
typedef enum {
|
||||
SPI_DIRECTION_INVALID = 0,
|
||||
SPI_DIRECTION_INPUT,
|
||||
SPI_DIRECTION_OUTPUT
|
||||
} SpiDirection;
|
||||
|
||||
typedef void (*SpiNotify) (void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __SPI_DEFS_H__ */
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_EVENT_H__
|
||||
#define __SPI_EVENT_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _SpiEvent SpiEvent;
|
||||
|
||||
#include <spi/defs.h>
|
||||
|
||||
/**
|
||||
* SpiEventType:
|
||||
* @SPI_EVENT_TYPE_INVALID: invalid event, should be ignored
|
||||
* @SPI_EVENT_TYPE_ACTIVATED: emited when the ACTIVATE command completes
|
||||
* @SPI_EVENT_TYPE_DEACTIVATED: emited when the DEACTIVATE command completes
|
||||
* @SPI_EVENT_TYPE_CAN_PULL_OUTPUT: emited when an async node has output that can be pulled
|
||||
* @SPI_EVENT_TYPE_CAN_PUSH_INTPUT: emited when more data can be pushed to an async node
|
||||
* @SPI_EVENT_TYPE_PULL_INPUT: emited when data needs to be provided on an input
|
||||
* @SPI_EVENT_TYPE_ADD_POLL: emited when a pollfd should be added
|
||||
* @SPI_EVENT_TYPE_REMOVE_POLL: emited when a pollfd should be removed
|
||||
* @SPI_EVENT_TYPE_DRAINED: emited when DRAIN command completed
|
||||
* @SPI_EVENT_TYPE_MARKER: emited when MARK command completed
|
||||
* @SPI_EVENT_TYPE_ERROR: emited when error occured
|
||||
* @SPI_EVENT_TYPE_BUFFERING: emited when buffering is in progress
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_EVENT_TYPE_INVALID = 0,
|
||||
SPI_EVENT_TYPE_ACTIVATED,
|
||||
SPI_EVENT_TYPE_DEACTIVATED,
|
||||
SPI_EVENT_TYPE_CAN_PULL_OUTPUT,
|
||||
SPI_EVENT_TYPE_CAN_PUSH_INTPUT,
|
||||
SPI_EVENT_TYPE_PULL_INPUT,
|
||||
SPI_EVENT_TYPE_ADD_POLL,
|
||||
SPI_EVENT_TYPE_REMOVE_POLL,
|
||||
SPI_EVENT_TYPE_DRAINED,
|
||||
SPI_EVENT_TYPE_MARKER,
|
||||
SPI_EVENT_TYPE_ERROR,
|
||||
SPI_EVENT_TYPE_BUFFERING,
|
||||
} SpiEventType;
|
||||
|
||||
struct _SpiEvent {
|
||||
volatile int refcount;
|
||||
SpiNotify notify;
|
||||
SpiEventType type;
|
||||
uint32_t port_id;
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_EVENT_H__ */
|
||||
367
pinos/spi/node.h
367
pinos/spi/node.h
|
|
@ -1,367 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_NODE_H__
|
||||
#define __SPI_NODE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _SpiNode SpiNode;
|
||||
|
||||
#include <spi/defs.h>
|
||||
#include <spi/plugin.h>
|
||||
#include <spi/params.h>
|
||||
#include <spi/port.h>
|
||||
#include <spi/event.h>
|
||||
#include <spi/buffer.h>
|
||||
#include <spi/command.h>
|
||||
|
||||
/**
|
||||
* SpiInputFlags:
|
||||
* @SPI_INPUT_FLAG_NONE: no flag
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_INPUT_FLAG_NONE = 0,
|
||||
} SpiInputFlags;
|
||||
|
||||
/**
|
||||
* SpiInputInfo:
|
||||
* @port_id: the port id
|
||||
* @flags: extra flags
|
||||
* @buffer: a buffer
|
||||
*
|
||||
* Input information for a node.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t port_id;
|
||||
SpiInputFlags flags;
|
||||
SpiBuffer *buffer;
|
||||
SpiEvent *event;
|
||||
SpiResult status;
|
||||
} SpiInputInfo;
|
||||
|
||||
/**
|
||||
* SpiOutputFlags:
|
||||
* @SPI_OUTPUT_FLAG_NONE: no flag
|
||||
* @SPI_OUTPUT_FLAG_PULL: force a #SPI_EVENT_NEED_INPUT event on the
|
||||
* peer input ports when no data is available.
|
||||
* @SPI_OUTPUT_FLAG_DISCARD: discard the buffer data
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_OUTPUT_FLAG_NONE = 0,
|
||||
SPI_OUTPUT_FLAG_PULL = (1 << 0),
|
||||
SPI_OUTPUT_FLAG_DISCARD = (1 << 1),
|
||||
} SpiOutputFlags;
|
||||
|
||||
/**
|
||||
* SpiOutputInfo:
|
||||
* @port_id: the port id
|
||||
* @flags: extra flags
|
||||
* @buffer: a buffer
|
||||
* @event: an event
|
||||
*
|
||||
* Output information for a node.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t port_id;
|
||||
SpiOutputFlags flags;
|
||||
SpiBuffer *buffer;
|
||||
SpiEvent *event;
|
||||
SpiResult status;
|
||||
} SpiOutputInfo;
|
||||
|
||||
/**
|
||||
* SpiEventCallback:
|
||||
* @node: a #SpiHandle emiting the event
|
||||
* @event: the event that was emited
|
||||
* @user_data: user data provided when registering the callback
|
||||
*
|
||||
* This will be called when an out-of-bound event is notified
|
||||
* on @node.
|
||||
*/
|
||||
typedef void (*SpiEventCallback) (SpiHandle *handle,
|
||||
SpiEvent *event,
|
||||
void *user_data);
|
||||
|
||||
#define SPI_INTERFACE_ID_NODE 0
|
||||
#define SPI_INTERFACE_ID_NODE_NAME "Node interface"
|
||||
#define SPI_INTERFACE_ID_NODE_DESCRIPTION "Main processing node interface"
|
||||
|
||||
/**
|
||||
* SpiNode:
|
||||
*
|
||||
* The main processing nodes.
|
||||
*/
|
||||
struct _SpiNode {
|
||||
/* the total size of this node. This can be used to expand this
|
||||
* structure in the future */
|
||||
size_t size;
|
||||
/**
|
||||
* SpiNode::get_params:
|
||||
* @handle: a #SpiHandle
|
||||
* @props: a location for a #SpiParams pointer
|
||||
*
|
||||
* Get the configurable parameters of @node.
|
||||
*
|
||||
* The returned @props is a snapshot of the current configuration and
|
||||
* can be modified. The modifications will take effect after a call
|
||||
* to SpiNode::set_params.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or props are %NULL
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no properties
|
||||
* implemented on @node
|
||||
*/
|
||||
SpiResult (*get_params) (SpiHandle *handle,
|
||||
SpiParams **props);
|
||||
/**
|
||||
* SpiNode::set_params:
|
||||
* @handle: a #SpiHandle
|
||||
* @props: a #SpiParams
|
||||
*
|
||||
* Set the configurable parameters in @node.
|
||||
*
|
||||
* Usually, @props will be obtained from SpiNode::get_params and then
|
||||
* modified but it is also possible to set another #SpiParams object
|
||||
* as long as its keys and types match those of SpiParams::get_params.
|
||||
*
|
||||
* Properties with keys that are not known are ignored.
|
||||
*
|
||||
* If @props is NULL, all the parameters are reset to their defaults.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when no properties can be
|
||||
* modified on @node.
|
||||
* #SPI_RESULT_WRONG_PARAM_TYPE when a property has the wrong
|
||||
* type.
|
||||
*/
|
||||
SpiResult (*set_params) (SpiHandle *handle,
|
||||
const SpiParams *props);
|
||||
/**
|
||||
* SpiNode::send_command:
|
||||
* @handle: a #SpiHandle
|
||||
* @command: a #SpiCommand
|
||||
*
|
||||
* Send a command to @node.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or command is %NULL
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when this node can't process commands
|
||||
* #SPI_RESULT_INVALID_COMMAND @command is an invalid command
|
||||
*/
|
||||
SpiResult (*send_command) (SpiHandle *handle,
|
||||
SpiCommand *command);
|
||||
/**
|
||||
* SpiNode::set_event_callback:
|
||||
* @handle: a #SpiHandle
|
||||
* @callback: a callback
|
||||
* @user_data: user data passed in the callback
|
||||
*
|
||||
* Set a callback to receive events from @node. if @callback is %NULL, the
|
||||
* current callback is removed.
|
||||
*
|
||||
* The callback can be emited from any thread. The caller should take
|
||||
* appropriate actions to handle the event in other threads when needed.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||
*/
|
||||
SpiResult (*set_event_callback) (SpiHandle *handle,
|
||||
SpiEventCallback callback,
|
||||
void *user_data);
|
||||
/**
|
||||
* SpiNode::get_n_ports:
|
||||
* @handle: a #SpiHandle
|
||||
* @n_input_ports: location to hold the number of input ports or %NULL
|
||||
* @max_input_ports: location to hold the maximum number of input ports or %NULL
|
||||
* @n_output_ports: location to hold the number of output ports or %NULL
|
||||
* @max_output_ports: location to hold the maximum number of output ports or %NULL
|
||||
*
|
||||
* Get the current number of input and output ports and also the maximum
|
||||
* number of ports.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||
*/
|
||||
SpiResult (*get_n_ports) (SpiHandle *handle,
|
||||
unsigned int *n_input_ports,
|
||||
unsigned int *max_input_ports,
|
||||
unsigned int *n_output_ports,
|
||||
unsigned int *max_output_ports);
|
||||
/**
|
||||
* SpiNode::get_port_ids:
|
||||
* @handle: a #SpiHandle
|
||||
* @n_input_ports: size of the @input_ids array
|
||||
* @input_ids: array to store the input stream ids
|
||||
* @n_output_ports: size of the @output_ids array
|
||||
* @output_ids: array to store the output stream ids
|
||||
*
|
||||
* Get the current number of input and output ports and also the maximum
|
||||
* number of ports.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||
*/
|
||||
SpiResult (*get_port_ids) (SpiHandle *handle,
|
||||
unsigned int n_input_ports,
|
||||
uint32_t *input_ids,
|
||||
unsigned int n_output_ports,
|
||||
uint32_t *output_ids);
|
||||
|
||||
SpiResult (*add_port) (SpiHandle *handle,
|
||||
SpiDirection direction,
|
||||
uint32_t *port_id);
|
||||
SpiResult (*remove_port) (SpiHandle *handle,
|
||||
uint32_t port_id);
|
||||
|
||||
/**
|
||||
* SpiNode::enum_port_formats:
|
||||
* @handle: a #SpiHandle
|
||||
* @port_id: the port to query
|
||||
* @index: the format index to retrieve
|
||||
* @format: pointer to a format
|
||||
*
|
||||
* Enumerate all possible formats on @port_id of @node.
|
||||
*
|
||||
* Use the index to retrieve the formats one by one until the function
|
||||
* returns #SPI_RESULT_ENUM_END.
|
||||
*
|
||||
* The result format can be queried and modified and ultimately be used
|
||||
* to call SpiNode::set_port_format.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or format is %NULL
|
||||
* #SPI_RESULT_INVALID_PORT when port_id is not valid
|
||||
* #SPI_RESULT_ENUM_END when no format exists for @index
|
||||
*
|
||||
*/
|
||||
SpiResult (*enum_port_formats) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
unsigned int index,
|
||||
SpiParams **format);
|
||||
/**
|
||||
* SpiNode::set_port_format:
|
||||
* @handle: a #SpiHandle
|
||||
* @port_id: the port to configure
|
||||
* @format: a #SpiParam with the format
|
||||
*
|
||||
* Set a format on @port_id of @node.
|
||||
*
|
||||
* When @format is %NULL, the current format will be removed.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node is %NULL
|
||||
* #SPI_RESULT_INVALID_PORT when port_id is not valid
|
||||
* #SPI_RESULT_INVALID_MEDIA_TYPE when the media type is not valid
|
||||
* #SPI_RESULT_INVALID_FORMAT_PARAMS when one of the mandatory format
|
||||
* parameters is not specified.
|
||||
* #SPI_RESULT_WRONG_PARAM_TYPE when the type of size of a parameter
|
||||
* is not correct.
|
||||
*/
|
||||
SpiResult (*set_port_format) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
int test_only,
|
||||
const SpiParams *format);
|
||||
/**
|
||||
* SpiNode::get_port_format:
|
||||
* @handle: a #SpiHandle
|
||||
* @port_id: the port to query
|
||||
* @format: a pointer to a location to hold the #SpiParam
|
||||
*
|
||||
* Get the format on @port_id of @node. The result #SpiParam can
|
||||
* not be modified.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when @node or @format is %NULL
|
||||
* #SPI_RESULT_INVALID_PORT when @port_id is not valid
|
||||
* #SPI_RESULT_INVALID_NO_FORMAT when no format was set
|
||||
*/
|
||||
SpiResult (*get_port_format) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
const SpiParams **format);
|
||||
|
||||
SpiResult (*get_port_info) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
SpiPortInfo *info);
|
||||
|
||||
SpiResult (*get_port_params) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
SpiParams **params);
|
||||
SpiResult (*set_port_params) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
const SpiParams *params);
|
||||
|
||||
SpiResult (*get_port_status) (SpiHandle *handle,
|
||||
uint32_t port_id,
|
||||
SpiPortStatus *status);
|
||||
|
||||
/**
|
||||
* SpiNode::push_port_input:
|
||||
* @handle: a #SpiHandle
|
||||
* @n_info: number of #SpiInputInfo in @info
|
||||
* @info: array of #SpiInputInfo
|
||||
*
|
||||
* Push a buffer and/or an event into one or more input ports of
|
||||
* @node.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
||||
* #SPI_RESULT_ERROR when one or more of the @info has an
|
||||
* error result. Check the status of all the
|
||||
* @info.
|
||||
* #SPI_RESULT_HAVE_ENOUGH_INPUT when output can be produced.
|
||||
*/
|
||||
SpiResult (*push_port_input) (SpiHandle *handle,
|
||||
unsigned int n_info,
|
||||
SpiInputInfo *info);
|
||||
/**
|
||||
* SpiNode::pull_port_output:
|
||||
* @handle: a #SpiHandle
|
||||
* @n_info: number of #SpiOutputInfo in @info
|
||||
* @info: array of #SpiOutputInfo
|
||||
*
|
||||
* Pull a buffer and/or an event from one or more output ports of
|
||||
* @node.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL
|
||||
* #SPI_RESULT_PORTS_CHANGED the number of ports has changed. None
|
||||
* of the @info fields are modified
|
||||
* #SPI_RESULT_FORMAT_CHANGED a format changed on some port.
|
||||
* the ports that changed are marked in the status.
|
||||
* #SPI_RESULT_PROPERTIES_CHANGED port properties changed. The
|
||||
* changed ports are marked in the status.
|
||||
* #SPI_RESULT_ERROR when one or more of the @info has an
|
||||
* error result. Check the status of all the @info.
|
||||
* #SPI_RESULT_NEED_MORE_INPUT when no output can be produced
|
||||
* because more input is needed.
|
||||
*/
|
||||
SpiResult (*pull_port_output) (SpiHandle *handle,
|
||||
unsigned int n_info,
|
||||
SpiOutputInfo *info);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_NODE_H__ */
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_PARAMS_H__
|
||||
#define __SPI_PARAMS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _SpiParams SpiParams;
|
||||
|
||||
#include <spi/defs.h>
|
||||
|
||||
/**
|
||||
* SpiParamType:
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_PARAM_TYPE_INVALID = 0,
|
||||
SPI_PARAM_TYPE_BOOL,
|
||||
SPI_PARAM_TYPE_INT8,
|
||||
SPI_PARAM_TYPE_UINT8,
|
||||
SPI_PARAM_TYPE_INT16,
|
||||
SPI_PARAM_TYPE_UINT16,
|
||||
SPI_PARAM_TYPE_INT32,
|
||||
SPI_PARAM_TYPE_UINT32,
|
||||
SPI_PARAM_TYPE_INT64,
|
||||
SPI_PARAM_TYPE_UINT64,
|
||||
SPI_PARAM_TYPE_FLOAT,
|
||||
SPI_PARAM_TYPE_DOUBLE,
|
||||
SPI_PARAM_TYPE_STRING,
|
||||
SPI_PARAM_TYPE_POINTER,
|
||||
SPI_PARAM_TYPE_FRACTION,
|
||||
SPI_PARAM_TYPE_BITMASK,
|
||||
SPI_PARAM_TYPE_BYTES,
|
||||
} SpiParamType;
|
||||
|
||||
/**
|
||||
* SpiParamFlags:
|
||||
* @SPI_PARAM_FLAG_NONE: no flags
|
||||
* @SPI_PARAM_FLAG_OPTIONAL: the value can be left unset
|
||||
* @SPI_PARAM_FLAG_READABLE: param is readable
|
||||
* @SPI_PARAM_FLAG_WRITABLE: param is writable
|
||||
* @SPI_PARAM_FLAG_READWRITE: param is readable and writable
|
||||
* @SPI_PARAM_FLAG_DEPRECATED: param is deprecated and should not be used
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_PARAM_FLAG_NONE = 0,
|
||||
SPI_PARAM_FLAG_OPTIONAL = (1 << 0),
|
||||
SPI_PARAM_FLAG_READABLE = (1 << 1),
|
||||
SPI_PARAM_FLAG_WRITABLE = (1 << 2),
|
||||
SPI_PARAM_FLAG_READWRITE = SPI_PARAM_FLAG_READABLE | SPI_PARAM_FLAG_WRITABLE,
|
||||
SPI_PARAM_FLAG_DEPRECATED = (1 << 3),
|
||||
} SpiParamFlags;
|
||||
|
||||
/* SpiParamRangeType:
|
||||
* @SPI_PARAM_RANGE_TYPE_NONE: no range specified, full range of type applies
|
||||
* @SPI_PARAM_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max
|
||||
* @SPI_PARAM_RANGE_TYPE_ENUM: range contains enum of possible values with
|
||||
* NULL-terminated name
|
||||
* @SPI_PARAM_RANGE_TYPE_FLAGS: range contains flags of possible values with
|
||||
* NULL-terminated name
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_PARAM_RANGE_TYPE_NONE = 0,
|
||||
SPI_PARAM_RANGE_TYPE_MIN_MAX,
|
||||
SPI_PARAM_RANGE_TYPE_ENUM,
|
||||
SPI_PARAM_RANGE_TYPE_FLAGS,
|
||||
} SpiParamRangeType;
|
||||
|
||||
/**
|
||||
* SpiParamRangeInfo:
|
||||
* @name: name of this value
|
||||
* @description: user visible description of this value
|
||||
* @size: the size of value
|
||||
* @value: a possible value
|
||||
*/
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *description;
|
||||
size_t size;
|
||||
const void *value;
|
||||
} SpiParamRangeInfo;
|
||||
|
||||
/**
|
||||
* SpiParamInfo:
|
||||
* @id: unique id
|
||||
* @name: human readable name
|
||||
* @description: description of the param
|
||||
* @flags: param flags
|
||||
* @type: param type
|
||||
* @max_size: maximum size of param value
|
||||
* @default_size: size of default value
|
||||
* @default_value: default value of param
|
||||
* @range_type: type of the range values
|
||||
* @range_values: array of possible values
|
||||
* @tags: extra tags, NULL terminated
|
||||
* @priv: extra private data
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
const char *name;
|
||||
const char *description;
|
||||
SpiParamFlags flags;
|
||||
SpiParamType type;
|
||||
size_t maxsize;
|
||||
size_t default_size;
|
||||
const void *default_value;
|
||||
SpiParamRangeType range_type;
|
||||
const SpiParamRangeInfo *range_values;
|
||||
const char **tags;
|
||||
const void *priv;
|
||||
} SpiParamInfo;
|
||||
|
||||
/**
|
||||
* SpiParams:
|
||||
*
|
||||
* Generic parameters.
|
||||
*/
|
||||
struct _SpiParams {
|
||||
/**
|
||||
* SpiParams::get_param_info:
|
||||
* @params: a #SpiParams
|
||||
* @idx: the param index
|
||||
* @info: pointer to a result #SpiParamInfo
|
||||
*
|
||||
* Gets the information about the parameter at @idx in @params.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success.
|
||||
* #SPI_RESULT_ENM_END when there is no param info
|
||||
* at @idx. This can be used to iterate the @params.
|
||||
*/
|
||||
SpiResult (*enum_param_info) (const SpiParams *params,
|
||||
unsigned int idx,
|
||||
const SpiParamInfo **infos);
|
||||
/**
|
||||
* SpiParams::set_param
|
||||
* @params: a #SpiParams
|
||||
* @id: the param id
|
||||
* @type: the value type to set
|
||||
* @size: the value size
|
||||
* @value: the value to set
|
||||
*
|
||||
* Sets @value in @param. @type should match the type specified
|
||||
* in the #SpiParamInfo for @id or else #SPI_RESULT_WRONG_PARAM_TYPE
|
||||
* is returned.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success.
|
||||
* #SPI_RESULT_INVALID_PARAM_ID when @id is not valid
|
||||
* #SPI_RESULT_WRONG_PARAM_TYPE when @type is not correct
|
||||
*/
|
||||
SpiResult (*set_param) (SpiParams *params,
|
||||
uint32_t id,
|
||||
SpiParamType type,
|
||||
size_t size,
|
||||
const void *value);
|
||||
/**
|
||||
* SpiParams::get_param
|
||||
* @params: a #SpiParams
|
||||
* @id: the param id
|
||||
* @type: a location for the value type
|
||||
* @size: a location for the value size
|
||||
* @value: a location for the value pointer
|
||||
*
|
||||
* Get the type, size and value of the parameter with @id.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success.
|
||||
* #SPI_RESULT_INVALID_PARAM_ID when @id is not valid
|
||||
* #SPI_RESULT_PARAM_UNSET when no value has been set yet
|
||||
*/
|
||||
SpiResult (*get_param) (const SpiParams *params,
|
||||
uint32_t id,
|
||||
SpiParamType *type,
|
||||
size_t *size,
|
||||
const void **value);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_PARAMS_H__ */
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/* Simple Plugin Interface
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_PLUGIN_H__
|
||||
#define __SPI_PLUGIN_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <spi/defs.h>
|
||||
#include <spi/params.h>
|
||||
|
||||
typedef struct _SpiHandle SpiHandle;
|
||||
typedef struct _SpiHandleFactory SpiHandleFactory;
|
||||
|
||||
struct _SpiHandle {
|
||||
/* user_data that can be set by the application */
|
||||
void * user_data;
|
||||
/**
|
||||
* SpiHandle::get_interface:
|
||||
* @handle: a #SpiHandle
|
||||
* @interface_id: the interface id
|
||||
* @interface: result to hold the interface.
|
||||
*
|
||||
* Get the interface provided by @handle with @interface_id.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no extensions
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
||||
*/
|
||||
SpiResult (*get_interface) (SpiHandle *handle,
|
||||
uint32_t interface_id,
|
||||
void **interface);
|
||||
};
|
||||
|
||||
/**
|
||||
* SpiInterfaceInfo:
|
||||
* @interface_id: the id of the interface, can be used to get the interface
|
||||
* @name: name of the interface
|
||||
* @description: Human readable description of the interface.
|
||||
*
|
||||
* This structure lists the information about available interfaces on
|
||||
* handles.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t interface_id;
|
||||
const char *name;
|
||||
const char *description;
|
||||
} SpiInterfaceInfo;
|
||||
|
||||
struct _SpiHandleFactory {
|
||||
/**
|
||||
* SpiHandleFactory::name
|
||||
*
|
||||
* The name
|
||||
*/
|
||||
const char * name;
|
||||
/**
|
||||
* SpiHandleFactory::info
|
||||
*
|
||||
* Extra information about the handles of this factory.
|
||||
*/
|
||||
const SpiParams * info;
|
||||
|
||||
/**
|
||||
* SpiHandleFactory::instantiate
|
||||
* @factory: a #SpiHandleFactory
|
||||
* @handle: a pointer to hold the result
|
||||
*
|
||||
* Make an instance of this factory.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when an instance can't be made
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when factory or handle are %NULL
|
||||
*/
|
||||
SpiResult (*instantiate) (SpiHandleFactory *factory,
|
||||
SpiHandle **handle);
|
||||
/**
|
||||
* SpiHandle::enum_interface_info:
|
||||
* @factory: a #SpiHandleFactory
|
||||
* @index: the interface index
|
||||
* @info: result to hold SpiInterfaceInfo.
|
||||
*
|
||||
* Get the interface information at @index.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_NOT_IMPLEMENTED when there are no interfaces
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when handle or info is %NULL
|
||||
* #SPI_RESULT_ENUM_END when there are no more infos
|
||||
*/
|
||||
SpiResult (*enum_interface_info) (SpiHandleFactory *factory,
|
||||
unsigned int index,
|
||||
const SpiInterfaceInfo **info);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* spi_enum_handle_factory:
|
||||
* @index: the index to enumerate
|
||||
* @factory: a location to hold the factory result
|
||||
*
|
||||
* The main entry point in a plugin.
|
||||
*
|
||||
* Returns: #SPI_RESULT_OK on success
|
||||
* #SPI_RESULT_INVALID_ARGUMENTS when factory is %NULL
|
||||
* #SPI_RESULT_ENUM_END when there are no more factories
|
||||
*/
|
||||
SpiResult spi_enum_handle_factory (unsigned int index,
|
||||
const SpiHandleFactory **factory);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_PLUGIN_H__ */
|
||||
|
|
@ -1,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__ */
|
||||
|
|
@ -1,479 +0,0 @@
|
|||
/* Pinos
|
||||
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <spi/node.h>
|
||||
#include "spi-plugins.h"
|
||||
|
||||
typedef struct {
|
||||
SpiNode *src_node;
|
||||
SpiNode *sink_node;
|
||||
SpiHandle *src;
|
||||
SpiHandle *sink;
|
||||
} AppData;
|
||||
|
||||
static void
|
||||
print_value (const char *prefix, SpiParamType type, int size, const void *value)
|
||||
{
|
||||
printf ("%s", prefix);
|
||||
switch (type) {
|
||||
case SPI_PARAM_TYPE_INVALID:
|
||||
printf ("invalid");
|
||||
break;
|
||||
case SPI_PARAM_TYPE_BOOL:
|
||||
printf ("%s", *(bool *)value ? "true" : "false");
|
||||
break;
|
||||
case SPI_PARAM_TYPE_INT8:
|
||||
printf ("%" PRIi8, *(int8_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_UINT8:
|
||||
printf ("%" PRIu8, *(uint8_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_INT16:
|
||||
printf ("%" PRIi16, *(int16_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_UINT16:
|
||||
printf ("%" PRIu16, *(uint16_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_INT32:
|
||||
printf ("%" PRIi32, *(int32_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_UINT32:
|
||||
printf ("%" PRIu32, *(uint32_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_INT64:
|
||||
printf ("%" PRIi64 "\n", *(int64_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_UINT64:
|
||||
printf ("%" PRIu64 "\n", *(uint64_t *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_FLOAT:
|
||||
printf ("%f", *(float *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_DOUBLE:
|
||||
printf ("%g", *(double *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_STRING:
|
||||
printf ("%s", (char *)value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_POINTER:
|
||||
printf ("%p", value);
|
||||
break;
|
||||
case SPI_PARAM_TYPE_FRACTION:
|
||||
break;
|
||||
case SPI_PARAM_TYPE_BITMASK:
|
||||
break;
|
||||
case SPI_PARAM_TYPE_BYTES:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_params (const SpiParams *params, int print_ranges)
|
||||
{
|
||||
SpiResult res;
|
||||
const SpiParamInfo *info;
|
||||
int i, j;
|
||||
SpiParamType type;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
const void *value;
|
||||
size_t size;
|
||||
|
||||
if ((res = params->enum_param_info (params, i, &info)) < 0) {
|
||||
if (res != SPI_RESULT_ENUM_END)
|
||||
printf ("got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("id:\t\t%d\n", info->id);
|
||||
printf ("name:\t\t%s\n", info->name);
|
||||
printf ("description:\t%s\n", info->description);
|
||||
printf ("flags:\t\t%d\n", info->flags);
|
||||
printf ("type:\t\t%d\n", info->type);
|
||||
printf ("maxsize:\t%zu\n", info->maxsize);
|
||||
|
||||
res = params->get_param (params, info->id, &type, &size, &value);
|
||||
if (res == SPI_RESULT_PARAM_UNSET)
|
||||
printf ("value:\t\tunset\n");
|
||||
else
|
||||
print_value ("value:\t\t", type, size, value);
|
||||
|
||||
if (print_ranges) {
|
||||
if (info->default_value)
|
||||
print_value ("default:\t", info->type, info->default_size, info->default_value);
|
||||
else
|
||||
printf ("default:\tunset\n");
|
||||
|
||||
printf ("range_type:\t%d\n", info->range_type);
|
||||
if (info->range_values) {
|
||||
for (j = 0; info->range_values[j].name; j++) {
|
||||
const SpiParamRangeInfo *rinfo = &info->range_values[j];
|
||||
printf (" name:\t%s\n", rinfo->name);
|
||||
printf (" description:\t%s\n", rinfo->description);
|
||||
print_value (" value:\t", info->type, rinfo->size, rinfo->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info->tags) {
|
||||
for (j = 0; info->tags[j]; j++) {
|
||||
printf ("tag:\t%s\n", info->tags[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
inspect_node (SpiNode *node, SpiHandle *handle)
|
||||
{
|
||||
SpiResult res;
|
||||
SpiParams *params;
|
||||
unsigned int n_input, max_input, n_output, max_output, i;
|
||||
SpiParams *format;
|
||||
|
||||
if ((res = node->get_params (handle, ¶ms)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
else
|
||||
print_params (params, 1);
|
||||
|
||||
if ((res = node->get_n_ports (handle, &n_input, &max_input, &n_output, &max_output)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
else
|
||||
printf ("supported ports %d %d %d %d\n", n_input, max_input, n_output, max_output);
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if ((res = node->enum_port_formats (handle, 0, i, &format)) < 0) {
|
||||
if (res != SPI_RESULT_ENUM_END)
|
||||
printf ("got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
print_params (format, 1);
|
||||
}
|
||||
if ((res = node->get_port_params (handle, 0, ¶ms)) < 0)
|
||||
printf ("get_port_params error: %d\n", res);
|
||||
else
|
||||
printf ("got params %p\n", params);
|
||||
}
|
||||
|
||||
static void
|
||||
set_format (AppData *data)
|
||||
{
|
||||
SpiParams *format;
|
||||
SpiResult res;
|
||||
uint32_t val;
|
||||
|
||||
if ((res = data->src_node->enum_port_formats (data->src, 0, 0, &format)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
|
||||
printf ("setting format\n");
|
||||
if ((res = format->set_param (format, 1, SPI_PARAM_TYPE_STRING, 5, "S16LE")) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
val = 1;
|
||||
if ((res = format->set_param (format, 2, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
val = 44100;
|
||||
if ((res = format->set_param (format, 3, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
val = 2;
|
||||
if ((res = format->set_param (format, 4, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
|
||||
if ((res = data->src_node->set_port_format (data->src, 0, 0, format)) < 0)
|
||||
printf ("set format failed: %d\n", res);
|
||||
if ((res = data->sink_node->set_port_format (data->sink, 0, 0, format)) < 0)
|
||||
printf ("set format failed: %d\n", res);
|
||||
}
|
||||
|
||||
typedef struct _MyBuffer MyBuffer;
|
||||
|
||||
struct _MyBuffer {
|
||||
SpiBuffer buffer;
|
||||
SpiMeta meta[1];
|
||||
SpiMetaHeader header;
|
||||
SpiData data[1];
|
||||
MyBuffer *next;
|
||||
uint16_t samples[4096];
|
||||
};
|
||||
|
||||
#if 0
|
||||
static MyBuffer my_buffers[4];
|
||||
static MyBuffer *free_list = NULL;
|
||||
|
||||
static void
|
||||
my_buffer_notify (MyBuffer *buffer)
|
||||
{
|
||||
printf ("free buffer %p\n", buffer);
|
||||
buffer->next = free_list;
|
||||
free_list = buffer;
|
||||
}
|
||||
|
||||
static SpiResult
|
||||
setup_buffers (SpiNode *node)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
my_buffers[i].buffer.refcount = 0;
|
||||
my_buffers[i].buffer.notify = (SpiNotify) my_buffer_notify;
|
||||
my_buffers[i].buffer.size = sizeof (MyBuffer);
|
||||
my_buffers[i].buffer.n_metas = 1;
|
||||
my_buffers[i].buffer.metas = my_buffers[i].meta;
|
||||
my_buffers[i].buffer.n_datas = 1;
|
||||
my_buffers[i].buffer.datas = my_buffers[i].data;
|
||||
|
||||
my_buffers[i].header.flags = 0;
|
||||
my_buffers[i].header.seq = 0;
|
||||
my_buffers[i].header.pts = 0;
|
||||
my_buffers[i].header.dts_offset = 0;
|
||||
|
||||
my_buffers[i].meta[0].type = SPI_META_TYPE_HEADER;
|
||||
my_buffers[i].meta[0].data = &my_buffers[i].header;
|
||||
my_buffers[i].meta[0].size = sizeof (my_buffers[i].header);
|
||||
|
||||
my_buffers[i].data[0].type = SPI_DATA_TYPE_MEMPTR;
|
||||
my_buffers[i].data[0].data = my_buffers[i].samples;
|
||||
my_buffers[i].data[0].size = sizeof (my_buffers[i].samples);
|
||||
|
||||
my_buffers[i].next = free_list;
|
||||
free_list = &my_buffers[i];
|
||||
}
|
||||
return SPI_RESULT_OK;
|
||||
}
|
||||
|
||||
static SpiResult
|
||||
push_input (SpiNode *node)
|
||||
{
|
||||
SpiResult res;
|
||||
SpiDataInfo info;
|
||||
MyBuffer *mybuf;
|
||||
|
||||
mybuf = free_list;
|
||||
free_list = mybuf->next;
|
||||
|
||||
printf ("alloc input buffer %p\n", mybuf);
|
||||
mybuf->buffer.refcount = 1;
|
||||
|
||||
info.port_id = 0;
|
||||
info.flags = SPI_DATA_FLAG_NONE;
|
||||
info.buffer = &mybuf->buffer;
|
||||
info.event = NULL;
|
||||
|
||||
res = node->send_port_data (node, &info);
|
||||
|
||||
spi_buffer_unref (&mybuf->buffer);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static SpiResult
|
||||
pull_output (SpiNode *node)
|
||||
{
|
||||
SpiDataInfo info[1] = { { 0, }, };
|
||||
SpiResult res;
|
||||
MyBuffer *mybuf;
|
||||
SpiBuffer *buf;
|
||||
|
||||
mybuf = free_list;
|
||||
free_list = mybuf->next;
|
||||
|
||||
printf ("alloc output buffer %p\n", mybuf);
|
||||
mybuf->buffer.refcount = 1;
|
||||
|
||||
info[0].port_id = 1;
|
||||
info[0].buffer = &mybuf->buffer;
|
||||
info[0].event = NULL;
|
||||
|
||||
res = node->receive_port_data (node, 1, info);
|
||||
|
||||
buf = info[0].buffer;
|
||||
spi_buffer_unref (buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
run_volume (SpiNode *node)
|
||||
{
|
||||
int state;
|
||||
SpiResult res;
|
||||
|
||||
set_params (node);
|
||||
set_format (node);
|
||||
|
||||
state = 0;
|
||||
while (TRUE) {
|
||||
SpiPortStatus status;
|
||||
|
||||
if (state == 0) {
|
||||
if ((res = push_input (node)) < 0) {
|
||||
if (res == SPI_RESULT_HAVE_ENOUGH_INPUT)
|
||||
state = 1;
|
||||
else {
|
||||
printf ("got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((res = node->get_port_status (node, 1, &status)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
else if (status.flags & SPI_PORT_STATUS_FLAG_HAVE_OUTPUT)
|
||||
state = 1;
|
||||
}
|
||||
if (state == 1) {
|
||||
if ((res = pull_output (node)) < 0) {
|
||||
if (res == SPI_RESULT_NEED_MORE_INPUT)
|
||||
state = 0;
|
||||
else {
|
||||
printf ("got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((res = node->get_port_status (node, 0, &status)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
else if (status.flags & SPI_PORT_STATUS_FLAG_NEED_INPUT)
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
on_event (SpiHandle *handle, SpiEvent *event, void *user_data)
|
||||
{
|
||||
AppData *data = user_data;
|
||||
|
||||
switch (event->type) {
|
||||
case SPI_EVENT_TYPE_PULL_INPUT:
|
||||
{
|
||||
SpiBuffer *buf;
|
||||
SpiInputInfo iinfo;
|
||||
SpiOutputInfo oinfo;
|
||||
SpiResult res;
|
||||
|
||||
buf = event->data;
|
||||
|
||||
oinfo.port_id = event->port_id;
|
||||
oinfo.flags = SPI_OUTPUT_FLAG_NONE;
|
||||
oinfo.buffer = buf;
|
||||
oinfo.event = NULL;
|
||||
|
||||
if ((res = data->src_node->pull_port_output (data->src, 1, &oinfo)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
|
||||
iinfo.port_id = 0;
|
||||
iinfo.flags = SPI_INPUT_FLAG_NONE;
|
||||
iinfo.buffer = oinfo.buffer;
|
||||
iinfo.event = oinfo.event;
|
||||
|
||||
if ((res = data->sink_node->push_port_input (data->sink, 1, &iinfo)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf ("got event %d\n", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run_async_sink (AppData *data)
|
||||
{
|
||||
SpiResult res;
|
||||
SpiCommand cmd;
|
||||
|
||||
set_format (data);
|
||||
|
||||
cmd.type = SPI_COMMAND_START;
|
||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
|
||||
printf ("sleeping for 10 seconds\n");
|
||||
sleep (10);
|
||||
|
||||
cmd.type = SPI_COMMAND_STOP;
|
||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_source (AppData *data)
|
||||
{
|
||||
SpiCommand cmd;
|
||||
SpiResult res;
|
||||
|
||||
data->src = spi_audiotestsrc_new ();
|
||||
data->src->get_interface (data->src, SPI_INTERFACE_ID_NODE, (void **)&data->src_node);
|
||||
|
||||
cmd.type = SPI_COMMAND_ACTIVATE;
|
||||
if ((res = data->src_node->send_command (data->src, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_sink (AppData *data)
|
||||
{
|
||||
SpiCommand cmd;
|
||||
SpiResult res;
|
||||
SpiParams *params;
|
||||
|
||||
data->sink = spi_alsa_sink_new ();
|
||||
data->sink->get_interface (data->sink, SPI_INTERFACE_ID_NODE, (void **)&data->sink_node);
|
||||
|
||||
data->sink_node->set_event_callback (data->sink, on_event, data);
|
||||
|
||||
if ((res = data->sink_node->get_params (data->sink, ¶ms)) < 0)
|
||||
printf ("got get_params error %d\n", res);
|
||||
|
||||
params->set_param (params, 0, SPI_PARAM_TYPE_STRING, strlen ("hw:1")+1, "hw:1");
|
||||
|
||||
if ((res = data->sink_node->set_params (data->sink, params)) < 0)
|
||||
printf ("got set_params error %d\n", res);
|
||||
|
||||
cmd.type = SPI_COMMAND_ACTIVATE;
|
||||
if ((res = data->sink_node->send_command (data->sink, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
SpiResult res;
|
||||
SpiCommand cmd;
|
||||
AppData data;
|
||||
|
||||
setup_source (&data);
|
||||
setup_sink (&data);
|
||||
|
||||
run_async_sink (&data);
|
||||
|
||||
cmd.type = SPI_COMMAND_DEACTIVATE;
|
||||
if ((res = data.sink_node->send_command (data.sink, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
cmd.type = SPI_COMMAND_DEACTIVATE;
|
||||
if ((res = data.src_node->send_command (data.src, &cmd)) < 0)
|
||||
printf ("got error %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue