From bb0b105b83c0f4ee56b4c7e9a179606aee296aa9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 20 Jul 2004 01:07:06 +0000 Subject: sample cache work git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@98 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 3 +- polyp/cli-command.c | 59 ++++++++++++++ polyp/clitext.c | 35 +++++++++ polyp/clitext.h | 1 + polyp/core.c | 4 + polyp/core.h | 4 +- polyp/debug.h | 8 ++ polyp/hashmap.c | 14 ++++ polyp/hashmap.h | 5 ++ polyp/main.c | 2 +- polyp/modargs.c | 4 + polyp/module-alsa-sink.c | 2 +- polyp/protocol-esound.c | 194 +++++++++++++++++++++++++++++++++++++++++++---- polyp/scache.c | 181 +++++++++++++++++++++++++++++++++++++++++++ polyp/scache.h | 24 ++++++ 15 files changed, 520 insertions(+), 20 deletions(-) create mode 100644 polyp/debug.h create mode 100644 polyp/scache.c create mode 100644 polyp/scache.h (limited to 'polyp') diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 94edf616..d1615585 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -105,7 +105,8 @@ polypaudio_SOURCES = idxset.c idxset.h \ cli-command.c cli-command.h \ clitext.c clitext.h \ tokenizer.c tokenizer.h \ - dynarray.c dynarray.h + dynarray.c dynarray.h \ + scache.c scache.h polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) diff --git a/polyp/cli-command.c b/polyp/cli-command.c index f3a2f8a0..03bd125b 100644 --- a/polyp/cli-command.c +++ b/polyp/cli-command.c @@ -40,6 +40,8 @@ #include "strbuf.h" #include "namereg.h" #include "clitext.h" +#include "scache.h" +#include "sample-util.h" struct command { const char *name; @@ -67,6 +69,9 @@ static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); static const struct command commands[] = { { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, @@ -90,6 +95,9 @@ static const struct command commands[] = { { "kill_client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, { "kill_sink_input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, { "kill_source_output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, + { "scache_list", pa_cli_command_scache_list, "List all entries in the sample cache", 2}, + { "scache_play", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3}, + { "scache_remove", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2}, { NULL, NULL, NULL, 0 } }; @@ -199,6 +207,7 @@ static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_cli_command_clients(c, t, buf, fail, verbose); pa_cli_command_sink_inputs(c, t, buf, fail, verbose); pa_cli_command_source_outputs(c, t, buf, fail, verbose); + pa_cli_command_scache_list(c, t, buf, fail, verbose); return 0; } @@ -429,6 +438,56 @@ static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokeni return 0; } +static int pa_cli_command_scache_list(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_scache_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_scache_play(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n, *sink_name; + struct pa_sink *sink; + assert(c && t && buf && fail && verbose); + + if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) { + pa_strbuf_puts(buf, "No sink by that name.\n"); + return -1; + } + + if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) { + pa_strbuf_puts(buf, "Failed to play sample.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + assert(c && t && buf && fail && verbose); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sample name.\n"); + return -1; + } + + if (pa_scache_remove_item(c, n) < 0) { + pa_strbuf_puts(buf, "Failed to remove sample.\n"); + return -1; + } + + return 0; +} + int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) { const char *cs; diff --git a/polyp/clitext.c b/polyp/clitext.c index c1b9953b..d0c3f9a7 100644 --- a/polyp/clitext.c +++ b/polyp/clitext.c @@ -24,6 +24,7 @@ #endif #include +#include #include "clitext.h" #include "module.h" @@ -34,6 +35,7 @@ #include "source-output.h" #include "strbuf.h" #include "sample-util.h" +#include "scache.h" char *pa_module_list_to_string(struct pa_core *c) { struct pa_strbuf *s; @@ -201,3 +203,36 @@ char *pa_sink_input_list_to_string(struct pa_core *c) { return pa_strbuf_tostring_free(s); } + +char *pa_scache_list_to_string(struct pa_core *c) { + struct pa_scache_entry *e; + void *state = NULL; + struct pa_strbuf *s; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u cache entries available.\n", c->scache_hashmap ? pa_hashmap_ncontents(c->scache_hashmap) : 0); + + if (c->scache_hashmap) { + + while ((e = pa_hashmap_iterate(c->scache_hashmap, &state))) { + double l; + char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; + pa_sample_snprint(ss, sizeof(ss), &e->sample_spec); + + l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); + + pa_strbuf_printf( + s, " name: <%s>\n\tindex: <%i>\n\tsample_spec: <%s>\n\tlength: <%u>\n\tduration: <%0.1fs>\n", + e->name, + e->index, + ss, + e->memchunk.length, + l); + } + } + + return pa_strbuf_tostring_free(s); +} diff --git a/polyp/clitext.h b/polyp/clitext.h index b1718cb5..4e5252fe 100644 --- a/polyp/clitext.h +++ b/polyp/clitext.h @@ -30,6 +30,7 @@ char *pa_sink_list_to_string(struct pa_core *core); char *pa_source_list_to_string(struct pa_core *c); char *pa_client_list_to_string(struct pa_core *c); char *pa_module_list_to_string(struct pa_core *c); +char *pa_scache_list_to_string(struct pa_core *c); #endif diff --git a/polyp/core.c b/polyp/core.c index dc9525a8..1c69f914 100644 --- a/polyp/core.c +++ b/polyp/core.c @@ -33,6 +33,7 @@ #include "source.h" #include "namereg.h" #include "util.h" +#include "scache.h" struct pa_core* pa_core_new(struct pa_mainloop_api *m) { struct pa_core* c; @@ -50,6 +51,8 @@ struct pa_core* pa_core_new(struct pa_mainloop_api *m) { c->modules = NULL; c->namereg = NULL; + c->scache_idxset = NULL; + c->scache_hashmap = NULL; c->default_sample_spec.format = PA_SAMPLE_S16NE; c->default_sample_spec.rate = 44100; @@ -82,6 +85,7 @@ void pa_core_free(struct pa_core *c) { pa_idxset_free(c->sink_inputs, NULL, NULL); pa_namereg_free(c); + pa_scache_free(c); free(c); }; diff --git a/polyp/core.h b/polyp/core.h index 99d7d76a..03b8671a 100644 --- a/polyp/core.h +++ b/polyp/core.h @@ -30,9 +30,9 @@ struct pa_core { struct pa_mainloop_api *mainloop; - struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules; + struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache_idxset; - struct pa_hashmap *namereg; + struct pa_hashmap *namereg, *scache_hashmap; uint32_t default_source_index, default_sink_index; diff --git a/polyp/debug.h b/polyp/debug.h new file mode 100644 index 00000000..fb2b889e --- /dev/null +++ b/polyp/debug.h @@ -0,0 +1,8 @@ +#ifndef foodebughfoo +#define foodebughfoo + +/* A nice trick for debuggers, working on x86 only */ + +#define DEBUG_TRAP __asm__("int $3") + +#endif diff --git a/polyp/hashmap.c b/polyp/hashmap.c index 2c7c92b5..51e3879b 100644 --- a/polyp/hashmap.c +++ b/polyp/hashmap.c @@ -168,3 +168,17 @@ int pa_hashmap_remove(struct pa_hashmap *h, const void *key) { unsigned pa_hashmap_ncontents(struct pa_hashmap *h) { return h->n_entries; } + +void *pa_hashmap_iterate(struct pa_hashmap *h, void **state) { + assert(h && state); + + if (!*state) { + *state = h->first_entry; + } else + *state = ((struct hashmap_entry*) *state)->next; + + if (!*state) + return NULL; + + return ((struct hashmap_entry*) *state)->value; +} diff --git a/polyp/hashmap.h b/polyp/hashmap.h index b24e74a5..3b79d7ae 100644 --- a/polyp/hashmap.h +++ b/polyp/hashmap.h @@ -34,4 +34,9 @@ int pa_hashmap_remove(struct pa_hashmap *h, const void *key); unsigned pa_hashmap_ncontents(struct pa_hashmap *h); +/* Maybe used to iterate through the hashmap. Initial state should + point to a NULL pointer. The hashmap may not be modified during + iteration */ +void *pa_hashmap_iterate(struct pa_hashmap *h, void **state); + #endif diff --git a/polyp/main.c b/polyp/main.c index bcc1fada..0c6104a4 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -126,7 +126,7 @@ int main(int argc, char *argv[]) { r = lt_dlinit(); assert(r == 0); #ifdef DLSEARCHDIR - lt_dladdsearchdir(DLSEARCHDIR); +/* lt_dladdsearchdir(DLSEARCHDIR);*/ #endif mainloop = pa_mainloop_new(); diff --git a/polyp/modargs.c b/polyp/modargs.c index ea104761..3841a9ec 100644 --- a/polyp/modargs.c +++ b/polyp/modargs.c @@ -36,6 +36,8 @@ #include "sink.h" #include "source.h" +#include "debug.h" + struct pa_modargs; struct entry { @@ -213,6 +215,8 @@ int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss struct pa_sample_spec ss; assert(ma && rss); +/* DEBUG_TRAP;*/ + ss = *rss; if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) return -1; diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c index 8a3388af..c250d1cf 100644 --- a/polyp/module-alsa-sink.c +++ b/polyp/module-alsa-sink.c @@ -154,7 +154,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) { unsigned periods, fragsize; snd_pcm_uframes_t buffer_size; size_t frame_size; - + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { fprintf(stderr, __FILE__": failed to parse module arguments\n"); goto fail; 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); } diff --git a/polyp/scache.c b/polyp/scache.c new file mode 100644 index 00000000..d2a84827 --- /dev/null +++ b/polyp/scache.c @@ -0,0 +1,181 @@ +#include +#include +#include + +#include "scache.h" +#include "sink-input.h" + +static void free_entry(struct pa_scache_entry *e) { + assert(e); + free(e->name); + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + free(e); +} + +void pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index) { + struct pa_scache_entry *e; + int put; + assert(c && name); + + if (c->scache_hashmap && (e = pa_hashmap_get(c->scache_hashmap, name))) { + put = 0; + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + } else { + put = 1; + e = malloc(sizeof(struct pa_scache_entry)); + assert(e); + e->name = strdup(name); + assert(e->name); + } + + if (ss) + e->sample_spec = *ss; + else + memset(&e->sample_spec, 0, sizeof(struct pa_sample_spec)); + + if (chunk) { + e->memchunk = *chunk; + pa_memblock_ref(e->memchunk.memblock); + } else { + e->memchunk.memblock = NULL; + e->memchunk.index = e->memchunk.length = 0; + } + + if (put) { + if (!c->scache_hashmap) { + c->scache_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->scache_hashmap); + } + + if (!c->scache_idxset) { + c->scache_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + assert(c->scache_idxset); + } + + pa_idxset_put(c->scache_idxset, e, &e->index); + pa_hashmap_put(c->scache_hashmap, e->name, e); + } + + if (index) + *index = e->index; +} + +int pa_scache_remove_item(struct pa_core *c, const char *name) { + struct pa_scache_entry *e; + assert(c && name); + + if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) + return -1; + + pa_hashmap_remove(c->scache_hashmap, name); + pa_idxset_remove_by_index(c->scache_idxset, e->index); + free_entry(e); + return 0; +} + +static void free_cb(void *p, void *userdata) { + struct pa_scache_entry *e = p; + assert(e); + free_entry(e); +} + +void pa_scache_free(struct pa_core *c) { + assert(c); + + if (c->scache_hashmap) { + pa_hashmap_free(c->scache_hashmap, free_cb, NULL); + c->scache_hashmap = NULL; + } + + if (c->scache_idxset) { + pa_idxset_free(c->scache_idxset, NULL, NULL); + c->scache_idxset = NULL; + } +} + +static void sink_input_kill(struct pa_sink_input *i) { + struct pa_memchunk *c; + assert(i && i->userdata); + c = i->userdata; + + pa_memblock_unref(c->memblock); + free(c); + pa_sink_input_free(i); +} + +static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { + struct pa_memchunk *c; + assert(i && chunk && i->userdata); + c = i->userdata; + + assert(c->length && c->memblock && c->memblock->length); + *chunk = *c; + pa_memblock_ref(c->memblock); + + return 0; +} + +static void sink_input_drop(struct pa_sink_input *i, size_t length) { + struct pa_memchunk *c; + assert(i && length && i->userdata); + c = i->userdata; + + assert(length <= c->length); + + c->length -= length; + c->index += length; + + if (c->length <= 0) + sink_input_kill(i); +} + +int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume) { + struct pa_sink_input *si; + struct pa_scache_entry *e; + struct pa_memchunk *chunk; + assert(c && name && sink); + + if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) + return -1; + + if (!e->memchunk.memblock) + return -1; + + if (!(si = pa_sink_input_new(sink, name, &e->sample_spec))) + return -1; + + si->volume = volume; + + si->peek = sink_input_peek; + si->drop = sink_input_drop; + si->kill = sink_input_kill; + si->userdata = chunk = malloc(sizeof(struct pa_memchunk)); + assert(chunk); + *chunk = e->memchunk; + pa_memblock_ref(chunk->memblock); + + return 0; +} + +const char * pa_scache_get_name_by_id(struct pa_core *c, uint32_t id) { + struct pa_scache_entry *e; + assert(c && id != PA_IDXSET_INVALID); + + if (!c->scache_idxset || !(e = pa_idxset_get_by_index(c->scache_idxset, id))) + return NULL; + + return e->name; + +} + +uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name) { + struct pa_scache_entry *e; + assert(c && name); + + if (!c->scache_hashmap || !(e = pa_hashmap_get(c->scache_hashmap, name))) + return PA_IDXSET_INVALID; + + return e->index; +} diff --git a/polyp/scache.h b/polyp/scache.h new file mode 100644 index 00000000..73759b81 --- /dev/null +++ b/polyp/scache.h @@ -0,0 +1,24 @@ +#ifndef fooscachehfoo +#define fooscachehfoo + +#include "core.h" +#include "memchunk.h" +#include "sink.h" + +struct pa_scache_entry { + uint32_t index; + char *name; + struct pa_sample_spec sample_spec; + struct pa_memchunk memchunk; +}; + +void pa_scache_add_item(struct pa_core *c, const char *name, struct pa_sample_spec *ss, struct pa_memchunk *chunk, uint32_t *index); + +int pa_scache_remove_item(struct pa_core *c, const char *name); +int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume); +void pa_scache_free(struct pa_core *c); + +const char *pa_scache_get_name_by_id(struct pa_core *c, uint32_t id); +uint32_t pa_scache_get_id_by_name(struct pa_core *c, const char *name); + +#endif -- cgit