From 24291aff27c671c11619684cb10d3b36fdf87c0d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 3 Aug 2004 19:26:56 +0000 Subject: sample cache work git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@102 fefdeb5f-60dc-0310-8127-8f9354f1896f --- configure.ac | 4 + doc/todo | 12 +- polyp/Makefile.am | 15 +- polyp/cli-command.c | 55 +++++++ polyp/clitext.c | 5 +- polyp/module-alsa-sink.c | 4 +- polyp/module-alsa-source.c | 2 +- polyp/module-oss-mmap.c | 2 +- polyp/module-oss.c | 4 +- polyp/module-x11-bell.c | 2 +- polyp/native-common.h | 18 +++ polyp/pactl.c | 162 ++++++++++++++++++- polyp/pdispatch.c | 10 +- polyp/polyplib-def.h | 3 +- polyp/polyplib.c | 294 +++++++++++++++++++++++++--------- polyp/polyplib.h | 6 + polyp/protocol-esound.c | 6 +- polyp/protocol-native.c | 383 ++++++++++++++++++++++++++++++++++++--------- polyp/protocol-simple.c | 6 +- polyp/pstream.c | 7 +- polyp/resampler.c | 4 +- polyp/sample-util.c | 2 +- polyp/sample.c | 9 +- polyp/sample.h | 5 +- polyp/scache.c | 60 +------ polyp/sink.c | 2 +- polyp/source.c | 2 +- 27 files changed, 834 insertions(+), 250 deletions(-) diff --git a/configure.ac b/configure.ac index 3028228a..9e5352a5 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,10 @@ PKG_CHECK_MODULES(LIBSAMPLERATE, [ samplerate >= 0.1.0 ]) AC_SUBST(LIBSAMPLERATE_CFLAGS) AC_SUBST(LIBSAMPLERATE_LIBS) +PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.0 ]) +AC_SUBST(LIBSNDFILE_CFLAGS) +AC_SUBST(LIBSNDFILE_LIBS) + PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.0 ]) AC_SUBST(ASOUNDLIB_CFLAGS) AC_SUBST(ASOUNDLIB_LIBS) diff --git a/doc/todo b/doc/todo index 73401e7a..3f04bfca 100644 --- a/doc/todo +++ b/doc/todo @@ -2,16 +2,14 @@ *** 0.2 *** -- enable searchdir - -- scache remove() -- scache in native protocol -- scache/debug.h copyright - - future cancellation - autoloading/autounloading -- doxygen - make mcalign merge chunks + +- doxygen + +- scache.[ch]/module-x11-bell.c/debug.h copyright +- enable searchdir - autoscan - rename clitext.[ch] to cli-text.[ch] diff --git a/polyp/Makefile.am b/polyp/Makefile.am index abe1d074..48b984f8 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -107,11 +107,13 @@ polypaudio_SOURCES = idxset.c idxset.h \ clitext.c clitext.h \ tokenizer.c tokenizer.h \ dynarray.c dynarray.h \ - scache.c scache.h + scache.c scache.h \ + sound-file.c sound-file.h \ + play-memchunk.c play-memchunk.h -polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) +polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) polypaudio_INCLUDES = $(INCLTDL) -polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) +polypaudio_LDADD = $(AM_LDADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) polypaudio_LDFLAGS=-export-dynamic libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h @@ -270,7 +272,8 @@ libpolyp_la_SOURCES = polyplib.c polyplib.h \ memchunk.c memchunk.h \ authkey.c authkey.h \ socket-util.c socket-util.h \ - native-common.h + native-common.h \ + sample.c sample.h libpolyp_la_CFLAGS = $(AM_CFLAGS) libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \ @@ -290,8 +293,8 @@ pacat_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la pacat_CFLAGS = $(AM_CFLAGS) pactl_SOURCES = pactl.c -pactl_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la -pactl_CFLAGS = $(AM_CFLAGS) +pactl_LDADD = $(AM_LDADD) libpolyp.la libpolyp-error.la libpolyp-mainloop.la $(LIBSNDFILE_LIBS) +pactl_CFLAGS = $(AM_CFLAGS) $(LIBSDNFILE_CFLAGS) pacat_simple_SOURCES = pacat-simple.c pacat_simple_LDADD = $(AM_LDADD) libpolyp-simple.la libpolyp-error.la diff --git a/polyp/cli-command.c b/polyp/cli-command.c index 03bd125b..826789ce 100644 --- a/polyp/cli-command.c +++ b/polyp/cli-command.c @@ -42,6 +42,8 @@ #include "clitext.h" #include "scache.h" #include "sample-util.h" +#include "sound-file.h" +#include "play-memchunk.h" struct command { const char *name; @@ -72,6 +74,8 @@ static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokeni 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 int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_play_file(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 }, @@ -98,6 +102,8 @@ static const struct command commands[] = { { "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}, + { "scache_load", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: filename,name)", 3}, + { "play_file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, { NULL, NULL, NULL, 0 } }; @@ -488,6 +494,55 @@ static int pa_cli_command_scache_remove(struct pa_core *c, struct pa_tokenizer * return 0; } +static int pa_cli_command_scache_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *fname, *n; + struct pa_memchunk chunk; + struct pa_sample_spec ss; + assert(c && t && buf && fail && verbose); + + if (!(fname = pa_tokenizer_get(t, 1)) || !(n = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n"); + return -1; + } + + if (pa_sound_file_load(fname, &ss, &chunk) < 0) { + pa_strbuf_puts(buf, "Failed to load sound file.\n"); + return -1; + } + + pa_scache_add_item(c, n, &ss, &chunk, NULL); + pa_memblock_unref(chunk.memblock); + return 0; +} + +static int pa_cli_command_play_file(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *fname, *sink_name; + struct pa_memchunk chunk; + struct pa_sample_spec ss; + struct pa_sink *sink; + int ret; + assert(c && t && buf && fail && verbose); + + if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a file 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_sound_file_load(fname, &ss, &chunk) < 0) { + pa_strbuf_puts(buf, "Failed to load sound file.\n"); + return -1; + } + + ret = pa_play_memchunk(sink, fname, &ss, &chunk, PA_VOLUME_NORM); + pa_memblock_unref(chunk.memblock); + return ret; +} + 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 d0c3f9a7..fbce2223 100644 --- a/polyp/clitext.c +++ b/polyp/clitext.c @@ -225,12 +225,13 @@ char *pa_scache_list_to_string(struct pa_core *c) { 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", + s, " name: <%s>\n\tindex: <%i>\n\tsample_spec: <%s>\n\tlength: <%u>\n\tduration: <%0.1fs>\n\tvolume: <0x%04x>\n", e->name, e->index, ss, e->memchunk.length, - l); + l, + e->volume); } } diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c index c250d1cf..c1958227 100644 --- a/polyp/module-alsa-sink.c +++ b/polyp/module-alsa-sink.c @@ -142,7 +142,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) { if (frames < 0) frames = 0; - return pa_samples_usec(frames * u->frame_size, &s->sample_spec); + return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); } int pa_module_init(struct pa_core *c, struct pa_module*m) { @@ -165,7 +165,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) { fprintf(stderr, __FILE__": failed to parse sample specification\n"); goto fail; } - frame_size = pa_sample_size(&ss); + frame_size = pa_frame_size(&ss); periods = 12; fragsize = 1024; diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c index 287a0350..a453944e 100644 --- a/polyp/module-alsa-source.c +++ b/polyp/module-alsa-source.c @@ -149,7 +149,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) { fprintf(stderr, __FILE__": failed to parse sample specification\n"); goto fail; } - frame_size = pa_sample_size(&ss); + frame_size = pa_frame_size(&ss); periods = 12; fragsize = 1024; diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c index 800eaf25..30ff3ce6 100644 --- a/polyp/module-oss-mmap.c +++ b/polyp/module-oss-mmap.c @@ -198,7 +198,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) { assert(s && u); do_write(u); - return pa_samples_usec(u->out_fill, &s->sample_spec); + return pa_bytes_to_usec(u->out_fill, &s->sample_spec); } int pa_module_init(struct pa_core *c, struct pa_module*m) { diff --git a/polyp/module-oss.c b/polyp/module-oss.c index d727534a..51585ca9 100644 --- a/polyp/module-oss.c +++ b/polyp/module-oss.c @@ -153,7 +153,7 @@ static uint32_t sink_get_latency_cb(struct pa_sink *s) { return 0; } - return pa_samples_usec(arg, &s->sample_spec); + return pa_bytes_to_usec(arg, &s->sample_spec); } int pa_module_init(struct pa_core *c, struct pa_module*m) { @@ -258,7 +258,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) { u->memchunk.memblock = NULL; u->memchunk.length = 0; - u->sample_size = pa_sample_size(&ss); + u->sample_size = pa_frame_size(&ss); u->out_fragment_size = out_frag_size; u->in_fragment_size = in_frag_size; diff --git a/polyp/module-x11-bell.c b/polyp/module-x11-bell.c index 29663134..4da3c880 100644 --- a/polyp/module-x11-bell.c +++ b/polyp/module-x11-bell.c @@ -110,7 +110,7 @@ int pa_module_init(struct pa_core *c, struct pa_module*m) { u->core = c; u->display = NULL; u->x11_sources = NULL; - u->scache_item = strdup(pa_modargs_get_value(ma, "sample", "bell")); + u->scache_item = strdup(pa_modargs_get_value(ma, "sample", "x11-bell")); assert(u->scache_item); if (pa_modargs_get_sink_index(ma, c, &u->sink_index) < 0) { diff --git a/polyp/native-common.h b/polyp/native-common.h index 2acbae8e..dc730e4b 100644 --- a/polyp/native-common.h +++ b/polyp/native-common.h @@ -41,6 +41,24 @@ enum { PA_COMMAND_RECORD_STREAM_KILLED, PA_COMMAND_STAT, PA_COMMAND_GET_PLAYBACK_LATENCY, + + PA_COMMAND_CREATE_UPLOAD_STREAM, + PA_COMMAND_DELETE_UPLOAD_STREAM, + PA_COMMAND_FINISH_UPLOAD_STREAM, + PA_COMMAND_PLAY_SAMPLE, + PA_COMMAND_REMOVE_SAMPLE, + + PA_COMMAND_GET_SINK, + PA_COMMAND_GET_SOURCE, + PA_COMMAND_GET_MODULE, + PA_COMMAND_GET_CLIENT, + PA_COMMAND_GET_SINK_INPUT, + PA_COMMAND_GET_SOURCE_OUTPUT, + PA_COMMAND_GET_SAMPLE, + + PA_COMMAND_SUBSCRIBE, + PA_COMMAND_SUBSCRIBE_EVENT, + PA_COMMAND_MAX }; diff --git a/polyp/pactl.c b/polyp/pactl.c index 61060c46..28b187b0 100644 --- a/polyp/pactl.c +++ b/polyp/pactl.c @@ -30,19 +30,37 @@ #include #include #include +#include + +#include #include #include #include #include +#include + +#define BUFSIZE 1024 static struct pa_context *context = NULL; static struct pa_mainloop_api *mainloop_api = NULL; +static char **process_argv = NULL; + +static SNDFILE *sndfile = NULL; +static struct pa_stream *sample_stream = NULL; +static struct pa_sample_spec sample_spec; +static size_t sample_length = 0; + +static char *sample_name = NULL; + static enum { NONE, EXIT, - STAT + STAT, + UPLOAD_SAMPLE, + PLAY_SAMPLE, + REMOVE_SAMPLE } action = NONE; static void quit(int ret) { @@ -78,6 +96,79 @@ static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, drain(); } +static void play_sample_callback(struct pa_context *c, int success, void *userdata) { + if (!success) { + fprintf(stderr, "Failed to play sample: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + drain(); +} + +static void remove_sample_callback(struct pa_context *c, int success, void *userdata) { + if (!success) { + fprintf(stderr, "Failed to remove sample: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + drain(); +} + +static void stream_die_callback(struct pa_stream *s, void *userdata) { + assert(s); + fprintf(stderr, "Stream deleted, exiting.\n"); + quit(1); +} + +static void finish_sample_callback(struct pa_stream *s, int success, void *userdata) { + assert(s); + + if (!success) { + fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(context))); + quit(1); + return; + } + + drain(); +} + +static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) { + sf_count_t l; + float *d; + assert(s && length && sndfile); + + d = malloc(length); + assert(d); + + assert(sample_length >= length); + l = length/pa_frame_size(&sample_spec); + + if ((sf_readf_float(sndfile, d, l)) != l) { + free(d); + fprintf(stderr, "Premature end of file\n"); + quit(1); + } + + pa_stream_write(s, d, length); + free(d); + + sample_length -= length; + + if (sample_length <= 0) { + pa_stream_set_write_callback(sample_stream, NULL, NULL); + pa_stream_finish_sample(sample_stream, finish_sample_callback, NULL); + } +} + +static void upload_callback(struct pa_stream *s, int success, void *userdata) { + if (!success) { + fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(context))); + quit(1); + } +} + static void context_complete_callback(struct pa_context *c, int success, void *userdata) { assert(c); @@ -90,7 +181,19 @@ static void context_complete_callback(struct pa_context *c, int success, void *u if (action == STAT) pa_context_stat(c, stat_callback, NULL); - else { + else if (action == PLAY_SAMPLE) + pa_context_play_sample(c, process_argv[2], NULL, 0x100, play_sample_callback, NULL); + else if (action == REMOVE_SAMPLE) + pa_context_remove_sample(c, process_argv[2], remove_sample_callback, NULL); + else if (action == UPLOAD_SAMPLE) { + if (!(sample_stream = pa_context_upload_sample(c, sample_name, &sample_spec, sample_length, upload_callback, NULL))) { + fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(c))); + goto fail; + } + + pa_stream_set_die_callback(sample_stream, stream_die_callback, NULL); + pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL); + } else { assert(action == EXIT); pa_context_exit(c); drain(); @@ -105,11 +208,12 @@ fail: static void exit_signal_callback(void *id, int sig, void *userdata) { fprintf(stderr, "Got SIGINT, exiting.\n"); quit(0); - } int main(int argc, char *argv[]) { struct pa_mainloop* m = NULL; + char tmp[PATH_MAX]; + int ret = 1, r; if (argc >= 2) { @@ -117,13 +221,60 @@ int main(int argc, char *argv[]) { action = STAT; else if (!strcmp(argv[1], "exit")) action = EXIT; + else if (!strcmp(argv[1], "scache_upload")) { + struct SF_INFO sfinfo; + action = UPLOAD_SAMPLE; + + if (argc < 3) { + fprintf(stderr, "Please specify a sample file to load\n"); + goto quit; + } + + if (argc >= 4) + sample_name = argv[3]; + else { + char *f = strrchr(argv[2], '/'); + if (f) + f++; + else + f = argv[2]; + + strncpy(sample_name = tmp, f, strcspn(f, ".")); + } + + memset(&sfinfo, 0, sizeof(sfinfo)); + if (!(sndfile = sf_open(argv[2], SFM_READ, &sfinfo))) { + fprintf(stderr, "Failed to open sound file.\n"); + goto quit; + } + + sample_spec.format = PA_SAMPLE_FLOAT32; + sample_spec.rate = sfinfo.samplerate; + sample_spec.channels = sfinfo.channels; + + sample_length = sfinfo.frames*pa_frame_size(&sample_spec); + } else if (!strcmp(argv[1], "scache_play")) { + action = PLAY_SAMPLE; + if (argc < 3) { + fprintf(stderr, "You have to specify a sample name to play\n"); + goto quit; + } + } else if (!strcmp(argv[1], "scache_remove")) { + action = REMOVE_SAMPLE; + if (argc < 3) { + fprintf(stderr, "You have to specify a sample name to remove\n"); + goto quit; + } + } } if (action == NONE) { - fprintf(stderr, "No valid action specified. Use one of: stat, exit\n"); + fprintf(stderr, "No valid action specified. Use one of: stat, exit, scache_upload, scache_play, scache_remove\n"); goto quit; } + process_argv = argv; + if (!(m = pa_mainloop_new())) { fprintf(stderr, "pa_mainloop_new() failed.\n"); goto quit; @@ -162,5 +313,8 @@ quit: pa_mainloop_free(m); } + if (sndfile) + sf_close(sndfile); + return ret; } diff --git a/polyp/pdispatch.c b/polyp/pdispatch.c index a28458a4..2ab98b52 100644 --- a/polyp/pdispatch.c +++ b/polyp/pdispatch.c @@ -30,6 +30,8 @@ #include "pdispatch.h" #include "native-common.h" +/*#define DEBUG_OPCODES*/ + #ifdef DEBUG_OPCODES static const char *command_names[PA_COMMAND_MAX] = { @@ -51,6 +53,11 @@ static const char *command_names[PA_COMMAND_MAX] = { [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", [PA_COMMAND_STAT] = "STAT", [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", + [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM", + [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM", + [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM", + [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE", + [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE", }; #endif @@ -108,12 +115,13 @@ struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const st pd->drain_userdata = NULL; pd->in_use = pd->shall_free = 0; + return pd; } void pa_pdispatch_free(struct pa_pdispatch *pd) { assert(pd); - + if (pd->in_use) { pd->shall_free = 1; return; diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h index e43f4b35..ec2d528b 100644 --- a/polyp/polyplib-def.h +++ b/polyp/polyplib-def.h @@ -26,7 +26,8 @@ enum pa_stream_direction { PA_STREAM_PLAYBACK, - PA_STREAM_RECORD + PA_STREAM_RECORD, + PA_STREAM_UPLOAD }; struct pa_buffer_attr { diff --git a/polyp/polyplib.c b/polyp/polyplib.c index ea5003b4..b1052a8d 100644 --- a/polyp/polyplib.c +++ b/polyp/polyplib.c @@ -81,6 +81,12 @@ struct pa_context { void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata); void *stat_userdata; + + void (*play_sample_callback)(struct pa_context*c, int success, void *userdata); + void *play_sample_userdata; + + void (*remove_sample_callback)(struct pa_context*c, int success, void *userdata); + void *remove_sample_userdata; uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; }; @@ -92,12 +98,12 @@ struct pa_stream { char *name; struct pa_buffer_attr buffer_attr; struct pa_sample_spec sample_spec; - uint32_t device_index; uint32_t channel; int channel_valid; + uint32_t device_index; enum pa_stream_direction direction; - enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state; + enum { STREAM_CREATING, STREAM_READY, STREAM_DEAD} state; uint32_t requested_bytes; void (*read_callback)(struct pa_stream *p, const void*data, size_t length, void *userdata); @@ -117,6 +123,9 @@ struct pa_stream { void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata); void *get_latency_userdata; + + void (*finish_sample_callback)(struct pa_stream*c, int success, void *userdata); + void *finish_sample_userdata; }; static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); @@ -167,6 +176,12 @@ struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char * c->stat_callback = NULL; c->stat_userdata = NULL; + c->play_sample_callback = NULL; + c->play_sample_userdata = NULL; + + c->remove_sample_callback = NULL; + c->remove_sample_userdata = NULL; + pa_check_for_sigpipe(); return c; } @@ -494,7 +509,7 @@ static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t return; s->requested_bytes += bytes; - + if (s->requested_bytes && s->write_callback) s->write_callback(s, s->requested_bytes, s->write_userdata); } @@ -517,7 +532,7 @@ static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, ui } if (pa_tagstruct_getu32(t, &s->channel) < 0 || - pa_tagstruct_getu32(t, &s->device_index) < 0 || + ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) || !pa_tagstruct_eof(t)) { s->context->error = PA_ERROR_PROTOCOL; context_dead(s->context); @@ -525,14 +540,14 @@ static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, ui } s->channel_valid = 1; - pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s); + pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); s->state = STREAM_READY; if (s->create_complete_callback) s->create_complete_callback(s, 1, s->create_complete_userdata); } -static void create_stream(struct pa_stream *s, uint32_t tdev_index) { +static void create_stream(struct pa_stream *s, const char *dev) { struct pa_tagstruct *t; uint32_t tag; assert(s); @@ -546,7 +561,8 @@ static void create_stream(struct pa_stream *s, uint32_t tdev_index) { pa_tagstruct_putu32(t, tag = s->context->ctag++); pa_tagstruct_puts(t, s->name); pa_tagstruct_put_sample_spec(t, &s->sample_spec); - pa_tagstruct_putu32(t, tdev_index); + pa_tagstruct_putu32(t, (uint32_t) -1); + pa_tagstruct_puts(t, dev ? dev : ""); pa_tagstruct_putu32(t, s->buffer_attr.maxlength); if (s->direction == PA_STREAM_PLAYBACK) { pa_tagstruct_putu32(t, s->buffer_attr.tlength); @@ -559,49 +575,42 @@ static void create_stream(struct pa_stream *s, uint32_t tdev_index) { pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, s); } -static void lookup_device_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s = userdata; - uint32_t tdev; - assert(pd && s && s->state == STREAM_LOOKING_UP); +static struct pa_stream *internal_stream_new(struct pa_context *c) { + struct pa_stream *s; - if (command != PA_COMMAND_REPLY) { - if (handle_error(s->context, command, t) < 0) { - context_dead(s->context); - return; - } + s = malloc(sizeof(struct pa_stream)); + assert(s); + s->context = c; - stream_dead(s); - if (s->create_complete_callback) - s->create_complete_callback(s, 0, s->create_complete_userdata); - return; - } + s->read_callback = NULL; + s->read_userdata = NULL; + s->write_callback = NULL; + s->write_userdata = NULL; + s->die_callback = NULL; + s->die_userdata = NULL; + s->create_complete_callback = NULL; + s->create_complete_userdata = NULL; + s->get_latency_callback = NULL; + s->get_latency_userdata = NULL; + s->finish_sample_callback = NULL; + s->finish_sample_userdata = NULL; - if (pa_tagstruct_getu32(t, &tdev) < 0 || - !pa_tagstruct_eof(t)) { - s->context->error = PA_ERROR_PROTOCOL; - context_dead(s->context); - return; - } - - create_stream(s, tdev); -} + s->name = NULL; + s->state = STREAM_CREATING; + s->requested_bytes = 0; + s->channel = 0; + s->channel_valid = 0; + s->device_index = (uint32_t) -1; -static void lookup_device(struct pa_stream *s, const char *tdev) { - struct pa_tagstruct *t; - uint32_t tag; - assert(s); + memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); - s->state = STREAM_LOOKING_UP; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_LOOKUP_SINK : PA_COMMAND_LOOKUP_SOURCE); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_puts(t, tdev); + s->next = c->first_stream; + if (s->next) + s->next->previous = s; + s->previous = NULL; + c->first_stream = s; - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s); + return s; } struct pa_stream* pa_stream_new( @@ -616,29 +625,15 @@ struct pa_stream* pa_stream_new( struct pa_stream *s; - assert(c && name && ss && c->state == CONTEXT_READY); - - s = malloc(sizeof(struct pa_stream)); + assert(c && name && ss && c->state == CONTEXT_READY && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD)); + + s = internal_stream_new(c); assert(s); - s->context = c; - s->read_callback = NULL; - s->read_userdata = NULL; - s->write_callback = NULL; - s->write_userdata = NULL; - s->die_callback = NULL; - s->die_userdata = NULL; s->create_complete_callback = complete; - s->create_complete_userdata = NULL; - s->get_latency_callback = NULL; - s->get_latency_userdata = NULL; - + s->create_complete_userdata = userdata; s->name = strdup(name); s->state = STREAM_CREATING; - s->requested_bytes = 0; - s->channel = 0; - s->channel_valid = 0; - s->device_index = (uint32_t) -1; s->direction = dir; s->sample_spec = *ss; if (attr) @@ -651,16 +646,7 @@ struct pa_stream* pa_stream_new( s->buffer_attr.fragsize = DEFAULT_FRAGSIZE; } - s->next = c->first_stream; - if (s->next) - s->next->previous = s; - s->previous = NULL; - c->first_stream = s; - - if (dev) - lookup_device(s, dev); - else - create_stream(s, (uint32_t) -1); + create_stream(s, dev); return s; } @@ -677,7 +663,8 @@ void pa_stream_free(struct pa_stream *s) { struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_DELETE_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : + (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)); pa_tagstruct_putu32(t, s->context->ctag++); pa_tagstruct_putu32(t, s->channel); pa_pstream_send_tagstruct(s->context->pstream, t); @@ -697,7 +684,6 @@ void pa_stream_free(struct pa_stream *s) { } void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) { - assert(s && cb); s->write_callback = cb; s->write_userdata = userdata; } @@ -971,3 +957,163 @@ void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, pa_pstream_send_tagstruct(p->context->pstream, t); pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p); } + +struct pa_stream* pa_context_upload_sample(struct pa_context *c, const char *name, const struct pa_sample_spec *ss, size_t length, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata) { + struct pa_stream *s; + struct pa_tagstruct *t; + uint32_t tag; + + s = internal_stream_new(c); + assert(s); + + s->create_complete_callback = cb; + s->create_complete_userdata = userdata; + s->name = strdup(name); + s->state = STREAM_CREATING; + s->direction = PA_STREAM_UPLOAD; + s->sample_spec = *ss; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_sample_spec(t, ss); + pa_tagstruct_putu32(t, length); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, s); + + return s; +} + +static void stream_finish_sample_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s = userdata; + assert(pd && s); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(s->context, command, t) < 0) { + context_dead(s->context); + return; + } + + if (s->finish_sample_callback) + s->finish_sample_callback(s, 0, s->finish_sample_userdata); + return; + } + + if (!pa_tagstruct_eof(t)) { + s->context->error = PA_ERROR_PROTOCOL; + context_dead(s->context); + return; + } + + if (s->finish_sample_callback) + s->finish_sample_callback(s, 1, s->finish_sample_userdata); +} + +void pa_stream_finish_sample(struct pa_stream *p, void (*cb)(struct pa_stream*s, int success, void *userdata), void *userdata) { + struct pa_tagstruct *t; + uint32_t tag; + assert(p); + + p->finish_sample_callback = cb; + p->finish_sample_userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = p->context->ctag++); + pa_tagstruct_putu32(t, p->channel); + pa_pstream_send_tagstruct(p->context->pstream, t); + pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_finish_sample_callback, p); +} + +static void context_play_sample_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + assert(pd && c); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(c, command, t) < 0) { + context_dead(c); + return; + } + + if (c->play_sample_callback) + c->play_sample_callback(c, 0, c->play_sample_userdata); + return; + } + + if (!pa_tagstruct_eof(t)) { + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + return; + } + + if (c->play_sample_callback) + c->play_sample_callback(c, 1, c->play_sample_userdata); +} + +void pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) { + struct pa_tagstruct *t; + uint32_t tag; + assert(c && name && *name && (!dev || *dev)); + + if (!volume) + return; + + c->play_sample_callback = cb; + c->play_sample_userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, (uint32_t) -1); + pa_tagstruct_puts(t, dev ? dev : ""); + pa_tagstruct_putu32(t, volume); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_play_sample_callback, c); +} + +static void context_remove_sample_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + assert(pd && c); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(c, command, t) < 0) { + context_dead(c); + return; + } + + if (c->remove_sample_callback) + c->remove_sample_callback(c, 0, c->remove_sample_userdata); + return; + } + + if (!pa_tagstruct_eof(t)) { + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + return; + } + + if (c->remove_sample_callback) + c->remove_sample_callback(c, 1, c->remove_sample_userdata); +} + +void pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata) { + struct pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + c->remove_sample_callback = cb; + c->remove_sample_userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_remove_sample_callback, c); +} diff --git a/polyp/polyplib.h b/polyp/polyplib.h index 440b9658..391cb0c8 100644 --- a/polyp/polyplib.h +++ b/polyp/polyplib.h @@ -91,4 +91,10 @@ void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, struct pa_context* pa_stream_get_context(struct pa_stream *p); +struct pa_stream* pa_context_upload_sample(struct pa_context *c, const char *name, const struct pa_sample_spec *ss, size_t length, void (*cb) (struct pa_stream*s, int success, void *userdata), void *userdata); +void pa_stream_finish_sample(struct pa_stream *p, void (*cb)(struct pa_stream*s, int success, void *userdata), void *userdata); + +void pa_context_play_sample(struct pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata); +void pa_context_remove_sample(struct pa_context *c, const char *name, void (*cb)(struct pa_context *c, int success, void *userdata), void *userdata); + #endif diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c index 5db0442f..d80445de 100644 --- a/polyp/protocol-esound.c +++ b/polyp/protocol-esound.c @@ -312,7 +312,7 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons assert(!c->input_memblockq); l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); assert(c->input_memblockq); pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); c->playback.fragment_size = l/10; @@ -376,7 +376,7 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co assert(!c->output_memblockq); l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), 0, 0); + c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0); assert(c->output_memblockq); pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); @@ -936,7 +936,7 @@ static void sink_input_kill_cb(struct pa_sink_input *i) { static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { struct connection*c = i->userdata; assert(i && c); - return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); + return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); } /*** source_output callbacks ***/ diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index 83c910d1..a2332a12 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -40,6 +40,7 @@ #include "pstream-util.h" #include "authkey.h" #include "namereg.h" +#include "scache.h" struct connection; struct pa_protocol_native; @@ -53,6 +54,7 @@ struct record_stream { }; struct playback_stream { + int type; struct connection *connection; uint32_t index; struct pa_sink_input *sink_input; @@ -62,13 +64,32 @@ struct playback_stream { uint32_t drain_tag; }; +struct upload_stream { + int type; + struct connection *connection; + uint32_t index; + struct pa_memchunk memchunk; + size_t length; + char *name; + struct pa_sample_spec sample_spec; +}; + +struct output_stream { + int type; +}; + +enum { + UPLOAD_STREAM, + PLAYBACK_STREAM +}; + struct connection { int authorized; struct pa_protocol_native *protocol; struct pa_client *client; struct pa_pstream *pstream; struct pa_pdispatch *pdispatch; - struct pa_idxset *record_streams, *playback_streams; + struct pa_idxset *record_streams, *output_streams; uint32_t rrobin_index; }; @@ -93,25 +114,28 @@ static void source_output_push_cb(struct pa_source_output *o, const struct pa_me static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_delete_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_remove_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = { NULL }, [PA_COMMAND_TIMEOUT] = { NULL }, [PA_COMMAND_REPLY] = { NULL }, [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { command_create_playback_stream }, - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_playback_stream }, + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_stream }, [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = { command_drain_playback_stream }, [PA_COMMAND_CREATE_RECORD_STREAM] = { command_create_record_stream }, - [PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_record_stream }, + [PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_stream }, [PA_COMMAND_AUTH] = { command_auth }, [PA_COMMAND_REQUEST] = { NULL }, [PA_COMMAND_EXIT] = { command_exit }, @@ -120,11 +144,51 @@ static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup }, [PA_COMMAND_STAT] = { command_stat }, [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency }, + [PA_COMMAND_CREATE_UPLOAD_STREAM] = { command_create_upload_stream }, + [PA_COMMAND_DELETE_UPLOAD_STREAM] = { command_delete_stream }, + [PA_COMMAND_FINISH_UPLOAD_STREAM] = { command_finish_upload_stream }, + [PA_COMMAND_PLAY_SAMPLE] = { command_play_sample }, + [PA_COMMAND_REMOVE_SAMPLE] = { command_remove_sample }, }; /* structure management */ -static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) { +static struct upload_stream* upload_stream_new(struct connection *c, const struct pa_sample_spec *ss, const char *name, size_t length) { + struct upload_stream *s; + assert(c && ss && name && length); + + s = malloc(sizeof(struct upload_stream)); + assert (s); + s->type = UPLOAD_STREAM; + s->connection = c; + s->sample_spec = *ss; + s->name = strdup(name); + assert(s->name); + + s->memchunk.memblock = NULL; + s->memchunk.index = 0; + s->memchunk.length = 0; + + s->length = length; + + pa_idxset_put(c->output_streams, s, &s->index); + return s; +} + +static void upload_stream_free(struct upload_stream *o) { + assert(o && o->connection); + + pa_idxset_remove_by_data(o->connection->output_streams, o, NULL); + + free(o->name); + + if (o->memchunk.memblock) + pa_memblock_unref(o->memchunk.memblock); + + free(o); +} + +static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, const struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) { struct record_stream *s; struct pa_source_output *source_output; size_t base; @@ -143,7 +207,7 @@ static struct record_stream* record_stream_new(struct connection *c, struct pa_s s->source_output->owner = c->protocol->module; s->source_output->client = c->client; - s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_sample_size(ss), 0, 0); + s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0); assert(s->memblockq); s->fragment_size = (fragment_size/base)*base; @@ -163,7 +227,7 @@ static void record_stream_free(struct record_stream* r) { free(r); } -static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name, +static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, const struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t tlength, size_t prebuf, @@ -177,6 +241,7 @@ static struct playback_stream* playback_stream_new(struct connection *c, struct s = malloc(sizeof(struct playback_stream)); assert (s); + s->type = PLAYBACK_STREAM; s->connection = c; s->sink_input = sink_input; @@ -188,13 +253,13 @@ static struct playback_stream* playback_stream_new(struct connection *c, struct s->sink_input->owner = c->protocol->module; s->sink_input->client = c->client; - s->memblockq = pa_memblockq_new(maxlength, tlength, pa_sample_size(ss), prebuf, minreq); + s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq); assert(s->memblockq); s->requested_bytes = 0; s->drain_request = 0; - pa_idxset_put(c->playback_streams, s, &s->index); + pa_idxset_put(c->output_streams, s, &s->index); return s; } @@ -204,7 +269,7 @@ static void playback_stream_free(struct playback_stream* p) { if (p->drain_request) pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY); - pa_idxset_remove_by_data(p->connection->playback_streams, p, NULL); + pa_idxset_remove_by_data(p->connection->output_streams, p, NULL); pa_sink_input_free(p->sink_input); pa_memblockq_free(p->memblockq); free(p); @@ -212,7 +277,7 @@ static void playback_stream_free(struct playback_stream* p) { static void connection_free(struct connection *c) { struct record_stream *r; - struct playback_stream *p; + struct output_stream *o; assert(c && c->protocol); pa_idxset_remove_by_data(c->protocol->connections, c, NULL); @@ -220,9 +285,12 @@ static void connection_free(struct connection *c) { record_stream_free(r); pa_idxset_free(c->record_streams, NULL, NULL); - while ((p = pa_idxset_first(c->playback_streams, NULL))) - playback_stream_free(p); - pa_idxset_free(c->playback_streams, NULL, NULL); + while ((o = pa_idxset_first(c->output_streams, NULL))) + if (o->type == PLAYBACK_STREAM) + playback_stream_free((struct playback_stream*) o); + else + upload_stream_free((struct upload_stream*) o); + pa_idxset_free(c->output_streams, NULL, NULL); pa_pdispatch_free(c->pdispatch); pa_pstream_free(c->pstream); @@ -351,7 +419,7 @@ static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { assert(i && i->userdata); s = i->userdata; - return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); + return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); } /*** source_output callbacks ***/ @@ -384,7 +452,7 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com struct playback_stream *s; size_t maxlength, tlength, prebuf, minreq; uint32_t sink_index; - const char *name; + const char *name, *sink_name; struct pa_sample_spec ss; struct pa_tagstruct *reply; struct pa_sink *sink; @@ -393,6 +461,7 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com if (pa_tagstruct_gets(t, &name) < 0 || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_getu32(t, &sink_index) < 0 || + pa_tagstruct_gets(t, &sink_name) < 0 || pa_tagstruct_getu32(t, &maxlength) < 0 || pa_tagstruct_getu32(t, &tlength) < 0 || pa_tagstruct_getu32(t, &prebuf) < 0 || @@ -407,10 +476,12 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com return; } - if (sink_index == (uint32_t) -1) + if (!*sink_name || sink_index == (uint32_t) -1) sink = pa_sink_get_default(c->protocol->core); - else + else if (sink_index != (uint32_t) -1) sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); + else + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK); if (!sink) { pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); @@ -433,10 +504,9 @@ static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t com request_bytes(s); } -static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { +static void command_delete_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { struct connection *c = userdata; uint32_t channel; - struct playback_stream *s; assert(c && t); if (pa_tagstruct_getu32(t, &channel) < 0 || @@ -449,13 +519,34 @@ static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t com pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); return; } - - if (!(s = pa_idxset_get_by_index(c->playback_streams, channel))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - playback_stream_free(s); + if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) { + struct playback_stream *s; + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + playback_stream_free(s); + } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) { + struct record_stream *s; + if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + record_stream_free(s); + } else { + struct upload_stream *s; + assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM); + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + upload_stream_free(s); + } + pa_pstream_send_simple_ack(c->pstream, tag); } @@ -464,7 +555,7 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma struct record_stream *s; size_t maxlength, fragment_size; uint32_t source_index; - const char *name; + const char *name, *source_name; struct pa_sample_spec ss; struct pa_tagstruct *reply; struct pa_source *source; @@ -473,6 +564,7 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma if (pa_tagstruct_gets(t, &name) < 0 || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_getu32(t, &source_index) < 0 || + pa_tagstruct_gets(t, &source_name) < 0 || pa_tagstruct_getu32(t, &maxlength) < 0 || pa_tagstruct_getu32(t, &fragment_size) < 0 || !pa_tagstruct_eof(t)) { @@ -485,10 +577,12 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma return; } - if (source_index == (uint32_t) -1) + if (!*source_name || source_index == (uint32_t) -1) source = pa_source_get_default(c->protocol->core); - else + else if (source_index != (uint32_t) -1) source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); + else + source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE); if (!source) { pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); @@ -510,32 +604,6 @@ static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t comma pa_pstream_send_tagstruct(c->pstream, reply); } -static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t channel; - struct record_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - record_stream_free(s); - pa_pstream_send_simple_ack(c->pstream, tag); -} - static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { struct connection *c = userdata; assert(c && t); @@ -652,7 +720,7 @@ static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t comm return; } - if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { + if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) { pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); return; } @@ -709,7 +777,7 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma return; } - if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { + if (!(s = pa_idxset_get_by_index(c->output_streams, index)) || s->type != PLAYBACK_STREAM) { pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); return; } @@ -723,6 +791,147 @@ static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t comma pa_pstream_send_tagstruct(c->pstream, reply); } +static void command_create_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct upload_stream *s; + size_t length; + const char *name; + struct pa_sample_spec ss; + struct pa_tagstruct *reply; + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_getu32(t, &length) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + if (!(s = upload_stream_new(c, &ss, name, length))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + pa_pstream_send_tagstruct(c->pstream, reply); + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REQUEST); + pa_tagstruct_putu32(reply, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(reply, s->index); + pa_tagstruct_putu32(reply, length); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_finish_upload_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t channel; + struct upload_stream *s; + uint32_t index; + assert(c && t); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->memchunk, &index); + pa_pstream_send_simple_ack(c->pstream, tag); + upload_stream_free(s); +} + +static void command_play_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t sink_index, volume; + struct pa_sink *sink; + const char *name, *sink_name; + assert(c && t); + + if (pa_tagstruct_getu32(t, &sink_index) < 0 || + pa_tagstruct_gets(t, &sink_name) < 0 || + pa_tagstruct_getu32(t, &volume) < 0 || + pa_tagstruct_gets(t, &name) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!*sink_name && sink_index == (uint32_t) -1) + sink = pa_sink_get_default(c->protocol->core); + else if (sink_index != (uint32_t) -1) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); + else + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK); + + if (!sink) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_remove_sample(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (pa_scache_remove_item(c->protocol->core, name) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + /*** pstream callbacks ***/ static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { @@ -737,25 +946,55 @@ static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *pack static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) { struct connection *c = userdata; - struct playback_stream *stream; + struct output_stream *stream; assert(p && chunk && userdata); - if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) { + if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) { fprintf(stderr, "protocol-native: client sent block for invalid stream.\n"); connection_free(c); return; } - if (chunk->length >= stream->requested_bytes) - stream->requested_bytes = 0; - else - stream->requested_bytes -= chunk->length; - - pa_memblockq_push_align(stream->memblockq, chunk, delta); - assert(stream->sink_input); - pa_sink_notify(stream->sink_input->sink); - - /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/ + if (stream->type == PLAYBACK_STREAM) { + struct playback_stream *p = (struct playback_stream*) stream; + if (chunk->length >= p->requested_bytes) + p->requested_bytes = 0; + else + p->requested_bytes -= chunk->length; + + pa_memblockq_push_align(p->memblockq, chunk, delta); + assert(p->sink_input); + pa_sink_notify(p->sink_input->sink); + /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/ + } else { + struct upload_stream *u = (struct upload_stream*) stream; + size_t l; + assert(u->type == UPLOAD_STREAM); + + if (!u->memchunk.memblock) { + if (u->length == chunk->length) { + u->memchunk = *chunk; + pa_memblock_ref(u->memchunk.memblock); + u->length = 0; + fprintf(stderr, "COPY\n"); + } else { + u->memchunk.memblock = pa_memblock_new(u->length); + u->memchunk.index = u->memchunk.length = 0; + } + } + + assert(u->memchunk.memblock); + + l = u->length; + if (l > chunk->length) + l = chunk->length; + + if (l > 0) { + memcpy(u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, chunk->memblock->data+chunk->index, l); + u->memchunk.length += l; + u->length -= l; + } + } } static void pstream_die_callback(struct pa_pstream *p, void *userdata) { @@ -811,8 +1050,8 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo assert(c->pdispatch); c->record_streams = pa_idxset_new(NULL, NULL); - c->playback_streams = pa_idxset_new(NULL, NULL); - assert(c->record_streams && c->playback_streams); + c->output_streams = pa_idxset_new(NULL, NULL); + assert(c->record_streams && c->output_streams); c->rrobin_index = PA_IDXSET_INVALID; diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c index 3a52e311..037d4f9a 100644 --- a/polyp/protocol-simple.c +++ b/polyp/protocol-simple.c @@ -221,7 +221,7 @@ static void sink_input_kill_cb(struct pa_sink_input *i) { static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { struct connection*c = i->userdata; assert(i && c); - return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); + return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); } /*** source_output callbacks ***/ @@ -319,7 +319,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo c->sink_input->userdata = c; l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); assert(c->input_memblockq); pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); c->playback.fragment_size = l/10; @@ -348,7 +348,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo c->source_output->userdata = c; l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), 0, 0); + c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0); pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); } diff --git a/polyp/pstream.c b/polyp/pstream.c index 3076b776..7d576a16 100644 --- a/polyp/pstream.c +++ b/polyp/pstream.c @@ -23,6 +23,7 @@ #include #endif +#include #include #include #include @@ -40,7 +41,7 @@ enum pa_pstream_descriptor_index { typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX]; #define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t)) -#define FRAME_SIZE_MAX (1024*64) +#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */ struct item_info { enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type; @@ -361,8 +362,10 @@ static void do_read(struct pa_pstream *p) { /* Reading of frame descriptor complete */ /* Frame size too large */ - if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) + if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) { + fprintf(stderr, "frame size too large\n"); goto die; + } assert(!p->read.packet && !p->read.memblock); diff --git a/polyp/resampler.c b/polyp/resampler.c index 320d7119..adf08e80 100644 --- a/polyp/resampler.c +++ b/polyp/resampler.c @@ -75,8 +75,8 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru r->i_ss = *a; r->o_ss = *b; - r->i_sz = pa_sample_size(a); - r->o_sz = pa_sample_size(b); + r->i_sz = pa_frame_size(a); + r->o_sz = pa_frame_size(b); r->to_float32_func = pa_get_convert_to_float32_function(a->format); r->from_float32_func = pa_get_convert_from_float32_function(b->format); diff --git a/polyp/sample-util.c b/polyp/sample-util.c index d608ce1b..5b1cd626 100644 --- a/polyp/sample-util.c +++ b/polyp/sample-util.c @@ -111,7 +111,7 @@ size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, siz void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume) { int16_t *d; size_t n; - assert(c && spec && (c->length % pa_sample_size(spec) == 0)); + assert(c && spec && (c->length % pa_frame_size(spec) == 0)); assert(spec->format == PA_SAMPLE_S16NE); if (volume == PA_VOLUME_NORM) diff --git a/polyp/sample.c b/polyp/sample.c index 8179475d..e4c4bd52 100644 --- a/polyp/sample.c +++ b/polyp/sample.c @@ -28,7 +28,7 @@ #include "sample.h" -size_t pa_sample_size(const struct pa_sample_spec *spec) { +size_t pa_frame_size(const struct pa_sample_spec *spec) { assert(spec); size_t b = 1; @@ -55,14 +55,13 @@ size_t pa_sample_size(const struct pa_sample_spec *spec) { size_t pa_bytes_per_second(const struct pa_sample_spec *spec) { assert(spec); - return spec->rate*pa_sample_size(spec); + return spec->rate*pa_frame_size(spec); } - -uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) { +uint32_t pa_bytes_to_usec(size_t length, const struct pa_sample_spec *spec) { assert(spec); - return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000); + return (uint32_t) (((double) length /pa_frame_size(spec))/spec->rate*1000000); } int pa_sample_spec_valid(const struct pa_sample_spec *spec) { diff --git a/polyp/sample.h b/polyp/sample.h index 825441f2..01a4efcf 100644 --- a/polyp/sample.h +++ b/polyp/sample.h @@ -52,12 +52,11 @@ struct pa_sample_spec { }; size_t pa_bytes_per_second(const struct pa_sample_spec *spec); -size_t pa_sample_size(const struct pa_sample_spec *spec); -uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec); +size_t pa_frame_size(const struct pa_sample_spec *spec); +uint32_t pa_bytes_to_usec(size_t length, const struct pa_sample_spec *spec); int pa_sample_spec_valid(const struct pa_sample_spec *spec); int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b); - #define PA_SAMPLE_SNPRINT_MAX_LENGTH 32 void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec); diff --git a/polyp/scache.c b/polyp/scache.c index f1f7ec5a..21af0e22 100644 --- a/polyp/scache.c +++ b/polyp/scache.c @@ -6,6 +6,8 @@ #include "scache.h" #include "sink-input.h" #include "mainloop.h" +#include "sample-util.h" +#include "play-memchunk.h" static void free_entry(struct pa_scache_entry *e) { assert(e); @@ -100,50 +102,8 @@ void pa_scache_free(struct pa_core *c) { } } -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 si_kill(void *i) { - sink_input_kill(i); -} - -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) - pa_mainloop_api_once(i->sink->core->mainloop, si_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))) @@ -151,20 +111,10 @@ int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sin 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); + if (pa_play_memchunk(sink, name, &e->sample_spec, &e->memchunk, pa_volume_multiply(volume, e->volume)) < 0) + return -1; + return 0; } diff --git a/polyp/sink.c b/polyp/sink.c index 20fa76a6..c2c873c6 100644 --- a/polyp/sink.c +++ b/polyp/sink.c @@ -41,7 +41,7 @@ struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, co char *n = NULL; char st[256]; int r; - assert(core && name && spec); + assert(core && name && *name && spec); s = malloc(sizeof(struct pa_sink)); assert(s); diff --git a/polyp/source.c b/polyp/source.c index ccde0e2f..13635414 100644 --- a/polyp/source.c +++ b/polyp/source.c @@ -36,7 +36,7 @@ struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail struct pa_source *s; char st[256]; int r; - assert(core && spec && name); + assert(core && spec && name && *name); s = malloc(sizeof(struct pa_source)); assert(s); -- cgit