summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-08-03 19:26:56 +0000
committerLennart Poettering <lennart@poettering.net>2004-08-03 19:26:56 +0000
commit24291aff27c671c11619684cb10d3b36fdf87c0d (patch)
tree25f5c7493a58b6d48b51fb1b9843fb39bbb77b8b
parente10b918009446186c80584273d2e3f5e84a6670b (diff)
sample cache work
git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@102 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r--configure.ac4
-rw-r--r--doc/todo12
-rw-r--r--polyp/Makefile.am15
-rw-r--r--polyp/cli-command.c55
-rw-r--r--polyp/clitext.c5
-rw-r--r--polyp/module-alsa-sink.c4
-rw-r--r--polyp/module-alsa-source.c2
-rw-r--r--polyp/module-oss-mmap.c2
-rw-r--r--polyp/module-oss.c4
-rw-r--r--polyp/module-x11-bell.c2
-rw-r--r--polyp/native-common.h18
-rw-r--r--polyp/pactl.c162
-rw-r--r--polyp/pdispatch.c10
-rw-r--r--polyp/polyplib-def.h3
-rw-r--r--polyp/polyplib.c294
-rw-r--r--polyp/polyplib.h6
-rw-r--r--polyp/protocol-esound.c6
-rw-r--r--polyp/protocol-native.c383
-rw-r--r--polyp/protocol-simple.c6
-rw-r--r--polyp/pstream.c7
-rw-r--r--polyp/resampler.c4
-rw-r--r--polyp/sample-util.c2
-rw-r--r--polyp/sample.c9
-rw-r--r--polyp/sample.h5
-rw-r--r--polyp/scache.c60
-rw-r--r--polyp/sink.c2
-rw-r--r--polyp/source.c2
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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <limits.h>
+
+#include <sndfile.h>
#include <polyp/polyplib.h>
#include <polyp/polyplib-error.h>
#include <polyp/mainloop.h>
#include <polyp/mainloop-signal.h>
+#include <polyp/sample.h>
+
+#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 <config.h>
#endif
+#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <netinet/in.h>
@@ -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);