summaryrefslogtreecommitdiffstats
path: root/polyp/protocol-esound.c
diff options
context:
space:
mode:
Diffstat (limited to 'polyp/protocol-esound.c')
-rw-r--r--polyp/protocol-esound.c194
1 files changed, 179 insertions, 15 deletions
diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c
index 8a7c4bcb..91e6b7d6 100644
--- a/polyp/protocol-esound.c
+++ b/polyp/protocol-esound.c
@@ -39,8 +39,10 @@
#include "source-output.h"
#include "source.h"
#include "sample.h"
-
+#include "scache.h"
+#include "sample-util.h"
#include "authkey.h"
+#include "debug.h"
#define DEFAULT_COOKIE_FILE ".esd_auth"
@@ -49,6 +51,10 @@
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
+#define MAX_CACHE_SAMPLE_SIZE (1024000)
+
+#define SCACHE_PREFIX "esound~"
+
/* This is heavily based on esound's code */
struct connection {
@@ -71,6 +77,11 @@ struct connection {
struct pa_memblock *current_memblock;
size_t memblock_index, fragment_size;
} playback;
+
+
+ struct pa_memchunk scache_memchunk;
+ char *scache_name;
+ struct pa_sample_spec scache_sample_spec;
};
struct pa_protocol_esound {
@@ -105,6 +116,9 @@ static int esd_proto_get_latency(struct connection *c, esd_proto_t request, cons
static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length);
static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length);
/* the big map of protocol handler info */
static struct proto_handler proto_map[ESD_PROTO_MAX] = {
@@ -116,9 +130,9 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
{ ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
- { ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" },
- { sizeof(int), NULL, "sample free" },
- { sizeof(int), NULL, "sample play" },
+ { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" },
+ { sizeof(int), esd_proto_sample_free_or_play, "sample free" },
+ { sizeof(int), esd_proto_sample_free_or_play, "sample play" },
{ sizeof(int), NULL, "sample loop" },
{ sizeof(int), NULL, "sample stop" },
{ -1, NULL, "TODO: sample kill" },
@@ -126,7 +140,7 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
{ ESD_KEY_LEN + sizeof(int), NULL, "standby" },
{ ESD_KEY_LEN + sizeof(int), NULL, "resume" },
- { ESD_NAME_MAX, NULL, "sample getid" },
+ { ESD_NAME_MAX, esd_proto_sample_get_id, "sample getid" },
{ ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
{ sizeof(int), esd_proto_server_info, "server info" },
@@ -170,6 +184,10 @@ static void connection_free(struct connection *c) {
if (c->fixed_source)
c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source);
+
+ if (c->scache_memchunk.memblock)
+ pa_memblock_unref(c->scache_memchunk.memblock);
+ free(c->scache_name);
free(c);
}
@@ -216,6 +234,22 @@ static void* connection_write(struct connection *c, size_t length) {
return c->write_data+i;
}
+static void format_esd2native(int format, struct pa_sample_spec *ss) {
+ assert(ss);
+
+ ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+ ss->format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
+}
+
+static int format_native2esd(struct pa_sample_spec *ss) {
+ int format = 0;
+
+ format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
+ format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
+
+ return format;
+}
+
/*** esound commands ***/
static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) {
@@ -260,8 +294,7 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons
rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
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;
+ format_esd2native(format, &ss);
if (!pa_sample_spec_valid(&ss))
return -1;
@@ -313,8 +346,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
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;
+ format_esd2native(format, &ss);
if (!pa_sample_spec_valid(&ss))
return -1;
@@ -390,8 +422,7 @@ static int esd_proto_server_info(struct connection *c, esd_proto_t request, cons
if ((sink = get_output_sink(c->protocol))) {
rate = sink->sample_spec.rate;
- format = (sink->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
- format |= (sink->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
+ format = format_native2esd(&sink->sample_spec);
}
response = connection_write(c, sizeof(int)*3);
@@ -428,8 +459,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
if (conn->sink_input) {
rate = conn->sink_input->sample_spec.rate;
volume = (conn->sink_input->volume*0xFF)/0x100;
- format = (conn->sink_input->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
- format |= (conn->sink_input->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO;
+ format = format_native2esd(&conn->sink_input->sample_spec);
}
/* id */
@@ -488,6 +518,103 @@ static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const
return 0;
}
+static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ struct pa_sample_spec ss;
+ int format, rate;
+ size_t sc_length;
+ uint32_t index;
+ int *ok;
+ char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+ assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int)));
+
+ format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data);
+ rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1));
+
+ ss.rate = rate;
+ format_esd2native(format, &ss);
+
+ sc_length = (size_t) maybe_swap_endian_32(c->swap_byte_order, (*((int*)data + 2)));
+
+ if (sc_length >= MAX_CACHE_SAMPLE_SIZE)
+ return -1;
+
+ strcpy(name, SCACHE_PREFIX);
+ strncpy(name+sizeof(SCACHE_PREFIX)-1, data+3*sizeof(int), ESD_NAME_MAX);
+ name[sizeof(name)-1] = 0;
+
+ assert(!c->scache_memchunk.memblock);
+ c->scache_memchunk.memblock = pa_memblock_new(sc_length);
+ c->scache_memchunk.index = 0;
+ c->scache_memchunk.length = sc_length;
+ c->scache_sample_spec = ss;
+ assert(!c->scache_name);
+ c->scache_name = strdup(name);
+ assert(c->scache_name);
+
+ c->state = ESD_CACHING_SAMPLE;
+
+ pa_scache_add_item(c->protocol->core, c->scache_name, NULL, NULL, &index);
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = index+1;
+
+ return 0;
+}
+
+static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ uint32_t index;
+ char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
+ assert(c && data && length == ESD_NAME_MAX);
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = -1;
+
+ strcpy(name, SCACHE_PREFIX);
+ strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
+ name[sizeof(name)-1] = 0;
+
+ if ((index = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
+ *ok = (int) index +1;
+
+ return 0;
+}
+
+static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
+ int *ok;
+ const char *name;
+ uint32_t index;
+ assert(c && data && length == sizeof(int));
+
+ index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data)-1;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+
+ *ok = 0;
+
+ if ((name = pa_scache_get_name_by_id(c->protocol->core, index))) {
+ if (request == ESD_PROTO_SAMPLE_PLAY) {
+ struct pa_sink *sink;
+
+ if ((sink = get_output_sink(c->protocol)))
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
+ *ok = (int) index+1;
+ } else {
+ assert(request == ESD_PROTO_SAMPLE_FREE);
+
+ if (pa_scache_remove_item(c->protocol->core, name) >= 0)
+ *ok = (int) index+1;
+ }
+ }
+
+ return 0;
+}
+
/*** client callbacks ***/
static void client_kill_cb(struct pa_client *c) {
@@ -566,6 +693,40 @@ static int do_read(struct connection *c) {
if (handler->proc(c, c->request, c->read_data, l) < 0)
return -1;
}
+ } else if (c->state == ESD_CACHING_SAMPLE) {
+ ssize_t r;
+
+ assert(c->scache_memchunk.memblock && c->scache_name && c->scache_memchunk.index < c->scache_memchunk.length);
+
+ if ((r = pa_iochannel_read(c->io, c->scache_memchunk.memblock->data+c->scache_memchunk.index, c->scache_memchunk.length-c->scache_memchunk.index)) <= 0) {
+ fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
+ return -1;
+ }
+
+ c->scache_memchunk.index += r;
+ assert(c->scache_memchunk.index <= c->scache_memchunk.length);
+
+ if (c->scache_memchunk.index == c->scache_memchunk.length) {
+ uint32_t index;
+ int *ok;
+
+ c->scache_memchunk.index = 0;
+ pa_scache_add_item(c->protocol->core, c->scache_name, &c->scache_sample_spec, &c->scache_memchunk, &index);
+
+ pa_memblock_unref(c->scache_memchunk.memblock);
+ c->scache_memchunk.memblock = NULL;
+ c->scache_memchunk.index = c->scache_memchunk.length = 0;
+
+ free(c->scache_name);
+ c->scache_name = NULL;
+
+ c->state = ESD_NEXT_REQUEST;
+
+ ok = connection_write(c, sizeof(int));
+ assert(ok);
+ *ok = index+1;
+ }
+
} else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
struct pa_memchunk chunk;
ssize_t r;
@@ -608,7 +769,6 @@ static int do_read(struct connection *c) {
pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
-
}
return 0;
@@ -789,10 +949,14 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
c->playback.memblock_index = 0;
c->playback.fragment_size = 0;
+ c->scache_memchunk.length = c->scache_memchunk.index = 0;
+ c->scache_memchunk.memblock = NULL;
+ c->scache_name = NULL;
+
c->fixed_source = c->protocol->core->mainloop->source_fixed(c->protocol->core->mainloop, fixed_callback, c);
assert(c->fixed_source);
c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0);
-
+
pa_idxset_put(c->protocol->connections, c, &c->index);
}