]> code.delx.au - pulseaudio/commitdiff
esound protocol
authorLennart Poettering <lennart@poettering.net>
Tue, 29 Jun 2004 16:48:37 +0000 (16:48 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 29 Jun 2004 16:48:37 +0000 (16:48 +0000)
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@40 fefdeb5f-60dc-0310-8127-8f9354f1896f

18 files changed:
src/Makefile.am
src/client.c
src/client.h
src/esound-spec.h [new file with mode: 0644]
src/main.c
src/module-pipe-sink.c
src/module-protocol-stub.c
src/oss-util.c
src/pacat.c
src/protocol-esound.c [new file with mode: 0644]
src/protocol-esound.h [new file with mode: 0644]
src/protocol-native.c
src/protocol-simple.c
src/sample-util.c
src/sample.c
src/sample.h
src/util.c
src/util.h

index a67f395e854c649cc19473f56424c2fbbfdedf75..af4478be6a35a450e182b740e5046069b07b1835 100644 (file)
@@ -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
index 4c8a04910b9964f16a4483afd664bb1a48fbabff..3769128320cefb2e64aa291d4058316300be6fb3 100644 (file)
@@ -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;
+}
index 4817205188d914fc31af8b45de02591758410acf..39167ee79988335ae259fa56d8909976a47b25d2 100644 (file)
@@ -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 (file)
index 0000000..1c2dc02
--- /dev/null
@@ -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
index 0fe333e0e77cf4da735c993001d6f01cac113a69..e50321f8d78218285a4e27676005f43bd911041f 100644 (file)
@@ -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");
index 6cc4de8825eccdfb302d800bcfeeabe1df5fb484..9747c330705b293e130b9e9a63d16d6a5a57d8f9 100644 (file)
@@ -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,
     };
index 97bf5ef3cb383ebe8ba57a9aaeb755538f42778a..29ce6b18c482b8002d336a1e2367f2c83c11cfcf 100644 (file)
@@ -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
 }
index bf6b62e29ad3e4b114b3d9809e18c0c8e8e87903..d3a5fecbbd3bf1abac5b623fd9aee9c71a7b723e 100644 (file)
@@ -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) {
index 5ee1b866c666fc816bb09d492684980b7536bfe6..fbd1d081e916700e8965c55101651e3f13066b19 100644 (file)
@@ -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 (file)
index 0000000..1668ef5
--- /dev/null
@@ -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 (file)
index 0000000..2600cfa
--- /dev/null
@@ -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
index 27b547a6086cd66800c086d745a0703bd8234ebe..9af438a9331d170e585a1ae422d8ad59e70ee035 100644 (file)
@@ -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);
     
index c8c458548a3286a266a8bf0179ce0fdb31caab1b..80249eef0d9cde548704edf0663f5b575667af47 100644 (file)
@@ -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);
index 7a3c267a8b9f881aecf3ce469a5273218139e195..ff14548c10711add6295828d1c744a9fb2c87fc3 100644 (file)
@@ -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;
index 2454630cf716684f920f843a608c0911ef969ccc..b0d0cdbd03fbcf27df47baf8afe183c72666ad10 100644 (file)
@@ -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;
+}
index a4a973bf18fde27aee28f24d563e2840ba2078d4..697937e053da08fae04f1e26c6587521c9baf493 100644 (file)
@@ -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
index 0383a0ad75ba498aa53927264625a97f5294e115..953504218bdb1465faf6c45bd160202f3301808c 100644 (file)
@@ -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;
+}
index 830ee2e0ccdf2d6d62a21831446f0b6c476012ba..2a5071989eb173a983052ee552585d82fa0361ff 100644 (file)
@@ -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