summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-06-29 16:48:37 +0000
committerLennart Poettering <lennart@poettering.net>2004-06-29 16:48:37 +0000
commitef422fa4ae626e9638ca70d1c56f27e701dd69c2 (patch)
tree1dd62aee8ec44f3d3c7d5f4bacb3d2b38769e5b3
parenta74cd2a1bd92eac6a4140d0794ac4b557be6c133 (diff)
esound protocol
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@40 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r--src/Makefile.am31
-rw-r--r--src/client.c6
-rw-r--r--src/client.h2
-rw-r--r--src/esound-spec.h191
-rw-r--r--src/main.c9
-rw-r--r--src/module-pipe-sink.c2
-rw-r--r--src/module-protocol-stub.c39
-rw-r--r--src/oss-util.c6
-rw-r--r--src/pacat.c2
-rw-r--r--src/protocol-esound.c462
-rw-r--r--src/protocol-esound.h12
-rw-r--r--src/protocol-native.c1
-rw-r--r--src/protocol-simple.c3
-rw-r--r--src/sample-util.c18
-rw-r--r--src/sample.c26
-rw-r--r--src/sample.h17
-rw-r--r--src/util.c23
-rw-r--r--src/util.h2
18 files changed, 806 insertions, 46 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index a67f395e..af4478be 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -29,7 +29,8 @@ pkglib_LTLIBRARIES=libiochannel.la libsocket-server.la libsocket-client.la \
libprotocol-cli.la module-cli-protocol-unix.la libtagstruct.la \
libpdispatch.la libprotocol-native.la libpstream-util.la \
module-native-protocol-tcp.la module-native-protocol-unix.la \
- libpolyp.la
+ libpolyp.la libprotocol-esound.la module-esound-protocol-unix.la \
+ module-esound-protocol-tcp.la
polypaudio_SOURCES = idxset.c idxset.h \
queue.c queue.h \
@@ -113,40 +114,54 @@ libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la
libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h
libprotocol_native_la_LDFLAGS = -avoid-version
-libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la
+libprotocol_native_la_LIBADD = libsocket-server.la libiochannel.la libpacket.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la
libtagstruct_la_SOURCES = tagstruct.c tagstruct.h
libtagstruct_la_LDFLAGS = -avoid-version
+libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h protocol-esound-spec.h
+libprotocol_esound_la_LDFLAGS = -avoid-version
+libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la
+
module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la
+module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libsocket-server.la
module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c
module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libiochannel.la
+module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libsocket-server.la
module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libiochannel.la
+module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libsocket-server.la
module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c
module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libiochannel.la
+module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libsocket-server.la
module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c
module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libiochannel.la libtagstruct.la
+module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libsocket-server.la
module_native_protocol_unix_la_SOURCES = module-protocol-stub.c
module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_native_protocol_unix_la_LIBADD = libprotocol-native.la libiochannel.la libtagstruct.la
+module_native_protocol_unix_la_LIBADD = libprotocol-native.la libsocket-server.la
+
+module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c
+module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_tcp_la_LIBADD = libprotocol-esound.la libsocket-server.la
+
+module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c
+module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
+module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
+module_esound_protocol_unix_la_LIBADD = libprotocol-esound.la libsocket-server.la
module_pipe_sink_la_SOURCES = module-pipe-sink.c
module_pipe_sink_la_LDFLAGS = -module -avoid-version
diff --git a/src/client.c b/src/client.c
index 4c8a0491..37691283 100644
--- a/src/client.c
+++ b/src/client.c
@@ -60,3 +60,9 @@ char *client_list_to_string(struct core *c) {
return strbuf_tostring_free(s);
}
+
+void client_rename(struct client *c, const char *name) {
+ assert(c);
+ free(c->name);
+ c->name = name ? strdup(name) : NULL;
+}
diff --git a/src/client.h b/src/client.h
index 48172051..39167ee7 100644
--- a/src/client.h
+++ b/src/client.h
@@ -25,4 +25,6 @@ void client_kill(struct client *c);
char *client_list_to_string(struct core *c);
+void client_rename(struct client *c, const char *name);
+
#endif
diff --git a/src/esound-spec.h b/src/esound-spec.h
new file mode 100644
index 00000000..1c2dc022
--- /dev/null
+++ b/src/esound-spec.h
@@ -0,0 +1,191 @@
+#ifndef fooesoundhfoo
+#define fooesoundhfoo
+
+/* Most of the following is blatantly stolen from esound. */
+
+
+/* path and name of the default EsounD domain socket */
+#define ESD_UNIX_SOCKET_DIR "/tmp/.esd"
+#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket"
+
+/* length of the audio buffer size */
+#define ESD_BUF_SIZE (4 * 1024)
+/* maximum size we can write(). Otherwise we might overflow */
+#define ESD_MAX_WRITE_SIZE (21 * 4096)
+
+/* length of the authorization key, octets */
+#define ESD_KEY_LEN (16)
+
+/* default port for the EsounD server */
+#define ESD_DEFAULT_PORT (16001)
+
+/* default sample rate for the EsounD server */
+#define ESD_DEFAULT_RATE (44100)
+
+/* maximum length of a stream/sample name */
+#define ESD_NAME_MAX (128)
+
+/* a magic number to identify the relative endianness of a client */
+#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N')))
+
+#define ESD_VOLUME_BASE (256)
+
+
+/*************************************/
+/* what can we do to/with the EsounD */
+enum esd_proto {
+ ESD_PROTO_CONNECT, /* implied on inital client connection */
+
+ /* pseudo "security" functionality */
+ ESD_PROTO_LOCK, /* disable "foreign" client connections */
+ ESD_PROTO_UNLOCK, /* enable "foreign" client connections */
+
+ /* stream functionality: play, record, monitor */
+ ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */
+ ESD_PROTO_STREAM_REC, /* record data from card as a stream */
+ ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */
+
+ /* sample functionality: cache, free, play, loop, EOL, kill */
+ ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */
+ ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */
+ ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */
+ ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */
+ ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */
+ ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */
+
+ /* free and reclaim /dev/dsp functionality */
+ ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */
+ ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */
+
+ /* TODO: move these to a more logical place. NOTE: will break the protocol */
+ ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */
+ ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */
+
+ /* esd remote management */
+ ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */
+ ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */
+ ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */
+ ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */
+
+ /* esd remote control */
+ ESD_PROTO_STREAM_PAN, /* set stream panning */
+ ESD_PROTO_SAMPLE_PAN, /* set default sample panning */
+
+ /* esd status */
+ ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */
+
+ /* esd latency */
+ ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */
+
+ ESD_PROTO_MAX /* for bounds checking */
+};
+
+/******************/
+/* The EsounD api */
+
+/* the properties of a sound buffer are logically or'd */
+
+/* bits of stream/sample data */
+#define ESD_MASK_BITS ( 0x000F )
+#define ESD_BITS8 ( 0x0000 )
+#define ESD_BITS16 ( 0x0001 )
+
+/* how many interleaved channels of data */
+#define ESD_MASK_CHAN ( 0x00F0 )
+#define ESD_MONO ( 0x0010 )
+#define ESD_STEREO ( 0x0020 )
+
+/* whether it's a stream or a sample */
+#define ESD_MASK_MODE ( 0x0F00 )
+#define ESD_STREAM ( 0x0000 )
+#define ESD_SAMPLE ( 0x0100 )
+#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */
+
+/* the function of the stream/sample, and common functions */
+#define ESD_MASK_FUNC ( 0xF000 )
+#define ESD_PLAY ( 0x1000 )
+/* functions for streams only */
+#define ESD_MONITOR ( 0x0000 )
+#define ESD_RECORD ( 0x2000 )
+/* functions for samples only */
+#define ESD_STOP ( 0x0000 )
+#define ESD_LOOP ( 0x2000 )
+
+typedef int esd_format_t;
+typedef int esd_proto_t;
+
+/*******************************************************************/
+/* esdmgr.c - functions to implement a "sound manager" for esd */
+
+/* structures to retrieve information about streams/samples from the server */
+typedef struct esd_server_info {
+
+ int version; /* server version encoded as an int */
+ esd_format_t format; /* magic int with the format info */
+ int rate; /* sample rate */
+
+} esd_server_info_t;
+
+typedef struct esd_player_info {
+
+ struct esd_player_info *next; /* point to next entry in list */
+ esd_server_info_t *server; /* the server that contains this stream */
+
+ int source_id; /* either a stream fd or sample id */
+ char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
+ int rate; /* sample rate */
+ int left_vol_scale; /* volume scaling */
+ int right_vol_scale;
+
+ esd_format_t format; /* magic int with the format info */
+
+} esd_player_info_t;
+
+typedef struct esd_sample_info {
+
+ struct esd_sample_info *next; /* point to next entry in list */
+ esd_server_info_t *server; /* the server that contains this sample */
+
+ int sample_id; /* either a stream fd or sample id */
+ char name[ ESD_NAME_MAX ]; /* name of stream for remote control */
+ int rate; /* sample rate */
+ int left_vol_scale; /* volume scaling */
+ int right_vol_scale;
+
+ esd_format_t format; /* magic int with the format info */
+ int length; /* total buffer length */
+
+} esd_sample_info_t;
+
+typedef struct esd_info {
+
+ esd_server_info_t *server;
+ esd_player_info_t *player_list;
+ esd_sample_info_t *sample_list;
+
+} esd_info_t;
+
+enum esd_standby_mode {
+ ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING
+};
+typedef int esd_standby_mode_t;
+
+enum esd_client_state {
+ ESD_STREAMING_DATA, /* data from here on is streamed data */
+ ESD_CACHING_SAMPLE, /* midway through caching a sample */
+ ESD_NEEDS_REQDATA, /* more data needed to complere request */
+ ESD_NEXT_REQUEST, /* proceed to next request */
+ ESD_CLIENT_STATE_MAX /* place holder */
+};
+typedef int esd_client_state_t;
+
+/* switch endian order for cross platform playing */
+#define swap_endian_32(x) ((x >> 24) | ((x >> 8) & 0xFF00) | (((x & 0xFF00) << 8)) | (x << 24))
+
+/* the endian key is transferred in binary, if it's read into int, */
+/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */
+/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */
+#define ESD_SWAP_ENDIAN_KEY ((uint32_t) swap_endian_32(ESD_ENDIAN_KEY))
+
+
+#endif
diff --git a/src/main.c b/src/main.c
index 0fe333e0..e50321f8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -41,11 +41,12 @@ int main(int argc, char *argv[]) {
module_load(c, "module-oss-mmap", "/dev/dsp1");
/* module_load(c, "module-pipe-sink", NULL);
module_load(c, "module-simple-protocol-tcp", NULL);
- module_load(c, "module-simple-protocol-unix", NULL);*/
+ module_load(c, "module-simple-protocol-unix", NULL);
module_load(c, "module-cli-protocol-tcp", NULL);
-/* module_load(c, "module-cli-protocol-unix", NULL);
- module_load(c, "module-native-protocol-tcp", NULL);*/
- module_load(c, "module-native-protocol-unix", NULL);
+ module_load(c, "module-cli-protocol-unix", NULL);
+ module_load(c, "module-native-protocol-tcp", NULL);
+ module_load(c, "module-native-protocol-unix", NULL);*/
+ module_load(c, "module-esound-protocol-tcp", NULL);
module_load(c, "module-cli", NULL);
fprintf(stderr, "main: mainloop entry.\n");
diff --git a/src/module-pipe-sink.c b/src/module-pipe-sink.c
index 6cc4de88..9747c330 100644
--- a/src/module-pipe-sink.c
+++ b/src/module-pipe-sink.c
@@ -79,7 +79,7 @@ int module_init(struct core *c, struct module*m) {
char *p;
int fd = -1;
static const struct pa_sample_spec ss = {
- .format = SAMPLE_S16NE,
+ .format = PA_SAMPLE_S16NE,
.rate = 44100,
.channels = 2,
};
diff --git a/src/module-protocol-stub.c b/src/module-protocol-stub.c
index 97bf5ef3..29ce6b18 100644
--- a/src/module-protocol-stub.c
+++ b/src/module-protocol-stub.c
@@ -1,31 +1,47 @@
+#include <stdio.h>
#include <assert.h>
#include <arpa/inet.h>
+#include <unistd.h>
#include "module.h"
#include "socket-server.h"
+#include "util.h"
#ifdef USE_PROTOCOL_SIMPLE
#include "protocol-simple.h"
#define protocol_free protocol_simple_free
#define IPV4_PORT 4711
- #define UNIX_SOCKET "/tmp/polypaudio_simple"
+ #define UNIX_SOCKET_DIR "/tmp/polypaudio"
+ #define UNIX_SOCKET "/tmp/polypaudio/simple"
#else
#ifdef USE_PROTOCOL_CLI
#include "protocol-cli.h"
#define protocol_new protocol_cli_new
#define protocol_free protocol_cli_free
#define IPV4_PORT 4712
- #define UNIX_SOCKET "/tmp/polypaudio_cli"
+ #define UNIX_SOCKET_DIR "/tmp/polypaudio"
+ #define UNIX_SOCKET "/tmp/polypaudio/cli"
#else
#ifdef USE_PROTOCOL_NATIVE
#include "protocol-native.h"
#define protocol_new protocol_native_new
#define protocol_free protocol_native_free
#define IPV4_PORT 4713
- #define UNIX_SOCKET "/tmp/polypaudio_native"
+ #define UNIX_SOCKET_DIR "/tmp/polypaudio"
+ #define UNIX_SOCKET "/tmp/polypaudio/native"
#else
- #error "Broken build system"
- #endif
+ #ifdef USE_PROTOCOL_ESOUND
+ #include "protocol-esound.h"
+ #include "esound-spec.h"
+ #define protocol_new protocol_esound_new
+ #define protocol_free protocol_esound_free
+ #define IPV4_PORT ESD_DEFAULT_PORT
+ #define UNIX_SOCKET_DIR ESD_UNIX_SOCKET_DIR
+ #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME
+ #else
+ #error "Broken build system"
+ #endif
+ #endif
#endif
#endif
@@ -37,8 +53,15 @@ int module_init(struct core *c, struct module*m) {
if (!(s = socket_server_new_ipv4(c->mainloop, INADDR_LOOPBACK, IPV4_PORT)))
return -1;
#else
- if (!(s = socket_server_new_unix(c->mainloop, UNIX_SOCKET)))
+ if (make_secure_dir(UNIX_SOCKET_DIR) < 0) {
+ fprintf(stderr, "Failed to create secure socket directory.\n");
return -1;
+ }
+
+ if (!(s = socket_server_new_unix(c->mainloop, UNIX_SOCKET))) {
+ rmdir(UNIX_SOCKET_DIR);
+ return -1;
+ }
#endif
#ifdef USE_PROTOCOL_SIMPLE
@@ -55,4 +78,8 @@ void module_done(struct core *c, struct module*m) {
assert(c && m);
protocol_free(m->userdata);
+
+#ifndef USE_TCP_SOCKETS
+ rmdir(UNIX_SOCKET_DIR);
+#endif
}
diff --git a/src/oss-util.c b/src/oss-util.c
index bf6b62e2..d3a5fecb 100644
--- a/src/oss-util.c
+++ b/src/oss-util.c
@@ -22,11 +22,11 @@ int oss_auto_format(int fd, struct pa_sample_spec *ss) {
fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno));
return -1;
} else
- ss->format = SAMPLE_U8;
+ ss->format = PA_SAMPLE_U8;
} else
- ss->format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE;
+ ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
} else
- ss->format = SAMPLE_S16NE;
+ ss->format = PA_SAMPLE_S16NE;
channels = 2;
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
diff --git a/src/pacat.c b/src/pacat.c
index 5ee1b866..fbd1d081 100644
--- a/src/pacat.c
+++ b/src/pacat.c
@@ -75,7 +75,7 @@ static void stream_complete_callback(struct pa_context*c, struct pa_stream *s, v
static void context_complete_callback(struct pa_context *c, int success, void *userdata) {
static const struct pa_sample_spec ss = {
- .format = SAMPLE_S16NE,
+ .format = PA_SAMPLE_S16NE,
.rate = 44100,
.channels = 2
};
diff --git a/src/protocol-esound.c b/src/protocol-esound.c
new file mode 100644
index 00000000..1668ef55
--- /dev/null
+++ b/src/protocol-esound.c
@@ -0,0 +1,462 @@
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "protocol-esound.h"
+#include "esound-spec.h"
+#include "memblock.h"
+#include "client.h"
+#include "sinkinput.h"
+#include "sink.h"
+#include "sample.h"
+
+/* This is heavily based on esound's code */
+
+struct connection {
+ struct protocol_esound *protocol;
+ struct iochannel *io;
+ struct client *client;
+ int authorized, swap_byte_order;
+ void *read_data;
+ size_t read_data_alloc, read_data_length;
+ void *write_data;
+ size_t write_data_alloc, write_data_index, write_data_length;
+ esd_proto_t request;
+ esd_client_state_t state;
+ struct sink_input *sink_input;
+ struct memblockq *input_memblockq;
+};
+
+struct protocol_esound {
+ int public;
+ struct core *core;
+ struct socket_server *server;
+ struct idxset *connections;
+};
+
+typedef struct proto_handler {
+ size_t data_length;
+ int (*proc)(struct connection *c, const void *data, size_t length);
+ const char *description;
+} esd_proto_handler_info_t;
+
+#define BUFSIZE PIPE_BUF
+
+static void sink_input_drop_cb(struct sink_input *i, size_t length);
+static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk);
+static void sink_input_kill_cb(struct sink_input *i);
+static uint32_t sink_input_get_latency_cb(struct sink_input *i);
+
+static int esd_proto_connect(struct connection *c, const void *data, size_t length);
+static int esd_proto_stream_play(struct connection *c, const void *data, size_t length);
+static int esd_proto_stream_record(struct connection *c, const void *data, size_t length);
+
+static int do_write(struct connection *c);
+
+/* the big map of protocol handler info */
+static struct proto_handler proto_map[ESD_PROTO_MAX] = {
+ { ESD_KEY_LEN + sizeof(int), &esd_proto_connect, "connect" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "lock" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "unlock" },
+
+ { ESD_NAME_MAX + 2 * sizeof(int), &esd_proto_stream_play, "stream play" },
+ { ESD_NAME_MAX + 2 * sizeof(int), &esd_proto_stream_record, "stream rec" },
+ { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream mon" },
+
+ { ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" },
+ { sizeof(int), NULL, "sample free" },
+ { sizeof(int), NULL, "sample play" },
+ { sizeof(int), NULL, "sample loop" },
+ { sizeof(int), NULL, "sample stop" },
+ { -1, NULL, "TODO: sample kill" },
+
+ { ESD_KEY_LEN + sizeof(int), NULL, "standby" },
+ { ESD_KEY_LEN + sizeof(int), NULL, "resume" },
+
+ { ESD_NAME_MAX, NULL, "sample getid" },
+ { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
+
+ { sizeof(int), NULL, "server info" },
+ { sizeof(int), NULL, "all info" },
+ { -1, NULL, "TODO: subscribe" },
+ { -1, NULL, "TODO: unsubscribe" },
+
+ { 3 * sizeof(int), NULL, "stream pan"},
+ { 3 * sizeof(int), NULL, "sample pan" },
+
+ { sizeof(int), NULL, "standby mode" },
+ { 0, NULL, "get latency" }
+};
+
+
+static void connection_free(struct connection *c) {
+ assert(c);
+ idxset_remove_by_data(c->protocol->connections, c, NULL);
+
+ client_free(c->client);
+
+ if (c->sink_input)
+ sink_input_free(c->sink_input);
+ if (c->input_memblockq)
+ memblockq_free(c->input_memblockq);
+
+ free(c->read_data);
+ free(c->write_data);
+
+ iochannel_free(c->io);
+ free(c);
+}
+
+static struct sink* get_output_sink(struct protocol_esound *p) {
+ assert(p);
+ return sink_get_default(p->core);
+}
+
+static void* connection_write(struct connection *c, size_t length) {
+ size_t t, i;
+ assert(c);
+
+ t = c->write_data_length+length;
+
+ if (c->write_data_alloc < t)
+ c->write_data = realloc(c->write_data, c->write_data_alloc = t);
+
+ assert(c->write_data);
+
+ i = c->write_data_length;
+ c->write_data_length += length;
+
+ return c->write_data+i;
+}
+
+/*** esound commands ***/
+
+static int esd_proto_connect(struct connection *c, const void *data, size_t length) {
+ uint32_t ekey;
+ int *ok;
+ assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
+
+ c->authorized = 1;
+
+ ekey = *(uint32_t*)(data+ESD_KEY_LEN);
+ if (ekey == ESD_ENDIAN_KEY)
+ c->swap_byte_order = 0;
+ else if (ekey == ESD_SWAP_ENDIAN_KEY)
+ c->swap_byte_order = 1;
+ else {
+ fprintf(stderr, "protocol-esound.c: client sent invalid endian key\n");
+ return -1;
+ }
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+ *ok = 1;
+
+ do_write(c);
+
+ return 0;
+}
+
+static int esd_proto_stream_play(struct connection *c, const void *data, size_t length) {
+ char name[ESD_NAME_MAX];
+ int format, rate;
+ struct sink *sink;
+ struct pa_sample_spec ss;
+ assert(length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ format = *(int*)data;
+ rate = *((int*)data + 1);
+
+ if (c->swap_byte_order)
+ format = swap_endian_32(format);
+ if (c->swap_byte_order)
+ rate = swap_endian_32(rate);
+
+ ss.rate = rate;
+ ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
+
+ if (!pa_sample_spec_valid(&ss))
+ return -1;
+
+ if (!(sink = get_output_sink(c->protocol)))
+ return -1;
+
+ strncpy(name, data + sizeof(int)*2, sizeof(name));
+ name[sizeof(name)-1] = 0;
+
+ client_rename(c->client, name);
+
+ assert(!c->input_memblockq);
+ c->input_memblockq = memblockq_new(1024*10, pa_sample_size(&ss), 1024*2);
+ assert(c->input_memblockq);
+
+ assert(!c->sink_input);
+ c->sink_input = sink_input_new(sink, &ss, name);
+ assert(c->sink_input);
+
+ c->sink_input->peek = sink_input_peek_cb;
+ c->sink_input->drop = sink_input_drop_cb;
+ c->sink_input->kill = sink_input_kill_cb;
+ c->sink_input->get_latency = sink_input_get_latency_cb;
+ c->sink_input->userdata = c;
+
+ c->state = ESD_STREAMING_DATA;
+
+ return 0;
+}
+
+static int esd_proto_stream_record(struct connection *c, const void *data, size_t length) {
+ assert(0);
+}
+
+/*** client callbacks ***/
+
+static void client_kill_cb(struct client *c) {
+ assert(c && c->userdata);
+ connection_free(c->userdata);
+}
+
+/*** iochannel callbacks ***/
+
+static int do_read(struct connection *c) {
+ assert(c && c->io);
+
+ if (!iochannel_is_readable(c->io))
+ return 0;
+
+ if (c->state == ESD_NEXT_REQUEST) {
+ ssize_t r;
+ assert(c->read_data_length < sizeof(c->request));
+
+ if ((r = iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
+ fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ return -1;
+ }
+
+ if ((c->read_data_length+= r) >= sizeof(c->request)) {
+ struct proto_handler *handler;
+
+ if (c->swap_byte_order)
+ c->request = swap_endian_32(c->request);
+
+ if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+ fprintf(stderr, "protocol-esound.c: recieved invalid request.\n");
+ return -1;
+ }
+
+ handler = proto_map+c->request;
+
+ if (!handler->proc) {
+ fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n");
+ return -1;
+ }
+
+ if (handler->data_length == 0) {
+ c->read_data_length = 0;
+
+ if (handler->proc(c, NULL, 0) < 0)
+ return -1;
+
+ } else {
+ if (c->read_data_alloc < handler->data_length)
+ c->read_data = realloc(c->read_data, c->read_data_alloc = handler->data_length);
+ assert(c->read_data);
+
+ c->state = ESD_NEEDS_REQDATA;
+ c->read_data_length = 0;
+ }
+ }
+
+ } else if (c->state == ESD_NEEDS_REQDATA) {
+ ssize_t r;
+ struct proto_handler *handler = proto_map+c->request;
+
+ assert(handler->proc);
+
+ assert(c->read_data && c->read_data_length < handler->data_length);
+
+ if ((r = iochannel_read(c->io, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+ fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ return -1;
+ }
+
+ if ((c->read_data_length+= r) >= handler->data_length) {
+ size_t l = c->read_data_length;
+ assert(handler->proc);
+
+ c->state = ESD_NEXT_REQUEST;
+ c->read_data_length = 0;
+
+ if (handler->proc(c, c->read_data, l) < 0)
+ return -1;
+ }
+ } else if (c->state == ESD_STREAMING_DATA) {
+ struct memchunk chunk;
+ ssize_t r;
+
+ assert(c->input_memblockq);
+
+ if (!memblockq_is_writable(c->input_memblockq, BUFSIZE))
+ return 0;
+
+ chunk.memblock = memblock_new(BUFSIZE);
+ assert(chunk.memblock && chunk.memblock->data);
+
+ if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) {
+ fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ memblock_unref(chunk.memblock);
+ return -1;
+ }
+
+ chunk.memblock->length = chunk.length = r;
+ chunk.index = 0;
+
+ assert(c->input_memblockq);
+ memblockq_push(c->input_memblockq, &chunk, 0);
+ memblock_unref(chunk.memblock);
+ assert(c->sink_input);
+ sink_notify(c->sink_input->sink);
+ } else
+ assert(0);
+
+ return 0;
+}
+
+static int do_write(struct connection *c) {
+ ssize_t r;
+ assert(c && c->io);
+
+ if (!iochannel_is_writable(c->io))
+ return 0;
+
+ if (!c->write_data_length)
+ return 0;
+
+ assert(c->write_data_index < c->write_data_length);
+ if ((r = iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
+ fprintf(stderr, "protocol-esound.c: write() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if ((c->write_data_index +=r) >= c->write_data_length)
+ c->write_data_length = c->write_data_index = 0;
+
+ return 0;
+}
+
+static void io_callback(struct iochannel*io, void *userdata) {
+ struct connection *c = userdata;
+ assert(io && c && c->io == io);
+
+ if (do_read(c) < 0 || do_write(c) < 0)
+ connection_free(c);
+}
+
+/*** sink_input callbacks ***/
+
+static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk) {
+ struct connection*c;
+ assert(i && i->userdata && chunk);
+ c = i->userdata;
+
+ if (memblockq_peek(c->input_memblockq, chunk) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void sink_input_drop_cb(struct sink_input *i, size_t length) {
+ struct connection*c = i->userdata;
+ assert(i && c && length);
+
+ memblockq_drop(c->input_memblockq, length);
+
+ if (do_read(c) < 0)
+ connection_free(c);
+}
+
+static void sink_input_kill_cb(struct sink_input *i) {
+ assert(i && i->userdata);
+ connection_free((struct connection *) i->userdata);
+}
+
+
+static uint32_t sink_input_get_latency_cb(struct sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return pa_samples_usec(memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
+}
+
+/*** socket server callback ***/
+
+static void on_connection(struct socket_server*s, struct iochannel *io, void *userdata) {
+ struct connection *c;
+ char cname[256];
+ assert(s && io && userdata);
+
+ c = malloc(sizeof(struct connection));
+ assert(c);
+ c->protocol = userdata;
+ c->io = io;
+ iochannel_set_callback(c->io, io_callback, c);
+
+ iochannel_peer_to_string(io, cname, sizeof(cname));
+ assert(c->protocol->core);
+ c->client = client_new(c->protocol->core, "ESOUND", cname);
+ assert(c->client);
+ c->client->kill = client_kill_cb;
+ c->client->userdata = c;
+
+ c->authorized = c->protocol->public;
+ c->swap_byte_order = 0;
+
+ c->read_data_length = 0;
+ c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
+ assert(c->read_data);
+
+ c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
+ c->write_data = NULL;
+
+ c->state = ESD_NEEDS_REQDATA;
+ c->request = ESD_PROTO_CONNECT;
+
+ c->sink_input = NULL;
+ c->input_memblockq = NULL;
+
+ idxset_put(c->protocol->connections, c, NULL);
+}
+
+/*** entry points ***/
+
+struct protocol_esound* protocol_esound_new(struct core*core, struct socket_server *server) {
+ struct protocol_esound *p;
+ assert(core && server);
+
+ p = malloc(sizeof(struct protocol_esound));
+ assert(p);
+ p->public = 1;
+ p->server = server;
+ p->core = core;
+ p->connections = idxset_new(NULL, NULL);
+ assert(p->connections);
+
+ socket_server_set_callback(p->server, on_connection, p);
+
+ return p;
+}
+
+void protocol_esound_free(struct protocol_esound *p) {
+ struct connection *c;
+ assert(p);
+
+ while ((c = idxset_first(p->connections, NULL)))
+ connection_free(c);
+
+ idxset_free(p->connections, NULL, NULL);
+ socket_server_free(p->server);
+ free(p);
+}
diff --git a/src/protocol-esound.h b/src/protocol-esound.h
new file mode 100644
index 00000000..2600cfae
--- /dev/null
+++ b/src/protocol-esound.h
@@ -0,0 +1,12 @@
+#ifndef fooprotocolesoundhfoo
+#define fooprotocolesoundhfoo
+
+#include "core.h"
+#include "socket-server.h"
+
+struct protocol_esound;
+
+struct protocol_esound* protocol_esound_new(struct core*core, struct socket_server *server);
+void protocol_esound_free(struct protocol_esound *p);
+
+#endif
diff --git a/src/protocol-native.c b/src/protocol-native.c
index 27b547a6..9af438a9 100644
--- a/src/protocol-native.c
+++ b/src/protocol-native.c
@@ -384,6 +384,7 @@ struct protocol_native* protocol_native_new(struct core *core, struct socket_ser
p->server = server;
p->core = core;
p->connections = idxset_new(NULL, NULL);
+ assert(p->connections);
socket_server_set_callback(p->server, on_connection, p);
diff --git a/src/protocol-simple.c b/src/protocol-simple.c
index c8c45854..80249eef 100644
--- a/src/protocol-simple.c
+++ b/src/protocol-simple.c
@@ -73,8 +73,7 @@ static int do_read(struct connection *c) {
return -1;
}
- chunk.memblock->length = r;
- chunk.length = r;
+ chunk.memblock->length = chunk.length = r;
chunk.index = 0;
assert(c->input_memblockq);
diff --git a/src/sample-util.c b/src/sample-util.c
index 7a3c267a..ff14548c 100644
--- a/src/sample-util.c
+++ b/src/sample-util.c
@@ -4,7 +4,7 @@
#include "sample-util.h"
struct pa_sample_spec default_sample_spec = {
- .format = SAMPLE_S16NE,
+ .format = PA_SAMPLE_S16NE,
.rate = 44100,
.channels = 2
};
@@ -27,18 +27,20 @@ void silence_memory(void *p, size_t length, struct pa_sample_spec *spec) {
assert(p && length && spec);
switch (spec->format) {
- case SAMPLE_U8:
+ case PA_SAMPLE_U8:
c = 127;
break;
- case SAMPLE_S16LE:
- case SAMPLE_S16BE:
- case SAMPLE_FLOAT32:
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
+ case PA_SAMPLE_FLOAT32:
c = 0;
break;
- case SAMPLE_ALAW:
- case SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
+ case PA_SAMPLE_ULAW:
c = 80;
break;
+ default:
+ assert(0);
}
memset(p, c, length);
@@ -47,7 +49,7 @@ void silence_memory(void *p, size_t length, struct pa_sample_spec *spec) {
size_t mix_chunks(struct mix_info channels[], unsigned nchannels, void *data, size_t length, struct pa_sample_spec *spec, uint8_t volume) {
unsigned c, d;
assert(channels && data && length && spec);
- assert(spec->format == SAMPLE_S16NE);
+ assert(spec->format == PA_SAMPLE_S16NE);
for (d = 0;; d += sizeof(int16_t)) {
int32_t sum = 0;
diff --git a/src/sample.c b/src/sample.c
index 2454630c..b0d0cdbd 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -7,18 +7,20 @@ size_t pa_sample_size(struct pa_sample_spec *spec) {
size_t b = 1;
switch (spec->format) {
- case SAMPLE_U8:
- case SAMPLE_ULAW:
- case SAMPLE_ALAW:
+ case PA_SAMPLE_U8:
+ case PA_SAMPLE_ULAW:
+ case PA_SAMPLE_ALAW:
b = 1;
break;
- case SAMPLE_S16LE:
- case SAMPLE_S16BE:
+ case PA_SAMPLE_S16LE:
+ case PA_SAMPLE_S16BE:
b = 2;
break;
- case SAMPLE_FLOAT32:
+ case PA_SAMPLE_FLOAT32:
b = 4;
break;
+ default:
+ assert(0);
}
return b * spec->channels;
@@ -35,3 +37,15 @@ uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec) {
return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000);
}
+
+int pa_sample_spec_valid(struct pa_sample_spec *spec) {
+ assert(spec);
+
+ if (!spec->rate || !spec->channels)
+ return 0;
+
+ if (spec->format <= 0 || spec->format >= PA_SAMPLE_MAX)
+ return 0;
+
+ return 1;
+}
diff --git a/src/sample.h b/src/sample.h
index a4a973bf..697937e0 100644
--- a/src/sample.h
+++ b/src/sample.h
@@ -5,15 +5,16 @@
#include <sys/types.h>
enum pa_sample_format {
- SAMPLE_U8,
- SAMPLE_ALAW,
- SAMPLE_ULAW,
- SAMPLE_S16LE,
- SAMPLE_S16BE,
- SAMPLE_FLOAT32
+ PA_SAMPLE_U8,
+ PA_SAMPLE_ALAW,
+ PA_SAMPLE_ULAW,
+ PA_SAMPLE_S16LE,
+ PA_SAMPLE_S16BE,
+ PA_SAMPLE_FLOAT32,
+ PA_SAMPLE_MAX
};
-#define SAMPLE_S16NE SAMPLE_S16LE
+#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
struct pa_sample_spec {
enum pa_sample_format format;
@@ -25,4 +26,6 @@ size_t pa_bytes_per_second(struct pa_sample_spec *spec);
size_t pa_sample_size(struct pa_sample_spec *spec);
uint32_t pa_samples_usec(size_t length, struct pa_sample_spec *spec);
+int pa_sample_spec_valid(struct pa_sample_spec *spec);
+
#endif
diff --git a/src/util.c b/src/util.c
index 0383a0ad..95350421 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,9 +1,12 @@
+#include <errno.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
#include "util.h"
@@ -60,3 +63,23 @@ void peer_to_string(char *c, size_t l, int fd) {
snprintf(c, l, "Unknown client");
}
+
+int make_secure_dir(const char* dir) {
+ struct stat st;
+
+ if (mkdir(dir, 0700) < 0)
+ if (errno != EEXIST)
+ return -1;
+
+ if (lstat(dir, &st) < 0)
+ goto fail;
+
+ if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700))
+ goto fail;
+
+ return 0;
+
+fail:
+ rmdir(dir);
+ return -1;
+}
diff --git a/src/util.h b/src/util.h
index 830ee2e0..2a507198 100644
--- a/src/util.h
+++ b/src/util.h
@@ -5,4 +5,6 @@ void make_nonblock_fd(int fd);
void peer_to_string(char *c, size_t l, int fd);
+int make_secure_dir(const char* dir);
+
#endif